+++ /dev/null
-From 4c8a49854130da0117a0fdb858551824919a2389 Mon Sep 17 00:00:00 2001
-From: Ingo Molnar <mingo@kernel.org>
-Date: Tue, 27 Feb 2024 09:58:15 +0100
-Subject: [PATCH] smp: Avoid 'setup_max_cpus' namespace collision/shadowing
-
-bringup_nonboot_cpus() gets passed the 'setup_max_cpus'
-variable in init/main.c - which is also the name of the parameter,
-shadowing the name.
-
-To reduce confusion and to allow the 'setup_max_cpus' value
-to be #defined in the <linux/smp.h> header, use the 'max_cpus'
-name for the function parameter name.
-
-Signed-off-by: Ingo Molnar <mingo@kernel.org>
-Cc: Thomas Gleixner <tglx@linutronix.de>
-Cc: linux-kernel@vger.kernel.org
----
- include/linux/cpu.h | 2 +-
- kernel/cpu.c | 6 +++---
- 2 files changed, 4 insertions(+), 4 deletions(-)
-
---- a/include/linux/cpu.h
-+++ b/include/linux/cpu.h
-@@ -109,7 +109,7 @@ void notify_cpu_starting(unsigned int cp
- extern void cpu_maps_update_begin(void);
- extern void cpu_maps_update_done(void);
- int bringup_hibernate_cpu(unsigned int sleep_cpu);
--void bringup_nonboot_cpus(unsigned int setup_max_cpus);
-+void bringup_nonboot_cpus(unsigned int max_cpus);
-
- #else /* CONFIG_SMP */
- #define cpuhp_tasks_frozen 0
---- a/kernel/cpu.c
-+++ b/kernel/cpu.c
-@@ -1905,17 +1905,17 @@ static bool __init cpuhp_bringup_cpus_pa
- static inline bool cpuhp_bringup_cpus_parallel(unsigned int ncpus) { return false; }
- #endif /* CONFIG_HOTPLUG_PARALLEL */
-
--void __init bringup_nonboot_cpus(unsigned int setup_max_cpus)
-+void __init bringup_nonboot_cpus(unsigned int max_cpus)
- {
-- if (!setup_max_cpus)
-+ if (!max_cpus)
- return;
-
- /* Try parallel bringup optimization if enabled */
-- if (cpuhp_bringup_cpus_parallel(setup_max_cpus))
-+ if (cpuhp_bringup_cpus_parallel(max_cpus))
- return;
-
- /* Full per CPU serialized bringup */
-- cpuhp_bringup_mask(cpu_present_mask, setup_max_cpus, CPUHP_ONLINE);
-+ cpuhp_bringup_mask(cpu_present_mask, max_cpus, CPUHP_ONLINE);
- }
-
- #ifdef CONFIG_PM_SLEEP_SMP
+++ /dev/null
-From 443df175be581618d6ff781dc3af3aa1a9ba789d Mon Sep 17 00:00:00 2001
-From: Tony Ambardar <Tony.Ambardar@gmail.com>
-Date: Fri, 31 May 2024 23:55:55 -0700
-Subject: [PATCH 1/2] compiler_types.h: Define __retain for
- __attribute__((__retain__))
-
-Some code includes the __used macro to prevent functions and data from
-being optimized out. This macro implements __attribute__((__used__)), which
-operates at the compiler and IR-level, and so still allows a linker to
-remove objects intended to be kept.
-
-Compilers supporting __attribute__((__retain__)) can address this gap by
-setting the flag SHF_GNU_RETAIN on the section of a function/variable,
-indicating to the linker the object should be retained. This attribute is
-available since gcc 11, clang 13, and binutils 2.36.
-
-Provide a __retain macro implementing __attribute__((__retain__)), whose
-first user will be the '__bpf_kfunc' tag.
-
-Link: https://lore.kernel.org/bpf/ZlmGoT9KiYLZd91S@krava/T/
-Cc: stable@vger.kernel.org # v6.6+
-Signed-off-by: Tony Ambardar <Tony.Ambardar@gmail.com>
----
- include/linux/compiler_types.h | 23 +++++++++++++++++++++++
- 1 file changed, 23 insertions(+)
-
---- a/include/linux/compiler_types.h
-+++ b/include/linux/compiler_types.h
-@@ -145,6 +145,29 @@ static inline void __chk_io_ptr(const vo
- #define __has_builtin(x) (0)
- #endif
-
-+/*
-+ * Annotating a function/variable with __retain tells the compiler to place
-+ * the object in its own section and set the flag SHF_GNU_RETAIN. This flag
-+ * instructs the linker to retain the object during garbage-cleanup or LTO
-+ * phases.
-+ *
-+ * Note that the __used macro is also used to prevent functions or data
-+ * being optimized out, but operates at the compiler/IR-level and may still
-+ * allow unintended removal of objects during linking.
-+ *
-+ * Optional: only supported since gcc >= 11, clang >= 13
-+ *
-+ * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-retain-function-attribute
-+ * clang: https://clang.llvm.org/docs/AttributeReference.html#retain
-+ */
-+#if __has_attribute(__retain__) && \
-+ (defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || \
-+ defined(CONFIG_LTO_CLANG))
-+# define __retain __attribute__((__retain__))
-+#else
-+# define __retain
-+#endif
-+
- /* Compiler specific macros. */
- #ifdef __clang__
- #include <linux/compiler-clang.h>
+++ /dev/null
-From ac507ed9882fd91a94657d68fe9ceac04b957103 Mon Sep 17 00:00:00 2001
-From: Tony Ambardar <Tony.Ambardar@gmail.com>
-Date: Sat, 1 Jun 2024 00:00:21 -0700
-Subject: [PATCH 2/2] bpf: Harden __bpf_kfunc tag against linker kfunc removal
-
-BPF kfuncs are often not directly referenced and may be inadvertently
-removed by optimization steps during kernel builds, thus the __bpf_kfunc
-tag mitigates against this removal by including the __used macro. However,
-this macro alone does not prevent removal during linking, and may still
-yield build warnings (e.g. on mips64el):
-
- LD vmlinux
- BTFIDS vmlinux
- WARN: resolve_btfids: unresolved symbol bpf_verify_pkcs7_signature
- WARN: resolve_btfids: unresolved symbol bpf_lookup_user_key
- WARN: resolve_btfids: unresolved symbol bpf_lookup_system_key
- WARN: resolve_btfids: unresolved symbol bpf_key_put
- WARN: resolve_btfids: unresolved symbol bpf_iter_task_next
- WARN: resolve_btfids: unresolved symbol bpf_iter_css_task_new
- WARN: resolve_btfids: unresolved symbol bpf_get_file_xattr
- WARN: resolve_btfids: unresolved symbol bpf_ct_insert_entry
- WARN: resolve_btfids: unresolved symbol bpf_cgroup_release
- WARN: resolve_btfids: unresolved symbol bpf_cgroup_from_id
- WARN: resolve_btfids: unresolved symbol bpf_cgroup_acquire
- WARN: resolve_btfids: unresolved symbol bpf_arena_free_pages
- NM System.map
- SORTTAB vmlinux
- OBJCOPY vmlinux.32
-
-Update the __bpf_kfunc tag to better guard against linker optimization by
-including the new __retain compiler macro, which fixes the warnings above.
-
-Verify the __retain macro with readelf by checking object flags for 'R':
-
- $ readelf -Wa kernel/trace/bpf_trace.o
- Section Headers:
- [Nr] Name Type Address Off Size ES Flg Lk Inf Al
- ...
- [178] .text.bpf_key_put PROGBITS 00000000 6420 0050 00 AXR 0 0 8
- ...
- Key to Flags:
- ...
- R (retain), D (mbind), p (processor specific)
-
-Link: https://lore.kernel.org/bpf/ZlmGoT9KiYLZd91S@krava/T/
-Reported-by: kernel test robot <lkp@intel.com>
-Closes: https://lore.kernel.org/r/202401211357.OCX9yllM-lkp@intel.com/
-Fixes: 57e7c169cd6a ("bpf: Add __bpf_kfunc tag for marking kernel functions as kfuncs")
-Cc: stable@vger.kernel.org # v6.6+
-Signed-off-by: Tony Ambardar <Tony.Ambardar@gmail.com>
----
- include/linux/btf.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/include/linux/btf.h
-+++ b/include/linux/btf.h
-@@ -81,7 +81,7 @@
- * as to avoid issues such as the compiler inlining or eliding either a static
- * kfunc, or a global kfunc in an LTO build.
- */
--#define __bpf_kfunc __used noinline
-+#define __bpf_kfunc __used __retain noinline
-
- /*
- * Return the name of the passed struct, if exists, or halt the build if for
+++ /dev/null
-From fab45b962749184e1a1a57c7c583782b78fad539 Mon Sep 17 00:00:00 2001
-From: Sam James <sam@gentoo.org>
-Date: Tue, 13 Aug 2024 20:49:06 +0100
-Subject: [PATCH] libbpf: Workaround -Wmaybe-uninitialized false positive
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-In `elf_close`, we get this with GCC 15 -O3 (at least):
-```
-In function ‘elf_close’,
- inlined from ‘elf_close’ at elf.c:53:6,
- inlined from ‘elf_find_func_offset_from_file’ at elf.c:384:2:
-elf.c:57:9: warning: ‘elf_fd.elf’ may be used uninitialized [-Wmaybe-uninitialized]
- 57 | elf_end(elf_fd->elf);
- | ^~~~~~~~~~~~~~~~~~~~
-elf.c: In function ‘elf_find_func_offset_from_file’:
-elf.c:377:23: note: ‘elf_fd.elf’ was declared here
- 377 | struct elf_fd elf_fd;
- | ^~~~~~
-In function ‘elf_close’,
- inlined from ‘elf_close’ at elf.c:53:6,
- inlined from ‘elf_find_func_offset_from_file’ at elf.c:384:2:
-elf.c:58:9: warning: ‘elf_fd.fd’ may be used uninitialized [-Wmaybe-uninitialized]
- 58 | close(elf_fd->fd);
- | ^~~~~~~~~~~~~~~~~
-elf.c: In function ‘elf_find_func_offset_from_file’:
-elf.c:377:23: note: ‘elf_fd.fd’ was declared here
- 377 | struct elf_fd elf_fd;
- | ^~~~~~
-```
-
-In reality, our use is fine, it's just that GCC doesn't model errno
-here (see linked GCC bug). Suppress -Wmaybe-uninitialized accordingly
-by initializing elf_fd.fd to -1 and elf_fd.elf to NULL.
-
-I've done this in two other functions as well given it could easily
-occur there too (same access/use pattern).
-
-Signed-off-by: Sam James <sam@gentoo.org>
-Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
-Link: https://gcc.gnu.org/PR114952
-Link: https://lore.kernel.org/bpf/14ec488a1cac02794c2fa2b83ae0cef1bce2cb36.1723578546.git.sam@gentoo.org
----
- tools/lib/bpf/elf.c | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/tools/lib/bpf/elf.c
-+++ b/tools/lib/bpf/elf.c
-@@ -16,6 +16,9 @@ int elf_open(const char *binary_path, st
- int fd, ret;
- Elf *elf;
-
-+ elf_fd->elf = NULL;
-+ elf_fd->fd = -1;
-+
- if (elf_version(EV_CURRENT) == EV_NONE) {
- pr_warn("elf: failed to init libelf for %s\n", binary_path);
- return -LIBBPF_ERRNO__LIBELF;
+++ /dev/null
-From ed0f941022515ff40473ea5335769a5dc2524a3f Mon Sep 17 00:00:00 2001
-From: Yuntao Liu <liuyuntao12@huawei.com>
-Date: Mon, 3 Jun 2024 16:37:50 +0100
-Subject: [PATCH] ARM: 9404/1: arm32: enable HAVE_LD_DEAD_CODE_DATA_ELIMINATION
-
-The current arm32 architecture does not yet support the
-HAVE_LD_DEAD_CODE_DATA_ELIMINATION feature. arm32 is widely used in
-embedded scenarios, and enabling this feature would be beneficial for
-reducing the size of the kernel image.
-
-In order to make this work, we keep the necessary tables by annotating
-them with KEEP, also it requires further changes to linker script to KEEP
-some tables and wildcard compiler generated sections into the right place.
-When using ld.lld for linking, KEEP is not recognized within the OVERLAY
-command, and Ard proposed a concise method to solve this problem.
-
-It boots normally with defconfig, vexpress_defconfig and tinyconfig.
-
-The size comparison of zImage is as follows:
-defconfig vexpress_defconfig tinyconfig
-5137712 5138024 424192 no dce
-5032560 4997824 298384 dce
-2.0% 2.7% 29.7% shrink
-
-When using smaller config file, there is a significant reduction in the
-size of the zImage.
-
-We also tested this patch on a commercially available single-board
-computer, and the comparison is as follows:
-a15eb_config
-2161384 no dce
-2092240 dce
-3.2% shrink
-
-The zImage size has been reduced by approximately 3.2%, which is 70KB on
-2.1M.
-
-Signed-off-by: Yuntao Liu <liuyuntao12@huawei.com>
-Tested-by: Arnd Bergmann <arnd@arndb.de>
-Reviewed-by: Arnd Bergmann <arnd@arndb.de>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
----
- arch/arm/Kconfig | 1 +
- arch/arm/boot/compressed/vmlinux.lds.S | 2 +-
- arch/arm/include/asm/vmlinux.lds.h | 2 +-
- arch/arm/kernel/entry-armv.S | 3 +++
- arch/arm/kernel/vmlinux-xip.lds.S | 4 ++--
- arch/arm/kernel/vmlinux.lds.S | 6 +++---
- drivers/firmware/efi/libstub/Makefile | 4 ++++
- 7 files changed, 15 insertions(+), 7 deletions(-)
-
---- a/arch/arm/Kconfig
-+++ b/arch/arm/Kconfig
-@@ -111,6 +111,7 @@ config ARM
- select HAVE_KERNEL_XZ
- select HAVE_KPROBES if !XIP_KERNEL && !CPU_ENDIAN_BE32 && !CPU_V7M
- select HAVE_KRETPROBES if HAVE_KPROBES
-+ select HAVE_LD_DEAD_CODE_DATA_ELIMINATION
- select HAVE_MOD_ARCH_SPECIFIC
- select HAVE_NMI
- select HAVE_OPTPROBES if !THUMB2_KERNEL
---- a/arch/arm/boot/compressed/vmlinux.lds.S
-+++ b/arch/arm/boot/compressed/vmlinux.lds.S
-@@ -125,7 +125,7 @@ SECTIONS
-
- . = BSS_START;
- __bss_start = .;
-- .bss : { *(.bss) }
-+ .bss : { *(.bss .bss.*) }
- _end = .;
-
- . = ALIGN(8); /* the stack must be 64-bit aligned */
---- a/arch/arm/include/asm/vmlinux.lds.h
-+++ b/arch/arm/include/asm/vmlinux.lds.h
-@@ -42,7 +42,7 @@
- #define PROC_INFO \
- . = ALIGN(4); \
- __proc_info_begin = .; \
-- *(.proc.info.init) \
-+ KEEP(*(.proc.info.init)) \
- __proc_info_end = .;
-
- #define IDMAP_TEXT \
---- a/arch/arm/kernel/entry-armv.S
-+++ b/arch/arm/kernel/entry-armv.S
-@@ -1073,6 +1073,7 @@ vector_addrexcptn:
- .globl vector_fiq
-
- .section .vectors, "ax", %progbits
-+ .reloc .text, R_ARM_NONE, .
- W(b) vector_rst
- W(b) vector_und
- ARM( .reloc ., R_ARM_LDR_PC_G0, .L__vector_swi )
-@@ -1086,6 +1087,7 @@ THUMB( .reloc ., R_ARM_THM_PC12, .L__vec
-
- #ifdef CONFIG_HARDEN_BRANCH_HISTORY
- .section .vectors.bhb.loop8, "ax", %progbits
-+ .reloc .text, R_ARM_NONE, .
- W(b) vector_rst
- W(b) vector_bhb_loop8_und
- ARM( .reloc ., R_ARM_LDR_PC_G0, .L__vector_bhb_loop8_swi )
-@@ -1098,6 +1100,7 @@ THUMB( .reloc ., R_ARM_THM_PC12, .L__vec
- W(b) vector_bhb_loop8_fiq
-
- .section .vectors.bhb.bpiall, "ax", %progbits
-+ .reloc .text, R_ARM_NONE, .
- W(b) vector_rst
- W(b) vector_bhb_bpiall_und
- ARM( .reloc ., R_ARM_LDR_PC_G0, .L__vector_bhb_bpiall_swi )
---- a/arch/arm/kernel/vmlinux-xip.lds.S
-+++ b/arch/arm/kernel/vmlinux-xip.lds.S
-@@ -63,7 +63,7 @@ SECTIONS
- . = ALIGN(4);
- __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {
- __start___ex_table = .;
-- ARM_MMU_KEEP(*(__ex_table))
-+ ARM_MMU_KEEP(KEEP(*(__ex_table)))
- __stop___ex_table = .;
- }
-
-@@ -83,7 +83,7 @@ SECTIONS
- }
- .init.arch.info : {
- __arch_info_begin = .;
-- *(.arch.info.init)
-+ KEEP(*(.arch.info.init))
- __arch_info_end = .;
- }
- .init.tagtable : {
---- a/arch/arm/kernel/vmlinux.lds.S
-+++ b/arch/arm/kernel/vmlinux.lds.S
-@@ -74,7 +74,7 @@ SECTIONS
- . = ALIGN(4);
- __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {
- __start___ex_table = .;
-- ARM_MMU_KEEP(*(__ex_table))
-+ ARM_MMU_KEEP(KEEP(*(__ex_table)))
- __stop___ex_table = .;
- }
-
-@@ -99,7 +99,7 @@ SECTIONS
- }
- .init.arch.info : {
- __arch_info_begin = .;
-- *(.arch.info.init)
-+ KEEP(*(.arch.info.init))
- __arch_info_end = .;
- }
- .init.tagtable : {
-@@ -116,7 +116,7 @@ SECTIONS
- #endif
- .init.pv_table : {
- __pv_table_begin = .;
-- *(.pv_table)
-+ KEEP(*(.pv_table))
- __pv_table_end = .;
- }
-
---- a/drivers/firmware/efi/libstub/Makefile
-+++ b/drivers/firmware/efi/libstub/Makefile
-@@ -67,6 +67,10 @@ OBJECT_FILES_NON_STANDARD := y
- # Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
- KCOV_INSTRUMENT := n
-
-+# The .data section would be renamed to .data.efistub, therefore, remove
-+# `-fdata-sections` flag from KBUILD_CFLAGS_KERNEL
-+KBUILD_CFLAGS_KERNEL := $(filter-out -fdata-sections, $(KBUILD_CFLAGS_KERNEL))
-+
- lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \
- file.o mem.o random.o randomalloc.o pci.o \
- skip_spaces.o lib-cmdline.o lib-ctype.o \
+++ /dev/null
-From 65033574ade97afccba074d837fd269903a83a9a Mon Sep 17 00:00:00 2001
-From: Catalin Marinas <catalin.marinas@arm.com>
-Date: Thu, 5 Oct 2023 16:40:30 +0100
-Subject: [PATCH] arm64: swiotlb: Reduce the default size if no ZONE_DMA
- bouncing needed
-
-With CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC enabled, the arm64 kernel still
-allocates the default SWIOTLB buffer (64MB) even if ZONE_DMA is disabled
-or all the RAM fits into this zone. However, this potentially wastes a
-non-negligible amount of memory on platforms with little RAM.
-
-Reduce the SWIOTLB size to 1MB per 1GB of RAM if only needed for
-kmalloc() buffer bouncing.
-
-Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
-Suggested-by: Ross Burton <ross.burton@arm.com>
-Cc: Ross Burton <ross.burton@arm.com>
-Cc: Will Deacon <will@kernel.org>
-Reviewed-by: Robin Murphy <robin.murphy@arm.com>
----
- arch/arm64/mm/init.c | 11 ++++++++++-
- 1 file changed, 10 insertions(+), 1 deletion(-)
-
---- a/arch/arm64/mm/init.c
-+++ b/arch/arm64/mm/init.c
-@@ -16,6 +16,7 @@
- #include <linux/nodemask.h>
- #include <linux/initrd.h>
- #include <linux/gfp.h>
-+#include <linux/math.h>
- #include <linux/memblock.h>
- #include <linux/sort.h>
- #include <linux/of.h>
-@@ -493,8 +494,16 @@ void __init mem_init(void)
- {
- bool swiotlb = max_pfn > PFN_DOWN(arm64_dma_phys_limit);
-
-- if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC))
-+ if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) && !swiotlb) {
-+ /*
-+ * If no bouncing needed for ZONE_DMA, reduce the swiotlb
-+ * buffer for kmalloc() bouncing to 1MB per 1GB of RAM.
-+ */
-+ unsigned long size =
-+ DIV_ROUND_UP(memblock_phys_mem_size(), 1024);
-+ swiotlb_adjust_size(min(swiotlb_size_or_default(), size));
- swiotlb = true;
-+ }
-
- swiotlb_init(swiotlb, SWIOTLB_VERBOSE);
-
+++ /dev/null
-From 66a5c40f60f5d88ad8d47ba6a4ba05892853fa1f Mon Sep 17 00:00:00 2001
-From: Tanzir Hasan <tanzirh@google.com>
-Date: Tue, 26 Dec 2023 18:00:00 +0000
-Subject: [PATCH] kernel.h: removed REPEAT_BYTE from kernel.h
-
-This patch creates wordpart.h and includes it in asm/word-at-a-time.h
-for all architectures. WORD_AT_A_TIME_CONSTANTS depends on kernel.h
-because of REPEAT_BYTE. Moving this to another header and including it
-where necessary allows us to not include the bloated kernel.h. Making
-this implicit dependency on REPEAT_BYTE explicit allows for later
-improvements in the lib/string.c inclusion list.
-
-Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
-Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
-Signed-off-by: Tanzir Hasan <tanzirh@google.com>
-Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
-Link: https://lore.kernel.org/r/20231226-libstringheader-v6-1-80aa08c7652c@google.com
-Signed-off-by: Kees Cook <keescook@chromium.org>
----
- arch/arm/include/asm/word-at-a-time.h | 3 ++-
- arch/arm64/include/asm/word-at-a-time.h | 3 ++-
- arch/powerpc/include/asm/word-at-a-time.h | 4 ++--
- arch/riscv/include/asm/word-at-a-time.h | 3 ++-
- arch/s390/include/asm/word-at-a-time.h | 3 ++-
- arch/sh/include/asm/word-at-a-time.h | 2 ++
- arch/x86/include/asm/word-at-a-time.h | 3 ++-
- arch/x86/kvm/mmu/mmu.c | 1 +
- fs/namei.c | 2 +-
- include/asm-generic/word-at-a-time.h | 3 ++-
- include/linux/kernel.h | 8 --------
- include/linux/wordpart.h | 13 +++++++++++++
- 12 files changed, 31 insertions(+), 17 deletions(-)
- create mode 100644 include/linux/wordpart.h
-
---- a/arch/arm/include/asm/word-at-a-time.h
-+++ b/arch/arm/include/asm/word-at-a-time.h
-@@ -8,7 +8,8 @@
- * Little-endian word-at-a-time zero byte handling.
- * Heavily based on the x86 algorithm.
- */
--#include <linux/kernel.h>
-+#include <linux/bitops.h>
-+#include <linux/wordpart.h>
-
- struct word_at_a_time {
- const unsigned long one_bits, high_bits;
---- a/arch/arm64/include/asm/word-at-a-time.h
-+++ b/arch/arm64/include/asm/word-at-a-time.h
-@@ -9,7 +9,8 @@
-
- #ifndef __AARCH64EB__
-
--#include <linux/kernel.h>
-+#include <linux/bitops.h>
-+#include <linux/wordpart.h>
-
- struct word_at_a_time {
- const unsigned long one_bits, high_bits;
---- a/arch/powerpc/include/asm/word-at-a-time.h
-+++ b/arch/powerpc/include/asm/word-at-a-time.h
-@@ -4,8 +4,8 @@
- /*
- * Word-at-a-time interfaces for PowerPC.
- */
--
--#include <linux/kernel.h>
-+#include <linux/bitops.h>
-+#include <linux/wordpart.h>
- #include <asm/asm-compat.h>
- #include <asm/extable.h>
-
---- a/arch/sh/include/asm/word-at-a-time.h
-+++ b/arch/sh/include/asm/word-at-a-time.h
-@@ -5,6 +5,8 @@
- #ifdef CONFIG_CPU_BIG_ENDIAN
- # include <asm-generic/word-at-a-time.h>
- #else
-+#include <linux/bitops.h>
-+#include <linux/wordpart.h>
- /*
- * Little-endian version cribbed from x86.
- */
---- a/arch/x86/include/asm/word-at-a-time.h
-+++ b/arch/x86/include/asm/word-at-a-time.h
-@@ -2,7 +2,8 @@
- #ifndef _ASM_WORD_AT_A_TIME_H
- #define _ASM_WORD_AT_A_TIME_H
-
--#include <linux/kernel.h>
-+#include <linux/bitops.h>
-+#include <linux/wordpart.h>
-
- /*
- * This is largely generic for little-endian machines, but the
---- a/arch/x86/kvm/mmu/mmu.c
-+++ b/arch/x86/kvm/mmu/mmu.c
-@@ -47,6 +47,7 @@
- #include <linux/kern_levels.h>
- #include <linux/kstrtox.h>
- #include <linux/kthread.h>
-+#include <linux/wordpart.h>
-
- #include <asm/page.h>
- #include <asm/memtype.h>
---- a/fs/namei.c
-+++ b/fs/namei.c
-@@ -17,8 +17,8 @@
-
- #include <linux/init.h>
- #include <linux/export.h>
--#include <linux/kernel.h>
- #include <linux/slab.h>
-+#include <linux/wordpart.h>
- #include <linux/fs.h>
- #include <linux/filelock.h>
- #include <linux/namei.h>
---- a/include/asm-generic/word-at-a-time.h
-+++ b/include/asm-generic/word-at-a-time.h
-@@ -2,7 +2,8 @@
- #ifndef _ASM_WORD_AT_A_TIME_H
- #define _ASM_WORD_AT_A_TIME_H
-
--#include <linux/kernel.h>
-+#include <linux/bitops.h>
-+#include <linux/wordpart.h>
- #include <asm/byteorder.h>
-
- #ifdef __BIG_ENDIAN
---- a/include/linux/kernel.h
-+++ b/include/linux/kernel.h
-@@ -38,14 +38,6 @@
-
- #define STACK_MAGIC 0xdeadbeef
-
--/**
-- * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
-- * @x: value to repeat
-- *
-- * NOTE: @x is not checked for > 0xff; larger values produce odd results.
-- */
--#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
--
- /* generic data direction definitions */
- #define READ 0
- #define WRITE 1
---- /dev/null
-+++ b/include/linux/wordpart.h
-@@ -0,0 +1,13 @@
-+/* SPDX-License-Identifier: GPL-2.0 */
-+
-+#ifndef _LINUX_WORDPART_H
-+#define _LINUX_WORDPART_H
-+/**
-+ * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
-+ * @x: value to repeat
-+ *
-+ * NOTE: @x is not checked for > 0xff; larger values produce odd results.
-+ */
-+#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
-+
-+#endif // _LINUX_WORDPART_H
+++ /dev/null
-From adeb04362d74188c1e22ccb824b15a0a7b3de2f4 Mon Sep 17 00:00:00 2001
-From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-Date: Wed, 14 Feb 2024 19:26:32 +0200
-Subject: [PATCH] kernel.h: Move upper_*_bits() and lower_*_bits() to
- wordpart.h
-
-The wordpart.h header is collecting APIs related to the handling
-parts of the word (usually in byte granularity). The upper_*_bits()
-and lower_*_bits() are good candidates to be moved to there.
-
-This helps to clean up header dependency hell with regard to kernel.h
-as the latter gathers completely unrelated stuff together and slows
-down compilation (especially when it's included into other header).
-
-Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-Link: https://lore.kernel.org/r/20240214172752.3605073-1-andriy.shevchenko@linux.intel.com
-Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
-Signed-off-by: Kees Cook <keescook@chromium.org>
----
- include/linux/kernel.h | 30 ++----------------------------
- include/linux/wordpart.h | 29 +++++++++++++++++++++++++++++
- 2 files changed, 31 insertions(+), 28 deletions(-)
-
---- a/include/linux/kernel.h
-+++ b/include/linux/kernel.h
-@@ -32,6 +32,8 @@
- #include <linux/sprintf.h>
- #include <linux/static_call_types.h>
- #include <linux/instruction_pointer.h>
-+#include <linux/wordpart.h>
-+
- #include <asm/byteorder.h>
-
- #include <uapi/linux/kernel.h>
-@@ -57,34 +59,6 @@
- } \
- )
-
--/**
-- * upper_32_bits - return bits 32-63 of a number
-- * @n: the number we're accessing
-- *
-- * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress
-- * the "right shift count >= width of type" warning when that quantity is
-- * 32-bits.
-- */
--#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
--
--/**
-- * lower_32_bits - return bits 0-31 of a number
-- * @n: the number we're accessing
-- */
--#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
--
--/**
-- * upper_16_bits - return bits 16-31 of a number
-- * @n: the number we're accessing
-- */
--#define upper_16_bits(n) ((u16)((n) >> 16))
--
--/**
-- * lower_16_bits - return bits 0-15 of a number
-- * @n: the number we're accessing
-- */
--#define lower_16_bits(n) ((u16)((n) & 0xffff))
--
- struct completion;
- struct user;
-
---- a/include/linux/wordpart.h
-+++ b/include/linux/wordpart.h
-@@ -2,6 +2,35 @@
-
- #ifndef _LINUX_WORDPART_H
- #define _LINUX_WORDPART_H
-+
-+/**
-+ * upper_32_bits - return bits 32-63 of a number
-+ * @n: the number we're accessing
-+ *
-+ * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress
-+ * the "right shift count >= width of type" warning when that quantity is
-+ * 32-bits.
-+ */
-+#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
-+
-+/**
-+ * lower_32_bits - return bits 0-31 of a number
-+ * @n: the number we're accessing
-+ */
-+#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
-+
-+/**
-+ * upper_16_bits - return bits 16-31 of a number
-+ * @n: the number we're accessing
-+ */
-+#define upper_16_bits(n) ((u16)((n) >> 16))
-+
-+/**
-+ * lower_16_bits - return bits 0-15 of a number
-+ * @n: the number we're accessing
-+ */
-+#define lower_16_bits(n) ((u16)((n) & 0xffff))
-+
- /**
- * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
- * @x: value to repeat
+++ /dev/null
-From 8cd2accb71f5eb8e92d775fc1978d3779875c2e5 Mon Sep 17 00:00:00 2001
-From: Baoquan He <bhe@redhat.com>
-Date: Fri, 8 Dec 2023 15:30:34 +0800
-Subject: [PATCH] mips, kexec: fix the incorrect ifdeffery and dependency of
- CONFIG_KEXEC
-
-The select of KEXEC for CRASH_DUMP in kernel/Kconfig.kexec will be
-dropped, then compiling errors will be triggered if below config items are
-set:
-
-===
-CONFIG_CRASH_CORE=y
-CONFIG_KEXEC_CORE=y
-CONFIG_CRASH_DUMP=y
-===
-
---------------------------------------------------------------------
-mipsel-linux-ld: kernel/kexec_core.o: in function `kimage_free':
-kernel/kexec_core.c:(.text+0x2200): undefined reference to `machine_kexec_cleanup'
-mipsel-linux-ld: kernel/kexec_core.o: in function `__crash_kexec':
-kernel/kexec_core.c:(.text+0x2480): undefined reference to `machine_crash_shutdown'
-mipsel-linux-ld: kernel/kexec_core.c:(.text+0x2488): undefined reference to `machine_kexec'
-mipsel-linux-ld: kernel/kexec_core.o: in function `kernel_kexec':
-kernel/kexec_core.c:(.text+0x29b8): undefined reference to `machine_shutdown'
-mipsel-linux-ld: kernel/kexec_core.c:(.text+0x29c0): undefined reference to `machine_kexec'
---------------------------------------------------------------------
-
-Here, change the dependency of building kexec_core related object files,
-and the ifdeffery in mips from CONFIG_KEXEC to CONFIG_KEXEC_CORE.
-
-Link: https://lkml.kernel.org/r/20231208073036.7884-4-bhe@redhat.com
-Signed-off-by: Baoquan He <bhe@redhat.com>
-Reported-by: kernel test robot <lkp@intel.com>
-Closes: https://lore.kernel.org/oe-kbuild-all/202311302042.sn8cDPIX-lkp@intel.com/
-Cc: Eric DeVolder <eric_devolder@yahoo.com>
-Cc: Ignat Korchagin <ignat@cloudflare.com>
-Cc: Stephen Rothwell <sfr@canb.auug.org.au>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
----
- arch/mips/cavium-octeon/smp.c | 4 ++--
- arch/mips/include/asm/kexec.h | 2 +-
- arch/mips/include/asm/smp-ops.h | 2 +-
- arch/mips/include/asm/smp.h | 2 +-
- arch/mips/kernel/Makefile | 2 +-
- arch/mips/kernel/smp-bmips.c | 4 ++--
- arch/mips/kernel/smp-cps.c | 10 +++++-----
- arch/mips/loongson64/reset.c | 4 ++--
- arch/mips/loongson64/smp.c | 2 +-
- 9 files changed, 16 insertions(+), 16 deletions(-)
-
---- a/arch/mips/cavium-octeon/smp.c
-+++ b/arch/mips/cavium-octeon/smp.c
-@@ -422,7 +422,7 @@ static const struct plat_smp_ops octeon_
- .cpu_disable = octeon_cpu_disable,
- .cpu_die = octeon_cpu_die,
- #endif
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
- .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
- #endif
- };
-@@ -502,7 +502,7 @@ static const struct plat_smp_ops octeon_
- .cpu_disable = octeon_cpu_disable,
- .cpu_die = octeon_cpu_die,
- #endif
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
- .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
- #endif
- };
---- a/arch/mips/include/asm/kexec.h
-+++ b/arch/mips/include/asm/kexec.h
-@@ -31,7 +31,7 @@ static inline void crash_setup_regs(stru
- prepare_frametrace(newregs);
- }
-
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
- struct kimage;
- extern unsigned long kexec_args[4];
- extern int (*_machine_kexec_prepare)(struct kimage *);
---- a/arch/mips/include/asm/smp-ops.h
-+++ b/arch/mips/include/asm/smp-ops.h
-@@ -35,7 +35,7 @@ struct plat_smp_ops {
- void (*cpu_die)(unsigned int cpu);
- void (*cleanup_dead_cpu)(unsigned cpu);
- #endif
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
- void (*kexec_nonboot_cpu)(void);
- #endif
- };
---- a/arch/mips/include/asm/smp.h
-+++ b/arch/mips/include/asm/smp.h
-@@ -93,7 +93,7 @@ static inline void __cpu_die(unsigned in
- extern void __noreturn play_dead(void);
- #endif
-
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
- static inline void kexec_nonboot_cpu(void)
- {
- extern const struct plat_smp_ops *mp_ops; /* private */
---- a/arch/mips/kernel/Makefile
-+++ b/arch/mips/kernel/Makefile
-@@ -90,7 +90,7 @@ obj-$(CONFIG_GPIO_TXX9) += gpio_txx9.o
-
- obj-$(CONFIG_RELOCATABLE) += relocate.o
-
--obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o
-+obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o relocate_kernel.o crash.o
- obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
- obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
- obj-$(CONFIG_EARLY_PRINTK_8250) += early_printk_8250.o
---- a/arch/mips/kernel/smp-bmips.c
-+++ b/arch/mips/kernel/smp-bmips.c
-@@ -434,7 +434,7 @@ const struct plat_smp_ops bmips43xx_smp_
- .cpu_disable = bmips_cpu_disable,
- .cpu_die = bmips_cpu_die,
- #endif
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
- .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
- #endif
- };
-@@ -451,7 +451,7 @@ const struct plat_smp_ops bmips5000_smp_
- .cpu_disable = bmips_cpu_disable,
- .cpu_die = bmips_cpu_die,
- #endif
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
- .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
- #endif
- };
---- a/arch/mips/kernel/smp-cps.c
-+++ b/arch/mips/kernel/smp-cps.c
-@@ -395,7 +395,7 @@ static void cps_smp_finish(void)
- local_irq_enable();
- }
-
--#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_KEXEC)
-+#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_KEXEC_CORE)
-
- enum cpu_death {
- CPU_DEATH_HALT,
-@@ -432,7 +432,7 @@ static void cps_shutdown_this_cpu(enum c
- }
- }
-
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
-
- static void cps_kexec_nonboot_cpu(void)
- {
-@@ -442,9 +442,9 @@ static void cps_kexec_nonboot_cpu(void)
- cps_shutdown_this_cpu(CPU_DEATH_POWER);
- }
-
--#endif /* CONFIG_KEXEC */
-+#endif /* CONFIG_KEXEC_CORE */
-
--#endif /* CONFIG_HOTPLUG_CPU || CONFIG_KEXEC */
-+#endif /* CONFIG_HOTPLUG_CPU || CONFIG_KEXEC_CORE */
-
- #ifdef CONFIG_HOTPLUG_CPU
-
-@@ -613,7 +613,7 @@ static const struct plat_smp_ops cps_smp
- .cpu_die = cps_cpu_die,
- .cleanup_dead_cpu = cps_cleanup_dead_cpu,
- #endif
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
- .kexec_nonboot_cpu = cps_kexec_nonboot_cpu,
- #endif
- };
---- a/arch/mips/loongson64/reset.c
-+++ b/arch/mips/loongson64/reset.c
-@@ -39,7 +39,7 @@ static int firmware_poweroff(struct sys_
- return NOTIFY_DONE;
- }
-
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
-
- /* 0X80000000~0X80200000 is safe */
- #define MAX_ARGS 64
-@@ -152,7 +152,7 @@ static int __init mips_reboot_setup(void
- firmware_poweroff, NULL);
- }
-
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
- kexec_argv = kmalloc(KEXEC_ARGV_SIZE, GFP_KERNEL);
- if (WARN_ON(!kexec_argv))
- return -ENOMEM;
---- a/arch/mips/loongson64/smp.c
-+++ b/arch/mips/loongson64/smp.c
-@@ -883,7 +883,7 @@ const struct plat_smp_ops loongson3_smp_
- .cpu_disable = loongson3_cpu_disable,
- .cpu_die = loongson3_cpu_die,
- #endif
--#ifdef CONFIG_KEXEC
-+#ifdef CONFIG_KEXEC_CORE
- .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
- #endif
- };
+++ /dev/null
-From a5c05453a13ab324ad8719e8a23dfb6af01f3652 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 20 Jun 2024 17:26:42 +0200
-Subject: [PATCH 1/4] mips: bmips: rework and cache CBR addr handling
-
-Rework the handling of the CBR address and cache it. This address
-doesn't change and can be cached instead of reading the register every
-time.
-
-This is in preparation of permitting to tweak the CBR address in DT with
-broken SoC or bootloader.
-
-bmips_cbr_addr is defined in setup.c for each arch to keep compatibility
-with legacy brcm47xx/brcm63xx and generic BMIPS target.
-
-Acked-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
----
- arch/mips/bcm47xx/prom.c | 3 +++
- arch/mips/bcm47xx/setup.c | 4 ++++
- arch/mips/bcm63xx/prom.c | 3 +++
- arch/mips/bcm63xx/setup.c | 4 ++++
- arch/mips/bmips/dma.c | 2 +-
- arch/mips/bmips/setup.c | 7 ++++++-
- arch/mips/include/asm/bmips.h | 1 +
- arch/mips/kernel/smp-bmips.c | 4 ++--
- 8 files changed, 24 insertions(+), 4 deletions(-)
-
---- a/arch/mips/bcm47xx/prom.c
-+++ b/arch/mips/bcm47xx/prom.c
-@@ -32,6 +32,7 @@
- #include <linux/ssb/ssb_driver_chipcommon.h>
- #include <linux/ssb/ssb_regs.h>
- #include <linux/smp.h>
-+#include <asm/bmips.h>
- #include <asm/bootinfo.h>
- #include <bcm47xx.h>
- #include <bcm47xx_board.h>
-@@ -109,6 +110,8 @@ static __init void prom_init_mem(void)
-
- void __init prom_init(void)
- {
-+ /* Cache CBR addr before CPU/DMA setup */
-+ bmips_cbr_addr = BMIPS_GET_CBR();
- prom_init_mem();
- setup_8250_early_printk_port(CKSEG1ADDR(BCM47XX_SERIAL_ADDR), 0, 0);
- }
---- a/arch/mips/bcm47xx/setup.c
-+++ b/arch/mips/bcm47xx/setup.c
-@@ -37,6 +37,7 @@
- #include <linux/ssb/ssb.h>
- #include <linux/ssb/ssb_embedded.h>
- #include <linux/bcma/bcma_soc.h>
-+#include <asm/bmips.h>
- #include <asm/bootinfo.h>
- #include <asm/idle.h>
- #include <asm/prom.h>
-@@ -45,6 +46,9 @@
- #include <bcm47xx.h>
- #include <bcm47xx_board.h>
-
-+/* CBR addr doesn't change and we can cache it */
-+void __iomem *bmips_cbr_addr __read_mostly;
-+
- union bcm47xx_bus bcm47xx_bus;
- EXPORT_SYMBOL(bcm47xx_bus);
-
---- a/arch/mips/bcm63xx/prom.c
-+++ b/arch/mips/bcm63xx/prom.c
-@@ -22,6 +22,9 @@ void __init prom_init(void)
- {
- u32 reg, mask;
-
-+ /* Cache CBR addr before CPU/DMA setup */
-+ bmips_cbr_addr = BMIPS_GET_CBR();
-+
- bcm63xx_cpu_init();
-
- /* stop any running watchdog */
---- a/arch/mips/bcm63xx/setup.c
-+++ b/arch/mips/bcm63xx/setup.c
-@@ -12,6 +12,7 @@
- #include <linux/memblock.h>
- #include <linux/ioport.h>
- #include <linux/pm.h>
-+#include <asm/bmips.h>
- #include <asm/bootinfo.h>
- #include <asm/time.h>
- #include <asm/reboot.h>
-@@ -22,6 +23,9 @@
- #include <bcm63xx_io.h>
- #include <bcm63xx_gpio.h>
-
-+/* CBR addr doesn't change and we can cache it */
-+void __iomem *bmips_cbr_addr __read_mostly;
-+
- void bcm63xx_machine_halt(void)
- {
- pr_info("System halted\n");
---- a/arch/mips/bmips/dma.c
-+++ b/arch/mips/bmips/dma.c
-@@ -9,7 +9,7 @@ bool bmips_rac_flush_disable;
-
- void arch_sync_dma_for_cpu_all(void)
- {
-- void __iomem *cbr = BMIPS_GET_CBR();
-+ void __iomem *cbr = bmips_cbr_addr;
- u32 cfg;
-
- if (boot_cpu_type() != CPU_BMIPS3300 &&
---- a/arch/mips/bmips/setup.c
-+++ b/arch/mips/bmips/setup.c
-@@ -34,6 +34,9 @@
- #define REG_BCM6328_OTP ((void __iomem *)CKSEG1ADDR(0x1000062c))
- #define BCM6328_TP1_DISABLED BIT(9)
-
-+/* CBR addr doesn't change and we can cache it */
-+void __iomem *bmips_cbr_addr __read_mostly;
-+
- extern bool bmips_rac_flush_disable;
-
- static const unsigned long kbase = VMLINUX_LOAD_ADDRESS & 0xfff00000;
-@@ -111,7 +114,7 @@ static void bcm6358_quirks(void)
- * because the bootloader is not initializing it properly.
- */
- bmips_rac_flush_disable = !!(read_c0_brcm_cmt_local() & (1 << 31)) ||
-- !!BMIPS_GET_CBR();
-+ !!bmips_cbr_addr;
- }
-
- static void bcm6368_quirks(void)
-@@ -144,6 +147,8 @@ static void __init bmips_init_cfe(void)
-
- void __init prom_init(void)
- {
-+ /* Cache CBR addr before CPU/DMA setup */
-+ bmips_cbr_addr = BMIPS_GET_CBR();
- bmips_init_cfe();
- bmips_cpu_setup();
- register_bmips_smp_ops();
---- a/arch/mips/include/asm/bmips.h
-+++ b/arch/mips/include/asm/bmips.h
-@@ -81,6 +81,7 @@ extern char bmips_smp_movevec[];
- extern char bmips_smp_int_vec[];
- extern char bmips_smp_int_vec_end[];
-
-+extern void __iomem *bmips_cbr_addr;
- extern int bmips_smp_enabled;
- extern int bmips_cpu_offset;
- extern cpumask_t bmips_booted_mask;
---- a/arch/mips/kernel/smp-bmips.c
-+++ b/arch/mips/kernel/smp-bmips.c
-@@ -518,7 +518,7 @@ static void bmips_set_reset_vec(int cpu,
- info.val = val;
- bmips_set_reset_vec_remote(&info);
- } else {
-- void __iomem *cbr = BMIPS_GET_CBR();
-+ void __iomem *cbr = bmips_cbr_addr;
-
- if (cpu == 0)
- __raw_writel(val, cbr + BMIPS_RELO_VECTOR_CONTROL_0);
-@@ -591,7 +591,7 @@ asmlinkage void __weak plat_wired_tlb_se
-
- void bmips_cpu_setup(void)
- {
-- void __iomem __maybe_unused *cbr = BMIPS_GET_CBR();
-+ void __iomem __maybe_unused *cbr = bmips_cbr_addr;
- u32 __maybe_unused cfg;
-
- switch (current_cpu_type()) {
+++ /dev/null
-From b95b30e50aed225d26e20737873ae2404941901c Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 20 Jun 2024 17:26:44 +0200
-Subject: [PATCH 3/4] mips: bmips: setup: make CBR address configurable
-
-Add support to provide CBR address from DT to handle broken
-SoC/Bootloader that doesn't correctly init it. This permits to use the
-RAC flush even in these condition.
-
-To provide a CBR address from DT, the property "brcm,bmips-cbr-reg"
-needs to be set in the "cpus" node. On DT init, this property presence
-will be checked and will set the bmips_cbr_addr value accordingly. Also
-bmips_rac_flush_disable will be set to false as RAC flush can be
-correctly supported.
-
-The CBR address from DT will overwrite the cached one and the
-one set in the CBR register will be ignored.
-
-Also the DT CBR address is validated on being outside DRAM window.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Acked-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
----
- arch/mips/bcm47xx/setup.c | 6 +++++-
- arch/mips/bcm63xx/setup.c | 6 +++++-
- arch/mips/bmips/setup.c | 30 ++++++++++++++++++++++++++++--
- 3 files changed, 38 insertions(+), 4 deletions(-)
-
---- a/arch/mips/bcm47xx/setup.c
-+++ b/arch/mips/bcm47xx/setup.c
-@@ -46,7 +46,11 @@
- #include <bcm47xx.h>
- #include <bcm47xx_board.h>
-
--/* CBR addr doesn't change and we can cache it */
-+/*
-+ * CBR addr doesn't change and we can cache it.
-+ * For broken SoC/Bootloader CBR addr might also be provided via DT
-+ * with "brcm,bmips-cbr-reg" in the "cpus" node.
-+ */
- void __iomem *bmips_cbr_addr __read_mostly;
-
- union bcm47xx_bus bcm47xx_bus;
---- a/arch/mips/bcm63xx/setup.c
-+++ b/arch/mips/bcm63xx/setup.c
-@@ -23,7 +23,11 @@
- #include <bcm63xx_io.h>
- #include <bcm63xx_gpio.h>
-
--/* CBR addr doesn't change and we can cache it */
-+/*
-+ * CBR addr doesn't change and we can cache it.
-+ * For broken SoC/Bootloader CBR addr might also be provided via DT
-+ * with "brcm,bmips-cbr-reg" in the "cpus" node.
-+ */
- void __iomem *bmips_cbr_addr __read_mostly;
-
- void bcm63xx_machine_halt(void)
---- a/arch/mips/bmips/setup.c
-+++ b/arch/mips/bmips/setup.c
-@@ -34,7 +34,11 @@
- #define REG_BCM6328_OTP ((void __iomem *)CKSEG1ADDR(0x1000062c))
- #define BCM6328_TP1_DISABLED BIT(9)
-
--/* CBR addr doesn't change and we can cache it */
-+/*
-+ * CBR addr doesn't change and we can cache it.
-+ * For broken SoC/Bootloader CBR addr might also be provided via DT
-+ * with "brcm,bmips-cbr-reg" in the "cpus" node.
-+ */
- void __iomem *bmips_cbr_addr __read_mostly;
-
- extern bool bmips_rac_flush_disable;
-@@ -208,13 +212,35 @@ void __init plat_mem_setup(void)
- void __init device_tree_init(void)
- {
- struct device_node *np;
-+ u32 addr;
-
- unflatten_and_copy_device_tree();
-
- /* Disable SMP boot unless both CPUs are listed in DT and !disabled */
- np = of_find_node_by_name(NULL, "cpus");
-- if (np && of_get_available_child_count(np) <= 1)
-+ if (!np)
-+ return;
-+
-+ if (of_get_available_child_count(np) <= 1)
- bmips_smp_enabled = 0;
-+
-+ /* Check if DT provide a CBR address */
-+ if (of_property_read_u32(np, "brcm,bmips-cbr-reg", &addr))
-+ goto exit;
-+
-+ /* Make sure CBR address is outside DRAM window */
-+ if (addr >= (u32)memblock_start_of_DRAM() &&
-+ addr < (u32)memblock_end_of_DRAM()) {
-+ WARN(1, "DT CBR %x inside DRAM window. Ignoring DT CBR.\n",
-+ addr);
-+ goto exit;
-+ }
-+
-+ bmips_cbr_addr = (void __iomem *)addr;
-+ /* Since CBR is provided by DT, enable RAC flush */
-+ bmips_rac_flush_disable = false;
-+
-+exit:
- of_node_put(np);
- }
-
+++ /dev/null
-From 04f38d1a4db017f17e82442727b91ce03dd72759 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20Cabanelas?= <dgcbueu@gmail.com>
-Date: Thu, 20 Jun 2024 17:26:45 +0200
-Subject: [PATCH 4/4] mips: bmips: enable RAC on BMIPS4350
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The data RAC is left disabled by the bootloader in some SoCs, at least in
-the core it boots from.
-Enabling this feature increases the performance up to +30% depending on the
-task.
-
-Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
-Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
-[ rework code and reduce code duplication ]
-Acked-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
----
- arch/mips/kernel/smp-bmips.c | 18 ++++++++++++++++++
- 1 file changed, 18 insertions(+)
-
---- a/arch/mips/kernel/smp-bmips.c
-+++ b/arch/mips/kernel/smp-bmips.c
-@@ -592,6 +592,7 @@ asmlinkage void __weak plat_wired_tlb_se
- void bmips_cpu_setup(void)
- {
- void __iomem __maybe_unused *cbr = bmips_cbr_addr;
-+ u32 __maybe_unused rac_addr;
- u32 __maybe_unused cfg;
-
- switch (current_cpu_type()) {
-@@ -620,6 +621,23 @@ void bmips_cpu_setup(void)
- __raw_readl(cbr + BMIPS_RAC_ADDRESS_RANGE);
- break;
-
-+ case CPU_BMIPS4350:
-+ rac_addr = BMIPS_RAC_CONFIG_1;
-+
-+ if (!(read_c0_brcm_cmt_local() & (1 << 31)))
-+ rac_addr = BMIPS_RAC_CONFIG;
-+
-+ /* Enable data RAC */
-+ cfg = __raw_readl(cbr + rac_addr);
-+ __raw_writel(cfg | 0xf, cbr + rac_addr);
-+ __raw_readl(cbr + rac_addr);
-+
-+ /* Flush stale data out of the readahead cache */
-+ cfg = __raw_readl(cbr + BMIPS_RAC_CONFIG);
-+ __raw_writel(cfg | 0x100, cbr + BMIPS_RAC_CONFIG);
-+ __raw_readl(cbr + BMIPS_RAC_CONFIG);
-+ break;
-+
- case CPU_BMIPS4380:
- /* CBG workaround for early BMIPS4380 CPUs */
- switch (read_c0_prid()) {
+++ /dev/null
-From 8e7daa85641c9559c113f6b217bdc923397de77c Mon Sep 17 00:00:00 2001
-From: William Zhang <william.zhang@broadcom.com>
-Date: Thu, 22 Feb 2024 19:47:58 -0800
-Subject: [PATCH] mtd: rawnand: brcmnand: Support write protection setting from
- dts
-
-The write protection feature is controlled by the module parameter wp_on
-with default set to enabled. But not all the board use this feature
-especially in BCMBCA broadband board. And module parameter is not
-sufficient as different board can have different option. Add a device
-tree property and allow this feature to be configured through the board
-dts on per board basis.
-
-Signed-off-by: William Zhang <william.zhang@broadcom.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Reviewed-by: Kamal Dasu <kamal.dasu@broadcom.com>
-Reviewed-by: David Regan <dregan@broadcom.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/20240223034758.13753-14-william.zhang@broadcom.com
----
- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
---- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
-+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
-@@ -3194,6 +3194,10 @@ int brcmnand_probe(struct platform_devic
- /* Disable XOR addressing */
- brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
-
-+ /* Check if the board connects the WP pin */
-+ if (of_property_read_bool(dn, "brcm,wp-not-connected"))
-+ wp_on = 0;
-+
- if (ctrl->features & BRCMNAND_HAS_WP) {
- /* Permanently disable write protection */
- if (wp_on == 2)
+++ /dev/null
-From 25d88bfd35bac3196eafa666e3b05033b46ffa21 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 19 Dec 2023 02:32:00 +0000
-Subject: [PATCH 1/8] dt-bindings: mtd: add basic bindings for UBI
-
-Add basic bindings for UBI devices and volumes.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Richard Weinberger <richard@nod.at>
----
- .../bindings/mtd/partitions/linux,ubi.yaml | 65 +++++++++++++++++++
- .../bindings/mtd/partitions/ubi-volume.yaml | 35 ++++++++++
- 2 files changed, 100 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
- create mode 100644 Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
-
---- /dev/null
-+++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
-@@ -0,0 +1,65 @@
-+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
-+%YAML 1.2
-+---
-+$id: http://devicetree.org/schemas/mtd/partitions/linux,ubi.yaml#
-+$schema: http://devicetree.org/meta-schemas/core.yaml#
-+
-+title: Unsorted Block Images
-+
-+description: |
-+ UBI ("Unsorted Block Images") is a volume management system for raw
-+ flash devices which manages multiple logical volumes on a single
-+ physical flash device and spreads the I/O load (i.e wear-leveling)
-+ across the whole flash chip.
-+
-+maintainers:
-+ - Daniel Golle <daniel@makrotopia.org>
-+
-+allOf:
-+ - $ref: partition.yaml#
-+
-+properties:
-+ compatible:
-+ const: linux,ubi
-+
-+ volumes:
-+ type: object
-+ description: UBI Volumes
-+
-+ patternProperties:
-+ "^ubi-volume-.*$":
-+ $ref: /schemas/mtd/partitions/ubi-volume.yaml#
-+
-+ unevaluatedProperties: false
-+
-+required:
-+ - compatible
-+
-+unevaluatedProperties: false
-+
-+examples:
-+ - |
-+ partitions {
-+ compatible = "fixed-partitions";
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ partition@0 {
-+ reg = <0x0 0x100000>;
-+ label = "bootloader";
-+ read-only;
-+ };
-+
-+ partition@100000 {
-+ reg = <0x100000 0x1ff00000>;
-+ label = "ubi";
-+ compatible = "linux,ubi";
-+
-+ volumes {
-+ ubi-volume-caldata {
-+ volid = <2>;
-+ volname = "rf";
-+ };
-+ };
-+ };
-+ };
---- /dev/null
-+++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
-@@ -0,0 +1,35 @@
-+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
-+%YAML 1.2
-+---
-+$id: http://devicetree.org/schemas/mtd/partitions/ubi-volume.yaml#
-+$schema: http://devicetree.org/meta-schemas/core.yaml#
-+
-+title: UBI volume
-+
-+description: |
-+ This binding describes a single UBI volume. Volumes can be matches either
-+ by their ID or their name, or both.
-+
-+maintainers:
-+ - Daniel Golle <daniel@makrotopia.org>
-+
-+properties:
-+ volid:
-+ $ref: /schemas/types.yaml#/definitions/uint32
-+ description:
-+ Match UBI volume ID
-+
-+ volname:
-+ $ref: /schemas/types.yaml#/definitions/string
-+ description:
-+ Match UBI volume ID
-+
-+anyOf:
-+ - required:
-+ - volid
-+
-+ - required:
-+ - volname
-+
-+# This is a generic file other binding inherit from and extend
-+additionalProperties: true
+++ /dev/null
-From 95b113222b5164ac0887eb5c514ff3970a0136f0 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 19 Dec 2023 02:32:11 +0000
-Subject: [PATCH 2/8] dt-bindings: mtd: ubi-volume: allow UBI volumes to
- provide NVMEM
-
-UBI volumes may be used to contain NVMEM bits, typically device MAC
-addresses or wireless radio calibration data.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Richard Weinberger <richard@nod.at>
----
- .../devicetree/bindings/mtd/partitions/linux,ubi.yaml | 10 ++++++++++
- .../devicetree/bindings/mtd/partitions/ubi-volume.yaml | 5 +++++
- 2 files changed, 15 insertions(+)
-
---- a/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
-+++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
-@@ -59,6 +59,16 @@ examples:
- ubi-volume-caldata {
- volid = <2>;
- volname = "rf";
-+
-+ nvmem-layout {
-+ compatible = "fixed-layout";
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ eeprom@0 {
-+ reg = <0x0 0x1000>;
-+ };
-+ };
- };
- };
- };
---- a/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
-+++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
-@@ -24,6 +24,11 @@ properties:
- description:
- Match UBI volume ID
-
-+ nvmem-layout:
-+ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
-+ description:
-+ This container may reference an NVMEM layout parser.
-+
- anyOf:
- - required:
- - volid
+++ /dev/null
-From 2bba1cdcfcd2907d0696cc0139f1bd078d36ee81 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 19 Dec 2023 02:32:35 +0000
-Subject: [PATCH 3/8] mtd: ubi: block: use notifier to create ubiblock from
- parameter
-
-Use UBI_VOLUME_ADDED notification to create ubiblock device specified
-on kernel cmdline or module parameter.
-This makes thing more simple and has the advantage that ubiblock devices
-on volumes which are not present at the time the ubi module is probed
-will still be created.
-
-Suggested-by: Zhihao Cheng <chengzhihao1@huawei.com>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Richard Weinberger <richard@nod.at>
----
- drivers/mtd/ubi/block.c | 136 ++++++++++++++++++++--------------------
- drivers/mtd/ubi/kapi.c | 54 +++++++++++-----
- drivers/mtd/ubi/ubi.h | 1 +
- 3 files changed, 106 insertions(+), 85 deletions(-)
-
---- a/drivers/mtd/ubi/block.c
-+++ b/drivers/mtd/ubi/block.c
-@@ -65,10 +65,10 @@ struct ubiblock_pdu {
- };
-
- /* Numbers of elements set in the @ubiblock_param array */
--static int ubiblock_devs __initdata;
-+static int ubiblock_devs;
-
- /* MTD devices specification parameters */
--static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata;
-+static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES];
-
- struct ubiblock {
- struct ubi_volume_desc *desc;
-@@ -532,6 +532,70 @@ static int ubiblock_resize(struct ubi_vo
- return 0;
- }
-
-+static bool
-+match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id)
-+{
-+ int err, len, cur_ubi_num, cur_vol_id;
-+
-+ if (ubi_num == -1) {
-+ /* No ubi num, name must be a vol device path */
-+ err = ubi_get_num_by_path(name, &cur_ubi_num, &cur_vol_id);
-+ if (err || vi->ubi_num != cur_ubi_num || vi->vol_id != cur_vol_id)
-+ return false;
-+
-+ return true;
-+ }
-+
-+ if (vol_id == -1) {
-+ /* Got ubi_num, but no vol_id, name must be volume name */
-+ if (vi->ubi_num != ubi_num)
-+ return false;
-+
-+ len = strnlen(name, UBI_VOL_NAME_MAX + 1);
-+ if (len < 1 || vi->name_len != len)
-+ return false;
-+
-+ if (strcmp(name, vi->name))
-+ return false;
-+
-+ return true;
-+ }
-+
-+ if (vi->ubi_num != ubi_num)
-+ return false;
-+
-+ if (vi->vol_id != vol_id)
-+ return false;
-+
-+ return true;
-+}
-+
-+static void
-+ubiblock_create_from_param(struct ubi_volume_info *vi)
-+{
-+ int i, ret = 0;
-+ struct ubiblock_param *p;
-+
-+ /*
-+ * Iterate over ubiblock cmdline parameters. If a parameter matches the
-+ * newly added volume create the ubiblock device for it.
-+ */
-+ for (i = 0; i < ubiblock_devs; i++) {
-+ p = &ubiblock_param[i];
-+
-+ if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id))
-+ continue;
-+
-+ ret = ubiblock_create(vi);
-+ if (ret) {
-+ pr_err(
-+ "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n",
-+ vi->name, p->ubi_num, p->vol_id, ret);
-+ }
-+ break;
-+ }
-+}
-+
- static int ubiblock_notify(struct notifier_block *nb,
- unsigned long notification_type, void *ns_ptr)
- {
-@@ -539,10 +603,7 @@ static int ubiblock_notify(struct notifi
-
- switch (notification_type) {
- case UBI_VOLUME_ADDED:
-- /*
-- * We want to enforce explicit block device creation for
-- * volumes, so when a volume is added we do nothing.
-- */
-+ ubiblock_create_from_param(&nt->vi);
- break;
- case UBI_VOLUME_REMOVED:
- ubiblock_remove(&nt->vi);
-@@ -568,56 +629,6 @@ static struct notifier_block ubiblock_no
- .notifier_call = ubiblock_notify,
- };
-
--static struct ubi_volume_desc * __init
--open_volume_desc(const char *name, int ubi_num, int vol_id)
--{
-- if (ubi_num == -1)
-- /* No ubi num, name must be a vol device path */
-- return ubi_open_volume_path(name, UBI_READONLY);
-- else if (vol_id == -1)
-- /* No vol_id, must be vol_name */
-- return ubi_open_volume_nm(ubi_num, name, UBI_READONLY);
-- else
-- return ubi_open_volume(ubi_num, vol_id, UBI_READONLY);
--}
--
--static void __init ubiblock_create_from_param(void)
--{
-- int i, ret = 0;
-- struct ubiblock_param *p;
-- struct ubi_volume_desc *desc;
-- struct ubi_volume_info vi;
--
-- /*
-- * If there is an error creating one of the ubiblocks, continue on to
-- * create the following ubiblocks. This helps in a circumstance where
-- * the kernel command-line specifies multiple block devices and some
-- * may be broken, but we still want the working ones to come up.
-- */
-- for (i = 0; i < ubiblock_devs; i++) {
-- p = &ubiblock_param[i];
--
-- desc = open_volume_desc(p->name, p->ubi_num, p->vol_id);
-- if (IS_ERR(desc)) {
-- pr_err(
-- "UBI: block: can't open volume on ubi%d_%d, err=%ld\n",
-- p->ubi_num, p->vol_id, PTR_ERR(desc));
-- continue;
-- }
--
-- ubi_get_volume_info(desc, &vi);
-- ubi_close_volume(desc);
--
-- ret = ubiblock_create(&vi);
-- if (ret) {
-- pr_err(
-- "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n",
-- vi.name, p->ubi_num, p->vol_id, ret);
-- continue;
-- }
-- }
--}
--
- static void ubiblock_remove_all(void)
- {
- struct ubiblock *next;
-@@ -643,18 +654,7 @@ int __init ubiblock_init(void)
- if (ubiblock_major < 0)
- return ubiblock_major;
-
-- /*
-- * Attach block devices from 'block=' module param.
-- * Even if one block device in the param list fails to come up,
-- * still allow the module to load and leave any others up.
-- */
-- ubiblock_create_from_param();
--
-- /*
-- * Block devices are only created upon user requests, so we ignore
-- * existing volumes.
-- */
-- ret = ubi_register_volume_notifier(&ubiblock_notifier, 1);
-+ ret = ubi_register_volume_notifier(&ubiblock_notifier, 0);
- if (ret)
- goto err_unreg;
- return 0;
---- a/drivers/mtd/ubi/kapi.c
-+++ b/drivers/mtd/ubi/kapi.c
-@@ -280,6 +280,41 @@ struct ubi_volume_desc *ubi_open_volume_
- EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
-
- /**
-+ * ubi_get_num_by_path - get UBI device and volume number from device path
-+ * @pathname: volume character device node path
-+ * @ubi_num: pointer to UBI device number to be set
-+ * @vol_id: pointer to UBI volume ID to be set
-+ *
-+ * Returns 0 on success and sets ubi_num and vol_id, returns error otherwise.
-+ */
-+int ubi_get_num_by_path(const char *pathname, int *ubi_num, int *vol_id)
-+{
-+ int error;
-+ struct path path;
-+ struct kstat stat;
-+
-+ error = kern_path(pathname, LOOKUP_FOLLOW, &path);
-+ if (error)
-+ return error;
-+
-+ error = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
-+ path_put(&path);
-+ if (error)
-+ return error;
-+
-+ if (!S_ISCHR(stat.mode))
-+ return -EINVAL;
-+
-+ *ubi_num = ubi_major2num(MAJOR(stat.rdev));
-+ *vol_id = MINOR(stat.rdev) - 1;
-+
-+ if (*vol_id < 0 || *ubi_num < 0)
-+ return -ENODEV;
-+
-+ return 0;
-+}
-+
-+/**
- * ubi_open_volume_path - open UBI volume by its character device node path.
- * @pathname: volume character device node path
- * @mode: open mode
-@@ -290,32 +325,17 @@ EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
- struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode)
- {
- int error, ubi_num, vol_id;
-- struct path path;
-- struct kstat stat;
-
- dbg_gen("open volume %s, mode %d", pathname, mode);
-
- if (!pathname || !*pathname)
- return ERR_PTR(-EINVAL);
-
-- error = kern_path(pathname, LOOKUP_FOLLOW, &path);
-- if (error)
-- return ERR_PTR(error);
--
-- error = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
-- path_put(&path);
-+ error = ubi_get_num_by_path(pathname, &ubi_num, &vol_id);
- if (error)
- return ERR_PTR(error);
-
-- if (!S_ISCHR(stat.mode))
-- return ERR_PTR(-EINVAL);
--
-- ubi_num = ubi_major2num(MAJOR(stat.rdev));
-- vol_id = MINOR(stat.rdev) - 1;
--
-- if (vol_id >= 0 && ubi_num >= 0)
-- return ubi_open_volume(ubi_num, vol_id, mode);
-- return ERR_PTR(-ENODEV);
-+ return ubi_open_volume(ubi_num, vol_id, mode);
- }
- EXPORT_SYMBOL_GPL(ubi_open_volume_path);
-
---- a/drivers/mtd/ubi/ubi.h
-+++ b/drivers/mtd/ubi/ubi.h
-@@ -956,6 +956,7 @@ void ubi_free_internal_volumes(struct ub
- void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di);
- void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol,
- struct ubi_volume_info *vi);
-+int ubi_get_num_by_path(const char *pathname, int *ubi_num, int *vol_id);
- /* scan.c */
- int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
- int pnum, const struct ubi_vid_hdr *vid_hdr);
+++ /dev/null
-From 6e331888643887ce85657527bc03f97d46235e71 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 19 Dec 2023 02:33:14 +0000
-Subject: [PATCH 4/8] mtd: ubi: attach from device tree
-
-Introduce device tree compatible 'linux,ubi' and attach compatible MTD
-devices using the MTD add notifier. This is needed for a UBI device to
-be available early at boot (and not only after late_initcall), so
-volumes on them can be used eg. as NVMEM providers for other drivers.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Richard Weinberger <richard@nod.at>
----
- drivers/mtd/ubi/build.c | 135 ++++++++++++++++++++++++++++------------
- 1 file changed, 96 insertions(+), 39 deletions(-)
-
---- a/drivers/mtd/ubi/build.c
-+++ b/drivers/mtd/ubi/build.c
-@@ -27,6 +27,7 @@
- #include <linux/log2.h>
- #include <linux/kthread.h>
- #include <linux/kernel.h>
-+#include <linux/of.h>
- #include <linux/slab.h>
- #include <linux/major.h>
- #include "ubi.h"
-@@ -1214,43 +1215,43 @@ static struct mtd_info * __init open_mtd
- return mtd;
- }
-
--static int __init ubi_init(void)
-+static void ubi_notify_add(struct mtd_info *mtd)
- {
-- int err, i, k;
-+ struct device_node *np = mtd_get_of_node(mtd);
-+ int err;
-
-- /* Ensure that EC and VID headers have correct size */
-- BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64);
-- BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64);
-+ if (!of_device_is_compatible(np, "linux,ubi"))
-+ return;
-
-- if (mtd_devs > UBI_MAX_DEVICES) {
-- pr_err("UBI error: too many MTD devices, maximum is %d\n",
-- UBI_MAX_DEVICES);
-- return -EINVAL;
-- }
-+ /*
-+ * we are already holding &mtd_table_mutex, but still need
-+ * to bump refcount
-+ */
-+ err = __get_mtd_device(mtd);
-+ if (err)
-+ return;
-
-- /* Create base sysfs directory and sysfs files */
-- err = class_register(&ubi_class);
-+ /* called while holding mtd_table_mutex */
-+ mutex_lock_nested(&ubi_devices_mutex, SINGLE_DEPTH_NESTING);
-+ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0, false);
-+ mutex_unlock(&ubi_devices_mutex);
- if (err < 0)
-- return err;
--
-- err = misc_register(&ubi_ctrl_cdev);
-- if (err) {
-- pr_err("UBI error: cannot register device\n");
-- goto out;
-- }
-+ __put_mtd_device(mtd);
-+}
-
-- ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab",
-- sizeof(struct ubi_wl_entry),
-- 0, 0, NULL);
-- if (!ubi_wl_entry_slab) {
-- err = -ENOMEM;
-- goto out_dev_unreg;
-- }
-+static void ubi_notify_remove(struct mtd_info *mtd)
-+{
-+ /* do nothing for now */
-+}
-
-- err = ubi_debugfs_init();
-- if (err)
-- goto out_slab;
-+static struct mtd_notifier ubi_mtd_notifier = {
-+ .add = ubi_notify_add,
-+ .remove = ubi_notify_remove,
-+};
-
-+static int __init ubi_init_attach(void)
-+{
-+ int err, i, k;
-
- /* Attach MTD devices */
- for (i = 0; i < mtd_devs; i++) {
-@@ -1298,25 +1299,79 @@ static int __init ubi_init(void)
- }
- }
-
-+ return 0;
-+
-+out_detach:
-+ for (k = 0; k < i; k++)
-+ if (ubi_devices[k]) {
-+ mutex_lock(&ubi_devices_mutex);
-+ ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
-+ mutex_unlock(&ubi_devices_mutex);
-+ }
-+ return err;
-+}
-+#ifndef CONFIG_MTD_UBI_MODULE
-+late_initcall(ubi_init_attach);
-+#endif
-+
-+static int __init ubi_init(void)
-+{
-+ int err;
-+
-+ /* Ensure that EC and VID headers have correct size */
-+ BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64);
-+ BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64);
-+
-+ if (mtd_devs > UBI_MAX_DEVICES) {
-+ pr_err("UBI error: too many MTD devices, maximum is %d\n",
-+ UBI_MAX_DEVICES);
-+ return -EINVAL;
-+ }
-+
-+ /* Create base sysfs directory and sysfs files */
-+ err = class_register(&ubi_class);
-+ if (err < 0)
-+ return err;
-+
-+ err = misc_register(&ubi_ctrl_cdev);
-+ if (err) {
-+ pr_err("UBI error: cannot register device\n");
-+ goto out;
-+ }
-+
-+ ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab",
-+ sizeof(struct ubi_wl_entry),
-+ 0, 0, NULL);
-+ if (!ubi_wl_entry_slab) {
-+ err = -ENOMEM;
-+ goto out_dev_unreg;
-+ }
-+
-+ err = ubi_debugfs_init();
-+ if (err)
-+ goto out_slab;
-+
- err = ubiblock_init();
- if (err) {
- pr_err("UBI error: block: cannot initialize, error %d\n", err);
-
- /* See comment above re-ubi_is_module(). */
- if (ubi_is_module())
-- goto out_detach;
-+ goto out_slab;
-+ }
-+
-+ register_mtd_user(&ubi_mtd_notifier);
-+
-+ if (ubi_is_module()) {
-+ err = ubi_init_attach();
-+ if (err)
-+ goto out_mtd_notifier;
- }
-
- return 0;
-
--out_detach:
-- for (k = 0; k < i; k++)
-- if (ubi_devices[k]) {
-- mutex_lock(&ubi_devices_mutex);
-- ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
-- mutex_unlock(&ubi_devices_mutex);
-- }
-- ubi_debugfs_exit();
-+out_mtd_notifier:
-+ unregister_mtd_user(&ubi_mtd_notifier);
- out_slab:
- kmem_cache_destroy(ubi_wl_entry_slab);
- out_dev_unreg:
-@@ -1326,13 +1381,15 @@ out:
- pr_err("UBI error: cannot initialize UBI, error %d\n", err);
- return err;
- }
--late_initcall(ubi_init);
-+device_initcall(ubi_init);
-+
-
- static void __exit ubi_exit(void)
- {
- int i;
-
- ubiblock_exit();
-+ unregister_mtd_user(&ubi_mtd_notifier);
-
- for (i = 0; i < UBI_MAX_DEVICES; i++)
- if (ubi_devices[i]) {
+++ /dev/null
-From 924731fbed3247e3b82b8ab17db587ee28c2e781 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 19 Dec 2023 02:33:24 +0000
-Subject: [PATCH 5/8] mtd: ubi: introduce pre-removal notification for UBI
- volumes
-
-Introduce a new notification type UBI_VOLUME_SHUTDOWN to inform users
-that a volume is just about to be removed.
-This is needed because users (such as the NVMEM subsystem) expect that
-at the time their removal function is called, the parenting device is
-still available (for removal of sysfs nodes, for example, in case of
-NVMEM which otherwise WARNs on volume removal).
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Richard Weinberger <richard@nod.at>
----
- drivers/mtd/ubi/build.c | 19 ++++++++++++++-----
- drivers/mtd/ubi/kapi.c | 2 +-
- drivers/mtd/ubi/ubi.h | 2 ++
- drivers/mtd/ubi/vmt.c | 17 +++++++++++++++--
- include/linux/mtd/ubi.h | 2 ++
- 5 files changed, 34 insertions(+), 8 deletions(-)
-
---- a/drivers/mtd/ubi/build.c
-+++ b/drivers/mtd/ubi/build.c
-@@ -91,7 +91,7 @@ static struct ubi_device *ubi_devices[UB
- /* Serializes UBI devices creations and removals */
- DEFINE_MUTEX(ubi_devices_mutex);
-
--/* Protects @ubi_devices and @ubi->ref_count */
-+/* Protects @ubi_devices, @ubi->ref_count and @ubi->is_dead */
- static DEFINE_SPINLOCK(ubi_devices_lock);
-
- /* "Show" method for files in '/<sysfs>/class/ubi/' */
-@@ -259,6 +259,9 @@ struct ubi_device *ubi_get_device(int ub
-
- spin_lock(&ubi_devices_lock);
- ubi = ubi_devices[ubi_num];
-+ if (ubi && ubi->is_dead)
-+ ubi = NULL;
-+
- if (ubi) {
- ubi_assert(ubi->ref_count >= 0);
- ubi->ref_count += 1;
-@@ -296,7 +299,7 @@ struct ubi_device *ubi_get_by_major(int
- spin_lock(&ubi_devices_lock);
- for (i = 0; i < UBI_MAX_DEVICES; i++) {
- ubi = ubi_devices[i];
-- if (ubi && MAJOR(ubi->cdev.dev) == major) {
-+ if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) {
- ubi_assert(ubi->ref_count >= 0);
- ubi->ref_count += 1;
- get_device(&ubi->dev);
-@@ -325,7 +328,7 @@ int ubi_major2num(int major)
- for (i = 0; i < UBI_MAX_DEVICES; i++) {
- struct ubi_device *ubi = ubi_devices[i];
-
-- if (ubi && MAJOR(ubi->cdev.dev) == major) {
-+ if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) {
- ubi_num = ubi->ubi_num;
- break;
- }
-@@ -512,7 +515,7 @@ static void ubi_free_volumes_from(struct
- int i;
-
- for (i = from; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
-- if (!ubi->volumes[i])
-+ if (!ubi->volumes[i] || ubi->volumes[i]->is_dead)
- continue;
- ubi_eba_replace_table(ubi->volumes[i], NULL);
- ubi_fastmap_destroy_checkmap(ubi->volumes[i]);
-@@ -1094,7 +1097,6 @@ int ubi_detach_mtd_dev(int ubi_num, int
- return -EINVAL;
-
- spin_lock(&ubi_devices_lock);
-- put_device(&ubi->dev);
- ubi->ref_count -= 1;
- if (ubi->ref_count) {
- if (!anyway) {
-@@ -1105,6 +1107,13 @@ int ubi_detach_mtd_dev(int ubi_num, int
- ubi_err(ubi, "%s reference count %d, destroy anyway",
- ubi->ubi_name, ubi->ref_count);
- }
-+ ubi->is_dead = true;
-+ spin_unlock(&ubi_devices_lock);
-+
-+ ubi_notify_all(ubi, UBI_VOLUME_SHUTDOWN, NULL);
-+
-+ spin_lock(&ubi_devices_lock);
-+ put_device(&ubi->dev);
- ubi_devices[ubi_num] = NULL;
- spin_unlock(&ubi_devices_lock);
-
---- a/drivers/mtd/ubi/kapi.c
-+++ b/drivers/mtd/ubi/kapi.c
-@@ -152,7 +152,7 @@ struct ubi_volume_desc *ubi_open_volume(
-
- spin_lock(&ubi->volumes_lock);
- vol = ubi->volumes[vol_id];
-- if (!vol)
-+ if (!vol || vol->is_dead)
- goto out_unlock;
-
- err = -EBUSY;
---- a/drivers/mtd/ubi/ubi.h
-+++ b/drivers/mtd/ubi/ubi.h
-@@ -345,6 +345,7 @@ struct ubi_volume {
- int writers;
- int exclusive;
- int metaonly;
-+ bool is_dead;
-
- int reserved_pebs;
- int vol_type;
-@@ -564,6 +565,7 @@ struct ubi_device {
- spinlock_t volumes_lock;
- int ref_count;
- int image_seq;
-+ bool is_dead;
-
- int rsvd_pebs;
- int avail_pebs;
---- a/drivers/mtd/ubi/vmt.c
-+++ b/drivers/mtd/ubi/vmt.c
-@@ -59,7 +59,7 @@ static ssize_t vol_attribute_show(struct
- struct ubi_device *ubi = vol->ubi;
-
- spin_lock(&ubi->volumes_lock);
-- if (!ubi->volumes[vol->vol_id]) {
-+ if (!ubi->volumes[vol->vol_id] || ubi->volumes[vol->vol_id]->is_dead) {
- spin_unlock(&ubi->volumes_lock);
- return -ENODEV;
- }
-@@ -189,7 +189,7 @@ int ubi_create_volume(struct ubi_device
-
- /* Ensure that the name is unique */
- for (i = 0; i < ubi->vtbl_slots; i++)
-- if (ubi->volumes[i] &&
-+ if (ubi->volumes[i] && !ubi->volumes[i]->is_dead &&
- ubi->volumes[i]->name_len == req->name_len &&
- !strcmp(ubi->volumes[i]->name, req->name)) {
- ubi_err(ubi, "volume \"%s\" exists (ID %d)",
-@@ -352,6 +352,19 @@ int ubi_remove_volume(struct ubi_volume_
- err = -EBUSY;
- goto out_unlock;
- }
-+
-+ /*
-+ * Mark volume as dead at this point to prevent that anyone
-+ * can take a reference to the volume from now on.
-+ * This is necessary as we have to release the spinlock before
-+ * calling ubi_volume_notify.
-+ */
-+ vol->is_dead = true;
-+ spin_unlock(&ubi->volumes_lock);
-+
-+ ubi_volume_notify(ubi, vol, UBI_VOLUME_SHUTDOWN);
-+
-+ spin_lock(&ubi->volumes_lock);
- ubi->volumes[vol_id] = NULL;
- spin_unlock(&ubi->volumes_lock);
-
---- a/include/linux/mtd/ubi.h
-+++ b/include/linux/mtd/ubi.h
-@@ -192,6 +192,7 @@ struct ubi_device_info {
- * or a volume was removed)
- * @UBI_VOLUME_RESIZED: a volume has been re-sized
- * @UBI_VOLUME_RENAMED: a volume has been re-named
-+ * @UBI_VOLUME_SHUTDOWN: a volume is going to removed, shutdown users
- * @UBI_VOLUME_UPDATED: data has been written to a volume
- *
- * These constants define which type of event has happened when a volume
-@@ -202,6 +203,7 @@ enum {
- UBI_VOLUME_REMOVED,
- UBI_VOLUME_RESIZED,
- UBI_VOLUME_RENAMED,
-+ UBI_VOLUME_SHUTDOWN,
- UBI_VOLUME_UPDATED,
- };
-
+++ /dev/null
-From 1c54542170819e36baa43c17ca55bb3d7da89a53 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 19 Dec 2023 02:33:38 +0000
-Subject: [PATCH 6/8] mtd: ubi: populate ubi volume fwnode
-
-Look for the 'volumes' subnode of an MTD partition attached to a UBI
-device and attach matching child nodes to UBI volumes.
-This allows UBI volumes to be referenced in device tree, e.g. for use
-as NVMEM providers.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Richard Weinberger <richard@nod.at>
----
- drivers/mtd/ubi/vmt.c | 27 +++++++++++++++++++++++++++
- 1 file changed, 27 insertions(+)
-
---- a/drivers/mtd/ubi/vmt.c
-+++ b/drivers/mtd/ubi/vmt.c
-@@ -124,6 +124,31 @@ static void vol_release(struct device *d
- kfree(vol);
- }
-
-+static struct fwnode_handle *find_volume_fwnode(struct ubi_volume *vol)
-+{
-+ struct fwnode_handle *fw_vols, *fw_vol;
-+ const char *volname;
-+ u32 volid;
-+
-+ fw_vols = device_get_named_child_node(vol->dev.parent->parent, "volumes");
-+ if (!fw_vols)
-+ return NULL;
-+
-+ fwnode_for_each_child_node(fw_vols, fw_vol) {
-+ if (!fwnode_property_read_string(fw_vol, "volname", &volname) &&
-+ strncmp(volname, vol->name, vol->name_len))
-+ continue;
-+
-+ if (!fwnode_property_read_u32(fw_vol, "volid", &volid) &&
-+ vol->vol_id != volid)
-+ continue;
-+
-+ return fw_vol;
-+ }
-+
-+ return NULL;
-+}
-+
- /**
- * ubi_create_volume - create volume.
- * @ubi: UBI device description object
-@@ -223,6 +248,7 @@ int ubi_create_volume(struct ubi_device
- vol->name_len = req->name_len;
- memcpy(vol->name, req->name, vol->name_len);
- vol->ubi = ubi;
-+ device_set_node(&vol->dev, find_volume_fwnode(vol));
-
- /*
- * Finish all pending erases because there may be some LEBs belonging
-@@ -605,6 +631,7 @@ int ubi_add_volume(struct ubi_device *ub
- vol->dev.class = &ubi_class;
- vol->dev.groups = volume_dev_groups;
- dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
-+ device_set_node(&vol->dev, find_volume_fwnode(vol));
- err = device_register(&vol->dev);
- if (err) {
- cdev_del(&vol->cdev);
+++ /dev/null
-From 15fc7dc926c91c871f6c0305b2938dbdeb14203b Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 19 Dec 2023 02:33:48 +0000
-Subject: [PATCH 7/8] mtd: ubi: provide NVMEM layer over UBI volumes
-
-In an ideal world we would like UBI to be used where ever possible on a
-NAND chip. And with UBI support in ARM Trusted Firmware and U-Boot it
-is possible to achieve an (almost-)all-UBI flash layout. Hence the need
-for a way to also use UBI volumes to store board-level constants, such
-as MAC addresses and calibration data of wireless interfaces.
-
-Add UBI volume NVMEM driver module exposing UBI volumes as NVMEM
-providers. Allow UBI devices to have a "volumes" firmware subnode with
-volumes which may be compatible with "nvmem-cells".
-Access to UBI volumes via the NVMEM interface at this point is
-read-only, and it is slow, opening and closing the UBI volume for each
-access due to limitations of the NVMEM provider API.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Richard Weinberger <richard@nod.at>
----
- drivers/mtd/ubi/Kconfig | 12 +++
- drivers/mtd/ubi/Makefile | 1 +
- drivers/mtd/ubi/nvmem.c | 188 +++++++++++++++++++++++++++++++++++++++
- 3 files changed, 201 insertions(+)
- create mode 100644 drivers/mtd/ubi/nvmem.c
-
---- a/drivers/mtd/ubi/Kconfig
-+++ b/drivers/mtd/ubi/Kconfig
-@@ -104,4 +104,16 @@ config MTD_UBI_BLOCK
-
- If in doubt, say "N".
-
-+config MTD_UBI_NVMEM
-+ tristate "UBI virtual NVMEM"
-+ default n
-+ depends on NVMEM
-+ help
-+ This option enabled an additional driver exposing UBI volumes as NVMEM
-+ providers, intended for platforms where UBI is part of the firmware
-+ specification and used to store also e.g. MAC addresses or board-
-+ specific Wi-Fi calibration data.
-+
-+ If in doubt, say "N".
-+
- endif # MTD_UBI
---- a/drivers/mtd/ubi/Makefile
-+++ b/drivers/mtd/ubi/Makefile
-@@ -7,3 +7,4 @@ ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap
- ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o
-
- obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
-+obj-$(CONFIG_MTD_UBI_NVMEM) += nvmem.o
---- /dev/null
-+++ b/drivers/mtd/ubi/nvmem.c
-@@ -0,0 +1,188 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+/*
-+ * Copyright (c) 2023 Daniel Golle <daniel@makrotopia.org>
-+ */
-+
-+/* UBI NVMEM provider */
-+#include "ubi.h"
-+#include <linux/nvmem-provider.h>
-+#include <asm/div64.h>
-+
-+/* List of all NVMEM devices */
-+static LIST_HEAD(nvmem_devices);
-+static DEFINE_MUTEX(devices_mutex);
-+
-+struct ubi_nvmem {
-+ struct nvmem_device *nvmem;
-+ int ubi_num;
-+ int vol_id;
-+ int usable_leb_size;
-+ struct list_head list;
-+};
-+
-+static int ubi_nvmem_reg_read(void *priv, unsigned int from,
-+ void *val, size_t bytes)
-+{
-+ int err = 0, lnum = from, offs, bytes_left = bytes, to_read;
-+ struct ubi_nvmem *unv = priv;
-+ struct ubi_volume_desc *desc;
-+
-+ desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY);
-+ if (IS_ERR(desc))
-+ return PTR_ERR(desc);
-+
-+ offs = do_div(lnum, unv->usable_leb_size);
-+ while (bytes_left) {
-+ to_read = unv->usable_leb_size - offs;
-+
-+ if (to_read > bytes_left)
-+ to_read = bytes_left;
-+
-+ err = ubi_read(desc, lnum, val, offs, to_read);
-+ if (err)
-+ break;
-+
-+ lnum += 1;
-+ offs = 0;
-+ bytes_left -= to_read;
-+ val += to_read;
-+ }
-+ ubi_close_volume(desc);
-+
-+ if (err)
-+ return err;
-+
-+ return bytes_left == 0 ? 0 : -EIO;
-+}
-+
-+static int ubi_nvmem_add(struct ubi_volume_info *vi)
-+{
-+ struct device_node *np = dev_of_node(vi->dev);
-+ struct nvmem_config config = {};
-+ struct ubi_nvmem *unv;
-+ int ret;
-+
-+ if (!np)
-+ return 0;
-+
-+ if (!of_get_child_by_name(np, "nvmem-layout"))
-+ return 0;
-+
-+ if (WARN_ON_ONCE(vi->usable_leb_size <= 0) ||
-+ WARN_ON_ONCE(vi->size <= 0))
-+ return -EINVAL;
-+
-+ unv = kzalloc(sizeof(struct ubi_nvmem), GFP_KERNEL);
-+ if (!unv)
-+ return -ENOMEM;
-+
-+ config.id = NVMEM_DEVID_NONE;
-+ config.dev = vi->dev;
-+ config.name = dev_name(vi->dev);
-+ config.owner = THIS_MODULE;
-+ config.priv = unv;
-+ config.reg_read = ubi_nvmem_reg_read;
-+ config.size = vi->usable_leb_size * vi->size;
-+ config.word_size = 1;
-+ config.stride = 1;
-+ config.read_only = true;
-+ config.root_only = true;
-+ config.ignore_wp = true;
-+ config.of_node = np;
-+
-+ unv->ubi_num = vi->ubi_num;
-+ unv->vol_id = vi->vol_id;
-+ unv->usable_leb_size = vi->usable_leb_size;
-+ unv->nvmem = nvmem_register(&config);
-+ if (IS_ERR(unv->nvmem)) {
-+ ret = dev_err_probe(vi->dev, PTR_ERR(unv->nvmem),
-+ "Failed to register NVMEM device\n");
-+ kfree(unv);
-+ return ret;
-+ }
-+
-+ mutex_lock(&devices_mutex);
-+ list_add_tail(&unv->list, &nvmem_devices);
-+ mutex_unlock(&devices_mutex);
-+
-+ return 0;
-+}
-+
-+static void ubi_nvmem_remove(struct ubi_volume_info *vi)
-+{
-+ struct ubi_nvmem *unv_c, *unv = NULL;
-+
-+ mutex_lock(&devices_mutex);
-+ list_for_each_entry(unv_c, &nvmem_devices, list)
-+ if (unv_c->ubi_num == vi->ubi_num && unv_c->vol_id == vi->vol_id) {
-+ unv = unv_c;
-+ break;
-+ }
-+
-+ if (!unv) {
-+ mutex_unlock(&devices_mutex);
-+ return;
-+ }
-+
-+ list_del(&unv->list);
-+ mutex_unlock(&devices_mutex);
-+ nvmem_unregister(unv->nvmem);
-+ kfree(unv);
-+}
-+
-+/**
-+ * nvmem_notify - UBI notification handler.
-+ * @nb: registered notifier block
-+ * @l: notification type
-+ * @ns_ptr: pointer to the &struct ubi_notification object
-+ */
-+static int nvmem_notify(struct notifier_block *nb, unsigned long l,
-+ void *ns_ptr)
-+{
-+ struct ubi_notification *nt = ns_ptr;
-+
-+ switch (l) {
-+ case UBI_VOLUME_RESIZED:
-+ ubi_nvmem_remove(&nt->vi);
-+ fallthrough;
-+ case UBI_VOLUME_ADDED:
-+ ubi_nvmem_add(&nt->vi);
-+ break;
-+ case UBI_VOLUME_SHUTDOWN:
-+ ubi_nvmem_remove(&nt->vi);
-+ break;
-+ default:
-+ break;
-+ }
-+ return NOTIFY_OK;
-+}
-+
-+static struct notifier_block nvmem_notifier = {
-+ .notifier_call = nvmem_notify,
-+};
-+
-+static int __init ubi_nvmem_init(void)
-+{
-+ return ubi_register_volume_notifier(&nvmem_notifier, 0);
-+}
-+
-+static void __exit ubi_nvmem_exit(void)
-+{
-+ struct ubi_nvmem *unv, *tmp;
-+
-+ mutex_lock(&devices_mutex);
-+ list_for_each_entry_safe(unv, tmp, &nvmem_devices, list) {
-+ nvmem_unregister(unv->nvmem);
-+ list_del(&unv->list);
-+ kfree(unv);
-+ }
-+ mutex_unlock(&devices_mutex);
-+
-+ ubi_unregister_volume_notifier(&nvmem_notifier);
-+}
-+
-+module_init(ubi_nvmem_init);
-+module_exit(ubi_nvmem_exit);
-+MODULE_DESCRIPTION("NVMEM layer over UBI volumes");
-+MODULE_AUTHOR("Daniel Golle");
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From 04231c61dcd51db0f12061e49bb761b197109f2f Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Thu, 29 Feb 2024 03:47:24 +0000
-Subject: [PATCH 8/8] mtd: ubi: fix NVMEM over UBI volumes on 32-bit systems
-
-A compiler warning related to sizeof(int) != 8 when calling do_div()
-is triggered when building on 32-bit platforms.
-Address this by using integer types having a well-defined size.
-
-Fixes: 3ce485803da1 ("mtd: ubi: provide NVMEM layer over UBI volumes")
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Zhihao Cheng <chengzhihao1@huawei.com>
-Tested-by: Randy Dunlap <rdunlap@infradead.org>
-Signed-off-by: Richard Weinberger <richard@nod.at>
----
- drivers/mtd/ubi/nvmem.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
---- a/drivers/mtd/ubi/nvmem.c
-+++ b/drivers/mtd/ubi/nvmem.c
-@@ -23,9 +23,12 @@ struct ubi_nvmem {
- static int ubi_nvmem_reg_read(void *priv, unsigned int from,
- void *val, size_t bytes)
- {
-- int err = 0, lnum = from, offs, bytes_left = bytes, to_read;
-+ size_t to_read, bytes_left = bytes;
- struct ubi_nvmem *unv = priv;
- struct ubi_volume_desc *desc;
-+ uint32_t offs;
-+ uint64_t lnum = from;
-+ int err = 0;
-
- desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY);
- if (IS_ERR(desc))
+++ /dev/null
-From 49c8e854869d673df8452f24dfa8989cd0f615a8 Mon Sep 17 00:00:00 2001
-From: Martin Kurbanov <mmkurbanov@salutedevices.com>
-Date: Mon, 2 Oct 2023 17:04:58 +0300
-Subject: [PATCH] mtd: spinand: add support for FORESEE F35SQA002G
-
-Add support for FORESEE F35SQA002G SPI NAND.
-Datasheet:
- https://www.longsys.com/uploads/LM-00006FORESEEF35SQA002GDatasheet_1650183701.pdf
-
-Signed-off-by: Martin Kurbanov <mmkurbanov@salutedevices.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/20231002140458.147605-1-mmkurbanov@salutedevices.com
----
- drivers/mtd/nand/spi/Makefile | 2 +-
- drivers/mtd/nand/spi/core.c | 1 +
- drivers/mtd/nand/spi/foresee.c | 95 ++++++++++++++++++++++++++++++++++
- include/linux/mtd/spinand.h | 1 +
- 4 files changed, 98 insertions(+), 1 deletion(-)
- create mode 100644 drivers/mtd/nand/spi/foresee.c
-
---- a/drivers/mtd/nand/spi/Makefile
-+++ b/drivers/mtd/nand/spi/Makefile
-@@ -1,4 +1,4 @@
- # SPDX-License-Identifier: GPL-2.0
--spinand-objs := core.o alliancememory.o ato.o esmt.o gigadevice.o macronix.o
-+spinand-objs := core.o alliancememory.o ato.o esmt.o foresee.o gigadevice.o macronix.o
- spinand-objs += micron.o paragon.o toshiba.o winbond.o xtx.o
- obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
---- a/drivers/mtd/nand/spi/core.c
-+++ b/drivers/mtd/nand/spi/core.c
-@@ -940,6 +940,7 @@ static const struct spinand_manufacturer
- &alliancememory_spinand_manufacturer,
- &ato_spinand_manufacturer,
- &esmt_c8_spinand_manufacturer,
-+ &foresee_spinand_manufacturer,
- &gigadevice_spinand_manufacturer,
- ¯onix_spinand_manufacturer,
- µn_spinand_manufacturer,
---- /dev/null
-+++ b/drivers/mtd/nand/spi/foresee.c
-@@ -0,0 +1,95 @@
-+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
-+/*
-+ * Copyright (c) 2023, SberDevices. All Rights Reserved.
-+ *
-+ * Author: Martin Kurbanov <mmkurbanov@salutedevices.com>
-+ */
-+
-+#include <linux/device.h>
-+#include <linux/kernel.h>
-+#include <linux/mtd/spinand.h>
-+
-+#define SPINAND_MFR_FORESEE 0xCD
-+
-+static SPINAND_OP_VARIANTS(read_cache_variants,
-+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
-+ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
-+ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
-+ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
-+
-+static SPINAND_OP_VARIANTS(write_cache_variants,
-+ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
-+ SPINAND_PROG_LOAD(true, 0, NULL, 0));
-+
-+static SPINAND_OP_VARIANTS(update_cache_variants,
-+ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
-+ SPINAND_PROG_LOAD(false, 0, NULL, 0));
-+
-+static int f35sqa002g_ooblayout_ecc(struct mtd_info *mtd, int section,
-+ struct mtd_oob_region *region)
-+{
-+ return -ERANGE;
-+}
-+
-+static int f35sqa002g_ooblayout_free(struct mtd_info *mtd, int section,
-+ struct mtd_oob_region *region)
-+{
-+ if (section)
-+ return -ERANGE;
-+
-+ /* Reserve 2 bytes for the BBM. */
-+ region->offset = 2;
-+ region->length = 62;
-+
-+ return 0;
-+}
-+
-+static const struct mtd_ooblayout_ops f35sqa002g_ooblayout = {
-+ .ecc = f35sqa002g_ooblayout_ecc,
-+ .free = f35sqa002g_ooblayout_free,
-+};
-+
-+static int f35sqa002g_ecc_get_status(struct spinand_device *spinand, u8 status)
-+{
-+ struct nand_device *nand = spinand_to_nand(spinand);
-+
-+ switch (status & STATUS_ECC_MASK) {
-+ case STATUS_ECC_NO_BITFLIPS:
-+ return 0;
-+
-+ case STATUS_ECC_HAS_BITFLIPS:
-+ return nanddev_get_ecc_conf(nand)->strength;
-+
-+ default:
-+ break;
-+ }
-+
-+ /* More than 1-bit error was detected in one or more sectors and
-+ * cannot be corrected.
-+ */
-+ return -EBADMSG;
-+}
-+
-+static const struct spinand_info foresee_spinand_table[] = {
-+ SPINAND_INFO("F35SQA002G",
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x72, 0x72),
-+ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
-+ NAND_ECCREQ(1, 512),
-+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+ &write_cache_variants,
-+ &update_cache_variants),
-+ SPINAND_HAS_QE_BIT,
-+ SPINAND_ECCINFO(&f35sqa002g_ooblayout,
-+ f35sqa002g_ecc_get_status)),
-+};
-+
-+static const struct spinand_manufacturer_ops foresee_spinand_manuf_ops = {
-+};
-+
-+const struct spinand_manufacturer foresee_spinand_manufacturer = {
-+ .id = SPINAND_MFR_FORESEE,
-+ .name = "FORESEE",
-+ .chips = foresee_spinand_table,
-+ .nchips = ARRAY_SIZE(foresee_spinand_table),
-+ .ops = &foresee_spinand_manuf_ops,
-+};
---- a/include/linux/mtd/spinand.h
-+++ b/include/linux/mtd/spinand.h
-@@ -263,6 +263,7 @@ struct spinand_manufacturer {
- extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
- extern const struct spinand_manufacturer ato_spinand_manufacturer;
- extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
-+extern const struct spinand_manufacturer foresee_spinand_manufacturer;
- extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
- extern const struct spinand_manufacturer macronix_spinand_manufacturer;
- extern const struct spinand_manufacturer micron_spinand_manufacturer;
+++ /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) {
+++ /dev/null
-From e2a9fcb36e851adb5b25c4acea53a290fd48a636 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-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
-@@ -74,6 +74,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)
- {
-@@ -98,6 +110,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,
-@@ -160,6 +177,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 56364c910691f6d10ba88c964c9041b9ab777bd6 Mon Sep 17 00:00:00 2001
-From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
-Date: Mon, 25 Mar 2024 08:40:28 +0100
-Subject: [PATCH 1/4] net: Remove conditional threaded-NAPI wakeup based on
- task state.
-
-A NAPI thread is scheduled by first setting NAPI_STATE_SCHED bit. If
-successful (the bit was not yet set) then the NAPI_STATE_SCHED_THREADED
-is set but only if thread's state is not TASK_INTERRUPTIBLE (is
-TASK_RUNNING) followed by task wakeup.
-
-If the task is idle (TASK_INTERRUPTIBLE) then the
-NAPI_STATE_SCHED_THREADED bit is not set. The thread is no relying on
-the bit but always leaving the wait-loop after returning from schedule()
-because there must have been a wakeup.
-
-The smpboot-threads implementation for per-CPU threads requires an
-explicit condition and does not support "if we get out of schedule()
-then there must be something to do".
-
-Removing this optimisation simplifies the following integration.
-
-Set NAPI_STATE_SCHED_THREADED unconditionally on wakeup and rely on it
-in the wait path by removing the `woken' condition.
-
-Acked-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- net/core/dev.c | 14 ++------------
- 1 file changed, 2 insertions(+), 12 deletions(-)
-
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -4514,13 +4514,7 @@ static inline void ____napi_schedule(str
- */
- thread = READ_ONCE(napi->thread);
- if (thread) {
-- /* Avoid doing set_bit() if the thread is in
-- * INTERRUPTIBLE state, cause napi_thread_wait()
-- * makes sure to proceed with napi polling
-- * if the thread is explicitly woken from here.
-- */
-- if (READ_ONCE(thread->__state) != TASK_INTERRUPTIBLE)
-- set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
-+ set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
- wake_up_process(thread);
- return;
- }
-@@ -6676,8 +6670,6 @@ static int napi_poll(struct napi_struct
-
- static int napi_thread_wait(struct napi_struct *napi)
- {
-- bool woken = false;
--
- set_current_state(TASK_INTERRUPTIBLE);
-
- while (!kthread_should_stop()) {
-@@ -6686,15 +6678,13 @@ static int napi_thread_wait(struct napi_
- * Testing SCHED bit is not enough because SCHED bit might be
- * set by some other busy poll thread or by napi_disable().
- */
-- if (test_bit(NAPI_STATE_SCHED_THREADED, &napi->state) || woken) {
-+ if (test_bit(NAPI_STATE_SCHED_THREADED, &napi->state)) {
- WARN_ON(!list_empty(&napi->poll_list));
- __set_current_state(TASK_RUNNING);
- return 0;
- }
-
- schedule();
-- /* woken being true indicates this thread owns this napi. */
-- woken = true;
- set_current_state(TASK_INTERRUPTIBLE);
- }
- __set_current_state(TASK_RUNNING);
+++ /dev/null
-From dad6b97702639fba27a2bd3e986982ad6f0db3a7 Mon Sep 17 00:00:00 2001
-From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
-Date: Mon, 25 Mar 2024 08:40:29 +0100
-Subject: [PATCH 2/4] net: Allow to use SMP threads for backlog NAPI.
-
-Backlog NAPI is a per-CPU NAPI struct only (with no device behind it)
-used by drivers which don't do NAPI them self, RPS and parts of the
-stack which need to avoid recursive deadlocks while processing a packet.
-
-The non-NAPI driver use the CPU local backlog NAPI. If RPS is enabled
-then a flow for the skb is computed and based on the flow the skb can be
-enqueued on a remote CPU. Scheduling/ raising the softirq (for backlog's
-NAPI) on the remote CPU isn't trivial because the softirq is only
-scheduled on the local CPU and performed after the hardirq is done.
-In order to schedule a softirq on the remote CPU, an IPI is sent to the
-remote CPU which schedules the backlog-NAPI on the then local CPU.
-
-On PREEMPT_RT interrupts are force-threaded. The soft interrupts are
-raised within the interrupt thread and processed after the interrupt
-handler completed still within the context of the interrupt thread. The
-softirq is handled in the context where it originated.
-
-With force-threaded interrupts enabled, ksoftirqd is woken up if a
-softirq is raised from hardirq context. This is the case if it is raised
-from an IPI. Additionally there is a warning on PREEMPT_RT if the
-softirq is raised from the idle thread.
-This was done for two reasons:
-- With threaded interrupts the processing should happen in thread
- context (where it originated) and ksoftirqd is the only thread for
- this context if raised from hardirq. Using the currently running task
- instead would "punish" a random task.
-- Once ksoftirqd is active it consumes all further softirqs until it
- stops running. This changed recently and is no longer the case.
-
-Instead of keeping the backlog NAPI in ksoftirqd (in force-threaded/
-PREEMPT_RT setups) I am proposing NAPI-threads for backlog.
-The "proper" setup with threaded-NAPI is not doable because the threads
-are not pinned to an individual CPU and can be modified by the user.
-Additionally a dummy network device would have to be assigned. Also
-CPU-hotplug has to be considered if additional CPUs show up.
-All this can be probably done/ solved but the smpboot-threads already
-provide this infrastructure.
-
-Sending UDP packets over loopback expects that the packet is processed
-within the call. Delaying it by handing it over to the thread hurts
-performance. It is not beneficial to the outcome if the context switch
-happens immediately after enqueue or after a while to process a few
-packets in a batch.
-There is no need to always use the thread if the backlog NAPI is
-requested on the local CPU. This restores the loopback throuput. The
-performance drops mostly to the same value after enabling RPS on the
-loopback comparing the IPI and the tread result.
-
-Create NAPI-threads for backlog if request during boot. The thread runs
-the inner loop from napi_threaded_poll(), the wait part is different. It
-checks for NAPI_STATE_SCHED (the backlog NAPI can not be disabled).
-
-The NAPI threads for backlog are optional, it has to be enabled via the boot
-argument "thread_backlog_napi". It is mandatory for PREEMPT_RT to avoid the
-wakeup of ksoftirqd from the IPI.
-
-Acked-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- net/core/dev.c | 148 +++++++++++++++++++++++++++++++++++++------------
- 1 file changed, 113 insertions(+), 35 deletions(-)
-
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -78,6 +78,7 @@
- #include <linux/slab.h>
- #include <linux/sched.h>
- #include <linux/sched/mm.h>
-+#include <linux/smpboot.h>
- #include <linux/mutex.h>
- #include <linux/rwsem.h>
- #include <linux/string.h>
-@@ -217,6 +218,31 @@ static inline struct hlist_head *dev_ind
- return &net->dev_index_head[ifindex & (NETDEV_HASHENTRIES - 1)];
- }
-
-+#ifndef CONFIG_PREEMPT_RT
-+
-+static DEFINE_STATIC_KEY_FALSE(use_backlog_threads_key);
-+
-+static int __init setup_backlog_napi_threads(char *arg)
-+{
-+ static_branch_enable(&use_backlog_threads_key);
-+ return 0;
-+}
-+early_param("thread_backlog_napi", setup_backlog_napi_threads);
-+
-+static bool use_backlog_threads(void)
-+{
-+ return static_branch_unlikely(&use_backlog_threads_key);
-+}
-+
-+#else
-+
-+static bool use_backlog_threads(void)
-+{
-+ return true;
-+}
-+
-+#endif
-+
- static inline void rps_lock_irqsave(struct softnet_data *sd,
- unsigned long *flags)
- {
-@@ -4482,6 +4508,7 @@ EXPORT_SYMBOL(__dev_direct_xmit);
- /*************************************************************************
- * Receiver routines
- *************************************************************************/
-+static DEFINE_PER_CPU(struct task_struct *, backlog_napi);
-
- int netdev_max_backlog __read_mostly = 1000;
- EXPORT_SYMBOL(netdev_max_backlog);
-@@ -4514,12 +4541,16 @@ static inline void ____napi_schedule(str
- */
- thread = READ_ONCE(napi->thread);
- if (thread) {
-+ if (use_backlog_threads() && thread == raw_cpu_read(backlog_napi))
-+ goto use_local_napi;
-+
- set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
- wake_up_process(thread);
- return;
- }
- }
-
-+use_local_napi:
- list_add_tail(&napi->poll_list, &sd->poll_list);
- WRITE_ONCE(napi->list_owner, smp_processor_id());
- /* If not called from net_rx_action()
-@@ -4765,6 +4796,11 @@ static void napi_schedule_rps(struct sof
-
- #ifdef CONFIG_RPS
- if (sd != mysd) {
-+ if (use_backlog_threads()) {
-+ __napi_schedule_irqoff(&sd->backlog);
-+ return;
-+ }
-+
- sd->rps_ipi_next = mysd->rps_ipi_list;
- mysd->rps_ipi_list = sd;
-
-@@ -5988,7 +6024,7 @@ static void net_rps_action_and_irq_enabl
- #ifdef CONFIG_RPS
- struct softnet_data *remsd = sd->rps_ipi_list;
-
-- if (remsd) {
-+ if (!use_backlog_threads() && remsd) {
- sd->rps_ipi_list = NULL;
-
- local_irq_enable();
-@@ -6003,7 +6039,7 @@ static void net_rps_action_and_irq_enabl
- static bool sd_has_rps_ipi_waiting(struct softnet_data *sd)
- {
- #ifdef CONFIG_RPS
-- return sd->rps_ipi_list != NULL;
-+ return !use_backlog_threads() && sd->rps_ipi_list;
- #else
- return false;
- #endif
-@@ -6047,7 +6083,7 @@ static int process_backlog(struct napi_s
- * We can use a plain write instead of clear_bit(),
- * and we dont need an smp_mb() memory barrier.
- */
-- napi->state = 0;
-+ napi->state &= NAPIF_STATE_THREADED;
- again = false;
- } else {
- skb_queue_splice_tail_init(&sd->input_pkt_queue,
-@@ -6713,43 +6749,48 @@ static void skb_defer_free_flush(struct
- }
- }
-
--static int napi_threaded_poll(void *data)
-+static void napi_threaded_poll_loop(struct napi_struct *napi)
- {
-- struct napi_struct *napi = data;
- struct softnet_data *sd;
-- void *have;
-+ unsigned long last_qs = jiffies;
-
-- while (!napi_thread_wait(napi)) {
-- unsigned long last_qs = jiffies;
-+ for (;;) {
-+ bool repoll = false;
-+ void *have;
-
-- for (;;) {
-- bool repoll = false;
-+ local_bh_disable();
-+ sd = this_cpu_ptr(&softnet_data);
-+ sd->in_napi_threaded_poll = true;
-
-- local_bh_disable();
-- sd = this_cpu_ptr(&softnet_data);
-- sd->in_napi_threaded_poll = true;
--
-- have = netpoll_poll_lock(napi);
-- __napi_poll(napi, &repoll);
-- netpoll_poll_unlock(have);
--
-- sd->in_napi_threaded_poll = false;
-- barrier();
--
-- if (sd_has_rps_ipi_waiting(sd)) {
-- local_irq_disable();
-- net_rps_action_and_irq_enable(sd);
-- }
-- skb_defer_free_flush(sd);
-- local_bh_enable();
-+ have = netpoll_poll_lock(napi);
-+ __napi_poll(napi, &repoll);
-+ netpoll_poll_unlock(have);
-+
-+ sd->in_napi_threaded_poll = false;
-+ barrier();
-+
-+ if (sd_has_rps_ipi_waiting(sd)) {
-+ local_irq_disable();
-+ net_rps_action_and_irq_enable(sd);
-+ }
-+ skb_defer_free_flush(sd);
-+ local_bh_enable();
-
-- if (!repoll)
-- break;
-+ if (!repoll)
-+ break;
-
-- rcu_softirq_qs_periodic(last_qs);
-- cond_resched();
-- }
-+ rcu_softirq_qs_periodic(last_qs);
-+ cond_resched();
- }
-+}
-+
-+static int napi_threaded_poll(void *data)
-+{
-+ struct napi_struct *napi = data;
-+
-+ while (!napi_thread_wait(napi))
-+ napi_threaded_poll_loop(napi);
-+
- return 0;
- }
-
-@@ -11334,7 +11375,7 @@ static int dev_cpu_dead(unsigned int old
-
- list_del_init(&napi->poll_list);
- if (napi->poll == process_backlog)
-- napi->state = 0;
-+ napi->state &= NAPIF_STATE_THREADED;
- else
- ____napi_schedule(sd, napi);
- }
-@@ -11342,12 +11383,14 @@ static int dev_cpu_dead(unsigned int old
- raise_softirq_irqoff(NET_TX_SOFTIRQ);
- local_irq_enable();
-
-+ if (!use_backlog_threads()) {
- #ifdef CONFIG_RPS
-- remsd = oldsd->rps_ipi_list;
-- oldsd->rps_ipi_list = NULL;
-+ remsd = oldsd->rps_ipi_list;
-+ oldsd->rps_ipi_list = NULL;
- #endif
-- /* send out pending IPI's on offline CPU */
-- net_rps_send_ipi(remsd);
-+ /* send out pending IPI's on offline CPU */
-+ net_rps_send_ipi(remsd);
-+ }
-
- /* Process offline CPU's input_pkt_queue */
- while ((skb = __skb_dequeue(&oldsd->process_queue))) {
-@@ -11610,6 +11653,38 @@ static struct pernet_operations __net_in
- *
- */
-
-+static int backlog_napi_should_run(unsigned int cpu)
-+{
-+ struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu);
-+ struct napi_struct *napi = &sd->backlog;
-+
-+ return test_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
-+}
-+
-+static void run_backlog_napi(unsigned int cpu)
-+{
-+ struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu);
-+
-+ napi_threaded_poll_loop(&sd->backlog);
-+}
-+
-+static void backlog_napi_setup(unsigned int cpu)
-+{
-+ struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu);
-+ struct napi_struct *napi = &sd->backlog;
-+
-+ napi->thread = this_cpu_read(backlog_napi);
-+ set_bit(NAPI_STATE_THREADED, &napi->state);
-+}
-+
-+static struct smp_hotplug_thread backlog_threads = {
-+ .store = &backlog_napi,
-+ .thread_should_run = backlog_napi_should_run,
-+ .thread_fn = run_backlog_napi,
-+ .thread_comm = "backlog_napi/%u",
-+ .setup = backlog_napi_setup,
-+};
-+
- /*
- * This is called single threaded during boot, so no need
- * to take the rtnl semaphore.
-@@ -11660,7 +11735,10 @@ static int __init net_dev_init(void)
- init_gro_hash(&sd->backlog);
- sd->backlog.poll = process_backlog;
- sd->backlog.weight = weight_p;
-+ INIT_LIST_HEAD(&sd->backlog.poll_list);
- }
-+ if (use_backlog_threads())
-+ smpboot_register_percpu_thread(&backlog_threads);
-
- dev_boot_phase = 0;
-
+++ /dev/null
-From 80d2eefcb4c84aa9018b2a997ab3a4c567bc821a Mon Sep 17 00:00:00 2001
-From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
-Date: Mon, 25 Mar 2024 08:40:30 +0100
-Subject: [PATCH 3/4] net: Use backlog-NAPI to clean up the defer_list.
-
-The defer_list is a per-CPU list which is used to free skbs outside of
-the socket lock and on the CPU on which they have been allocated.
-The list is processed during NAPI callbacks so ideally the list is
-cleaned up.
-Should the amount of skbs on the list exceed a certain water mark then
-the softirq is triggered remotely on the target CPU by invoking a remote
-function call. The raise of the softirqs via a remote function call
-leads to waking the ksoftirqd on PREEMPT_RT which is undesired.
-The backlog-NAPI threads already provide the infrastructure which can be
-utilized to perform the cleanup of the defer_list.
-
-The NAPI state is updated with the input_pkt_queue.lock acquired. It
-order not to break the state, it is needed to also wake the backlog-NAPI
-thread with the lock held. This requires to acquire the use the lock in
-rps_lock_irq*() if the backlog-NAPI threads are used even with RPS
-disabled.
-
-Move the logic of remotely starting softirqs to clean up the defer_list
-into kick_defer_list_purge(). Make sure a lock is held in
-rps_lock_irq*() if backlog-NAPI threads are used. Schedule backlog-NAPI
-for defer_list cleanup if backlog-NAPI is available.
-
-Acked-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- include/linux/netdevice.h | 1 +
- net/core/dev.c | 25 +++++++++++++++++++++----
- net/core/skbuff.c | 4 ++--
- 3 files changed, 24 insertions(+), 6 deletions(-)
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -3308,6 +3308,7 @@ static inline void dev_xmit_recursion_de
- __this_cpu_dec(softnet_data.xmit.recursion);
- }
-
-+void kick_defer_list_purge(struct softnet_data *sd, unsigned int cpu);
- void __netif_schedule(struct Qdisc *q);
- void netif_schedule_queue(struct netdev_queue *txq);
-
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -246,7 +246,7 @@ static bool use_backlog_threads(void)
- static inline void rps_lock_irqsave(struct softnet_data *sd,
- unsigned long *flags)
- {
-- if (IS_ENABLED(CONFIG_RPS))
-+ if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
- spin_lock_irqsave(&sd->input_pkt_queue.lock, *flags);
- else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
- local_irq_save(*flags);
-@@ -254,7 +254,7 @@ static inline void rps_lock_irqsave(stru
-
- static inline void rps_lock_irq_disable(struct softnet_data *sd)
- {
-- if (IS_ENABLED(CONFIG_RPS))
-+ if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
- spin_lock_irq(&sd->input_pkt_queue.lock);
- else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
- local_irq_disable();
-@@ -263,7 +263,7 @@ static inline void rps_lock_irq_disable(
- static inline void rps_unlock_irq_restore(struct softnet_data *sd,
- unsigned long *flags)
- {
-- if (IS_ENABLED(CONFIG_RPS))
-+ if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
- spin_unlock_irqrestore(&sd->input_pkt_queue.lock, *flags);
- else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
- local_irq_restore(*flags);
-@@ -271,7 +271,7 @@ static inline void rps_unlock_irq_restor
-
- static inline void rps_unlock_irq_enable(struct softnet_data *sd)
- {
-- if (IS_ENABLED(CONFIG_RPS))
-+ if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
- spin_unlock_irq(&sd->input_pkt_queue.lock);
- else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
- local_irq_enable();
-@@ -4815,6 +4815,23 @@ static void napi_schedule_rps(struct sof
- __napi_schedule_irqoff(&mysd->backlog);
- }
-
-+void kick_defer_list_purge(struct softnet_data *sd, unsigned int cpu)
-+{
-+ unsigned long flags;
-+
-+ if (use_backlog_threads()) {
-+ rps_lock_irqsave(sd, &flags);
-+
-+ if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state))
-+ __napi_schedule_irqoff(&sd->backlog);
-+
-+ rps_unlock_irq_restore(sd, &flags);
-+
-+ } else if (!cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) {
-+ smp_call_function_single_async(cpu, &sd->defer_csd);
-+ }
-+}
-+
- #ifdef CONFIG_NET_FLOW_LIMIT
- int netdev_flow_limit_table_len __read_mostly = (1 << 12);
- #endif
---- a/net/core/skbuff.c
-+++ b/net/core/skbuff.c
-@@ -6863,8 +6863,8 @@ nodefer: __kfree_skb(skb);
- /* Make sure to trigger NET_RX_SOFTIRQ on the remote CPU
- * if we are unlucky enough (this seems very unlikely).
- */
-- if (unlikely(kick) && !cmpxchg(&sd->defer_ipi_scheduled, 0, 1))
-- smp_call_function_single_async(cpu, &sd->defer_csd);
-+ if (unlikely(kick))
-+ kick_defer_list_purge(sd, cpu);
- }
-
- static void skb_splice_csum_page(struct sk_buff *skb, struct page *page,
+++ /dev/null
-From 765b11f8f4e20b7433e4ba4a3e9106a0d59501ed Mon Sep 17 00:00:00 2001
-From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
-Date: Mon, 25 Mar 2024 08:40:31 +0100
-Subject: [PATCH 4/4] net: Rename rps_lock to backlog_lock.
-
-The rps_lock.*() functions use the inner lock of a sk_buff_head for
-locking. This lock is used if RPS is enabled, otherwise the list is
-accessed lockless and disabling interrupts is enough for the
-synchronisation because it is only accessed CPU local. Not only the list
-is protected but also the NAPI state protected.
-With the addition of backlog threads, the lock is also needed because of
-the cross CPU access even without RPS. The clean up of the defer_list
-list is also done via backlog threads (if enabled).
-
-It has been suggested to rename the locking function since it is no
-longer just RPS.
-
-Rename the rps_lock*() functions to backlog_lock*().
-
-Suggested-by: Jakub Kicinski <kuba@kernel.org>
-Acked-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- net/core/dev.c | 34 +++++++++++++++++-----------------
- 1 file changed, 17 insertions(+), 17 deletions(-)
-
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -243,8 +243,8 @@ static bool use_backlog_threads(void)
-
- #endif
-
--static inline void rps_lock_irqsave(struct softnet_data *sd,
-- unsigned long *flags)
-+static inline void backlog_lock_irq_save(struct softnet_data *sd,
-+ unsigned long *flags)
- {
- if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
- spin_lock_irqsave(&sd->input_pkt_queue.lock, *flags);
-@@ -252,7 +252,7 @@ static inline void rps_lock_irqsave(stru
- local_irq_save(*flags);
- }
-
--static inline void rps_lock_irq_disable(struct softnet_data *sd)
-+static inline void backlog_lock_irq_disable(struct softnet_data *sd)
- {
- if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
- spin_lock_irq(&sd->input_pkt_queue.lock);
-@@ -260,8 +260,8 @@ static inline void rps_lock_irq_disable(
- local_irq_disable();
- }
-
--static inline void rps_unlock_irq_restore(struct softnet_data *sd,
-- unsigned long *flags)
-+static inline void backlog_unlock_irq_restore(struct softnet_data *sd,
-+ unsigned long *flags)
- {
- if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
- spin_unlock_irqrestore(&sd->input_pkt_queue.lock, *flags);
-@@ -269,7 +269,7 @@ static inline void rps_unlock_irq_restor
- local_irq_restore(*flags);
- }
-
--static inline void rps_unlock_irq_enable(struct softnet_data *sd)
-+static inline void backlog_unlock_irq_enable(struct softnet_data *sd)
- {
- if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
- spin_unlock_irq(&sd->input_pkt_queue.lock);
-@@ -4820,12 +4820,12 @@ void kick_defer_list_purge(struct softne
- unsigned long flags;
-
- if (use_backlog_threads()) {
-- rps_lock_irqsave(sd, &flags);
-+ backlog_lock_irq_save(sd, &flags);
-
- if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state))
- __napi_schedule_irqoff(&sd->backlog);
-
-- rps_unlock_irq_restore(sd, &flags);
-+ backlog_unlock_irq_restore(sd, &flags);
-
- } else if (!cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) {
- smp_call_function_single_async(cpu, &sd->defer_csd);
-@@ -4887,7 +4887,7 @@ static int enqueue_to_backlog(struct sk_
- reason = SKB_DROP_REASON_NOT_SPECIFIED;
- sd = &per_cpu(softnet_data, cpu);
-
-- rps_lock_irqsave(sd, &flags);
-+ backlog_lock_irq_save(sd, &flags);
- if (!netif_running(skb->dev))
- goto drop;
- qlen = skb_queue_len(&sd->input_pkt_queue);
-@@ -4896,7 +4896,7 @@ static int enqueue_to_backlog(struct sk_
- enqueue:
- __skb_queue_tail(&sd->input_pkt_queue, skb);
- input_queue_tail_incr_save(sd, qtail);
-- rps_unlock_irq_restore(sd, &flags);
-+ backlog_unlock_irq_restore(sd, &flags);
- return NET_RX_SUCCESS;
- }
-
-@@ -4911,7 +4911,7 @@ enqueue:
-
- drop:
- sd->dropped++;
-- rps_unlock_irq_restore(sd, &flags);
-+ backlog_unlock_irq_restore(sd, &flags);
-
- dev_core_stats_rx_dropped_inc(skb->dev);
- kfree_skb_reason(skb, reason);
-@@ -5942,7 +5942,7 @@ static void flush_backlog(struct work_st
- local_bh_disable();
- sd = this_cpu_ptr(&softnet_data);
-
-- rps_lock_irq_disable(sd);
-+ backlog_lock_irq_disable(sd);
- skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) {
- if (skb->dev->reg_state == NETREG_UNREGISTERING) {
- __skb_unlink(skb, &sd->input_pkt_queue);
-@@ -5950,7 +5950,7 @@ static void flush_backlog(struct work_st
- input_queue_head_incr(sd);
- }
- }
-- rps_unlock_irq_enable(sd);
-+ backlog_unlock_irq_enable(sd);
-
- skb_queue_walk_safe(&sd->process_queue, skb, tmp) {
- if (skb->dev->reg_state == NETREG_UNREGISTERING) {
-@@ -5968,14 +5968,14 @@ static bool flush_required(int cpu)
- struct softnet_data *sd = &per_cpu(softnet_data, cpu);
- bool do_flush;
-
-- rps_lock_irq_disable(sd);
-+ backlog_lock_irq_disable(sd);
-
- /* as insertion into process_queue happens with the rps lock held,
- * process_queue access may race only with dequeue
- */
- do_flush = !skb_queue_empty(&sd->input_pkt_queue) ||
- !skb_queue_empty_lockless(&sd->process_queue);
-- rps_unlock_irq_enable(sd);
-+ backlog_unlock_irq_enable(sd);
-
- return do_flush;
- #endif
-@@ -6090,7 +6090,7 @@ static int process_backlog(struct napi_s
-
- }
-
-- rps_lock_irq_disable(sd);
-+ backlog_lock_irq_disable(sd);
- if (skb_queue_empty(&sd->input_pkt_queue)) {
- /*
- * Inline a custom version of __napi_complete().
-@@ -6106,7 +6106,7 @@ static int process_backlog(struct napi_s
- skb_queue_splice_tail_init(&sd->input_pkt_queue,
- &sd->process_queue);
- }
-- rps_unlock_irq_enable(sd);
-+ backlog_unlock_irq_enable(sd);
- }
-
- return work;
+++ /dev/null
-From 6c06c88fa838fcc1b7e5380facd086f57fd9d1c4 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Sun, 4 Feb 2024 15:16:46 +0100
-Subject: [PATCH] net: mdio: add 2.5g and 5g related PMA speed constants
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add constants indicating 2.5g and 5g ability in the MMD PMA speed
-register.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://lore.kernel.org/r/98e15038-d96c-442f-93e4-410100d27866@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- include/uapi/linux/mdio.h | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/include/uapi/linux/mdio.h
-+++ b/include/uapi/linux/mdio.h
-@@ -138,6 +138,8 @@
- #define MDIO_PMA_SPEED_1000 0x0010 /* 1000M capable */
- #define MDIO_PMA_SPEED_100 0x0020 /* 100M capable */
- #define MDIO_PMA_SPEED_10 0x0040 /* 10M capable */
-+#define MDIO_PMA_SPEED_2_5G 0x2000 /* 2.5G capable */
-+#define MDIO_PMA_SPEED_5G 0x4000 /* 5G capable */
- #define MDIO_PCS_SPEED_10P2B 0x0002 /* 10PASS-TS/2BASE-TL capable */
- #define MDIO_PCS_SPEED_2_5G 0x0040 /* 2.5G capable */
- #define MDIO_PCS_SPEED_5G 0x0080 /* 5G capable */
+++ /dev/null
-From: Jakub Sitnicki <jakub@cloudflare.com>
-Date: Wed, 26 Jun 2024 19:51:26 +0200
-Subject: [PATCH] udp: Allow GSO transmit from devices with no checksum offload
-
-Today sending a UDP GSO packet from a TUN device results in an EIO error:
-
- import fcntl, os, struct
- from socket import *
-
- TUNSETIFF = 0x400454CA
- IFF_TUN = 0x0001
- IFF_NO_PI = 0x1000
- UDP_SEGMENT = 103
-
- tun_fd = os.open("/dev/net/tun", os.O_RDWR)
- ifr = struct.pack("16sH", b"tun0", IFF_TUN | IFF_NO_PI)
- fcntl.ioctl(tun_fd, TUNSETIFF, ifr)
-
- os.system("ip addr add 192.0.2.1/24 dev tun0")
- os.system("ip link set dev tun0 up")
-
- s = socket(AF_INET, SOCK_DGRAM)
- s.setsockopt(SOL_UDP, UDP_SEGMENT, 1200)
- s.sendto(b"x" * 3000, ("192.0.2.2", 9)) # EIO
-
-This is due to a check in the udp stack if the egress device offers
-checksum offload. While TUN/TAP devices, by default, don't advertise this
-capability because it requires support from the TUN/TAP reader.
-
-However, the GSO stack has a software fallback for checksum calculation,
-which we can use. This way we don't force UDP_SEGMENT users to handle the
-EIO error and implement a segmentation fallback.
-
-Lift the restriction so that UDP_SEGMENT can be used with any egress
-device. We also need to adjust the UDP GSO code to match the GSO stack
-expectation about ip_summed field, as set in commit 8d63bee643f1 ("net:
-avoid skb_warn_bad_offload false positives on UFO"). Otherwise we will hit
-the bad offload check.
-
-Users should, however, expect a potential performance impact when
-batch-sending packets with UDP_SEGMENT without checksum offload on the
-egress device. In such case the packet payload is read twice: first during
-the sendmsg syscall when copying data from user memory, and then in the GSO
-stack for checksum computation. This double memory read can be less
-efficient than a regular sendmsg where the checksum is calculated during
-the initial data copy from user memory.
-
-Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
-Reviewed-by: Willem de Bruijn <willemb@google.com>
-Link: https://patch.msgid.link/20240626-linux-udpgso-v2-1-422dfcbd6b48@cloudflare.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/net/ipv4/udp.c
-+++ b/net/ipv4/udp.c
-@@ -942,8 +942,7 @@ static int udp_send_skb(struct sk_buff *
- kfree_skb(skb);
- return -EINVAL;
- }
-- if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite ||
-- dst_xfrm(skb_dst(skb))) {
-+ if (is_udplite || dst_xfrm(skb_dst(skb))) {
- kfree_skb(skb);
- return -EIO;
- }
---- a/net/ipv4/udp_offload.c
-+++ b/net/ipv4/udp_offload.c
-@@ -384,6 +384,14 @@ struct sk_buff *__udp_gso_segment(struct
- else
- uh->check = gso_make_checksum(seg, ~check) ? : CSUM_MANGLED_0;
-
-+ /* On the TX path, CHECKSUM_NONE and CHECKSUM_UNNECESSARY have the same
-+ * meaning. However, check for bad offloads in the GSO stack expects the
-+ * latter, if the checksum was calculated in software. To vouch for the
-+ * segment skbs we actually need to set it on the gso_skb.
-+ */
-+ if (gso_skb->ip_summed == CHECKSUM_NONE)
-+ gso_skb->ip_summed = CHECKSUM_UNNECESSARY;
-+
- /* update refcount for the packet */
- if (copy_dtor) {
- int delta = sum_truesize - gso_skb->truesize;
---- a/net/ipv6/udp.c
-+++ b/net/ipv6/udp.c
-@@ -1258,8 +1258,7 @@ static int udp_v6_send_skb(struct sk_buf
- kfree_skb(skb);
- return -EINVAL;
- }
-- if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite ||
-- dst_xfrm(skb_dst(skb))) {
-+ if (is_udplite || dst_xfrm(skb_dst(skb))) {
- kfree_skb(skb);
- return -EIO;
- }
+++ /dev/null
-From: Jakub Sitnicki <jakub@cloudflare.com>
-Date: Thu, 8 Aug 2024 11:56:21 +0200
-Subject: [PATCH] net: Make USO depend on CSUM offload
-
-UDP segmentation offload inherently depends on checksum offload. It should
-not be possible to disable checksum offload while leaving USO enabled.
-Enforce this dependency in code.
-
-There is a single tx-udp-segmentation feature flag to indicate support for
-both IPv4/6, hence the devices wishing to support USO must offer checksum
-offload for both IP versions.
-
-Fixes: 10154dbded6d ("udp: Allow GSO transmit from devices with no checksum offload")
-Suggested-by: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
-Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
-Reviewed-by: Willem de Bruijn <willemb@google.com>
-Link: https://patch.msgid.link/20240808-udp-gso-egress-from-tunnel-v4-1-f5c5b4149ab9@cloudflare.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -9796,6 +9796,15 @@ static void netdev_sync_lower_features(s
- }
- }
-
-+static bool netdev_has_ip_or_hw_csum(netdev_features_t features)
-+{
-+ netdev_features_t ip_csum_mask = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
-+ bool ip_csum = (features & ip_csum_mask) == ip_csum_mask;
-+ bool hw_csum = features & NETIF_F_HW_CSUM;
-+
-+ return ip_csum || hw_csum;
-+}
-+
- static netdev_features_t netdev_fix_features(struct net_device *dev,
- netdev_features_t features)
- {
-@@ -9877,15 +9886,9 @@ static netdev_features_t netdev_fix_feat
- features &= ~NETIF_F_LRO;
- }
-
-- if (features & NETIF_F_HW_TLS_TX) {
-- bool ip_csum = (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) ==
-- (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
-- bool hw_csum = features & NETIF_F_HW_CSUM;
--
-- if (!ip_csum && !hw_csum) {
-- netdev_dbg(dev, "Dropping TLS TX HW offload feature since no CSUM feature.\n");
-- features &= ~NETIF_F_HW_TLS_TX;
-- }
-+ if ((features & NETIF_F_HW_TLS_TX) && !netdev_has_ip_or_hw_csum(features)) {
-+ netdev_dbg(dev, "Dropping TLS TX HW offload feature since no CSUM feature.\n");
-+ features &= ~NETIF_F_HW_TLS_TX;
- }
-
- if ((features & NETIF_F_HW_TLS_RX) && !(features & NETIF_F_RXCSUM)) {
-@@ -9893,6 +9896,11 @@ static netdev_features_t netdev_fix_feat
- features &= ~NETIF_F_HW_TLS_RX;
- }
-
-+ if ((features & NETIF_F_GSO_UDP_L4) && !netdev_has_ip_or_hw_csum(features)) {
-+ netdev_dbg(dev, "Dropping USO feature since no CSUM feature.\n");
-+ features &= ~NETIF_F_GSO_UDP_L4;
-+ }
-+
- return features;
- }
-
+++ /dev/null
-From: Jakub Sitnicki <jakub@cloudflare.com>
-Date: Thu, 8 Aug 2024 11:56:22 +0200
-Subject: [PATCH] udp: Fall back to software USO if IPv6 extension headers are
- present
-
-In commit 10154dbded6d ("udp: Allow GSO transmit from devices with no
-checksum offload") we have intentionally allowed UDP GSO packets marked
-CHECKSUM_NONE to pass to the GSO stack, so that they can be segmented and
-checksummed by a software fallback when the egress device lacks these
-features.
-
-What was not taken into consideration is that a CHECKSUM_NONE skb can be
-handed over to the GSO stack also when the egress device advertises the
-tx-udp-segmentation / NETIF_F_GSO_UDP_L4 feature.
-
-This will happen when there are IPv6 extension headers present, which we
-check for in __ip6_append_data(). Syzbot has discovered this scenario,
-producing a warning as below:
-
- ip6tnl0: caps=(0x00000006401d7869, 0x00000006401d7869)
- WARNING: CPU: 0 PID: 5112 at net/core/dev.c:3293 skb_warn_bad_offload+0x166/0x1a0 net/core/dev.c:3291
- Modules linked in:
- CPU: 0 PID: 5112 Comm: syz-executor391 Not tainted 6.10.0-rc7-syzkaller-01603-g80ab5445da62 #0
- Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 06/07/2024
- RIP: 0010:skb_warn_bad_offload+0x166/0x1a0 net/core/dev.c:3291
- [...]
- Call Trace:
- <TASK>
- __skb_gso_segment+0x3be/0x4c0 net/core/gso.c:127
- skb_gso_segment include/net/gso.h:83 [inline]
- validate_xmit_skb+0x585/0x1120 net/core/dev.c:3661
- __dev_queue_xmit+0x17a4/0x3e90 net/core/dev.c:4415
- neigh_output include/net/neighbour.h:542 [inline]
- ip6_finish_output2+0xffa/0x1680 net/ipv6/ip6_output.c:137
- ip6_finish_output+0x41e/0x810 net/ipv6/ip6_output.c:222
- ip6_send_skb+0x112/0x230 net/ipv6/ip6_output.c:1958
- udp_v6_send_skb+0xbf5/0x1870 net/ipv6/udp.c:1292
- udpv6_sendmsg+0x23b3/0x3270 net/ipv6/udp.c:1588
- sock_sendmsg_nosec net/socket.c:730 [inline]
- __sock_sendmsg+0xef/0x270 net/socket.c:745
- ____sys_sendmsg+0x525/0x7d0 net/socket.c:2585
- ___sys_sendmsg net/socket.c:2639 [inline]
- __sys_sendmmsg+0x3b2/0x740 net/socket.c:2725
- __do_sys_sendmmsg net/socket.c:2754 [inline]
- __se_sys_sendmmsg net/socket.c:2751 [inline]
- __x64_sys_sendmmsg+0xa0/0xb0 net/socket.c:2751
- do_syscall_x64 arch/x86/entry/common.c:52 [inline]
- do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83
- entry_SYSCALL_64_after_hwframe+0x77/0x7f
- [...]
- </TASK>
-
-We are hitting the bad offload warning because when an egress device is
-capable of handling segmentation offload requested by
-skb_shinfo(skb)->gso_type, the chain of gso_segment callbacks won't produce
-any segment skbs and return NULL. See the skb_gso_ok() branch in
-{__udp,tcp,sctp}_gso_segment helpers.
-
-To fix it, force a fallback to software USO when processing a packet with
-IPv6 extension headers, since we don't know if these can checksummed by
-all devices which offer USO.
-
-Fixes: 10154dbded6d ("udp: Allow GSO transmit from devices with no checksum offload")
-Reported-by: syzbot+e15b7e15b8a751a91d9a@syzkaller.appspotmail.com
-Closes: https://lore.kernel.org/all/000000000000e1609a061d5330ce@google.com/
-Reviewed-by: Willem de Bruijn <willemb@google.com>
-Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
-Link: https://patch.msgid.link/20240808-udp-gso-egress-from-tunnel-v4-2-f5c5b4149ab9@cloudflare.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/net/ipv4/udp_offload.c
-+++ b/net/ipv4/udp_offload.c
-@@ -283,6 +283,12 @@ struct sk_buff *__udp_gso_segment(struct
- !(skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST)))
- return ERR_PTR(-EINVAL);
-
-+ /* We don't know if egress device can segment and checksum the packet
-+ * when IPv6 extension headers are present. Fall back to software GSO.
-+ */
-+ if (gso_skb->ip_summed != CHECKSUM_PARTIAL)
-+ features &= ~(NETIF_F_GSO_UDP_L4 | NETIF_F_CSUM_MASK);
-+
- if (skb_gso_ok(gso_skb, features | NETIF_F_GSO_ROBUST)) {
- /* Packet is from an untrusted source, reset gso_segs. */
- skb_shinfo(gso_skb)->gso_segs = DIV_ROUND_UP(gso_skb->len - sizeof(*uh),
+++ /dev/null
-From: Breno Leitao <leitao@debian.org>
-Date: Wed, 28 Feb 2024 03:31:21 -0800
-Subject: [PATCH] net: get stats64 if device if driver is configured
-
-If the network driver is relying in the net core to do stats allocation,
-then we want to dev_get_tstats64() instead of netdev_stats_to_stats64(),
-since there are per-cpu stats that needs to be taken in consideration.
-
-This will also simplify the drivers in regard to statistics. Once the
-driver sets NETDEV_PCPU_STAT_TSTATS, it doesn't not need to allocate the
-stacks, neither it needs to set `.ndo_get_stats64 = dev_get_tstats64`
-for the generic stats collection function anymore.
-
-Signed-off-by: Breno Leitao <leitao@debian.org>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -10703,6 +10703,8 @@ struct rtnl_link_stats64 *dev_get_stats(
- ops->ndo_get_stats64(dev, storage);
- } else if (ops->ndo_get_stats) {
- netdev_stats_to_stats64(storage, ops->ndo_get_stats(dev));
-+ } else if (dev->pcpu_stat_type == NETDEV_PCPU_STAT_TSTATS) {
-+ dev_get_tstats64(dev, storage);
- } else {
- netdev_stats_to_stats64(storage, &dev->stats);
- }
+++ /dev/null
-From: Yunsheng Lin <linyunsheng@huawei.com>
-Date: Fri, 13 Oct 2023 14:48:21 +0800
-Subject: [PATCH] page_pool: fragment API support for 32-bit arch with 64-bit
- DMA
-
-Currently page_pool_alloc_frag() is not supported in 32-bit
-arch with 64-bit DMA because of the overlap issue between
-pp_frag_count and dma_addr_upper in 'struct page' for those
-arches, which seems to be quite common, see [1], which means
-driver may need to handle it when using fragment API.
-
-It is assumed that the combination of the above arch with an
-address space >16TB does not exist, as all those arches have
-64b equivalent, it seems logical to use the 64b version for a
-system with a large address space. It is also assumed that dma
-address is page aligned when we are dma mapping a page aligned
-buffer, see [2].
-
-That means we're storing 12 bits of 0 at the lower end for a
-dma address, we can reuse those bits for the above arches to
-support 32b+12b, which is 16TB of memory.
-
-If we make a wrong assumption, a warning is emitted so that
-user can report to us.
-
-1. https://lore.kernel.org/all/20211117075652.58299-1-linyunsheng@huawei.com/
-2. https://lore.kernel.org/all/20230818145145.4b357c89@kernel.org/
-
-Tested-by: Alexander Lobakin <aleksander.lobakin@intel.com>
-Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
-CC: Lorenzo Bianconi <lorenzo@kernel.org>
-CC: Alexander Duyck <alexander.duyck@gmail.com>
-CC: Liang Chen <liangchen.linux@gmail.com>
-CC: Guillaume Tucker <guillaume.tucker@collabora.com>
-CC: Matthew Wilcox <willy@infradead.org>
-CC: Linux-MM <linux-mm@kvack.org>
-Link: https://lore.kernel.org/r/20231013064827.61135-2-linyunsheng@huawei.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/include/linux/mm_types.h
-+++ b/include/linux/mm_types.h
-@@ -125,18 +125,7 @@ struct page {
- struct page_pool *pp;
- unsigned long _pp_mapping_pad;
- unsigned long dma_addr;
-- union {
-- /**
-- * dma_addr_upper: might require a 64-bit
-- * value on 32-bit architectures.
-- */
-- unsigned long dma_addr_upper;
-- /**
-- * For frag page support, not supported in
-- * 32-bit architectures with 64-bit DMA.
-- */
-- atomic_long_t pp_frag_count;
-- };
-+ atomic_long_t pp_frag_count;
- };
- struct { /* Tail pages of compound page */
- unsigned long compound_head; /* Bit zero is set */
---- a/include/net/page_pool/helpers.h
-+++ b/include/net/page_pool/helpers.h
-@@ -197,7 +197,7 @@ static inline void page_pool_recycle_dir
- page_pool_put_full_page(pool, page, true);
- }
-
--#define PAGE_POOL_DMA_USE_PP_FRAG_COUNT \
-+#define PAGE_POOL_32BIT_ARCH_WITH_64BIT_DMA \
- (sizeof(dma_addr_t) > sizeof(unsigned long))
-
- /**
-@@ -211,17 +211,25 @@ static inline dma_addr_t page_pool_get_d
- {
- dma_addr_t ret = page->dma_addr;
-
-- if (PAGE_POOL_DMA_USE_PP_FRAG_COUNT)
-- ret |= (dma_addr_t)page->dma_addr_upper << 16 << 16;
-+ if (PAGE_POOL_32BIT_ARCH_WITH_64BIT_DMA)
-+ ret <<= PAGE_SHIFT;
-
- return ret;
- }
-
--static inline void page_pool_set_dma_addr(struct page *page, dma_addr_t addr)
-+static inline bool page_pool_set_dma_addr(struct page *page, dma_addr_t addr)
- {
-+ if (PAGE_POOL_32BIT_ARCH_WITH_64BIT_DMA) {
-+ page->dma_addr = addr >> PAGE_SHIFT;
-+
-+ /* We assume page alignment to shave off bottom bits,
-+ * if this "compression" doesn't work we need to drop.
-+ */
-+ return addr != (dma_addr_t)page->dma_addr << PAGE_SHIFT;
-+ }
-+
- page->dma_addr = addr;
-- if (PAGE_POOL_DMA_USE_PP_FRAG_COUNT)
-- page->dma_addr_upper = upper_32_bits(addr);
-+ return false;
- }
-
- static inline bool page_pool_put(struct page_pool *pool)
---- a/net/core/page_pool.c
-+++ b/net/core/page_pool.c
-@@ -211,10 +211,6 @@ static int page_pool_init(struct page_po
- */
- }
-
-- if (PAGE_POOL_DMA_USE_PP_FRAG_COUNT &&
-- pool->p.flags & PP_FLAG_PAGE_FRAG)
-- return -EINVAL;
--
- #ifdef CONFIG_PAGE_POOL_STATS
- pool->recycle_stats = alloc_percpu(struct page_pool_recycle_stats);
- if (!pool->recycle_stats)
-@@ -363,12 +359,20 @@ static bool page_pool_dma_map(struct pag
- if (dma_mapping_error(pool->p.dev, dma))
- return false;
-
-- page_pool_set_dma_addr(page, dma);
-+ if (page_pool_set_dma_addr(page, dma))
-+ goto unmap_failed;
-
- if (pool->p.flags & PP_FLAG_DMA_SYNC_DEV)
- page_pool_dma_sync_for_device(pool, page, pool->p.max_len);
-
- return true;
-+
-+unmap_failed:
-+ WARN_ON_ONCE("unexpected DMA address, please report to netdev@");
-+ dma_unmap_page_attrs(pool->p.dev, dma,
-+ PAGE_SIZE << pool->p.order, pool->p.dma_dir,
-+ DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING);
-+ return false;
- }
-
- static void page_pool_set_pp_info(struct page_pool *pool,
+++ /dev/null
-From: Yunsheng Lin <linyunsheng@huawei.com>
-Date: Fri, 20 Oct 2023 17:59:48 +0800
-Subject: [PATCH] page_pool: unify frag_count handling in
- page_pool_is_last_frag()
-
-Currently when page_pool_create() is called with
-PP_FLAG_PAGE_FRAG flag, page_pool_alloc_pages() is only
-allowed to be called under the below constraints:
-1. page_pool_fragment_page() need to be called to setup
- page->pp_frag_count immediately.
-2. page_pool_defrag_page() often need to be called to drain
- the page->pp_frag_count when there is no more user will
- be holding on to that page.
-
-Those constraints exist in order to support a page to be
-split into multi fragments.
-
-And those constraints have some overhead because of the
-cache line dirtying/bouncing and atomic update.
-
-Those constraints are unavoidable for case when we need a
-page to be split into more than one fragment, but there is
-also case that we want to avoid the above constraints and
-their overhead when a page can't be split as it can only
-hold a fragment as requested by user, depending on different
-use cases:
-use case 1: allocate page without page splitting.
-use case 2: allocate page with page splitting.
-use case 3: allocate page with or without page splitting
- depending on the fragment size.
-
-Currently page pool only provide page_pool_alloc_pages() and
-page_pool_alloc_frag() API to enable the 1 & 2 separately,
-so we can not use a combination of 1 & 2 to enable 3, it is
-not possible yet because of the per page_pool flag
-PP_FLAG_PAGE_FRAG.
-
-So in order to allow allocating unsplit page without the
-overhead of split page while still allow allocating split
-page we need to remove the per page_pool flag in
-page_pool_is_last_frag(), as best as I can think of, it seems
-there are two methods as below:
-1. Add per page flag/bit to indicate a page is split or
- not, which means we might need to update that flag/bit
- everytime the page is recycled, dirtying the cache line
- of 'struct page' for use case 1.
-2. Unify the page->pp_frag_count handling for both split and
- unsplit page by assuming all pages in the page pool is split
- into a big fragment initially.
-
-As page pool already supports use case 1 without dirtying the
-cache line of 'struct page' whenever a page is recyclable, we
-need to support the above use case 3 with minimal overhead,
-especially not adding any noticeable overhead for use case 1,
-and we are already doing an optimization by not updating
-pp_frag_count in page_pool_defrag_page() for the last fragment
-user, this patch chooses to unify the pp_frag_count handling
-to support the above use case 3.
-
-There is no noticeable performance degradation and some
-justification for unifying the frag_count handling with this
-patch applied using a micro-benchmark testing in [1].
-
-1. https://lore.kernel.org/all/bf2591f8-7b3c-4480-bb2c-31dc9da1d6ac@huawei.com/
-
-Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
-CC: Lorenzo Bianconi <lorenzo@kernel.org>
-CC: Alexander Duyck <alexander.duyck@gmail.com>
-CC: Liang Chen <liangchen.linux@gmail.com>
-CC: Alexander Lobakin <aleksander.lobakin@intel.com>
-Link: https://lore.kernel.org/r/20231020095952.11055-2-linyunsheng@huawei.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/include/net/page_pool/helpers.h
-+++ b/include/net/page_pool/helpers.h
-@@ -115,28 +115,49 @@ static inline long page_pool_defrag_page
- long ret;
-
- /* If nr == pp_frag_count then we have cleared all remaining
-- * references to the page. No need to actually overwrite it, instead
-- * we can leave this to be overwritten by the calling function.
-+ * references to the page:
-+ * 1. 'n == 1': no need to actually overwrite it.
-+ * 2. 'n != 1': overwrite it with one, which is the rare case
-+ * for pp_frag_count draining.
- *
-- * The main advantage to doing this is that an atomic_read is
-- * generally a much cheaper operation than an atomic update,
-- * especially when dealing with a page that may be partitioned
-- * into only 2 or 3 pieces.
-+ * The main advantage to doing this is that not only we avoid a atomic
-+ * update, as an atomic_read is generally a much cheaper operation than
-+ * an atomic update, especially when dealing with a page that may be
-+ * partitioned into only 2 or 3 pieces; but also unify the pp_frag_count
-+ * handling by ensuring all pages have partitioned into only 1 piece
-+ * initially, and only overwrite it when the page is partitioned into
-+ * more than one piece.
- */
-- if (atomic_long_read(&page->pp_frag_count) == nr)
-+ if (atomic_long_read(&page->pp_frag_count) == nr) {
-+ /* As we have ensured nr is always one for constant case using
-+ * the BUILD_BUG_ON(), only need to handle the non-constant case
-+ * here for pp_frag_count draining, which is a rare case.
-+ */
-+ BUILD_BUG_ON(__builtin_constant_p(nr) && nr != 1);
-+ if (!__builtin_constant_p(nr))
-+ atomic_long_set(&page->pp_frag_count, 1);
-+
- return 0;
-+ }
-
- ret = atomic_long_sub_return(nr, &page->pp_frag_count);
- WARN_ON(ret < 0);
-+
-+ /* We are the last user here too, reset pp_frag_count back to 1 to
-+ * ensure all pages have been partitioned into 1 piece initially,
-+ * this should be the rare case when the last two fragment users call
-+ * page_pool_defrag_page() currently.
-+ */
-+ if (unlikely(!ret))
-+ atomic_long_set(&page->pp_frag_count, 1);
-+
- return ret;
- }
-
--static inline bool page_pool_is_last_frag(struct page_pool *pool,
-- struct page *page)
-+static inline bool page_pool_is_last_frag(struct page *page)
- {
-- /* If fragments aren't enabled or count is 0 we were the last user */
-- return !(pool->p.flags & PP_FLAG_PAGE_FRAG) ||
-- (page_pool_defrag_page(page, 1) == 0);
-+ /* If page_pool_defrag_page() returns 0, we were the last user */
-+ return page_pool_defrag_page(page, 1) == 0;
- }
-
- /**
-@@ -161,7 +182,7 @@ static inline void page_pool_put_page(st
- * allow registering MEM_TYPE_PAGE_POOL, but shield linker.
- */
- #ifdef CONFIG_PAGE_POOL
-- if (!page_pool_is_last_frag(pool, page))
-+ if (!page_pool_is_last_frag(page))
- return;
-
- page_pool_put_defragged_page(pool, page, dma_sync_size, allow_direct);
---- a/net/core/page_pool.c
-+++ b/net/core/page_pool.c
-@@ -380,6 +380,14 @@ static void page_pool_set_pp_info(struct
- {
- page->pp = pool;
- page->pp_magic |= PP_SIGNATURE;
-+
-+ /* Ensuring all pages have been split into one fragment initially:
-+ * page_pool_set_pp_info() is only called once for every page when it
-+ * is allocated from the page allocator and page_pool_fragment_page()
-+ * is dirtying the same cache line as the page->pp_magic above, so
-+ * the overhead is negligible.
-+ */
-+ page_pool_fragment_page(page, 1);
- if (pool->p.init_callback)
- pool->p.init_callback(page, pool->p.init_arg);
- }
-@@ -676,7 +684,7 @@ void page_pool_put_page_bulk(struct page
- struct page *page = virt_to_head_page(data[i]);
-
- /* It is not the last user for the page frag case */
-- if (!page_pool_is_last_frag(pool, page))
-+ if (!page_pool_is_last_frag(page))
- continue;
-
- page = __page_pool_put_page(pool, page, -1, false);
-@@ -752,8 +760,7 @@ struct page *page_pool_alloc_frag(struct
- unsigned int max_size = PAGE_SIZE << pool->p.order;
- struct page *page = pool->frag_page;
-
-- if (WARN_ON(!(pool->p.flags & PP_FLAG_PAGE_FRAG) ||
-- size > max_size))
-+ if (WARN_ON(size > max_size))
- return NULL;
-
- size = ALIGN(size, dma_get_cache_alignment());
+++ /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:
+++ /dev/null
-From 17bd3bd82f9f79f3feba15476c2b2c95a9b11ff8 Mon Sep 17 00:00:00 2001
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 26 Sep 2024 10:53:14 +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
-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
-@@ -104,8 +104,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 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;
+++ /dev/null
-From c661050f93d3fd37a33c06041bb18a89688de7d2 Mon Sep 17 00:00:00 2001
-From: Breno Leitao <leitao@debian.org>
-Date: Mon, 22 Apr 2024 05:38:56 -0700
-Subject: [PATCH] net: create a dummy net_device allocator
-
-It is impossible to use init_dummy_netdev together with alloc_netdev()
-as the 'setup' argument.
-
-This is because alloc_netdev() initializes some fields in the net_device
-structure, and later init_dummy_netdev() memzero them all. This causes
-some problems as reported here:
-
- https://lore.kernel.org/all/20240322082336.49f110cc@kernel.org/
-
-Split the init_dummy_netdev() function in two. Create a new function called
-init_dummy_netdev_core() that does not memzero the net_device structure.
-Then have init_dummy_netdev() memzero-ing and calling
-init_dummy_netdev_core(), keeping the old behaviour.
-
-init_dummy_netdev_core() is the new function that could be called as an
-argument for alloc_netdev().
-
-Also, create a helper to allocate and initialize dummy net devices,
-leveraging init_dummy_netdev_core() as the setup argument. This function
-basically simplify the allocation of dummy devices, by allocating and
-initializing it. Freeing the device continue to be done through
-free_netdev()
-
-Suggested-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Breno Leitao <leitao@debian.org>
-Reviewed-by: Ido Schimmel <idosch@nvidia.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- include/linux/netdevice.h | 3 +++
- net/core/dev.c | 56 ++++++++++++++++++++++++++-------------
- 2 files changed, 41 insertions(+), 18 deletions(-)
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -4569,6 +4569,9 @@ static inline void netif_addr_unlock_bh(
-
- void ether_setup(struct net_device *dev);
-
-+/* Allocate dummy net_device */
-+struct net_device *alloc_netdev_dummy(int sizeof_priv);
-+
- /* Support for loadable net-drivers */
- struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
- unsigned char name_assign_type,
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -10390,25 +10390,12 @@ err_free_name:
- }
- EXPORT_SYMBOL(register_netdevice);
-
--/**
-- * init_dummy_netdev - init a dummy network device for NAPI
-- * @dev: device to init
-- *
-- * This takes a network device structure and initialize the minimum
-- * amount of fields so it can be used to schedule NAPI polls without
-- * registering a full blown interface. This is to be used by drivers
-- * that need to tie several hardware interfaces to a single NAPI
-- * poll scheduler due to HW limitations.
-+/* Initialize the core of a dummy net device.
-+ * This is useful if you are calling this function after alloc_netdev(),
-+ * since it does not memset the net_device fields.
- */
--int init_dummy_netdev(struct net_device *dev)
-+static void init_dummy_netdev_core(struct net_device *dev)
- {
-- /* Clear everything. Note we don't initialize spinlocks
-- * are they aren't supposed to be taken by any of the
-- * NAPI code and this dummy netdev is supposed to be
-- * only ever used for NAPI polls
-- */
-- memset(dev, 0, sizeof(struct net_device));
--
- /* make sure we BUG if trying to hit standard
- * register/unregister code path
- */
-@@ -10428,12 +10415,32 @@ int init_dummy_netdev(struct net_device
- * because users of this 'device' dont need to change
- * its refcount.
- */
-+}
-+
-+/**
-+ * init_dummy_netdev - init a dummy network device for NAPI
-+ * @dev: device to init
-+ *
-+ * This takes a network device structure and initializes the minimum
-+ * amount of fields so it can be used to schedule NAPI polls without
-+ * registering a full blown interface. This is to be used by drivers
-+ * that need to tie several hardware interfaces to a single NAPI
-+ * poll scheduler due to HW limitations.
-+ */
-+int init_dummy_netdev(struct net_device *dev)
-+{
-+ /* Clear everything. Note we don't initialize spinlocks
-+ * as they aren't supposed to be taken by any of the
-+ * NAPI code and this dummy netdev is supposed to be
-+ * only ever used for NAPI polls
-+ */
-+ memset(dev, 0, sizeof(struct net_device));
-+ init_dummy_netdev_core(dev);
-
- return 0;
- }
- EXPORT_SYMBOL_GPL(init_dummy_netdev);
-
--
- /**
- * register_netdev - register a network device
- * @dev: device to register
-@@ -11027,6 +11034,19 @@ void free_netdev(struct net_device *dev)
- EXPORT_SYMBOL(free_netdev);
-
- /**
-+ * alloc_netdev_dummy - Allocate and initialize a dummy net device.
-+ * @sizeof_priv: size of private data to allocate space for
-+ *
-+ * Return: the allocated net_device on success, NULL otherwise
-+ */
-+struct net_device *alloc_netdev_dummy(int sizeof_priv)
-+{
-+ return alloc_netdev(sizeof_priv, "dummy#", NET_NAME_UNKNOWN,
-+ init_dummy_netdev_core);
-+}
-+EXPORT_SYMBOL_GPL(alloc_netdev_dummy);
-+
-+/**
- * synchronize_net - Synchronize with packet receive processing
- *
- * Wait for packets currently being received to be done.
+++ /dev/null
-From 84443741faab9045d53f022a9ac6a6633067a481 Mon Sep 17 00:00:00 2001
-From: Felix Fietkau <nbd@nbd.name>
-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")
-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")
-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
-@@ -367,6 +367,7 @@ static void nft_flow_offload_eval(const
- if (tcph)
- flow_offload_ct_tcp(ct);
-
-+ __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags);
- ret = flow_offload_add(flowtable, flow);
- if (ret < 0)
- goto err_flow_add;
+++ /dev/null
-From d2213db3f49bce8e7a87c8de05b9a091f78f654e Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 14 Nov 2023 15:08:41 +0100
-Subject: [PATCH 1/3] net: phy: aquantia: move to separate directory
-
-Move aquantia PHY driver to separate driectory in preparation for
-firmware loading support to keep things tidy.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/Kconfig | 5 +----
- drivers/net/phy/Makefile | 6 +-----
- drivers/net/phy/aquantia/Kconfig | 5 +++++
- drivers/net/phy/aquantia/Makefile | 6 ++++++
- drivers/net/phy/{ => aquantia}/aquantia.h | 0
- drivers/net/phy/{ => aquantia}/aquantia_hwmon.c | 0
- drivers/net/phy/{ => aquantia}/aquantia_main.c | 0
- 7 files changed, 13 insertions(+), 9 deletions(-)
- create mode 100644 drivers/net/phy/aquantia/Kconfig
- create mode 100644 drivers/net/phy/aquantia/Makefile
- rename drivers/net/phy/{ => aquantia}/aquantia.h (100%)
- rename drivers/net/phy/{ => aquantia}/aquantia_hwmon.c (100%)
- rename drivers/net/phy/{ => aquantia}/aquantia_main.c (100%)
-
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -96,10 +96,7 @@ config ADIN1100_PHY
- Currently supports the:
- - ADIN1100 - Robust,Industrial, Low Power 10BASE-T1L Ethernet PHY
-
--config AQUANTIA_PHY
-- tristate "Aquantia PHYs"
-- help
-- Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405
-+source "drivers/net/phy/aquantia/Kconfig"
-
- config AX88796B_PHY
- tristate "Asix PHYs"
---- a/drivers/net/phy/Makefile
-+++ b/drivers/net/phy/Makefile
-@@ -35,11 +35,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m)
- obj-$(CONFIG_ADIN_PHY) += adin.o
- obj-$(CONFIG_ADIN1100_PHY) += adin1100.o
- obj-$(CONFIG_AMD_PHY) += amd.o
--aquantia-objs += aquantia_main.o
--ifdef CONFIG_HWMON
--aquantia-objs += aquantia_hwmon.o
--endif
--obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o
-+obj-$(CONFIG_AQUANTIA_PHY) += aquantia/
- obj-$(CONFIG_AT803X_PHY) += at803x.o
- obj-$(CONFIG_AX88796B_PHY) += ax88796b.o
- obj-$(CONFIG_BCM54140_PHY) += bcm54140.o
---- /dev/null
-+++ b/drivers/net/phy/aquantia/Kconfig
-@@ -0,0 +1,5 @@
-+# SPDX-License-Identifier: GPL-2.0-only
-+config AQUANTIA_PHY
-+ tristate "Aquantia PHYs"
-+ help
-+ Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405
---- /dev/null
-+++ b/drivers/net/phy/aquantia/Makefile
-@@ -0,0 +1,6 @@
-+# SPDX-License-Identifier: GPL-2.0
-+aquantia-objs += aquantia_main.o
-+ifdef CONFIG_HWMON
-+aquantia-objs += aquantia_hwmon.o
-+endif
-+obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o
---- a/drivers/net/phy/aquantia.h
-+++ /dev/null
-@@ -1,16 +0,0 @@
--/* SPDX-License-Identifier: GPL-2.0 */
--/* HWMON driver for Aquantia PHY
-- *
-- * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
-- * Author: Andrew Lunn <andrew@lunn.ch>
-- * Author: Heiner Kallweit <hkallweit1@gmail.com>
-- */
--
--#include <linux/device.h>
--#include <linux/phy.h>
--
--#if IS_REACHABLE(CONFIG_HWMON)
--int aqr_hwmon_probe(struct phy_device *phydev);
--#else
--static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; }
--#endif
---- /dev/null
-+++ b/drivers/net/phy/aquantia/aquantia.h
-@@ -0,0 +1,16 @@
-+/* SPDX-License-Identifier: GPL-2.0 */
-+/* HWMON driver for Aquantia PHY
-+ *
-+ * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
-+ * Author: Andrew Lunn <andrew@lunn.ch>
-+ * Author: Heiner Kallweit <hkallweit1@gmail.com>
-+ */
-+
-+#include <linux/device.h>
-+#include <linux/phy.h>
-+
-+#if IS_REACHABLE(CONFIG_HWMON)
-+int aqr_hwmon_probe(struct phy_device *phydev);
-+#else
-+static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; }
-+#endif
---- /dev/null
-+++ b/drivers/net/phy/aquantia/aquantia_hwmon.c
-@@ -0,0 +1,250 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/* HWMON driver for Aquantia PHY
-+ *
-+ * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
-+ * Author: Andrew Lunn <andrew@lunn.ch>
-+ * Author: Heiner Kallweit <hkallweit1@gmail.com>
-+ */
-+
-+#include <linux/phy.h>
-+#include <linux/device.h>
-+#include <linux/ctype.h>
-+#include <linux/hwmon.h>
-+
-+#include "aquantia.h"
-+
-+/* Vendor specific 1, MDIO_MMD_VEND2 */
-+#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
-+#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
-+#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
-+#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424
-+#define VEND1_THERMAL_STAT1 0xc820
-+#define VEND1_THERMAL_STAT2 0xc821
-+#define VEND1_THERMAL_STAT2_VALID BIT(0)
-+#define VEND1_GENERAL_STAT1 0xc830
-+#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14)
-+#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13)
-+#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12)
-+#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11)
-+
-+#if IS_REACHABLE(CONFIG_HWMON)
-+
-+static umode_t aqr_hwmon_is_visible(const void *data,
-+ enum hwmon_sensor_types type,
-+ u32 attr, int channel)
-+{
-+ if (type != hwmon_temp)
-+ return 0;
-+
-+ switch (attr) {
-+ case hwmon_temp_input:
-+ case hwmon_temp_min_alarm:
-+ case hwmon_temp_max_alarm:
-+ case hwmon_temp_lcrit_alarm:
-+ case hwmon_temp_crit_alarm:
-+ return 0444;
-+ case hwmon_temp_min:
-+ case hwmon_temp_max:
-+ case hwmon_temp_lcrit:
-+ case hwmon_temp_crit:
-+ return 0644;
-+ default:
-+ return 0;
-+ }
-+}
-+
-+static int aqr_hwmon_get(struct phy_device *phydev, int reg, long *value)
-+{
-+ int temp = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg);
-+
-+ if (temp < 0)
-+ return temp;
-+
-+ /* 16 bit value is 2's complement with LSB = 1/256th degree Celsius */
-+ *value = (s16)temp * 1000 / 256;
-+
-+ return 0;
-+}
-+
-+static int aqr_hwmon_set(struct phy_device *phydev, int reg, long value)
-+{
-+ int temp;
-+
-+ if (value >= 128000 || value < -128000)
-+ return -ERANGE;
-+
-+ temp = value * 256 / 1000;
-+
-+ /* temp is in s16 range and we're interested in lower 16 bits only */
-+ return phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, (u16)temp);
-+}
-+
-+static int aqr_hwmon_test_bit(struct phy_device *phydev, int reg, int bit)
-+{
-+ int val = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg);
-+
-+ if (val < 0)
-+ return val;
-+
-+ return !!(val & bit);
-+}
-+
-+static int aqr_hwmon_status1(struct phy_device *phydev, int bit, long *value)
-+{
-+ int val = aqr_hwmon_test_bit(phydev, VEND1_GENERAL_STAT1, bit);
-+
-+ if (val < 0)
-+ return val;
-+
-+ *value = val;
-+
-+ return 0;
-+}
-+
-+static int aqr_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
-+ u32 attr, int channel, long *value)
-+{
-+ struct phy_device *phydev = dev_get_drvdata(dev);
-+ int reg;
-+
-+ if (type != hwmon_temp)
-+ return -EOPNOTSUPP;
-+
-+ switch (attr) {
-+ case hwmon_temp_input:
-+ reg = aqr_hwmon_test_bit(phydev, VEND1_THERMAL_STAT2,
-+ VEND1_THERMAL_STAT2_VALID);
-+ if (reg < 0)
-+ return reg;
-+ if (!reg)
-+ return -EBUSY;
-+
-+ return aqr_hwmon_get(phydev, VEND1_THERMAL_STAT1, value);
-+
-+ case hwmon_temp_lcrit:
-+ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL,
-+ value);
-+ case hwmon_temp_min:
-+ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN,
-+ value);
-+ case hwmon_temp_max:
-+ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN,
-+ value);
-+ case hwmon_temp_crit:
-+ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL,
-+ value);
-+ case hwmon_temp_lcrit_alarm:
-+ return aqr_hwmon_status1(phydev,
-+ VEND1_GENERAL_STAT1_LOW_TEMP_FAIL,
-+ value);
-+ case hwmon_temp_min_alarm:
-+ return aqr_hwmon_status1(phydev,
-+ VEND1_GENERAL_STAT1_LOW_TEMP_WARN,
-+ value);
-+ case hwmon_temp_max_alarm:
-+ return aqr_hwmon_status1(phydev,
-+ VEND1_GENERAL_STAT1_HIGH_TEMP_WARN,
-+ value);
-+ case hwmon_temp_crit_alarm:
-+ return aqr_hwmon_status1(phydev,
-+ VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL,
-+ value);
-+ default:
-+ return -EOPNOTSUPP;
-+ }
-+}
-+
-+static int aqr_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
-+ u32 attr, int channel, long value)
-+{
-+ struct phy_device *phydev = dev_get_drvdata(dev);
-+
-+ if (type != hwmon_temp)
-+ return -EOPNOTSUPP;
-+
-+ switch (attr) {
-+ case hwmon_temp_lcrit:
-+ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL,
-+ value);
-+ case hwmon_temp_min:
-+ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN,
-+ value);
-+ case hwmon_temp_max:
-+ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN,
-+ value);
-+ case hwmon_temp_crit:
-+ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL,
-+ value);
-+ default:
-+ return -EOPNOTSUPP;
-+ }
-+}
-+
-+static const struct hwmon_ops aqr_hwmon_ops = {
-+ .is_visible = aqr_hwmon_is_visible,
-+ .read = aqr_hwmon_read,
-+ .write = aqr_hwmon_write,
-+};
-+
-+static u32 aqr_hwmon_chip_config[] = {
-+ HWMON_C_REGISTER_TZ,
-+ 0,
-+};
-+
-+static const struct hwmon_channel_info aqr_hwmon_chip = {
-+ .type = hwmon_chip,
-+ .config = aqr_hwmon_chip_config,
-+};
-+
-+static u32 aqr_hwmon_temp_config[] = {
-+ HWMON_T_INPUT |
-+ HWMON_T_MAX | HWMON_T_MIN |
-+ HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM |
-+ HWMON_T_CRIT | HWMON_T_LCRIT |
-+ HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM,
-+ 0,
-+};
-+
-+static const struct hwmon_channel_info aqr_hwmon_temp = {
-+ .type = hwmon_temp,
-+ .config = aqr_hwmon_temp_config,
-+};
-+
-+static const struct hwmon_channel_info * const aqr_hwmon_info[] = {
-+ &aqr_hwmon_chip,
-+ &aqr_hwmon_temp,
-+ NULL,
-+};
-+
-+static const struct hwmon_chip_info aqr_hwmon_chip_info = {
-+ .ops = &aqr_hwmon_ops,
-+ .info = aqr_hwmon_info,
-+};
-+
-+int aqr_hwmon_probe(struct phy_device *phydev)
-+{
-+ struct device *dev = &phydev->mdio.dev;
-+ struct device *hwmon_dev;
-+ char *hwmon_name;
-+ int i, j;
-+
-+ hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
-+ if (!hwmon_name)
-+ return -ENOMEM;
-+
-+ for (i = j = 0; hwmon_name[i]; i++) {
-+ if (isalnum(hwmon_name[i])) {
-+ if (i != j)
-+ hwmon_name[j] = hwmon_name[i];
-+ j++;
-+ }
-+ }
-+ hwmon_name[j] = '\0';
-+
-+ hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name,
-+ phydev, &aqr_hwmon_chip_info, NULL);
-+
-+ return PTR_ERR_OR_ZERO(hwmon_dev);
-+}
-+
-+#endif
---- /dev/null
-+++ b/drivers/net/phy/aquantia/aquantia_main.c
-@@ -0,0 +1,882 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * Driver for Aquantia PHY
-+ *
-+ * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
-+ *
-+ * Copyright 2015 Freescale Semiconductor, Inc.
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/delay.h>
-+#include <linux/bitfield.h>
-+#include <linux/phy.h>
-+
-+#include "aquantia.h"
-+
-+#define PHY_ID_AQ1202 0x03a1b445
-+#define PHY_ID_AQ2104 0x03a1b460
-+#define PHY_ID_AQR105 0x03a1b4a2
-+#define PHY_ID_AQR106 0x03a1b4d0
-+#define PHY_ID_AQR107 0x03a1b4e0
-+#define PHY_ID_AQCS109 0x03a1b5c2
-+#define PHY_ID_AQR405 0x03a1b4b0
-+#define PHY_ID_AQR112 0x03a1b662
-+#define PHY_ID_AQR412 0x03a1b712
-+#define PHY_ID_AQR113C 0x31c31c12
-+
-+#define MDIO_PHYXS_VEND_IF_STATUS 0xe812
-+#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3)
-+#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR 0
-+#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX 1
-+#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI 2
-+#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII 3
-+#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI 4
-+#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII 6
-+#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI 7
-+#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII 10
-+
-+#define MDIO_AN_VEND_PROV 0xc400
-+#define MDIO_AN_VEND_PROV_1000BASET_FULL BIT(15)
-+#define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14)
-+#define MDIO_AN_VEND_PROV_5000BASET_FULL BIT(11)
-+#define MDIO_AN_VEND_PROV_2500BASET_FULL BIT(10)
-+#define MDIO_AN_VEND_PROV_DOWNSHIFT_EN BIT(4)
-+#define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK GENMASK(3, 0)
-+#define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT 4
-+
-+#define MDIO_AN_TX_VEND_STATUS1 0xc800
-+#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK GENMASK(3, 1)
-+#define MDIO_AN_TX_VEND_STATUS1_10BASET 0
-+#define MDIO_AN_TX_VEND_STATUS1_100BASETX 1
-+#define MDIO_AN_TX_VEND_STATUS1_1000BASET 2
-+#define MDIO_AN_TX_VEND_STATUS1_10GBASET 3
-+#define MDIO_AN_TX_VEND_STATUS1_2500BASET 4
-+#define MDIO_AN_TX_VEND_STATUS1_5000BASET 5
-+#define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0)
-+
-+#define MDIO_AN_TX_VEND_INT_STATUS1 0xcc00
-+#define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1)
-+
-+#define MDIO_AN_TX_VEND_INT_STATUS2 0xcc01
-+#define MDIO_AN_TX_VEND_INT_STATUS2_MASK BIT(0)
-+
-+#define MDIO_AN_TX_VEND_INT_MASK2 0xd401
-+#define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0)
-+
-+#define MDIO_AN_RX_LP_STAT1 0xe820
-+#define MDIO_AN_RX_LP_STAT1_1000BASET_FULL BIT(15)
-+#define MDIO_AN_RX_LP_STAT1_1000BASET_HALF BIT(14)
-+#define MDIO_AN_RX_LP_STAT1_SHORT_REACH BIT(13)
-+#define MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT BIT(12)
-+#define MDIO_AN_RX_LP_STAT1_AQ_PHY BIT(2)
-+
-+#define MDIO_AN_RX_LP_STAT4 0xe823
-+#define MDIO_AN_RX_LP_STAT4_FW_MAJOR GENMASK(15, 8)
-+#define MDIO_AN_RX_LP_STAT4_FW_MINOR GENMASK(7, 0)
-+
-+#define MDIO_AN_RX_VEND_STAT3 0xe832
-+#define MDIO_AN_RX_VEND_STAT3_AFR BIT(0)
-+
-+/* MDIO_MMD_C22EXT */
-+#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES 0xd292
-+#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES 0xd294
-+#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER 0xd297
-+#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES 0xd313
-+#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES 0xd315
-+#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER 0xd317
-+#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS 0xd318
-+#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS 0xd319
-+#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a
-+#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b
-+
-+/* Vendor specific 1, MDIO_MMD_VEND1 */
-+#define VEND1_GLOBAL_FW_ID 0x0020
-+#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
-+#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
-+
-+#define VEND1_GLOBAL_GEN_STAT2 0xc831
-+#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15)
-+
-+/* The following registers all have similar layouts; first the registers... */
-+#define VEND1_GLOBAL_CFG_10M 0x0310
-+#define VEND1_GLOBAL_CFG_100M 0x031b
-+#define VEND1_GLOBAL_CFG_1G 0x031c
-+#define VEND1_GLOBAL_CFG_2_5G 0x031d
-+#define VEND1_GLOBAL_CFG_5G 0x031e
-+#define VEND1_GLOBAL_CFG_10G 0x031f
-+/* ...and now the fields */
-+#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7)
-+#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0
-+#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1
-+#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
-+
-+#define VEND1_GLOBAL_RSVD_STAT1 0xc885
-+#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4)
-+#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0)
-+
-+#define VEND1_GLOBAL_RSVD_STAT9 0xc88d
-+#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0)
-+#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23
-+
-+#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00
-+#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01
-+
-+#define VEND1_GLOBAL_INT_STD_MASK 0xff00
-+#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15)
-+#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14)
-+#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13)
-+#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12)
-+#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11)
-+#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10)
-+#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9)
-+#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8)
-+#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7)
-+#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6)
-+#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0)
-+
-+#define VEND1_GLOBAL_INT_VEND_MASK 0xff01
-+#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15)
-+#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14)
-+#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13)
-+#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12)
-+#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11)
-+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2)
-+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
-+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
-+
-+/* Sleep and timeout for checking if the Processor-Intensive
-+ * MDIO operation is finished
-+ */
-+#define AQR107_OP_IN_PROG_SLEEP 1000
-+#define AQR107_OP_IN_PROG_TIMEOUT 100000
-+
-+struct aqr107_hw_stat {
-+ const char *name;
-+ int reg;
-+ int size;
-+};
-+
-+#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s }
-+static const struct aqr107_hw_stat aqr107_hw_stats[] = {
-+ SGMII_STAT("sgmii_rx_good_frames", RX_GOOD_FRAMES, 26),
-+ SGMII_STAT("sgmii_rx_bad_frames", RX_BAD_FRAMES, 26),
-+ SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER, 8),
-+ SGMII_STAT("sgmii_tx_good_frames", TX_GOOD_FRAMES, 26),
-+ SGMII_STAT("sgmii_tx_bad_frames", TX_BAD_FRAMES, 26),
-+ SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER, 8),
-+ SGMII_STAT("sgmii_tx_collisions", TX_COLLISIONS, 8),
-+ SGMII_STAT("sgmii_tx_line_collisions", TX_LINE_COLLISIONS, 8),
-+ SGMII_STAT("sgmii_tx_frame_alignment_err", TX_FRAME_ALIGN_ERR, 16),
-+ SGMII_STAT("sgmii_tx_runt_frames", TX_RUNT_FRAMES, 22),
-+};
-+#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats)
-+
-+struct aqr107_priv {
-+ u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
-+};
-+
-+static int aqr107_get_sset_count(struct phy_device *phydev)
-+{
-+ return AQR107_SGMII_STAT_SZ;
-+}
-+
-+static void aqr107_get_strings(struct phy_device *phydev, u8 *data)
-+{
-+ int i;
-+
-+ for (i = 0; i < AQR107_SGMII_STAT_SZ; i++)
-+ strscpy(data + i * ETH_GSTRING_LEN, aqr107_hw_stats[i].name,
-+ ETH_GSTRING_LEN);
-+}
-+
-+static u64 aqr107_get_stat(struct phy_device *phydev, int index)
-+{
-+ const struct aqr107_hw_stat *stat = aqr107_hw_stats + index;
-+ int len_l = min(stat->size, 16);
-+ int len_h = stat->size - len_l;
-+ u64 ret;
-+ int val;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg);
-+ if (val < 0)
-+ return U64_MAX;
-+
-+ ret = val & GENMASK(len_l - 1, 0);
-+ if (len_h) {
-+ val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg + 1);
-+ if (val < 0)
-+ return U64_MAX;
-+
-+ ret += (val & GENMASK(len_h - 1, 0)) << 16;
-+ }
-+
-+ return ret;
-+}
-+
-+static void aqr107_get_stats(struct phy_device *phydev,
-+ struct ethtool_stats *stats, u64 *data)
-+{
-+ struct aqr107_priv *priv = phydev->priv;
-+ u64 val;
-+ int i;
-+
-+ for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) {
-+ val = aqr107_get_stat(phydev, i);
-+ if (val == U64_MAX)
-+ phydev_err(phydev, "Reading HW Statistics failed for %s\n",
-+ aqr107_hw_stats[i].name);
-+ else
-+ priv->sgmii_stats[i] += val;
-+
-+ data[i] = priv->sgmii_stats[i];
-+ }
-+}
-+
-+static int aqr_config_aneg(struct phy_device *phydev)
-+{
-+ bool changed = false;
-+ u16 reg;
-+ int ret;
-+
-+ if (phydev->autoneg == AUTONEG_DISABLE)
-+ return genphy_c45_pma_setup_forced(phydev);
-+
-+ ret = genphy_c45_an_config_aneg(phydev);
-+ if (ret < 0)
-+ return ret;
-+ if (ret > 0)
-+ changed = true;
-+
-+ /* Clause 45 has no standardized support for 1000BaseT, therefore
-+ * use vendor registers for this mode.
-+ */
-+ reg = 0;
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-+ phydev->advertising))
-+ reg |= MDIO_AN_VEND_PROV_1000BASET_FULL;
-+
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
-+ phydev->advertising))
-+ reg |= MDIO_AN_VEND_PROV_1000BASET_HALF;
-+
-+ /* Handle the case when the 2.5G and 5G speeds are not advertised */
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
-+ phydev->advertising))
-+ reg |= MDIO_AN_VEND_PROV_2500BASET_FULL;
-+
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
-+ phydev->advertising))
-+ reg |= MDIO_AN_VEND_PROV_5000BASET_FULL;
-+
-+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV,
-+ MDIO_AN_VEND_PROV_1000BASET_HALF |
-+ MDIO_AN_VEND_PROV_1000BASET_FULL |
-+ MDIO_AN_VEND_PROV_2500BASET_FULL |
-+ MDIO_AN_VEND_PROV_5000BASET_FULL, reg);
-+ if (ret < 0)
-+ return ret;
-+ if (ret > 0)
-+ changed = true;
-+
-+ return genphy_c45_check_and_restart_aneg(phydev, changed);
-+}
-+
-+static int aqr_config_intr(struct phy_device *phydev)
-+{
-+ bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED;
-+ int err;
-+
-+ if (en) {
-+ /* Clear any pending interrupts before enabling them */
-+ err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2);
-+ if (err < 0)
-+ return err;
-+ }
-+
-+ err = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_MASK2,
-+ en ? MDIO_AN_TX_VEND_INT_MASK2_LINK : 0);
-+ if (err < 0)
-+ return err;
-+
-+ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_STD_MASK,
-+ en ? VEND1_GLOBAL_INT_STD_MASK_ALL : 0);
-+ if (err < 0)
-+ return err;
-+
-+ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_VEND_MASK,
-+ en ? VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 |
-+ VEND1_GLOBAL_INT_VEND_MASK_AN : 0);
-+ if (err < 0)
-+ return err;
-+
-+ if (!en) {
-+ /* Clear any pending interrupts after we have disabled them */
-+ err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2);
-+ if (err < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+static irqreturn_t aqr_handle_interrupt(struct phy_device *phydev)
-+{
-+ int irq_status;
-+
-+ irq_status = phy_read_mmd(phydev, MDIO_MMD_AN,
-+ MDIO_AN_TX_VEND_INT_STATUS2);
-+ if (irq_status < 0) {
-+ phy_error(phydev);
-+ return IRQ_NONE;
-+ }
-+
-+ if (!(irq_status & MDIO_AN_TX_VEND_INT_STATUS2_MASK))
-+ return IRQ_NONE;
-+
-+ phy_trigger_machine(phydev);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static int aqr_read_status(struct phy_device *phydev)
-+{
-+ int val;
-+
-+ if (phydev->autoneg == AUTONEG_ENABLE) {
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1);
-+ if (val < 0)
-+ return val;
-+
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-+ phydev->lp_advertising,
-+ val & MDIO_AN_RX_LP_STAT1_1000BASET_FULL);
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
-+ phydev->lp_advertising,
-+ val & MDIO_AN_RX_LP_STAT1_1000BASET_HALF);
-+ }
-+
-+ return genphy_c45_read_status(phydev);
-+}
-+
-+static int aqr107_read_rate(struct phy_device *phydev)
-+{
-+ u32 config_reg;
-+ int val;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1);
-+ if (val < 0)
-+ return val;
-+
-+ if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX)
-+ phydev->duplex = DUPLEX_FULL;
-+ else
-+ phydev->duplex = DUPLEX_HALF;
-+
-+ switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) {
-+ case MDIO_AN_TX_VEND_STATUS1_10BASET:
-+ phydev->speed = SPEED_10;
-+ config_reg = VEND1_GLOBAL_CFG_10M;
-+ break;
-+ case MDIO_AN_TX_VEND_STATUS1_100BASETX:
-+ phydev->speed = SPEED_100;
-+ config_reg = VEND1_GLOBAL_CFG_100M;
-+ break;
-+ case MDIO_AN_TX_VEND_STATUS1_1000BASET:
-+ phydev->speed = SPEED_1000;
-+ config_reg = VEND1_GLOBAL_CFG_1G;
-+ break;
-+ case MDIO_AN_TX_VEND_STATUS1_2500BASET:
-+ phydev->speed = SPEED_2500;
-+ config_reg = VEND1_GLOBAL_CFG_2_5G;
-+ break;
-+ case MDIO_AN_TX_VEND_STATUS1_5000BASET:
-+ phydev->speed = SPEED_5000;
-+ config_reg = VEND1_GLOBAL_CFG_5G;
-+ break;
-+ case MDIO_AN_TX_VEND_STATUS1_10GBASET:
-+ phydev->speed = SPEED_10000;
-+ config_reg = VEND1_GLOBAL_CFG_10G;
-+ break;
-+ default:
-+ phydev->speed = SPEED_UNKNOWN;
-+ return 0;
-+ }
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, config_reg);
-+ if (val < 0)
-+ return val;
-+
-+ if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) ==
-+ VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE)
-+ phydev->rate_matching = RATE_MATCH_PAUSE;
-+ else
-+ phydev->rate_matching = RATE_MATCH_NONE;
-+
-+ return 0;
-+}
-+
-+static int aqr107_read_status(struct phy_device *phydev)
-+{
-+ int val, ret;
-+
-+ ret = aqr_read_status(phydev);
-+ if (ret)
-+ return ret;
-+
-+ if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE)
-+ return 0;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS);
-+ if (val < 0)
-+ return val;
-+
-+ switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) {
-+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR:
-+ phydev->interface = PHY_INTERFACE_MODE_10GKR;
-+ break;
-+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX:
-+ phydev->interface = PHY_INTERFACE_MODE_1000BASEKX;
-+ break;
-+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI:
-+ phydev->interface = PHY_INTERFACE_MODE_10GBASER;
-+ break;
-+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII:
-+ phydev->interface = PHY_INTERFACE_MODE_USXGMII;
-+ break;
-+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI:
-+ phydev->interface = PHY_INTERFACE_MODE_XAUI;
-+ break;
-+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII:
-+ phydev->interface = PHY_INTERFACE_MODE_SGMII;
-+ break;
-+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI:
-+ phydev->interface = PHY_INTERFACE_MODE_RXAUI;
-+ break;
-+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII:
-+ phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
-+ break;
-+ default:
-+ phydev->interface = PHY_INTERFACE_MODE_NA;
-+ break;
-+ }
-+
-+ /* Read possibly downshifted rate from vendor register */
-+ return aqr107_read_rate(phydev);
-+}
-+
-+static int aqr107_get_downshift(struct phy_device *phydev, u8 *data)
-+{
-+ int val, cnt, enable;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV);
-+ if (val < 0)
-+ return val;
-+
-+ enable = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_EN, val);
-+ cnt = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val);
-+
-+ *data = enable && cnt ? cnt : DOWNSHIFT_DEV_DISABLE;
-+
-+ return 0;
-+}
-+
-+static int aqr107_set_downshift(struct phy_device *phydev, u8 cnt)
-+{
-+ int val = 0;
-+
-+ if (!FIELD_FIT(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt))
-+ return -E2BIG;
-+
-+ if (cnt != DOWNSHIFT_DEV_DISABLE) {
-+ val = MDIO_AN_VEND_PROV_DOWNSHIFT_EN;
-+ val |= FIELD_PREP(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt);
-+ }
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV,
-+ MDIO_AN_VEND_PROV_DOWNSHIFT_EN |
-+ MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val);
-+}
-+
-+static int aqr107_get_tunable(struct phy_device *phydev,
-+ struct ethtool_tunable *tuna, void *data)
-+{
-+ switch (tuna->id) {
-+ case ETHTOOL_PHY_DOWNSHIFT:
-+ return aqr107_get_downshift(phydev, data);
-+ default:
-+ return -EOPNOTSUPP;
-+ }
-+}
-+
-+static int aqr107_set_tunable(struct phy_device *phydev,
-+ struct ethtool_tunable *tuna, const void *data)
-+{
-+ switch (tuna->id) {
-+ case ETHTOOL_PHY_DOWNSHIFT:
-+ return aqr107_set_downshift(phydev, *(const u8 *)data);
-+ default:
-+ return -EOPNOTSUPP;
-+ }
-+}
-+
-+/* If we configure settings whilst firmware is still initializing the chip,
-+ * then these settings may be overwritten. Therefore make sure chip
-+ * initialization has completed. Use presence of the firmware ID as
-+ * indicator for initialization having completed.
-+ * The chip also provides a "reset completed" bit, but it's cleared after
-+ * read. Therefore function would time out if called again.
-+ */
-+static int aqr107_wait_reset_complete(struct phy_device *phydev)
-+{
-+ int val;
-+
-+ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
-+ VEND1_GLOBAL_FW_ID, val, val != 0,
-+ 20000, 2000000, false);
-+}
-+
-+static void aqr107_chip_info(struct phy_device *phydev)
-+{
-+ u8 fw_major, fw_minor, build_id, prov_id;
-+ int val;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID);
-+ if (val < 0)
-+ return;
-+
-+ fw_major = FIELD_GET(VEND1_GLOBAL_FW_ID_MAJOR, val);
-+ fw_minor = FIELD_GET(VEND1_GLOBAL_FW_ID_MINOR, val);
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT1);
-+ if (val < 0)
-+ return;
-+
-+ build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val);
-+ prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val);
-+
-+ phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n",
-+ fw_major, fw_minor, build_id, prov_id);
-+}
-+
-+static int aqr107_config_init(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ /* Check that the PHY interface type is compatible */
-+ if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
-+ phydev->interface != PHY_INTERFACE_MODE_1000BASEKX &&
-+ phydev->interface != PHY_INTERFACE_MODE_2500BASEX &&
-+ phydev->interface != PHY_INTERFACE_MODE_XGMII &&
-+ phydev->interface != PHY_INTERFACE_MODE_USXGMII &&
-+ phydev->interface != PHY_INTERFACE_MODE_10GKR &&
-+ phydev->interface != PHY_INTERFACE_MODE_10GBASER &&
-+ phydev->interface != PHY_INTERFACE_MODE_XAUI &&
-+ phydev->interface != PHY_INTERFACE_MODE_RXAUI)
-+ return -ENODEV;
-+
-+ WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII,
-+ "Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n");
-+
-+ ret = aqr107_wait_reset_complete(phydev);
-+ if (!ret)
-+ aqr107_chip_info(phydev);
-+
-+ return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
-+}
-+
-+static int aqcs109_config_init(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ /* Check that the PHY interface type is compatible */
-+ if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
-+ phydev->interface != PHY_INTERFACE_MODE_2500BASEX)
-+ return -ENODEV;
-+
-+ ret = aqr107_wait_reset_complete(phydev);
-+ if (!ret)
-+ aqr107_chip_info(phydev);
-+
-+ /* AQCS109 belongs to a chip family partially supporting 10G and 5G.
-+ * PMA speed ability bits are the same for all members of the family,
-+ * AQCS109 however supports speeds up to 2.5G only.
-+ */
-+ phy_set_max_speed(phydev, SPEED_2500);
-+
-+ return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
-+}
-+
-+static void aqr107_link_change_notify(struct phy_device *phydev)
-+{
-+ u8 fw_major, fw_minor;
-+ bool downshift, short_reach, afr;
-+ int mode, val;
-+
-+ if (phydev->state != PHY_RUNNING || phydev->autoneg == AUTONEG_DISABLE)
-+ return;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1);
-+ /* call failed or link partner is no Aquantia PHY */
-+ if (val < 0 || !(val & MDIO_AN_RX_LP_STAT1_AQ_PHY))
-+ return;
-+
-+ short_reach = val & MDIO_AN_RX_LP_STAT1_SHORT_REACH;
-+ downshift = val & MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT4);
-+ if (val < 0)
-+ return;
-+
-+ fw_major = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MAJOR, val);
-+ fw_minor = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MINOR, val);
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_VEND_STAT3);
-+ if (val < 0)
-+ return;
-+
-+ afr = val & MDIO_AN_RX_VEND_STAT3_AFR;
-+
-+ phydev_dbg(phydev, "Link partner is Aquantia PHY, FW %u.%u%s%s%s\n",
-+ fw_major, fw_minor,
-+ short_reach ? ", short reach mode" : "",
-+ downshift ? ", fast-retrain downshift advertised" : "",
-+ afr ? ", fast reframe advertised" : "");
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT9);
-+ if (val < 0)
-+ return;
-+
-+ mode = FIELD_GET(VEND1_GLOBAL_RSVD_STAT9_MODE, val);
-+ if (mode == VEND1_GLOBAL_RSVD_STAT9_1000BT2)
-+ phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n");
-+}
-+
-+static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
-+{
-+ int val, err;
-+
-+ /* The datasheet notes to wait at least 1ms after issuing a
-+ * processor intensive operation before checking.
-+ * We cannot use the 'sleep_before_read' parameter of read_poll_timeout
-+ * because that just determines the maximum time slept, not the minimum.
-+ */
-+ usleep_range(1000, 5000);
-+
-+ err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
-+ VEND1_GLOBAL_GEN_STAT2, val,
-+ !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG),
-+ AQR107_OP_IN_PROG_SLEEP,
-+ AQR107_OP_IN_PROG_TIMEOUT, false);
-+ if (err) {
-+ phydev_err(phydev, "timeout: processor-intensive MDIO operation\n");
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+static int aqr107_get_rate_matching(struct phy_device *phydev,
-+ phy_interface_t iface)
-+{
-+ if (iface == PHY_INTERFACE_MODE_10GBASER ||
-+ iface == PHY_INTERFACE_MODE_2500BASEX ||
-+ iface == PHY_INTERFACE_MODE_NA)
-+ return RATE_MATCH_PAUSE;
-+ return RATE_MATCH_NONE;
-+}
-+
-+static int aqr107_suspend(struct phy_device *phydev)
-+{
-+ int err;
-+
-+ err = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
-+ MDIO_CTRL1_LPOWER);
-+ if (err)
-+ return err;
-+
-+ return aqr107_wait_processor_intensive_op(phydev);
-+}
-+
-+static int aqr107_resume(struct phy_device *phydev)
-+{
-+ int err;
-+
-+ err = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
-+ MDIO_CTRL1_LPOWER);
-+ if (err)
-+ return err;
-+
-+ return aqr107_wait_processor_intensive_op(phydev);
-+}
-+
-+static int aqr107_probe(struct phy_device *phydev)
-+{
-+ phydev->priv = devm_kzalloc(&phydev->mdio.dev,
-+ sizeof(struct aqr107_priv), GFP_KERNEL);
-+ if (!phydev->priv)
-+ return -ENOMEM;
-+
-+ return aqr_hwmon_probe(phydev);
-+}
-+
-+static struct phy_driver aqr_driver[] = {
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQ1202),
-+ .name = "Aquantia AQ1202",
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr_read_status,
-+},
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQ2104),
-+ .name = "Aquantia AQ2104",
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr_read_status,
-+},
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR105),
-+ .name = "Aquantia AQR105",
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr_read_status,
-+ .suspend = aqr107_suspend,
-+ .resume = aqr107_resume,
-+},
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR106),
-+ .name = "Aquantia AQR106",
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr_read_status,
-+},
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR107),
-+ .name = "Aquantia AQR107",
-+ .probe = aqr107_probe,
-+ .get_rate_matching = aqr107_get_rate_matching,
-+ .config_init = aqr107_config_init,
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr107_read_status,
-+ .get_tunable = aqr107_get_tunable,
-+ .set_tunable = aqr107_set_tunable,
-+ .suspend = aqr107_suspend,
-+ .resume = aqr107_resume,
-+ .get_sset_count = aqr107_get_sset_count,
-+ .get_strings = aqr107_get_strings,
-+ .get_stats = aqr107_get_stats,
-+ .link_change_notify = aqr107_link_change_notify,
-+},
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQCS109),
-+ .name = "Aquantia AQCS109",
-+ .probe = aqr107_probe,
-+ .get_rate_matching = aqr107_get_rate_matching,
-+ .config_init = aqcs109_config_init,
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr107_read_status,
-+ .get_tunable = aqr107_get_tunable,
-+ .set_tunable = aqr107_set_tunable,
-+ .suspend = aqr107_suspend,
-+ .resume = aqr107_resume,
-+ .get_sset_count = aqr107_get_sset_count,
-+ .get_strings = aqr107_get_strings,
-+ .get_stats = aqr107_get_stats,
-+ .link_change_notify = aqr107_link_change_notify,
-+},
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR405),
-+ .name = "Aquantia AQR405",
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr_read_status,
-+},
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR112),
-+ .name = "Aquantia AQR112",
-+ .probe = aqr107_probe,
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .get_tunable = aqr107_get_tunable,
-+ .set_tunable = aqr107_set_tunable,
-+ .suspend = aqr107_suspend,
-+ .resume = aqr107_resume,
-+ .read_status = aqr107_read_status,
-+ .get_rate_matching = aqr107_get_rate_matching,
-+ .get_sset_count = aqr107_get_sset_count,
-+ .get_strings = aqr107_get_strings,
-+ .get_stats = aqr107_get_stats,
-+ .link_change_notify = aqr107_link_change_notify,
-+},
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
-+ .name = "Aquantia AQR412",
-+ .probe = aqr107_probe,
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .get_tunable = aqr107_get_tunable,
-+ .set_tunable = aqr107_set_tunable,
-+ .suspend = aqr107_suspend,
-+ .resume = aqr107_resume,
-+ .read_status = aqr107_read_status,
-+ .get_rate_matching = aqr107_get_rate_matching,
-+ .get_sset_count = aqr107_get_sset_count,
-+ .get_strings = aqr107_get_strings,
-+ .get_stats = aqr107_get_stats,
-+ .link_change_notify = aqr107_link_change_notify,
-+},
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
-+ .name = "Aquantia AQR113C",
-+ .probe = aqr107_probe,
-+ .get_rate_matching = aqr107_get_rate_matching,
-+ .config_init = aqr107_config_init,
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr107_read_status,
-+ .get_tunable = aqr107_get_tunable,
-+ .set_tunable = aqr107_set_tunable,
-+ .suspend = aqr107_suspend,
-+ .resume = aqr107_resume,
-+ .get_sset_count = aqr107_get_sset_count,
-+ .get_strings = aqr107_get_strings,
-+ .get_stats = aqr107_get_stats,
-+ .link_change_notify = aqr107_link_change_notify,
-+},
-+};
-+
-+module_phy_driver(aqr_driver);
-+
-+static struct mdio_device_id __maybe_unused aqr_tbl[] = {
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQ2104) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
-+ { }
-+};
-+
-+MODULE_DEVICE_TABLE(mdio, aqr_tbl);
-+
-+MODULE_DESCRIPTION("Aquantia PHY driver");
-+MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
-+MODULE_LICENSE("GPL v2");
---- a/drivers/net/phy/aquantia_hwmon.c
-+++ /dev/null
-@@ -1,250 +0,0 @@
--// SPDX-License-Identifier: GPL-2.0
--/* HWMON driver for Aquantia PHY
-- *
-- * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
-- * Author: Andrew Lunn <andrew@lunn.ch>
-- * Author: Heiner Kallweit <hkallweit1@gmail.com>
-- */
--
--#include <linux/phy.h>
--#include <linux/device.h>
--#include <linux/ctype.h>
--#include <linux/hwmon.h>
--
--#include "aquantia.h"
--
--/* Vendor specific 1, MDIO_MMD_VEND2 */
--#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
--#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
--#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
--#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424
--#define VEND1_THERMAL_STAT1 0xc820
--#define VEND1_THERMAL_STAT2 0xc821
--#define VEND1_THERMAL_STAT2_VALID BIT(0)
--#define VEND1_GENERAL_STAT1 0xc830
--#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14)
--#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13)
--#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12)
--#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11)
--
--#if IS_REACHABLE(CONFIG_HWMON)
--
--static umode_t aqr_hwmon_is_visible(const void *data,
-- enum hwmon_sensor_types type,
-- u32 attr, int channel)
--{
-- if (type != hwmon_temp)
-- return 0;
--
-- switch (attr) {
-- case hwmon_temp_input:
-- case hwmon_temp_min_alarm:
-- case hwmon_temp_max_alarm:
-- case hwmon_temp_lcrit_alarm:
-- case hwmon_temp_crit_alarm:
-- return 0444;
-- case hwmon_temp_min:
-- case hwmon_temp_max:
-- case hwmon_temp_lcrit:
-- case hwmon_temp_crit:
-- return 0644;
-- default:
-- return 0;
-- }
--}
--
--static int aqr_hwmon_get(struct phy_device *phydev, int reg, long *value)
--{
-- int temp = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg);
--
-- if (temp < 0)
-- return temp;
--
-- /* 16 bit value is 2's complement with LSB = 1/256th degree Celsius */
-- *value = (s16)temp * 1000 / 256;
--
-- return 0;
--}
--
--static int aqr_hwmon_set(struct phy_device *phydev, int reg, long value)
--{
-- int temp;
--
-- if (value >= 128000 || value < -128000)
-- return -ERANGE;
--
-- temp = value * 256 / 1000;
--
-- /* temp is in s16 range and we're interested in lower 16 bits only */
-- return phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, (u16)temp);
--}
--
--static int aqr_hwmon_test_bit(struct phy_device *phydev, int reg, int bit)
--{
-- int val = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg);
--
-- if (val < 0)
-- return val;
--
-- return !!(val & bit);
--}
--
--static int aqr_hwmon_status1(struct phy_device *phydev, int bit, long *value)
--{
-- int val = aqr_hwmon_test_bit(phydev, VEND1_GENERAL_STAT1, bit);
--
-- if (val < 0)
-- return val;
--
-- *value = val;
--
-- return 0;
--}
--
--static int aqr_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
-- u32 attr, int channel, long *value)
--{
-- struct phy_device *phydev = dev_get_drvdata(dev);
-- int reg;
--
-- if (type != hwmon_temp)
-- return -EOPNOTSUPP;
--
-- switch (attr) {
-- case hwmon_temp_input:
-- reg = aqr_hwmon_test_bit(phydev, VEND1_THERMAL_STAT2,
-- VEND1_THERMAL_STAT2_VALID);
-- if (reg < 0)
-- return reg;
-- if (!reg)
-- return -EBUSY;
--
-- return aqr_hwmon_get(phydev, VEND1_THERMAL_STAT1, value);
--
-- case hwmon_temp_lcrit:
-- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL,
-- value);
-- case hwmon_temp_min:
-- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN,
-- value);
-- case hwmon_temp_max:
-- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN,
-- value);
-- case hwmon_temp_crit:
-- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL,
-- value);
-- case hwmon_temp_lcrit_alarm:
-- return aqr_hwmon_status1(phydev,
-- VEND1_GENERAL_STAT1_LOW_TEMP_FAIL,
-- value);
-- case hwmon_temp_min_alarm:
-- return aqr_hwmon_status1(phydev,
-- VEND1_GENERAL_STAT1_LOW_TEMP_WARN,
-- value);
-- case hwmon_temp_max_alarm:
-- return aqr_hwmon_status1(phydev,
-- VEND1_GENERAL_STAT1_HIGH_TEMP_WARN,
-- value);
-- case hwmon_temp_crit_alarm:
-- return aqr_hwmon_status1(phydev,
-- VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL,
-- value);
-- default:
-- return -EOPNOTSUPP;
-- }
--}
--
--static int aqr_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
-- u32 attr, int channel, long value)
--{
-- struct phy_device *phydev = dev_get_drvdata(dev);
--
-- if (type != hwmon_temp)
-- return -EOPNOTSUPP;
--
-- switch (attr) {
-- case hwmon_temp_lcrit:
-- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL,
-- value);
-- case hwmon_temp_min:
-- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN,
-- value);
-- case hwmon_temp_max:
-- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN,
-- value);
-- case hwmon_temp_crit:
-- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL,
-- value);
-- default:
-- return -EOPNOTSUPP;
-- }
--}
--
--static const struct hwmon_ops aqr_hwmon_ops = {
-- .is_visible = aqr_hwmon_is_visible,
-- .read = aqr_hwmon_read,
-- .write = aqr_hwmon_write,
--};
--
--static u32 aqr_hwmon_chip_config[] = {
-- HWMON_C_REGISTER_TZ,
-- 0,
--};
--
--static const struct hwmon_channel_info aqr_hwmon_chip = {
-- .type = hwmon_chip,
-- .config = aqr_hwmon_chip_config,
--};
--
--static u32 aqr_hwmon_temp_config[] = {
-- HWMON_T_INPUT |
-- HWMON_T_MAX | HWMON_T_MIN |
-- HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM |
-- HWMON_T_CRIT | HWMON_T_LCRIT |
-- HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM,
-- 0,
--};
--
--static const struct hwmon_channel_info aqr_hwmon_temp = {
-- .type = hwmon_temp,
-- .config = aqr_hwmon_temp_config,
--};
--
--static const struct hwmon_channel_info * const aqr_hwmon_info[] = {
-- &aqr_hwmon_chip,
-- &aqr_hwmon_temp,
-- NULL,
--};
--
--static const struct hwmon_chip_info aqr_hwmon_chip_info = {
-- .ops = &aqr_hwmon_ops,
-- .info = aqr_hwmon_info,
--};
--
--int aqr_hwmon_probe(struct phy_device *phydev)
--{
-- struct device *dev = &phydev->mdio.dev;
-- struct device *hwmon_dev;
-- char *hwmon_name;
-- int i, j;
--
-- hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
-- if (!hwmon_name)
-- return -ENOMEM;
--
-- for (i = j = 0; hwmon_name[i]; i++) {
-- if (isalnum(hwmon_name[i])) {
-- if (i != j)
-- hwmon_name[j] = hwmon_name[i];
-- j++;
-- }
-- }
-- hwmon_name[j] = '\0';
--
-- hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name,
-- phydev, &aqr_hwmon_chip_info, NULL);
--
-- return PTR_ERR_OR_ZERO(hwmon_dev);
--}
--
--#endif
---- a/drivers/net/phy/aquantia_main.c
-+++ /dev/null
-@@ -1,882 +0,0 @@
--// SPDX-License-Identifier: GPL-2.0
--/*
-- * Driver for Aquantia PHY
-- *
-- * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
-- *
-- * Copyright 2015 Freescale Semiconductor, Inc.
-- */
--
--#include <linux/kernel.h>
--#include <linux/module.h>
--#include <linux/delay.h>
--#include <linux/bitfield.h>
--#include <linux/phy.h>
--
--#include "aquantia.h"
--
--#define PHY_ID_AQ1202 0x03a1b445
--#define PHY_ID_AQ2104 0x03a1b460
--#define PHY_ID_AQR105 0x03a1b4a2
--#define PHY_ID_AQR106 0x03a1b4d0
--#define PHY_ID_AQR107 0x03a1b4e0
--#define PHY_ID_AQCS109 0x03a1b5c2
--#define PHY_ID_AQR405 0x03a1b4b0
--#define PHY_ID_AQR112 0x03a1b662
--#define PHY_ID_AQR412 0x03a1b712
--#define PHY_ID_AQR113C 0x31c31c12
--
--#define MDIO_PHYXS_VEND_IF_STATUS 0xe812
--#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3)
--#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR 0
--#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX 1
--#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI 2
--#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII 3
--#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI 4
--#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII 6
--#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI 7
--#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII 10
--
--#define MDIO_AN_VEND_PROV 0xc400
--#define MDIO_AN_VEND_PROV_1000BASET_FULL BIT(15)
--#define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14)
--#define MDIO_AN_VEND_PROV_5000BASET_FULL BIT(11)
--#define MDIO_AN_VEND_PROV_2500BASET_FULL BIT(10)
--#define MDIO_AN_VEND_PROV_DOWNSHIFT_EN BIT(4)
--#define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK GENMASK(3, 0)
--#define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT 4
--
--#define MDIO_AN_TX_VEND_STATUS1 0xc800
--#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK GENMASK(3, 1)
--#define MDIO_AN_TX_VEND_STATUS1_10BASET 0
--#define MDIO_AN_TX_VEND_STATUS1_100BASETX 1
--#define MDIO_AN_TX_VEND_STATUS1_1000BASET 2
--#define MDIO_AN_TX_VEND_STATUS1_10GBASET 3
--#define MDIO_AN_TX_VEND_STATUS1_2500BASET 4
--#define MDIO_AN_TX_VEND_STATUS1_5000BASET 5
--#define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0)
--
--#define MDIO_AN_TX_VEND_INT_STATUS1 0xcc00
--#define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1)
--
--#define MDIO_AN_TX_VEND_INT_STATUS2 0xcc01
--#define MDIO_AN_TX_VEND_INT_STATUS2_MASK BIT(0)
--
--#define MDIO_AN_TX_VEND_INT_MASK2 0xd401
--#define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0)
--
--#define MDIO_AN_RX_LP_STAT1 0xe820
--#define MDIO_AN_RX_LP_STAT1_1000BASET_FULL BIT(15)
--#define MDIO_AN_RX_LP_STAT1_1000BASET_HALF BIT(14)
--#define MDIO_AN_RX_LP_STAT1_SHORT_REACH BIT(13)
--#define MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT BIT(12)
--#define MDIO_AN_RX_LP_STAT1_AQ_PHY BIT(2)
--
--#define MDIO_AN_RX_LP_STAT4 0xe823
--#define MDIO_AN_RX_LP_STAT4_FW_MAJOR GENMASK(15, 8)
--#define MDIO_AN_RX_LP_STAT4_FW_MINOR GENMASK(7, 0)
--
--#define MDIO_AN_RX_VEND_STAT3 0xe832
--#define MDIO_AN_RX_VEND_STAT3_AFR BIT(0)
--
--/* MDIO_MMD_C22EXT */
--#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES 0xd292
--#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES 0xd294
--#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER 0xd297
--#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES 0xd313
--#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES 0xd315
--#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER 0xd317
--#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS 0xd318
--#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS 0xd319
--#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a
--#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b
--
--/* Vendor specific 1, MDIO_MMD_VEND1 */
--#define VEND1_GLOBAL_FW_ID 0x0020
--#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
--#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
--
--#define VEND1_GLOBAL_GEN_STAT2 0xc831
--#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15)
--
--/* The following registers all have similar layouts; first the registers... */
--#define VEND1_GLOBAL_CFG_10M 0x0310
--#define VEND1_GLOBAL_CFG_100M 0x031b
--#define VEND1_GLOBAL_CFG_1G 0x031c
--#define VEND1_GLOBAL_CFG_2_5G 0x031d
--#define VEND1_GLOBAL_CFG_5G 0x031e
--#define VEND1_GLOBAL_CFG_10G 0x031f
--/* ...and now the fields */
--#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7)
--#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0
--#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1
--#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
--
--#define VEND1_GLOBAL_RSVD_STAT1 0xc885
--#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4)
--#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0)
--
--#define VEND1_GLOBAL_RSVD_STAT9 0xc88d
--#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0)
--#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23
--
--#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00
--#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01
--
--#define VEND1_GLOBAL_INT_STD_MASK 0xff00
--#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15)
--#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14)
--#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13)
--#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12)
--#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11)
--#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10)
--#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9)
--#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8)
--#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7)
--#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6)
--#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0)
--
--#define VEND1_GLOBAL_INT_VEND_MASK 0xff01
--#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15)
--#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14)
--#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13)
--#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12)
--#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11)
--#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2)
--#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
--#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
--
--/* Sleep and timeout for checking if the Processor-Intensive
-- * MDIO operation is finished
-- */
--#define AQR107_OP_IN_PROG_SLEEP 1000
--#define AQR107_OP_IN_PROG_TIMEOUT 100000
--
--struct aqr107_hw_stat {
-- const char *name;
-- int reg;
-- int size;
--};
--
--#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s }
--static const struct aqr107_hw_stat aqr107_hw_stats[] = {
-- SGMII_STAT("sgmii_rx_good_frames", RX_GOOD_FRAMES, 26),
-- SGMII_STAT("sgmii_rx_bad_frames", RX_BAD_FRAMES, 26),
-- SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER, 8),
-- SGMII_STAT("sgmii_tx_good_frames", TX_GOOD_FRAMES, 26),
-- SGMII_STAT("sgmii_tx_bad_frames", TX_BAD_FRAMES, 26),
-- SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER, 8),
-- SGMII_STAT("sgmii_tx_collisions", TX_COLLISIONS, 8),
-- SGMII_STAT("sgmii_tx_line_collisions", TX_LINE_COLLISIONS, 8),
-- SGMII_STAT("sgmii_tx_frame_alignment_err", TX_FRAME_ALIGN_ERR, 16),
-- SGMII_STAT("sgmii_tx_runt_frames", TX_RUNT_FRAMES, 22),
--};
--#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats)
--
--struct aqr107_priv {
-- u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
--};
--
--static int aqr107_get_sset_count(struct phy_device *phydev)
--{
-- return AQR107_SGMII_STAT_SZ;
--}
--
--static void aqr107_get_strings(struct phy_device *phydev, u8 *data)
--{
-- int i;
--
-- for (i = 0; i < AQR107_SGMII_STAT_SZ; i++)
-- strscpy(data + i * ETH_GSTRING_LEN, aqr107_hw_stats[i].name,
-- ETH_GSTRING_LEN);
--}
--
--static u64 aqr107_get_stat(struct phy_device *phydev, int index)
--{
-- const struct aqr107_hw_stat *stat = aqr107_hw_stats + index;
-- int len_l = min(stat->size, 16);
-- int len_h = stat->size - len_l;
-- u64 ret;
-- int val;
--
-- val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg);
-- if (val < 0)
-- return U64_MAX;
--
-- ret = val & GENMASK(len_l - 1, 0);
-- if (len_h) {
-- val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg + 1);
-- if (val < 0)
-- return U64_MAX;
--
-- ret += (val & GENMASK(len_h - 1, 0)) << 16;
-- }
--
-- return ret;
--}
--
--static void aqr107_get_stats(struct phy_device *phydev,
-- struct ethtool_stats *stats, u64 *data)
--{
-- struct aqr107_priv *priv = phydev->priv;
-- u64 val;
-- int i;
--
-- for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) {
-- val = aqr107_get_stat(phydev, i);
-- if (val == U64_MAX)
-- phydev_err(phydev, "Reading HW Statistics failed for %s\n",
-- aqr107_hw_stats[i].name);
-- else
-- priv->sgmii_stats[i] += val;
--
-- data[i] = priv->sgmii_stats[i];
-- }
--}
--
--static int aqr_config_aneg(struct phy_device *phydev)
--{
-- bool changed = false;
-- u16 reg;
-- int ret;
--
-- if (phydev->autoneg == AUTONEG_DISABLE)
-- return genphy_c45_pma_setup_forced(phydev);
--
-- ret = genphy_c45_an_config_aneg(phydev);
-- if (ret < 0)
-- return ret;
-- if (ret > 0)
-- changed = true;
--
-- /* Clause 45 has no standardized support for 1000BaseT, therefore
-- * use vendor registers for this mode.
-- */
-- reg = 0;
-- if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-- phydev->advertising))
-- reg |= MDIO_AN_VEND_PROV_1000BASET_FULL;
--
-- if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
-- phydev->advertising))
-- reg |= MDIO_AN_VEND_PROV_1000BASET_HALF;
--
-- /* Handle the case when the 2.5G and 5G speeds are not advertised */
-- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
-- phydev->advertising))
-- reg |= MDIO_AN_VEND_PROV_2500BASET_FULL;
--
-- if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
-- phydev->advertising))
-- reg |= MDIO_AN_VEND_PROV_5000BASET_FULL;
--
-- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV,
-- MDIO_AN_VEND_PROV_1000BASET_HALF |
-- MDIO_AN_VEND_PROV_1000BASET_FULL |
-- MDIO_AN_VEND_PROV_2500BASET_FULL |
-- MDIO_AN_VEND_PROV_5000BASET_FULL, reg);
-- if (ret < 0)
-- return ret;
-- if (ret > 0)
-- changed = true;
--
-- return genphy_c45_check_and_restart_aneg(phydev, changed);
--}
--
--static int aqr_config_intr(struct phy_device *phydev)
--{
-- bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED;
-- int err;
--
-- if (en) {
-- /* Clear any pending interrupts before enabling them */
-- err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2);
-- if (err < 0)
-- return err;
-- }
--
-- err = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_MASK2,
-- en ? MDIO_AN_TX_VEND_INT_MASK2_LINK : 0);
-- if (err < 0)
-- return err;
--
-- err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_STD_MASK,
-- en ? VEND1_GLOBAL_INT_STD_MASK_ALL : 0);
-- if (err < 0)
-- return err;
--
-- err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_VEND_MASK,
-- en ? VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 |
-- VEND1_GLOBAL_INT_VEND_MASK_AN : 0);
-- if (err < 0)
-- return err;
--
-- if (!en) {
-- /* Clear any pending interrupts after we have disabled them */
-- err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2);
-- if (err < 0)
-- return err;
-- }
--
-- return 0;
--}
--
--static irqreturn_t aqr_handle_interrupt(struct phy_device *phydev)
--{
-- int irq_status;
--
-- irq_status = phy_read_mmd(phydev, MDIO_MMD_AN,
-- MDIO_AN_TX_VEND_INT_STATUS2);
-- if (irq_status < 0) {
-- phy_error(phydev);
-- return IRQ_NONE;
-- }
--
-- if (!(irq_status & MDIO_AN_TX_VEND_INT_STATUS2_MASK))
-- return IRQ_NONE;
--
-- phy_trigger_machine(phydev);
--
-- return IRQ_HANDLED;
--}
--
--static int aqr_read_status(struct phy_device *phydev)
--{
-- int val;
--
-- if (phydev->autoneg == AUTONEG_ENABLE) {
-- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1);
-- if (val < 0)
-- return val;
--
-- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-- phydev->lp_advertising,
-- val & MDIO_AN_RX_LP_STAT1_1000BASET_FULL);
-- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
-- phydev->lp_advertising,
-- val & MDIO_AN_RX_LP_STAT1_1000BASET_HALF);
-- }
--
-- return genphy_c45_read_status(phydev);
--}
--
--static int aqr107_read_rate(struct phy_device *phydev)
--{
-- u32 config_reg;
-- int val;
--
-- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1);
-- if (val < 0)
-- return val;
--
-- if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX)
-- phydev->duplex = DUPLEX_FULL;
-- else
-- phydev->duplex = DUPLEX_HALF;
--
-- switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) {
-- case MDIO_AN_TX_VEND_STATUS1_10BASET:
-- phydev->speed = SPEED_10;
-- config_reg = VEND1_GLOBAL_CFG_10M;
-- break;
-- case MDIO_AN_TX_VEND_STATUS1_100BASETX:
-- phydev->speed = SPEED_100;
-- config_reg = VEND1_GLOBAL_CFG_100M;
-- break;
-- case MDIO_AN_TX_VEND_STATUS1_1000BASET:
-- phydev->speed = SPEED_1000;
-- config_reg = VEND1_GLOBAL_CFG_1G;
-- break;
-- case MDIO_AN_TX_VEND_STATUS1_2500BASET:
-- phydev->speed = SPEED_2500;
-- config_reg = VEND1_GLOBAL_CFG_2_5G;
-- break;
-- case MDIO_AN_TX_VEND_STATUS1_5000BASET:
-- phydev->speed = SPEED_5000;
-- config_reg = VEND1_GLOBAL_CFG_5G;
-- break;
-- case MDIO_AN_TX_VEND_STATUS1_10GBASET:
-- phydev->speed = SPEED_10000;
-- config_reg = VEND1_GLOBAL_CFG_10G;
-- break;
-- default:
-- phydev->speed = SPEED_UNKNOWN;
-- return 0;
-- }
--
-- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, config_reg);
-- if (val < 0)
-- return val;
--
-- if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) ==
-- VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE)
-- phydev->rate_matching = RATE_MATCH_PAUSE;
-- else
-- phydev->rate_matching = RATE_MATCH_NONE;
--
-- return 0;
--}
--
--static int aqr107_read_status(struct phy_device *phydev)
--{
-- int val, ret;
--
-- ret = aqr_read_status(phydev);
-- if (ret)
-- return ret;
--
-- if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE)
-- return 0;
--
-- val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS);
-- if (val < 0)
-- return val;
--
-- switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) {
-- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR:
-- phydev->interface = PHY_INTERFACE_MODE_10GKR;
-- break;
-- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX:
-- phydev->interface = PHY_INTERFACE_MODE_1000BASEKX;
-- break;
-- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI:
-- phydev->interface = PHY_INTERFACE_MODE_10GBASER;
-- break;
-- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII:
-- phydev->interface = PHY_INTERFACE_MODE_USXGMII;
-- break;
-- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI:
-- phydev->interface = PHY_INTERFACE_MODE_XAUI;
-- break;
-- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII:
-- phydev->interface = PHY_INTERFACE_MODE_SGMII;
-- break;
-- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI:
-- phydev->interface = PHY_INTERFACE_MODE_RXAUI;
-- break;
-- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII:
-- phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
-- break;
-- default:
-- phydev->interface = PHY_INTERFACE_MODE_NA;
-- break;
-- }
--
-- /* Read possibly downshifted rate from vendor register */
-- return aqr107_read_rate(phydev);
--}
--
--static int aqr107_get_downshift(struct phy_device *phydev, u8 *data)
--{
-- int val, cnt, enable;
--
-- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV);
-- if (val < 0)
-- return val;
--
-- enable = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_EN, val);
-- cnt = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val);
--
-- *data = enable && cnt ? cnt : DOWNSHIFT_DEV_DISABLE;
--
-- return 0;
--}
--
--static int aqr107_set_downshift(struct phy_device *phydev, u8 cnt)
--{
-- int val = 0;
--
-- if (!FIELD_FIT(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt))
-- return -E2BIG;
--
-- if (cnt != DOWNSHIFT_DEV_DISABLE) {
-- val = MDIO_AN_VEND_PROV_DOWNSHIFT_EN;
-- val |= FIELD_PREP(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt);
-- }
--
-- return phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV,
-- MDIO_AN_VEND_PROV_DOWNSHIFT_EN |
-- MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val);
--}
--
--static int aqr107_get_tunable(struct phy_device *phydev,
-- struct ethtool_tunable *tuna, void *data)
--{
-- switch (tuna->id) {
-- case ETHTOOL_PHY_DOWNSHIFT:
-- return aqr107_get_downshift(phydev, data);
-- default:
-- return -EOPNOTSUPP;
-- }
--}
--
--static int aqr107_set_tunable(struct phy_device *phydev,
-- struct ethtool_tunable *tuna, const void *data)
--{
-- switch (tuna->id) {
-- case ETHTOOL_PHY_DOWNSHIFT:
-- return aqr107_set_downshift(phydev, *(const u8 *)data);
-- default:
-- return -EOPNOTSUPP;
-- }
--}
--
--/* If we configure settings whilst firmware is still initializing the chip,
-- * then these settings may be overwritten. Therefore make sure chip
-- * initialization has completed. Use presence of the firmware ID as
-- * indicator for initialization having completed.
-- * The chip also provides a "reset completed" bit, but it's cleared after
-- * read. Therefore function would time out if called again.
-- */
--static int aqr107_wait_reset_complete(struct phy_device *phydev)
--{
-- int val;
--
-- return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
-- VEND1_GLOBAL_FW_ID, val, val != 0,
-- 20000, 2000000, false);
--}
--
--static void aqr107_chip_info(struct phy_device *phydev)
--{
-- u8 fw_major, fw_minor, build_id, prov_id;
-- int val;
--
-- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID);
-- if (val < 0)
-- return;
--
-- fw_major = FIELD_GET(VEND1_GLOBAL_FW_ID_MAJOR, val);
-- fw_minor = FIELD_GET(VEND1_GLOBAL_FW_ID_MINOR, val);
--
-- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT1);
-- if (val < 0)
-- return;
--
-- build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val);
-- prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val);
--
-- phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n",
-- fw_major, fw_minor, build_id, prov_id);
--}
--
--static int aqr107_config_init(struct phy_device *phydev)
--{
-- int ret;
--
-- /* Check that the PHY interface type is compatible */
-- if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
-- phydev->interface != PHY_INTERFACE_MODE_1000BASEKX &&
-- phydev->interface != PHY_INTERFACE_MODE_2500BASEX &&
-- phydev->interface != PHY_INTERFACE_MODE_XGMII &&
-- phydev->interface != PHY_INTERFACE_MODE_USXGMII &&
-- phydev->interface != PHY_INTERFACE_MODE_10GKR &&
-- phydev->interface != PHY_INTERFACE_MODE_10GBASER &&
-- phydev->interface != PHY_INTERFACE_MODE_XAUI &&
-- phydev->interface != PHY_INTERFACE_MODE_RXAUI)
-- return -ENODEV;
--
-- WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII,
-- "Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n");
--
-- ret = aqr107_wait_reset_complete(phydev);
-- if (!ret)
-- aqr107_chip_info(phydev);
--
-- return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
--}
--
--static int aqcs109_config_init(struct phy_device *phydev)
--{
-- int ret;
--
-- /* Check that the PHY interface type is compatible */
-- if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
-- phydev->interface != PHY_INTERFACE_MODE_2500BASEX)
-- return -ENODEV;
--
-- ret = aqr107_wait_reset_complete(phydev);
-- if (!ret)
-- aqr107_chip_info(phydev);
--
-- /* AQCS109 belongs to a chip family partially supporting 10G and 5G.
-- * PMA speed ability bits are the same for all members of the family,
-- * AQCS109 however supports speeds up to 2.5G only.
-- */
-- phy_set_max_speed(phydev, SPEED_2500);
--
-- return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
--}
--
--static void aqr107_link_change_notify(struct phy_device *phydev)
--{
-- u8 fw_major, fw_minor;
-- bool downshift, short_reach, afr;
-- int mode, val;
--
-- if (phydev->state != PHY_RUNNING || phydev->autoneg == AUTONEG_DISABLE)
-- return;
--
-- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1);
-- /* call failed or link partner is no Aquantia PHY */
-- if (val < 0 || !(val & MDIO_AN_RX_LP_STAT1_AQ_PHY))
-- return;
--
-- short_reach = val & MDIO_AN_RX_LP_STAT1_SHORT_REACH;
-- downshift = val & MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT;
--
-- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT4);
-- if (val < 0)
-- return;
--
-- fw_major = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MAJOR, val);
-- fw_minor = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MINOR, val);
--
-- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_VEND_STAT3);
-- if (val < 0)
-- return;
--
-- afr = val & MDIO_AN_RX_VEND_STAT3_AFR;
--
-- phydev_dbg(phydev, "Link partner is Aquantia PHY, FW %u.%u%s%s%s\n",
-- fw_major, fw_minor,
-- short_reach ? ", short reach mode" : "",
-- downshift ? ", fast-retrain downshift advertised" : "",
-- afr ? ", fast reframe advertised" : "");
--
-- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT9);
-- if (val < 0)
-- return;
--
-- mode = FIELD_GET(VEND1_GLOBAL_RSVD_STAT9_MODE, val);
-- if (mode == VEND1_GLOBAL_RSVD_STAT9_1000BT2)
-- phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n");
--}
--
--static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
--{
-- int val, err;
--
-- /* The datasheet notes to wait at least 1ms after issuing a
-- * processor intensive operation before checking.
-- * We cannot use the 'sleep_before_read' parameter of read_poll_timeout
-- * because that just determines the maximum time slept, not the minimum.
-- */
-- usleep_range(1000, 5000);
--
-- err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
-- VEND1_GLOBAL_GEN_STAT2, val,
-- !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG),
-- AQR107_OP_IN_PROG_SLEEP,
-- AQR107_OP_IN_PROG_TIMEOUT, false);
-- if (err) {
-- phydev_err(phydev, "timeout: processor-intensive MDIO operation\n");
-- return err;
-- }
--
-- return 0;
--}
--
--static int aqr107_get_rate_matching(struct phy_device *phydev,
-- phy_interface_t iface)
--{
-- if (iface == PHY_INTERFACE_MODE_10GBASER ||
-- iface == PHY_INTERFACE_MODE_2500BASEX ||
-- iface == PHY_INTERFACE_MODE_NA)
-- return RATE_MATCH_PAUSE;
-- return RATE_MATCH_NONE;
--}
--
--static int aqr107_suspend(struct phy_device *phydev)
--{
-- int err;
--
-- err = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
-- MDIO_CTRL1_LPOWER);
-- if (err)
-- return err;
--
-- return aqr107_wait_processor_intensive_op(phydev);
--}
--
--static int aqr107_resume(struct phy_device *phydev)
--{
-- int err;
--
-- err = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
-- MDIO_CTRL1_LPOWER);
-- if (err)
-- return err;
--
-- return aqr107_wait_processor_intensive_op(phydev);
--}
--
--static int aqr107_probe(struct phy_device *phydev)
--{
-- phydev->priv = devm_kzalloc(&phydev->mdio.dev,
-- sizeof(struct aqr107_priv), GFP_KERNEL);
-- if (!phydev->priv)
-- return -ENOMEM;
--
-- return aqr_hwmon_probe(phydev);
--}
--
--static struct phy_driver aqr_driver[] = {
--{
-- PHY_ID_MATCH_MODEL(PHY_ID_AQ1202),
-- .name = "Aquantia AQ1202",
-- .config_aneg = aqr_config_aneg,
-- .config_intr = aqr_config_intr,
-- .handle_interrupt = aqr_handle_interrupt,
-- .read_status = aqr_read_status,
--},
--{
-- PHY_ID_MATCH_MODEL(PHY_ID_AQ2104),
-- .name = "Aquantia AQ2104",
-- .config_aneg = aqr_config_aneg,
-- .config_intr = aqr_config_intr,
-- .handle_interrupt = aqr_handle_interrupt,
-- .read_status = aqr_read_status,
--},
--{
-- PHY_ID_MATCH_MODEL(PHY_ID_AQR105),
-- .name = "Aquantia AQR105",
-- .config_aneg = aqr_config_aneg,
-- .config_intr = aqr_config_intr,
-- .handle_interrupt = aqr_handle_interrupt,
-- .read_status = aqr_read_status,
-- .suspend = aqr107_suspend,
-- .resume = aqr107_resume,
--},
--{
-- PHY_ID_MATCH_MODEL(PHY_ID_AQR106),
-- .name = "Aquantia AQR106",
-- .config_aneg = aqr_config_aneg,
-- .config_intr = aqr_config_intr,
-- .handle_interrupt = aqr_handle_interrupt,
-- .read_status = aqr_read_status,
--},
--{
-- PHY_ID_MATCH_MODEL(PHY_ID_AQR107),
-- .name = "Aquantia AQR107",
-- .probe = aqr107_probe,
-- .get_rate_matching = aqr107_get_rate_matching,
-- .config_init = aqr107_config_init,
-- .config_aneg = aqr_config_aneg,
-- .config_intr = aqr_config_intr,
-- .handle_interrupt = aqr_handle_interrupt,
-- .read_status = aqr107_read_status,
-- .get_tunable = aqr107_get_tunable,
-- .set_tunable = aqr107_set_tunable,
-- .suspend = aqr107_suspend,
-- .resume = aqr107_resume,
-- .get_sset_count = aqr107_get_sset_count,
-- .get_strings = aqr107_get_strings,
-- .get_stats = aqr107_get_stats,
-- .link_change_notify = aqr107_link_change_notify,
--},
--{
-- PHY_ID_MATCH_MODEL(PHY_ID_AQCS109),
-- .name = "Aquantia AQCS109",
-- .probe = aqr107_probe,
-- .get_rate_matching = aqr107_get_rate_matching,
-- .config_init = aqcs109_config_init,
-- .config_aneg = aqr_config_aneg,
-- .config_intr = aqr_config_intr,
-- .handle_interrupt = aqr_handle_interrupt,
-- .read_status = aqr107_read_status,
-- .get_tunable = aqr107_get_tunable,
-- .set_tunable = aqr107_set_tunable,
-- .suspend = aqr107_suspend,
-- .resume = aqr107_resume,
-- .get_sset_count = aqr107_get_sset_count,
-- .get_strings = aqr107_get_strings,
-- .get_stats = aqr107_get_stats,
-- .link_change_notify = aqr107_link_change_notify,
--},
--{
-- PHY_ID_MATCH_MODEL(PHY_ID_AQR405),
-- .name = "Aquantia AQR405",
-- .config_aneg = aqr_config_aneg,
-- .config_intr = aqr_config_intr,
-- .handle_interrupt = aqr_handle_interrupt,
-- .read_status = aqr_read_status,
--},
--{
-- PHY_ID_MATCH_MODEL(PHY_ID_AQR112),
-- .name = "Aquantia AQR112",
-- .probe = aqr107_probe,
-- .config_aneg = aqr_config_aneg,
-- .config_intr = aqr_config_intr,
-- .handle_interrupt = aqr_handle_interrupt,
-- .get_tunable = aqr107_get_tunable,
-- .set_tunable = aqr107_set_tunable,
-- .suspend = aqr107_suspend,
-- .resume = aqr107_resume,
-- .read_status = aqr107_read_status,
-- .get_rate_matching = aqr107_get_rate_matching,
-- .get_sset_count = aqr107_get_sset_count,
-- .get_strings = aqr107_get_strings,
-- .get_stats = aqr107_get_stats,
-- .link_change_notify = aqr107_link_change_notify,
--},
--{
-- PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
-- .name = "Aquantia AQR412",
-- .probe = aqr107_probe,
-- .config_aneg = aqr_config_aneg,
-- .config_intr = aqr_config_intr,
-- .handle_interrupt = aqr_handle_interrupt,
-- .get_tunable = aqr107_get_tunable,
-- .set_tunable = aqr107_set_tunable,
-- .suspend = aqr107_suspend,
-- .resume = aqr107_resume,
-- .read_status = aqr107_read_status,
-- .get_rate_matching = aqr107_get_rate_matching,
-- .get_sset_count = aqr107_get_sset_count,
-- .get_strings = aqr107_get_strings,
-- .get_stats = aqr107_get_stats,
-- .link_change_notify = aqr107_link_change_notify,
--},
--{
-- PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
-- .name = "Aquantia AQR113C",
-- .probe = aqr107_probe,
-- .get_rate_matching = aqr107_get_rate_matching,
-- .config_init = aqr107_config_init,
-- .config_aneg = aqr_config_aneg,
-- .config_intr = aqr_config_intr,
-- .handle_interrupt = aqr_handle_interrupt,
-- .read_status = aqr107_read_status,
-- .get_tunable = aqr107_get_tunable,
-- .set_tunable = aqr107_set_tunable,
-- .suspend = aqr107_suspend,
-- .resume = aqr107_resume,
-- .get_sset_count = aqr107_get_sset_count,
-- .get_strings = aqr107_get_strings,
-- .get_stats = aqr107_get_stats,
-- .link_change_notify = aqr107_link_change_notify,
--},
--};
--
--module_phy_driver(aqr_driver);
--
--static struct mdio_device_id __maybe_unused aqr_tbl[] = {
-- { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202) },
-- { PHY_ID_MATCH_MODEL(PHY_ID_AQ2104) },
-- { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) },
-- { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
-- { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
-- { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
-- { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
-- { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
-- { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
-- { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
-- { }
--};
--
--MODULE_DEVICE_TABLE(mdio, aqr_tbl);
--
--MODULE_DESCRIPTION("Aquantia PHY driver");
--MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
--MODULE_LICENSE("GPL v2");
+++ /dev/null
-From e1fbfa4a995d42e02e22b0dff2f8b4fdee1504b3 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 14 Nov 2023 15:08:42 +0100
-Subject: [PATCH 2/3] net: phy: aquantia: move MMD_VEND define to header
-
-Move MMD_VEND define to header to clean things up and in preparation for
-firmware loading support that require some define placed in
-aquantia_main.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/aquantia/aquantia.h | 69 +++++++++++++++++++++++
- drivers/net/phy/aquantia/aquantia_hwmon.c | 14 -----
- drivers/net/phy/aquantia/aquantia_main.c | 55 ------------------
- 3 files changed, 69 insertions(+), 69 deletions(-)
-
---- a/drivers/net/phy/aquantia/aquantia.h
-+++ b/drivers/net/phy/aquantia/aquantia.h
-@@ -9,6 +9,75 @@
- #include <linux/device.h>
- #include <linux/phy.h>
-
-+/* Vendor specific 1, MDIO_MMD_VEND1 */
-+#define VEND1_GLOBAL_FW_ID 0x0020
-+#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
-+#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
-+
-+/* The following registers all have similar layouts; first the registers... */
-+#define VEND1_GLOBAL_CFG_10M 0x0310
-+#define VEND1_GLOBAL_CFG_100M 0x031b
-+#define VEND1_GLOBAL_CFG_1G 0x031c
-+#define VEND1_GLOBAL_CFG_2_5G 0x031d
-+#define VEND1_GLOBAL_CFG_5G 0x031e
-+#define VEND1_GLOBAL_CFG_10G 0x031f
-+/* ...and now the fields */
-+#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7)
-+#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0
-+#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1
-+#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
-+
-+/* Vendor specific 1, MDIO_MMD_VEND2 */
-+#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
-+#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
-+#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
-+#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424
-+#define VEND1_THERMAL_STAT1 0xc820
-+#define VEND1_THERMAL_STAT2 0xc821
-+#define VEND1_THERMAL_STAT2_VALID BIT(0)
-+#define VEND1_GENERAL_STAT1 0xc830
-+#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14)
-+#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13)
-+#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12)
-+#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11)
-+
-+#define VEND1_GLOBAL_GEN_STAT2 0xc831
-+#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15)
-+
-+#define VEND1_GLOBAL_RSVD_STAT1 0xc885
-+#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4)
-+#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0)
-+
-+#define VEND1_GLOBAL_RSVD_STAT9 0xc88d
-+#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0)
-+#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23
-+
-+#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00
-+#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01
-+
-+#define VEND1_GLOBAL_INT_STD_MASK 0xff00
-+#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15)
-+#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14)
-+#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13)
-+#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12)
-+#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11)
-+#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10)
-+#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9)
-+#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8)
-+#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7)
-+#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6)
-+#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0)
-+
-+#define VEND1_GLOBAL_INT_VEND_MASK 0xff01
-+#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15)
-+#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14)
-+#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13)
-+#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12)
-+#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11)
-+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2)
-+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
-+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
-+
- #if IS_REACHABLE(CONFIG_HWMON)
- int aqr_hwmon_probe(struct phy_device *phydev);
- #else
---- a/drivers/net/phy/aquantia/aquantia_hwmon.c
-+++ b/drivers/net/phy/aquantia/aquantia_hwmon.c
-@@ -13,20 +13,6 @@
-
- #include "aquantia.h"
-
--/* Vendor specific 1, MDIO_MMD_VEND2 */
--#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
--#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
--#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
--#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424
--#define VEND1_THERMAL_STAT1 0xc820
--#define VEND1_THERMAL_STAT2 0xc821
--#define VEND1_THERMAL_STAT2_VALID BIT(0)
--#define VEND1_GENERAL_STAT1 0xc830
--#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14)
--#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13)
--#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12)
--#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11)
--
- #if IS_REACHABLE(CONFIG_HWMON)
-
- static umode_t aqr_hwmon_is_visible(const void *data,
---- a/drivers/net/phy/aquantia/aquantia_main.c
-+++ b/drivers/net/phy/aquantia/aquantia_main.c
-@@ -91,61 +91,6 @@
- #define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a
- #define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b
-
--/* Vendor specific 1, MDIO_MMD_VEND1 */
--#define VEND1_GLOBAL_FW_ID 0x0020
--#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
--#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
--
--#define VEND1_GLOBAL_GEN_STAT2 0xc831
--#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15)
--
--/* The following registers all have similar layouts; first the registers... */
--#define VEND1_GLOBAL_CFG_10M 0x0310
--#define VEND1_GLOBAL_CFG_100M 0x031b
--#define VEND1_GLOBAL_CFG_1G 0x031c
--#define VEND1_GLOBAL_CFG_2_5G 0x031d
--#define VEND1_GLOBAL_CFG_5G 0x031e
--#define VEND1_GLOBAL_CFG_10G 0x031f
--/* ...and now the fields */
--#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7)
--#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0
--#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1
--#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
--
--#define VEND1_GLOBAL_RSVD_STAT1 0xc885
--#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4)
--#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0)
--
--#define VEND1_GLOBAL_RSVD_STAT9 0xc88d
--#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0)
--#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23
--
--#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00
--#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01
--
--#define VEND1_GLOBAL_INT_STD_MASK 0xff00
--#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15)
--#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14)
--#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13)
--#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12)
--#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11)
--#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10)
--#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9)
--#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8)
--#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7)
--#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6)
--#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0)
--
--#define VEND1_GLOBAL_INT_VEND_MASK 0xff01
--#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15)
--#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14)
--#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13)
--#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12)
--#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11)
--#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2)
--#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
--#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
--
- /* Sleep and timeout for checking if the Processor-Intensive
- * MDIO operation is finished
- */
+++ /dev/null
-From e93984ebc1c82bd34f7a1b3391efaceee0a8ae96 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Tue, 14 Nov 2023 15:08:43 +0100
-Subject: [PATCH 3/3] net: phy: aquantia: add firmware load support
-
-Aquantia PHY-s require firmware to be loaded before they start operating.
-It can be automatically loaded in case when there is a SPI-NOR connected
-to Aquantia PHY-s or can be loaded from the host via MDIO.
-
-This patch adds support for loading the firmware via MDIO as in most cases
-there is no SPI-NOR being used to save on cost.
-Firmware loading code itself is ported from mainline U-boot with cleanups.
-
-The firmware has mixed values both in big and little endian.
-PHY core itself is big-endian but it expects values to be in little-endian.
-The firmware is little-endian but CRC-16 value for it is stored at the end
-of firmware in big-endian.
-
-It seems the PHY does the conversion internally from firmware that is
-little-endian to the PHY that is big-endian on using the mailbox
-but mailbox returns a big-endian CRC-16 to verify the written data
-integrity.
-
-Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/aquantia/Kconfig | 1 +
- drivers/net/phy/aquantia/Makefile | 2 +-
- drivers/net/phy/aquantia/aquantia.h | 32 ++
- drivers/net/phy/aquantia/aquantia_firmware.c | 370 +++++++++++++++++++
- drivers/net/phy/aquantia/aquantia_main.c | 6 +
- 5 files changed, 410 insertions(+), 1 deletion(-)
- create mode 100644 drivers/net/phy/aquantia/aquantia_firmware.c
-
---- a/drivers/net/phy/aquantia/Kconfig
-+++ b/drivers/net/phy/aquantia/Kconfig
-@@ -1,5 +1,6 @@
- # SPDX-License-Identifier: GPL-2.0-only
- config AQUANTIA_PHY
- tristate "Aquantia PHYs"
-+ select CRC_CCITT
- help
- Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405
---- a/drivers/net/phy/aquantia/Makefile
-+++ b/drivers/net/phy/aquantia/Makefile
-@@ -1,5 +1,5 @@
- # SPDX-License-Identifier: GPL-2.0
--aquantia-objs += aquantia_main.o
-+aquantia-objs += aquantia_main.o aquantia_firmware.o
- ifdef CONFIG_HWMON
- aquantia-objs += aquantia_hwmon.o
- endif
---- a/drivers/net/phy/aquantia/aquantia.h
-+++ b/drivers/net/phy/aquantia/aquantia.h
-@@ -10,10 +10,35 @@
- #include <linux/phy.h>
-
- /* Vendor specific 1, MDIO_MMD_VEND1 */
-+#define VEND1_GLOBAL_SC 0x0
-+#define VEND1_GLOBAL_SC_SOFT_RESET BIT(15)
-+#define VEND1_GLOBAL_SC_LOW_POWER BIT(11)
-+
- #define VEND1_GLOBAL_FW_ID 0x0020
- #define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
- #define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
-
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE1 0x0200
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE BIT(15)
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE BIT(14)
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET BIT(12)
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_BUSY BIT(8)
-+
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE2 0x0201
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE3 0x0202
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK GENMASK(15, 0)
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK, (u16)((x) >> 16))
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE4 0x0203
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK GENMASK(15, 2)
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK, (u16)(x))
-+
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE5 0x0204
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK GENMASK(15, 0)
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK, (u16)((x) >> 16))
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE6 0x0205
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK GENMASK(15, 0)
-+#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK, (u16)(x))
-+
- /* The following registers all have similar layouts; first the registers... */
- #define VEND1_GLOBAL_CFG_10M 0x0310
- #define VEND1_GLOBAL_CFG_100M 0x031b
-@@ -28,6 +53,11 @@
- #define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
-
- /* Vendor specific 1, MDIO_MMD_VEND2 */
-+#define VEND1_GLOBAL_CONTROL2 0xc001
-+#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST BIT(15)
-+#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD BIT(6)
-+#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL BIT(0)
-+
- #define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
- #define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
- #define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
-@@ -83,3 +113,5 @@ int aqr_hwmon_probe(struct phy_device *p
- #else
- static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; }
- #endif
-+
-+int aqr_firmware_load(struct phy_device *phydev);
---- /dev/null
-+++ b/drivers/net/phy/aquantia/aquantia_firmware.c
-@@ -0,0 +1,370 @@
-+// SPDX-License-Identifier: GPL-2.0
-+
-+#include <linux/bitfield.h>
-+#include <linux/of.h>
-+#include <linux/firmware.h>
-+#include <linux/crc-ccitt.h>
-+#include <linux/nvmem-consumer.h>
-+
-+#include <asm/unaligned.h>
-+
-+#include "aquantia.h"
-+
-+#define UP_RESET_SLEEP 100
-+
-+/* addresses of memory segments in the phy */
-+#define DRAM_BASE_ADDR 0x3FFE0000
-+#define IRAM_BASE_ADDR 0x40000000
-+
-+/* firmware image format constants */
-+#define VERSION_STRING_SIZE 0x40
-+#define VERSION_STRING_OFFSET 0x0200
-+/* primary offset is written at an offset from the start of the fw blob */
-+#define PRIMARY_OFFSET_OFFSET 0x8
-+/* primary offset needs to be then added to a base offset */
-+#define PRIMARY_OFFSET_SHIFT 12
-+#define PRIMARY_OFFSET(x) ((x) << PRIMARY_OFFSET_SHIFT)
-+#define HEADER_OFFSET 0x300
-+
-+struct aqr_fw_header {
-+ u32 padding;
-+ u8 iram_offset[3];
-+ u8 iram_size[3];
-+ u8 dram_offset[3];
-+ u8 dram_size[3];
-+} __packed;
-+
-+enum aqr_fw_src {
-+ AQR_FW_SRC_NVMEM = 0,
-+ AQR_FW_SRC_FS,
-+};
-+
-+static const char * const aqr_fw_src_string[] = {
-+ [AQR_FW_SRC_NVMEM] = "NVMEM",
-+ [AQR_FW_SRC_FS] = "FS",
-+};
-+
-+/* AQR firmware doesn't have fixed offsets for iram and dram section
-+ * but instead provide an header with the offset to use on reading
-+ * and parsing the firmware.
-+ *
-+ * AQR firmware can't be trusted and each offset is validated to be
-+ * not negative and be in the size of the firmware itself.
-+ */
-+static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size)
-+{
-+ return offset + get_size <= size;
-+}
-+
-+static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value)
-+{
-+ if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
-+ return -EINVAL;
-+
-+ *value = get_unaligned_be16(data + offset);
-+
-+ return 0;
-+}
-+
-+static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value)
-+{
-+ if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
-+ return -EINVAL;
-+
-+ *value = get_unaligned_le16(data + offset);
-+
-+ return 0;
-+}
-+
-+static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value)
-+{
-+ if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3))
-+ return -EINVAL;
-+
-+ *value = get_unaligned_le24(data + offset);
-+
-+ return 0;
-+}
-+
-+/* load data into the phy's memory */
-+static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr,
-+ const u8 *data, size_t len)
-+{
-+ u16 crc = 0, up_crc;
-+ size_t pos;
-+
-+ /* PHY expect addr in LE */
-+ addr = (__force u32)cpu_to_le32(addr);
-+
-+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
-+ VEND1_GLOBAL_MAILBOX_INTERFACE1,
-+ VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET);
-+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
-+ VEND1_GLOBAL_MAILBOX_INTERFACE3,
-+ VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr));
-+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
-+ VEND1_GLOBAL_MAILBOX_INTERFACE4,
-+ VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr));
-+
-+ /* We assume and enforce the size to be word aligned.
-+ * If a firmware that is not word aligned is found, please report upstream.
-+ */
-+ for (pos = 0; pos < len; pos += sizeof(u32)) {
-+ u32 word;
-+
-+ /* FW data is always stored in little-endian */
-+ word = get_unaligned((const u32 *)(data + pos));
-+
-+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5,
-+ VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word));
-+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6,
-+ VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word));
-+
-+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1,
-+ VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE |
-+ VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE);
-+
-+ /* calculate CRC as we load data to the mailbox.
-+ * We convert word to big-endian as PHY is BE and mailbox will
-+ * return a BE CRC.
-+ */
-+ word = (__force u32)cpu_to_be32(word);
-+ crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word));
-+ }
-+
-+ up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2);
-+ if (crc != up_crc) {
-+ phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n",
-+ crc, up_crc);
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size,
-+ enum aqr_fw_src fw_src)
-+{
-+ u16 calculated_crc, read_crc, read_primary_offset;
-+ u32 iram_offset = 0, iram_size = 0;
-+ u32 dram_offset = 0, dram_size = 0;
-+ char version[VERSION_STRING_SIZE];
-+ u32 primary_offset = 0;
-+ int ret;
-+
-+ /* extract saved CRC at the end of the fw
-+ * CRC is saved in big-endian as PHY is BE
-+ */
-+ ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc);
-+ if (ret) {
-+ phydev_err(phydev, "bad firmware CRC in firmware\n");
-+ return ret;
-+ }
-+ calculated_crc = crc_ccitt_false(0, data, size - sizeof(u16));
-+ if (read_crc != calculated_crc) {
-+ phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n",
-+ read_crc, calculated_crc);
-+ return -EINVAL;
-+ }
-+
-+ /* Get the primary offset to extract DRAM and IRAM sections. */
-+ ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset);
-+ if (ret) {
-+ phydev_err(phydev, "bad primary offset in firmware\n");
-+ return ret;
-+ }
-+ primary_offset = PRIMARY_OFFSET(read_primary_offset);
-+
-+ /* Find the DRAM and IRAM sections within the firmware file.
-+ * Make sure the fw_header is correctly in the firmware.
-+ */
-+ if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET,
-+ sizeof(struct aqr_fw_header))) {
-+ phydev_err(phydev, "bad fw_header in firmware\n");
-+ return -EINVAL;
-+ }
-+
-+ /* offset are in LE and values needs to be converted to cpu endian */
-+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
-+ offsetof(struct aqr_fw_header, iram_offset),
-+ size, &iram_offset);
-+ if (ret) {
-+ phydev_err(phydev, "bad iram offset in firmware\n");
-+ return ret;
-+ }
-+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
-+ offsetof(struct aqr_fw_header, iram_size),
-+ size, &iram_size);
-+ if (ret) {
-+ phydev_err(phydev, "invalid iram size in firmware\n");
-+ return ret;
-+ }
-+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
-+ offsetof(struct aqr_fw_header, dram_offset),
-+ size, &dram_offset);
-+ if (ret) {
-+ phydev_err(phydev, "bad dram offset in firmware\n");
-+ return ret;
-+ }
-+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
-+ offsetof(struct aqr_fw_header, dram_size),
-+ size, &dram_size);
-+ if (ret) {
-+ phydev_err(phydev, "invalid dram size in firmware\n");
-+ return ret;
-+ }
-+
-+ /* Increment the offset with the primary offset.
-+ * Validate iram/dram offset and size.
-+ */
-+ iram_offset += primary_offset;
-+ if (iram_size % sizeof(u32)) {
-+ phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n");
-+ return -EINVAL;
-+ }
-+ if (!aqr_fw_validate_get(size, iram_offset, iram_size)) {
-+ phydev_err(phydev, "invalid iram offset for iram size\n");
-+ return -EINVAL;
-+ }
-+
-+ dram_offset += primary_offset;
-+ if (dram_size % sizeof(u32)) {
-+ phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n");
-+ return -EINVAL;
-+ }
-+ if (!aqr_fw_validate_get(size, dram_offset, dram_size)) {
-+ phydev_err(phydev, "invalid iram offset for iram size\n");
-+ return -EINVAL;
-+ }
-+
-+ phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n",
-+ primary_offset, iram_offset, iram_size, dram_offset, dram_size);
-+
-+ if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET,
-+ VERSION_STRING_SIZE)) {
-+ phydev_err(phydev, "invalid version in firmware\n");
-+ return -EINVAL;
-+ }
-+ strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET,
-+ VERSION_STRING_SIZE);
-+ if (version[0] == '\0') {
-+ phydev_err(phydev, "invalid version in firmware\n");
-+ return -EINVAL;
-+ }
-+ phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version,
-+ aqr_fw_src_string[fw_src]);
-+
-+ /* stall the microcprocessor */
-+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
-+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
-+
-+ phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n",
-+ DRAM_BASE_ADDR, dram_offset, dram_size);
-+ ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset,
-+ dram_size);
-+ if (ret)
-+ return ret;
-+
-+ phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n",
-+ IRAM_BASE_ADDR, iram_offset, iram_size);
-+ ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset,
-+ iram_size);
-+ if (ret)
-+ return ret;
-+
-+ /* make sure soft reset and low power mode are clear */
-+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC,
-+ VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER);
-+
-+ /* Release the microprocessor. UP_RESET must be held for 100 usec. */
-+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
-+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL |
-+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD |
-+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST);
-+ usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2);
-+
-+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
-+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
-+
-+ return 0;
-+}
-+
-+static int aqr_firmware_load_nvmem(struct phy_device *phydev)
-+{
-+ struct nvmem_cell *cell;
-+ size_t size;
-+ u8 *buf;
-+ int ret;
-+
-+ cell = nvmem_cell_get(&phydev->mdio.dev, "firmware");
-+ if (IS_ERR(cell))
-+ return PTR_ERR(cell);
-+
-+ buf = nvmem_cell_read(cell, &size);
-+ if (IS_ERR(buf)) {
-+ ret = PTR_ERR(buf);
-+ goto exit;
-+ }
-+
-+ ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM);
-+ if (ret)
-+ phydev_err(phydev, "firmware loading failed: %d\n", ret);
-+
-+ kfree(buf);
-+exit:
-+ nvmem_cell_put(cell);
-+
-+ return ret;
-+}
-+
-+static int aqr_firmware_load_fs(struct phy_device *phydev)
-+{
-+ struct device *dev = &phydev->mdio.dev;
-+ const struct firmware *fw;
-+ const char *fw_name;
-+ int ret;
-+
-+ ret = of_property_read_string(dev->of_node, "firmware-name",
-+ &fw_name);
-+ if (ret)
-+ return ret;
-+
-+ ret = request_firmware(&fw, fw_name, dev);
-+ if (ret) {
-+ phydev_err(phydev, "failed to find FW file %s (%d)\n",
-+ fw_name, ret);
-+ return ret;
-+ }
-+
-+ ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS);
-+ if (ret)
-+ phydev_err(phydev, "firmware loading failed: %d\n", ret);
-+
-+ release_firmware(fw);
-+
-+ return ret;
-+}
-+
-+int aqr_firmware_load(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ /* Check if the firmware is not already loaded by pooling
-+ * the current version returned by the PHY. If 0 is returned,
-+ * no firmware is loaded.
-+ */
-+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID);
-+ if (ret > 0)
-+ goto exit;
-+
-+ ret = aqr_firmware_load_nvmem(phydev);
-+ if (!ret)
-+ goto exit;
-+
-+ ret = aqr_firmware_load_fs(phydev);
-+ if (ret)
-+ return ret;
-+
-+exit:
-+ return 0;
-+}
---- a/drivers/net/phy/aquantia/aquantia_main.c
-+++ b/drivers/net/phy/aquantia/aquantia_main.c
-@@ -658,11 +658,17 @@ static int aqr107_resume(struct phy_devi
-
- static int aqr107_probe(struct phy_device *phydev)
- {
-+ int ret;
-+
- phydev->priv = devm_kzalloc(&phydev->mdio.dev,
- sizeof(struct aqr107_priv), GFP_KERNEL);
- if (!phydev->priv)
- return -ENOMEM;
-
-+ ret = aqr_firmware_load(phydev);
-+ if (ret)
-+ return ret;
-+
- return aqr_hwmon_probe(phydev);
- }
-
+++ /dev/null
-From 6a3b8c573b5a152a6aa7a0b54c5e18b84c6ba6f5 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:51:49 +0100
-Subject: [PATCH 02/13] net: phy: at803x: move disable WOL to specific at8031
- probe
-
-Move the WOL disable call to specific at8031 probe to make at803x_probe
-more generic and drop extra check for PHY ID.
-
-Keep the same previous behaviour by first calling at803x_probe and then
-disabling WOL.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 27 +++++++++++++++++----------
- 1 file changed, 17 insertions(+), 10 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -886,15 +886,6 @@ static int at803x_probe(struct phy_devic
- priv->is_fiber = true;
- break;
- }
--
-- /* Disable WoL in 1588 register which is enabled
-- * by default
-- */
-- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
-- AT803X_PHY_MMD3_WOL_CTRL,
-- AT803X_WOL_EN, 0);
-- if (ret)
-- return ret;
- }
-
- return 0;
-@@ -1591,6 +1582,22 @@ static int at803x_cable_test_start(struc
- return 0;
- }
-
-+static int at8031_probe(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = at803x_probe(phydev);
-+ if (ret)
-+ return ret;
-+
-+ /* Disable WoL in 1588 register which is enabled
-+ * by default
-+ */
-+ return phy_modify_mmd(phydev, MDIO_MMD_PCS,
-+ AT803X_PHY_MMD3_WOL_CTRL,
-+ AT803X_WOL_EN, 0);
-+}
-+
- static int qca83xx_config_init(struct phy_device *phydev)
- {
- u8 switch_revision;
-@@ -2092,7 +2099,7 @@ static struct phy_driver at803x_driver[]
- PHY_ID_MATCH_EXACT(ATH8031_PHY_ID),
- .name = "Qualcomm Atheros AR8031/AR8033",
- .flags = PHY_POLL_CABLE_TEST,
-- .probe = at803x_probe,
-+ .probe = at8031_probe,
- .config_init = at803x_config_init,
- .config_aneg = at803x_config_aneg,
- .soft_reset = genphy_soft_reset,
+++ /dev/null
-From 07b1ad83b9ed6db1735ba10baf67b7a565ac0cef Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:51:50 +0100
-Subject: [PATCH 03/13] net: phy: at803x: raname hw_stats functions to qca83xx
- specific name
-
-The function and the struct related to hw_stats were specific to qca83xx
-PHY but were called following the convention in the driver of calling
-everything with at803x prefix.
-
-To better organize the code, rename these function a more specific name
-to better describe that they are specific to 83xx PHY family.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 44 ++++++++++++++++++++--------------------
- 1 file changed, 22 insertions(+), 22 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -295,7 +295,7 @@ struct at803x_hw_stat {
- enum stat_access_type access_type;
- };
-
--static struct at803x_hw_stat at803x_hw_stats[] = {
-+static struct at803x_hw_stat qca83xx_hw_stats[] = {
- { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
- { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
- { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
-@@ -311,7 +311,7 @@ struct at803x_priv {
- bool is_1000basex;
- struct regulator_dev *vddio_rdev;
- struct regulator_dev *vddh_rdev;
-- u64 stats[ARRAY_SIZE(at803x_hw_stats)];
-+ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
- };
-
- struct at803x_context {
-@@ -529,24 +529,24 @@ static void at803x_get_wol(struct phy_de
- wol->wolopts |= WAKE_MAGIC;
- }
-
--static int at803x_get_sset_count(struct phy_device *phydev)
-+static int qca83xx_get_sset_count(struct phy_device *phydev)
- {
-- return ARRAY_SIZE(at803x_hw_stats);
-+ return ARRAY_SIZE(qca83xx_hw_stats);
- }
-
--static void at803x_get_strings(struct phy_device *phydev, u8 *data)
-+static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
- {
- int i;
-
-- for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) {
-+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
- strscpy(data + i * ETH_GSTRING_LEN,
-- at803x_hw_stats[i].string, ETH_GSTRING_LEN);
-+ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
- }
- }
-
--static u64 at803x_get_stat(struct phy_device *phydev, int i)
-+static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
- {
-- struct at803x_hw_stat stat = at803x_hw_stats[i];
-+ struct at803x_hw_stat stat = qca83xx_hw_stats[i];
- struct at803x_priv *priv = phydev->priv;
- int val;
- u64 ret;
-@@ -567,13 +567,13 @@ static u64 at803x_get_stat(struct phy_de
- return ret;
- }
-
--static void at803x_get_stats(struct phy_device *phydev,
-- struct ethtool_stats *stats, u64 *data)
-+static void qca83xx_get_stats(struct phy_device *phydev,
-+ struct ethtool_stats *stats, u64 *data)
- {
- int i;
-
-- for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++)
-- data[i] = at803x_get_stat(phydev, i);
-+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
-+ data[i] = qca83xx_get_stat(phydev, i);
- }
-
- static int at803x_suspend(struct phy_device *phydev)
-@@ -2175,9 +2175,9 @@ static struct phy_driver at803x_driver[]
- .flags = PHY_IS_INTERNAL,
- .config_init = qca83xx_config_init,
- .soft_reset = genphy_soft_reset,
-- .get_sset_count = at803x_get_sset_count,
-- .get_strings = at803x_get_strings,
-- .get_stats = at803x_get_stats,
-+ .get_sset_count = qca83xx_get_sset_count,
-+ .get_strings = qca83xx_get_strings,
-+ .get_stats = qca83xx_get_stats,
- .suspend = qca83xx_suspend,
- .resume = qca83xx_resume,
- }, {
-@@ -2191,9 +2191,9 @@ static struct phy_driver at803x_driver[]
- .flags = PHY_IS_INTERNAL,
- .config_init = qca83xx_config_init,
- .soft_reset = genphy_soft_reset,
-- .get_sset_count = at803x_get_sset_count,
-- .get_strings = at803x_get_strings,
-- .get_stats = at803x_get_stats,
-+ .get_sset_count = qca83xx_get_sset_count,
-+ .get_strings = qca83xx_get_strings,
-+ .get_stats = qca83xx_get_stats,
- .suspend = qca83xx_suspend,
- .resume = qca83xx_resume,
- }, {
-@@ -2207,9 +2207,9 @@ static struct phy_driver at803x_driver[]
- .flags = PHY_IS_INTERNAL,
- .config_init = qca83xx_config_init,
- .soft_reset = genphy_soft_reset,
-- .get_sset_count = at803x_get_sset_count,
-- .get_strings = at803x_get_strings,
-- .get_stats = at803x_get_stats,
-+ .get_sset_count = qca83xx_get_sset_count,
-+ .get_strings = qca83xx_get_strings,
-+ .get_stats = qca83xx_get_stats,
- .suspend = qca83xx_suspend,
- .resume = qca83xx_resume,
- }, {
+++ /dev/null
-From d43cff3f82336c0bd965ea552232d9f4ddac71a6 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:51:51 +0100
-Subject: [PATCH 04/13] net: phy: at803x: move qca83xx specific check in
- dedicated functions
-
-Rework qca83xx specific check to dedicated function to tidy things up
-and drop useless phy_id check.
-
-Also drop an useless link_change_notify for QCA8337 as it did nothing an
-returned early.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 68 ++++++++++++++++++++++------------------
- 1 file changed, 37 insertions(+), 31 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -1623,27 +1623,26 @@ static int qca83xx_config_init(struct ph
- break;
- }
-
-+ /* Following original QCA sourcecode set port to prefer master */
-+ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
-+
-+ return 0;
-+}
-+
-+static int qca8327_config_init(struct phy_device *phydev)
-+{
- /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
- * Disable on init and enable only with 100m speed following
- * qca original source code.
- */
-- if (phydev->drv->phy_id == QCA8327_A_PHY_ID ||
-- phydev->drv->phy_id == QCA8327_B_PHY_ID)
-- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-- QCA8327_DEBUG_MANU_CTRL_EN, 0);
-+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
-
-- /* Following original QCA sourcecode set port to prefer master */
-- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
--
-- return 0;
-+ return qca83xx_config_init(phydev);
- }
-
- static void qca83xx_link_change_notify(struct phy_device *phydev)
- {
-- /* QCA8337 doesn't require DAC Amplitude adjustement */
-- if (phydev->drv->phy_id == QCA8337_PHY_ID)
-- return;
--
- /* Set DAC Amplitude adjustment to +6% for 100m on link running */
- if (phydev->state == PHY_RUNNING) {
- if (phydev->speed == SPEED_100)
-@@ -1686,19 +1685,6 @@ static int qca83xx_resume(struct phy_dev
-
- static int qca83xx_suspend(struct phy_device *phydev)
- {
-- u16 mask = 0;
--
-- /* Only QCA8337 support actual suspend.
-- * QCA8327 cause port unreliability when phy suspend
-- * is set.
-- */
-- if (phydev->drv->phy_id == QCA8337_PHY_ID) {
-- genphy_suspend(phydev);
-- } else {
-- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
-- phy_modify(phydev, MII_BMCR, mask, 0);
-- }
--
- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
- AT803X_DEBUG_GATE_CLK_IN1000, 0);
-
-@@ -1709,6 +1695,27 @@ static int qca83xx_suspend(struct phy_de
- return 0;
- }
-
-+static int qca8337_suspend(struct phy_device *phydev)
-+{
-+ /* Only QCA8337 support actual suspend. */
-+ genphy_suspend(phydev);
-+
-+ return qca83xx_suspend(phydev);
-+}
-+
-+static int qca8327_suspend(struct phy_device *phydev)
-+{
-+ u16 mask = 0;
-+
-+ /* QCA8327 cause port unreliability when phy suspend
-+ * is set.
-+ */
-+ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
-+ phy_modify(phydev, MII_BMCR, mask, 0);
-+
-+ return qca83xx_suspend(phydev);
-+}
-+
- static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
- {
- int ret;
-@@ -2170,7 +2177,6 @@ static struct phy_driver at803x_driver[]
- .phy_id_mask = QCA8K_PHY_ID_MASK,
- .name = "Qualcomm Atheros 8337 internal PHY",
- /* PHY_GBIT_FEATURES */
-- .link_change_notify = qca83xx_link_change_notify,
- .probe = at803x_probe,
- .flags = PHY_IS_INTERNAL,
- .config_init = qca83xx_config_init,
-@@ -2178,7 +2184,7 @@ static struct phy_driver at803x_driver[]
- .get_sset_count = qca83xx_get_sset_count,
- .get_strings = qca83xx_get_strings,
- .get_stats = qca83xx_get_stats,
-- .suspend = qca83xx_suspend,
-+ .suspend = qca8337_suspend,
- .resume = qca83xx_resume,
- }, {
- /* QCA8327-A from switch QCA8327-AL1A */
-@@ -2189,12 +2195,12 @@ static struct phy_driver at803x_driver[]
- .link_change_notify = qca83xx_link_change_notify,
- .probe = at803x_probe,
- .flags = PHY_IS_INTERNAL,
-- .config_init = qca83xx_config_init,
-+ .config_init = qca8327_config_init,
- .soft_reset = genphy_soft_reset,
- .get_sset_count = qca83xx_get_sset_count,
- .get_strings = qca83xx_get_strings,
- .get_stats = qca83xx_get_stats,
-- .suspend = qca83xx_suspend,
-+ .suspend = qca8327_suspend,
- .resume = qca83xx_resume,
- }, {
- /* QCA8327-B from switch QCA8327-BL1A */
-@@ -2205,12 +2211,12 @@ static struct phy_driver at803x_driver[]
- .link_change_notify = qca83xx_link_change_notify,
- .probe = at803x_probe,
- .flags = PHY_IS_INTERNAL,
-- .config_init = qca83xx_config_init,
-+ .config_init = qca8327_config_init,
- .soft_reset = genphy_soft_reset,
- .get_sset_count = qca83xx_get_sset_count,
- .get_strings = qca83xx_get_strings,
- .get_stats = qca83xx_get_stats,
-- .suspend = qca83xx_suspend,
-+ .suspend = qca8327_suspend,
- .resume = qca83xx_resume,
- }, {
- /* Qualcomm QCA8081 */
+++ /dev/null
-From 900eef75cc5018e149c52fe305c9c3fe424c52a7 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:51:52 +0100
-Subject: [PATCH 05/13] net: phy: at803x: move specific DT option for at8031 to
- specific probe
-
-Move specific DT options for at8031 to specific probe to tidy things up
-and make at803x_parse_dt more generic.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 55 ++++++++++++++++++++++------------------
- 1 file changed, 31 insertions(+), 24 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -825,30 +825,6 @@ static int at803x_parse_dt(struct phy_de
- }
- }
-
-- /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
-- * options.
-- */
-- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
-- if (of_property_read_bool(node, "qca,keep-pll-enabled"))
-- priv->flags |= AT803X_KEEP_PLL_ENABLED;
--
-- ret = at8031_register_regulators(phydev);
-- if (ret < 0)
-- return ret;
--
-- ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
-- "vddio");
-- if (ret) {
-- phydev_err(phydev, "failed to get VDDIO regulator\n");
-- return ret;
-- }
--
-- /* Only AR8031/8033 support 1000Base-X for SFP modules */
-- ret = phy_sfp_probe(phydev, &at803x_sfp_ops);
-- if (ret < 0)
-- return ret;
-- }
--
- return 0;
- }
-
-@@ -1582,6 +1558,30 @@ static int at803x_cable_test_start(struc
- return 0;
- }
-
-+static int at8031_parse_dt(struct phy_device *phydev)
-+{
-+ struct device_node *node = phydev->mdio.dev.of_node;
-+ struct at803x_priv *priv = phydev->priv;
-+ int ret;
-+
-+ if (of_property_read_bool(node, "qca,keep-pll-enabled"))
-+ priv->flags |= AT803X_KEEP_PLL_ENABLED;
-+
-+ ret = at8031_register_regulators(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
-+ "vddio");
-+ if (ret) {
-+ phydev_err(phydev, "failed to get VDDIO regulator\n");
-+ return ret;
-+ }
-+
-+ /* Only AR8031/8033 support 1000Base-X for SFP modules */
-+ return phy_sfp_probe(phydev, &at803x_sfp_ops);
-+}
-+
- static int at8031_probe(struct phy_device *phydev)
- {
- int ret;
-@@ -1590,6 +1590,13 @@ static int at8031_probe(struct phy_devic
- if (ret)
- return ret;
-
-+ /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
-+ * options.
-+ */
-+ ret = at8031_parse_dt(phydev);
-+ if (ret)
-+ return ret;
-+
- /* Disable WoL in 1588 register which is enabled
- * by default
- */
+++ /dev/null
-From 25d2ba94005fac18fe68878cddff59a67e115554 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:51:53 +0100
-Subject: [PATCH 06/13] net: phy: at803x: move specific at8031 probe mode check
- to dedicated probe
-
-Move specific at8031 probe mode check to dedicated probe to make
-at803x_probe more generic and keep code tidy.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 39 +++++++++++++++++++--------------------
- 1 file changed, 19 insertions(+), 20 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -844,26 +844,6 @@ static int at803x_probe(struct phy_devic
- if (ret)
- return ret;
-
-- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
-- int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
-- int mode_cfg;
--
-- if (ccr < 0)
-- return ccr;
-- mode_cfg = ccr & AT803X_MODE_CFG_MASK;
--
-- switch (mode_cfg) {
-- case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
-- case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
-- priv->is_1000basex = true;
-- fallthrough;
-- case AT803X_MODE_CFG_FX100_RGMII_50OHM:
-- case AT803X_MODE_CFG_FX100_RGMII_75OHM:
-- priv->is_fiber = true;
-- break;
-- }
-- }
--
- return 0;
- }
-
-@@ -1584,6 +1564,9 @@ static int at8031_parse_dt(struct phy_de
-
- static int at8031_probe(struct phy_device *phydev)
- {
-+ struct at803x_priv *priv = phydev->priv;
-+ int mode_cfg;
-+ int ccr;
- int ret;
-
- ret = at803x_probe(phydev);
-@@ -1597,6 +1580,22 @@ static int at8031_probe(struct phy_devic
- if (ret)
- return ret;
-
-+ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
-+ if (ccr < 0)
-+ return ccr;
-+ mode_cfg = ccr & AT803X_MODE_CFG_MASK;
-+
-+ switch (mode_cfg) {
-+ case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
-+ case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
-+ priv->is_1000basex = true;
-+ fallthrough;
-+ case AT803X_MODE_CFG_FX100_RGMII_50OHM:
-+ case AT803X_MODE_CFG_FX100_RGMII_75OHM:
-+ priv->is_fiber = true;
-+ break;
-+ }
-+
- /* Disable WoL in 1588 register which is enabled
- * by default
- */
+++ /dev/null
-From 3ae3bc426eaf57ca8f53d75777d9a5ef779bc7b7 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:51:54 +0100
-Subject: [PATCH 07/13] net: phy: at803x: move specific at8031 config_init to
- dedicated function
-
-Move specific at8031 config_init to dedicated function to make
-at803x_config_init more generic and tidy things up.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 45 ++++++++++++++++++++++------------------
- 1 file changed, 25 insertions(+), 20 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -951,27 +951,8 @@ static int at803x_hibernation_mode_confi
-
- static int at803x_config_init(struct phy_device *phydev)
- {
-- struct at803x_priv *priv = phydev->priv;
- int ret;
-
-- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
-- /* Some bootloaders leave the fiber page selected.
-- * Switch to the appropriate page (fiber or copper), as otherwise we
-- * read the PHY capabilities from the wrong page.
-- */
-- phy_lock_mdio_bus(phydev);
-- ret = at803x_write_page(phydev,
-- priv->is_fiber ? AT803X_PAGE_FIBER :
-- AT803X_PAGE_COPPER);
-- phy_unlock_mdio_bus(phydev);
-- if (ret)
-- return ret;
--
-- ret = at8031_pll_config(phydev);
-- if (ret < 0)
-- return ret;
-- }
--
- /* The RX and TX delay default is:
- * after HW reset: RX delay enabled and TX delay disabled
- * after SW reset: RX delay enabled, while TX delay retains the
-@@ -1604,6 +1585,30 @@ static int at8031_probe(struct phy_devic
- AT803X_WOL_EN, 0);
- }
-
-+static int at8031_config_init(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ int ret;
-+
-+ /* Some bootloaders leave the fiber page selected.
-+ * Switch to the appropriate page (fiber or copper), as otherwise we
-+ * read the PHY capabilities from the wrong page.
-+ */
-+ phy_lock_mdio_bus(phydev);
-+ ret = at803x_write_page(phydev,
-+ priv->is_fiber ? AT803X_PAGE_FIBER :
-+ AT803X_PAGE_COPPER);
-+ phy_unlock_mdio_bus(phydev);
-+ if (ret)
-+ return ret;
-+
-+ ret = at8031_pll_config(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ return at803x_config_init(phydev);
-+}
-+
- static int qca83xx_config_init(struct phy_device *phydev)
- {
- u8 switch_revision;
-@@ -2113,7 +2118,7 @@ static struct phy_driver at803x_driver[]
- .name = "Qualcomm Atheros AR8031/AR8033",
- .flags = PHY_POLL_CABLE_TEST,
- .probe = at8031_probe,
-- .config_init = at803x_config_init,
-+ .config_init = at8031_config_init,
- .config_aneg = at803x_config_aneg,
- .soft_reset = genphy_soft_reset,
- .set_wol = at803x_set_wol,
+++ /dev/null
-From 27b89c9dc1b0393090d68d651b82f30ad2696baa Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:51:55 +0100
-Subject: [PATCH 08/13] net: phy: at803x: move specific at8031 WOL bits to
- dedicated function
-
-Move specific at8031 WOL enable/disable to dedicated function to make
-at803x_set_wol more generic.
-
-This is needed in preparation for PHY driver split as qca8081 share the
-same function to toggle WOL settings.
-
-In this new implementation WOL module in at8031 is enabled after the
-generic interrupt is setup. This should not cause any problem as the
-WOL_INT has a separate implementation and only relay on MAC bits.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 42 ++++++++++++++++++++++++----------------
- 1 file changed, 25 insertions(+), 17 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -466,27 +466,11 @@ static int at803x_set_wol(struct phy_dev
- phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
- mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
-
-- /* Enable WOL function for 1588 */
-- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
-- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
-- AT803X_PHY_MMD3_WOL_CTRL,
-- 0, AT803X_WOL_EN);
-- if (ret)
-- return ret;
-- }
- /* Enable WOL interrupt */
- ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
- if (ret)
- return ret;
- } else {
-- /* Disable WoL function for 1588 */
-- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
-- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
-- AT803X_PHY_MMD3_WOL_CTRL,
-- AT803X_WOL_EN, 0);
-- if (ret)
-- return ret;
-- }
- /* Disable WOL interrupt */
- ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
- if (ret)
-@@ -1609,6 +1593,30 @@ static int at8031_config_init(struct phy
- return at803x_config_init(phydev);
- }
-
-+static int at8031_set_wol(struct phy_device *phydev,
-+ struct ethtool_wolinfo *wol)
-+{
-+ int ret;
-+
-+ /* First setup MAC address and enable WOL interrupt */
-+ ret = at803x_set_wol(phydev, wol);
-+ if (ret)
-+ return ret;
-+
-+ if (wol->wolopts & WAKE_MAGIC)
-+ /* Enable WOL function for 1588 */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
-+ AT803X_PHY_MMD3_WOL_CTRL,
-+ 0, AT803X_WOL_EN);
-+ else
-+ /* Disable WoL function for 1588 */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
-+ AT803X_PHY_MMD3_WOL_CTRL,
-+ AT803X_WOL_EN, 0);
-+
-+ return ret;
-+}
-+
- static int qca83xx_config_init(struct phy_device *phydev)
- {
- u8 switch_revision;
-@@ -2121,7 +2129,7 @@ static struct phy_driver at803x_driver[]
- .config_init = at8031_config_init,
- .config_aneg = at803x_config_aneg,
- .soft_reset = genphy_soft_reset,
-- .set_wol = at803x_set_wol,
-+ .set_wol = at8031_set_wol,
- .get_wol = at803x_get_wol,
- .suspend = at803x_suspend,
- .resume = at803x_resume,
+++ /dev/null
-From 30dd62191d3dd97c08f7f9dc9ce77ffab457e4fb Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:51:56 +0100
-Subject: [PATCH 09/13] net: phy: at803x: move specific at8031 config_intr to
- dedicated function
-
-Move specific at8031 config_intr bits to dedicated function to make
-at803x_config_initr more generic.
-
-This is needed in preparation for PHY driver split as qca8081 share the
-same function to setup interrupts.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 30 ++++++++++++++++++++++++------
- 1 file changed, 24 insertions(+), 6 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -990,7 +990,6 @@ static int at803x_ack_interrupt(struct p
-
- static int at803x_config_intr(struct phy_device *phydev)
- {
-- struct at803x_priv *priv = phydev->priv;
- int err;
- int value;
-
-@@ -1007,10 +1006,6 @@ static int at803x_config_intr(struct phy
- value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
- value |= AT803X_INTR_ENABLE_LINK_FAIL;
- value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
-- if (priv->is_fiber) {
-- value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
-- value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
-- }
-
- err = phy_write(phydev, AT803X_INTR_ENABLE, value);
- } else {
-@@ -1617,6 +1612,29 @@ static int at8031_set_wol(struct phy_dev
- return ret;
- }
-
-+static int at8031_config_intr(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ int err, value = 0;
-+
-+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED &&
-+ priv->is_fiber) {
-+ /* Clear any pending interrupts */
-+ err = at803x_ack_interrupt(phydev);
-+ if (err)
-+ return err;
-+
-+ value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
-+ value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
-+
-+ err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value);
-+ if (err)
-+ return err;
-+ }
-+
-+ return at803x_config_intr(phydev);
-+}
-+
- static int qca83xx_config_init(struct phy_device *phydev)
- {
- u8 switch_revision;
-@@ -2137,7 +2155,7 @@ static struct phy_driver at803x_driver[]
- .write_page = at803x_write_page,
- .get_features = at803x_get_features,
- .read_status = at803x_read_status,
-- .config_intr = at803x_config_intr,
-+ .config_intr = at8031_config_intr,
- .handle_interrupt = at803x_handle_interrupt,
- .get_tunable = at803x_get_tunable,
- .set_tunable = at803x_set_tunable,
+++ /dev/null
-From a5ab9d8e7ae0da8328ac1637a9755311508dc8ab Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:51:57 +0100
-Subject: [PATCH 10/13] net: phy: at803x: make at8031 related DT functions name
- more specific
-
-Rename at8031 related DT function name to a more specific name
-referencing they are only related to at8031 and not to the generic
-at803x PHY family.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 16 ++++++++--------
- 1 file changed, 8 insertions(+), 8 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -583,7 +583,7 @@ static int at803x_resume(struct phy_devi
- return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
- }
-
--static int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
-+static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
- unsigned int selector)
- {
- struct phy_device *phydev = rdev_get_drvdata(rdev);
-@@ -596,7 +596,7 @@ static int at803x_rgmii_reg_set_voltage_
- AT803X_DEBUG_RGMII_1V8, 0);
- }
-
--static int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
-+static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
- {
- struct phy_device *phydev = rdev_get_drvdata(rdev);
- int val;
-@@ -610,8 +610,8 @@ static int at803x_rgmii_reg_get_voltage_
-
- static const struct regulator_ops vddio_regulator_ops = {
- .list_voltage = regulator_list_voltage_table,
-- .set_voltage_sel = at803x_rgmii_reg_set_voltage_sel,
-- .get_voltage_sel = at803x_rgmii_reg_get_voltage_sel,
-+ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
-+ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
- };
-
- static const unsigned int vddio_voltage_table[] = {
-@@ -666,7 +666,7 @@ static int at8031_register_regulators(st
- return 0;
- }
-
--static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
-+static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
- {
- struct phy_device *phydev = upstream;
- __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
-@@ -710,10 +710,10 @@ static int at803x_sfp_insert(void *upstr
- return 0;
- }
-
--static const struct sfp_upstream_ops at803x_sfp_ops = {
-+static const struct sfp_upstream_ops at8031_sfp_ops = {
- .attach = phy_sfp_attach,
- .detach = phy_sfp_detach,
-- .module_insert = at803x_sfp_insert,
-+ .module_insert = at8031_sfp_insert,
- };
-
- static int at803x_parse_dt(struct phy_device *phydev)
-@@ -1519,7 +1519,7 @@ static int at8031_parse_dt(struct phy_de
- }
-
- /* Only AR8031/8033 support 1000Base-X for SFP modules */
-- return phy_sfp_probe(phydev, &at803x_sfp_ops);
-+ return phy_sfp_probe(phydev, &at8031_sfp_ops);
- }
-
- static int at8031_probe(struct phy_device *phydev)
+++ /dev/null
-From f932a6dc8bae0dae9645b5b1b4c65aed8a8acb2a Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:51:58 +0100
-Subject: [PATCH 11/13] net: phy: at803x: move at8031 functions in dedicated
- section
-
-Move at8031 functions in dedicated section with dedicated at8031
-parse_dt and probe.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 266 +++++++++++++++++++--------------------
- 1 file changed, 133 insertions(+), 133 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -583,139 +583,6 @@ static int at803x_resume(struct phy_devi
- return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
- }
-
--static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
-- unsigned int selector)
--{
-- struct phy_device *phydev = rdev_get_drvdata(rdev);
--
-- if (selector)
-- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-- 0, AT803X_DEBUG_RGMII_1V8);
-- else
-- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-- AT803X_DEBUG_RGMII_1V8, 0);
--}
--
--static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
--{
-- struct phy_device *phydev = rdev_get_drvdata(rdev);
-- int val;
--
-- val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
-- if (val < 0)
-- return val;
--
-- return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
--}
--
--static const struct regulator_ops vddio_regulator_ops = {
-- .list_voltage = regulator_list_voltage_table,
-- .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
-- .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
--};
--
--static const unsigned int vddio_voltage_table[] = {
-- 1500000,
-- 1800000,
--};
--
--static const struct regulator_desc vddio_desc = {
-- .name = "vddio",
-- .of_match = of_match_ptr("vddio-regulator"),
-- .n_voltages = ARRAY_SIZE(vddio_voltage_table),
-- .volt_table = vddio_voltage_table,
-- .ops = &vddio_regulator_ops,
-- .type = REGULATOR_VOLTAGE,
-- .owner = THIS_MODULE,
--};
--
--static const struct regulator_ops vddh_regulator_ops = {
--};
--
--static const struct regulator_desc vddh_desc = {
-- .name = "vddh",
-- .of_match = of_match_ptr("vddh-regulator"),
-- .n_voltages = 1,
-- .fixed_uV = 2500000,
-- .ops = &vddh_regulator_ops,
-- .type = REGULATOR_VOLTAGE,
-- .owner = THIS_MODULE,
--};
--
--static int at8031_register_regulators(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
-- struct device *dev = &phydev->mdio.dev;
-- struct regulator_config config = { };
--
-- config.dev = dev;
-- config.driver_data = phydev;
--
-- priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
-- if (IS_ERR(priv->vddio_rdev)) {
-- phydev_err(phydev, "failed to register VDDIO regulator\n");
-- return PTR_ERR(priv->vddio_rdev);
-- }
--
-- priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
-- if (IS_ERR(priv->vddh_rdev)) {
-- phydev_err(phydev, "failed to register VDDH regulator\n");
-- return PTR_ERR(priv->vddh_rdev);
-- }
--
-- return 0;
--}
--
--static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
--{
-- struct phy_device *phydev = upstream;
-- __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
-- __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
-- DECLARE_PHY_INTERFACE_MASK(interfaces);
-- phy_interface_t iface;
--
-- linkmode_zero(phy_support);
-- phylink_set(phy_support, 1000baseX_Full);
-- phylink_set(phy_support, 1000baseT_Full);
-- phylink_set(phy_support, Autoneg);
-- phylink_set(phy_support, Pause);
-- phylink_set(phy_support, Asym_Pause);
--
-- linkmode_zero(sfp_support);
-- sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
-- /* Some modules support 10G modes as well as others we support.
-- * Mask out non-supported modes so the correct interface is picked.
-- */
-- linkmode_and(sfp_support, phy_support, sfp_support);
--
-- if (linkmode_empty(sfp_support)) {
-- dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
-- return -EINVAL;
-- }
--
-- iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
--
-- /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
-- * interface for use with SFP modules.
-- * However, some copper modules detected as having a preferred SGMII
-- * interface do default to and function in 1000Base-X mode, so just
-- * print a warning and allow such modules, as they may have some chance
-- * of working.
-- */
-- if (iface == PHY_INTERFACE_MODE_SGMII)
-- dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
-- else if (iface != PHY_INTERFACE_MODE_1000BASEX)
-- return -EINVAL;
--
-- return 0;
--}
--
--static const struct sfp_upstream_ops at8031_sfp_ops = {
-- .attach = phy_sfp_attach,
-- .detach = phy_sfp_detach,
-- .module_insert = at8031_sfp_insert,
--};
--
- static int at803x_parse_dt(struct phy_device *phydev)
- {
- struct device_node *node = phydev->mdio.dev.of_node;
-@@ -1498,6 +1365,139 @@ static int at803x_cable_test_start(struc
- return 0;
- }
-
-+static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
-+ unsigned int selector)
-+{
-+ struct phy_device *phydev = rdev_get_drvdata(rdev);
-+
-+ if (selector)
-+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-+ 0, AT803X_DEBUG_RGMII_1V8);
-+ else
-+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-+ AT803X_DEBUG_RGMII_1V8, 0);
-+}
-+
-+static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
-+{
-+ struct phy_device *phydev = rdev_get_drvdata(rdev);
-+ int val;
-+
-+ val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
-+ if (val < 0)
-+ return val;
-+
-+ return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
-+}
-+
-+static const struct regulator_ops vddio_regulator_ops = {
-+ .list_voltage = regulator_list_voltage_table,
-+ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
-+ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
-+};
-+
-+static const unsigned int vddio_voltage_table[] = {
-+ 1500000,
-+ 1800000,
-+};
-+
-+static const struct regulator_desc vddio_desc = {
-+ .name = "vddio",
-+ .of_match = of_match_ptr("vddio-regulator"),
-+ .n_voltages = ARRAY_SIZE(vddio_voltage_table),
-+ .volt_table = vddio_voltage_table,
-+ .ops = &vddio_regulator_ops,
-+ .type = REGULATOR_VOLTAGE,
-+ .owner = THIS_MODULE,
-+};
-+
-+static const struct regulator_ops vddh_regulator_ops = {
-+};
-+
-+static const struct regulator_desc vddh_desc = {
-+ .name = "vddh",
-+ .of_match = of_match_ptr("vddh-regulator"),
-+ .n_voltages = 1,
-+ .fixed_uV = 2500000,
-+ .ops = &vddh_regulator_ops,
-+ .type = REGULATOR_VOLTAGE,
-+ .owner = THIS_MODULE,
-+};
-+
-+static int at8031_register_regulators(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ struct device *dev = &phydev->mdio.dev;
-+ struct regulator_config config = { };
-+
-+ config.dev = dev;
-+ config.driver_data = phydev;
-+
-+ priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
-+ if (IS_ERR(priv->vddio_rdev)) {
-+ phydev_err(phydev, "failed to register VDDIO regulator\n");
-+ return PTR_ERR(priv->vddio_rdev);
-+ }
-+
-+ priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
-+ if (IS_ERR(priv->vddh_rdev)) {
-+ phydev_err(phydev, "failed to register VDDH regulator\n");
-+ return PTR_ERR(priv->vddh_rdev);
-+ }
-+
-+ return 0;
-+}
-+
-+static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
-+{
-+ struct phy_device *phydev = upstream;
-+ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
-+ __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
-+ DECLARE_PHY_INTERFACE_MASK(interfaces);
-+ phy_interface_t iface;
-+
-+ linkmode_zero(phy_support);
-+ phylink_set(phy_support, 1000baseX_Full);
-+ phylink_set(phy_support, 1000baseT_Full);
-+ phylink_set(phy_support, Autoneg);
-+ phylink_set(phy_support, Pause);
-+ phylink_set(phy_support, Asym_Pause);
-+
-+ linkmode_zero(sfp_support);
-+ sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
-+ /* Some modules support 10G modes as well as others we support.
-+ * Mask out non-supported modes so the correct interface is picked.
-+ */
-+ linkmode_and(sfp_support, phy_support, sfp_support);
-+
-+ if (linkmode_empty(sfp_support)) {
-+ dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
-+ return -EINVAL;
-+ }
-+
-+ iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
-+
-+ /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
-+ * interface for use with SFP modules.
-+ * However, some copper modules detected as having a preferred SGMII
-+ * interface do default to and function in 1000Base-X mode, so just
-+ * print a warning and allow such modules, as they may have some chance
-+ * of working.
-+ */
-+ if (iface == PHY_INTERFACE_MODE_SGMII)
-+ dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
-+ else if (iface != PHY_INTERFACE_MODE_1000BASEX)
-+ return -EINVAL;
-+
-+ return 0;
-+}
-+
-+static const struct sfp_upstream_ops at8031_sfp_ops = {
-+ .attach = phy_sfp_attach,
-+ .detach = phy_sfp_detach,
-+ .module_insert = at8031_sfp_insert,
-+};
-+
- static int at8031_parse_dt(struct phy_device *phydev)
- {
- struct device_node *node = phydev->mdio.dev.of_node;
+++ /dev/null
-From 21a2802a8365cfa82cc02187c1f95136d85592ad Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:51:59 +0100
-Subject: [PATCH 12/13] net: phy: at803x: move at8035 specific DT parse to
- dedicated probe
-
-Move at8035 specific DT parse for clock out frequency to dedicated probe
-to make at803x probe function more generic.
-
-This is to tidy code and no behaviour change are intended.
-
-Detection logic is changed, we check if the clk 25m mask is set and if
-it's not zero, we assume the qca,clk-out-frequency property is set.
-
-The property is checked in the generic at803x_parse_dt called by
-at803x_probe.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 60 +++++++++++++++++++++++++++-------------
- 1 file changed, 41 insertions(+), 19 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -638,23 +638,6 @@ static int at803x_parse_dt(struct phy_de
-
- priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel);
- priv->clk_25m_mask |= AT803X_CLK_OUT_MASK;
--
-- /* Fixup for the AR8030/AR8035. This chip has another mask and
-- * doesn't support the DSP reference. Eg. the lowest bit of the
-- * mask. The upper two bits select the same frequencies. Mask
-- * the lowest bit here.
-- *
-- * Warning:
-- * There was no datasheet for the AR8030 available so this is
-- * just a guess. But the AR8035 is listed as pin compatible
-- * to the AR8030 so there might be a good chance it works on
-- * the AR8030 too.
-- */
-- if (phydev->drv->phy_id == ATH8030_PHY_ID ||
-- phydev->drv->phy_id == ATH8035_PHY_ID) {
-- priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
-- priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
-- }
- }
-
- ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
-@@ -1635,6 +1618,45 @@ static int at8031_config_intr(struct phy
- return at803x_config_intr(phydev);
- }
-
-+static int at8035_parse_dt(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+
-+ /* Mask is set by the generic at803x_parse_dt
-+ * if property is set. Assume property is set
-+ * with the mask not zero.
-+ */
-+ if (priv->clk_25m_mask) {
-+ /* Fixup for the AR8030/AR8035. This chip has another mask and
-+ * doesn't support the DSP reference. Eg. the lowest bit of the
-+ * mask. The upper two bits select the same frequencies. Mask
-+ * the lowest bit here.
-+ *
-+ * Warning:
-+ * There was no datasheet for the AR8030 available so this is
-+ * just a guess. But the AR8035 is listed as pin compatible
-+ * to the AR8030 so there might be a good chance it works on
-+ * the AR8030 too.
-+ */
-+ priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
-+ priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
-+ }
-+
-+ return 0;
-+}
-+
-+/* AR8030 and AR8035 shared the same special mask for clk_25m */
-+static int at8035_probe(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = at803x_probe(phydev);
-+ if (ret)
-+ return ret;
-+
-+ return at8035_parse_dt(phydev);
-+}
-+
- static int qca83xx_config_init(struct phy_device *phydev)
- {
- u8 switch_revision;
-@@ -2107,7 +2129,7 @@ static struct phy_driver at803x_driver[]
- PHY_ID_MATCH_EXACT(ATH8035_PHY_ID),
- .name = "Qualcomm Atheros AR8035",
- .flags = PHY_POLL_CABLE_TEST,
-- .probe = at803x_probe,
-+ .probe = at8035_probe,
- .config_aneg = at803x_config_aneg,
- .config_init = at803x_config_init,
- .soft_reset = genphy_soft_reset,
-@@ -2128,7 +2150,7 @@ static struct phy_driver at803x_driver[]
- .phy_id = ATH8030_PHY_ID,
- .name = "Qualcomm Atheros AR8030",
- .phy_id_mask = AT8030_PHY_ID_MASK,
-- .probe = at803x_probe,
-+ .probe = at8035_probe,
- .config_init = at803x_config_init,
- .link_change_notify = at803x_link_change_notify,
- .set_wol = at803x_set_wol,
+++ /dev/null
-From ef9df47b449e32e06501a11272809be49019bdb6 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 8 Dec 2023 15:52:00 +0100
-Subject: [PATCH 13/13] net: phy: at803x: drop specific PHY ID check from cable
- test functions
-
-Drop specific PHY ID check for cable test functions for at803x. This is
-done to make functions more generic. While at it better describe what
-the functions does by using more symbolic function names.
-
-PHYs that requires to set additional reg are moved to specific function
-calling the more generic one.
-
-cdt_start and cdt_wait_for_completion are changed to take an additional
-arg to pass specific values specific to the PHY.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 95 +++++++++++++++++++++-------------------
- 1 file changed, 50 insertions(+), 45 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -1222,31 +1222,16 @@ static int at803x_cdt_fault_length(u16 s
- return (dt * 824) / 10;
- }
-
--static int at803x_cdt_start(struct phy_device *phydev, int pair)
-+static int at803x_cdt_start(struct phy_device *phydev,
-+ u32 cdt_start)
- {
-- u16 cdt;
--
-- /* qca8081 takes the different bit 15 to enable CDT test */
-- if (phydev->drv->phy_id == QCA8081_PHY_ID)
-- cdt = QCA808X_CDT_ENABLE_TEST |
-- QCA808X_CDT_LENGTH_UNIT |
-- QCA808X_CDT_INTER_CHECK_DIS;
-- else
-- cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
-- AT803X_CDT_ENABLE_TEST;
--
-- return phy_write(phydev, AT803X_CDT, cdt);
-+ return phy_write(phydev, AT803X_CDT, cdt_start);
- }
-
--static int at803x_cdt_wait_for_completion(struct phy_device *phydev)
-+static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
-+ u32 cdt_en)
- {
- int val, ret;
-- u16 cdt_en;
--
-- if (phydev->drv->phy_id == QCA8081_PHY_ID)
-- cdt_en = QCA808X_CDT_ENABLE_TEST;
-- else
-- cdt_en = AT803X_CDT_ENABLE_TEST;
-
- /* One test run takes about 25ms */
- ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
-@@ -1266,11 +1251,13 @@ static int at803x_cable_test_one_pair(st
- };
- int ret, val;
-
-- ret = at803x_cdt_start(phydev, pair);
-+ val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
-+ AT803X_CDT_ENABLE_TEST;
-+ ret = at803x_cdt_start(phydev, val);
- if (ret)
- return ret;
-
-- ret = at803x_cdt_wait_for_completion(phydev);
-+ ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST);
- if (ret)
- return ret;
-
-@@ -1292,19 +1279,11 @@ static int at803x_cable_test_one_pair(st
- }
-
- static int at803x_cable_test_get_status(struct phy_device *phydev,
-- bool *finished)
-+ bool *finished, unsigned long pair_mask)
- {
-- unsigned long pair_mask;
- int retries = 20;
- int pair, ret;
-
-- if (phydev->phy_id == ATH9331_PHY_ID ||
-- phydev->phy_id == ATH8032_PHY_ID ||
-- phydev->phy_id == QCA9561_PHY_ID)
-- pair_mask = 0x3;
-- else
-- pair_mask = 0xf;
--
- *finished = false;
-
- /* According to the datasheet the CDT can be performed when
-@@ -1331,7 +1310,7 @@ static int at803x_cable_test_get_status(
- return 0;
- }
-
--static int at803x_cable_test_start(struct phy_device *phydev)
-+static void at803x_cable_test_autoneg(struct phy_device *phydev)
- {
- /* Enable auto-negotiation, but advertise no capabilities, no link
- * will be established. A restart of the auto-negotiation is not
-@@ -1339,11 +1318,11 @@ static int at803x_cable_test_start(struc
- */
- phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
- phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
-- if (phydev->phy_id != ATH9331_PHY_ID &&
-- phydev->phy_id != ATH8032_PHY_ID &&
-- phydev->phy_id != QCA9561_PHY_ID)
-- phy_write(phydev, MII_CTRL1000, 0);
-+}
-
-+static int at803x_cable_test_start(struct phy_device *phydev)
-+{
-+ at803x_cable_test_autoneg(phydev);
- /* we do all the (time consuming) work later */
- return 0;
- }
-@@ -1618,6 +1597,29 @@ static int at8031_config_intr(struct phy
- return at803x_config_intr(phydev);
- }
-
-+/* AR8031 and AR8035 share the same cable test get status reg */
-+static int at8031_cable_test_get_status(struct phy_device *phydev,
-+ bool *finished)
-+{
-+ return at803x_cable_test_get_status(phydev, finished, 0xf);
-+}
-+
-+/* AR8031 and AR8035 share the same cable test start logic */
-+static int at8031_cable_test_start(struct phy_device *phydev)
-+{
-+ at803x_cable_test_autoneg(phydev);
-+ phy_write(phydev, MII_CTRL1000, 0);
-+ /* we do all the (time consuming) work later */
-+ return 0;
-+}
-+
-+/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */
-+static int at8032_cable_test_get_status(struct phy_device *phydev,
-+ bool *finished)
-+{
-+ return at803x_cable_test_get_status(phydev, finished, 0x3);
-+}
-+
- static int at8035_parse_dt(struct phy_device *phydev)
- {
- struct at803x_priv *priv = phydev->priv;
-@@ -2041,11 +2043,14 @@ static int qca808x_cable_test_get_status
-
- *finished = false;
-
-- ret = at803x_cdt_start(phydev, 0);
-+ val = QCA808X_CDT_ENABLE_TEST |
-+ QCA808X_CDT_LENGTH_UNIT |
-+ QCA808X_CDT_INTER_CHECK_DIS;
-+ ret = at803x_cdt_start(phydev, val);
- if (ret)
- return ret;
-
-- ret = at803x_cdt_wait_for_completion(phydev);
-+ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
- if (ret)
- return ret;
-
-@@ -2143,8 +2148,8 @@ static struct phy_driver at803x_driver[]
- .handle_interrupt = at803x_handle_interrupt,
- .get_tunable = at803x_get_tunable,
- .set_tunable = at803x_set_tunable,
-- .cable_test_start = at803x_cable_test_start,
-- .cable_test_get_status = at803x_cable_test_get_status,
-+ .cable_test_start = at8031_cable_test_start,
-+ .cable_test_get_status = at8031_cable_test_get_status,
- }, {
- /* Qualcomm Atheros AR8030 */
- .phy_id = ATH8030_PHY_ID,
-@@ -2181,8 +2186,8 @@ static struct phy_driver at803x_driver[]
- .handle_interrupt = at803x_handle_interrupt,
- .get_tunable = at803x_get_tunable,
- .set_tunable = at803x_set_tunable,
-- .cable_test_start = at803x_cable_test_start,
-- .cable_test_get_status = at803x_cable_test_get_status,
-+ .cable_test_start = at8031_cable_test_start,
-+ .cable_test_get_status = at8031_cable_test_get_status,
- }, {
- /* Qualcomm Atheros AR8032 */
- PHY_ID_MATCH_EXACT(ATH8032_PHY_ID),
-@@ -2197,7 +2202,7 @@ static struct phy_driver at803x_driver[]
- .config_intr = at803x_config_intr,
- .handle_interrupt = at803x_handle_interrupt,
- .cable_test_start = at803x_cable_test_start,
-- .cable_test_get_status = at803x_cable_test_get_status,
-+ .cable_test_get_status = at8032_cable_test_get_status,
- }, {
- /* ATHEROS AR9331 */
- PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
-@@ -2210,7 +2215,7 @@ static struct phy_driver at803x_driver[]
- .config_intr = at803x_config_intr,
- .handle_interrupt = at803x_handle_interrupt,
- .cable_test_start = at803x_cable_test_start,
-- .cable_test_get_status = at803x_cable_test_get_status,
-+ .cable_test_get_status = at8032_cable_test_get_status,
- .read_status = at803x_read_status,
- .soft_reset = genphy_soft_reset,
- .config_aneg = at803x_config_aneg,
-@@ -2226,7 +2231,7 @@ static struct phy_driver at803x_driver[]
- .config_intr = at803x_config_intr,
- .handle_interrupt = at803x_handle_interrupt,
- .cable_test_start = at803x_cable_test_start,
-- .cable_test_get_status = at803x_cable_test_get_status,
-+ .cable_test_get_status = at8032_cable_test_get_status,
- .read_status = at803x_read_status,
- .soft_reset = genphy_soft_reset,
- .config_aneg = at803x_config_aneg,
+++ /dev/null
-From 8e732f1c6f2dc5e18f766d0f1b11df9db2dd044a Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 14 Dec 2023 01:44:31 +0100
-Subject: [PATCH 1/2] net: phy: at803x: move specific qca808x config_aneg to
- dedicated function
-
-Move specific qca808x config_aneg to dedicated function to permit easier
-split of qca808x portion from at803x driver.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 66 ++++++++++++++++++++++++----------------
- 1 file changed, 40 insertions(+), 26 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -1045,9 +1045,8 @@ static int at803x_config_mdix(struct phy
- FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
- }
-
--static int at803x_config_aneg(struct phy_device *phydev)
-+static int at803x_prepare_config_aneg(struct phy_device *phydev)
- {
-- struct at803x_priv *priv = phydev->priv;
- int ret;
-
- ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
-@@ -1064,33 +1063,22 @@ static int at803x_config_aneg(struct phy
- return ret;
- }
-
-- if (priv->is_1000basex)
-- return genphy_c37_config_aneg(phydev);
--
-- /* Do not restart auto-negotiation by setting ret to 0 defautly,
-- * when calling __genphy_config_aneg later.
-- */
-- ret = 0;
--
-- if (phydev->drv->phy_id == QCA8081_PHY_ID) {
-- int phy_ctrl = 0;
-+ return 0;
-+}
-
-- /* The reg MII_BMCR also needs to be configured for force mode, the
-- * genphy_config_aneg is also needed.
-- */
-- if (phydev->autoneg == AUTONEG_DISABLE)
-- genphy_c45_pma_setup_forced(phydev);
-+static int at803x_config_aneg(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ int ret;
-
-- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
-- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
-+ ret = at803x_prepare_config_aneg(phydev);
-+ if (ret)
-+ return ret;
-
-- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
-- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
-- if (ret < 0)
-- return ret;
-- }
-+ if (priv->is_1000basex)
-+ return genphy_c37_config_aneg(phydev);
-
-- return __genphy_config_aneg(phydev, ret);
-+ return genphy_config_aneg(phydev);
- }
-
- static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
-@@ -2118,6 +2106,32 @@ static int qca808x_get_features(struct p
- return 0;
- }
-
-+static int qca808x_config_aneg(struct phy_device *phydev)
-+{
-+ int phy_ctrl = 0;
-+ int ret;
-+
-+ ret = at803x_prepare_config_aneg(phydev);
-+ if (ret)
-+ return ret;
-+
-+ /* The reg MII_BMCR also needs to be configured for force mode, the
-+ * genphy_config_aneg is also needed.
-+ */
-+ if (phydev->autoneg == AUTONEG_DISABLE)
-+ genphy_c45_pma_setup_forced(phydev);
-+
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
-+ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
-+
-+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
-+ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
-+ if (ret < 0)
-+ return ret;
-+
-+ return __genphy_config_aneg(phydev, ret);
-+}
-+
- static void qca808x_link_change_notify(struct phy_device *phydev)
- {
- /* Assert interface sgmii fifo on link down, deassert it on link up,
-@@ -2295,7 +2309,7 @@ static struct phy_driver at803x_driver[]
- .set_wol = at803x_set_wol,
- .get_wol = at803x_get_wol,
- .get_features = qca808x_get_features,
-- .config_aneg = at803x_config_aneg,
-+ .config_aneg = qca808x_config_aneg,
- .suspend = genphy_suspend,
- .resume = genphy_resume,
- .read_status = qca808x_read_status,
+++ /dev/null
-From 38eb804e8458ba181a03a0498ce4bf84eebd1931 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 14 Dec 2023 01:44:32 +0100
-Subject: [PATCH 2/2] net: phy: at803x: make read specific status function more
- generic
-
-Rework read specific status function to be more generic. The function
-apply different speed mask based on the PHY ID. Make it more generic by
-adding an additional arg to pass the specific speed (ss) mask and use
-the provided mask to parse the speed value.
-
-This is needed to permit an easier deatch of qca808x code from the
-at803x driver.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 26 ++++++++++++++++++--------
- 1 file changed, 18 insertions(+), 8 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -301,6 +301,11 @@ static struct at803x_hw_stat qca83xx_hw_
- { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
- };
-
-+struct at803x_ss_mask {
-+ u16 speed_mask;
-+ u8 speed_shift;
-+};
-+
- struct at803x_priv {
- int flags;
- u16 clk_25m_reg;
-@@ -921,7 +926,8 @@ static void at803x_link_change_notify(st
- }
- }
-
--static int at803x_read_specific_status(struct phy_device *phydev)
-+static int at803x_read_specific_status(struct phy_device *phydev,
-+ struct at803x_ss_mask ss_mask)
- {
- int ss;
-
-@@ -940,11 +946,8 @@ static int at803x_read_specific_status(s
- if (sfc < 0)
- return sfc;
-
-- /* qca8081 takes the different bits for speed value from at803x */
-- if (phydev->drv->phy_id == QCA8081_PHY_ID)
-- speed = FIELD_GET(QCA808X_SS_SPEED_MASK, ss);
-- else
-- speed = FIELD_GET(AT803X_SS_SPEED_MASK, ss);
-+ speed = ss & ss_mask.speed_mask;
-+ speed >>= ss_mask.speed_shift;
-
- switch (speed) {
- case AT803X_SS_SPEED_10:
-@@ -989,6 +992,7 @@ static int at803x_read_specific_status(s
- static int at803x_read_status(struct phy_device *phydev)
- {
- struct at803x_priv *priv = phydev->priv;
-+ struct at803x_ss_mask ss_mask = { 0 };
- int err, old_link = phydev->link;
-
- if (priv->is_1000basex)
-@@ -1012,7 +1016,9 @@ static int at803x_read_status(struct phy
- if (err < 0)
- return err;
-
-- err = at803x_read_specific_status(phydev);
-+ ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
-+ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
-+ err = at803x_read_specific_status(phydev, ss_mask);
- if (err < 0)
- return err;
-
-@@ -1869,6 +1875,7 @@ static int qca808x_config_init(struct ph
-
- static int qca808x_read_status(struct phy_device *phydev)
- {
-+ struct at803x_ss_mask ss_mask = { 0 };
- int ret;
-
- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
-@@ -1882,7 +1889,10 @@ static int qca808x_read_status(struct ph
- if (ret)
- return ret;
-
-- ret = at803x_read_specific_status(phydev);
-+ /* qca8081 takes the different bits for speed value from at803x */
-+ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
-+ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
-+ ret = at803x_read_specific_status(phydev, ss_mask);
- if (ret < 0)
- return ret;
-
+++ /dev/null
-From fc9d7264ddc32eaa647d6bfcdc25cdf9f786fde0 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 18 Dec 2023 00:27:39 +0100
-Subject: [PATCH 1/2] net: phy: at803x: remove extra space after cast
-
-Remove extra space after cast as reported by checkpatch to keep code
-clean.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20231217232739.27065-1-ansuelsmth@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/phy/at803x.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -462,7 +462,7 @@ static int at803x_set_wol(struct phy_dev
- if (!ndev)
- return -ENODEV;
-
-- mac = (const u8 *) ndev->dev_addr;
-+ mac = (const u8 *)ndev->dev_addr;
-
- if (!is_valid_ether_addr(mac))
- return -EINVAL;
+++ /dev/null
-From 3ab5720881a924fb6405d9e6a3b09f1026467c47 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 18 Dec 2023 00:25:08 +0100
-Subject: [PATCH 2/2] net: phy: at803x: replace msleep(1) with usleep_range
-
-Replace msleep(1) with usleep_range as suggested by timers-howto guide.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20231217232508.26470-1-ansuelsmth@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/phy/at803x.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -916,9 +916,9 @@ static void at803x_link_change_notify(st
- at803x_context_save(phydev, &context);
-
- phy_device_reset(phydev, 1);
-- msleep(1);
-+ usleep_range(1000, 2000);
- phy_device_reset(phydev, 0);
-- msleep(1);
-+ usleep_range(1000, 2000);
-
- at803x_context_restore(phydev, &context);
-
-@@ -1733,7 +1733,7 @@ static int qca83xx_resume(struct phy_dev
- if (ret)
- return ret;
-
-- msleep(1);
-+ usleep_range(1000, 2000);
-
- return 0;
- }
+++ /dev/null
-From 7961ef1fa10ec35ad6923fb5751877116e4b035b Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 19 Dec 2023 21:21:24 +0100
-Subject: [PATCH] net: phy: at803x: better align function varibles to open
- parenthesis
-
-Better align function variables to open parenthesis as suggested by
-checkpatch script for qca808x function to make code cleaner.
-
-For cable_test_get_status function some additional rework was needed to
-handle too long functions.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 67 ++++++++++++++++++++++------------------
- 1 file changed, 37 insertions(+), 30 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -1781,27 +1781,27 @@ static int qca808x_phy_fast_retrain_conf
- return ret;
-
- phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
-- QCA808X_TOP_OPTION1_DATA);
-+ QCA808X_TOP_OPTION1_DATA);
- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
-- QCA808X_MSE_THRESHOLD_20DB_VALUE);
-+ QCA808X_MSE_THRESHOLD_20DB_VALUE);
- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
-- QCA808X_MSE_THRESHOLD_17DB_VALUE);
-+ QCA808X_MSE_THRESHOLD_17DB_VALUE);
- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
-- QCA808X_MSE_THRESHOLD_27DB_VALUE);
-+ QCA808X_MSE_THRESHOLD_27DB_VALUE);
- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
-- QCA808X_MSE_THRESHOLD_28DB_VALUE);
-+ QCA808X_MSE_THRESHOLD_28DB_VALUE);
- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
-- QCA808X_MMD3_DEBUG_1_VALUE);
-+ QCA808X_MMD3_DEBUG_1_VALUE);
- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
-- QCA808X_MMD3_DEBUG_4_VALUE);
-+ QCA808X_MMD3_DEBUG_4_VALUE);
- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
-- QCA808X_MMD3_DEBUG_5_VALUE);
-+ QCA808X_MMD3_DEBUG_5_VALUE);
- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
-- QCA808X_MMD3_DEBUG_3_VALUE);
-+ QCA808X_MMD3_DEBUG_3_VALUE);
- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
-- QCA808X_MMD3_DEBUG_6_VALUE);
-+ QCA808X_MMD3_DEBUG_6_VALUE);
- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
-- QCA808X_MMD3_DEBUG_2_VALUE);
-+ QCA808X_MMD3_DEBUG_2_VALUE);
-
- return 0;
- }
-@@ -1838,13 +1838,14 @@ static int qca808x_config_init(struct ph
-
- /* Active adc&vga on 802.3az for the link 1000M and 100M */
- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
-- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
-+ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
- if (ret)
- return ret;
-
- /* Adjust the threshold on 802.3az for the link 1000M */
- ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
-- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, QCA808X_MMD3_AZ_TRAINING_VAL);
-+ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
-+ QCA808X_MMD3_AZ_TRAINING_VAL);
- if (ret)
- return ret;
-
-@@ -1870,7 +1871,8 @@ static int qca808x_config_init(struct ph
-
- /* Configure adc threshold as 100mv for the link 10M */
- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
-- QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV);
-+ QCA808X_ADC_THRESHOLD_MASK,
-+ QCA808X_ADC_THRESHOLD_100MV);
- }
-
- static int qca808x_read_status(struct phy_device *phydev)
-@@ -1883,7 +1885,7 @@ static int qca808x_read_status(struct ph
- return ret;
-
- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
-- ret & MDIO_AN_10GBT_STAT_LP2_5G);
-+ ret & MDIO_AN_10GBT_STAT_LP2_5G);
-
- ret = genphy_read_status(phydev);
- if (ret)
-@@ -1913,7 +1915,7 @@ static int qca808x_read_status(struct ph
- */
- if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
-- qca808x_is_prefer_master(phydev)) {
-+ qca808x_is_prefer_master(phydev)) {
- qca808x_phy_ms_seed_enable(phydev, false);
- } else {
- qca808x_phy_ms_seed_enable(phydev, true);
-@@ -2070,18 +2072,22 @@ static int qca808x_cable_test_get_status
- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
- qca808x_cable_test_result_trans(pair_d));
-
-- if (qca808x_cdt_fault_length_valid(pair_a))
-- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A,
-- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A));
-- if (qca808x_cdt_fault_length_valid(pair_b))
-- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B,
-- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B));
-- if (qca808x_cdt_fault_length_valid(pair_c))
-- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C,
-- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C));
-- if (qca808x_cdt_fault_length_valid(pair_d))
-- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D,
-- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D));
-+ if (qca808x_cdt_fault_length_valid(pair_a)) {
-+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A);
-+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
-+ }
-+ if (qca808x_cdt_fault_length_valid(pair_b)) {
-+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B);
-+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
-+ }
-+ if (qca808x_cdt_fault_length_valid(pair_c)) {
-+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C);
-+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
-+ }
-+ if (qca808x_cdt_fault_length_valid(pair_d)) {
-+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D);
-+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
-+ }
-
- *finished = true;
-
-@@ -2148,8 +2154,9 @@ static void qca808x_link_change_notify(s
- * the interface device address is always phy address added by 1.
- */
- mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
-- MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
-- QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
-+ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
-+ QCA8081_PHY_FIFO_RSTN,
-+ phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
- }
-
- static struct phy_driver at803x_driver[] = {
+++ /dev/null
-From 22eb276098da820d9440fad22901f9b74ed4d659 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 4 Jan 2024 22:30:38 +0100
-Subject: [PATCH 1/4] net: phy: at803x: generalize cdt fault length function
-
-Generalize cable test fault length function since they all base on the
-same magic values (already reverse engineered to understand the meaning
-of it) to have consistenct values on every PHY.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 13 ++++++-------
- 1 file changed, 6 insertions(+), 7 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -1192,10 +1192,8 @@ static bool at803x_cdt_fault_length_vali
- return false;
- }
-
--static int at803x_cdt_fault_length(u16 status)
-+static int at803x_cdt_fault_length(int dt)
- {
-- int dt;
--
- /* According to the datasheet the distance to the fault is
- * DELTA_TIME * 0.824 meters.
- *
-@@ -1211,8 +1209,6 @@ static int at803x_cdt_fault_length(u16 s
- * With a VF of 0.69 we get the factor 0.824 mentioned in the
- * datasheet.
- */
-- dt = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, status);
--
- return (dt * 824) / 10;
- }
-
-@@ -1265,9 +1261,11 @@ static int at803x_cable_test_one_pair(st
- ethnl_cable_test_result(phydev, ethtool_pair[pair],
- at803x_cable_test_result_trans(val));
-
-- if (at803x_cdt_fault_length_valid(val))
-+ if (at803x_cdt_fault_length_valid(val)) {
-+ val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val);
- ethnl_cable_test_fault_length(phydev, ethtool_pair[pair],
- at803x_cdt_fault_length(val));
-+ }
-
- return 1;
- }
-@@ -1992,7 +1990,8 @@ static int qca808x_cdt_fault_length(stru
- if (val < 0)
- return val;
-
-- return (FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val) * 824) / 10;
-+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val);
-+ return at803x_cdt_fault_length(val);
- }
-
- static int qca808x_cable_test_start(struct phy_device *phydev)
+++ /dev/null
-From e0e9ada1df6133513249861c1d91c1dbefd9383b Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 4 Jan 2024 22:30:39 +0100
-Subject: [PATCH 2/4] net: phy: at803x: refactor qca808x cable test get status
- function
-
-Refactor qca808x cable test get status function to remove code
-duplication and clean things up.
-
-The same logic is applied to each pair hence it can be generalized and
-moved to a common function.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 80 ++++++++++++++++++++++++----------------
- 1 file changed, 49 insertions(+), 31 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -2035,10 +2035,43 @@ static int qca808x_cable_test_start(stru
- return 0;
- }
-
-+static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
-+ u16 status)
-+{
-+ u16 pair_code;
-+ int length;
-+
-+ switch (pair) {
-+ case ETHTOOL_A_CABLE_PAIR_A:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_B:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_C:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_D:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ ethnl_cable_test_result(phydev, pair,
-+ qca808x_cable_test_result_trans(pair_code));
-+
-+ if (qca808x_cdt_fault_length_valid(pair_code)) {
-+ length = qca808x_cdt_fault_length(phydev, pair);
-+ ethnl_cable_test_fault_length(phydev, pair, length);
-+ }
-+
-+ return 0;
-+}
-+
- static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
- {
- int ret, val;
-- int pair_a, pair_b, pair_c, pair_d;
-
- *finished = false;
-
-@@ -2057,36 +2090,21 @@ static int qca808x_cable_test_get_status
- if (val < 0)
- return val;
-
-- pair_a = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, val);
-- pair_b = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, val);
-- pair_c = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, val);
-- pair_d = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, val);
--
-- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
-- qca808x_cable_test_result_trans(pair_a));
-- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
-- qca808x_cable_test_result_trans(pair_b));
-- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
-- qca808x_cable_test_result_trans(pair_c));
-- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
-- qca808x_cable_test_result_trans(pair_d));
--
-- if (qca808x_cdt_fault_length_valid(pair_a)) {
-- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A);
-- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
-- }
-- if (qca808x_cdt_fault_length_valid(pair_b)) {
-- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B);
-- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
-- }
-- if (qca808x_cdt_fault_length_valid(pair_c)) {
-- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C);
-- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
-- }
-- if (qca808x_cdt_fault_length_valid(pair_d)) {
-- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D);
-- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
-- }
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
-+ if (ret)
-+ return ret;
-
- *finished = true;
-
+++ /dev/null
-From ea73e5ea442ee2aade67b1fb1233ccb3cbea2ceb Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 4 Jan 2024 22:30:40 +0100
-Subject: [PATCH 3/4] net: phy: at803x: add support for cdt cross short test
- for qca808x
-
-QCA808x PHY Family supports Cable Diagnostic Test also for Cross Pair
-Short.
-
-Add all the define to make enable and support these additional tests.
-
-Cross Short test was previously disabled by default, this is now changed
-and enabled by default. In this mode, the mask changed a bit and length
-is shifted based on the fault condition.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 86 ++++++++++++++++++++++++++++++++--------
- 1 file changed, 69 insertions(+), 17 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -254,6 +254,7 @@
-
- #define QCA808X_CDT_ENABLE_TEST BIT(15)
- #define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
-+#define QCA808X_CDT_STATUS BIT(11)
- #define QCA808X_CDT_LENGTH_UNIT BIT(10)
-
- #define QCA808X_MMD3_CDT_STATUS 0x8064
-@@ -261,16 +262,44 @@
- #define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
- #define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
- #define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
--#define QCA808X_CDT_DIAG_LENGTH GENMASK(7, 0)
-+#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
-+#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
-
- #define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
- #define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
- #define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
- #define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
--#define QCA808X_CDT_STATUS_STAT_FAIL 0
--#define QCA808X_CDT_STATUS_STAT_NORMAL 1
--#define QCA808X_CDT_STATUS_STAT_OPEN 2
--#define QCA808X_CDT_STATUS_STAT_SHORT 3
-+
-+#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
-+#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
-+#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
-+#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
-+#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
-+
-+#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
-+#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
-+#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
-+#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
-+
-+/* NORMAL are MDI with type set to 0 */
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI1)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI1)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI2)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI2)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI3)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI3)
-+
-+/* Added for reference of existence but should be handled by wait_for_completion already */
-+#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
-
- /* QCA808X 1G chip type */
- #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
-@@ -1941,8 +1970,17 @@ static int qca808x_soft_reset(struct phy
- static bool qca808x_cdt_fault_length_valid(int cdt_code)
- {
- switch (cdt_code) {
-- case QCA808X_CDT_STATUS_STAT_SHORT:
-- case QCA808X_CDT_STATUS_STAT_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
- return true;
- default:
- return false;
-@@ -1954,17 +1992,28 @@ static int qca808x_cable_test_result_tra
- switch (cdt_code) {
- case QCA808X_CDT_STATUS_STAT_NORMAL:
- return ETHTOOL_A_CABLE_RESULT_CODE_OK;
-- case QCA808X_CDT_STATUS_STAT_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
-- case QCA808X_CDT_STATUS_STAT_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
- case QCA808X_CDT_STATUS_STAT_FAIL:
- default:
- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
- }
- }
-
--static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair)
-+static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
-+ int result)
- {
- int val;
- u32 cdt_length_reg = 0;
-@@ -1990,7 +2039,11 @@ static int qca808x_cdt_fault_length(stru
- if (val < 0)
- return val;
-
-- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val);
-+ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
-+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
-+ else
-+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
-+
- return at803x_cdt_fault_length(val);
- }
-
-@@ -2038,8 +2091,8 @@ static int qca808x_cable_test_start(stru
- static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
- u16 status)
- {
-+ int length, result;
- u16 pair_code;
-- int length;
-
- switch (pair) {
- case ETHTOOL_A_CABLE_PAIR_A:
-@@ -2058,11 +2111,11 @@ static int qca808x_cable_test_get_pair_s
- return -EINVAL;
- }
-
-- ethnl_cable_test_result(phydev, pair,
-- qca808x_cable_test_result_trans(pair_code));
-+ result = qca808x_cable_test_result_trans(pair_code);
-+ ethnl_cable_test_result(phydev, pair, result);
-
- if (qca808x_cdt_fault_length_valid(pair_code)) {
-- length = qca808x_cdt_fault_length(phydev, pair);
-+ length = qca808x_cdt_fault_length(phydev, pair, result);
- ethnl_cable_test_fault_length(phydev, pair, length);
- }
-
-@@ -2076,8 +2129,7 @@ static int qca808x_cable_test_get_status
- *finished = false;
-
- val = QCA808X_CDT_ENABLE_TEST |
-- QCA808X_CDT_LENGTH_UNIT |
-- QCA808X_CDT_INTER_CHECK_DIS;
-+ QCA808X_CDT_LENGTH_UNIT;
- ret = at803x_cdt_start(phydev, val);
- if (ret)
- return ret;
+++ /dev/null
-From c34d9452d4e5d98a655d7b625e85466320885416 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 4 Jan 2024 22:30:41 +0100
-Subject: [PATCH 4/4] net: phy: at803x: make read_status more generic
-
-Make read_status more generic in preparation on moving it to shared
-library as other PHY Family Driver will have the exact same
-implementation.
-
-The only specific part was a check for AR8031/33 if 1000basex was used.
-The check is moved to a dedicated function specific for those PHYs.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 17 ++++++++++++-----
- 1 file changed, 12 insertions(+), 5 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -1020,13 +1020,9 @@ static int at803x_read_specific_status(s
-
- static int at803x_read_status(struct phy_device *phydev)
- {
-- struct at803x_priv *priv = phydev->priv;
- struct at803x_ss_mask ss_mask = { 0 };
- int err, old_link = phydev->link;
-
-- if (priv->is_1000basex)
-- return genphy_c37_read_status(phydev);
--
- /* Update the link, but return if there was an error */
- err = genphy_update_link(phydev);
- if (err)
-@@ -1618,6 +1614,17 @@ static int at8031_config_intr(struct phy
- return at803x_config_intr(phydev);
- }
-
-+/* AR8031 and AR8033 share the same read status logic */
-+static int at8031_read_status(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+
-+ if (priv->is_1000basex)
-+ return genphy_c37_read_status(phydev);
-+
-+ return at803x_read_status(phydev);
-+}
-+
- /* AR8031 and AR8035 share the same cable test get status reg */
- static int at8031_cable_test_get_status(struct phy_device *phydev,
- bool *finished)
-@@ -2281,7 +2288,7 @@ static struct phy_driver at803x_driver[]
- .read_page = at803x_read_page,
- .write_page = at803x_write_page,
- .get_features = at803x_get_features,
-- .read_status = at803x_read_status,
-+ .read_status = at8031_read_status,
- .config_intr = at8031_config_intr,
- .handle_interrupt = at803x_handle_interrupt,
- .get_tunable = at803x_get_tunable,
+++ /dev/null
-From 7196062b64ee470b91015f3d2e82d225948258ea Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 25 Jan 2024 21:37:01 +0100
-Subject: [PATCH 5/5] net: phy: at803x: add LED support for qca808x
-
-Add LED support for QCA8081 PHY.
-
-Documentation for this LEDs PHY is very scarce even with NDA access
-to Documentation for OEMs. Only the blink pattern are documented and are
-very confusing most of the time. No documentation is present about
-forcing the LED on/off or to always blink.
-
-Those settings were reversed by poking the regs and trying to find the
-correct bits to trigger these modes. Some bits mode are not clear and
-maybe the documentation option are not 100% correct. For the sake of LED
-support the reversed option are enough to add support for current LED
-APIs.
-
-Supported HW control modes are:
-- tx
-- rx
-- link_10
-- link_100
-- link_1000
-- link_2500
-- half_duplex
-- full_duplex
-
-Also add support for LED polarity set to set LED polarity to active
-high or low. QSDK sets this value to high by default but PHY reset value
-doesn't have this enabled by default.
-
-QSDK also sets 2 additional bits but their usage is not clear, info about
-this is added in the header. It was verified that for correct function
-of the LED if active high is needed, only BIT 6 is needed.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240125203702.4552-6-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/at803x.c | 327 +++++++++++++++++++++++++++++++++++++++
- 1 file changed, 327 insertions(+)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -301,6 +301,87 @@
- /* Added for reference of existence but should be handled by wait_for_completion already */
- #define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
-
-+#define QCA808X_MMD7_LED_GLOBAL 0x8073
-+#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
-+#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
-+/* Values are the same for both BLINK_1 and BLINK_2 */
-+#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
-+#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
-+#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
-+#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
-+#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
-+#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
-+#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
-+#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
-+#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
-+#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
-+#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
-+#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
-+#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
-+#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
-+#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
-+#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
-+#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
-+#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
-+
-+#define QCA808X_MMD7_LED2_CTRL 0x8074
-+#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075
-+#define QCA808X_MMD7_LED1_CTRL 0x8076
-+#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077
-+#define QCA808X_MMD7_LED0_CTRL 0x8078
-+#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))
-+
-+/* LED hw control pattern is the same for every LED */
-+#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
-+#define QCA808X_LED_SPEED2500_ON BIT(15)
-+#define QCA808X_LED_SPEED2500_BLINK BIT(14)
-+/* Follow blink trigger even if duplex or speed condition doesn't match */
-+#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
-+#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
-+#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
-+#define QCA808X_LED_TX_BLINK BIT(10)
-+#define QCA808X_LED_RX_BLINK BIT(9)
-+#define QCA808X_LED_TX_ON_10MS BIT(8)
-+#define QCA808X_LED_RX_ON_10MS BIT(7)
-+#define QCA808X_LED_SPEED1000_ON BIT(6)
-+#define QCA808X_LED_SPEED100_ON BIT(5)
-+#define QCA808X_LED_SPEED10_ON BIT(4)
-+#define QCA808X_LED_COLLISION_BLINK BIT(3)
-+#define QCA808X_LED_SPEED1000_BLINK BIT(2)
-+#define QCA808X_LED_SPEED100_BLINK BIT(1)
-+#define QCA808X_LED_SPEED10_BLINK BIT(0)
-+
-+#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079
-+#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))
-+
-+/* LED force ctrl is the same for every LED
-+ * No documentation exist for this, not even internal one
-+ * with NDA as QCOM gives only info about configuring
-+ * hw control pattern rules and doesn't indicate any way
-+ * to force the LED to specific mode.
-+ * These define comes from reverse and testing and maybe
-+ * lack of some info or some info are not entirely correct.
-+ * For the basic LED control and hw control these finding
-+ * are enough to support LED control in all the required APIs.
-+ *
-+ * On doing some comparison with implementation with qca807x,
-+ * it was found that it's 1:1 equal to it and confirms all the
-+ * reverse done. It was also found further specification with the
-+ * force mode and the blink modes.
-+ */
-+#define QCA808X_LED_FORCE_EN BIT(15)
-+#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
-+#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
-+#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
-+#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
-+#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
-+
-+#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a
-+/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
-+ * LED to active high. It's not clear what BIT 3 and BIT 4 does.
-+ */
-+#define QCA808X_LED_ACTIVE_HIGH BIT(6)
-+
- /* QCA808X 1G chip type */
- #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
- #define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
-@@ -346,6 +427,7 @@ struct at803x_priv {
- struct regulator_dev *vddio_rdev;
- struct regulator_dev *vddh_rdev;
- u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
-+ int led_polarity_mode;
- };
-
- struct at803x_context {
-@@ -706,6 +788,9 @@ static int at803x_probe(struct phy_devic
- if (!priv)
- return -ENOMEM;
-
-+ /* Init LED polarity mode to -1 */
-+ priv->led_polarity_mode = -1;
-+
- phydev->priv = priv;
-
- ret = at803x_parse_dt(phydev);
-@@ -2235,6 +2320,242 @@ static void qca808x_link_change_notify(s
- phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
- }
-
-+static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
-+ u16 *offload_trigger)
-+{
-+ /* Parsing specific to netdev trigger */
-+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
-+ *offload_trigger |= QCA808X_LED_TX_BLINK;
-+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
-+ *offload_trigger |= QCA808X_LED_RX_BLINK;
-+ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED10_ON;
-+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED100_ON;
-+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED1000_ON;
-+ if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED2500_ON;
-+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
-+ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
-+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
-+ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
-+
-+ if (rules && !*offload_trigger)
-+ return -EOPNOTSUPP;
-+
-+ /* Enable BLINK_CHECK_BYPASS by default to make the LED
-+ * blink even with duplex or speed mode not enabled.
-+ */
-+ *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
-+
-+ return 0;
-+}
-+
-+static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN);
-+}
-+
-+static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ u16 offload_trigger = 0;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
-+}
-+
-+static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ u16 reg, offload_trigger = 0;
-+ int ret;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_CTRL(index);
-+
-+ ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_led_hw_control_enable(phydev, index);
-+ if (ret)
-+ return ret;
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_PATTERN_MASK,
-+ offload_trigger);
-+}
-+
-+static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg;
-+ int val;
-+
-+ if (index > 2)
-+ return false;
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
-+
-+ return !(val & QCA808X_LED_FORCE_EN);
-+}
-+
-+static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
-+ unsigned long *rules)
-+{
-+ u16 reg;
-+ int val;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ /* Check if we have hw control enabled */
-+ if (qca808x_led_hw_control_status(phydev, index))
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_CTRL(index);
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
-+ if (val & QCA808X_LED_TX_BLINK)
-+ set_bit(TRIGGER_NETDEV_TX, rules);
-+ if (val & QCA808X_LED_RX_BLINK)
-+ set_bit(TRIGGER_NETDEV_RX, rules);
-+ if (val & QCA808X_LED_SPEED10_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_10, rules);
-+ if (val & QCA808X_LED_SPEED100_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
-+ if (val & QCA808X_LED_SPEED1000_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
-+ if (val & QCA808X_LED_SPEED2500_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_2500, rules);
-+ if (val & QCA808X_LED_HALF_DUPLEX_ON)
-+ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
-+ if (val & QCA808X_LED_FULL_DUPLEX_ON)
-+ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
-+
-+ return 0;
-+}
-+
-+static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_CTRL(index);
-+
-+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_PATTERN_MASK);
-+}
-+
-+static int qca808x_led_brightness_set(struct phy_device *phydev,
-+ u8 index, enum led_brightness value)
-+{
-+ u16 reg;
-+ int ret;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ if (!value) {
-+ ret = qca808x_led_hw_control_reset(phydev, index);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-+ QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
-+ QCA808X_LED_FORCE_OFF);
-+}
-+
-+static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ int ret;
-+ u16 reg;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ /* Set blink to 50% off, 50% on at 4Hz by default */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
-+ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
-+ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
-+ if (ret)
-+ return ret;
-+
-+ /* We use BLINK_1 for normal blinking */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
-+ if (ret)
-+ return ret;
-+
-+ /* We set blink to 4Hz, aka 250ms */
-+ *delay_on = 250 / 2;
-+ *delay_off = 250 / 2;
-+
-+ return 0;
-+}
-+
-+static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
-+ unsigned long modes)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ bool active_low = false;
-+ u32 mode;
-+
-+ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
-+ switch (mode) {
-+ case PHY_LED_ACTIVE_LOW:
-+ active_low = true;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+ }
-+
-+ /* PHY polarity is global and can't be set per LED.
-+ * To detect this, check if last requested polarity mode
-+ * match the new one.
-+ */
-+ if (priv->led_polarity_mode >= 0 &&
-+ priv->led_polarity_mode != active_low) {
-+ phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
-+ return -EINVAL;
-+ }
-+
-+ /* Save the last PHY polarity mode */
-+ priv->led_polarity_mode = active_low;
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN,
-+ QCA808X_MMD7_LED_POLARITY_CTRL,
-+ QCA808X_LED_ACTIVE_HIGH,
-+ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
-+}
-+
- static struct phy_driver at803x_driver[] = {
- {
- /* Qualcomm Atheros AR8035 */
-@@ -2411,6 +2732,12 @@ static struct phy_driver at803x_driver[]
- .cable_test_start = qca808x_cable_test_start,
- .cable_test_get_status = qca808x_cable_test_get_status,
- .link_change_notify = qca808x_link_change_notify,
-+ .led_brightness_set = qca808x_led_brightness_set,
-+ .led_blink_set = qca808x_led_blink_set,
-+ .led_hw_is_supported = qca808x_led_hw_is_supported,
-+ .led_hw_control_set = qca808x_led_hw_control_set,
-+ .led_hw_control_get = qca808x_led_hw_control_get,
-+ .led_polarity_set = qca808x_led_polarity_set,
- }, };
-
- module_phy_driver(at803x_driver);
+++ /dev/null
-From 9e56ff53b4115875667760445b028357848b4748 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 Jan 2024 15:15:19 +0100
-Subject: [PATCH 1/5] net: phy: move at803x PHY driver to dedicated directory
-
-In preparation for addition of other Qcom PHY and to tidy things up,
-move the at803x PHY driver to dedicated directory.
-
-The same order in the Kconfig selection is saved.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240129141600.2592-2-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/Kconfig | 7 +------
- drivers/net/phy/Makefile | 2 +-
- drivers/net/phy/qcom/Kconfig | 7 +++++++
- drivers/net/phy/qcom/Makefile | 2 ++
- drivers/net/phy/{ => qcom}/at803x.c | 0
- 5 files changed, 11 insertions(+), 7 deletions(-)
- create mode 100644 drivers/net/phy/qcom/Kconfig
- create mode 100644 drivers/net/phy/qcom/Makefile
- rename drivers/net/phy/{ => qcom}/at803x.c (100%)
-
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -318,12 +318,7 @@ config NCN26000_PHY
- Currently supports the NCN26000 10BASE-T1S Industrial PHY
- with MII interface.
-
--config AT803X_PHY
-- tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
-- depends on REGULATOR
-- help
-- Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
-- QCA8337(Internal qca8k PHY) model
-+source "drivers/net/phy/qcom/Kconfig"
-
- config QSEMI_PHY
- tristate "Quality Semiconductor PHYs"
---- a/drivers/net/phy/Makefile
-+++ b/drivers/net/phy/Makefile
-@@ -36,7 +36,6 @@ obj-$(CONFIG_ADIN_PHY) += adin.o
- obj-$(CONFIG_ADIN1100_PHY) += adin1100.o
- obj-$(CONFIG_AMD_PHY) += amd.o
- obj-$(CONFIG_AQUANTIA_PHY) += aquantia/
--obj-$(CONFIG_AT803X_PHY) += at803x.o
- obj-$(CONFIG_AX88796B_PHY) += ax88796b.o
- obj-$(CONFIG_BCM54140_PHY) += bcm54140.o
- obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o
-@@ -82,6 +81,7 @@ obj-$(CONFIG_NCN26000_PHY) += ncn26000.o
- obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja11xx.o
- obj-$(CONFIG_NXP_CBTX_PHY) += nxp-cbtx.o
- obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
-+obj-y += qcom/
- obj-$(CONFIG_QSEMI_PHY) += qsemi.o
- obj-$(CONFIG_REALTEK_PHY) += realtek.o
- obj-$(CONFIG_RENESAS_PHY) += uPD60620.o
---- /dev/null
-+++ b/drivers/net/phy/qcom/Kconfig
-@@ -0,0 +1,7 @@
-+# SPDX-License-Identifier: GPL-2.0-only
-+config AT803X_PHY
-+ tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
-+ depends on REGULATOR
-+ help
-+ Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
-+ QCA8337(Internal qca8k PHY) model
---- /dev/null
-+++ b/drivers/net/phy/qcom/Makefile
-@@ -0,0 +1,2 @@
-+# SPDX-License-Identifier: GPL-2.0
-+obj-$(CONFIG_AT803X_PHY) += at803x.o
---- a/drivers/net/phy/at803x.c
-+++ /dev/null
-@@ -1,2759 +0,0 @@
--// SPDX-License-Identifier: GPL-2.0+
--/*
-- * drivers/net/phy/at803x.c
-- *
-- * Driver for Qualcomm Atheros AR803x PHY
-- *
-- * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
-- */
--
--#include <linux/phy.h>
--#include <linux/module.h>
--#include <linux/string.h>
--#include <linux/netdevice.h>
--#include <linux/etherdevice.h>
--#include <linux/ethtool_netlink.h>
--#include <linux/bitfield.h>
--#include <linux/regulator/of_regulator.h>
--#include <linux/regulator/driver.h>
--#include <linux/regulator/consumer.h>
--#include <linux/of.h>
--#include <linux/phylink.h>
--#include <linux/sfp.h>
--#include <dt-bindings/net/qca-ar803x.h>
--
--#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10
--#define AT803X_SFC_ASSERT_CRS BIT(11)
--#define AT803X_SFC_FORCE_LINK BIT(10)
--#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5)
--#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3
--#define AT803X_SFC_MANUAL_MDIX 0x1
--#define AT803X_SFC_MANUAL_MDI 0x0
--#define AT803X_SFC_SQE_TEST BIT(2)
--#define AT803X_SFC_POLARITY_REVERSAL BIT(1)
--#define AT803X_SFC_DISABLE_JABBER BIT(0)
--
--#define AT803X_SPECIFIC_STATUS 0x11
--#define AT803X_SS_SPEED_MASK GENMASK(15, 14)
--#define AT803X_SS_SPEED_1000 2
--#define AT803X_SS_SPEED_100 1
--#define AT803X_SS_SPEED_10 0
--#define AT803X_SS_DUPLEX BIT(13)
--#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11)
--#define AT803X_SS_MDIX BIT(6)
--
--#define QCA808X_SS_SPEED_MASK GENMASK(9, 7)
--#define QCA808X_SS_SPEED_2500 4
--
--#define AT803X_INTR_ENABLE 0x12
--#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15)
--#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14)
--#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13)
--#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12)
--#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11)
--#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10)
--#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8)
--#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7)
--#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5)
--#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1)
--#define AT803X_INTR_ENABLE_WOL BIT(0)
--
--#define AT803X_INTR_STATUS 0x13
--
--#define AT803X_SMART_SPEED 0x14
--#define AT803X_SMART_SPEED_ENABLE BIT(5)
--#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2)
--#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1)
--#define AT803X_CDT 0x16
--#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8)
--#define AT803X_CDT_ENABLE_TEST BIT(0)
--#define AT803X_CDT_STATUS 0x1c
--#define AT803X_CDT_STATUS_STAT_NORMAL 0
--#define AT803X_CDT_STATUS_STAT_SHORT 1
--#define AT803X_CDT_STATUS_STAT_OPEN 2
--#define AT803X_CDT_STATUS_STAT_FAIL 3
--#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8)
--#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0)
--#define AT803X_LED_CONTROL 0x18
--
--#define AT803X_PHY_MMD3_WOL_CTRL 0x8012
--#define AT803X_WOL_EN BIT(5)
--#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
--#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B
--#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
--#define AT803X_REG_CHIP_CONFIG 0x1f
--#define AT803X_BT_BX_REG_SEL 0x8000
--
--#define AT803X_DEBUG_ADDR 0x1D
--#define AT803X_DEBUG_DATA 0x1E
--
--#define AT803X_MODE_CFG_MASK 0x0F
--#define AT803X_MODE_CFG_BASET_RGMII 0x00
--#define AT803X_MODE_CFG_BASET_SGMII 0x01
--#define AT803X_MODE_CFG_BX1000_RGMII_50OHM 0x02
--#define AT803X_MODE_CFG_BX1000_RGMII_75OHM 0x03
--#define AT803X_MODE_CFG_BX1000_CONV_50OHM 0x04
--#define AT803X_MODE_CFG_BX1000_CONV_75OHM 0x05
--#define AT803X_MODE_CFG_FX100_RGMII_50OHM 0x06
--#define AT803X_MODE_CFG_FX100_CONV_50OHM 0x07
--#define AT803X_MODE_CFG_RGMII_AUTO_MDET 0x0B
--#define AT803X_MODE_CFG_FX100_RGMII_75OHM 0x0E
--#define AT803X_MODE_CFG_FX100_CONV_75OHM 0x0F
--
--#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
--#define AT803X_PSSR_MR_AN_COMPLETE 0x0200
--
--#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00
--#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2)
--#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2)
--#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15)
--
--#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05
--#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
--
--#define AT803X_DEBUG_REG_HIB_CTRL 0x0b
--#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10)
--#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13)
--#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15)
--
--#define AT803X_DEBUG_REG_3C 0x3C
--
--#define AT803X_DEBUG_REG_GREEN 0x3D
--#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6)
--
--#define AT803X_DEBUG_REG_1F 0x1F
--#define AT803X_DEBUG_PLL_ON BIT(2)
--#define AT803X_DEBUG_RGMII_1V8 BIT(3)
--
--#define MDIO_AZ_DEBUG 0x800D
--
--/* AT803x supports either the XTAL input pad, an internal PLL or the
-- * DSP as clock reference for the clock output pad. The XTAL reference
-- * is only used for 25 MHz output, all other frequencies need the PLL.
-- * The DSP as a clock reference is used in synchronous ethernet
-- * applications.
-- *
-- * By default the PLL is only enabled if there is a link. Otherwise
-- * the PHY will go into low power state and disabled the PLL. You can
-- * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always
-- * enabled.
-- */
--#define AT803X_MMD7_CLK25M 0x8016
--#define AT803X_CLK_OUT_MASK GENMASK(4, 2)
--#define AT803X_CLK_OUT_25MHZ_XTAL 0
--#define AT803X_CLK_OUT_25MHZ_DSP 1
--#define AT803X_CLK_OUT_50MHZ_PLL 2
--#define AT803X_CLK_OUT_50MHZ_DSP 3
--#define AT803X_CLK_OUT_62_5MHZ_PLL 4
--#define AT803X_CLK_OUT_62_5MHZ_DSP 5
--#define AT803X_CLK_OUT_125MHZ_PLL 6
--#define AT803X_CLK_OUT_125MHZ_DSP 7
--
--/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask
-- * but doesn't support choosing between XTAL/PLL and DSP.
-- */
--#define AT8035_CLK_OUT_MASK GENMASK(4, 3)
--
--#define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7)
--#define AT803X_CLK_OUT_STRENGTH_FULL 0
--#define AT803X_CLK_OUT_STRENGTH_HALF 1
--#define AT803X_CLK_OUT_STRENGTH_QUARTER 2
--
--#define AT803X_DEFAULT_DOWNSHIFT 5
--#define AT803X_MIN_DOWNSHIFT 2
--#define AT803X_MAX_DOWNSHIFT 9
--
--#define AT803X_MMD3_SMARTEEE_CTL1 0x805b
--#define AT803X_MMD3_SMARTEEE_CTL2 0x805c
--#define AT803X_MMD3_SMARTEEE_CTL3 0x805d
--#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8)
--
--#define ATH9331_PHY_ID 0x004dd041
--#define ATH8030_PHY_ID 0x004dd076
--#define ATH8031_PHY_ID 0x004dd074
--#define ATH8032_PHY_ID 0x004dd023
--#define ATH8035_PHY_ID 0x004dd072
--#define AT8030_PHY_ID_MASK 0xffffffef
--
--#define QCA8081_PHY_ID 0x004dd101
--
--#define QCA8327_A_PHY_ID 0x004dd033
--#define QCA8327_B_PHY_ID 0x004dd034
--#define QCA8337_PHY_ID 0x004dd036
--#define QCA9561_PHY_ID 0x004dd042
--#define QCA8K_PHY_ID_MASK 0xffffffff
--
--#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0)
--
--#define AT803X_PAGE_FIBER 0
--#define AT803X_PAGE_COPPER 1
--
--/* don't turn off internal PLL */
--#define AT803X_KEEP_PLL_ENABLED BIT(0)
--#define AT803X_DISABLE_SMARTEEE BIT(1)
--
--/* disable hibernation mode */
--#define AT803X_DISABLE_HIBERNATION_MODE BIT(2)
--
--/* ADC threshold */
--#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80
--#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0)
--#define QCA808X_ADC_THRESHOLD_80MV 0
--#define QCA808X_ADC_THRESHOLD_100MV 0xf0
--#define QCA808X_ADC_THRESHOLD_200MV 0x0f
--#define QCA808X_ADC_THRESHOLD_300MV 0xff
--
--/* CLD control */
--#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007
--#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4)
--#define QCA808X_8023AZ_AFE_EN 0x90
--
--/* AZ control */
--#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008
--#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32
--
--#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014
--#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529
--
--#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E
--#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341
--
--#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E
--#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419
--
--#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020
--#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341
--
--#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c
--#define QCA808X_TOP_OPTION1_DATA 0x0
--
--#define QCA808X_PHY_MMD3_DEBUG_1 0xa100
--#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203
--#define QCA808X_PHY_MMD3_DEBUG_2 0xa101
--#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad
--#define QCA808X_PHY_MMD3_DEBUG_3 0xa103
--#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698
--#define QCA808X_PHY_MMD3_DEBUG_4 0xa105
--#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001
--#define QCA808X_PHY_MMD3_DEBUG_5 0xa106
--#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111
--#define QCA808X_PHY_MMD3_DEBUG_6 0xa011
--#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85
--
--/* master/slave seed config */
--#define QCA808X_PHY_DEBUG_LOCAL_SEED 9
--#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1)
--#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2)
--#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32
--
--/* Hibernation yields lower power consumpiton in contrast with normal operation mode.
-- * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s.
-- */
--#define QCA808X_DBG_AN_TEST 0xb
--#define QCA808X_HIBERNATION_EN BIT(15)
--
--#define QCA808X_CDT_ENABLE_TEST BIT(15)
--#define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
--#define QCA808X_CDT_STATUS BIT(11)
--#define QCA808X_CDT_LENGTH_UNIT BIT(10)
--
--#define QCA808X_MMD3_CDT_STATUS 0x8064
--#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065
--#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
--#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
--#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
--#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
--#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
--
--#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
--#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
--#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
--#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
--
--#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
--#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
--#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
--#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
--#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
--
--#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
--#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
--#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
--#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
--
--/* NORMAL are MDI with type set to 0 */
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-- QCA808X_CDT_STATUS_STAT_MDI1)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-- QCA808X_CDT_STATUS_STAT_MDI1)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-- QCA808X_CDT_STATUS_STAT_MDI2)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-- QCA808X_CDT_STATUS_STAT_MDI2)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-- QCA808X_CDT_STATUS_STAT_MDI3)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-- QCA808X_CDT_STATUS_STAT_MDI3)
--
--/* Added for reference of existence but should be handled by wait_for_completion already */
--#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
--
--#define QCA808X_MMD7_LED_GLOBAL 0x8073
--#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
--#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
--/* Values are the same for both BLINK_1 and BLINK_2 */
--#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
--#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
--#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
--#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
--#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
--#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
--#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
--#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
--#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
--#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
--#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
--#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
--#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
--#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
--#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
--#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
--#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
--#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
--
--#define QCA808X_MMD7_LED2_CTRL 0x8074
--#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075
--#define QCA808X_MMD7_LED1_CTRL 0x8076
--#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077
--#define QCA808X_MMD7_LED0_CTRL 0x8078
--#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))
--
--/* LED hw control pattern is the same for every LED */
--#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
--#define QCA808X_LED_SPEED2500_ON BIT(15)
--#define QCA808X_LED_SPEED2500_BLINK BIT(14)
--/* Follow blink trigger even if duplex or speed condition doesn't match */
--#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
--#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
--#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
--#define QCA808X_LED_TX_BLINK BIT(10)
--#define QCA808X_LED_RX_BLINK BIT(9)
--#define QCA808X_LED_TX_ON_10MS BIT(8)
--#define QCA808X_LED_RX_ON_10MS BIT(7)
--#define QCA808X_LED_SPEED1000_ON BIT(6)
--#define QCA808X_LED_SPEED100_ON BIT(5)
--#define QCA808X_LED_SPEED10_ON BIT(4)
--#define QCA808X_LED_COLLISION_BLINK BIT(3)
--#define QCA808X_LED_SPEED1000_BLINK BIT(2)
--#define QCA808X_LED_SPEED100_BLINK BIT(1)
--#define QCA808X_LED_SPEED10_BLINK BIT(0)
--
--#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079
--#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))
--
--/* LED force ctrl is the same for every LED
-- * No documentation exist for this, not even internal one
-- * with NDA as QCOM gives only info about configuring
-- * hw control pattern rules and doesn't indicate any way
-- * to force the LED to specific mode.
-- * These define comes from reverse and testing and maybe
-- * lack of some info or some info are not entirely correct.
-- * For the basic LED control and hw control these finding
-- * are enough to support LED control in all the required APIs.
-- *
-- * On doing some comparison with implementation with qca807x,
-- * it was found that it's 1:1 equal to it and confirms all the
-- * reverse done. It was also found further specification with the
-- * force mode and the blink modes.
-- */
--#define QCA808X_LED_FORCE_EN BIT(15)
--#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
--#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
--#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
--#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
--#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
--
--#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a
--/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
-- * LED to active high. It's not clear what BIT 3 and BIT 4 does.
-- */
--#define QCA808X_LED_ACTIVE_HIGH BIT(6)
--
--/* QCA808X 1G chip type */
--#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
--#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
--
--#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072
--#define QCA8081_PHY_FIFO_RSTN BIT(11)
--
--MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
--MODULE_AUTHOR("Matus Ujhelyi");
--MODULE_LICENSE("GPL");
--
--enum stat_access_type {
-- PHY,
-- MMD
--};
--
--struct at803x_hw_stat {
-- const char *string;
-- u8 reg;
-- u32 mask;
-- enum stat_access_type access_type;
--};
--
--static struct at803x_hw_stat qca83xx_hw_stats[] = {
-- { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
-- { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
-- { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
--};
--
--struct at803x_ss_mask {
-- u16 speed_mask;
-- u8 speed_shift;
--};
--
--struct at803x_priv {
-- int flags;
-- u16 clk_25m_reg;
-- u16 clk_25m_mask;
-- u8 smarteee_lpi_tw_1g;
-- u8 smarteee_lpi_tw_100m;
-- bool is_fiber;
-- bool is_1000basex;
-- struct regulator_dev *vddio_rdev;
-- struct regulator_dev *vddh_rdev;
-- u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
-- int led_polarity_mode;
--};
--
--struct at803x_context {
-- u16 bmcr;
-- u16 advertise;
-- u16 control1000;
-- u16 int_enable;
-- u16 smart_speed;
-- u16 led_control;
--};
--
--static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
--{
-- int ret;
--
-- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
-- if (ret < 0)
-- return ret;
--
-- return phy_write(phydev, AT803X_DEBUG_DATA, data);
--}
--
--static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
--{
-- int ret;
--
-- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
-- if (ret < 0)
-- return ret;
--
-- return phy_read(phydev, AT803X_DEBUG_DATA);
--}
--
--static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
-- u16 clear, u16 set)
--{
-- u16 val;
-- int ret;
--
-- ret = at803x_debug_reg_read(phydev, reg);
-- if (ret < 0)
-- return ret;
--
-- val = ret & 0xffff;
-- val &= ~clear;
-- val |= set;
--
-- return phy_write(phydev, AT803X_DEBUG_DATA, val);
--}
--
--static int at803x_write_page(struct phy_device *phydev, int page)
--{
-- int mask;
-- int set;
--
-- if (page == AT803X_PAGE_COPPER) {
-- set = AT803X_BT_BX_REG_SEL;
-- mask = 0;
-- } else {
-- set = 0;
-- mask = AT803X_BT_BX_REG_SEL;
-- }
--
-- return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set);
--}
--
--static int at803x_read_page(struct phy_device *phydev)
--{
-- int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG);
--
-- if (ccr < 0)
-- return ccr;
--
-- if (ccr & AT803X_BT_BX_REG_SEL)
-- return AT803X_PAGE_COPPER;
--
-- return AT803X_PAGE_FIBER;
--}
--
--static int at803x_enable_rx_delay(struct phy_device *phydev)
--{
-- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0,
-- AT803X_DEBUG_RX_CLK_DLY_EN);
--}
--
--static int at803x_enable_tx_delay(struct phy_device *phydev)
--{
-- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0,
-- AT803X_DEBUG_TX_CLK_DLY_EN);
--}
--
--static int at803x_disable_rx_delay(struct phy_device *phydev)
--{
-- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-- AT803X_DEBUG_RX_CLK_DLY_EN, 0);
--}
--
--static int at803x_disable_tx_delay(struct phy_device *phydev)
--{
-- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE,
-- AT803X_DEBUG_TX_CLK_DLY_EN, 0);
--}
--
--/* save relevant PHY registers to private copy */
--static void at803x_context_save(struct phy_device *phydev,
-- struct at803x_context *context)
--{
-- context->bmcr = phy_read(phydev, MII_BMCR);
-- context->advertise = phy_read(phydev, MII_ADVERTISE);
-- context->control1000 = phy_read(phydev, MII_CTRL1000);
-- context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE);
-- context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED);
-- context->led_control = phy_read(phydev, AT803X_LED_CONTROL);
--}
--
--/* restore relevant PHY registers from private copy */
--static void at803x_context_restore(struct phy_device *phydev,
-- const struct at803x_context *context)
--{
-- phy_write(phydev, MII_BMCR, context->bmcr);
-- phy_write(phydev, MII_ADVERTISE, context->advertise);
-- phy_write(phydev, MII_CTRL1000, context->control1000);
-- phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable);
-- phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed);
-- phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
--}
--
--static int at803x_set_wol(struct phy_device *phydev,
-- struct ethtool_wolinfo *wol)
--{
-- int ret, irq_enabled;
--
-- if (wol->wolopts & WAKE_MAGIC) {
-- struct net_device *ndev = phydev->attached_dev;
-- const u8 *mac;
-- unsigned int i;
-- static const unsigned int offsets[] = {
-- AT803X_LOC_MAC_ADDR_32_47_OFFSET,
-- AT803X_LOC_MAC_ADDR_16_31_OFFSET,
-- AT803X_LOC_MAC_ADDR_0_15_OFFSET,
-- };
--
-- if (!ndev)
-- return -ENODEV;
--
-- mac = (const u8 *)ndev->dev_addr;
--
-- if (!is_valid_ether_addr(mac))
-- return -EINVAL;
--
-- for (i = 0; i < 3; i++)
-- phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
-- mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
--
-- /* Enable WOL interrupt */
-- ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
-- if (ret)
-- return ret;
-- } else {
-- /* Disable WOL interrupt */
-- ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
-- if (ret)
-- return ret;
-- }
--
-- /* Clear WOL status */
-- ret = phy_read(phydev, AT803X_INTR_STATUS);
-- if (ret < 0)
-- return ret;
--
-- /* Check if there are other interrupts except for WOL triggered when PHY is
-- * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
-- * be passed up to the interrupt PIN.
-- */
-- irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
-- if (irq_enabled < 0)
-- return irq_enabled;
--
-- irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
-- if (ret & irq_enabled && !phy_polling_mode(phydev))
-- phy_trigger_machine(phydev);
--
-- return 0;
--}
--
--static void at803x_get_wol(struct phy_device *phydev,
-- struct ethtool_wolinfo *wol)
--{
-- int value;
--
-- wol->supported = WAKE_MAGIC;
-- wol->wolopts = 0;
--
-- value = phy_read(phydev, AT803X_INTR_ENABLE);
-- if (value < 0)
-- return;
--
-- if (value & AT803X_INTR_ENABLE_WOL)
-- wol->wolopts |= WAKE_MAGIC;
--}
--
--static int qca83xx_get_sset_count(struct phy_device *phydev)
--{
-- return ARRAY_SIZE(qca83xx_hw_stats);
--}
--
--static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
--{
-- int i;
--
-- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
-- strscpy(data + i * ETH_GSTRING_LEN,
-- qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
-- }
--}
--
--static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
--{
-- struct at803x_hw_stat stat = qca83xx_hw_stats[i];
-- struct at803x_priv *priv = phydev->priv;
-- int val;
-- u64 ret;
--
-- if (stat.access_type == MMD)
-- val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
-- else
-- val = phy_read(phydev, stat.reg);
--
-- if (val < 0) {
-- ret = U64_MAX;
-- } else {
-- val = val & stat.mask;
-- priv->stats[i] += val;
-- ret = priv->stats[i];
-- }
--
-- return ret;
--}
--
--static void qca83xx_get_stats(struct phy_device *phydev,
-- struct ethtool_stats *stats, u64 *data)
--{
-- int i;
--
-- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
-- data[i] = qca83xx_get_stat(phydev, i);
--}
--
--static int at803x_suspend(struct phy_device *phydev)
--{
-- int value;
-- int wol_enabled;
--
-- value = phy_read(phydev, AT803X_INTR_ENABLE);
-- wol_enabled = value & AT803X_INTR_ENABLE_WOL;
--
-- if (wol_enabled)
-- value = BMCR_ISOLATE;
-- else
-- value = BMCR_PDOWN;
--
-- phy_modify(phydev, MII_BMCR, 0, value);
--
-- return 0;
--}
--
--static int at803x_resume(struct phy_device *phydev)
--{
-- return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
--}
--
--static int at803x_parse_dt(struct phy_device *phydev)
--{
-- struct device_node *node = phydev->mdio.dev.of_node;
-- struct at803x_priv *priv = phydev->priv;
-- u32 freq, strength, tw;
-- unsigned int sel;
-- int ret;
--
-- if (!IS_ENABLED(CONFIG_OF_MDIO))
-- return 0;
--
-- if (of_property_read_bool(node, "qca,disable-smarteee"))
-- priv->flags |= AT803X_DISABLE_SMARTEEE;
--
-- if (of_property_read_bool(node, "qca,disable-hibernation-mode"))
-- priv->flags |= AT803X_DISABLE_HIBERNATION_MODE;
--
-- if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) {
-- if (!tw || tw > 255) {
-- phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n");
-- return -EINVAL;
-- }
-- priv->smarteee_lpi_tw_1g = tw;
-- }
--
-- if (!of_property_read_u32(node, "qca,smarteee-tw-us-100m", &tw)) {
-- if (!tw || tw > 255) {
-- phydev_err(phydev, "invalid qca,smarteee-tw-us-100m\n");
-- return -EINVAL;
-- }
-- priv->smarteee_lpi_tw_100m = tw;
-- }
--
-- ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq);
-- if (!ret) {
-- switch (freq) {
-- case 25000000:
-- sel = AT803X_CLK_OUT_25MHZ_XTAL;
-- break;
-- case 50000000:
-- sel = AT803X_CLK_OUT_50MHZ_PLL;
-- break;
-- case 62500000:
-- sel = AT803X_CLK_OUT_62_5MHZ_PLL;
-- break;
-- case 125000000:
-- sel = AT803X_CLK_OUT_125MHZ_PLL;
-- break;
-- default:
-- phydev_err(phydev, "invalid qca,clk-out-frequency\n");
-- return -EINVAL;
-- }
--
-- priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel);
-- priv->clk_25m_mask |= AT803X_CLK_OUT_MASK;
-- }
--
-- ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
-- if (!ret) {
-- priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK;
-- switch (strength) {
-- case AR803X_STRENGTH_FULL:
-- priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL;
-- break;
-- case AR803X_STRENGTH_HALF:
-- priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF;
-- break;
-- case AR803X_STRENGTH_QUARTER:
-- priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER;
-- break;
-- default:
-- phydev_err(phydev, "invalid qca,clk-out-strength\n");
-- return -EINVAL;
-- }
-- }
--
-- return 0;
--}
--
--static int at803x_probe(struct phy_device *phydev)
--{
-- struct device *dev = &phydev->mdio.dev;
-- struct at803x_priv *priv;
-- int ret;
--
-- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-- if (!priv)
-- return -ENOMEM;
--
-- /* Init LED polarity mode to -1 */
-- priv->led_polarity_mode = -1;
--
-- phydev->priv = priv;
--
-- ret = at803x_parse_dt(phydev);
-- if (ret)
-- return ret;
--
-- return 0;
--}
--
--static int at803x_get_features(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
-- int err;
--
-- err = genphy_read_abilities(phydev);
-- if (err)
-- return err;
--
-- if (phydev->drv->phy_id != ATH8031_PHY_ID)
-- return 0;
--
-- /* AR8031/AR8033 have different status registers
-- * for copper and fiber operation. However, the
-- * extended status register is the same for both
-- * operation modes.
-- *
-- * As a result of that, ESTATUS_1000_XFULL is set
-- * to 1 even when operating in copper TP mode.
-- *
-- * Remove this mode from the supported link modes
-- * when not operating in 1000BaseX mode.
-- */
-- if (!priv->is_1000basex)
-- linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
-- phydev->supported);
--
-- return 0;
--}
--
--static int at803x_smarteee_config(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
-- u16 mask = 0, val = 0;
-- int ret;
--
-- if (priv->flags & AT803X_DISABLE_SMARTEEE)
-- return phy_modify_mmd(phydev, MDIO_MMD_PCS,
-- AT803X_MMD3_SMARTEEE_CTL3,
-- AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0);
--
-- if (priv->smarteee_lpi_tw_1g) {
-- mask |= 0xff00;
-- val |= priv->smarteee_lpi_tw_1g << 8;
-- }
-- if (priv->smarteee_lpi_tw_100m) {
-- mask |= 0x00ff;
-- val |= priv->smarteee_lpi_tw_100m;
-- }
-- if (!mask)
-- return 0;
--
-- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1,
-- mask, val);
-- if (ret)
-- return ret;
--
-- return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3,
-- AT803X_MMD3_SMARTEEE_CTL3_LPI_EN,
-- AT803X_MMD3_SMARTEEE_CTL3_LPI_EN);
--}
--
--static int at803x_clk_out_config(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
--
-- if (!priv->clk_25m_mask)
-- return 0;
--
-- return phy_modify_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M,
-- priv->clk_25m_mask, priv->clk_25m_reg);
--}
--
--static int at8031_pll_config(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
--
-- /* The default after hardware reset is PLL OFF. After a soft reset, the
-- * values are retained.
-- */
-- if (priv->flags & AT803X_KEEP_PLL_ENABLED)
-- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-- 0, AT803X_DEBUG_PLL_ON);
-- else
-- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-- AT803X_DEBUG_PLL_ON, 0);
--}
--
--static int at803x_hibernation_mode_config(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
--
-- /* The default after hardware reset is hibernation mode enabled. After
-- * software reset, the value is retained.
-- */
-- if (!(priv->flags & AT803X_DISABLE_HIBERNATION_MODE))
-- return 0;
--
-- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
-- AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0);
--}
--
--static int at803x_config_init(struct phy_device *phydev)
--{
-- int ret;
--
-- /* The RX and TX delay default is:
-- * after HW reset: RX delay enabled and TX delay disabled
-- * after SW reset: RX delay enabled, while TX delay retains the
-- * value before reset.
-- */
-- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
-- phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
-- ret = at803x_enable_rx_delay(phydev);
-- else
-- ret = at803x_disable_rx_delay(phydev);
-- if (ret < 0)
-- return ret;
--
-- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
-- phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
-- ret = at803x_enable_tx_delay(phydev);
-- else
-- ret = at803x_disable_tx_delay(phydev);
-- if (ret < 0)
-- return ret;
--
-- ret = at803x_smarteee_config(phydev);
-- if (ret < 0)
-- return ret;
--
-- ret = at803x_clk_out_config(phydev);
-- if (ret < 0)
-- return ret;
--
-- ret = at803x_hibernation_mode_config(phydev);
-- if (ret < 0)
-- return ret;
--
-- /* Ar803x extended next page bit is enabled by default. Cisco
-- * multigig switches read this bit and attempt to negotiate 10Gbps
-- * rates even if the next page bit is disabled. This is incorrect
-- * behaviour but we still need to accommodate it. XNP is only needed
-- * for 10Gbps support, so disable XNP.
-- */
-- return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0);
--}
--
--static int at803x_ack_interrupt(struct phy_device *phydev)
--{
-- int err;
--
-- err = phy_read(phydev, AT803X_INTR_STATUS);
--
-- return (err < 0) ? err : 0;
--}
--
--static int at803x_config_intr(struct phy_device *phydev)
--{
-- int err;
-- int value;
--
-- value = phy_read(phydev, AT803X_INTR_ENABLE);
--
-- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
-- /* Clear any pending interrupts */
-- err = at803x_ack_interrupt(phydev);
-- if (err)
-- return err;
--
-- value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
-- value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
-- value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
-- value |= AT803X_INTR_ENABLE_LINK_FAIL;
-- value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
--
-- err = phy_write(phydev, AT803X_INTR_ENABLE, value);
-- } else {
-- err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
-- if (err)
-- return err;
--
-- /* Clear any pending interrupts */
-- err = at803x_ack_interrupt(phydev);
-- }
--
-- return err;
--}
--
--static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)
--{
-- int irq_status, int_enabled;
--
-- irq_status = phy_read(phydev, AT803X_INTR_STATUS);
-- if (irq_status < 0) {
-- phy_error(phydev);
-- return IRQ_NONE;
-- }
--
-- /* Read the current enabled interrupts */
-- int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
-- if (int_enabled < 0) {
-- phy_error(phydev);
-- return IRQ_NONE;
-- }
--
-- /* See if this was one of our enabled interrupts */
-- if (!(irq_status & int_enabled))
-- return IRQ_NONE;
--
-- phy_trigger_machine(phydev);
--
-- return IRQ_HANDLED;
--}
--
--static void at803x_link_change_notify(struct phy_device *phydev)
--{
-- /*
-- * Conduct a hardware reset for AT8030 every time a link loss is
-- * signalled. This is necessary to circumvent a hardware bug that
-- * occurs when the cable is unplugged while TX packets are pending
-- * in the FIFO. In such cases, the FIFO enters an error mode it
-- * cannot recover from by software.
-- */
-- if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) {
-- struct at803x_context context;
--
-- at803x_context_save(phydev, &context);
--
-- phy_device_reset(phydev, 1);
-- usleep_range(1000, 2000);
-- phy_device_reset(phydev, 0);
-- usleep_range(1000, 2000);
--
-- at803x_context_restore(phydev, &context);
--
-- phydev_dbg(phydev, "%s(): phy was reset\n", __func__);
-- }
--}
--
--static int at803x_read_specific_status(struct phy_device *phydev,
-- struct at803x_ss_mask ss_mask)
--{
-- int ss;
--
-- /* Read the AT8035 PHY-Specific Status register, which indicates the
-- * speed and duplex that the PHY is actually using, irrespective of
-- * whether we are in autoneg mode or not.
-- */
-- ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
-- if (ss < 0)
-- return ss;
--
-- if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
-- int sfc, speed;
--
-- sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
-- if (sfc < 0)
-- return sfc;
--
-- speed = ss & ss_mask.speed_mask;
-- speed >>= ss_mask.speed_shift;
--
-- switch (speed) {
-- case AT803X_SS_SPEED_10:
-- phydev->speed = SPEED_10;
-- break;
-- case AT803X_SS_SPEED_100:
-- phydev->speed = SPEED_100;
-- break;
-- case AT803X_SS_SPEED_1000:
-- phydev->speed = SPEED_1000;
-- break;
-- case QCA808X_SS_SPEED_2500:
-- phydev->speed = SPEED_2500;
-- break;
-- }
-- if (ss & AT803X_SS_DUPLEX)
-- phydev->duplex = DUPLEX_FULL;
-- else
-- phydev->duplex = DUPLEX_HALF;
--
-- if (ss & AT803X_SS_MDIX)
-- phydev->mdix = ETH_TP_MDI_X;
-- else
-- phydev->mdix = ETH_TP_MDI;
--
-- switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
-- case AT803X_SFC_MANUAL_MDI:
-- phydev->mdix_ctrl = ETH_TP_MDI;
-- break;
-- case AT803X_SFC_MANUAL_MDIX:
-- phydev->mdix_ctrl = ETH_TP_MDI_X;
-- break;
-- case AT803X_SFC_AUTOMATIC_CROSSOVER:
-- phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
-- break;
-- }
-- }
--
-- return 0;
--}
--
--static int at803x_read_status(struct phy_device *phydev)
--{
-- struct at803x_ss_mask ss_mask = { 0 };
-- int err, old_link = phydev->link;
--
-- /* Update the link, but return if there was an error */
-- err = genphy_update_link(phydev);
-- if (err)
-- return err;
--
-- /* why bother the PHY if nothing can have changed */
-- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
-- return 0;
--
-- phydev->speed = SPEED_UNKNOWN;
-- phydev->duplex = DUPLEX_UNKNOWN;
-- phydev->pause = 0;
-- phydev->asym_pause = 0;
--
-- err = genphy_read_lpa(phydev);
-- if (err < 0)
-- return err;
--
-- ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
-- ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
-- err = at803x_read_specific_status(phydev, ss_mask);
-- if (err < 0)
-- return err;
--
-- if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
-- phy_resolve_aneg_pause(phydev);
--
-- return 0;
--}
--
--static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
--{
-- u16 val;
--
-- switch (ctrl) {
-- case ETH_TP_MDI:
-- val = AT803X_SFC_MANUAL_MDI;
-- break;
-- case ETH_TP_MDI_X:
-- val = AT803X_SFC_MANUAL_MDIX;
-- break;
-- case ETH_TP_MDI_AUTO:
-- val = AT803X_SFC_AUTOMATIC_CROSSOVER;
-- break;
-- default:
-- return 0;
-- }
--
-- return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
-- AT803X_SFC_MDI_CROSSOVER_MODE_M,
-- FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
--}
--
--static int at803x_prepare_config_aneg(struct phy_device *phydev)
--{
-- int ret;
--
-- ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
-- if (ret < 0)
-- return ret;
--
-- /* Changes of the midx bits are disruptive to the normal operation;
-- * therefore any changes to these registers must be followed by a
-- * software reset to take effect.
-- */
-- if (ret == 1) {
-- ret = genphy_soft_reset(phydev);
-- if (ret < 0)
-- return ret;
-- }
--
-- return 0;
--}
--
--static int at803x_config_aneg(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
-- int ret;
--
-- ret = at803x_prepare_config_aneg(phydev);
-- if (ret)
-- return ret;
--
-- if (priv->is_1000basex)
-- return genphy_c37_config_aneg(phydev);
--
-- return genphy_config_aneg(phydev);
--}
--
--static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
--{
-- int val;
--
-- val = phy_read(phydev, AT803X_SMART_SPEED);
-- if (val < 0)
-- return val;
--
-- if (val & AT803X_SMART_SPEED_ENABLE)
-- *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
-- else
-- *d = DOWNSHIFT_DEV_DISABLE;
--
-- return 0;
--}
--
--static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
--{
-- u16 mask, set;
-- int ret;
--
-- switch (cnt) {
-- case DOWNSHIFT_DEV_DEFAULT_COUNT:
-- cnt = AT803X_DEFAULT_DOWNSHIFT;
-- fallthrough;
-- case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
-- set = AT803X_SMART_SPEED_ENABLE |
-- AT803X_SMART_SPEED_BYPASS_TIMER |
-- FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
-- mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
-- break;
-- case DOWNSHIFT_DEV_DISABLE:
-- set = 0;
-- mask = AT803X_SMART_SPEED_ENABLE |
-- AT803X_SMART_SPEED_BYPASS_TIMER;
-- break;
-- default:
-- return -EINVAL;
-- }
--
-- ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
--
-- /* After changing the smart speed settings, we need to perform a
-- * software reset, use phy_init_hw() to make sure we set the
-- * reapply any values which might got lost during software reset.
-- */
-- if (ret == 1)
-- ret = phy_init_hw(phydev);
--
-- return ret;
--}
--
--static int at803x_get_tunable(struct phy_device *phydev,
-- struct ethtool_tunable *tuna, void *data)
--{
-- switch (tuna->id) {
-- case ETHTOOL_PHY_DOWNSHIFT:
-- return at803x_get_downshift(phydev, data);
-- default:
-- return -EOPNOTSUPP;
-- }
--}
--
--static int at803x_set_tunable(struct phy_device *phydev,
-- struct ethtool_tunable *tuna, const void *data)
--{
-- switch (tuna->id) {
-- case ETHTOOL_PHY_DOWNSHIFT:
-- return at803x_set_downshift(phydev, *(const u8 *)data);
-- default:
-- return -EOPNOTSUPP;
-- }
--}
--
--static int at803x_cable_test_result_trans(u16 status)
--{
-- switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
-- case AT803X_CDT_STATUS_STAT_NORMAL:
-- return ETHTOOL_A_CABLE_RESULT_CODE_OK;
-- case AT803X_CDT_STATUS_STAT_SHORT:
-- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
-- case AT803X_CDT_STATUS_STAT_OPEN:
-- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
-- case AT803X_CDT_STATUS_STAT_FAIL:
-- default:
-- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
-- }
--}
--
--static bool at803x_cdt_test_failed(u16 status)
--{
-- return FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status) ==
-- AT803X_CDT_STATUS_STAT_FAIL;
--}
--
--static bool at803x_cdt_fault_length_valid(u16 status)
--{
-- switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
-- case AT803X_CDT_STATUS_STAT_OPEN:
-- case AT803X_CDT_STATUS_STAT_SHORT:
-- return true;
-- }
-- return false;
--}
--
--static int at803x_cdt_fault_length(int dt)
--{
-- /* According to the datasheet the distance to the fault is
-- * DELTA_TIME * 0.824 meters.
-- *
-- * The author suspect the correct formula is:
-- *
-- * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
-- *
-- * where c is the speed of light, VF is the velocity factor of
-- * the twisted pair cable, 125MHz the counter frequency and
-- * we need to divide by 2 because the hardware will measure the
-- * round trip time to the fault and back to the PHY.
-- *
-- * With a VF of 0.69 we get the factor 0.824 mentioned in the
-- * datasheet.
-- */
-- return (dt * 824) / 10;
--}
--
--static int at803x_cdt_start(struct phy_device *phydev,
-- u32 cdt_start)
--{
-- return phy_write(phydev, AT803X_CDT, cdt_start);
--}
--
--static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
-- u32 cdt_en)
--{
-- int val, ret;
--
-- /* One test run takes about 25ms */
-- ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
-- !(val & cdt_en),
-- 30000, 100000, true);
--
-- return ret < 0 ? ret : 0;
--}
--
--static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair)
--{
-- static const int ethtool_pair[] = {
-- ETHTOOL_A_CABLE_PAIR_A,
-- ETHTOOL_A_CABLE_PAIR_B,
-- ETHTOOL_A_CABLE_PAIR_C,
-- ETHTOOL_A_CABLE_PAIR_D,
-- };
-- int ret, val;
--
-- val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
-- AT803X_CDT_ENABLE_TEST;
-- ret = at803x_cdt_start(phydev, val);
-- if (ret)
-- return ret;
--
-- ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST);
-- if (ret)
-- return ret;
--
-- val = phy_read(phydev, AT803X_CDT_STATUS);
-- if (val < 0)
-- return val;
--
-- if (at803x_cdt_test_failed(val))
-- return 0;
--
-- ethnl_cable_test_result(phydev, ethtool_pair[pair],
-- at803x_cable_test_result_trans(val));
--
-- if (at803x_cdt_fault_length_valid(val)) {
-- val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val);
-- ethnl_cable_test_fault_length(phydev, ethtool_pair[pair],
-- at803x_cdt_fault_length(val));
-- }
--
-- return 1;
--}
--
--static int at803x_cable_test_get_status(struct phy_device *phydev,
-- bool *finished, unsigned long pair_mask)
--{
-- int retries = 20;
-- int pair, ret;
--
-- *finished = false;
--
-- /* According to the datasheet the CDT can be performed when
-- * there is no link partner or when the link partner is
-- * auto-negotiating. Starting the test will restart the AN
-- * automatically. It seems that doing this repeatedly we will
-- * get a slot where our link partner won't disturb our
-- * measurement.
-- */
-- while (pair_mask && retries--) {
-- for_each_set_bit(pair, &pair_mask, 4) {
-- ret = at803x_cable_test_one_pair(phydev, pair);
-- if (ret < 0)
-- return ret;
-- if (ret)
-- clear_bit(pair, &pair_mask);
-- }
-- if (pair_mask)
-- msleep(250);
-- }
--
-- *finished = true;
--
-- return 0;
--}
--
--static void at803x_cable_test_autoneg(struct phy_device *phydev)
--{
-- /* Enable auto-negotiation, but advertise no capabilities, no link
-- * will be established. A restart of the auto-negotiation is not
-- * required, because the cable test will automatically break the link.
-- */
-- phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
-- phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
--}
--
--static int at803x_cable_test_start(struct phy_device *phydev)
--{
-- at803x_cable_test_autoneg(phydev);
-- /* we do all the (time consuming) work later */
-- return 0;
--}
--
--static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
-- unsigned int selector)
--{
-- struct phy_device *phydev = rdev_get_drvdata(rdev);
--
-- if (selector)
-- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-- 0, AT803X_DEBUG_RGMII_1V8);
-- else
-- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-- AT803X_DEBUG_RGMII_1V8, 0);
--}
--
--static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
--{
-- struct phy_device *phydev = rdev_get_drvdata(rdev);
-- int val;
--
-- val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
-- if (val < 0)
-- return val;
--
-- return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
--}
--
--static const struct regulator_ops vddio_regulator_ops = {
-- .list_voltage = regulator_list_voltage_table,
-- .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
-- .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
--};
--
--static const unsigned int vddio_voltage_table[] = {
-- 1500000,
-- 1800000,
--};
--
--static const struct regulator_desc vddio_desc = {
-- .name = "vddio",
-- .of_match = of_match_ptr("vddio-regulator"),
-- .n_voltages = ARRAY_SIZE(vddio_voltage_table),
-- .volt_table = vddio_voltage_table,
-- .ops = &vddio_regulator_ops,
-- .type = REGULATOR_VOLTAGE,
-- .owner = THIS_MODULE,
--};
--
--static const struct regulator_ops vddh_regulator_ops = {
--};
--
--static const struct regulator_desc vddh_desc = {
-- .name = "vddh",
-- .of_match = of_match_ptr("vddh-regulator"),
-- .n_voltages = 1,
-- .fixed_uV = 2500000,
-- .ops = &vddh_regulator_ops,
-- .type = REGULATOR_VOLTAGE,
-- .owner = THIS_MODULE,
--};
--
--static int at8031_register_regulators(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
-- struct device *dev = &phydev->mdio.dev;
-- struct regulator_config config = { };
--
-- config.dev = dev;
-- config.driver_data = phydev;
--
-- priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
-- if (IS_ERR(priv->vddio_rdev)) {
-- phydev_err(phydev, "failed to register VDDIO regulator\n");
-- return PTR_ERR(priv->vddio_rdev);
-- }
--
-- priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
-- if (IS_ERR(priv->vddh_rdev)) {
-- phydev_err(phydev, "failed to register VDDH regulator\n");
-- return PTR_ERR(priv->vddh_rdev);
-- }
--
-- return 0;
--}
--
--static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
--{
-- struct phy_device *phydev = upstream;
-- __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
-- __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
-- DECLARE_PHY_INTERFACE_MASK(interfaces);
-- phy_interface_t iface;
--
-- linkmode_zero(phy_support);
-- phylink_set(phy_support, 1000baseX_Full);
-- phylink_set(phy_support, 1000baseT_Full);
-- phylink_set(phy_support, Autoneg);
-- phylink_set(phy_support, Pause);
-- phylink_set(phy_support, Asym_Pause);
--
-- linkmode_zero(sfp_support);
-- sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
-- /* Some modules support 10G modes as well as others we support.
-- * Mask out non-supported modes so the correct interface is picked.
-- */
-- linkmode_and(sfp_support, phy_support, sfp_support);
--
-- if (linkmode_empty(sfp_support)) {
-- dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
-- return -EINVAL;
-- }
--
-- iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
--
-- /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
-- * interface for use with SFP modules.
-- * However, some copper modules detected as having a preferred SGMII
-- * interface do default to and function in 1000Base-X mode, so just
-- * print a warning and allow such modules, as they may have some chance
-- * of working.
-- */
-- if (iface == PHY_INTERFACE_MODE_SGMII)
-- dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
-- else if (iface != PHY_INTERFACE_MODE_1000BASEX)
-- return -EINVAL;
--
-- return 0;
--}
--
--static const struct sfp_upstream_ops at8031_sfp_ops = {
-- .attach = phy_sfp_attach,
-- .detach = phy_sfp_detach,
-- .module_insert = at8031_sfp_insert,
--};
--
--static int at8031_parse_dt(struct phy_device *phydev)
--{
-- struct device_node *node = phydev->mdio.dev.of_node;
-- struct at803x_priv *priv = phydev->priv;
-- int ret;
--
-- if (of_property_read_bool(node, "qca,keep-pll-enabled"))
-- priv->flags |= AT803X_KEEP_PLL_ENABLED;
--
-- ret = at8031_register_regulators(phydev);
-- if (ret < 0)
-- return ret;
--
-- ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
-- "vddio");
-- if (ret) {
-- phydev_err(phydev, "failed to get VDDIO regulator\n");
-- return ret;
-- }
--
-- /* Only AR8031/8033 support 1000Base-X for SFP modules */
-- return phy_sfp_probe(phydev, &at8031_sfp_ops);
--}
--
--static int at8031_probe(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
-- int mode_cfg;
-- int ccr;
-- int ret;
--
-- ret = at803x_probe(phydev);
-- if (ret)
-- return ret;
--
-- /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
-- * options.
-- */
-- ret = at8031_parse_dt(phydev);
-- if (ret)
-- return ret;
--
-- ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
-- if (ccr < 0)
-- return ccr;
-- mode_cfg = ccr & AT803X_MODE_CFG_MASK;
--
-- switch (mode_cfg) {
-- case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
-- case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
-- priv->is_1000basex = true;
-- fallthrough;
-- case AT803X_MODE_CFG_FX100_RGMII_50OHM:
-- case AT803X_MODE_CFG_FX100_RGMII_75OHM:
-- priv->is_fiber = true;
-- break;
-- }
--
-- /* Disable WoL in 1588 register which is enabled
-- * by default
-- */
-- return phy_modify_mmd(phydev, MDIO_MMD_PCS,
-- AT803X_PHY_MMD3_WOL_CTRL,
-- AT803X_WOL_EN, 0);
--}
--
--static int at8031_config_init(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
-- int ret;
--
-- /* Some bootloaders leave the fiber page selected.
-- * Switch to the appropriate page (fiber or copper), as otherwise we
-- * read the PHY capabilities from the wrong page.
-- */
-- phy_lock_mdio_bus(phydev);
-- ret = at803x_write_page(phydev,
-- priv->is_fiber ? AT803X_PAGE_FIBER :
-- AT803X_PAGE_COPPER);
-- phy_unlock_mdio_bus(phydev);
-- if (ret)
-- return ret;
--
-- ret = at8031_pll_config(phydev);
-- if (ret < 0)
-- return ret;
--
-- return at803x_config_init(phydev);
--}
--
--static int at8031_set_wol(struct phy_device *phydev,
-- struct ethtool_wolinfo *wol)
--{
-- int ret;
--
-- /* First setup MAC address and enable WOL interrupt */
-- ret = at803x_set_wol(phydev, wol);
-- if (ret)
-- return ret;
--
-- if (wol->wolopts & WAKE_MAGIC)
-- /* Enable WOL function for 1588 */
-- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
-- AT803X_PHY_MMD3_WOL_CTRL,
-- 0, AT803X_WOL_EN);
-- else
-- /* Disable WoL function for 1588 */
-- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
-- AT803X_PHY_MMD3_WOL_CTRL,
-- AT803X_WOL_EN, 0);
--
-- return ret;
--}
--
--static int at8031_config_intr(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
-- int err, value = 0;
--
-- if (phydev->interrupts == PHY_INTERRUPT_ENABLED &&
-- priv->is_fiber) {
-- /* Clear any pending interrupts */
-- err = at803x_ack_interrupt(phydev);
-- if (err)
-- return err;
--
-- value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
-- value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
--
-- err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value);
-- if (err)
-- return err;
-- }
--
-- return at803x_config_intr(phydev);
--}
--
--/* AR8031 and AR8033 share the same read status logic */
--static int at8031_read_status(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
--
-- if (priv->is_1000basex)
-- return genphy_c37_read_status(phydev);
--
-- return at803x_read_status(phydev);
--}
--
--/* AR8031 and AR8035 share the same cable test get status reg */
--static int at8031_cable_test_get_status(struct phy_device *phydev,
-- bool *finished)
--{
-- return at803x_cable_test_get_status(phydev, finished, 0xf);
--}
--
--/* AR8031 and AR8035 share the same cable test start logic */
--static int at8031_cable_test_start(struct phy_device *phydev)
--{
-- at803x_cable_test_autoneg(phydev);
-- phy_write(phydev, MII_CTRL1000, 0);
-- /* we do all the (time consuming) work later */
-- return 0;
--}
--
--/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */
--static int at8032_cable_test_get_status(struct phy_device *phydev,
-- bool *finished)
--{
-- return at803x_cable_test_get_status(phydev, finished, 0x3);
--}
--
--static int at8035_parse_dt(struct phy_device *phydev)
--{
-- struct at803x_priv *priv = phydev->priv;
--
-- /* Mask is set by the generic at803x_parse_dt
-- * if property is set. Assume property is set
-- * with the mask not zero.
-- */
-- if (priv->clk_25m_mask) {
-- /* Fixup for the AR8030/AR8035. This chip has another mask and
-- * doesn't support the DSP reference. Eg. the lowest bit of the
-- * mask. The upper two bits select the same frequencies. Mask
-- * the lowest bit here.
-- *
-- * Warning:
-- * There was no datasheet for the AR8030 available so this is
-- * just a guess. But the AR8035 is listed as pin compatible
-- * to the AR8030 so there might be a good chance it works on
-- * the AR8030 too.
-- */
-- priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
-- priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
-- }
--
-- return 0;
--}
--
--/* AR8030 and AR8035 shared the same special mask for clk_25m */
--static int at8035_probe(struct phy_device *phydev)
--{
-- int ret;
--
-- ret = at803x_probe(phydev);
-- if (ret)
-- return ret;
--
-- return at8035_parse_dt(phydev);
--}
--
--static int qca83xx_config_init(struct phy_device *phydev)
--{
-- u8 switch_revision;
--
-- switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
--
-- switch (switch_revision) {
-- case 1:
-- /* For 100M waveform */
-- at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
-- /* Turn on Gigabit clock */
-- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
-- break;
--
-- case 2:
-- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
-- fallthrough;
-- case 4:
-- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
-- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860);
-- at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46);
-- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000);
-- break;
-- }
--
-- /* Following original QCA sourcecode set port to prefer master */
-- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
--
-- return 0;
--}
--
--static int qca8327_config_init(struct phy_device *phydev)
--{
-- /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
-- * Disable on init and enable only with 100m speed following
-- * qca original source code.
-- */
-- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-- QCA8327_DEBUG_MANU_CTRL_EN, 0);
--
-- return qca83xx_config_init(phydev);
--}
--
--static void qca83xx_link_change_notify(struct phy_device *phydev)
--{
-- /* Set DAC Amplitude adjustment to +6% for 100m on link running */
-- if (phydev->state == PHY_RUNNING) {
-- if (phydev->speed == SPEED_100)
-- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-- QCA8327_DEBUG_MANU_CTRL_EN,
-- QCA8327_DEBUG_MANU_CTRL_EN);
-- } else {
-- /* Reset DAC Amplitude adjustment */
-- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-- QCA8327_DEBUG_MANU_CTRL_EN, 0);
-- }
--}
--
--static int qca83xx_resume(struct phy_device *phydev)
--{
-- int ret, val;
--
-- /* Skip reset if not suspended */
-- if (!phydev->suspended)
-- return 0;
--
-- /* Reinit the port, reset values set by suspend */
-- qca83xx_config_init(phydev);
--
-- /* Reset the port on port resume */
-- phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
--
-- /* On resume from suspend the switch execute a reset and
-- * restart auto-negotiation. Wait for reset to complete.
-- */
-- ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
-- 50000, 600000, true);
-- if (ret)
-- return ret;
--
-- usleep_range(1000, 2000);
--
-- return 0;
--}
--
--static int qca83xx_suspend(struct phy_device *phydev)
--{
-- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
-- AT803X_DEBUG_GATE_CLK_IN1000, 0);
--
-- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
-- AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
-- AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
--
-- return 0;
--}
--
--static int qca8337_suspend(struct phy_device *phydev)
--{
-- /* Only QCA8337 support actual suspend. */
-- genphy_suspend(phydev);
--
-- return qca83xx_suspend(phydev);
--}
--
--static int qca8327_suspend(struct phy_device *phydev)
--{
-- u16 mask = 0;
--
-- /* QCA8327 cause port unreliability when phy suspend
-- * is set.
-- */
-- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
-- phy_modify(phydev, MII_BMCR, mask, 0);
--
-- return qca83xx_suspend(phydev);
--}
--
--static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
--{
-- int ret;
--
-- /* Enable fast retrain */
-- ret = genphy_c45_fast_retrain(phydev, true);
-- if (ret)
-- return ret;
--
-- phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
-- QCA808X_TOP_OPTION1_DATA);
-- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
-- QCA808X_MSE_THRESHOLD_20DB_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
-- QCA808X_MSE_THRESHOLD_17DB_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
-- QCA808X_MSE_THRESHOLD_27DB_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
-- QCA808X_MSE_THRESHOLD_28DB_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
-- QCA808X_MMD3_DEBUG_1_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
-- QCA808X_MMD3_DEBUG_4_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
-- QCA808X_MMD3_DEBUG_5_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
-- QCA808X_MMD3_DEBUG_3_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
-- QCA808X_MMD3_DEBUG_6_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
-- QCA808X_MMD3_DEBUG_2_VALUE);
--
-- return 0;
--}
--
--static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
--{
-- u16 seed_value;
--
-- if (!enable)
-- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
-- QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
--
-- seed_value = get_random_u32_below(QCA808X_MASTER_SLAVE_SEED_RANGE);
-- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
-- QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
-- FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
-- QCA808X_MASTER_SLAVE_SEED_ENABLE);
--}
--
--static bool qca808x_is_prefer_master(struct phy_device *phydev)
--{
-- return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
-- (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
--}
--
--static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
--{
-- return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
--}
--
--static int qca808x_config_init(struct phy_device *phydev)
--{
-- int ret;
--
-- /* Active adc&vga on 802.3az for the link 1000M and 100M */
-- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
-- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
-- if (ret)
-- return ret;
--
-- /* Adjust the threshold on 802.3az for the link 1000M */
-- ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
-- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
-- QCA808X_MMD3_AZ_TRAINING_VAL);
-- if (ret)
-- return ret;
--
-- if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
-- /* Config the fast retrain for the link 2500M */
-- ret = qca808x_phy_fast_retrain_config(phydev);
-- if (ret)
-- return ret;
--
-- ret = genphy_read_master_slave(phydev);
-- if (ret < 0)
-- return ret;
--
-- if (!qca808x_is_prefer_master(phydev)) {
-- /* Enable seed and configure lower ramdom seed to make phy
-- * linked as slave mode.
-- */
-- ret = qca808x_phy_ms_seed_enable(phydev, true);
-- if (ret)
-- return ret;
-- }
-- }
--
-- /* Configure adc threshold as 100mv for the link 10M */
-- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
-- QCA808X_ADC_THRESHOLD_MASK,
-- QCA808X_ADC_THRESHOLD_100MV);
--}
--
--static int qca808x_read_status(struct phy_device *phydev)
--{
-- struct at803x_ss_mask ss_mask = { 0 };
-- int ret;
--
-- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
-- if (ret < 0)
-- return ret;
--
-- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
-- ret & MDIO_AN_10GBT_STAT_LP2_5G);
--
-- ret = genphy_read_status(phydev);
-- if (ret)
-- return ret;
--
-- /* qca8081 takes the different bits for speed value from at803x */
-- ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
-- ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
-- ret = at803x_read_specific_status(phydev, ss_mask);
-- if (ret < 0)
-- return ret;
--
-- if (phydev->link) {
-- if (phydev->speed == SPEED_2500)
-- phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
-- else
-- phydev->interface = PHY_INTERFACE_MODE_SGMII;
-- } else {
-- /* generate seed as a lower random value to make PHY linked as SLAVE easily,
-- * except for master/slave configuration fault detected or the master mode
-- * preferred.
-- *
-- * the reason for not putting this code into the function link_change_notify is
-- * the corner case where the link partner is also the qca8081 PHY and the seed
-- * value is configured as the same value, the link can't be up and no link change
-- * occurs.
-- */
-- if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
-- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
-- qca808x_is_prefer_master(phydev)) {
-- qca808x_phy_ms_seed_enable(phydev, false);
-- } else {
-- qca808x_phy_ms_seed_enable(phydev, true);
-- }
-- }
-- }
--
-- return 0;
--}
--
--static int qca808x_soft_reset(struct phy_device *phydev)
--{
-- int ret;
--
-- ret = genphy_soft_reset(phydev);
-- if (ret < 0)
-- return ret;
--
-- if (qca808x_has_fast_retrain_or_slave_seed(phydev))
-- ret = qca808x_phy_ms_seed_enable(phydev, true);
--
-- return ret;
--}
--
--static bool qca808x_cdt_fault_length_valid(int cdt_code)
--{
-- switch (cdt_code) {
-- case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-- return true;
-- default:
-- return false;
-- }
--}
--
--static int qca808x_cable_test_result_trans(int cdt_code)
--{
-- switch (cdt_code) {
-- case QCA808X_CDT_STATUS_STAT_NORMAL:
-- return ETHTOOL_A_CABLE_RESULT_CODE_OK;
-- case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
-- case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-- return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
-- case QCA808X_CDT_STATUS_STAT_FAIL:
-- default:
-- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
-- }
--}
--
--static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
-- int result)
--{
-- int val;
-- u32 cdt_length_reg = 0;
--
-- switch (pair) {
-- case ETHTOOL_A_CABLE_PAIR_A:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
-- break;
-- case ETHTOOL_A_CABLE_PAIR_B:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
-- break;
-- case ETHTOOL_A_CABLE_PAIR_C:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
-- break;
-- case ETHTOOL_A_CABLE_PAIR_D:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
-- break;
-- default:
-- return -EINVAL;
-- }
--
-- val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
-- if (val < 0)
-- return val;
--
-- if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
-- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
-- else
-- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
--
-- return at803x_cdt_fault_length(val);
--}
--
--static int qca808x_cable_test_start(struct phy_device *phydev)
--{
-- int ret;
--
-- /* perform CDT with the following configs:
-- * 1. disable hibernation.
-- * 2. force PHY working in MDI mode.
-- * 3. for PHY working in 1000BaseT.
-- * 4. configure the threshold.
-- */
--
-- ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0);
-- if (ret < 0)
-- return ret;
--
-- ret = at803x_config_mdix(phydev, ETH_TP_MDI);
-- if (ret < 0)
-- return ret;
--
-- /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */
-- phydev->duplex = DUPLEX_FULL;
-- phydev->speed = SPEED_1000;
-- ret = genphy_c45_pma_setup_forced(phydev);
-- if (ret < 0)
-- return ret;
--
-- ret = genphy_setup_forced(phydev);
-- if (ret < 0)
-- return ret;
--
-- /* configure the thresholds for open, short, pair ok test */
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
--
-- return 0;
--}
--
--static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
-- u16 status)
--{
-- int length, result;
-- u16 pair_code;
--
-- switch (pair) {
-- case ETHTOOL_A_CABLE_PAIR_A:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
-- break;
-- case ETHTOOL_A_CABLE_PAIR_B:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
-- break;
-- case ETHTOOL_A_CABLE_PAIR_C:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
-- break;
-- case ETHTOOL_A_CABLE_PAIR_D:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
-- break;
-- default:
-- return -EINVAL;
-- }
--
-- result = qca808x_cable_test_result_trans(pair_code);
-- ethnl_cable_test_result(phydev, pair, result);
--
-- if (qca808x_cdt_fault_length_valid(pair_code)) {
-- length = qca808x_cdt_fault_length(phydev, pair, result);
-- ethnl_cable_test_fault_length(phydev, pair, length);
-- }
--
-- return 0;
--}
--
--static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
--{
-- int ret, val;
--
-- *finished = false;
--
-- val = QCA808X_CDT_ENABLE_TEST |
-- QCA808X_CDT_LENGTH_UNIT;
-- ret = at803x_cdt_start(phydev, val);
-- if (ret)
-- return ret;
--
-- ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
-- if (ret)
-- return ret;
--
-- val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
-- if (val < 0)
-- return val;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
-- if (ret)
-- return ret;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
-- if (ret)
-- return ret;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
-- if (ret)
-- return ret;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
-- if (ret)
-- return ret;
--
-- *finished = true;
--
-- return 0;
--}
--
--static int qca808x_get_features(struct phy_device *phydev)
--{
-- int ret;
--
-- ret = genphy_c45_pma_read_abilities(phydev);
-- if (ret)
-- return ret;
--
-- /* The autoneg ability is not existed in bit3 of MMD7.1,
-- * but it is supported by qca808x PHY, so we add it here
-- * manually.
-- */
-- linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
--
-- /* As for the qca8081 1G version chip, the 2500baseT ability is also
-- * existed in the bit0 of MMD1.21, we need to remove it manually if
-- * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
-- */
-- ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
-- if (ret < 0)
-- return ret;
--
-- if (QCA808X_PHY_CHIP_TYPE_1G & ret)
-- linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
--
-- return 0;
--}
--
--static int qca808x_config_aneg(struct phy_device *phydev)
--{
-- int phy_ctrl = 0;
-- int ret;
--
-- ret = at803x_prepare_config_aneg(phydev);
-- if (ret)
-- return ret;
--
-- /* The reg MII_BMCR also needs to be configured for force mode, the
-- * genphy_config_aneg is also needed.
-- */
-- if (phydev->autoneg == AUTONEG_DISABLE)
-- genphy_c45_pma_setup_forced(phydev);
--
-- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
-- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
--
-- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
-- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
-- if (ret < 0)
-- return ret;
--
-- return __genphy_config_aneg(phydev, ret);
--}
--
--static void qca808x_link_change_notify(struct phy_device *phydev)
--{
-- /* Assert interface sgmii fifo on link down, deassert it on link up,
-- * the interface device address is always phy address added by 1.
-- */
-- mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
-- MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
-- QCA8081_PHY_FIFO_RSTN,
-- phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
--}
--
--static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
-- u16 *offload_trigger)
--{
-- /* Parsing specific to netdev trigger */
-- if (test_bit(TRIGGER_NETDEV_TX, &rules))
-- *offload_trigger |= QCA808X_LED_TX_BLINK;
-- if (test_bit(TRIGGER_NETDEV_RX, &rules))
-- *offload_trigger |= QCA808X_LED_RX_BLINK;
-- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
-- *offload_trigger |= QCA808X_LED_SPEED10_ON;
-- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
-- *offload_trigger |= QCA808X_LED_SPEED100_ON;
-- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
-- *offload_trigger |= QCA808X_LED_SPEED1000_ON;
-- if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
-- *offload_trigger |= QCA808X_LED_SPEED2500_ON;
-- if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
-- *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
-- if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
-- *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
--
-- if (rules && !*offload_trigger)
-- return -EOPNOTSUPP;
--
-- /* Enable BLINK_CHECK_BYPASS by default to make the LED
-- * blink even with duplex or speed mode not enabled.
-- */
-- *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
--
-- return 0;
--}
--
--static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
--{
-- u16 reg;
--
-- if (index > 2)
-- return -EINVAL;
--
-- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_FORCE_EN);
--}
--
--static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
-- unsigned long rules)
--{
-- u16 offload_trigger = 0;
--
-- if (index > 2)
-- return -EINVAL;
--
-- return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
--}
--
--static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
-- unsigned long rules)
--{
-- u16 reg, offload_trigger = 0;
-- int ret;
--
-- if (index > 2)
-- return -EINVAL;
--
-- reg = QCA808X_MMD7_LED_CTRL(index);
--
-- ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
-- if (ret)
-- return ret;
--
-- ret = qca808x_led_hw_control_enable(phydev, index);
-- if (ret)
-- return ret;
--
-- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_PATTERN_MASK,
-- offload_trigger);
--}
--
--static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
--{
-- u16 reg;
-- int val;
--
-- if (index > 2)
-- return false;
--
-- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
--
-- return !(val & QCA808X_LED_FORCE_EN);
--}
--
--static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
-- unsigned long *rules)
--{
-- u16 reg;
-- int val;
--
-- if (index > 2)
-- return -EINVAL;
--
-- /* Check if we have hw control enabled */
-- if (qca808x_led_hw_control_status(phydev, index))
-- return -EINVAL;
--
-- reg = QCA808X_MMD7_LED_CTRL(index);
--
-- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
-- if (val & QCA808X_LED_TX_BLINK)
-- set_bit(TRIGGER_NETDEV_TX, rules);
-- if (val & QCA808X_LED_RX_BLINK)
-- set_bit(TRIGGER_NETDEV_RX, rules);
-- if (val & QCA808X_LED_SPEED10_ON)
-- set_bit(TRIGGER_NETDEV_LINK_10, rules);
-- if (val & QCA808X_LED_SPEED100_ON)
-- set_bit(TRIGGER_NETDEV_LINK_100, rules);
-- if (val & QCA808X_LED_SPEED1000_ON)
-- set_bit(TRIGGER_NETDEV_LINK_1000, rules);
-- if (val & QCA808X_LED_SPEED2500_ON)
-- set_bit(TRIGGER_NETDEV_LINK_2500, rules);
-- if (val & QCA808X_LED_HALF_DUPLEX_ON)
-- set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
-- if (val & QCA808X_LED_FULL_DUPLEX_ON)
-- set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
--
-- return 0;
--}
--
--static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
--{
-- u16 reg;
--
-- if (index > 2)
-- return -EINVAL;
--
-- reg = QCA808X_MMD7_LED_CTRL(index);
--
-- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_PATTERN_MASK);
--}
--
--static int qca808x_led_brightness_set(struct phy_device *phydev,
-- u8 index, enum led_brightness value)
--{
-- u16 reg;
-- int ret;
--
-- if (index > 2)
-- return -EINVAL;
--
-- if (!value) {
-- ret = qca808x_led_hw_control_reset(phydev, index);
-- if (ret)
-- return ret;
-- }
--
-- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-- QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
-- QCA808X_LED_FORCE_OFF);
--}
--
--static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
-- unsigned long *delay_on,
-- unsigned long *delay_off)
--{
-- int ret;
-- u16 reg;
--
-- if (index > 2)
-- return -EINVAL;
--
-- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- /* Set blink to 50% off, 50% on at 4Hz by default */
-- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
-- QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
-- QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
-- if (ret)
-- return ret;
--
-- /* We use BLINK_1 for normal blinking */
-- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
-- if (ret)
-- return ret;
--
-- /* We set blink to 4Hz, aka 250ms */
-- *delay_on = 250 / 2;
-- *delay_off = 250 / 2;
--
-- return 0;
--}
--
--static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
-- unsigned long modes)
--{
-- struct at803x_priv *priv = phydev->priv;
-- bool active_low = false;
-- u32 mode;
--
-- for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
-- switch (mode) {
-- case PHY_LED_ACTIVE_LOW:
-- active_low = true;
-- break;
-- default:
-- return -EINVAL;
-- }
-- }
--
-- /* PHY polarity is global and can't be set per LED.
-- * To detect this, check if last requested polarity mode
-- * match the new one.
-- */
-- if (priv->led_polarity_mode >= 0 &&
-- priv->led_polarity_mode != active_low) {
-- phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
-- return -EINVAL;
-- }
--
-- /* Save the last PHY polarity mode */
-- priv->led_polarity_mode = active_low;
--
-- return phy_modify_mmd(phydev, MDIO_MMD_AN,
-- QCA808X_MMD7_LED_POLARITY_CTRL,
-- QCA808X_LED_ACTIVE_HIGH,
-- active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
--}
--
--static struct phy_driver at803x_driver[] = {
--{
-- /* Qualcomm Atheros AR8035 */
-- PHY_ID_MATCH_EXACT(ATH8035_PHY_ID),
-- .name = "Qualcomm Atheros AR8035",
-- .flags = PHY_POLL_CABLE_TEST,
-- .probe = at8035_probe,
-- .config_aneg = at803x_config_aneg,
-- .config_init = at803x_config_init,
-- .soft_reset = genphy_soft_reset,
-- .set_wol = at803x_set_wol,
-- .get_wol = at803x_get_wol,
-- .suspend = at803x_suspend,
-- .resume = at803x_resume,
-- /* PHY_GBIT_FEATURES */
-- .read_status = at803x_read_status,
-- .config_intr = at803x_config_intr,
-- .handle_interrupt = at803x_handle_interrupt,
-- .get_tunable = at803x_get_tunable,
-- .set_tunable = at803x_set_tunable,
-- .cable_test_start = at8031_cable_test_start,
-- .cable_test_get_status = at8031_cable_test_get_status,
--}, {
-- /* Qualcomm Atheros AR8030 */
-- .phy_id = ATH8030_PHY_ID,
-- .name = "Qualcomm Atheros AR8030",
-- .phy_id_mask = AT8030_PHY_ID_MASK,
-- .probe = at8035_probe,
-- .config_init = at803x_config_init,
-- .link_change_notify = at803x_link_change_notify,
-- .set_wol = at803x_set_wol,
-- .get_wol = at803x_get_wol,
-- .suspend = at803x_suspend,
-- .resume = at803x_resume,
-- /* PHY_BASIC_FEATURES */
-- .config_intr = at803x_config_intr,
-- .handle_interrupt = at803x_handle_interrupt,
--}, {
-- /* Qualcomm Atheros AR8031/AR8033 */
-- PHY_ID_MATCH_EXACT(ATH8031_PHY_ID),
-- .name = "Qualcomm Atheros AR8031/AR8033",
-- .flags = PHY_POLL_CABLE_TEST,
-- .probe = at8031_probe,
-- .config_init = at8031_config_init,
-- .config_aneg = at803x_config_aneg,
-- .soft_reset = genphy_soft_reset,
-- .set_wol = at8031_set_wol,
-- .get_wol = at803x_get_wol,
-- .suspend = at803x_suspend,
-- .resume = at803x_resume,
-- .read_page = at803x_read_page,
-- .write_page = at803x_write_page,
-- .get_features = at803x_get_features,
-- .read_status = at8031_read_status,
-- .config_intr = at8031_config_intr,
-- .handle_interrupt = at803x_handle_interrupt,
-- .get_tunable = at803x_get_tunable,
-- .set_tunable = at803x_set_tunable,
-- .cable_test_start = at8031_cable_test_start,
-- .cable_test_get_status = at8031_cable_test_get_status,
--}, {
-- /* Qualcomm Atheros AR8032 */
-- PHY_ID_MATCH_EXACT(ATH8032_PHY_ID),
-- .name = "Qualcomm Atheros AR8032",
-- .probe = at803x_probe,
-- .flags = PHY_POLL_CABLE_TEST,
-- .config_init = at803x_config_init,
-- .link_change_notify = at803x_link_change_notify,
-- .suspend = at803x_suspend,
-- .resume = at803x_resume,
-- /* PHY_BASIC_FEATURES */
-- .config_intr = at803x_config_intr,
-- .handle_interrupt = at803x_handle_interrupt,
-- .cable_test_start = at803x_cable_test_start,
-- .cable_test_get_status = at8032_cable_test_get_status,
--}, {
-- /* ATHEROS AR9331 */
-- PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
-- .name = "Qualcomm Atheros AR9331 built-in PHY",
-- .probe = at803x_probe,
-- .suspend = at803x_suspend,
-- .resume = at803x_resume,
-- .flags = PHY_POLL_CABLE_TEST,
-- /* PHY_BASIC_FEATURES */
-- .config_intr = at803x_config_intr,
-- .handle_interrupt = at803x_handle_interrupt,
-- .cable_test_start = at803x_cable_test_start,
-- .cable_test_get_status = at8032_cable_test_get_status,
-- .read_status = at803x_read_status,
-- .soft_reset = genphy_soft_reset,
-- .config_aneg = at803x_config_aneg,
--}, {
-- /* Qualcomm Atheros QCA9561 */
-- PHY_ID_MATCH_EXACT(QCA9561_PHY_ID),
-- .name = "Qualcomm Atheros QCA9561 built-in PHY",
-- .probe = at803x_probe,
-- .suspend = at803x_suspend,
-- .resume = at803x_resume,
-- .flags = PHY_POLL_CABLE_TEST,
-- /* PHY_BASIC_FEATURES */
-- .config_intr = at803x_config_intr,
-- .handle_interrupt = at803x_handle_interrupt,
-- .cable_test_start = at803x_cable_test_start,
-- .cable_test_get_status = at8032_cable_test_get_status,
-- .read_status = at803x_read_status,
-- .soft_reset = genphy_soft_reset,
-- .config_aneg = at803x_config_aneg,
--}, {
-- /* QCA8337 */
-- .phy_id = QCA8337_PHY_ID,
-- .phy_id_mask = QCA8K_PHY_ID_MASK,
-- .name = "Qualcomm Atheros 8337 internal PHY",
-- /* PHY_GBIT_FEATURES */
-- .probe = at803x_probe,
-- .flags = PHY_IS_INTERNAL,
-- .config_init = qca83xx_config_init,
-- .soft_reset = genphy_soft_reset,
-- .get_sset_count = qca83xx_get_sset_count,
-- .get_strings = qca83xx_get_strings,
-- .get_stats = qca83xx_get_stats,
-- .suspend = qca8337_suspend,
-- .resume = qca83xx_resume,
--}, {
-- /* QCA8327-A from switch QCA8327-AL1A */
-- .phy_id = QCA8327_A_PHY_ID,
-- .phy_id_mask = QCA8K_PHY_ID_MASK,
-- .name = "Qualcomm Atheros 8327-A internal PHY",
-- /* PHY_GBIT_FEATURES */
-- .link_change_notify = qca83xx_link_change_notify,
-- .probe = at803x_probe,
-- .flags = PHY_IS_INTERNAL,
-- .config_init = qca8327_config_init,
-- .soft_reset = genphy_soft_reset,
-- .get_sset_count = qca83xx_get_sset_count,
-- .get_strings = qca83xx_get_strings,
-- .get_stats = qca83xx_get_stats,
-- .suspend = qca8327_suspend,
-- .resume = qca83xx_resume,
--}, {
-- /* QCA8327-B from switch QCA8327-BL1A */
-- .phy_id = QCA8327_B_PHY_ID,
-- .phy_id_mask = QCA8K_PHY_ID_MASK,
-- .name = "Qualcomm Atheros 8327-B internal PHY",
-- /* PHY_GBIT_FEATURES */
-- .link_change_notify = qca83xx_link_change_notify,
-- .probe = at803x_probe,
-- .flags = PHY_IS_INTERNAL,
-- .config_init = qca8327_config_init,
-- .soft_reset = genphy_soft_reset,
-- .get_sset_count = qca83xx_get_sset_count,
-- .get_strings = qca83xx_get_strings,
-- .get_stats = qca83xx_get_stats,
-- .suspend = qca8327_suspend,
-- .resume = qca83xx_resume,
--}, {
-- /* Qualcomm QCA8081 */
-- PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
-- .name = "Qualcomm QCA8081",
-- .flags = PHY_POLL_CABLE_TEST,
-- .probe = at803x_probe,
-- .config_intr = at803x_config_intr,
-- .handle_interrupt = at803x_handle_interrupt,
-- .get_tunable = at803x_get_tunable,
-- .set_tunable = at803x_set_tunable,
-- .set_wol = at803x_set_wol,
-- .get_wol = at803x_get_wol,
-- .get_features = qca808x_get_features,
-- .config_aneg = qca808x_config_aneg,
-- .suspend = genphy_suspend,
-- .resume = genphy_resume,
-- .read_status = qca808x_read_status,
-- .config_init = qca808x_config_init,
-- .soft_reset = qca808x_soft_reset,
-- .cable_test_start = qca808x_cable_test_start,
-- .cable_test_get_status = qca808x_cable_test_get_status,
-- .link_change_notify = qca808x_link_change_notify,
-- .led_brightness_set = qca808x_led_brightness_set,
-- .led_blink_set = qca808x_led_blink_set,
-- .led_hw_is_supported = qca808x_led_hw_is_supported,
-- .led_hw_control_set = qca808x_led_hw_control_set,
-- .led_hw_control_get = qca808x_led_hw_control_get,
-- .led_polarity_set = qca808x_led_polarity_set,
--}, };
--
--module_phy_driver(at803x_driver);
--
--static struct mdio_device_id __maybe_unused atheros_tbl[] = {
-- { ATH8030_PHY_ID, AT8030_PHY_ID_MASK },
-- { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
-- { }
--};
--
--MODULE_DEVICE_TABLE(mdio, atheros_tbl);
---- /dev/null
-+++ b/drivers/net/phy/qcom/at803x.c
-@@ -0,0 +1,2759 @@
-+// SPDX-License-Identifier: GPL-2.0+
-+/*
-+ * drivers/net/phy/at803x.c
-+ *
-+ * Driver for Qualcomm Atheros AR803x PHY
-+ *
-+ * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
-+ */
-+
-+#include <linux/phy.h>
-+#include <linux/module.h>
-+#include <linux/string.h>
-+#include <linux/netdevice.h>
-+#include <linux/etherdevice.h>
-+#include <linux/ethtool_netlink.h>
-+#include <linux/bitfield.h>
-+#include <linux/regulator/of_regulator.h>
-+#include <linux/regulator/driver.h>
-+#include <linux/regulator/consumer.h>
-+#include <linux/of.h>
-+#include <linux/phylink.h>
-+#include <linux/sfp.h>
-+#include <dt-bindings/net/qca-ar803x.h>
-+
-+#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10
-+#define AT803X_SFC_ASSERT_CRS BIT(11)
-+#define AT803X_SFC_FORCE_LINK BIT(10)
-+#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5)
-+#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3
-+#define AT803X_SFC_MANUAL_MDIX 0x1
-+#define AT803X_SFC_MANUAL_MDI 0x0
-+#define AT803X_SFC_SQE_TEST BIT(2)
-+#define AT803X_SFC_POLARITY_REVERSAL BIT(1)
-+#define AT803X_SFC_DISABLE_JABBER BIT(0)
-+
-+#define AT803X_SPECIFIC_STATUS 0x11
-+#define AT803X_SS_SPEED_MASK GENMASK(15, 14)
-+#define AT803X_SS_SPEED_1000 2
-+#define AT803X_SS_SPEED_100 1
-+#define AT803X_SS_SPEED_10 0
-+#define AT803X_SS_DUPLEX BIT(13)
-+#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11)
-+#define AT803X_SS_MDIX BIT(6)
-+
-+#define QCA808X_SS_SPEED_MASK GENMASK(9, 7)
-+#define QCA808X_SS_SPEED_2500 4
-+
-+#define AT803X_INTR_ENABLE 0x12
-+#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15)
-+#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14)
-+#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13)
-+#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12)
-+#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11)
-+#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10)
-+#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8)
-+#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7)
-+#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5)
-+#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1)
-+#define AT803X_INTR_ENABLE_WOL BIT(0)
-+
-+#define AT803X_INTR_STATUS 0x13
-+
-+#define AT803X_SMART_SPEED 0x14
-+#define AT803X_SMART_SPEED_ENABLE BIT(5)
-+#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2)
-+#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1)
-+#define AT803X_CDT 0x16
-+#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8)
-+#define AT803X_CDT_ENABLE_TEST BIT(0)
-+#define AT803X_CDT_STATUS 0x1c
-+#define AT803X_CDT_STATUS_STAT_NORMAL 0
-+#define AT803X_CDT_STATUS_STAT_SHORT 1
-+#define AT803X_CDT_STATUS_STAT_OPEN 2
-+#define AT803X_CDT_STATUS_STAT_FAIL 3
-+#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8)
-+#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0)
-+#define AT803X_LED_CONTROL 0x18
-+
-+#define AT803X_PHY_MMD3_WOL_CTRL 0x8012
-+#define AT803X_WOL_EN BIT(5)
-+#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
-+#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B
-+#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
-+#define AT803X_REG_CHIP_CONFIG 0x1f
-+#define AT803X_BT_BX_REG_SEL 0x8000
-+
-+#define AT803X_DEBUG_ADDR 0x1D
-+#define AT803X_DEBUG_DATA 0x1E
-+
-+#define AT803X_MODE_CFG_MASK 0x0F
-+#define AT803X_MODE_CFG_BASET_RGMII 0x00
-+#define AT803X_MODE_CFG_BASET_SGMII 0x01
-+#define AT803X_MODE_CFG_BX1000_RGMII_50OHM 0x02
-+#define AT803X_MODE_CFG_BX1000_RGMII_75OHM 0x03
-+#define AT803X_MODE_CFG_BX1000_CONV_50OHM 0x04
-+#define AT803X_MODE_CFG_BX1000_CONV_75OHM 0x05
-+#define AT803X_MODE_CFG_FX100_RGMII_50OHM 0x06
-+#define AT803X_MODE_CFG_FX100_CONV_50OHM 0x07
-+#define AT803X_MODE_CFG_RGMII_AUTO_MDET 0x0B
-+#define AT803X_MODE_CFG_FX100_RGMII_75OHM 0x0E
-+#define AT803X_MODE_CFG_FX100_CONV_75OHM 0x0F
-+
-+#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
-+#define AT803X_PSSR_MR_AN_COMPLETE 0x0200
-+
-+#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00
-+#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2)
-+#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2)
-+#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15)
-+
-+#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05
-+#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
-+
-+#define AT803X_DEBUG_REG_HIB_CTRL 0x0b
-+#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10)
-+#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13)
-+#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15)
-+
-+#define AT803X_DEBUG_REG_3C 0x3C
-+
-+#define AT803X_DEBUG_REG_GREEN 0x3D
-+#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6)
-+
-+#define AT803X_DEBUG_REG_1F 0x1F
-+#define AT803X_DEBUG_PLL_ON BIT(2)
-+#define AT803X_DEBUG_RGMII_1V8 BIT(3)
-+
-+#define MDIO_AZ_DEBUG 0x800D
-+
-+/* AT803x supports either the XTAL input pad, an internal PLL or the
-+ * DSP as clock reference for the clock output pad. The XTAL reference
-+ * is only used for 25 MHz output, all other frequencies need the PLL.
-+ * The DSP as a clock reference is used in synchronous ethernet
-+ * applications.
-+ *
-+ * By default the PLL is only enabled if there is a link. Otherwise
-+ * the PHY will go into low power state and disabled the PLL. You can
-+ * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always
-+ * enabled.
-+ */
-+#define AT803X_MMD7_CLK25M 0x8016
-+#define AT803X_CLK_OUT_MASK GENMASK(4, 2)
-+#define AT803X_CLK_OUT_25MHZ_XTAL 0
-+#define AT803X_CLK_OUT_25MHZ_DSP 1
-+#define AT803X_CLK_OUT_50MHZ_PLL 2
-+#define AT803X_CLK_OUT_50MHZ_DSP 3
-+#define AT803X_CLK_OUT_62_5MHZ_PLL 4
-+#define AT803X_CLK_OUT_62_5MHZ_DSP 5
-+#define AT803X_CLK_OUT_125MHZ_PLL 6
-+#define AT803X_CLK_OUT_125MHZ_DSP 7
-+
-+/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask
-+ * but doesn't support choosing between XTAL/PLL and DSP.
-+ */
-+#define AT8035_CLK_OUT_MASK GENMASK(4, 3)
-+
-+#define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7)
-+#define AT803X_CLK_OUT_STRENGTH_FULL 0
-+#define AT803X_CLK_OUT_STRENGTH_HALF 1
-+#define AT803X_CLK_OUT_STRENGTH_QUARTER 2
-+
-+#define AT803X_DEFAULT_DOWNSHIFT 5
-+#define AT803X_MIN_DOWNSHIFT 2
-+#define AT803X_MAX_DOWNSHIFT 9
-+
-+#define AT803X_MMD3_SMARTEEE_CTL1 0x805b
-+#define AT803X_MMD3_SMARTEEE_CTL2 0x805c
-+#define AT803X_MMD3_SMARTEEE_CTL3 0x805d
-+#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8)
-+
-+#define ATH9331_PHY_ID 0x004dd041
-+#define ATH8030_PHY_ID 0x004dd076
-+#define ATH8031_PHY_ID 0x004dd074
-+#define ATH8032_PHY_ID 0x004dd023
-+#define ATH8035_PHY_ID 0x004dd072
-+#define AT8030_PHY_ID_MASK 0xffffffef
-+
-+#define QCA8081_PHY_ID 0x004dd101
-+
-+#define QCA8327_A_PHY_ID 0x004dd033
-+#define QCA8327_B_PHY_ID 0x004dd034
-+#define QCA8337_PHY_ID 0x004dd036
-+#define QCA9561_PHY_ID 0x004dd042
-+#define QCA8K_PHY_ID_MASK 0xffffffff
-+
-+#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0)
-+
-+#define AT803X_PAGE_FIBER 0
-+#define AT803X_PAGE_COPPER 1
-+
-+/* don't turn off internal PLL */
-+#define AT803X_KEEP_PLL_ENABLED BIT(0)
-+#define AT803X_DISABLE_SMARTEEE BIT(1)
-+
-+/* disable hibernation mode */
-+#define AT803X_DISABLE_HIBERNATION_MODE BIT(2)
-+
-+/* ADC threshold */
-+#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80
-+#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0)
-+#define QCA808X_ADC_THRESHOLD_80MV 0
-+#define QCA808X_ADC_THRESHOLD_100MV 0xf0
-+#define QCA808X_ADC_THRESHOLD_200MV 0x0f
-+#define QCA808X_ADC_THRESHOLD_300MV 0xff
-+
-+/* CLD control */
-+#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007
-+#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4)
-+#define QCA808X_8023AZ_AFE_EN 0x90
-+
-+/* AZ control */
-+#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008
-+#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32
-+
-+#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014
-+#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529
-+
-+#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E
-+#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341
-+
-+#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E
-+#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419
-+
-+#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020
-+#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341
-+
-+#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c
-+#define QCA808X_TOP_OPTION1_DATA 0x0
-+
-+#define QCA808X_PHY_MMD3_DEBUG_1 0xa100
-+#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203
-+#define QCA808X_PHY_MMD3_DEBUG_2 0xa101
-+#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad
-+#define QCA808X_PHY_MMD3_DEBUG_3 0xa103
-+#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698
-+#define QCA808X_PHY_MMD3_DEBUG_4 0xa105
-+#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001
-+#define QCA808X_PHY_MMD3_DEBUG_5 0xa106
-+#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111
-+#define QCA808X_PHY_MMD3_DEBUG_6 0xa011
-+#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85
-+
-+/* master/slave seed config */
-+#define QCA808X_PHY_DEBUG_LOCAL_SEED 9
-+#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1)
-+#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2)
-+#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32
-+
-+/* Hibernation yields lower power consumpiton in contrast with normal operation mode.
-+ * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s.
-+ */
-+#define QCA808X_DBG_AN_TEST 0xb
-+#define QCA808X_HIBERNATION_EN BIT(15)
-+
-+#define QCA808X_CDT_ENABLE_TEST BIT(15)
-+#define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
-+#define QCA808X_CDT_STATUS BIT(11)
-+#define QCA808X_CDT_LENGTH_UNIT BIT(10)
-+
-+#define QCA808X_MMD3_CDT_STATUS 0x8064
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
-+#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
-+#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
-+
-+#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
-+#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
-+#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
-+#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
-+
-+#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
-+#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
-+#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
-+#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
-+#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
-+
-+#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
-+#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
-+#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
-+#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
-+
-+/* NORMAL are MDI with type set to 0 */
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI1)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI1)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI2)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI2)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI3)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI3)
-+
-+/* Added for reference of existence but should be handled by wait_for_completion already */
-+#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
-+
-+#define QCA808X_MMD7_LED_GLOBAL 0x8073
-+#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
-+#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
-+/* Values are the same for both BLINK_1 and BLINK_2 */
-+#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
-+#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
-+#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
-+#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
-+#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
-+#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
-+#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
-+#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
-+#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
-+#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
-+#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
-+#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
-+#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
-+#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
-+#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
-+#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
-+#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
-+#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
-+
-+#define QCA808X_MMD7_LED2_CTRL 0x8074
-+#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075
-+#define QCA808X_MMD7_LED1_CTRL 0x8076
-+#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077
-+#define QCA808X_MMD7_LED0_CTRL 0x8078
-+#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))
-+
-+/* LED hw control pattern is the same for every LED */
-+#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
-+#define QCA808X_LED_SPEED2500_ON BIT(15)
-+#define QCA808X_LED_SPEED2500_BLINK BIT(14)
-+/* Follow blink trigger even if duplex or speed condition doesn't match */
-+#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
-+#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
-+#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
-+#define QCA808X_LED_TX_BLINK BIT(10)
-+#define QCA808X_LED_RX_BLINK BIT(9)
-+#define QCA808X_LED_TX_ON_10MS BIT(8)
-+#define QCA808X_LED_RX_ON_10MS BIT(7)
-+#define QCA808X_LED_SPEED1000_ON BIT(6)
-+#define QCA808X_LED_SPEED100_ON BIT(5)
-+#define QCA808X_LED_SPEED10_ON BIT(4)
-+#define QCA808X_LED_COLLISION_BLINK BIT(3)
-+#define QCA808X_LED_SPEED1000_BLINK BIT(2)
-+#define QCA808X_LED_SPEED100_BLINK BIT(1)
-+#define QCA808X_LED_SPEED10_BLINK BIT(0)
-+
-+#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079
-+#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))
-+
-+/* LED force ctrl is the same for every LED
-+ * No documentation exist for this, not even internal one
-+ * with NDA as QCOM gives only info about configuring
-+ * hw control pattern rules and doesn't indicate any way
-+ * to force the LED to specific mode.
-+ * These define comes from reverse and testing and maybe
-+ * lack of some info or some info are not entirely correct.
-+ * For the basic LED control and hw control these finding
-+ * are enough to support LED control in all the required APIs.
-+ *
-+ * On doing some comparison with implementation with qca807x,
-+ * it was found that it's 1:1 equal to it and confirms all the
-+ * reverse done. It was also found further specification with the
-+ * force mode and the blink modes.
-+ */
-+#define QCA808X_LED_FORCE_EN BIT(15)
-+#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
-+#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
-+#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
-+#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
-+#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
-+
-+#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a
-+/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
-+ * LED to active high. It's not clear what BIT 3 and BIT 4 does.
-+ */
-+#define QCA808X_LED_ACTIVE_HIGH BIT(6)
-+
-+/* QCA808X 1G chip type */
-+#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
-+#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
-+
-+#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072
-+#define QCA8081_PHY_FIFO_RSTN BIT(11)
-+
-+MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
-+MODULE_AUTHOR("Matus Ujhelyi");
-+MODULE_LICENSE("GPL");
-+
-+enum stat_access_type {
-+ PHY,
-+ MMD
-+};
-+
-+struct at803x_hw_stat {
-+ const char *string;
-+ u8 reg;
-+ u32 mask;
-+ enum stat_access_type access_type;
-+};
-+
-+static struct at803x_hw_stat qca83xx_hw_stats[] = {
-+ { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
-+ { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
-+ { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
-+};
-+
-+struct at803x_ss_mask {
-+ u16 speed_mask;
-+ u8 speed_shift;
-+};
-+
-+struct at803x_priv {
-+ int flags;
-+ u16 clk_25m_reg;
-+ u16 clk_25m_mask;
-+ u8 smarteee_lpi_tw_1g;
-+ u8 smarteee_lpi_tw_100m;
-+ bool is_fiber;
-+ bool is_1000basex;
-+ struct regulator_dev *vddio_rdev;
-+ struct regulator_dev *vddh_rdev;
-+ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
-+ int led_polarity_mode;
-+};
-+
-+struct at803x_context {
-+ u16 bmcr;
-+ u16 advertise;
-+ u16 control1000;
-+ u16 int_enable;
-+ u16 smart_speed;
-+ u16 led_control;
-+};
-+
-+static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
-+{
-+ int ret;
-+
-+ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
-+ if (ret < 0)
-+ return ret;
-+
-+ return phy_write(phydev, AT803X_DEBUG_DATA, data);
-+}
-+
-+static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
-+{
-+ int ret;
-+
-+ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
-+ if (ret < 0)
-+ return ret;
-+
-+ return phy_read(phydev, AT803X_DEBUG_DATA);
-+}
-+
-+static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
-+ u16 clear, u16 set)
-+{
-+ u16 val;
-+ int ret;
-+
-+ ret = at803x_debug_reg_read(phydev, reg);
-+ if (ret < 0)
-+ return ret;
-+
-+ val = ret & 0xffff;
-+ val &= ~clear;
-+ val |= set;
-+
-+ return phy_write(phydev, AT803X_DEBUG_DATA, val);
-+}
-+
-+static int at803x_write_page(struct phy_device *phydev, int page)
-+{
-+ int mask;
-+ int set;
-+
-+ if (page == AT803X_PAGE_COPPER) {
-+ set = AT803X_BT_BX_REG_SEL;
-+ mask = 0;
-+ } else {
-+ set = 0;
-+ mask = AT803X_BT_BX_REG_SEL;
-+ }
-+
-+ return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set);
-+}
-+
-+static int at803x_read_page(struct phy_device *phydev)
-+{
-+ int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG);
-+
-+ if (ccr < 0)
-+ return ccr;
-+
-+ if (ccr & AT803X_BT_BX_REG_SEL)
-+ return AT803X_PAGE_COPPER;
-+
-+ return AT803X_PAGE_FIBER;
-+}
-+
-+static int at803x_enable_rx_delay(struct phy_device *phydev)
-+{
-+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0,
-+ AT803X_DEBUG_RX_CLK_DLY_EN);
-+}
-+
-+static int at803x_enable_tx_delay(struct phy_device *phydev)
-+{
-+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0,
-+ AT803X_DEBUG_TX_CLK_DLY_EN);
-+}
-+
-+static int at803x_disable_rx_delay(struct phy_device *phydev)
-+{
-+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-+ AT803X_DEBUG_RX_CLK_DLY_EN, 0);
-+}
-+
-+static int at803x_disable_tx_delay(struct phy_device *phydev)
-+{
-+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE,
-+ AT803X_DEBUG_TX_CLK_DLY_EN, 0);
-+}
-+
-+/* save relevant PHY registers to private copy */
-+static void at803x_context_save(struct phy_device *phydev,
-+ struct at803x_context *context)
-+{
-+ context->bmcr = phy_read(phydev, MII_BMCR);
-+ context->advertise = phy_read(phydev, MII_ADVERTISE);
-+ context->control1000 = phy_read(phydev, MII_CTRL1000);
-+ context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE);
-+ context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED);
-+ context->led_control = phy_read(phydev, AT803X_LED_CONTROL);
-+}
-+
-+/* restore relevant PHY registers from private copy */
-+static void at803x_context_restore(struct phy_device *phydev,
-+ const struct at803x_context *context)
-+{
-+ phy_write(phydev, MII_BMCR, context->bmcr);
-+ phy_write(phydev, MII_ADVERTISE, context->advertise);
-+ phy_write(phydev, MII_CTRL1000, context->control1000);
-+ phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable);
-+ phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed);
-+ phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
-+}
-+
-+static int at803x_set_wol(struct phy_device *phydev,
-+ struct ethtool_wolinfo *wol)
-+{
-+ int ret, irq_enabled;
-+
-+ if (wol->wolopts & WAKE_MAGIC) {
-+ struct net_device *ndev = phydev->attached_dev;
-+ const u8 *mac;
-+ unsigned int i;
-+ static const unsigned int offsets[] = {
-+ AT803X_LOC_MAC_ADDR_32_47_OFFSET,
-+ AT803X_LOC_MAC_ADDR_16_31_OFFSET,
-+ AT803X_LOC_MAC_ADDR_0_15_OFFSET,
-+ };
-+
-+ if (!ndev)
-+ return -ENODEV;
-+
-+ mac = (const u8 *)ndev->dev_addr;
-+
-+ if (!is_valid_ether_addr(mac))
-+ return -EINVAL;
-+
-+ for (i = 0; i < 3; i++)
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
-+ mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
-+
-+ /* Enable WOL interrupt */
-+ ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
-+ if (ret)
-+ return ret;
-+ } else {
-+ /* Disable WOL interrupt */
-+ ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ /* Clear WOL status */
-+ ret = phy_read(phydev, AT803X_INTR_STATUS);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Check if there are other interrupts except for WOL triggered when PHY is
-+ * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
-+ * be passed up to the interrupt PIN.
-+ */
-+ irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
-+ if (irq_enabled < 0)
-+ return irq_enabled;
-+
-+ irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
-+ if (ret & irq_enabled && !phy_polling_mode(phydev))
-+ phy_trigger_machine(phydev);
-+
-+ return 0;
-+}
-+
-+static void at803x_get_wol(struct phy_device *phydev,
-+ struct ethtool_wolinfo *wol)
-+{
-+ int value;
-+
-+ wol->supported = WAKE_MAGIC;
-+ wol->wolopts = 0;
-+
-+ value = phy_read(phydev, AT803X_INTR_ENABLE);
-+ if (value < 0)
-+ return;
-+
-+ if (value & AT803X_INTR_ENABLE_WOL)
-+ wol->wolopts |= WAKE_MAGIC;
-+}
-+
-+static int qca83xx_get_sset_count(struct phy_device *phydev)
-+{
-+ return ARRAY_SIZE(qca83xx_hw_stats);
-+}
-+
-+static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
-+{
-+ int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
-+ strscpy(data + i * ETH_GSTRING_LEN,
-+ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
-+ }
-+}
-+
-+static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
-+{
-+ struct at803x_hw_stat stat = qca83xx_hw_stats[i];
-+ struct at803x_priv *priv = phydev->priv;
-+ int val;
-+ u64 ret;
-+
-+ if (stat.access_type == MMD)
-+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
-+ else
-+ val = phy_read(phydev, stat.reg);
-+
-+ if (val < 0) {
-+ ret = U64_MAX;
-+ } else {
-+ val = val & stat.mask;
-+ priv->stats[i] += val;
-+ ret = priv->stats[i];
-+ }
-+
-+ return ret;
-+}
-+
-+static void qca83xx_get_stats(struct phy_device *phydev,
-+ struct ethtool_stats *stats, u64 *data)
-+{
-+ int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
-+ data[i] = qca83xx_get_stat(phydev, i);
-+}
-+
-+static int at803x_suspend(struct phy_device *phydev)
-+{
-+ int value;
-+ int wol_enabled;
-+
-+ value = phy_read(phydev, AT803X_INTR_ENABLE);
-+ wol_enabled = value & AT803X_INTR_ENABLE_WOL;
-+
-+ if (wol_enabled)
-+ value = BMCR_ISOLATE;
-+ else
-+ value = BMCR_PDOWN;
-+
-+ phy_modify(phydev, MII_BMCR, 0, value);
-+
-+ return 0;
-+}
-+
-+static int at803x_resume(struct phy_device *phydev)
-+{
-+ return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
-+}
-+
-+static int at803x_parse_dt(struct phy_device *phydev)
-+{
-+ struct device_node *node = phydev->mdio.dev.of_node;
-+ struct at803x_priv *priv = phydev->priv;
-+ u32 freq, strength, tw;
-+ unsigned int sel;
-+ int ret;
-+
-+ if (!IS_ENABLED(CONFIG_OF_MDIO))
-+ return 0;
-+
-+ if (of_property_read_bool(node, "qca,disable-smarteee"))
-+ priv->flags |= AT803X_DISABLE_SMARTEEE;
-+
-+ if (of_property_read_bool(node, "qca,disable-hibernation-mode"))
-+ priv->flags |= AT803X_DISABLE_HIBERNATION_MODE;
-+
-+ if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) {
-+ if (!tw || tw > 255) {
-+ phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n");
-+ return -EINVAL;
-+ }
-+ priv->smarteee_lpi_tw_1g = tw;
-+ }
-+
-+ if (!of_property_read_u32(node, "qca,smarteee-tw-us-100m", &tw)) {
-+ if (!tw || tw > 255) {
-+ phydev_err(phydev, "invalid qca,smarteee-tw-us-100m\n");
-+ return -EINVAL;
-+ }
-+ priv->smarteee_lpi_tw_100m = tw;
-+ }
-+
-+ ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq);
-+ if (!ret) {
-+ switch (freq) {
-+ case 25000000:
-+ sel = AT803X_CLK_OUT_25MHZ_XTAL;
-+ break;
-+ case 50000000:
-+ sel = AT803X_CLK_OUT_50MHZ_PLL;
-+ break;
-+ case 62500000:
-+ sel = AT803X_CLK_OUT_62_5MHZ_PLL;
-+ break;
-+ case 125000000:
-+ sel = AT803X_CLK_OUT_125MHZ_PLL;
-+ break;
-+ default:
-+ phydev_err(phydev, "invalid qca,clk-out-frequency\n");
-+ return -EINVAL;
-+ }
-+
-+ priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel);
-+ priv->clk_25m_mask |= AT803X_CLK_OUT_MASK;
-+ }
-+
-+ ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
-+ if (!ret) {
-+ priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK;
-+ switch (strength) {
-+ case AR803X_STRENGTH_FULL:
-+ priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL;
-+ break;
-+ case AR803X_STRENGTH_HALF:
-+ priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF;
-+ break;
-+ case AR803X_STRENGTH_QUARTER:
-+ priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER;
-+ break;
-+ default:
-+ phydev_err(phydev, "invalid qca,clk-out-strength\n");
-+ return -EINVAL;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static int at803x_probe(struct phy_device *phydev)
-+{
-+ struct device *dev = &phydev->mdio.dev;
-+ struct at803x_priv *priv;
-+ int ret;
-+
-+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ /* Init LED polarity mode to -1 */
-+ priv->led_polarity_mode = -1;
-+
-+ phydev->priv = priv;
-+
-+ ret = at803x_parse_dt(phydev);
-+ if (ret)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int at803x_get_features(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ int err;
-+
-+ err = genphy_read_abilities(phydev);
-+ if (err)
-+ return err;
-+
-+ if (phydev->drv->phy_id != ATH8031_PHY_ID)
-+ return 0;
-+
-+ /* AR8031/AR8033 have different status registers
-+ * for copper and fiber operation. However, the
-+ * extended status register is the same for both
-+ * operation modes.
-+ *
-+ * As a result of that, ESTATUS_1000_XFULL is set
-+ * to 1 even when operating in copper TP mode.
-+ *
-+ * Remove this mode from the supported link modes
-+ * when not operating in 1000BaseX mode.
-+ */
-+ if (!priv->is_1000basex)
-+ linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
-+ phydev->supported);
-+
-+ return 0;
-+}
-+
-+static int at803x_smarteee_config(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ u16 mask = 0, val = 0;
-+ int ret;
-+
-+ if (priv->flags & AT803X_DISABLE_SMARTEEE)
-+ return phy_modify_mmd(phydev, MDIO_MMD_PCS,
-+ AT803X_MMD3_SMARTEEE_CTL3,
-+ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0);
-+
-+ if (priv->smarteee_lpi_tw_1g) {
-+ mask |= 0xff00;
-+ val |= priv->smarteee_lpi_tw_1g << 8;
-+ }
-+ if (priv->smarteee_lpi_tw_100m) {
-+ mask |= 0x00ff;
-+ val |= priv->smarteee_lpi_tw_100m;
-+ }
-+ if (!mask)
-+ return 0;
-+
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1,
-+ mask, val);
-+ if (ret)
-+ return ret;
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3,
-+ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN,
-+ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN);
-+}
-+
-+static int at803x_clk_out_config(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+
-+ if (!priv->clk_25m_mask)
-+ return 0;
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M,
-+ priv->clk_25m_mask, priv->clk_25m_reg);
-+}
-+
-+static int at8031_pll_config(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+
-+ /* The default after hardware reset is PLL OFF. After a soft reset, the
-+ * values are retained.
-+ */
-+ if (priv->flags & AT803X_KEEP_PLL_ENABLED)
-+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-+ 0, AT803X_DEBUG_PLL_ON);
-+ else
-+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-+ AT803X_DEBUG_PLL_ON, 0);
-+}
-+
-+static int at803x_hibernation_mode_config(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+
-+ /* The default after hardware reset is hibernation mode enabled. After
-+ * software reset, the value is retained.
-+ */
-+ if (!(priv->flags & AT803X_DISABLE_HIBERNATION_MODE))
-+ return 0;
-+
-+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
-+ AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0);
-+}
-+
-+static int at803x_config_init(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ /* The RX and TX delay default is:
-+ * after HW reset: RX delay enabled and TX delay disabled
-+ * after SW reset: RX delay enabled, while TX delay retains the
-+ * value before reset.
-+ */
-+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
-+ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
-+ ret = at803x_enable_rx_delay(phydev);
-+ else
-+ ret = at803x_disable_rx_delay(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
-+ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
-+ ret = at803x_enable_tx_delay(phydev);
-+ else
-+ ret = at803x_disable_tx_delay(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = at803x_smarteee_config(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = at803x_clk_out_config(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = at803x_hibernation_mode_config(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Ar803x extended next page bit is enabled by default. Cisco
-+ * multigig switches read this bit and attempt to negotiate 10Gbps
-+ * rates even if the next page bit is disabled. This is incorrect
-+ * behaviour but we still need to accommodate it. XNP is only needed
-+ * for 10Gbps support, so disable XNP.
-+ */
-+ return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0);
-+}
-+
-+static int at803x_ack_interrupt(struct phy_device *phydev)
-+{
-+ int err;
-+
-+ err = phy_read(phydev, AT803X_INTR_STATUS);
-+
-+ return (err < 0) ? err : 0;
-+}
-+
-+static int at803x_config_intr(struct phy_device *phydev)
-+{
-+ int err;
-+ int value;
-+
-+ value = phy_read(phydev, AT803X_INTR_ENABLE);
-+
-+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
-+ /* Clear any pending interrupts */
-+ err = at803x_ack_interrupt(phydev);
-+ if (err)
-+ return err;
-+
-+ value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
-+ value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
-+ value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
-+ value |= AT803X_INTR_ENABLE_LINK_FAIL;
-+ value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
-+
-+ err = phy_write(phydev, AT803X_INTR_ENABLE, value);
-+ } else {
-+ err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
-+ if (err)
-+ return err;
-+
-+ /* Clear any pending interrupts */
-+ err = at803x_ack_interrupt(phydev);
-+ }
-+
-+ return err;
-+}
-+
-+static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)
-+{
-+ int irq_status, int_enabled;
-+
-+ irq_status = phy_read(phydev, AT803X_INTR_STATUS);
-+ if (irq_status < 0) {
-+ phy_error(phydev);
-+ return IRQ_NONE;
-+ }
-+
-+ /* Read the current enabled interrupts */
-+ int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
-+ if (int_enabled < 0) {
-+ phy_error(phydev);
-+ return IRQ_NONE;
-+ }
-+
-+ /* See if this was one of our enabled interrupts */
-+ if (!(irq_status & int_enabled))
-+ return IRQ_NONE;
-+
-+ phy_trigger_machine(phydev);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static void at803x_link_change_notify(struct phy_device *phydev)
-+{
-+ /*
-+ * Conduct a hardware reset for AT8030 every time a link loss is
-+ * signalled. This is necessary to circumvent a hardware bug that
-+ * occurs when the cable is unplugged while TX packets are pending
-+ * in the FIFO. In such cases, the FIFO enters an error mode it
-+ * cannot recover from by software.
-+ */
-+ if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) {
-+ struct at803x_context context;
-+
-+ at803x_context_save(phydev, &context);
-+
-+ phy_device_reset(phydev, 1);
-+ usleep_range(1000, 2000);
-+ phy_device_reset(phydev, 0);
-+ usleep_range(1000, 2000);
-+
-+ at803x_context_restore(phydev, &context);
-+
-+ phydev_dbg(phydev, "%s(): phy was reset\n", __func__);
-+ }
-+}
-+
-+static int at803x_read_specific_status(struct phy_device *phydev,
-+ struct at803x_ss_mask ss_mask)
-+{
-+ int ss;
-+
-+ /* Read the AT8035 PHY-Specific Status register, which indicates the
-+ * speed and duplex that the PHY is actually using, irrespective of
-+ * whether we are in autoneg mode or not.
-+ */
-+ ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
-+ if (ss < 0)
-+ return ss;
-+
-+ if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
-+ int sfc, speed;
-+
-+ sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
-+ if (sfc < 0)
-+ return sfc;
-+
-+ speed = ss & ss_mask.speed_mask;
-+ speed >>= ss_mask.speed_shift;
-+
-+ switch (speed) {
-+ case AT803X_SS_SPEED_10:
-+ phydev->speed = SPEED_10;
-+ break;
-+ case AT803X_SS_SPEED_100:
-+ phydev->speed = SPEED_100;
-+ break;
-+ case AT803X_SS_SPEED_1000:
-+ phydev->speed = SPEED_1000;
-+ break;
-+ case QCA808X_SS_SPEED_2500:
-+ phydev->speed = SPEED_2500;
-+ break;
-+ }
-+ if (ss & AT803X_SS_DUPLEX)
-+ phydev->duplex = DUPLEX_FULL;
-+ else
-+ phydev->duplex = DUPLEX_HALF;
-+
-+ if (ss & AT803X_SS_MDIX)
-+ phydev->mdix = ETH_TP_MDI_X;
-+ else
-+ phydev->mdix = ETH_TP_MDI;
-+
-+ switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
-+ case AT803X_SFC_MANUAL_MDI:
-+ phydev->mdix_ctrl = ETH_TP_MDI;
-+ break;
-+ case AT803X_SFC_MANUAL_MDIX:
-+ phydev->mdix_ctrl = ETH_TP_MDI_X;
-+ break;
-+ case AT803X_SFC_AUTOMATIC_CROSSOVER:
-+ phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
-+ break;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static int at803x_read_status(struct phy_device *phydev)
-+{
-+ struct at803x_ss_mask ss_mask = { 0 };
-+ int err, old_link = phydev->link;
-+
-+ /* Update the link, but return if there was an error */
-+ err = genphy_update_link(phydev);
-+ if (err)
-+ return err;
-+
-+ /* why bother the PHY if nothing can have changed */
-+ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
-+ return 0;
-+
-+ phydev->speed = SPEED_UNKNOWN;
-+ phydev->duplex = DUPLEX_UNKNOWN;
-+ phydev->pause = 0;
-+ phydev->asym_pause = 0;
-+
-+ err = genphy_read_lpa(phydev);
-+ if (err < 0)
-+ return err;
-+
-+ ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
-+ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
-+ err = at803x_read_specific_status(phydev, ss_mask);
-+ if (err < 0)
-+ return err;
-+
-+ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
-+ phy_resolve_aneg_pause(phydev);
-+
-+ return 0;
-+}
-+
-+static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
-+{
-+ u16 val;
-+
-+ switch (ctrl) {
-+ case ETH_TP_MDI:
-+ val = AT803X_SFC_MANUAL_MDI;
-+ break;
-+ case ETH_TP_MDI_X:
-+ val = AT803X_SFC_MANUAL_MDIX;
-+ break;
-+ case ETH_TP_MDI_AUTO:
-+ val = AT803X_SFC_AUTOMATIC_CROSSOVER;
-+ break;
-+ default:
-+ return 0;
-+ }
-+
-+ return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
-+ AT803X_SFC_MDI_CROSSOVER_MODE_M,
-+ FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
-+}
-+
-+static int at803x_prepare_config_aneg(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Changes of the midx bits are disruptive to the normal operation;
-+ * therefore any changes to these registers must be followed by a
-+ * software reset to take effect.
-+ */
-+ if (ret == 1) {
-+ ret = genphy_soft_reset(phydev);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int at803x_config_aneg(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ int ret;
-+
-+ ret = at803x_prepare_config_aneg(phydev);
-+ if (ret)
-+ return ret;
-+
-+ if (priv->is_1000basex)
-+ return genphy_c37_config_aneg(phydev);
-+
-+ return genphy_config_aneg(phydev);
-+}
-+
-+static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
-+{
-+ int val;
-+
-+ val = phy_read(phydev, AT803X_SMART_SPEED);
-+ if (val < 0)
-+ return val;
-+
-+ if (val & AT803X_SMART_SPEED_ENABLE)
-+ *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
-+ else
-+ *d = DOWNSHIFT_DEV_DISABLE;
-+
-+ return 0;
-+}
-+
-+static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
-+{
-+ u16 mask, set;
-+ int ret;
-+
-+ switch (cnt) {
-+ case DOWNSHIFT_DEV_DEFAULT_COUNT:
-+ cnt = AT803X_DEFAULT_DOWNSHIFT;
-+ fallthrough;
-+ case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
-+ set = AT803X_SMART_SPEED_ENABLE |
-+ AT803X_SMART_SPEED_BYPASS_TIMER |
-+ FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
-+ mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
-+ break;
-+ case DOWNSHIFT_DEV_DISABLE:
-+ set = 0;
-+ mask = AT803X_SMART_SPEED_ENABLE |
-+ AT803X_SMART_SPEED_BYPASS_TIMER;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
-+
-+ /* After changing the smart speed settings, we need to perform a
-+ * software reset, use phy_init_hw() to make sure we set the
-+ * reapply any values which might got lost during software reset.
-+ */
-+ if (ret == 1)
-+ ret = phy_init_hw(phydev);
-+
-+ return ret;
-+}
-+
-+static int at803x_get_tunable(struct phy_device *phydev,
-+ struct ethtool_tunable *tuna, void *data)
-+{
-+ switch (tuna->id) {
-+ case ETHTOOL_PHY_DOWNSHIFT:
-+ return at803x_get_downshift(phydev, data);
-+ default:
-+ return -EOPNOTSUPP;
-+ }
-+}
-+
-+static int at803x_set_tunable(struct phy_device *phydev,
-+ struct ethtool_tunable *tuna, const void *data)
-+{
-+ switch (tuna->id) {
-+ case ETHTOOL_PHY_DOWNSHIFT:
-+ return at803x_set_downshift(phydev, *(const u8 *)data);
-+ default:
-+ return -EOPNOTSUPP;
-+ }
-+}
-+
-+static int at803x_cable_test_result_trans(u16 status)
-+{
-+ switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
-+ case AT803X_CDT_STATUS_STAT_NORMAL:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
-+ case AT803X_CDT_STATUS_STAT_SHORT:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
-+ case AT803X_CDT_STATUS_STAT_OPEN:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
-+ case AT803X_CDT_STATUS_STAT_FAIL:
-+ default:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
-+ }
-+}
-+
-+static bool at803x_cdt_test_failed(u16 status)
-+{
-+ return FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status) ==
-+ AT803X_CDT_STATUS_STAT_FAIL;
-+}
-+
-+static bool at803x_cdt_fault_length_valid(u16 status)
-+{
-+ switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
-+ case AT803X_CDT_STATUS_STAT_OPEN:
-+ case AT803X_CDT_STATUS_STAT_SHORT:
-+ return true;
-+ }
-+ return false;
-+}
-+
-+static int at803x_cdt_fault_length(int dt)
-+{
-+ /* According to the datasheet the distance to the fault is
-+ * DELTA_TIME * 0.824 meters.
-+ *
-+ * The author suspect the correct formula is:
-+ *
-+ * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
-+ *
-+ * where c is the speed of light, VF is the velocity factor of
-+ * the twisted pair cable, 125MHz the counter frequency and
-+ * we need to divide by 2 because the hardware will measure the
-+ * round trip time to the fault and back to the PHY.
-+ *
-+ * With a VF of 0.69 we get the factor 0.824 mentioned in the
-+ * datasheet.
-+ */
-+ return (dt * 824) / 10;
-+}
-+
-+static int at803x_cdt_start(struct phy_device *phydev,
-+ u32 cdt_start)
-+{
-+ return phy_write(phydev, AT803X_CDT, cdt_start);
-+}
-+
-+static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
-+ u32 cdt_en)
-+{
-+ int val, ret;
-+
-+ /* One test run takes about 25ms */
-+ ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
-+ !(val & cdt_en),
-+ 30000, 100000, true);
-+
-+ return ret < 0 ? ret : 0;
-+}
-+
-+static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair)
-+{
-+ static const int ethtool_pair[] = {
-+ ETHTOOL_A_CABLE_PAIR_A,
-+ ETHTOOL_A_CABLE_PAIR_B,
-+ ETHTOOL_A_CABLE_PAIR_C,
-+ ETHTOOL_A_CABLE_PAIR_D,
-+ };
-+ int ret, val;
-+
-+ val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
-+ AT803X_CDT_ENABLE_TEST;
-+ ret = at803x_cdt_start(phydev, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST);
-+ if (ret)
-+ return ret;
-+
-+ val = phy_read(phydev, AT803X_CDT_STATUS);
-+ if (val < 0)
-+ return val;
-+
-+ if (at803x_cdt_test_failed(val))
-+ return 0;
-+
-+ ethnl_cable_test_result(phydev, ethtool_pair[pair],
-+ at803x_cable_test_result_trans(val));
-+
-+ if (at803x_cdt_fault_length_valid(val)) {
-+ val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val);
-+ ethnl_cable_test_fault_length(phydev, ethtool_pair[pair],
-+ at803x_cdt_fault_length(val));
-+ }
-+
-+ return 1;
-+}
-+
-+static int at803x_cable_test_get_status(struct phy_device *phydev,
-+ bool *finished, unsigned long pair_mask)
-+{
-+ int retries = 20;
-+ int pair, ret;
-+
-+ *finished = false;
-+
-+ /* According to the datasheet the CDT can be performed when
-+ * there is no link partner or when the link partner is
-+ * auto-negotiating. Starting the test will restart the AN
-+ * automatically. It seems that doing this repeatedly we will
-+ * get a slot where our link partner won't disturb our
-+ * measurement.
-+ */
-+ while (pair_mask && retries--) {
-+ for_each_set_bit(pair, &pair_mask, 4) {
-+ ret = at803x_cable_test_one_pair(phydev, pair);
-+ if (ret < 0)
-+ return ret;
-+ if (ret)
-+ clear_bit(pair, &pair_mask);
-+ }
-+ if (pair_mask)
-+ msleep(250);
-+ }
-+
-+ *finished = true;
-+
-+ return 0;
-+}
-+
-+static void at803x_cable_test_autoneg(struct phy_device *phydev)
-+{
-+ /* Enable auto-negotiation, but advertise no capabilities, no link
-+ * will be established. A restart of the auto-negotiation is not
-+ * required, because the cable test will automatically break the link.
-+ */
-+ phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
-+ phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
-+}
-+
-+static int at803x_cable_test_start(struct phy_device *phydev)
-+{
-+ at803x_cable_test_autoneg(phydev);
-+ /* we do all the (time consuming) work later */
-+ return 0;
-+}
-+
-+static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
-+ unsigned int selector)
-+{
-+ struct phy_device *phydev = rdev_get_drvdata(rdev);
-+
-+ if (selector)
-+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-+ 0, AT803X_DEBUG_RGMII_1V8);
-+ else
-+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
-+ AT803X_DEBUG_RGMII_1V8, 0);
-+}
-+
-+static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
-+{
-+ struct phy_device *phydev = rdev_get_drvdata(rdev);
-+ int val;
-+
-+ val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
-+ if (val < 0)
-+ return val;
-+
-+ return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
-+}
-+
-+static const struct regulator_ops vddio_regulator_ops = {
-+ .list_voltage = regulator_list_voltage_table,
-+ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
-+ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
-+};
-+
-+static const unsigned int vddio_voltage_table[] = {
-+ 1500000,
-+ 1800000,
-+};
-+
-+static const struct regulator_desc vddio_desc = {
-+ .name = "vddio",
-+ .of_match = of_match_ptr("vddio-regulator"),
-+ .n_voltages = ARRAY_SIZE(vddio_voltage_table),
-+ .volt_table = vddio_voltage_table,
-+ .ops = &vddio_regulator_ops,
-+ .type = REGULATOR_VOLTAGE,
-+ .owner = THIS_MODULE,
-+};
-+
-+static const struct regulator_ops vddh_regulator_ops = {
-+};
-+
-+static const struct regulator_desc vddh_desc = {
-+ .name = "vddh",
-+ .of_match = of_match_ptr("vddh-regulator"),
-+ .n_voltages = 1,
-+ .fixed_uV = 2500000,
-+ .ops = &vddh_regulator_ops,
-+ .type = REGULATOR_VOLTAGE,
-+ .owner = THIS_MODULE,
-+};
-+
-+static int at8031_register_regulators(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ struct device *dev = &phydev->mdio.dev;
-+ struct regulator_config config = { };
-+
-+ config.dev = dev;
-+ config.driver_data = phydev;
-+
-+ priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
-+ if (IS_ERR(priv->vddio_rdev)) {
-+ phydev_err(phydev, "failed to register VDDIO regulator\n");
-+ return PTR_ERR(priv->vddio_rdev);
-+ }
-+
-+ priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
-+ if (IS_ERR(priv->vddh_rdev)) {
-+ phydev_err(phydev, "failed to register VDDH regulator\n");
-+ return PTR_ERR(priv->vddh_rdev);
-+ }
-+
-+ return 0;
-+}
-+
-+static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
-+{
-+ struct phy_device *phydev = upstream;
-+ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
-+ __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
-+ DECLARE_PHY_INTERFACE_MASK(interfaces);
-+ phy_interface_t iface;
-+
-+ linkmode_zero(phy_support);
-+ phylink_set(phy_support, 1000baseX_Full);
-+ phylink_set(phy_support, 1000baseT_Full);
-+ phylink_set(phy_support, Autoneg);
-+ phylink_set(phy_support, Pause);
-+ phylink_set(phy_support, Asym_Pause);
-+
-+ linkmode_zero(sfp_support);
-+ sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
-+ /* Some modules support 10G modes as well as others we support.
-+ * Mask out non-supported modes so the correct interface is picked.
-+ */
-+ linkmode_and(sfp_support, phy_support, sfp_support);
-+
-+ if (linkmode_empty(sfp_support)) {
-+ dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
-+ return -EINVAL;
-+ }
-+
-+ iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
-+
-+ /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
-+ * interface for use with SFP modules.
-+ * However, some copper modules detected as having a preferred SGMII
-+ * interface do default to and function in 1000Base-X mode, so just
-+ * print a warning and allow such modules, as they may have some chance
-+ * of working.
-+ */
-+ if (iface == PHY_INTERFACE_MODE_SGMII)
-+ dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
-+ else if (iface != PHY_INTERFACE_MODE_1000BASEX)
-+ return -EINVAL;
-+
-+ return 0;
-+}
-+
-+static const struct sfp_upstream_ops at8031_sfp_ops = {
-+ .attach = phy_sfp_attach,
-+ .detach = phy_sfp_detach,
-+ .module_insert = at8031_sfp_insert,
-+};
-+
-+static int at8031_parse_dt(struct phy_device *phydev)
-+{
-+ struct device_node *node = phydev->mdio.dev.of_node;
-+ struct at803x_priv *priv = phydev->priv;
-+ int ret;
-+
-+ if (of_property_read_bool(node, "qca,keep-pll-enabled"))
-+ priv->flags |= AT803X_KEEP_PLL_ENABLED;
-+
-+ ret = at8031_register_regulators(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
-+ "vddio");
-+ if (ret) {
-+ phydev_err(phydev, "failed to get VDDIO regulator\n");
-+ return ret;
-+ }
-+
-+ /* Only AR8031/8033 support 1000Base-X for SFP modules */
-+ return phy_sfp_probe(phydev, &at8031_sfp_ops);
-+}
-+
-+static int at8031_probe(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ int mode_cfg;
-+ int ccr;
-+ int ret;
-+
-+ ret = at803x_probe(phydev);
-+ if (ret)
-+ return ret;
-+
-+ /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
-+ * options.
-+ */
-+ ret = at8031_parse_dt(phydev);
-+ if (ret)
-+ return ret;
-+
-+ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
-+ if (ccr < 0)
-+ return ccr;
-+ mode_cfg = ccr & AT803X_MODE_CFG_MASK;
-+
-+ switch (mode_cfg) {
-+ case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
-+ case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
-+ priv->is_1000basex = true;
-+ fallthrough;
-+ case AT803X_MODE_CFG_FX100_RGMII_50OHM:
-+ case AT803X_MODE_CFG_FX100_RGMII_75OHM:
-+ priv->is_fiber = true;
-+ break;
-+ }
-+
-+ /* Disable WoL in 1588 register which is enabled
-+ * by default
-+ */
-+ return phy_modify_mmd(phydev, MDIO_MMD_PCS,
-+ AT803X_PHY_MMD3_WOL_CTRL,
-+ AT803X_WOL_EN, 0);
-+}
-+
-+static int at8031_config_init(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ int ret;
-+
-+ /* Some bootloaders leave the fiber page selected.
-+ * Switch to the appropriate page (fiber or copper), as otherwise we
-+ * read the PHY capabilities from the wrong page.
-+ */
-+ phy_lock_mdio_bus(phydev);
-+ ret = at803x_write_page(phydev,
-+ priv->is_fiber ? AT803X_PAGE_FIBER :
-+ AT803X_PAGE_COPPER);
-+ phy_unlock_mdio_bus(phydev);
-+ if (ret)
-+ return ret;
-+
-+ ret = at8031_pll_config(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ return at803x_config_init(phydev);
-+}
-+
-+static int at8031_set_wol(struct phy_device *phydev,
-+ struct ethtool_wolinfo *wol)
-+{
-+ int ret;
-+
-+ /* First setup MAC address and enable WOL interrupt */
-+ ret = at803x_set_wol(phydev, wol);
-+ if (ret)
-+ return ret;
-+
-+ if (wol->wolopts & WAKE_MAGIC)
-+ /* Enable WOL function for 1588 */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
-+ AT803X_PHY_MMD3_WOL_CTRL,
-+ 0, AT803X_WOL_EN);
-+ else
-+ /* Disable WoL function for 1588 */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
-+ AT803X_PHY_MMD3_WOL_CTRL,
-+ AT803X_WOL_EN, 0);
-+
-+ return ret;
-+}
-+
-+static int at8031_config_intr(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ int err, value = 0;
-+
-+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED &&
-+ priv->is_fiber) {
-+ /* Clear any pending interrupts */
-+ err = at803x_ack_interrupt(phydev);
-+ if (err)
-+ return err;
-+
-+ value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
-+ value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
-+
-+ err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value);
-+ if (err)
-+ return err;
-+ }
-+
-+ return at803x_config_intr(phydev);
-+}
-+
-+/* AR8031 and AR8033 share the same read status logic */
-+static int at8031_read_status(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+
-+ if (priv->is_1000basex)
-+ return genphy_c37_read_status(phydev);
-+
-+ return at803x_read_status(phydev);
-+}
-+
-+/* AR8031 and AR8035 share the same cable test get status reg */
-+static int at8031_cable_test_get_status(struct phy_device *phydev,
-+ bool *finished)
-+{
-+ return at803x_cable_test_get_status(phydev, finished, 0xf);
-+}
-+
-+/* AR8031 and AR8035 share the same cable test start logic */
-+static int at8031_cable_test_start(struct phy_device *phydev)
-+{
-+ at803x_cable_test_autoneg(phydev);
-+ phy_write(phydev, MII_CTRL1000, 0);
-+ /* we do all the (time consuming) work later */
-+ return 0;
-+}
-+
-+/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */
-+static int at8032_cable_test_get_status(struct phy_device *phydev,
-+ bool *finished)
-+{
-+ return at803x_cable_test_get_status(phydev, finished, 0x3);
-+}
-+
-+static int at8035_parse_dt(struct phy_device *phydev)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+
-+ /* Mask is set by the generic at803x_parse_dt
-+ * if property is set. Assume property is set
-+ * with the mask not zero.
-+ */
-+ if (priv->clk_25m_mask) {
-+ /* Fixup for the AR8030/AR8035. This chip has another mask and
-+ * doesn't support the DSP reference. Eg. the lowest bit of the
-+ * mask. The upper two bits select the same frequencies. Mask
-+ * the lowest bit here.
-+ *
-+ * Warning:
-+ * There was no datasheet for the AR8030 available so this is
-+ * just a guess. But the AR8035 is listed as pin compatible
-+ * to the AR8030 so there might be a good chance it works on
-+ * the AR8030 too.
-+ */
-+ priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
-+ priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
-+ }
-+
-+ return 0;
-+}
-+
-+/* AR8030 and AR8035 shared the same special mask for clk_25m */
-+static int at8035_probe(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = at803x_probe(phydev);
-+ if (ret)
-+ return ret;
-+
-+ return at8035_parse_dt(phydev);
-+}
-+
-+static int qca83xx_config_init(struct phy_device *phydev)
-+{
-+ u8 switch_revision;
-+
-+ switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
-+
-+ switch (switch_revision) {
-+ case 1:
-+ /* For 100M waveform */
-+ at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
-+ /* Turn on Gigabit clock */
-+ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
-+ break;
-+
-+ case 2:
-+ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
-+ fallthrough;
-+ case 4:
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
-+ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860);
-+ at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46);
-+ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000);
-+ break;
-+ }
-+
-+ /* Following original QCA sourcecode set port to prefer master */
-+ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
-+
-+ return 0;
-+}
-+
-+static int qca8327_config_init(struct phy_device *phydev)
-+{
-+ /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
-+ * Disable on init and enable only with 100m speed following
-+ * qca original source code.
-+ */
-+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
-+
-+ return qca83xx_config_init(phydev);
-+}
-+
-+static void qca83xx_link_change_notify(struct phy_device *phydev)
-+{
-+ /* Set DAC Amplitude adjustment to +6% for 100m on link running */
-+ if (phydev->state == PHY_RUNNING) {
-+ if (phydev->speed == SPEED_100)
-+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-+ QCA8327_DEBUG_MANU_CTRL_EN,
-+ QCA8327_DEBUG_MANU_CTRL_EN);
-+ } else {
-+ /* Reset DAC Amplitude adjustment */
-+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
-+ }
-+}
-+
-+static int qca83xx_resume(struct phy_device *phydev)
-+{
-+ int ret, val;
-+
-+ /* Skip reset if not suspended */
-+ if (!phydev->suspended)
-+ return 0;
-+
-+ /* Reinit the port, reset values set by suspend */
-+ qca83xx_config_init(phydev);
-+
-+ /* Reset the port on port resume */
-+ phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
-+
-+ /* On resume from suspend the switch execute a reset and
-+ * restart auto-negotiation. Wait for reset to complete.
-+ */
-+ ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
-+ 50000, 600000, true);
-+ if (ret)
-+ return ret;
-+
-+ usleep_range(1000, 2000);
-+
-+ return 0;
-+}
-+
-+static int qca83xx_suspend(struct phy_device *phydev)
-+{
-+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
-+ AT803X_DEBUG_GATE_CLK_IN1000, 0);
-+
-+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
-+ AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
-+ AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
-+
-+ return 0;
-+}
-+
-+static int qca8337_suspend(struct phy_device *phydev)
-+{
-+ /* Only QCA8337 support actual suspend. */
-+ genphy_suspend(phydev);
-+
-+ return qca83xx_suspend(phydev);
-+}
-+
-+static int qca8327_suspend(struct phy_device *phydev)
-+{
-+ u16 mask = 0;
-+
-+ /* QCA8327 cause port unreliability when phy suspend
-+ * is set.
-+ */
-+ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
-+ phy_modify(phydev, MII_BMCR, mask, 0);
-+
-+ return qca83xx_suspend(phydev);
-+}
-+
-+static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ /* Enable fast retrain */
-+ ret = genphy_c45_fast_retrain(phydev, true);
-+ if (ret)
-+ return ret;
-+
-+ phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
-+ QCA808X_TOP_OPTION1_DATA);
-+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
-+ QCA808X_MSE_THRESHOLD_20DB_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
-+ QCA808X_MSE_THRESHOLD_17DB_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
-+ QCA808X_MSE_THRESHOLD_27DB_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
-+ QCA808X_MSE_THRESHOLD_28DB_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
-+ QCA808X_MMD3_DEBUG_1_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
-+ QCA808X_MMD3_DEBUG_4_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
-+ QCA808X_MMD3_DEBUG_5_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
-+ QCA808X_MMD3_DEBUG_3_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
-+ QCA808X_MMD3_DEBUG_6_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
-+ QCA808X_MMD3_DEBUG_2_VALUE);
-+
-+ return 0;
-+}
-+
-+static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
-+{
-+ u16 seed_value;
-+
-+ if (!enable)
-+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
-+ QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
-+
-+ seed_value = get_random_u32_below(QCA808X_MASTER_SLAVE_SEED_RANGE);
-+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
-+ QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
-+ FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
-+ QCA808X_MASTER_SLAVE_SEED_ENABLE);
-+}
-+
-+static bool qca808x_is_prefer_master(struct phy_device *phydev)
-+{
-+ return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
-+ (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
-+}
-+
-+static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
-+{
-+ return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
-+}
-+
-+static int qca808x_config_init(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ /* Active adc&vga on 802.3az for the link 1000M and 100M */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
-+ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
-+ if (ret)
-+ return ret;
-+
-+ /* Adjust the threshold on 802.3az for the link 1000M */
-+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
-+ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
-+ QCA808X_MMD3_AZ_TRAINING_VAL);
-+ if (ret)
-+ return ret;
-+
-+ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
-+ /* Config the fast retrain for the link 2500M */
-+ ret = qca808x_phy_fast_retrain_config(phydev);
-+ if (ret)
-+ return ret;
-+
-+ ret = genphy_read_master_slave(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (!qca808x_is_prefer_master(phydev)) {
-+ /* Enable seed and configure lower ramdom seed to make phy
-+ * linked as slave mode.
-+ */
-+ ret = qca808x_phy_ms_seed_enable(phydev, true);
-+ if (ret)
-+ return ret;
-+ }
-+ }
-+
-+ /* Configure adc threshold as 100mv for the link 10M */
-+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
-+ QCA808X_ADC_THRESHOLD_MASK,
-+ QCA808X_ADC_THRESHOLD_100MV);
-+}
-+
-+static int qca808x_read_status(struct phy_device *phydev)
-+{
-+ struct at803x_ss_mask ss_mask = { 0 };
-+ int ret;
-+
-+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
-+ if (ret < 0)
-+ return ret;
-+
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
-+ ret & MDIO_AN_10GBT_STAT_LP2_5G);
-+
-+ ret = genphy_read_status(phydev);
-+ if (ret)
-+ return ret;
-+
-+ /* qca8081 takes the different bits for speed value from at803x */
-+ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
-+ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
-+ ret = at803x_read_specific_status(phydev, ss_mask);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (phydev->link) {
-+ if (phydev->speed == SPEED_2500)
-+ phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
-+ else
-+ phydev->interface = PHY_INTERFACE_MODE_SGMII;
-+ } else {
-+ /* generate seed as a lower random value to make PHY linked as SLAVE easily,
-+ * except for master/slave configuration fault detected or the master mode
-+ * preferred.
-+ *
-+ * the reason for not putting this code into the function link_change_notify is
-+ * the corner case where the link partner is also the qca8081 PHY and the seed
-+ * value is configured as the same value, the link can't be up and no link change
-+ * occurs.
-+ */
-+ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
-+ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
-+ qca808x_is_prefer_master(phydev)) {
-+ qca808x_phy_ms_seed_enable(phydev, false);
-+ } else {
-+ qca808x_phy_ms_seed_enable(phydev, true);
-+ }
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static int qca808x_soft_reset(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = genphy_soft_reset(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (qca808x_has_fast_retrain_or_slave_seed(phydev))
-+ ret = qca808x_phy_ms_seed_enable(phydev, true);
-+
-+ return ret;
-+}
-+
-+static bool qca808x_cdt_fault_length_valid(int cdt_code)
-+{
-+ switch (cdt_code) {
-+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-+ return true;
-+ default:
-+ return false;
-+ }
-+}
-+
-+static int qca808x_cable_test_result_trans(int cdt_code)
-+{
-+ switch (cdt_code) {
-+ case QCA808X_CDT_STATUS_STAT_NORMAL:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
-+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
-+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
-+ case QCA808X_CDT_STATUS_STAT_FAIL:
-+ default:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
-+ }
-+}
-+
-+static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
-+ int result)
-+{
-+ int val;
-+ u32 cdt_length_reg = 0;
-+
-+ switch (pair) {
-+ case ETHTOOL_A_CABLE_PAIR_A:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_B:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_C:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_D:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
-+ if (val < 0)
-+ return val;
-+
-+ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
-+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
-+ else
-+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
-+
-+ return at803x_cdt_fault_length(val);
-+}
-+
-+static int qca808x_cable_test_start(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ /* perform CDT with the following configs:
-+ * 1. disable hibernation.
-+ * 2. force PHY working in MDI mode.
-+ * 3. for PHY working in 1000BaseT.
-+ * 4. configure the threshold.
-+ */
-+
-+ ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = at803x_config_mdix(phydev, ETH_TP_MDI);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */
-+ phydev->duplex = DUPLEX_FULL;
-+ phydev->speed = SPEED_1000;
-+ ret = genphy_c45_pma_setup_forced(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = genphy_setup_forced(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* configure the thresholds for open, short, pair ok test */
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
-+
-+ return 0;
-+}
-+
-+static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
-+ u16 status)
-+{
-+ int length, result;
-+ u16 pair_code;
-+
-+ switch (pair) {
-+ case ETHTOOL_A_CABLE_PAIR_A:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_B:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_C:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_D:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ result = qca808x_cable_test_result_trans(pair_code);
-+ ethnl_cable_test_result(phydev, pair, result);
-+
-+ if (qca808x_cdt_fault_length_valid(pair_code)) {
-+ length = qca808x_cdt_fault_length(phydev, pair, result);
-+ ethnl_cable_test_fault_length(phydev, pair, length);
-+ }
-+
-+ return 0;
-+}
-+
-+static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
-+{
-+ int ret, val;
-+
-+ *finished = false;
-+
-+ val = QCA808X_CDT_ENABLE_TEST |
-+ QCA808X_CDT_LENGTH_UNIT;
-+ ret = at803x_cdt_start(phydev, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
-+ if (ret)
-+ return ret;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
-+ if (val < 0)
-+ return val;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
-+ if (ret)
-+ return ret;
-+
-+ *finished = true;
-+
-+ return 0;
-+}
-+
-+static int qca808x_get_features(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = genphy_c45_pma_read_abilities(phydev);
-+ if (ret)
-+ return ret;
-+
-+ /* The autoneg ability is not existed in bit3 of MMD7.1,
-+ * but it is supported by qca808x PHY, so we add it here
-+ * manually.
-+ */
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
-+
-+ /* As for the qca8081 1G version chip, the 2500baseT ability is also
-+ * existed in the bit0 of MMD1.21, we need to remove it manually if
-+ * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
-+ */
-+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (QCA808X_PHY_CHIP_TYPE_1G & ret)
-+ linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
-+
-+ return 0;
-+}
-+
-+static int qca808x_config_aneg(struct phy_device *phydev)
-+{
-+ int phy_ctrl = 0;
-+ int ret;
-+
-+ ret = at803x_prepare_config_aneg(phydev);
-+ if (ret)
-+ return ret;
-+
-+ /* The reg MII_BMCR also needs to be configured for force mode, the
-+ * genphy_config_aneg is also needed.
-+ */
-+ if (phydev->autoneg == AUTONEG_DISABLE)
-+ genphy_c45_pma_setup_forced(phydev);
-+
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
-+ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
-+
-+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
-+ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
-+ if (ret < 0)
-+ return ret;
-+
-+ return __genphy_config_aneg(phydev, ret);
-+}
-+
-+static void qca808x_link_change_notify(struct phy_device *phydev)
-+{
-+ /* Assert interface sgmii fifo on link down, deassert it on link up,
-+ * the interface device address is always phy address added by 1.
-+ */
-+ mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
-+ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
-+ QCA8081_PHY_FIFO_RSTN,
-+ phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
-+}
-+
-+static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
-+ u16 *offload_trigger)
-+{
-+ /* Parsing specific to netdev trigger */
-+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
-+ *offload_trigger |= QCA808X_LED_TX_BLINK;
-+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
-+ *offload_trigger |= QCA808X_LED_RX_BLINK;
-+ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED10_ON;
-+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED100_ON;
-+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED1000_ON;
-+ if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED2500_ON;
-+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
-+ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
-+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
-+ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
-+
-+ if (rules && !*offload_trigger)
-+ return -EOPNOTSUPP;
-+
-+ /* Enable BLINK_CHECK_BYPASS by default to make the LED
-+ * blink even with duplex or speed mode not enabled.
-+ */
-+ *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
-+
-+ return 0;
-+}
-+
-+static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN);
-+}
-+
-+static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ u16 offload_trigger = 0;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
-+}
-+
-+static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ u16 reg, offload_trigger = 0;
-+ int ret;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_CTRL(index);
-+
-+ ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_led_hw_control_enable(phydev, index);
-+ if (ret)
-+ return ret;
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_PATTERN_MASK,
-+ offload_trigger);
-+}
-+
-+static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg;
-+ int val;
-+
-+ if (index > 2)
-+ return false;
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
-+
-+ return !(val & QCA808X_LED_FORCE_EN);
-+}
-+
-+static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
-+ unsigned long *rules)
-+{
-+ u16 reg;
-+ int val;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ /* Check if we have hw control enabled */
-+ if (qca808x_led_hw_control_status(phydev, index))
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_CTRL(index);
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
-+ if (val & QCA808X_LED_TX_BLINK)
-+ set_bit(TRIGGER_NETDEV_TX, rules);
-+ if (val & QCA808X_LED_RX_BLINK)
-+ set_bit(TRIGGER_NETDEV_RX, rules);
-+ if (val & QCA808X_LED_SPEED10_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_10, rules);
-+ if (val & QCA808X_LED_SPEED100_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
-+ if (val & QCA808X_LED_SPEED1000_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
-+ if (val & QCA808X_LED_SPEED2500_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_2500, rules);
-+ if (val & QCA808X_LED_HALF_DUPLEX_ON)
-+ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
-+ if (val & QCA808X_LED_FULL_DUPLEX_ON)
-+ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
-+
-+ return 0;
-+}
-+
-+static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_CTRL(index);
-+
-+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_PATTERN_MASK);
-+}
-+
-+static int qca808x_led_brightness_set(struct phy_device *phydev,
-+ u8 index, enum led_brightness value)
-+{
-+ u16 reg;
-+ int ret;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ if (!value) {
-+ ret = qca808x_led_hw_control_reset(phydev, index);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-+ QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
-+ QCA808X_LED_FORCE_OFF);
-+}
-+
-+static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ int ret;
-+ u16 reg;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ /* Set blink to 50% off, 50% on at 4Hz by default */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
-+ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
-+ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
-+ if (ret)
-+ return ret;
-+
-+ /* We use BLINK_1 for normal blinking */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
-+ if (ret)
-+ return ret;
-+
-+ /* We set blink to 4Hz, aka 250ms */
-+ *delay_on = 250 / 2;
-+ *delay_off = 250 / 2;
-+
-+ return 0;
-+}
-+
-+static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
-+ unsigned long modes)
-+{
-+ struct at803x_priv *priv = phydev->priv;
-+ bool active_low = false;
-+ u32 mode;
-+
-+ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
-+ switch (mode) {
-+ case PHY_LED_ACTIVE_LOW:
-+ active_low = true;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+ }
-+
-+ /* PHY polarity is global and can't be set per LED.
-+ * To detect this, check if last requested polarity mode
-+ * match the new one.
-+ */
-+ if (priv->led_polarity_mode >= 0 &&
-+ priv->led_polarity_mode != active_low) {
-+ phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
-+ return -EINVAL;
-+ }
-+
-+ /* Save the last PHY polarity mode */
-+ priv->led_polarity_mode = active_low;
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN,
-+ QCA808X_MMD7_LED_POLARITY_CTRL,
-+ QCA808X_LED_ACTIVE_HIGH,
-+ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
-+}
-+
-+static struct phy_driver at803x_driver[] = {
-+{
-+ /* Qualcomm Atheros AR8035 */
-+ PHY_ID_MATCH_EXACT(ATH8035_PHY_ID),
-+ .name = "Qualcomm Atheros AR8035",
-+ .flags = PHY_POLL_CABLE_TEST,
-+ .probe = at8035_probe,
-+ .config_aneg = at803x_config_aneg,
-+ .config_init = at803x_config_init,
-+ .soft_reset = genphy_soft_reset,
-+ .set_wol = at803x_set_wol,
-+ .get_wol = at803x_get_wol,
-+ .suspend = at803x_suspend,
-+ .resume = at803x_resume,
-+ /* PHY_GBIT_FEATURES */
-+ .read_status = at803x_read_status,
-+ .config_intr = at803x_config_intr,
-+ .handle_interrupt = at803x_handle_interrupt,
-+ .get_tunable = at803x_get_tunable,
-+ .set_tunable = at803x_set_tunable,
-+ .cable_test_start = at8031_cable_test_start,
-+ .cable_test_get_status = at8031_cable_test_get_status,
-+}, {
-+ /* Qualcomm Atheros AR8030 */
-+ .phy_id = ATH8030_PHY_ID,
-+ .name = "Qualcomm Atheros AR8030",
-+ .phy_id_mask = AT8030_PHY_ID_MASK,
-+ .probe = at8035_probe,
-+ .config_init = at803x_config_init,
-+ .link_change_notify = at803x_link_change_notify,
-+ .set_wol = at803x_set_wol,
-+ .get_wol = at803x_get_wol,
-+ .suspend = at803x_suspend,
-+ .resume = at803x_resume,
-+ /* PHY_BASIC_FEATURES */
-+ .config_intr = at803x_config_intr,
-+ .handle_interrupt = at803x_handle_interrupt,
-+}, {
-+ /* Qualcomm Atheros AR8031/AR8033 */
-+ PHY_ID_MATCH_EXACT(ATH8031_PHY_ID),
-+ .name = "Qualcomm Atheros AR8031/AR8033",
-+ .flags = PHY_POLL_CABLE_TEST,
-+ .probe = at8031_probe,
-+ .config_init = at8031_config_init,
-+ .config_aneg = at803x_config_aneg,
-+ .soft_reset = genphy_soft_reset,
-+ .set_wol = at8031_set_wol,
-+ .get_wol = at803x_get_wol,
-+ .suspend = at803x_suspend,
-+ .resume = at803x_resume,
-+ .read_page = at803x_read_page,
-+ .write_page = at803x_write_page,
-+ .get_features = at803x_get_features,
-+ .read_status = at8031_read_status,
-+ .config_intr = at8031_config_intr,
-+ .handle_interrupt = at803x_handle_interrupt,
-+ .get_tunable = at803x_get_tunable,
-+ .set_tunable = at803x_set_tunable,
-+ .cable_test_start = at8031_cable_test_start,
-+ .cable_test_get_status = at8031_cable_test_get_status,
-+}, {
-+ /* Qualcomm Atheros AR8032 */
-+ PHY_ID_MATCH_EXACT(ATH8032_PHY_ID),
-+ .name = "Qualcomm Atheros AR8032",
-+ .probe = at803x_probe,
-+ .flags = PHY_POLL_CABLE_TEST,
-+ .config_init = at803x_config_init,
-+ .link_change_notify = at803x_link_change_notify,
-+ .suspend = at803x_suspend,
-+ .resume = at803x_resume,
-+ /* PHY_BASIC_FEATURES */
-+ .config_intr = at803x_config_intr,
-+ .handle_interrupt = at803x_handle_interrupt,
-+ .cable_test_start = at803x_cable_test_start,
-+ .cable_test_get_status = at8032_cable_test_get_status,
-+}, {
-+ /* ATHEROS AR9331 */
-+ PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
-+ .name = "Qualcomm Atheros AR9331 built-in PHY",
-+ .probe = at803x_probe,
-+ .suspend = at803x_suspend,
-+ .resume = at803x_resume,
-+ .flags = PHY_POLL_CABLE_TEST,
-+ /* PHY_BASIC_FEATURES */
-+ .config_intr = at803x_config_intr,
-+ .handle_interrupt = at803x_handle_interrupt,
-+ .cable_test_start = at803x_cable_test_start,
-+ .cable_test_get_status = at8032_cable_test_get_status,
-+ .read_status = at803x_read_status,
-+ .soft_reset = genphy_soft_reset,
-+ .config_aneg = at803x_config_aneg,
-+}, {
-+ /* Qualcomm Atheros QCA9561 */
-+ PHY_ID_MATCH_EXACT(QCA9561_PHY_ID),
-+ .name = "Qualcomm Atheros QCA9561 built-in PHY",
-+ .probe = at803x_probe,
-+ .suspend = at803x_suspend,
-+ .resume = at803x_resume,
-+ .flags = PHY_POLL_CABLE_TEST,
-+ /* PHY_BASIC_FEATURES */
-+ .config_intr = at803x_config_intr,
-+ .handle_interrupt = at803x_handle_interrupt,
-+ .cable_test_start = at803x_cable_test_start,
-+ .cable_test_get_status = at8032_cable_test_get_status,
-+ .read_status = at803x_read_status,
-+ .soft_reset = genphy_soft_reset,
-+ .config_aneg = at803x_config_aneg,
-+}, {
-+ /* QCA8337 */
-+ .phy_id = QCA8337_PHY_ID,
-+ .phy_id_mask = QCA8K_PHY_ID_MASK,
-+ .name = "Qualcomm Atheros 8337 internal PHY",
-+ /* PHY_GBIT_FEATURES */
-+ .probe = at803x_probe,
-+ .flags = PHY_IS_INTERNAL,
-+ .config_init = qca83xx_config_init,
-+ .soft_reset = genphy_soft_reset,
-+ .get_sset_count = qca83xx_get_sset_count,
-+ .get_strings = qca83xx_get_strings,
-+ .get_stats = qca83xx_get_stats,
-+ .suspend = qca8337_suspend,
-+ .resume = qca83xx_resume,
-+}, {
-+ /* QCA8327-A from switch QCA8327-AL1A */
-+ .phy_id = QCA8327_A_PHY_ID,
-+ .phy_id_mask = QCA8K_PHY_ID_MASK,
-+ .name = "Qualcomm Atheros 8327-A internal PHY",
-+ /* PHY_GBIT_FEATURES */
-+ .link_change_notify = qca83xx_link_change_notify,
-+ .probe = at803x_probe,
-+ .flags = PHY_IS_INTERNAL,
-+ .config_init = qca8327_config_init,
-+ .soft_reset = genphy_soft_reset,
-+ .get_sset_count = qca83xx_get_sset_count,
-+ .get_strings = qca83xx_get_strings,
-+ .get_stats = qca83xx_get_stats,
-+ .suspend = qca8327_suspend,
-+ .resume = qca83xx_resume,
-+}, {
-+ /* QCA8327-B from switch QCA8327-BL1A */
-+ .phy_id = QCA8327_B_PHY_ID,
-+ .phy_id_mask = QCA8K_PHY_ID_MASK,
-+ .name = "Qualcomm Atheros 8327-B internal PHY",
-+ /* PHY_GBIT_FEATURES */
-+ .link_change_notify = qca83xx_link_change_notify,
-+ .probe = at803x_probe,
-+ .flags = PHY_IS_INTERNAL,
-+ .config_init = qca8327_config_init,
-+ .soft_reset = genphy_soft_reset,
-+ .get_sset_count = qca83xx_get_sset_count,
-+ .get_strings = qca83xx_get_strings,
-+ .get_stats = qca83xx_get_stats,
-+ .suspend = qca8327_suspend,
-+ .resume = qca83xx_resume,
-+}, {
-+ /* Qualcomm QCA8081 */
-+ PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
-+ .name = "Qualcomm QCA8081",
-+ .flags = PHY_POLL_CABLE_TEST,
-+ .probe = at803x_probe,
-+ .config_intr = at803x_config_intr,
-+ .handle_interrupt = at803x_handle_interrupt,
-+ .get_tunable = at803x_get_tunable,
-+ .set_tunable = at803x_set_tunable,
-+ .set_wol = at803x_set_wol,
-+ .get_wol = at803x_get_wol,
-+ .get_features = qca808x_get_features,
-+ .config_aneg = qca808x_config_aneg,
-+ .suspend = genphy_suspend,
-+ .resume = genphy_resume,
-+ .read_status = qca808x_read_status,
-+ .config_init = qca808x_config_init,
-+ .soft_reset = qca808x_soft_reset,
-+ .cable_test_start = qca808x_cable_test_start,
-+ .cable_test_get_status = qca808x_cable_test_get_status,
-+ .link_change_notify = qca808x_link_change_notify,
-+ .led_brightness_set = qca808x_led_brightness_set,
-+ .led_blink_set = qca808x_led_blink_set,
-+ .led_hw_is_supported = qca808x_led_hw_is_supported,
-+ .led_hw_control_set = qca808x_led_hw_control_set,
-+ .led_hw_control_get = qca808x_led_hw_control_get,
-+ .led_polarity_set = qca808x_led_polarity_set,
-+}, };
-+
-+module_phy_driver(at803x_driver);
-+
-+static struct mdio_device_id __maybe_unused atheros_tbl[] = {
-+ { ATH8030_PHY_ID, AT8030_PHY_ID_MASK },
-+ { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) },
-+ { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) },
-+ { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) },
-+ { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
-+ { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) },
-+ { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) },
-+ { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
-+ { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) },
-+ { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
-+ { }
-+};
-+
-+MODULE_DEVICE_TABLE(mdio, atheros_tbl);
+++ /dev/null
-From 6fb760972c49490b03f3db2ad64cf30bdd28c54a Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 Jan 2024 15:15:20 +0100
-Subject: [PATCH 2/5] net: phy: qcom: create and move functions to shared
- library
-
-Create and move functions to shared library in preparation for qca83xx
-PHY Family to be detached from at803x driver.
-
-Only the shared defines are moved to the shared qcom.h header.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240129141600.2592-3-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/qcom/Kconfig | 4 ++
- drivers/net/phy/qcom/Makefile | 1 +
- drivers/net/phy/qcom/at803x.c | 69 +----------------------------
- drivers/net/phy/qcom/qcom-phy-lib.c | 53 ++++++++++++++++++++++
- drivers/net/phy/qcom/qcom.h | 34 ++++++++++++++
- 5 files changed, 94 insertions(+), 67 deletions(-)
- create mode 100644 drivers/net/phy/qcom/qcom-phy-lib.c
- create mode 100644 drivers/net/phy/qcom/qcom.h
-
---- a/drivers/net/phy/qcom/Kconfig
-+++ b/drivers/net/phy/qcom/Kconfig
-@@ -1,6 +1,10 @@
- # SPDX-License-Identifier: GPL-2.0-only
-+config QCOM_NET_PHYLIB
-+ tristate
-+
- config AT803X_PHY
- tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
-+ select QCOM_NET_PHYLIB
- depends on REGULATOR
- help
- Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
---- a/drivers/net/phy/qcom/Makefile
-+++ b/drivers/net/phy/qcom/Makefile
-@@ -1,2 +1,3 @@
- # SPDX-License-Identifier: GPL-2.0
-+obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
- obj-$(CONFIG_AT803X_PHY) += at803x.o
---- a/drivers/net/phy/qcom/at803x.c
-+++ b/drivers/net/phy/qcom/at803x.c
-@@ -22,6 +22,8 @@
- #include <linux/sfp.h>
- #include <dt-bindings/net/qca-ar803x.h>
-
-+#include "qcom.h"
-+
- #define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10
- #define AT803X_SFC_ASSERT_CRS BIT(11)
- #define AT803X_SFC_FORCE_LINK BIT(10)
-@@ -84,9 +86,6 @@
- #define AT803X_REG_CHIP_CONFIG 0x1f
- #define AT803X_BT_BX_REG_SEL 0x8000
-
--#define AT803X_DEBUG_ADDR 0x1D
--#define AT803X_DEBUG_DATA 0x1E
--
- #define AT803X_MODE_CFG_MASK 0x0F
- #define AT803X_MODE_CFG_BASET_RGMII 0x00
- #define AT803X_MODE_CFG_BASET_SGMII 0x01
-@@ -103,19 +102,6 @@
- #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
- #define AT803X_PSSR_MR_AN_COMPLETE 0x0200
-
--#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00
--#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2)
--#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2)
--#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15)
--
--#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05
--#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
--
--#define AT803X_DEBUG_REG_HIB_CTRL 0x0b
--#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10)
--#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13)
--#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15)
--
- #define AT803X_DEBUG_REG_3C 0x3C
-
- #define AT803X_DEBUG_REG_GREEN 0x3D
-@@ -393,18 +379,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8
- MODULE_AUTHOR("Matus Ujhelyi");
- MODULE_LICENSE("GPL");
-
--enum stat_access_type {
-- PHY,
-- MMD
--};
--
--struct at803x_hw_stat {
-- const char *string;
-- u8 reg;
-- u32 mask;
-- enum stat_access_type access_type;
--};
--
- static struct at803x_hw_stat qca83xx_hw_stats[] = {
- { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
- { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
-@@ -439,45 +413,6 @@ struct at803x_context {
- u16 led_control;
- };
-
--static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
--{
-- int ret;
--
-- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
-- if (ret < 0)
-- return ret;
--
-- return phy_write(phydev, AT803X_DEBUG_DATA, data);
--}
--
--static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
--{
-- int ret;
--
-- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
-- if (ret < 0)
-- return ret;
--
-- return phy_read(phydev, AT803X_DEBUG_DATA);
--}
--
--static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
-- u16 clear, u16 set)
--{
-- u16 val;
-- int ret;
--
-- ret = at803x_debug_reg_read(phydev, reg);
-- if (ret < 0)
-- return ret;
--
-- val = ret & 0xffff;
-- val &= ~clear;
-- val |= set;
--
-- return phy_write(phydev, AT803X_DEBUG_DATA, val);
--}
--
- static int at803x_write_page(struct phy_device *phydev, int page)
- {
- int mask;
---- /dev/null
-+++ b/drivers/net/phy/qcom/qcom-phy-lib.c
-@@ -0,0 +1,53 @@
-+// SPDX-License-Identifier: GPL-2.0
-+
-+#include <linux/phy.h>
-+#include <linux/module.h>
-+
-+#include "qcom.h"
-+
-+MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions");
-+MODULE_AUTHOR("Matus Ujhelyi");
-+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
-+MODULE_LICENSE("GPL");
-+
-+int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
-+{
-+ int ret;
-+
-+ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
-+ if (ret < 0)
-+ return ret;
-+
-+ return phy_read(phydev, AT803X_DEBUG_DATA);
-+}
-+EXPORT_SYMBOL_GPL(at803x_debug_reg_read);
-+
-+int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
-+ u16 clear, u16 set)
-+{
-+ u16 val;
-+ int ret;
-+
-+ ret = at803x_debug_reg_read(phydev, reg);
-+ if (ret < 0)
-+ return ret;
-+
-+ val = ret & 0xffff;
-+ val &= ~clear;
-+ val |= set;
-+
-+ return phy_write(phydev, AT803X_DEBUG_DATA, val);
-+}
-+EXPORT_SYMBOL_GPL(at803x_debug_reg_mask);
-+
-+int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
-+{
-+ int ret;
-+
-+ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
-+ if (ret < 0)
-+ return ret;
-+
-+ return phy_write(phydev, AT803X_DEBUG_DATA, data);
-+}
-+EXPORT_SYMBOL_GPL(at803x_debug_reg_write);
---- /dev/null
-+++ b/drivers/net/phy/qcom/qcom.h
-@@ -0,0 +1,34 @@
-+/* SPDX-License-Identifier: GPL-2.0 */
-+
-+#define AT803X_DEBUG_ADDR 0x1D
-+#define AT803X_DEBUG_DATA 0x1E
-+
-+#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00
-+#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2)
-+#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2)
-+#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15)
-+
-+#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05
-+#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
-+
-+#define AT803X_DEBUG_REG_HIB_CTRL 0x0b
-+#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10)
-+#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13)
-+#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15)
-+
-+enum stat_access_type {
-+ PHY,
-+ MMD
-+};
-+
-+struct at803x_hw_stat {
-+ const char *string;
-+ u8 reg;
-+ u32 mask;
-+ enum stat_access_type access_type;
-+};
-+
-+int at803x_debug_reg_read(struct phy_device *phydev, u16 reg);
-+int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
-+ u16 clear, u16 set);
-+int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data);
+++ /dev/null
-From 2e45d404d99d43bb7127b74b5dea8818df64996c Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 Jan 2024 15:15:21 +0100
-Subject: [PATCH 3/5] net: phy: qcom: deatch qca83xx PHY driver from at803x
-
-Deatch qca83xx PHY driver from at803x.
-
-The QCA83xx PHYs implement specific function and doesn't use generic
-at803x so it can be detached from the driver and moved to a dedicated
-one.
-
-Probe function and priv struct is reimplemented to allocate and use
-only the qca83xx specific data. Unused data from at803x PHY driver
-are dropped from at803x priv struct.
-
-This is to make slimmer PHY drivers instead of including lots of bloat
-that would never be used in specific SoC.
-
-A new Kconfig flag QCA83XX_PHY is introduced to compile the new
-introduced PHY driver.
-
-As the Kconfig name starts with Qualcomm the same order is kept.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240129141600.2592-4-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/qcom/Kconfig | 11 +-
- drivers/net/phy/qcom/Makefile | 1 +
- drivers/net/phy/qcom/at803x.c | 235 ----------------------------
- drivers/net/phy/qcom/qca83xx.c | 275 +++++++++++++++++++++++++++++++++
- 4 files changed, 284 insertions(+), 238 deletions(-)
- create mode 100644 drivers/net/phy/qcom/qca83xx.c
-
---- a/drivers/net/phy/qcom/Kconfig
-+++ b/drivers/net/phy/qcom/Kconfig
-@@ -3,9 +3,14 @@ config QCOM_NET_PHYLIB
- tristate
-
- config AT803X_PHY
-- tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
-+ tristate "Qualcomm Atheros AR803X PHYs"
- select QCOM_NET_PHYLIB
- depends on REGULATOR
- help
-- Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
-- QCA8337(Internal qca8k PHY) model
-+ Currently supports the AR8030, AR8031, AR8033, AR8035 model
-+
-+config QCA83XX_PHY
-+ tristate "Qualcomm Atheros QCA833x PHYs"
-+ select QCOM_NET_PHYLIB
-+ help
-+ Currently supports the internal QCA8337(Internal qca8k PHY) model
---- a/drivers/net/phy/qcom/Makefile
-+++ b/drivers/net/phy/qcom/Makefile
-@@ -1,3 +1,4 @@
- # SPDX-License-Identifier: GPL-2.0
- obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
- obj-$(CONFIG_AT803X_PHY) += at803x.o
-+obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o
---- a/drivers/net/phy/qcom/at803x.c
-+++ b/drivers/net/phy/qcom/at803x.c
-@@ -102,17 +102,10 @@
- #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
- #define AT803X_PSSR_MR_AN_COMPLETE 0x0200
-
--#define AT803X_DEBUG_REG_3C 0x3C
--
--#define AT803X_DEBUG_REG_GREEN 0x3D
--#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6)
--
- #define AT803X_DEBUG_REG_1F 0x1F
- #define AT803X_DEBUG_PLL_ON BIT(2)
- #define AT803X_DEBUG_RGMII_1V8 BIT(3)
-
--#define MDIO_AZ_DEBUG 0x800D
--
- /* AT803x supports either the XTAL input pad, an internal PLL or the
- * DSP as clock reference for the clock output pad. The XTAL reference
- * is only used for 25 MHz output, all other frequencies need the PLL.
-@@ -163,13 +156,7 @@
-
- #define QCA8081_PHY_ID 0x004dd101
-
--#define QCA8327_A_PHY_ID 0x004dd033
--#define QCA8327_B_PHY_ID 0x004dd034
--#define QCA8337_PHY_ID 0x004dd036
- #define QCA9561_PHY_ID 0x004dd042
--#define QCA8K_PHY_ID_MASK 0xffffffff
--
--#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0)
-
- #define AT803X_PAGE_FIBER 0
- #define AT803X_PAGE_COPPER 1
-@@ -379,12 +366,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8
- MODULE_AUTHOR("Matus Ujhelyi");
- MODULE_LICENSE("GPL");
-
--static struct at803x_hw_stat qca83xx_hw_stats[] = {
-- { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
-- { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
-- { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
--};
--
- struct at803x_ss_mask {
- u16 speed_mask;
- u8 speed_shift;
-@@ -400,7 +381,6 @@ struct at803x_priv {
- bool is_1000basex;
- struct regulator_dev *vddio_rdev;
- struct regulator_dev *vddh_rdev;
-- u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
- int led_polarity_mode;
- };
-
-@@ -564,53 +544,6 @@ static void at803x_get_wol(struct phy_de
- wol->wolopts |= WAKE_MAGIC;
- }
-
--static int qca83xx_get_sset_count(struct phy_device *phydev)
--{
-- return ARRAY_SIZE(qca83xx_hw_stats);
--}
--
--static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
--{
-- int i;
--
-- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
-- strscpy(data + i * ETH_GSTRING_LEN,
-- qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
-- }
--}
--
--static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
--{
-- struct at803x_hw_stat stat = qca83xx_hw_stats[i];
-- struct at803x_priv *priv = phydev->priv;
-- int val;
-- u64 ret;
--
-- if (stat.access_type == MMD)
-- val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
-- else
-- val = phy_read(phydev, stat.reg);
--
-- if (val < 0) {
-- ret = U64_MAX;
-- } else {
-- val = val & stat.mask;
-- priv->stats[i] += val;
-- ret = priv->stats[i];
-- }
--
-- return ret;
--}
--
--static void qca83xx_get_stats(struct phy_device *phydev,
-- struct ethtool_stats *stats, u64 *data)
--{
-- int i;
--
-- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
-- data[i] = qca83xx_get_stat(phydev, i);
--}
--
- static int at803x_suspend(struct phy_device *phydev)
- {
- int value;
-@@ -1707,124 +1640,6 @@ static int at8035_probe(struct phy_devic
- return at8035_parse_dt(phydev);
- }
-
--static int qca83xx_config_init(struct phy_device *phydev)
--{
-- u8 switch_revision;
--
-- switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
--
-- switch (switch_revision) {
-- case 1:
-- /* For 100M waveform */
-- at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
-- /* Turn on Gigabit clock */
-- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
-- break;
--
-- case 2:
-- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
-- fallthrough;
-- case 4:
-- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
-- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860);
-- at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46);
-- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000);
-- break;
-- }
--
-- /* Following original QCA sourcecode set port to prefer master */
-- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
--
-- return 0;
--}
--
--static int qca8327_config_init(struct phy_device *phydev)
--{
-- /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
-- * Disable on init and enable only with 100m speed following
-- * qca original source code.
-- */
-- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-- QCA8327_DEBUG_MANU_CTRL_EN, 0);
--
-- return qca83xx_config_init(phydev);
--}
--
--static void qca83xx_link_change_notify(struct phy_device *phydev)
--{
-- /* Set DAC Amplitude adjustment to +6% for 100m on link running */
-- if (phydev->state == PHY_RUNNING) {
-- if (phydev->speed == SPEED_100)
-- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-- QCA8327_DEBUG_MANU_CTRL_EN,
-- QCA8327_DEBUG_MANU_CTRL_EN);
-- } else {
-- /* Reset DAC Amplitude adjustment */
-- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-- QCA8327_DEBUG_MANU_CTRL_EN, 0);
-- }
--}
--
--static int qca83xx_resume(struct phy_device *phydev)
--{
-- int ret, val;
--
-- /* Skip reset if not suspended */
-- if (!phydev->suspended)
-- return 0;
--
-- /* Reinit the port, reset values set by suspend */
-- qca83xx_config_init(phydev);
--
-- /* Reset the port on port resume */
-- phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
--
-- /* On resume from suspend the switch execute a reset and
-- * restart auto-negotiation. Wait for reset to complete.
-- */
-- ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
-- 50000, 600000, true);
-- if (ret)
-- return ret;
--
-- usleep_range(1000, 2000);
--
-- return 0;
--}
--
--static int qca83xx_suspend(struct phy_device *phydev)
--{
-- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
-- AT803X_DEBUG_GATE_CLK_IN1000, 0);
--
-- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
-- AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
-- AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
--
-- return 0;
--}
--
--static int qca8337_suspend(struct phy_device *phydev)
--{
-- /* Only QCA8337 support actual suspend. */
-- genphy_suspend(phydev);
--
-- return qca83xx_suspend(phydev);
--}
--
--static int qca8327_suspend(struct phy_device *phydev)
--{
-- u16 mask = 0;
--
-- /* QCA8327 cause port unreliability when phy suspend
-- * is set.
-- */
-- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
-- phy_modify(phydev, MII_BMCR, mask, 0);
--
-- return qca83xx_suspend(phydev);
--}
--
- static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
- {
- int ret;
-@@ -2599,53 +2414,6 @@ static struct phy_driver at803x_driver[]
- .soft_reset = genphy_soft_reset,
- .config_aneg = at803x_config_aneg,
- }, {
-- /* QCA8337 */
-- .phy_id = QCA8337_PHY_ID,
-- .phy_id_mask = QCA8K_PHY_ID_MASK,
-- .name = "Qualcomm Atheros 8337 internal PHY",
-- /* PHY_GBIT_FEATURES */
-- .probe = at803x_probe,
-- .flags = PHY_IS_INTERNAL,
-- .config_init = qca83xx_config_init,
-- .soft_reset = genphy_soft_reset,
-- .get_sset_count = qca83xx_get_sset_count,
-- .get_strings = qca83xx_get_strings,
-- .get_stats = qca83xx_get_stats,
-- .suspend = qca8337_suspend,
-- .resume = qca83xx_resume,
--}, {
-- /* QCA8327-A from switch QCA8327-AL1A */
-- .phy_id = QCA8327_A_PHY_ID,
-- .phy_id_mask = QCA8K_PHY_ID_MASK,
-- .name = "Qualcomm Atheros 8327-A internal PHY",
-- /* PHY_GBIT_FEATURES */
-- .link_change_notify = qca83xx_link_change_notify,
-- .probe = at803x_probe,
-- .flags = PHY_IS_INTERNAL,
-- .config_init = qca8327_config_init,
-- .soft_reset = genphy_soft_reset,
-- .get_sset_count = qca83xx_get_sset_count,
-- .get_strings = qca83xx_get_strings,
-- .get_stats = qca83xx_get_stats,
-- .suspend = qca8327_suspend,
-- .resume = qca83xx_resume,
--}, {
-- /* QCA8327-B from switch QCA8327-BL1A */
-- .phy_id = QCA8327_B_PHY_ID,
-- .phy_id_mask = QCA8K_PHY_ID_MASK,
-- .name = "Qualcomm Atheros 8327-B internal PHY",
-- /* PHY_GBIT_FEATURES */
-- .link_change_notify = qca83xx_link_change_notify,
-- .probe = at803x_probe,
-- .flags = PHY_IS_INTERNAL,
-- .config_init = qca8327_config_init,
-- .soft_reset = genphy_soft_reset,
-- .get_sset_count = qca83xx_get_sset_count,
-- .get_strings = qca83xx_get_strings,
-- .get_stats = qca83xx_get_stats,
-- .suspend = qca8327_suspend,
-- .resume = qca83xx_resume,
--}, {
- /* Qualcomm QCA8081 */
- PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
- .name = "Qualcomm QCA8081",
-@@ -2683,9 +2451,6 @@ static struct mdio_device_id __maybe_unu
- { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) },
- { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) },
- { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
- { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) },
- { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
- { }
---- /dev/null
-+++ b/drivers/net/phy/qcom/qca83xx.c
-@@ -0,0 +1,275 @@
-+// SPDX-License-Identifier: GPL-2.0+
-+
-+#include <linux/phy.h>
-+#include <linux/module.h>
-+
-+#include "qcom.h"
-+
-+#define AT803X_DEBUG_REG_3C 0x3C
-+
-+#define AT803X_DEBUG_REG_GREEN 0x3D
-+#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6)
-+
-+#define MDIO_AZ_DEBUG 0x800D
-+
-+#define QCA8327_A_PHY_ID 0x004dd033
-+#define QCA8327_B_PHY_ID 0x004dd034
-+#define QCA8337_PHY_ID 0x004dd036
-+#define QCA8K_PHY_ID_MASK 0xffffffff
-+
-+#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0)
-+
-+static struct at803x_hw_stat qca83xx_hw_stats[] = {
-+ { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
-+ { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
-+ { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
-+};
-+
-+struct qca83xx_priv {
-+ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
-+};
-+
-+MODULE_DESCRIPTION("Qualcomm Atheros QCA83XX PHY driver");
-+MODULE_AUTHOR("Matus Ujhelyi");
-+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
-+MODULE_LICENSE("GPL");
-+
-+static int qca83xx_get_sset_count(struct phy_device *phydev)
-+{
-+ return ARRAY_SIZE(qca83xx_hw_stats);
-+}
-+
-+static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
-+{
-+ int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
-+ strscpy(data + i * ETH_GSTRING_LEN,
-+ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
-+ }
-+}
-+
-+static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
-+{
-+ struct at803x_hw_stat stat = qca83xx_hw_stats[i];
-+ struct qca83xx_priv *priv = phydev->priv;
-+ int val;
-+ u64 ret;
-+
-+ if (stat.access_type == MMD)
-+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
-+ else
-+ val = phy_read(phydev, stat.reg);
-+
-+ if (val < 0) {
-+ ret = U64_MAX;
-+ } else {
-+ val = val & stat.mask;
-+ priv->stats[i] += val;
-+ ret = priv->stats[i];
-+ }
-+
-+ return ret;
-+}
-+
-+static void qca83xx_get_stats(struct phy_device *phydev,
-+ struct ethtool_stats *stats, u64 *data)
-+{
-+ int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
-+ data[i] = qca83xx_get_stat(phydev, i);
-+}
-+
-+static int qca83xx_probe(struct phy_device *phydev)
-+{
-+ struct device *dev = &phydev->mdio.dev;
-+ struct qca83xx_priv *priv;
-+
-+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ phydev->priv = priv;
-+
-+ return 0;
-+}
-+
-+static int qca83xx_config_init(struct phy_device *phydev)
-+{
-+ u8 switch_revision;
-+
-+ switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
-+
-+ switch (switch_revision) {
-+ case 1:
-+ /* For 100M waveform */
-+ at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
-+ /* Turn on Gigabit clock */
-+ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
-+ break;
-+
-+ case 2:
-+ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
-+ fallthrough;
-+ case 4:
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
-+ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860);
-+ at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46);
-+ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000);
-+ break;
-+ }
-+
-+ /* Following original QCA sourcecode set port to prefer master */
-+ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
-+
-+ return 0;
-+}
-+
-+static int qca8327_config_init(struct phy_device *phydev)
-+{
-+ /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
-+ * Disable on init and enable only with 100m speed following
-+ * qca original source code.
-+ */
-+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
-+
-+ return qca83xx_config_init(phydev);
-+}
-+
-+static void qca83xx_link_change_notify(struct phy_device *phydev)
-+{
-+ /* Set DAC Amplitude adjustment to +6% for 100m on link running */
-+ if (phydev->state == PHY_RUNNING) {
-+ if (phydev->speed == SPEED_100)
-+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-+ QCA8327_DEBUG_MANU_CTRL_EN,
-+ QCA8327_DEBUG_MANU_CTRL_EN);
-+ } else {
-+ /* Reset DAC Amplitude adjustment */
-+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
-+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
-+ }
-+}
-+
-+static int qca83xx_resume(struct phy_device *phydev)
-+{
-+ int ret, val;
-+
-+ /* Skip reset if not suspended */
-+ if (!phydev->suspended)
-+ return 0;
-+
-+ /* Reinit the port, reset values set by suspend */
-+ qca83xx_config_init(phydev);
-+
-+ /* Reset the port on port resume */
-+ phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
-+
-+ /* On resume from suspend the switch execute a reset and
-+ * restart auto-negotiation. Wait for reset to complete.
-+ */
-+ ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
-+ 50000, 600000, true);
-+ if (ret)
-+ return ret;
-+
-+ usleep_range(1000, 2000);
-+
-+ return 0;
-+}
-+
-+static int qca83xx_suspend(struct phy_device *phydev)
-+{
-+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
-+ AT803X_DEBUG_GATE_CLK_IN1000, 0);
-+
-+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
-+ AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
-+ AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
-+
-+ return 0;
-+}
-+
-+static int qca8337_suspend(struct phy_device *phydev)
-+{
-+ /* Only QCA8337 support actual suspend. */
-+ genphy_suspend(phydev);
-+
-+ return qca83xx_suspend(phydev);
-+}
-+
-+static int qca8327_suspend(struct phy_device *phydev)
-+{
-+ u16 mask = 0;
-+
-+ /* QCA8327 cause port unreliability when phy suspend
-+ * is set.
-+ */
-+ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
-+ phy_modify(phydev, MII_BMCR, mask, 0);
-+
-+ return qca83xx_suspend(phydev);
-+}
-+
-+static struct phy_driver qca83xx_driver[] = {
-+{
-+ /* QCA8337 */
-+ .phy_id = QCA8337_PHY_ID,
-+ .phy_id_mask = QCA8K_PHY_ID_MASK,
-+ .name = "Qualcomm Atheros 8337 internal PHY",
-+ /* PHY_GBIT_FEATURES */
-+ .probe = qca83xx_probe,
-+ .flags = PHY_IS_INTERNAL,
-+ .config_init = qca83xx_config_init,
-+ .soft_reset = genphy_soft_reset,
-+ .get_sset_count = qca83xx_get_sset_count,
-+ .get_strings = qca83xx_get_strings,
-+ .get_stats = qca83xx_get_stats,
-+ .suspend = qca8337_suspend,
-+ .resume = qca83xx_resume,
-+}, {
-+ /* QCA8327-A from switch QCA8327-AL1A */
-+ .phy_id = QCA8327_A_PHY_ID,
-+ .phy_id_mask = QCA8K_PHY_ID_MASK,
-+ .name = "Qualcomm Atheros 8327-A internal PHY",
-+ /* PHY_GBIT_FEATURES */
-+ .link_change_notify = qca83xx_link_change_notify,
-+ .probe = qca83xx_probe,
-+ .flags = PHY_IS_INTERNAL,
-+ .config_init = qca8327_config_init,
-+ .soft_reset = genphy_soft_reset,
-+ .get_sset_count = qca83xx_get_sset_count,
-+ .get_strings = qca83xx_get_strings,
-+ .get_stats = qca83xx_get_stats,
-+ .suspend = qca8327_suspend,
-+ .resume = qca83xx_resume,
-+}, {
-+ /* QCA8327-B from switch QCA8327-BL1A */
-+ .phy_id = QCA8327_B_PHY_ID,
-+ .phy_id_mask = QCA8K_PHY_ID_MASK,
-+ .name = "Qualcomm Atheros 8327-B internal PHY",
-+ /* PHY_GBIT_FEATURES */
-+ .link_change_notify = qca83xx_link_change_notify,
-+ .probe = qca83xx_probe,
-+ .flags = PHY_IS_INTERNAL,
-+ .config_init = qca8327_config_init,
-+ .soft_reset = genphy_soft_reset,
-+ .get_sset_count = qca83xx_get_sset_count,
-+ .get_strings = qca83xx_get_strings,
-+ .get_stats = qca83xx_get_stats,
-+ .suspend = qca8327_suspend,
-+ .resume = qca83xx_resume,
-+}, };
-+
-+module_phy_driver(qca83xx_driver);
-+
-+static struct mdio_device_id __maybe_unused qca83xx_tbl[] = {
-+ { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) },
-+ { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) },
-+ { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
-+ { }
-+};
-+
-+MODULE_DEVICE_TABLE(mdio, qca83xx_tbl);
+++ /dev/null
-From 249d2b80e4db0e38503ed0ec2af6c7401bc099b9 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 Jan 2024 15:15:22 +0100
-Subject: [PATCH 4/5] net: phy: qcom: move additional functions to shared
- library
-
-Move additional functions to shared library in preparation for qca808x
-PHY Family to be detached from at803x driver.
-
-Only the shared defines are moved to the shared qcom.h header.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240129141600.2592-5-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/qcom/at803x.c | 426 +---------------------------
- drivers/net/phy/qcom/qcom-phy-lib.c | 376 ++++++++++++++++++++++++
- drivers/net/phy/qcom/qcom.h | 86 ++++++
- 3 files changed, 463 insertions(+), 425 deletions(-)
-
---- a/drivers/net/phy/qcom/at803x.c
-+++ b/drivers/net/phy/qcom/at803x.c
-@@ -24,65 +24,11 @@
-
- #include "qcom.h"
-
--#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10
--#define AT803X_SFC_ASSERT_CRS BIT(11)
--#define AT803X_SFC_FORCE_LINK BIT(10)
--#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5)
--#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3
--#define AT803X_SFC_MANUAL_MDIX 0x1
--#define AT803X_SFC_MANUAL_MDI 0x0
--#define AT803X_SFC_SQE_TEST BIT(2)
--#define AT803X_SFC_POLARITY_REVERSAL BIT(1)
--#define AT803X_SFC_DISABLE_JABBER BIT(0)
--
--#define AT803X_SPECIFIC_STATUS 0x11
--#define AT803X_SS_SPEED_MASK GENMASK(15, 14)
--#define AT803X_SS_SPEED_1000 2
--#define AT803X_SS_SPEED_100 1
--#define AT803X_SS_SPEED_10 0
--#define AT803X_SS_DUPLEX BIT(13)
--#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11)
--#define AT803X_SS_MDIX BIT(6)
--
--#define QCA808X_SS_SPEED_MASK GENMASK(9, 7)
--#define QCA808X_SS_SPEED_2500 4
--
--#define AT803X_INTR_ENABLE 0x12
--#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15)
--#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14)
--#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13)
--#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12)
--#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11)
--#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10)
--#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8)
--#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7)
--#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5)
--#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1)
--#define AT803X_INTR_ENABLE_WOL BIT(0)
--
--#define AT803X_INTR_STATUS 0x13
--
--#define AT803X_SMART_SPEED 0x14
--#define AT803X_SMART_SPEED_ENABLE BIT(5)
--#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2)
--#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1)
--#define AT803X_CDT 0x16
--#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8)
--#define AT803X_CDT_ENABLE_TEST BIT(0)
--#define AT803X_CDT_STATUS 0x1c
--#define AT803X_CDT_STATUS_STAT_NORMAL 0
--#define AT803X_CDT_STATUS_STAT_SHORT 1
--#define AT803X_CDT_STATUS_STAT_OPEN 2
--#define AT803X_CDT_STATUS_STAT_FAIL 3
--#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8)
--#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0)
- #define AT803X_LED_CONTROL 0x18
-
- #define AT803X_PHY_MMD3_WOL_CTRL 0x8012
- #define AT803X_WOL_EN BIT(5)
--#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
--#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B
--#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
-+
- #define AT803X_REG_CHIP_CONFIG 0x1f
- #define AT803X_BT_BX_REG_SEL 0x8000
-
-@@ -138,10 +84,6 @@
- #define AT803X_CLK_OUT_STRENGTH_HALF 1
- #define AT803X_CLK_OUT_STRENGTH_QUARTER 2
-
--#define AT803X_DEFAULT_DOWNSHIFT 5
--#define AT803X_MIN_DOWNSHIFT 2
--#define AT803X_MAX_DOWNSHIFT 9
--
- #define AT803X_MMD3_SMARTEEE_CTL1 0x805b
- #define AT803X_MMD3_SMARTEEE_CTL2 0x805c
- #define AT803X_MMD3_SMARTEEE_CTL3 0x805d
-@@ -366,11 +308,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8
- MODULE_AUTHOR("Matus Ujhelyi");
- MODULE_LICENSE("GPL");
-
--struct at803x_ss_mask {
-- u16 speed_mask;
-- u8 speed_shift;
--};
--
- struct at803x_priv {
- int flags;
- u16 clk_25m_reg;
-@@ -470,80 +407,6 @@ static void at803x_context_restore(struc
- phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
- }
-
--static int at803x_set_wol(struct phy_device *phydev,
-- struct ethtool_wolinfo *wol)
--{
-- int ret, irq_enabled;
--
-- if (wol->wolopts & WAKE_MAGIC) {
-- struct net_device *ndev = phydev->attached_dev;
-- const u8 *mac;
-- unsigned int i;
-- static const unsigned int offsets[] = {
-- AT803X_LOC_MAC_ADDR_32_47_OFFSET,
-- AT803X_LOC_MAC_ADDR_16_31_OFFSET,
-- AT803X_LOC_MAC_ADDR_0_15_OFFSET,
-- };
--
-- if (!ndev)
-- return -ENODEV;
--
-- mac = (const u8 *)ndev->dev_addr;
--
-- if (!is_valid_ether_addr(mac))
-- return -EINVAL;
--
-- for (i = 0; i < 3; i++)
-- phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
-- mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
--
-- /* Enable WOL interrupt */
-- ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
-- if (ret)
-- return ret;
-- } else {
-- /* Disable WOL interrupt */
-- ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
-- if (ret)
-- return ret;
-- }
--
-- /* Clear WOL status */
-- ret = phy_read(phydev, AT803X_INTR_STATUS);
-- if (ret < 0)
-- return ret;
--
-- /* Check if there are other interrupts except for WOL triggered when PHY is
-- * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
-- * be passed up to the interrupt PIN.
-- */
-- irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
-- if (irq_enabled < 0)
-- return irq_enabled;
--
-- irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
-- if (ret & irq_enabled && !phy_polling_mode(phydev))
-- phy_trigger_machine(phydev);
--
-- return 0;
--}
--
--static void at803x_get_wol(struct phy_device *phydev,
-- struct ethtool_wolinfo *wol)
--{
-- int value;
--
-- wol->supported = WAKE_MAGIC;
-- wol->wolopts = 0;
--
-- value = phy_read(phydev, AT803X_INTR_ENABLE);
-- if (value < 0)
-- return;
--
-- if (value & AT803X_INTR_ENABLE_WOL)
-- wol->wolopts |= WAKE_MAGIC;
--}
--
- static int at803x_suspend(struct phy_device *phydev)
- {
- int value;
-@@ -816,73 +679,6 @@ static int at803x_config_init(struct phy
- return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0);
- }
-
--static int at803x_ack_interrupt(struct phy_device *phydev)
--{
-- int err;
--
-- err = phy_read(phydev, AT803X_INTR_STATUS);
--
-- return (err < 0) ? err : 0;
--}
--
--static int at803x_config_intr(struct phy_device *phydev)
--{
-- int err;
-- int value;
--
-- value = phy_read(phydev, AT803X_INTR_ENABLE);
--
-- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
-- /* Clear any pending interrupts */
-- err = at803x_ack_interrupt(phydev);
-- if (err)
-- return err;
--
-- value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
-- value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
-- value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
-- value |= AT803X_INTR_ENABLE_LINK_FAIL;
-- value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
--
-- err = phy_write(phydev, AT803X_INTR_ENABLE, value);
-- } else {
-- err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
-- if (err)
-- return err;
--
-- /* Clear any pending interrupts */
-- err = at803x_ack_interrupt(phydev);
-- }
--
-- return err;
--}
--
--static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)
--{
-- int irq_status, int_enabled;
--
-- irq_status = phy_read(phydev, AT803X_INTR_STATUS);
-- if (irq_status < 0) {
-- phy_error(phydev);
-- return IRQ_NONE;
-- }
--
-- /* Read the current enabled interrupts */
-- int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
-- if (int_enabled < 0) {
-- phy_error(phydev);
-- return IRQ_NONE;
-- }
--
-- /* See if this was one of our enabled interrupts */
-- if (!(irq_status & int_enabled))
-- return IRQ_NONE;
--
-- phy_trigger_machine(phydev);
--
-- return IRQ_HANDLED;
--}
--
- static void at803x_link_change_notify(struct phy_device *phydev)
- {
- /*
-@@ -908,69 +704,6 @@ static void at803x_link_change_notify(st
- }
- }
-
--static int at803x_read_specific_status(struct phy_device *phydev,
-- struct at803x_ss_mask ss_mask)
--{
-- int ss;
--
-- /* Read the AT8035 PHY-Specific Status register, which indicates the
-- * speed and duplex that the PHY is actually using, irrespective of
-- * whether we are in autoneg mode or not.
-- */
-- ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
-- if (ss < 0)
-- return ss;
--
-- if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
-- int sfc, speed;
--
-- sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
-- if (sfc < 0)
-- return sfc;
--
-- speed = ss & ss_mask.speed_mask;
-- speed >>= ss_mask.speed_shift;
--
-- switch (speed) {
-- case AT803X_SS_SPEED_10:
-- phydev->speed = SPEED_10;
-- break;
-- case AT803X_SS_SPEED_100:
-- phydev->speed = SPEED_100;
-- break;
-- case AT803X_SS_SPEED_1000:
-- phydev->speed = SPEED_1000;
-- break;
-- case QCA808X_SS_SPEED_2500:
-- phydev->speed = SPEED_2500;
-- break;
-- }
-- if (ss & AT803X_SS_DUPLEX)
-- phydev->duplex = DUPLEX_FULL;
-- else
-- phydev->duplex = DUPLEX_HALF;
--
-- if (ss & AT803X_SS_MDIX)
-- phydev->mdix = ETH_TP_MDI_X;
-- else
-- phydev->mdix = ETH_TP_MDI;
--
-- switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
-- case AT803X_SFC_MANUAL_MDI:
-- phydev->mdix_ctrl = ETH_TP_MDI;
-- break;
-- case AT803X_SFC_MANUAL_MDIX:
-- phydev->mdix_ctrl = ETH_TP_MDI_X;
-- break;
-- case AT803X_SFC_AUTOMATIC_CROSSOVER:
-- phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
-- break;
-- }
-- }
--
-- return 0;
--}
--
- static int at803x_read_status(struct phy_device *phydev)
- {
- struct at803x_ss_mask ss_mask = { 0 };
-@@ -1006,50 +739,6 @@ static int at803x_read_status(struct phy
- return 0;
- }
-
--static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
--{
-- u16 val;
--
-- switch (ctrl) {
-- case ETH_TP_MDI:
-- val = AT803X_SFC_MANUAL_MDI;
-- break;
-- case ETH_TP_MDI_X:
-- val = AT803X_SFC_MANUAL_MDIX;
-- break;
-- case ETH_TP_MDI_AUTO:
-- val = AT803X_SFC_AUTOMATIC_CROSSOVER;
-- break;
-- default:
-- return 0;
-- }
--
-- return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
-- AT803X_SFC_MDI_CROSSOVER_MODE_M,
-- FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
--}
--
--static int at803x_prepare_config_aneg(struct phy_device *phydev)
--{
-- int ret;
--
-- ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
-- if (ret < 0)
-- return ret;
--
-- /* Changes of the midx bits are disruptive to the normal operation;
-- * therefore any changes to these registers must be followed by a
-- * software reset to take effect.
-- */
-- if (ret == 1) {
-- ret = genphy_soft_reset(phydev);
-- if (ret < 0)
-- return ret;
-- }
--
-- return 0;
--}
--
- static int at803x_config_aneg(struct phy_device *phydev)
- {
- struct at803x_priv *priv = phydev->priv;
-@@ -1065,80 +754,6 @@ static int at803x_config_aneg(struct phy
- return genphy_config_aneg(phydev);
- }
-
--static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
--{
-- int val;
--
-- val = phy_read(phydev, AT803X_SMART_SPEED);
-- if (val < 0)
-- return val;
--
-- if (val & AT803X_SMART_SPEED_ENABLE)
-- *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
-- else
-- *d = DOWNSHIFT_DEV_DISABLE;
--
-- return 0;
--}
--
--static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
--{
-- u16 mask, set;
-- int ret;
--
-- switch (cnt) {
-- case DOWNSHIFT_DEV_DEFAULT_COUNT:
-- cnt = AT803X_DEFAULT_DOWNSHIFT;
-- fallthrough;
-- case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
-- set = AT803X_SMART_SPEED_ENABLE |
-- AT803X_SMART_SPEED_BYPASS_TIMER |
-- FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
-- mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
-- break;
-- case DOWNSHIFT_DEV_DISABLE:
-- set = 0;
-- mask = AT803X_SMART_SPEED_ENABLE |
-- AT803X_SMART_SPEED_BYPASS_TIMER;
-- break;
-- default:
-- return -EINVAL;
-- }
--
-- ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
--
-- /* After changing the smart speed settings, we need to perform a
-- * software reset, use phy_init_hw() to make sure we set the
-- * reapply any values which might got lost during software reset.
-- */
-- if (ret == 1)
-- ret = phy_init_hw(phydev);
--
-- return ret;
--}
--
--static int at803x_get_tunable(struct phy_device *phydev,
-- struct ethtool_tunable *tuna, void *data)
--{
-- switch (tuna->id) {
-- case ETHTOOL_PHY_DOWNSHIFT:
-- return at803x_get_downshift(phydev, data);
-- default:
-- return -EOPNOTSUPP;
-- }
--}
--
--static int at803x_set_tunable(struct phy_device *phydev,
-- struct ethtool_tunable *tuna, const void *data)
--{
-- switch (tuna->id) {
-- case ETHTOOL_PHY_DOWNSHIFT:
-- return at803x_set_downshift(phydev, *(const u8 *)data);
-- default:
-- return -EOPNOTSUPP;
-- }
--}
--
- static int at803x_cable_test_result_trans(u16 status)
- {
- switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
-@@ -1170,45 +785,6 @@ static bool at803x_cdt_fault_length_vali
- return false;
- }
-
--static int at803x_cdt_fault_length(int dt)
--{
-- /* According to the datasheet the distance to the fault is
-- * DELTA_TIME * 0.824 meters.
-- *
-- * The author suspect the correct formula is:
-- *
-- * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
-- *
-- * where c is the speed of light, VF is the velocity factor of
-- * the twisted pair cable, 125MHz the counter frequency and
-- * we need to divide by 2 because the hardware will measure the
-- * round trip time to the fault and back to the PHY.
-- *
-- * With a VF of 0.69 we get the factor 0.824 mentioned in the
-- * datasheet.
-- */
-- return (dt * 824) / 10;
--}
--
--static int at803x_cdt_start(struct phy_device *phydev,
-- u32 cdt_start)
--{
-- return phy_write(phydev, AT803X_CDT, cdt_start);
--}
--
--static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
-- u32 cdt_en)
--{
-- int val, ret;
--
-- /* One test run takes about 25ms */
-- ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
-- !(val & cdt_en),
-- 30000, 100000, true);
--
-- return ret < 0 ? ret : 0;
--}
--
- static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair)
- {
- static const int ethtool_pair[] = {
---- a/drivers/net/phy/qcom/qcom-phy-lib.c
-+++ b/drivers/net/phy/qcom/qcom-phy-lib.c
-@@ -3,6 +3,9 @@
- #include <linux/phy.h>
- #include <linux/module.h>
-
-+#include <linux/netdevice.h>
-+#include <linux/etherdevice.h>
-+
- #include "qcom.h"
-
- MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions");
-@@ -51,3 +54,376 @@ int at803x_debug_reg_write(struct phy_de
- return phy_write(phydev, AT803X_DEBUG_DATA, data);
- }
- EXPORT_SYMBOL_GPL(at803x_debug_reg_write);
-+
-+int at803x_set_wol(struct phy_device *phydev,
-+ struct ethtool_wolinfo *wol)
-+{
-+ int ret, irq_enabled;
-+
-+ if (wol->wolopts & WAKE_MAGIC) {
-+ struct net_device *ndev = phydev->attached_dev;
-+ const u8 *mac;
-+ unsigned int i;
-+ static const unsigned int offsets[] = {
-+ AT803X_LOC_MAC_ADDR_32_47_OFFSET,
-+ AT803X_LOC_MAC_ADDR_16_31_OFFSET,
-+ AT803X_LOC_MAC_ADDR_0_15_OFFSET,
-+ };
-+
-+ if (!ndev)
-+ return -ENODEV;
-+
-+ mac = (const u8 *)ndev->dev_addr;
-+
-+ if (!is_valid_ether_addr(mac))
-+ return -EINVAL;
-+
-+ for (i = 0; i < 3; i++)
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
-+ mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
-+
-+ /* Enable WOL interrupt */
-+ ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
-+ if (ret)
-+ return ret;
-+ } else {
-+ /* Disable WOL interrupt */
-+ ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ /* Clear WOL status */
-+ ret = phy_read(phydev, AT803X_INTR_STATUS);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Check if there are other interrupts except for WOL triggered when PHY is
-+ * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
-+ * be passed up to the interrupt PIN.
-+ */
-+ irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
-+ if (irq_enabled < 0)
-+ return irq_enabled;
-+
-+ irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
-+ if (ret & irq_enabled && !phy_polling_mode(phydev))
-+ phy_trigger_machine(phydev);
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(at803x_set_wol);
-+
-+void at803x_get_wol(struct phy_device *phydev,
-+ struct ethtool_wolinfo *wol)
-+{
-+ int value;
-+
-+ wol->supported = WAKE_MAGIC;
-+ wol->wolopts = 0;
-+
-+ value = phy_read(phydev, AT803X_INTR_ENABLE);
-+ if (value < 0)
-+ return;
-+
-+ if (value & AT803X_INTR_ENABLE_WOL)
-+ wol->wolopts |= WAKE_MAGIC;
-+}
-+EXPORT_SYMBOL_GPL(at803x_get_wol);
-+
-+int at803x_ack_interrupt(struct phy_device *phydev)
-+{
-+ int err;
-+
-+ err = phy_read(phydev, AT803X_INTR_STATUS);
-+
-+ return (err < 0) ? err : 0;
-+}
-+EXPORT_SYMBOL_GPL(at803x_ack_interrupt);
-+
-+int at803x_config_intr(struct phy_device *phydev)
-+{
-+ int err;
-+ int value;
-+
-+ value = phy_read(phydev, AT803X_INTR_ENABLE);
-+
-+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
-+ /* Clear any pending interrupts */
-+ err = at803x_ack_interrupt(phydev);
-+ if (err)
-+ return err;
-+
-+ value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
-+ value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
-+ value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
-+ value |= AT803X_INTR_ENABLE_LINK_FAIL;
-+ value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
-+
-+ err = phy_write(phydev, AT803X_INTR_ENABLE, value);
-+ } else {
-+ err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
-+ if (err)
-+ return err;
-+
-+ /* Clear any pending interrupts */
-+ err = at803x_ack_interrupt(phydev);
-+ }
-+
-+ return err;
-+}
-+EXPORT_SYMBOL_GPL(at803x_config_intr);
-+
-+irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)
-+{
-+ int irq_status, int_enabled;
-+
-+ irq_status = phy_read(phydev, AT803X_INTR_STATUS);
-+ if (irq_status < 0) {
-+ phy_error(phydev);
-+ return IRQ_NONE;
-+ }
-+
-+ /* Read the current enabled interrupts */
-+ int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
-+ if (int_enabled < 0) {
-+ phy_error(phydev);
-+ return IRQ_NONE;
-+ }
-+
-+ /* See if this was one of our enabled interrupts */
-+ if (!(irq_status & int_enabled))
-+ return IRQ_NONE;
-+
-+ phy_trigger_machine(phydev);
-+
-+ return IRQ_HANDLED;
-+}
-+EXPORT_SYMBOL_GPL(at803x_handle_interrupt);
-+
-+int at803x_read_specific_status(struct phy_device *phydev,
-+ struct at803x_ss_mask ss_mask)
-+{
-+ int ss;
-+
-+ /* Read the AT8035 PHY-Specific Status register, which indicates the
-+ * speed and duplex that the PHY is actually using, irrespective of
-+ * whether we are in autoneg mode or not.
-+ */
-+ ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
-+ if (ss < 0)
-+ return ss;
-+
-+ if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
-+ int sfc, speed;
-+
-+ sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
-+ if (sfc < 0)
-+ return sfc;
-+
-+ speed = ss & ss_mask.speed_mask;
-+ speed >>= ss_mask.speed_shift;
-+
-+ switch (speed) {
-+ case AT803X_SS_SPEED_10:
-+ phydev->speed = SPEED_10;
-+ break;
-+ case AT803X_SS_SPEED_100:
-+ phydev->speed = SPEED_100;
-+ break;
-+ case AT803X_SS_SPEED_1000:
-+ phydev->speed = SPEED_1000;
-+ break;
-+ case QCA808X_SS_SPEED_2500:
-+ phydev->speed = SPEED_2500;
-+ break;
-+ }
-+ if (ss & AT803X_SS_DUPLEX)
-+ phydev->duplex = DUPLEX_FULL;
-+ else
-+ phydev->duplex = DUPLEX_HALF;
-+
-+ if (ss & AT803X_SS_MDIX)
-+ phydev->mdix = ETH_TP_MDI_X;
-+ else
-+ phydev->mdix = ETH_TP_MDI;
-+
-+ switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
-+ case AT803X_SFC_MANUAL_MDI:
-+ phydev->mdix_ctrl = ETH_TP_MDI;
-+ break;
-+ case AT803X_SFC_MANUAL_MDIX:
-+ phydev->mdix_ctrl = ETH_TP_MDI_X;
-+ break;
-+ case AT803X_SFC_AUTOMATIC_CROSSOVER:
-+ phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
-+ break;
-+ }
-+ }
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(at803x_read_specific_status);
-+
-+int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
-+{
-+ u16 val;
-+
-+ switch (ctrl) {
-+ case ETH_TP_MDI:
-+ val = AT803X_SFC_MANUAL_MDI;
-+ break;
-+ case ETH_TP_MDI_X:
-+ val = AT803X_SFC_MANUAL_MDIX;
-+ break;
-+ case ETH_TP_MDI_AUTO:
-+ val = AT803X_SFC_AUTOMATIC_CROSSOVER;
-+ break;
-+ default:
-+ return 0;
-+ }
-+
-+ return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
-+ AT803X_SFC_MDI_CROSSOVER_MODE_M,
-+ FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
-+}
-+EXPORT_SYMBOL_GPL(at803x_config_mdix);
-+
-+int at803x_prepare_config_aneg(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Changes of the midx bits are disruptive to the normal operation;
-+ * therefore any changes to these registers must be followed by a
-+ * software reset to take effect.
-+ */
-+ if (ret == 1) {
-+ ret = genphy_soft_reset(phydev);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);
-+
-+static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
-+{
-+ int val;
-+
-+ val = phy_read(phydev, AT803X_SMART_SPEED);
-+ if (val < 0)
-+ return val;
-+
-+ if (val & AT803X_SMART_SPEED_ENABLE)
-+ *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
-+ else
-+ *d = DOWNSHIFT_DEV_DISABLE;
-+
-+ return 0;
-+}
-+
-+static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
-+{
-+ u16 mask, set;
-+ int ret;
-+
-+ switch (cnt) {
-+ case DOWNSHIFT_DEV_DEFAULT_COUNT:
-+ cnt = AT803X_DEFAULT_DOWNSHIFT;
-+ fallthrough;
-+ case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
-+ set = AT803X_SMART_SPEED_ENABLE |
-+ AT803X_SMART_SPEED_BYPASS_TIMER |
-+ FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
-+ mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
-+ break;
-+ case DOWNSHIFT_DEV_DISABLE:
-+ set = 0;
-+ mask = AT803X_SMART_SPEED_ENABLE |
-+ AT803X_SMART_SPEED_BYPASS_TIMER;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
-+
-+ /* After changing the smart speed settings, we need to perform a
-+ * software reset, use phy_init_hw() to make sure we set the
-+ * reapply any values which might got lost during software reset.
-+ */
-+ if (ret == 1)
-+ ret = phy_init_hw(phydev);
-+
-+ return ret;
-+}
-+
-+int at803x_get_tunable(struct phy_device *phydev,
-+ struct ethtool_tunable *tuna, void *data)
-+{
-+ switch (tuna->id) {
-+ case ETHTOOL_PHY_DOWNSHIFT:
-+ return at803x_get_downshift(phydev, data);
-+ default:
-+ return -EOPNOTSUPP;
-+ }
-+}
-+EXPORT_SYMBOL_GPL(at803x_get_tunable);
-+
-+int at803x_set_tunable(struct phy_device *phydev,
-+ struct ethtool_tunable *tuna, const void *data)
-+{
-+ switch (tuna->id) {
-+ case ETHTOOL_PHY_DOWNSHIFT:
-+ return at803x_set_downshift(phydev, *(const u8 *)data);
-+ default:
-+ return -EOPNOTSUPP;
-+ }
-+}
-+EXPORT_SYMBOL_GPL(at803x_set_tunable);
-+
-+int at803x_cdt_fault_length(int dt)
-+{
-+ /* According to the datasheet the distance to the fault is
-+ * DELTA_TIME * 0.824 meters.
-+ *
-+ * The author suspect the correct formula is:
-+ *
-+ * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
-+ *
-+ * where c is the speed of light, VF is the velocity factor of
-+ * the twisted pair cable, 125MHz the counter frequency and
-+ * we need to divide by 2 because the hardware will measure the
-+ * round trip time to the fault and back to the PHY.
-+ *
-+ * With a VF of 0.69 we get the factor 0.824 mentioned in the
-+ * datasheet.
-+ */
-+ return (dt * 824) / 10;
-+}
-+EXPORT_SYMBOL_GPL(at803x_cdt_fault_length);
-+
-+int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start)
-+{
-+ return phy_write(phydev, AT803X_CDT, cdt_start);
-+}
-+EXPORT_SYMBOL_GPL(at803x_cdt_start);
-+
-+int at803x_cdt_wait_for_completion(struct phy_device *phydev,
-+ u32 cdt_en)
-+{
-+ int val, ret;
-+
-+ /* One test run takes about 25ms */
-+ ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
-+ !(val & cdt_en),
-+ 30000, 100000, true);
-+
-+ return ret < 0 ? ret : 0;
-+}
-+EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);
---- a/drivers/net/phy/qcom/qcom.h
-+++ b/drivers/net/phy/qcom/qcom.h
-@@ -1,5 +1,63 @@
- /* SPDX-License-Identifier: GPL-2.0 */
-
-+#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10
-+#define AT803X_SFC_ASSERT_CRS BIT(11)
-+#define AT803X_SFC_FORCE_LINK BIT(10)
-+#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5)
-+#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3
-+#define AT803X_SFC_MANUAL_MDIX 0x1
-+#define AT803X_SFC_MANUAL_MDI 0x0
-+#define AT803X_SFC_SQE_TEST BIT(2)
-+#define AT803X_SFC_POLARITY_REVERSAL BIT(1)
-+#define AT803X_SFC_DISABLE_JABBER BIT(0)
-+
-+#define AT803X_SPECIFIC_STATUS 0x11
-+#define AT803X_SS_SPEED_MASK GENMASK(15, 14)
-+#define AT803X_SS_SPEED_1000 2
-+#define AT803X_SS_SPEED_100 1
-+#define AT803X_SS_SPEED_10 0
-+#define AT803X_SS_DUPLEX BIT(13)
-+#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11)
-+#define AT803X_SS_MDIX BIT(6)
-+
-+#define QCA808X_SS_SPEED_MASK GENMASK(9, 7)
-+#define QCA808X_SS_SPEED_2500 4
-+
-+#define AT803X_INTR_ENABLE 0x12
-+#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15)
-+#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14)
-+#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13)
-+#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12)
-+#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11)
-+#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10)
-+#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8)
-+#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7)
-+#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5)
-+#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1)
-+#define AT803X_INTR_ENABLE_WOL BIT(0)
-+
-+#define AT803X_INTR_STATUS 0x13
-+
-+#define AT803X_SMART_SPEED 0x14
-+#define AT803X_SMART_SPEED_ENABLE BIT(5)
-+#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2)
-+#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1)
-+
-+#define AT803X_CDT 0x16
-+#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8)
-+#define AT803X_CDT_ENABLE_TEST BIT(0)
-+#define AT803X_CDT_STATUS 0x1c
-+#define AT803X_CDT_STATUS_STAT_NORMAL 0
-+#define AT803X_CDT_STATUS_STAT_SHORT 1
-+#define AT803X_CDT_STATUS_STAT_OPEN 2
-+#define AT803X_CDT_STATUS_STAT_FAIL 3
-+#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8)
-+#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0)
-+
-+#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
-+#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B
-+#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
-+
- #define AT803X_DEBUG_ADDR 0x1D
- #define AT803X_DEBUG_DATA 0x1E
-
-@@ -16,6 +74,10 @@
- #define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13)
- #define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15)
-
-+#define AT803X_DEFAULT_DOWNSHIFT 5
-+#define AT803X_MIN_DOWNSHIFT 2
-+#define AT803X_MAX_DOWNSHIFT 9
-+
- enum stat_access_type {
- PHY,
- MMD
-@@ -28,7 +90,31 @@ struct at803x_hw_stat {
- enum stat_access_type access_type;
- };
-
-+struct at803x_ss_mask {
-+ u16 speed_mask;
-+ u8 speed_shift;
-+};
-+
- int at803x_debug_reg_read(struct phy_device *phydev, u16 reg);
- int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
- u16 clear, u16 set);
- int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data);
-+int at803x_set_wol(struct phy_device *phydev,
-+ struct ethtool_wolinfo *wol);
-+void at803x_get_wol(struct phy_device *phydev,
-+ struct ethtool_wolinfo *wol);
-+int at803x_ack_interrupt(struct phy_device *phydev);
-+int at803x_config_intr(struct phy_device *phydev);
-+irqreturn_t at803x_handle_interrupt(struct phy_device *phydev);
-+int at803x_read_specific_status(struct phy_device *phydev,
-+ struct at803x_ss_mask ss_mask);
-+int at803x_config_mdix(struct phy_device *phydev, u8 ctrl);
-+int at803x_prepare_config_aneg(struct phy_device *phydev);
-+int at803x_get_tunable(struct phy_device *phydev,
-+ struct ethtool_tunable *tuna, void *data);
-+int at803x_set_tunable(struct phy_device *phydev,
-+ struct ethtool_tunable *tuna, const void *data);
-+int at803x_cdt_fault_length(int dt);
-+int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start);
-+int at803x_cdt_wait_for_completion(struct phy_device *phydev,
-+ u32 cdt_en);
+++ /dev/null
-From c89414adf2ec7cd9e7080c419aa5847f1db1009c Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 Jan 2024 15:15:23 +0100
-Subject: [PATCH 5/5] net: phy: qcom: detach qca808x PHY driver from at803x
-
-Almost all the QCA8081 PHY driver OPs are specific and only some of them
-use the generic at803x.
-
-To make the at803x code slimmer, move all the specific qca808x regs and
-functions to a dedicated PHY driver.
-
-Probe function and priv struct is reworked to allocate and use only the
-qca808x specific data. Unused data from at803x PHY driver are dropped
-from at803x priv struct.
-
-Also a new Kconfig is introduced QCA808X_PHY, to compile the newly
-introduced PHY driver for QCA8081 PHY.
-
-As the Kconfig name starts with Qualcomm the same order is kept.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240129141600.2592-6-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/qcom/Kconfig | 6 +
- drivers/net/phy/qcom/Makefile | 1 +
- drivers/net/phy/qcom/at803x.c | 897 +------------------------------
- drivers/net/phy/qcom/qca808x.c | 934 +++++++++++++++++++++++++++++++++
- 4 files changed, 942 insertions(+), 896 deletions(-)
- create mode 100644 drivers/net/phy/qcom/qca808x.c
-
---- a/drivers/net/phy/qcom/Kconfig
-+++ b/drivers/net/phy/qcom/Kconfig
-@@ -14,3 +14,9 @@ config QCA83XX_PHY
- select QCOM_NET_PHYLIB
- help
- Currently supports the internal QCA8337(Internal qca8k PHY) model
-+
-+config QCA808X_PHY
-+ tristate "Qualcomm QCA808x PHYs"
-+ select QCOM_NET_PHYLIB
-+ help
-+ Currently supports the QCA8081 model
---- a/drivers/net/phy/qcom/Makefile
-+++ b/drivers/net/phy/qcom/Makefile
-@@ -2,3 +2,4 @@
- obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
- obj-$(CONFIG_AT803X_PHY) += at803x.o
- obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o
-+obj-$(CONFIG_QCA808X_PHY) += qca808x.o
---- a/drivers/net/phy/qcom/at803x.c
-+++ b/drivers/net/phy/qcom/at803x.c
-@@ -96,8 +96,6 @@
- #define ATH8035_PHY_ID 0x004dd072
- #define AT8030_PHY_ID_MASK 0xffffffef
-
--#define QCA8081_PHY_ID 0x004dd101
--
- #define QCA9561_PHY_ID 0x004dd042
-
- #define AT803X_PAGE_FIBER 0
-@@ -110,201 +108,7 @@
- /* disable hibernation mode */
- #define AT803X_DISABLE_HIBERNATION_MODE BIT(2)
-
--/* ADC threshold */
--#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80
--#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0)
--#define QCA808X_ADC_THRESHOLD_80MV 0
--#define QCA808X_ADC_THRESHOLD_100MV 0xf0
--#define QCA808X_ADC_THRESHOLD_200MV 0x0f
--#define QCA808X_ADC_THRESHOLD_300MV 0xff
--
--/* CLD control */
--#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007
--#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4)
--#define QCA808X_8023AZ_AFE_EN 0x90
--
--/* AZ control */
--#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008
--#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32
--
--#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014
--#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529
--
--#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E
--#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341
--
--#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E
--#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419
--
--#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020
--#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341
--
--#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c
--#define QCA808X_TOP_OPTION1_DATA 0x0
--
--#define QCA808X_PHY_MMD3_DEBUG_1 0xa100
--#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203
--#define QCA808X_PHY_MMD3_DEBUG_2 0xa101
--#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad
--#define QCA808X_PHY_MMD3_DEBUG_3 0xa103
--#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698
--#define QCA808X_PHY_MMD3_DEBUG_4 0xa105
--#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001
--#define QCA808X_PHY_MMD3_DEBUG_5 0xa106
--#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111
--#define QCA808X_PHY_MMD3_DEBUG_6 0xa011
--#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85
--
--/* master/slave seed config */
--#define QCA808X_PHY_DEBUG_LOCAL_SEED 9
--#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1)
--#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2)
--#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32
--
--/* Hibernation yields lower power consumpiton in contrast with normal operation mode.
-- * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s.
-- */
--#define QCA808X_DBG_AN_TEST 0xb
--#define QCA808X_HIBERNATION_EN BIT(15)
--
--#define QCA808X_CDT_ENABLE_TEST BIT(15)
--#define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
--#define QCA808X_CDT_STATUS BIT(11)
--#define QCA808X_CDT_LENGTH_UNIT BIT(10)
--
--#define QCA808X_MMD3_CDT_STATUS 0x8064
--#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065
--#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
--#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
--#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
--#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
--#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
--
--#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
--#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
--#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
--#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
--
--#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
--#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
--#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
--#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
--#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
--
--#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
--#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
--#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
--#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
--
--/* NORMAL are MDI with type set to 0 */
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-- QCA808X_CDT_STATUS_STAT_MDI1)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-- QCA808X_CDT_STATUS_STAT_MDI1)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-- QCA808X_CDT_STATUS_STAT_MDI2)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-- QCA808X_CDT_STATUS_STAT_MDI2)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-- QCA808X_CDT_STATUS_STAT_MDI3)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-- QCA808X_CDT_STATUS_STAT_MDI3)
--
--/* Added for reference of existence but should be handled by wait_for_completion already */
--#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
--
--#define QCA808X_MMD7_LED_GLOBAL 0x8073
--#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
--#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
--/* Values are the same for both BLINK_1 and BLINK_2 */
--#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
--#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
--#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
--#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
--#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
--#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
--#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
--#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
--#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
--#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
--#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
--#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
--#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
--#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
--#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
--#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
--#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
--#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
--
--#define QCA808X_MMD7_LED2_CTRL 0x8074
--#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075
--#define QCA808X_MMD7_LED1_CTRL 0x8076
--#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077
--#define QCA808X_MMD7_LED0_CTRL 0x8078
--#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))
--
--/* LED hw control pattern is the same for every LED */
--#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
--#define QCA808X_LED_SPEED2500_ON BIT(15)
--#define QCA808X_LED_SPEED2500_BLINK BIT(14)
--/* Follow blink trigger even if duplex or speed condition doesn't match */
--#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
--#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
--#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
--#define QCA808X_LED_TX_BLINK BIT(10)
--#define QCA808X_LED_RX_BLINK BIT(9)
--#define QCA808X_LED_TX_ON_10MS BIT(8)
--#define QCA808X_LED_RX_ON_10MS BIT(7)
--#define QCA808X_LED_SPEED1000_ON BIT(6)
--#define QCA808X_LED_SPEED100_ON BIT(5)
--#define QCA808X_LED_SPEED10_ON BIT(4)
--#define QCA808X_LED_COLLISION_BLINK BIT(3)
--#define QCA808X_LED_SPEED1000_BLINK BIT(2)
--#define QCA808X_LED_SPEED100_BLINK BIT(1)
--#define QCA808X_LED_SPEED10_BLINK BIT(0)
--
--#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079
--#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))
--
--/* LED force ctrl is the same for every LED
-- * No documentation exist for this, not even internal one
-- * with NDA as QCOM gives only info about configuring
-- * hw control pattern rules and doesn't indicate any way
-- * to force the LED to specific mode.
-- * These define comes from reverse and testing and maybe
-- * lack of some info or some info are not entirely correct.
-- * For the basic LED control and hw control these finding
-- * are enough to support LED control in all the required APIs.
-- *
-- * On doing some comparison with implementation with qca807x,
-- * it was found that it's 1:1 equal to it and confirms all the
-- * reverse done. It was also found further specification with the
-- * force mode and the blink modes.
-- */
--#define QCA808X_LED_FORCE_EN BIT(15)
--#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
--#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
--#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
--#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
--#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
--
--#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a
--/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
-- * LED to active high. It's not clear what BIT 3 and BIT 4 does.
-- */
--#define QCA808X_LED_ACTIVE_HIGH BIT(6)
--
--/* QCA808X 1G chip type */
--#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
--#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
--
--#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072
--#define QCA8081_PHY_FIFO_RSTN BIT(11)
--
--MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
-+MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
- MODULE_AUTHOR("Matus Ujhelyi");
- MODULE_LICENSE("GPL");
-
-@@ -318,7 +122,6 @@ struct at803x_priv {
- bool is_1000basex;
- struct regulator_dev *vddio_rdev;
- struct regulator_dev *vddh_rdev;
-- int led_polarity_mode;
- };
-
- struct at803x_context {
-@@ -519,9 +322,6 @@ static int at803x_probe(struct phy_devic
- if (!priv)
- return -ENOMEM;
-
-- /* Init LED polarity mode to -1 */
-- priv->led_polarity_mode = -1;
--
- phydev->priv = priv;
-
- ret = at803x_parse_dt(phydev);
-@@ -1216,672 +1016,6 @@ static int at8035_probe(struct phy_devic
- return at8035_parse_dt(phydev);
- }
-
--static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
--{
-- int ret;
--
-- /* Enable fast retrain */
-- ret = genphy_c45_fast_retrain(phydev, true);
-- if (ret)
-- return ret;
--
-- phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
-- QCA808X_TOP_OPTION1_DATA);
-- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
-- QCA808X_MSE_THRESHOLD_20DB_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
-- QCA808X_MSE_THRESHOLD_17DB_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
-- QCA808X_MSE_THRESHOLD_27DB_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
-- QCA808X_MSE_THRESHOLD_28DB_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
-- QCA808X_MMD3_DEBUG_1_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
-- QCA808X_MMD3_DEBUG_4_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
-- QCA808X_MMD3_DEBUG_5_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
-- QCA808X_MMD3_DEBUG_3_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
-- QCA808X_MMD3_DEBUG_6_VALUE);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
-- QCA808X_MMD3_DEBUG_2_VALUE);
--
-- return 0;
--}
--
--static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
--{
-- u16 seed_value;
--
-- if (!enable)
-- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
-- QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
--
-- seed_value = get_random_u32_below(QCA808X_MASTER_SLAVE_SEED_RANGE);
-- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
-- QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
-- FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
-- QCA808X_MASTER_SLAVE_SEED_ENABLE);
--}
--
--static bool qca808x_is_prefer_master(struct phy_device *phydev)
--{
-- return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
-- (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
--}
--
--static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
--{
-- return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
--}
--
--static int qca808x_config_init(struct phy_device *phydev)
--{
-- int ret;
--
-- /* Active adc&vga on 802.3az for the link 1000M and 100M */
-- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
-- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
-- if (ret)
-- return ret;
--
-- /* Adjust the threshold on 802.3az for the link 1000M */
-- ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
-- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
-- QCA808X_MMD3_AZ_TRAINING_VAL);
-- if (ret)
-- return ret;
--
-- if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
-- /* Config the fast retrain for the link 2500M */
-- ret = qca808x_phy_fast_retrain_config(phydev);
-- if (ret)
-- return ret;
--
-- ret = genphy_read_master_slave(phydev);
-- if (ret < 0)
-- return ret;
--
-- if (!qca808x_is_prefer_master(phydev)) {
-- /* Enable seed and configure lower ramdom seed to make phy
-- * linked as slave mode.
-- */
-- ret = qca808x_phy_ms_seed_enable(phydev, true);
-- if (ret)
-- return ret;
-- }
-- }
--
-- /* Configure adc threshold as 100mv for the link 10M */
-- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
-- QCA808X_ADC_THRESHOLD_MASK,
-- QCA808X_ADC_THRESHOLD_100MV);
--}
--
--static int qca808x_read_status(struct phy_device *phydev)
--{
-- struct at803x_ss_mask ss_mask = { 0 };
-- int ret;
--
-- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
-- if (ret < 0)
-- return ret;
--
-- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
-- ret & MDIO_AN_10GBT_STAT_LP2_5G);
--
-- ret = genphy_read_status(phydev);
-- if (ret)
-- return ret;
--
-- /* qca8081 takes the different bits for speed value from at803x */
-- ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
-- ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
-- ret = at803x_read_specific_status(phydev, ss_mask);
-- if (ret < 0)
-- return ret;
--
-- if (phydev->link) {
-- if (phydev->speed == SPEED_2500)
-- phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
-- else
-- phydev->interface = PHY_INTERFACE_MODE_SGMII;
-- } else {
-- /* generate seed as a lower random value to make PHY linked as SLAVE easily,
-- * except for master/slave configuration fault detected or the master mode
-- * preferred.
-- *
-- * the reason for not putting this code into the function link_change_notify is
-- * the corner case where the link partner is also the qca8081 PHY and the seed
-- * value is configured as the same value, the link can't be up and no link change
-- * occurs.
-- */
-- if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
-- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
-- qca808x_is_prefer_master(phydev)) {
-- qca808x_phy_ms_seed_enable(phydev, false);
-- } else {
-- qca808x_phy_ms_seed_enable(phydev, true);
-- }
-- }
-- }
--
-- return 0;
--}
--
--static int qca808x_soft_reset(struct phy_device *phydev)
--{
-- int ret;
--
-- ret = genphy_soft_reset(phydev);
-- if (ret < 0)
-- return ret;
--
-- if (qca808x_has_fast_retrain_or_slave_seed(phydev))
-- ret = qca808x_phy_ms_seed_enable(phydev, true);
--
-- return ret;
--}
--
--static bool qca808x_cdt_fault_length_valid(int cdt_code)
--{
-- switch (cdt_code) {
-- case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-- return true;
-- default:
-- return false;
-- }
--}
--
--static int qca808x_cable_test_result_trans(int cdt_code)
--{
-- switch (cdt_code) {
-- case QCA808X_CDT_STATUS_STAT_NORMAL:
-- return ETHTOOL_A_CABLE_RESULT_CODE_OK;
-- case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
-- case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-- return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
-- case QCA808X_CDT_STATUS_STAT_FAIL:
-- default:
-- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
-- }
--}
--
--static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
-- int result)
--{
-- int val;
-- u32 cdt_length_reg = 0;
--
-- switch (pair) {
-- case ETHTOOL_A_CABLE_PAIR_A:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
-- break;
-- case ETHTOOL_A_CABLE_PAIR_B:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
-- break;
-- case ETHTOOL_A_CABLE_PAIR_C:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
-- break;
-- case ETHTOOL_A_CABLE_PAIR_D:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
-- break;
-- default:
-- return -EINVAL;
-- }
--
-- val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
-- if (val < 0)
-- return val;
--
-- if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
-- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
-- else
-- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
--
-- return at803x_cdt_fault_length(val);
--}
--
--static int qca808x_cable_test_start(struct phy_device *phydev)
--{
-- int ret;
--
-- /* perform CDT with the following configs:
-- * 1. disable hibernation.
-- * 2. force PHY working in MDI mode.
-- * 3. for PHY working in 1000BaseT.
-- * 4. configure the threshold.
-- */
--
-- ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0);
-- if (ret < 0)
-- return ret;
--
-- ret = at803x_config_mdix(phydev, ETH_TP_MDI);
-- if (ret < 0)
-- return ret;
--
-- /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */
-- phydev->duplex = DUPLEX_FULL;
-- phydev->speed = SPEED_1000;
-- ret = genphy_c45_pma_setup_forced(phydev);
-- if (ret < 0)
-- return ret;
--
-- ret = genphy_setup_forced(phydev);
-- if (ret < 0)
-- return ret;
--
-- /* configure the thresholds for open, short, pair ok test */
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
-- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
--
-- return 0;
--}
--
--static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
-- u16 status)
--{
-- int length, result;
-- u16 pair_code;
--
-- switch (pair) {
-- case ETHTOOL_A_CABLE_PAIR_A:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
-- break;
-- case ETHTOOL_A_CABLE_PAIR_B:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
-- break;
-- case ETHTOOL_A_CABLE_PAIR_C:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
-- break;
-- case ETHTOOL_A_CABLE_PAIR_D:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
-- break;
-- default:
-- return -EINVAL;
-- }
--
-- result = qca808x_cable_test_result_trans(pair_code);
-- ethnl_cable_test_result(phydev, pair, result);
--
-- if (qca808x_cdt_fault_length_valid(pair_code)) {
-- length = qca808x_cdt_fault_length(phydev, pair, result);
-- ethnl_cable_test_fault_length(phydev, pair, length);
-- }
--
-- return 0;
--}
--
--static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
--{
-- int ret, val;
--
-- *finished = false;
--
-- val = QCA808X_CDT_ENABLE_TEST |
-- QCA808X_CDT_LENGTH_UNIT;
-- ret = at803x_cdt_start(phydev, val);
-- if (ret)
-- return ret;
--
-- ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
-- if (ret)
-- return ret;
--
-- val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
-- if (val < 0)
-- return val;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
-- if (ret)
-- return ret;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
-- if (ret)
-- return ret;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
-- if (ret)
-- return ret;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
-- if (ret)
-- return ret;
--
-- *finished = true;
--
-- return 0;
--}
--
--static int qca808x_get_features(struct phy_device *phydev)
--{
-- int ret;
--
-- ret = genphy_c45_pma_read_abilities(phydev);
-- if (ret)
-- return ret;
--
-- /* The autoneg ability is not existed in bit3 of MMD7.1,
-- * but it is supported by qca808x PHY, so we add it here
-- * manually.
-- */
-- linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
--
-- /* As for the qca8081 1G version chip, the 2500baseT ability is also
-- * existed in the bit0 of MMD1.21, we need to remove it manually if
-- * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
-- */
-- ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
-- if (ret < 0)
-- return ret;
--
-- if (QCA808X_PHY_CHIP_TYPE_1G & ret)
-- linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
--
-- return 0;
--}
--
--static int qca808x_config_aneg(struct phy_device *phydev)
--{
-- int phy_ctrl = 0;
-- int ret;
--
-- ret = at803x_prepare_config_aneg(phydev);
-- if (ret)
-- return ret;
--
-- /* The reg MII_BMCR also needs to be configured for force mode, the
-- * genphy_config_aneg is also needed.
-- */
-- if (phydev->autoneg == AUTONEG_DISABLE)
-- genphy_c45_pma_setup_forced(phydev);
--
-- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
-- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
--
-- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
-- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
-- if (ret < 0)
-- return ret;
--
-- return __genphy_config_aneg(phydev, ret);
--}
--
--static void qca808x_link_change_notify(struct phy_device *phydev)
--{
-- /* Assert interface sgmii fifo on link down, deassert it on link up,
-- * the interface device address is always phy address added by 1.
-- */
-- mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
-- MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
-- QCA8081_PHY_FIFO_RSTN,
-- phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
--}
--
--static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
-- u16 *offload_trigger)
--{
-- /* Parsing specific to netdev trigger */
-- if (test_bit(TRIGGER_NETDEV_TX, &rules))
-- *offload_trigger |= QCA808X_LED_TX_BLINK;
-- if (test_bit(TRIGGER_NETDEV_RX, &rules))
-- *offload_trigger |= QCA808X_LED_RX_BLINK;
-- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
-- *offload_trigger |= QCA808X_LED_SPEED10_ON;
-- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
-- *offload_trigger |= QCA808X_LED_SPEED100_ON;
-- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
-- *offload_trigger |= QCA808X_LED_SPEED1000_ON;
-- if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
-- *offload_trigger |= QCA808X_LED_SPEED2500_ON;
-- if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
-- *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
-- if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
-- *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
--
-- if (rules && !*offload_trigger)
-- return -EOPNOTSUPP;
--
-- /* Enable BLINK_CHECK_BYPASS by default to make the LED
-- * blink even with duplex or speed mode not enabled.
-- */
-- *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
--
-- return 0;
--}
--
--static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
--{
-- u16 reg;
--
-- if (index > 2)
-- return -EINVAL;
--
-- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_FORCE_EN);
--}
--
--static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
-- unsigned long rules)
--{
-- u16 offload_trigger = 0;
--
-- if (index > 2)
-- return -EINVAL;
--
-- return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
--}
--
--static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
-- unsigned long rules)
--{
-- u16 reg, offload_trigger = 0;
-- int ret;
--
-- if (index > 2)
-- return -EINVAL;
--
-- reg = QCA808X_MMD7_LED_CTRL(index);
--
-- ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
-- if (ret)
-- return ret;
--
-- ret = qca808x_led_hw_control_enable(phydev, index);
-- if (ret)
-- return ret;
--
-- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_PATTERN_MASK,
-- offload_trigger);
--}
--
--static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
--{
-- u16 reg;
-- int val;
--
-- if (index > 2)
-- return false;
--
-- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
--
-- return !(val & QCA808X_LED_FORCE_EN);
--}
--
--static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
-- unsigned long *rules)
--{
-- u16 reg;
-- int val;
--
-- if (index > 2)
-- return -EINVAL;
--
-- /* Check if we have hw control enabled */
-- if (qca808x_led_hw_control_status(phydev, index))
-- return -EINVAL;
--
-- reg = QCA808X_MMD7_LED_CTRL(index);
--
-- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
-- if (val & QCA808X_LED_TX_BLINK)
-- set_bit(TRIGGER_NETDEV_TX, rules);
-- if (val & QCA808X_LED_RX_BLINK)
-- set_bit(TRIGGER_NETDEV_RX, rules);
-- if (val & QCA808X_LED_SPEED10_ON)
-- set_bit(TRIGGER_NETDEV_LINK_10, rules);
-- if (val & QCA808X_LED_SPEED100_ON)
-- set_bit(TRIGGER_NETDEV_LINK_100, rules);
-- if (val & QCA808X_LED_SPEED1000_ON)
-- set_bit(TRIGGER_NETDEV_LINK_1000, rules);
-- if (val & QCA808X_LED_SPEED2500_ON)
-- set_bit(TRIGGER_NETDEV_LINK_2500, rules);
-- if (val & QCA808X_LED_HALF_DUPLEX_ON)
-- set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
-- if (val & QCA808X_LED_FULL_DUPLEX_ON)
-- set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
--
-- return 0;
--}
--
--static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
--{
-- u16 reg;
--
-- if (index > 2)
-- return -EINVAL;
--
-- reg = QCA808X_MMD7_LED_CTRL(index);
--
-- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_PATTERN_MASK);
--}
--
--static int qca808x_led_brightness_set(struct phy_device *phydev,
-- u8 index, enum led_brightness value)
--{
-- u16 reg;
-- int ret;
--
-- if (index > 2)
-- return -EINVAL;
--
-- if (!value) {
-- ret = qca808x_led_hw_control_reset(phydev, index);
-- if (ret)
-- return ret;
-- }
--
-- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-- QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
-- QCA808X_LED_FORCE_OFF);
--}
--
--static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
-- unsigned long *delay_on,
-- unsigned long *delay_off)
--{
-- int ret;
-- u16 reg;
--
-- if (index > 2)
-- return -EINVAL;
--
-- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- /* Set blink to 50% off, 50% on at 4Hz by default */
-- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
-- QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
-- QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
-- if (ret)
-- return ret;
--
-- /* We use BLINK_1 for normal blinking */
-- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
-- if (ret)
-- return ret;
--
-- /* We set blink to 4Hz, aka 250ms */
-- *delay_on = 250 / 2;
-- *delay_off = 250 / 2;
--
-- return 0;
--}
--
--static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
-- unsigned long modes)
--{
-- struct at803x_priv *priv = phydev->priv;
-- bool active_low = false;
-- u32 mode;
--
-- for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
-- switch (mode) {
-- case PHY_LED_ACTIVE_LOW:
-- active_low = true;
-- break;
-- default:
-- return -EINVAL;
-- }
-- }
--
-- /* PHY polarity is global and can't be set per LED.
-- * To detect this, check if last requested polarity mode
-- * match the new one.
-- */
-- if (priv->led_polarity_mode >= 0 &&
-- priv->led_polarity_mode != active_low) {
-- phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
-- return -EINVAL;
-- }
--
-- /* Save the last PHY polarity mode */
-- priv->led_polarity_mode = active_low;
--
-- return phy_modify_mmd(phydev, MDIO_MMD_AN,
-- QCA808X_MMD7_LED_POLARITY_CTRL,
-- QCA808X_LED_ACTIVE_HIGH,
-- active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
--}
--
- static struct phy_driver at803x_driver[] = {
- {
- /* Qualcomm Atheros AR8035 */
-@@ -1989,34 +1123,6 @@ static struct phy_driver at803x_driver[]
- .read_status = at803x_read_status,
- .soft_reset = genphy_soft_reset,
- .config_aneg = at803x_config_aneg,
--}, {
-- /* Qualcomm QCA8081 */
-- PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
-- .name = "Qualcomm QCA8081",
-- .flags = PHY_POLL_CABLE_TEST,
-- .probe = at803x_probe,
-- .config_intr = at803x_config_intr,
-- .handle_interrupt = at803x_handle_interrupt,
-- .get_tunable = at803x_get_tunable,
-- .set_tunable = at803x_set_tunable,
-- .set_wol = at803x_set_wol,
-- .get_wol = at803x_get_wol,
-- .get_features = qca808x_get_features,
-- .config_aneg = qca808x_config_aneg,
-- .suspend = genphy_suspend,
-- .resume = genphy_resume,
-- .read_status = qca808x_read_status,
-- .config_init = qca808x_config_init,
-- .soft_reset = qca808x_soft_reset,
-- .cable_test_start = qca808x_cable_test_start,
-- .cable_test_get_status = qca808x_cable_test_get_status,
-- .link_change_notify = qca808x_link_change_notify,
-- .led_brightness_set = qca808x_led_brightness_set,
-- .led_blink_set = qca808x_led_blink_set,
-- .led_hw_is_supported = qca808x_led_hw_is_supported,
-- .led_hw_control_set = qca808x_led_hw_control_set,
-- .led_hw_control_get = qca808x_led_hw_control_get,
-- .led_polarity_set = qca808x_led_polarity_set,
- }, };
-
- module_phy_driver(at803x_driver);
-@@ -2028,7 +1134,6 @@ static struct mdio_device_id __maybe_unu
- { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) },
- { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
- { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) },
-- { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
- { }
- };
-
---- /dev/null
-+++ b/drivers/net/phy/qcom/qca808x.c
-@@ -0,0 +1,934 @@
-+// SPDX-License-Identifier: GPL-2.0+
-+
-+#include <linux/phy.h>
-+#include <linux/module.h>
-+#include <linux/ethtool_netlink.h>
-+
-+#include "qcom.h"
-+
-+/* ADC threshold */
-+#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80
-+#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0)
-+#define QCA808X_ADC_THRESHOLD_80MV 0
-+#define QCA808X_ADC_THRESHOLD_100MV 0xf0
-+#define QCA808X_ADC_THRESHOLD_200MV 0x0f
-+#define QCA808X_ADC_THRESHOLD_300MV 0xff
-+
-+/* CLD control */
-+#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007
-+#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4)
-+#define QCA808X_8023AZ_AFE_EN 0x90
-+
-+/* AZ control */
-+#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008
-+#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32
-+
-+#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014
-+#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529
-+
-+#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E
-+#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341
-+
-+#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E
-+#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419
-+
-+#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020
-+#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341
-+
-+#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c
-+#define QCA808X_TOP_OPTION1_DATA 0x0
-+
-+#define QCA808X_PHY_MMD3_DEBUG_1 0xa100
-+#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203
-+#define QCA808X_PHY_MMD3_DEBUG_2 0xa101
-+#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad
-+#define QCA808X_PHY_MMD3_DEBUG_3 0xa103
-+#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698
-+#define QCA808X_PHY_MMD3_DEBUG_4 0xa105
-+#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001
-+#define QCA808X_PHY_MMD3_DEBUG_5 0xa106
-+#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111
-+#define QCA808X_PHY_MMD3_DEBUG_6 0xa011
-+#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85
-+
-+/* master/slave seed config */
-+#define QCA808X_PHY_DEBUG_LOCAL_SEED 9
-+#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1)
-+#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2)
-+#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32
-+
-+/* Hibernation yields lower power consumpiton in contrast with normal operation mode.
-+ * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s.
-+ */
-+#define QCA808X_DBG_AN_TEST 0xb
-+#define QCA808X_HIBERNATION_EN BIT(15)
-+
-+#define QCA808X_CDT_ENABLE_TEST BIT(15)
-+#define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
-+#define QCA808X_CDT_STATUS BIT(11)
-+#define QCA808X_CDT_LENGTH_UNIT BIT(10)
-+
-+#define QCA808X_MMD3_CDT_STATUS 0x8064
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
-+#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
-+#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
-+
-+#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
-+#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
-+#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
-+#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
-+
-+#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
-+#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
-+#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
-+#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
-+#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
-+
-+#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
-+#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
-+#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
-+#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
-+
-+/* NORMAL are MDI with type set to 0 */
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI1)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI1)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI2)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI2)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI3)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI3)
-+
-+/* Added for reference of existence but should be handled by wait_for_completion already */
-+#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
-+
-+#define QCA808X_MMD7_LED_GLOBAL 0x8073
-+#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
-+#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
-+/* Values are the same for both BLINK_1 and BLINK_2 */
-+#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
-+#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
-+#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
-+#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
-+#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
-+#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
-+#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
-+#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
-+#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
-+#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
-+#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
-+#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
-+#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
-+#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
-+#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
-+#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
-+#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
-+#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
-+
-+#define QCA808X_MMD7_LED2_CTRL 0x8074
-+#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075
-+#define QCA808X_MMD7_LED1_CTRL 0x8076
-+#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077
-+#define QCA808X_MMD7_LED0_CTRL 0x8078
-+#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))
-+
-+/* LED hw control pattern is the same for every LED */
-+#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
-+#define QCA808X_LED_SPEED2500_ON BIT(15)
-+#define QCA808X_LED_SPEED2500_BLINK BIT(14)
-+/* Follow blink trigger even if duplex or speed condition doesn't match */
-+#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
-+#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
-+#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
-+#define QCA808X_LED_TX_BLINK BIT(10)
-+#define QCA808X_LED_RX_BLINK BIT(9)
-+#define QCA808X_LED_TX_ON_10MS BIT(8)
-+#define QCA808X_LED_RX_ON_10MS BIT(7)
-+#define QCA808X_LED_SPEED1000_ON BIT(6)
-+#define QCA808X_LED_SPEED100_ON BIT(5)
-+#define QCA808X_LED_SPEED10_ON BIT(4)
-+#define QCA808X_LED_COLLISION_BLINK BIT(3)
-+#define QCA808X_LED_SPEED1000_BLINK BIT(2)
-+#define QCA808X_LED_SPEED100_BLINK BIT(1)
-+#define QCA808X_LED_SPEED10_BLINK BIT(0)
-+
-+#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079
-+#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))
-+
-+/* LED force ctrl is the same for every LED
-+ * No documentation exist for this, not even internal one
-+ * with NDA as QCOM gives only info about configuring
-+ * hw control pattern rules and doesn't indicate any way
-+ * to force the LED to specific mode.
-+ * These define comes from reverse and testing and maybe
-+ * lack of some info or some info are not entirely correct.
-+ * For the basic LED control and hw control these finding
-+ * are enough to support LED control in all the required APIs.
-+ *
-+ * On doing some comparison with implementation with qca807x,
-+ * it was found that it's 1:1 equal to it and confirms all the
-+ * reverse done. It was also found further specification with the
-+ * force mode and the blink modes.
-+ */
-+#define QCA808X_LED_FORCE_EN BIT(15)
-+#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
-+#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
-+#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
-+#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
-+#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
-+
-+#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a
-+/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
-+ * LED to active high. It's not clear what BIT 3 and BIT 4 does.
-+ */
-+#define QCA808X_LED_ACTIVE_HIGH BIT(6)
-+
-+/* QCA808X 1G chip type */
-+#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
-+#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
-+
-+#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072
-+#define QCA8081_PHY_FIFO_RSTN BIT(11)
-+
-+#define QCA8081_PHY_ID 0x004dd101
-+
-+MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
-+MODULE_AUTHOR("Matus Ujhelyi");
-+MODULE_LICENSE("GPL");
-+
-+struct qca808x_priv {
-+ int led_polarity_mode;
-+};
-+
-+static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ /* Enable fast retrain */
-+ ret = genphy_c45_fast_retrain(phydev, true);
-+ if (ret)
-+ return ret;
-+
-+ phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
-+ QCA808X_TOP_OPTION1_DATA);
-+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
-+ QCA808X_MSE_THRESHOLD_20DB_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
-+ QCA808X_MSE_THRESHOLD_17DB_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
-+ QCA808X_MSE_THRESHOLD_27DB_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
-+ QCA808X_MSE_THRESHOLD_28DB_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
-+ QCA808X_MMD3_DEBUG_1_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
-+ QCA808X_MMD3_DEBUG_4_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
-+ QCA808X_MMD3_DEBUG_5_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
-+ QCA808X_MMD3_DEBUG_3_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
-+ QCA808X_MMD3_DEBUG_6_VALUE);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
-+ QCA808X_MMD3_DEBUG_2_VALUE);
-+
-+ return 0;
-+}
-+
-+static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
-+{
-+ u16 seed_value;
-+
-+ if (!enable)
-+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
-+ QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
-+
-+ seed_value = get_random_u32_below(QCA808X_MASTER_SLAVE_SEED_RANGE);
-+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
-+ QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
-+ FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
-+ QCA808X_MASTER_SLAVE_SEED_ENABLE);
-+}
-+
-+static bool qca808x_is_prefer_master(struct phy_device *phydev)
-+{
-+ return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
-+ (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
-+}
-+
-+static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
-+{
-+ return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
-+}
-+
-+static int qca808x_probe(struct phy_device *phydev)
-+{
-+ struct device *dev = &phydev->mdio.dev;
-+ struct qca808x_priv *priv;
-+
-+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ /* Init LED polarity mode to -1 */
-+ priv->led_polarity_mode = -1;
-+
-+ phydev->priv = priv;
-+
-+ return 0;
-+}
-+
-+static int qca808x_config_init(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ /* Active adc&vga on 802.3az for the link 1000M and 100M */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
-+ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
-+ if (ret)
-+ return ret;
-+
-+ /* Adjust the threshold on 802.3az for the link 1000M */
-+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
-+ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
-+ QCA808X_MMD3_AZ_TRAINING_VAL);
-+ if (ret)
-+ return ret;
-+
-+ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
-+ /* Config the fast retrain for the link 2500M */
-+ ret = qca808x_phy_fast_retrain_config(phydev);
-+ if (ret)
-+ return ret;
-+
-+ ret = genphy_read_master_slave(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (!qca808x_is_prefer_master(phydev)) {
-+ /* Enable seed and configure lower ramdom seed to make phy
-+ * linked as slave mode.
-+ */
-+ ret = qca808x_phy_ms_seed_enable(phydev, true);
-+ if (ret)
-+ return ret;
-+ }
-+ }
-+
-+ /* Configure adc threshold as 100mv for the link 10M */
-+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
-+ QCA808X_ADC_THRESHOLD_MASK,
-+ QCA808X_ADC_THRESHOLD_100MV);
-+}
-+
-+static int qca808x_read_status(struct phy_device *phydev)
-+{
-+ struct at803x_ss_mask ss_mask = { 0 };
-+ int ret;
-+
-+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
-+ if (ret < 0)
-+ return ret;
-+
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
-+ ret & MDIO_AN_10GBT_STAT_LP2_5G);
-+
-+ ret = genphy_read_status(phydev);
-+ if (ret)
-+ return ret;
-+
-+ /* qca8081 takes the different bits for speed value from at803x */
-+ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
-+ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
-+ ret = at803x_read_specific_status(phydev, ss_mask);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (phydev->link) {
-+ if (phydev->speed == SPEED_2500)
-+ phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
-+ else
-+ phydev->interface = PHY_INTERFACE_MODE_SGMII;
-+ } else {
-+ /* generate seed as a lower random value to make PHY linked as SLAVE easily,
-+ * except for master/slave configuration fault detected or the master mode
-+ * preferred.
-+ *
-+ * the reason for not putting this code into the function link_change_notify is
-+ * the corner case where the link partner is also the qca8081 PHY and the seed
-+ * value is configured as the same value, the link can't be up and no link change
-+ * occurs.
-+ */
-+ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
-+ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
-+ qca808x_is_prefer_master(phydev)) {
-+ qca808x_phy_ms_seed_enable(phydev, false);
-+ } else {
-+ qca808x_phy_ms_seed_enable(phydev, true);
-+ }
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static int qca808x_soft_reset(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = genphy_soft_reset(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (qca808x_has_fast_retrain_or_slave_seed(phydev))
-+ ret = qca808x_phy_ms_seed_enable(phydev, true);
-+
-+ return ret;
-+}
-+
-+static bool qca808x_cdt_fault_length_valid(int cdt_code)
-+{
-+ switch (cdt_code) {
-+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-+ return true;
-+ default:
-+ return false;
-+ }
-+}
-+
-+static int qca808x_cable_test_result_trans(int cdt_code)
-+{
-+ switch (cdt_code) {
-+ case QCA808X_CDT_STATUS_STAT_NORMAL:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
-+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
-+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
-+ case QCA808X_CDT_STATUS_STAT_FAIL:
-+ default:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
-+ }
-+}
-+
-+static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
-+ int result)
-+{
-+ int val;
-+ u32 cdt_length_reg = 0;
-+
-+ switch (pair) {
-+ case ETHTOOL_A_CABLE_PAIR_A:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_B:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_C:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_D:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
-+ if (val < 0)
-+ return val;
-+
-+ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
-+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
-+ else
-+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
-+
-+ return at803x_cdt_fault_length(val);
-+}
-+
-+static int qca808x_cable_test_start(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ /* perform CDT with the following configs:
-+ * 1. disable hibernation.
-+ * 2. force PHY working in MDI mode.
-+ * 3. for PHY working in 1000BaseT.
-+ * 4. configure the threshold.
-+ */
-+
-+ ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = at803x_config_mdix(phydev, ETH_TP_MDI);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */
-+ phydev->duplex = DUPLEX_FULL;
-+ phydev->speed = SPEED_1000;
-+ ret = genphy_c45_pma_setup_forced(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = genphy_setup_forced(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* configure the thresholds for open, short, pair ok test */
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
-+
-+ return 0;
-+}
-+
-+static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
-+ u16 status)
-+{
-+ int length, result;
-+ u16 pair_code;
-+
-+ switch (pair) {
-+ case ETHTOOL_A_CABLE_PAIR_A:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_B:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_C:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_D:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ result = qca808x_cable_test_result_trans(pair_code);
-+ ethnl_cable_test_result(phydev, pair, result);
-+
-+ if (qca808x_cdt_fault_length_valid(pair_code)) {
-+ length = qca808x_cdt_fault_length(phydev, pair, result);
-+ ethnl_cable_test_fault_length(phydev, pair, length);
-+ }
-+
-+ return 0;
-+}
-+
-+static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
-+{
-+ int ret, val;
-+
-+ *finished = false;
-+
-+ val = QCA808X_CDT_ENABLE_TEST |
-+ QCA808X_CDT_LENGTH_UNIT;
-+ ret = at803x_cdt_start(phydev, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
-+ if (ret)
-+ return ret;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
-+ if (val < 0)
-+ return val;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
-+ if (ret)
-+ return ret;
-+
-+ *finished = true;
-+
-+ return 0;
-+}
-+
-+static int qca808x_get_features(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = genphy_c45_pma_read_abilities(phydev);
-+ if (ret)
-+ return ret;
-+
-+ /* The autoneg ability is not existed in bit3 of MMD7.1,
-+ * but it is supported by qca808x PHY, so we add it here
-+ * manually.
-+ */
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
-+
-+ /* As for the qca8081 1G version chip, the 2500baseT ability is also
-+ * existed in the bit0 of MMD1.21, we need to remove it manually if
-+ * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
-+ */
-+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (QCA808X_PHY_CHIP_TYPE_1G & ret)
-+ linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
-+
-+ return 0;
-+}
-+
-+static int qca808x_config_aneg(struct phy_device *phydev)
-+{
-+ int phy_ctrl = 0;
-+ int ret;
-+
-+ ret = at803x_prepare_config_aneg(phydev);
-+ if (ret)
-+ return ret;
-+
-+ /* The reg MII_BMCR also needs to be configured for force mode, the
-+ * genphy_config_aneg is also needed.
-+ */
-+ if (phydev->autoneg == AUTONEG_DISABLE)
-+ genphy_c45_pma_setup_forced(phydev);
-+
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
-+ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
-+
-+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
-+ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
-+ if (ret < 0)
-+ return ret;
-+
-+ return __genphy_config_aneg(phydev, ret);
-+}
-+
-+static void qca808x_link_change_notify(struct phy_device *phydev)
-+{
-+ /* Assert interface sgmii fifo on link down, deassert it on link up,
-+ * the interface device address is always phy address added by 1.
-+ */
-+ mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
-+ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
-+ QCA8081_PHY_FIFO_RSTN,
-+ phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
-+}
-+
-+static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
-+ u16 *offload_trigger)
-+{
-+ /* Parsing specific to netdev trigger */
-+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
-+ *offload_trigger |= QCA808X_LED_TX_BLINK;
-+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
-+ *offload_trigger |= QCA808X_LED_RX_BLINK;
-+ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED10_ON;
-+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED100_ON;
-+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED1000_ON;
-+ if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED2500_ON;
-+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
-+ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
-+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
-+ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
-+
-+ if (rules && !*offload_trigger)
-+ return -EOPNOTSUPP;
-+
-+ /* Enable BLINK_CHECK_BYPASS by default to make the LED
-+ * blink even with duplex or speed mode not enabled.
-+ */
-+ *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
-+
-+ return 0;
-+}
-+
-+static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN);
-+}
-+
-+static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ u16 offload_trigger = 0;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
-+}
-+
-+static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ u16 reg, offload_trigger = 0;
-+ int ret;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_CTRL(index);
-+
-+ ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_led_hw_control_enable(phydev, index);
-+ if (ret)
-+ return ret;
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_PATTERN_MASK,
-+ offload_trigger);
-+}
-+
-+static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg;
-+ int val;
-+
-+ if (index > 2)
-+ return false;
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
-+
-+ return !(val & QCA808X_LED_FORCE_EN);
-+}
-+
-+static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
-+ unsigned long *rules)
-+{
-+ u16 reg;
-+ int val;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ /* Check if we have hw control enabled */
-+ if (qca808x_led_hw_control_status(phydev, index))
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_CTRL(index);
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
-+ if (val & QCA808X_LED_TX_BLINK)
-+ set_bit(TRIGGER_NETDEV_TX, rules);
-+ if (val & QCA808X_LED_RX_BLINK)
-+ set_bit(TRIGGER_NETDEV_RX, rules);
-+ if (val & QCA808X_LED_SPEED10_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_10, rules);
-+ if (val & QCA808X_LED_SPEED100_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
-+ if (val & QCA808X_LED_SPEED1000_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
-+ if (val & QCA808X_LED_SPEED2500_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_2500, rules);
-+ if (val & QCA808X_LED_HALF_DUPLEX_ON)
-+ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
-+ if (val & QCA808X_LED_FULL_DUPLEX_ON)
-+ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
-+
-+ return 0;
-+}
-+
-+static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_CTRL(index);
-+
-+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_PATTERN_MASK);
-+}
-+
-+static int qca808x_led_brightness_set(struct phy_device *phydev,
-+ u8 index, enum led_brightness value)
-+{
-+ u16 reg;
-+ int ret;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ if (!value) {
-+ ret = qca808x_led_hw_control_reset(phydev, index);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-+ QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
-+ QCA808X_LED_FORCE_OFF);
-+}
-+
-+static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ int ret;
-+ u16 reg;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
-+
-+ /* Set blink to 50% off, 50% on at 4Hz by default */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
-+ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
-+ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
-+ if (ret)
-+ return ret;
-+
-+ /* We use BLINK_1 for normal blinking */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
-+ if (ret)
-+ return ret;
-+
-+ /* We set blink to 4Hz, aka 250ms */
-+ *delay_on = 250 / 2;
-+ *delay_off = 250 / 2;
-+
-+ return 0;
-+}
-+
-+static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
-+ unsigned long modes)
-+{
-+ struct qca808x_priv *priv = phydev->priv;
-+ bool active_low = false;
-+ u32 mode;
-+
-+ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
-+ switch (mode) {
-+ case PHY_LED_ACTIVE_LOW:
-+ active_low = true;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+ }
-+
-+ /* PHY polarity is global and can't be set per LED.
-+ * To detect this, check if last requested polarity mode
-+ * match the new one.
-+ */
-+ if (priv->led_polarity_mode >= 0 &&
-+ priv->led_polarity_mode != active_low) {
-+ phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
-+ return -EINVAL;
-+ }
-+
-+ /* Save the last PHY polarity mode */
-+ priv->led_polarity_mode = active_low;
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN,
-+ QCA808X_MMD7_LED_POLARITY_CTRL,
-+ QCA808X_LED_ACTIVE_HIGH,
-+ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
-+}
-+
-+static struct phy_driver qca808x_driver[] = {
-+{
-+ /* Qualcomm QCA8081 */
-+ PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
-+ .name = "Qualcomm QCA8081",
-+ .flags = PHY_POLL_CABLE_TEST,
-+ .probe = qca808x_probe,
-+ .config_intr = at803x_config_intr,
-+ .handle_interrupt = at803x_handle_interrupt,
-+ .get_tunable = at803x_get_tunable,
-+ .set_tunable = at803x_set_tunable,
-+ .set_wol = at803x_set_wol,
-+ .get_wol = at803x_get_wol,
-+ .get_features = qca808x_get_features,
-+ .config_aneg = qca808x_config_aneg,
-+ .suspend = genphy_suspend,
-+ .resume = genphy_resume,
-+ .read_status = qca808x_read_status,
-+ .config_init = qca808x_config_init,
-+ .soft_reset = qca808x_soft_reset,
-+ .cable_test_start = qca808x_cable_test_start,
-+ .cable_test_get_status = qca808x_cable_test_get_status,
-+ .link_change_notify = qca808x_link_change_notify,
-+ .led_brightness_set = qca808x_led_brightness_set,
-+ .led_blink_set = qca808x_led_blink_set,
-+ .led_hw_is_supported = qca808x_led_hw_is_supported,
-+ .led_hw_control_set = qca808x_led_hw_control_set,
-+ .led_hw_control_get = qca808x_led_hw_control_get,
-+ .led_polarity_set = qca808x_led_polarity_set,
-+}, };
-+
-+module_phy_driver(qca808x_driver);
-+
-+static struct mdio_device_id __maybe_unused qca808x_tbl[] = {
-+ { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
-+ { }
-+};
-+
-+MODULE_DEVICE_TABLE(mdio, qca808x_tbl);
+++ /dev/null
-From ebb30ccbbdbd6fae5177b676da4f4ac92bb4f635 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 15 Dec 2023 14:15:31 +0100
-Subject: [PATCH 1/4] net: phy: make addr type u8 in phy_package_shared struct
-
-Switch addr type in phy_package_shared struct to u8.
-
-The value is already checked to be non negative and to be less than
-PHY_MAX_ADDR, hence u8 is better suited than using int.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- include/linux/phy.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -338,7 +338,7 @@ struct mdio_bus_stats {
- * phy_package_leave().
- */
- struct phy_package_shared {
-- int addr;
-+ u8 addr;
- refcount_t refcnt;
- unsigned long flags;
- size_t priv_size;
+++ /dev/null
-From 9eea577eb1155fe4a183bc5e7bf269b0b2e7a6ba Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 15 Dec 2023 14:15:32 +0100
-Subject: [PATCH 2/4] net: phy: extend PHY package API to support multiple
- global address
-
-Current API for PHY package are limited to single address to configure
-global settings for the PHY package.
-
-It was found that some PHY package (for example the qca807x, a PHY
-package that is shipped with a bundle of 5 PHY) requires multiple PHY
-address to configure global settings. An example scenario is a PHY that
-have a dedicated PHY for PSGMII/serdes calibrarion and have a specific
-PHY in the package where the global PHY mode is set and affects every
-other PHY in the package.
-
-Change the API in the following way:
-- Change phy_package_join() to take the base addr of the PHY package
- instead of the global PHY addr.
-- Make __/phy_package_write/read() require an additional arg that
- select what global PHY address to use by passing the offset from the
- base addr passed on phy_package_join().
-
-Each user of this API is updated to follow this new implementation
-following a pattern where an enum is defined to declare the offset of the
-addr.
-
-We also drop the check if shared is defined as any user of the
-phy_package_read/write is expected to use phy_package_join first. Misuse
-of this will correctly trigger a kernel panic for NULL pointer
-exception.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/bcm54140.c | 16 ++++++--
- drivers/net/phy/mscc/mscc.h | 5 +++
- drivers/net/phy/mscc/mscc_main.c | 4 +-
- drivers/net/phy/phy_device.c | 35 +++++++++--------
- include/linux/phy.h | 64 +++++++++++++++++++++-----------
- 5 files changed, 80 insertions(+), 44 deletions(-)
-
---- a/drivers/net/phy/bcm54140.c
-+++ b/drivers/net/phy/bcm54140.c
-@@ -128,6 +128,10 @@
- #define BCM54140_DEFAULT_DOWNSHIFT 5
- #define BCM54140_MAX_DOWNSHIFT 9
-
-+enum bcm54140_global_phy {
-+ BCM54140_BASE_ADDR = 0,
-+};
-+
- struct bcm54140_priv {
- int port;
- int base_addr;
-@@ -429,11 +433,13 @@ static int bcm54140_base_read_rdb(struct
- int ret;
-
- phy_lock_mdio_bus(phydev);
-- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
-+ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
-+ MII_BCM54XX_RDB_ADDR, rdb);
- if (ret < 0)
- goto out;
-
-- ret = __phy_package_read(phydev, MII_BCM54XX_RDB_DATA);
-+ ret = __phy_package_read(phydev, BCM54140_BASE_ADDR,
-+ MII_BCM54XX_RDB_DATA);
-
- out:
- phy_unlock_mdio_bus(phydev);
-@@ -446,11 +452,13 @@ static int bcm54140_base_write_rdb(struc
- int ret;
-
- phy_lock_mdio_bus(phydev);
-- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
-+ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
-+ MII_BCM54XX_RDB_ADDR, rdb);
- if (ret < 0)
- goto out;
-
-- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_DATA, val);
-+ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
-+ MII_BCM54XX_RDB_DATA, val);
-
- out:
- phy_unlock_mdio_bus(phydev);
---- a/drivers/net/phy/mscc/mscc.h
-+++ b/drivers/net/phy/mscc/mscc.h
-@@ -416,6 +416,11 @@ struct vsc8531_private {
- * gpio_lock: used for PHC operations. Common for all PHYs as the load/save GPIO
- * is shared.
- */
-+
-+enum vsc85xx_global_phy {
-+ VSC88XX_BASE_ADDR = 0,
-+};
-+
- struct vsc85xx_shared_private {
- struct mutex gpio_lock;
- };
---- a/drivers/net/phy/mscc/mscc_main.c
-+++ b/drivers/net/phy/mscc/mscc_main.c
-@@ -711,7 +711,7 @@ int phy_base_write(struct phy_device *ph
- dump_stack();
- }
-
-- return __phy_package_write(phydev, regnum, val);
-+ return __phy_package_write(phydev, VSC88XX_BASE_ADDR, regnum, val);
- }
-
- /* phydev->bus->mdio_lock should be locked when using this function */
-@@ -722,7 +722,7 @@ int phy_base_read(struct phy_device *phy
- dump_stack();
- }
-
-- return __phy_package_read(phydev, regnum);
-+ return __phy_package_read(phydev, VSC88XX_BASE_ADDR, regnum);
- }
-
- u32 vsc85xx_csr_read(struct phy_device *phydev,
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -1658,20 +1658,22 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1
- /**
- * phy_package_join - join a common PHY group
- * @phydev: target phy_device struct
-- * @addr: cookie and PHY address for global register access
-+ * @base_addr: cookie and base PHY address of PHY package for offset
-+ * calculation of global register access
- * @priv_size: if non-zero allocate this amount of bytes for private data
- *
- * This joins a PHY group and provides a shared storage for all phydevs in
- * this group. This is intended to be used for packages which contain
- * more than one PHY, for example a quad PHY transceiver.
- *
-- * The addr parameter serves as a cookie which has to have the same value
-- * for all members of one group and as a PHY address to access generic
-- * registers of a PHY package. Usually, one of the PHY addresses of the
-- * different PHYs in the package provides access to these global registers.
-+ * The base_addr parameter serves as cookie which has to have the same values
-+ * for all members of one group and as the base PHY address of the PHY package
-+ * for offset calculation to access generic registers of a PHY package.
-+ * Usually, one of the PHY addresses of the different PHYs in the package
-+ * provides access to these global registers.
- * The address which is given here, will be used in the phy_package_read()
-- * and phy_package_write() convenience functions. If your PHY doesn't have
-- * global registers you can just pick any of the PHY addresses.
-+ * and phy_package_write() convenience functions as base and added to the
-+ * passed offset in those functions.
- *
- * This will set the shared pointer of the phydev to the shared storage.
- * If this is the first call for a this cookie the shared storage will be
-@@ -1681,17 +1683,17 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1
- * Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
- * with the same cookie but a different priv_size is an error.
- */
--int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size)
-+int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size)
- {
- struct mii_bus *bus = phydev->mdio.bus;
- struct phy_package_shared *shared;
- int ret;
-
-- if (addr < 0 || addr >= PHY_MAX_ADDR)
-+ if (base_addr < 0 || base_addr >= PHY_MAX_ADDR)
- return -EINVAL;
-
- mutex_lock(&bus->shared_lock);
-- shared = bus->shared[addr];
-+ shared = bus->shared[base_addr];
- if (!shared) {
- ret = -ENOMEM;
- shared = kzalloc(sizeof(*shared), GFP_KERNEL);
-@@ -1703,9 +1705,9 @@ int phy_package_join(struct phy_device *
- goto err_free;
- shared->priv_size = priv_size;
- }
-- shared->addr = addr;
-+ shared->base_addr = base_addr;
- refcount_set(&shared->refcnt, 1);
-- bus->shared[addr] = shared;
-+ bus->shared[base_addr] = shared;
- } else {
- ret = -EINVAL;
- if (priv_size && priv_size != shared->priv_size)
-@@ -1743,7 +1745,7 @@ void phy_package_leave(struct phy_device
- return;
-
- if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
-- bus->shared[shared->addr] = NULL;
-+ bus->shared[shared->base_addr] = NULL;
- mutex_unlock(&bus->shared_lock);
- kfree(shared->priv);
- kfree(shared);
-@@ -1762,7 +1764,8 @@ static void devm_phy_package_leave(struc
- * devm_phy_package_join - resource managed phy_package_join()
- * @dev: device that is registering this PHY package
- * @phydev: target phy_device struct
-- * @addr: cookie and PHY address for global register access
-+ * @base_addr: cookie and base PHY address of PHY package for offset
-+ * calculation of global register access
- * @priv_size: if non-zero allocate this amount of bytes for private data
- *
- * Managed phy_package_join(). Shared storage fetched by this function,
-@@ -1770,7 +1773,7 @@ static void devm_phy_package_leave(struc
- * phy_package_join() for more information.
- */
- int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
-- int addr, size_t priv_size)
-+ int base_addr, size_t priv_size)
- {
- struct phy_device **ptr;
- int ret;
-@@ -1780,7 +1783,7 @@ int devm_phy_package_join(struct device
- if (!ptr)
- return -ENOMEM;
-
-- ret = phy_package_join(phydev, addr, priv_size);
-+ ret = phy_package_join(phydev, base_addr, priv_size);
-
- if (!ret) {
- *ptr = phydev;
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -327,7 +327,8 @@ struct mdio_bus_stats {
-
- /**
- * struct phy_package_shared - Shared information in PHY packages
-- * @addr: Common PHY address used to combine PHYs in one package
-+ * @base_addr: Base PHY address of PHY package used to combine PHYs
-+ * in one package and for offset calculation of phy_package_read/write
- * @refcnt: Number of PHYs connected to this shared data
- * @flags: Initialization of PHY package
- * @priv_size: Size of the shared private data @priv
-@@ -338,7 +339,7 @@ struct mdio_bus_stats {
- * phy_package_leave().
- */
- struct phy_package_shared {
-- u8 addr;
-+ u8 base_addr;
- refcount_t refcnt;
- unsigned long flags;
- size_t priv_size;
-@@ -1969,10 +1970,10 @@ int phy_ethtool_get_link_ksettings(struc
- int phy_ethtool_set_link_ksettings(struct net_device *ndev,
- const struct ethtool_link_ksettings *cmd);
- int phy_ethtool_nway_reset(struct net_device *ndev);
--int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size);
-+int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size);
- void phy_package_leave(struct phy_device *phydev);
- int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
-- int addr, size_t priv_size);
-+ int base_addr, size_t priv_size);
-
- int __init mdio_bus_init(void);
- void mdio_bus_exit(void);
-@@ -1995,46 +1996,65 @@ int __phy_hwtstamp_set(struct phy_device
- struct kernel_hwtstamp_config *config,
- struct netlink_ext_ack *extack);
-
--static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
-+static inline int phy_package_address(struct phy_device *phydev,
-+ unsigned int addr_offset)
- {
- struct phy_package_shared *shared = phydev->shared;
-+ u8 base_addr = shared->base_addr;
-
-- if (!shared)
-+ if (addr_offset >= PHY_MAX_ADDR - base_addr)
- return -EIO;
-
-- return mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
-+ /* we know that addr will be in the range 0..31 and thus the
-+ * implicit cast to a signed int is not a problem.
-+ */
-+ return base_addr + addr_offset;
- }
-
--static inline int __phy_package_read(struct phy_device *phydev, u32 regnum)
-+static inline int phy_package_read(struct phy_device *phydev,
-+ unsigned int addr_offset, u32 regnum)
- {
-- struct phy_package_shared *shared = phydev->shared;
-+ int addr = phy_package_address(phydev, addr_offset);
-
-- if (!shared)
-- return -EIO;
-+ if (addr < 0)
-+ return addr;
-+
-+ return mdiobus_read(phydev->mdio.bus, addr, regnum);
-+}
-+
-+static inline int __phy_package_read(struct phy_device *phydev,
-+ unsigned int addr_offset, u32 regnum)
-+{
-+ int addr = phy_package_address(phydev, addr_offset);
-+
-+ if (addr < 0)
-+ return addr;
-
-- return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
-+ return __mdiobus_read(phydev->mdio.bus, addr, regnum);
- }
-
- static inline int phy_package_write(struct phy_device *phydev,
-- u32 regnum, u16 val)
-+ unsigned int addr_offset, u32 regnum,
-+ u16 val)
- {
-- struct phy_package_shared *shared = phydev->shared;
-+ int addr = phy_package_address(phydev, addr_offset);
-
-- if (!shared)
-- return -EIO;
-+ if (addr < 0)
-+ return addr;
-
-- return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
-+ return mdiobus_write(phydev->mdio.bus, addr, regnum, val);
- }
-
- static inline int __phy_package_write(struct phy_device *phydev,
-- u32 regnum, u16 val)
-+ unsigned int addr_offset, u32 regnum,
-+ u16 val)
- {
-- struct phy_package_shared *shared = phydev->shared;
-+ int addr = phy_package_address(phydev, addr_offset);
-
-- if (!shared)
-- return -EIO;
-+ if (addr < 0)
-+ return addr;
-
-- return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
-+ return __mdiobus_write(phydev->mdio.bus, addr, regnum, val);
- }
-
- static inline bool __phy_package_set_once(struct phy_device *phydev,
+++ /dev/null
-From 028672bd1d73cf65249a420c1de75e8d2acd2f6a Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 15 Dec 2023 14:15:33 +0100
-Subject: [PATCH 3/4] net: phy: restructure __phy_write/read_mmd to helper and
- phydev user
-
-Restructure phy_write_mmd and phy_read_mmd to implement generic helper
-for direct mdiobus access for mmd and use these helper for phydev user.
-
-This is needed in preparation of PHY package API that requires generic
-access to the mdiobus and are deatched from phydev struct but instead
-access them based on PHY package base_addr and offsets.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phy-core.c | 64 ++++++++++++++++++--------------------
- 1 file changed, 30 insertions(+), 34 deletions(-)
-
---- a/drivers/net/phy/phy-core.c
-+++ b/drivers/net/phy/phy-core.c
-@@ -540,6 +540,28 @@ static void mmd_phy_indirect(struct mii_
- devad | MII_MMD_CTRL_NOINCR);
- }
-
-+static int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45,
-+ int devad, u32 regnum)
-+{
-+ if (is_c45)
-+ return __mdiobus_c45_read(bus, phy_addr, devad, regnum);
-+
-+ mmd_phy_indirect(bus, phy_addr, devad, regnum);
-+ /* Read the content of the MMD's selected register */
-+ return __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
-+}
-+
-+static int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45,
-+ int devad, u32 regnum, u16 val)
-+{
-+ if (is_c45)
-+ return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val);
-+
-+ mmd_phy_indirect(bus, phy_addr, devad, regnum);
-+ /* Write the data into MMD's selected register */
-+ return __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
-+}
-+
- /**
- * __phy_read_mmd - Convenience function for reading a register
- * from an MMD on a given PHY.
-@@ -551,26 +573,14 @@ static void mmd_phy_indirect(struct mii_
- */
- int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
- {
-- int val;
--
- if (regnum > (u16)~0 || devad > 32)
- return -EINVAL;
-
-- if (phydev->drv && phydev->drv->read_mmd) {
-- val = phydev->drv->read_mmd(phydev, devad, regnum);
-- } else if (phydev->is_c45) {
-- val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
-- devad, regnum);
-- } else {
-- struct mii_bus *bus = phydev->mdio.bus;
-- int phy_addr = phydev->mdio.addr;
--
-- mmd_phy_indirect(bus, phy_addr, devad, regnum);
--
-- /* Read the content of the MMD's selected register */
-- val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
-- }
-- return val;
-+ if (phydev->drv && phydev->drv->read_mmd)
-+ return phydev->drv->read_mmd(phydev, devad, regnum);
-+
-+ return mmd_phy_read(phydev->mdio.bus, phydev->mdio.addr,
-+ phydev->is_c45, devad, regnum);
- }
- EXPORT_SYMBOL(__phy_read_mmd);
-
-@@ -607,28 +617,14 @@ EXPORT_SYMBOL(phy_read_mmd);
- */
- int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
- {
-- int ret;
--
- if (regnum > (u16)~0 || devad > 32)
- return -EINVAL;
-
-- if (phydev->drv && phydev->drv->write_mmd) {
-- ret = phydev->drv->write_mmd(phydev, devad, regnum, val);
-- } else if (phydev->is_c45) {
-- ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr,
-- devad, regnum, val);
-- } else {
-- struct mii_bus *bus = phydev->mdio.bus;
-- int phy_addr = phydev->mdio.addr;
-+ if (phydev->drv && phydev->drv->write_mmd)
-+ return phydev->drv->write_mmd(phydev, devad, regnum, val);
-
-- mmd_phy_indirect(bus, phy_addr, devad, regnum);
--
-- /* Write the data into MMD's selected register */
-- __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
--
-- ret = 0;
-- }
-- return ret;
-+ return mmd_phy_write(phydev->mdio.bus, phydev->mdio.addr,
-+ phydev->is_c45, devad, regnum, val);
- }
- EXPORT_SYMBOL(__phy_write_mmd);
-
+++ /dev/null
-From d63710fc0f1a501fd75a7025e3070a96ffa1645f Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 15 Dec 2023 14:15:34 +0100
-Subject: [PATCH 4/4] net: phy: add support for PHY package MMD read/write
-
-Some PHY in PHY package may require to read/write MMD regs to correctly
-configure the PHY package.
-
-Add support for these additional required function in both lock and no
-lock variant.
-
-It's assumed that the entire PHY package is either C22 or C45. We use
-C22 or C45 way of writing/reading to mmd regs based on the passed phydev
-whether it's C22 or C45.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phy-core.c | 140 +++++++++++++++++++++++++++++++++++++
- include/linux/phy.h | 16 +++++
- 2 files changed, 156 insertions(+)
-
---- a/drivers/net/phy/phy-core.c
-+++ b/drivers/net/phy/phy-core.c
-@@ -651,6 +651,146 @@ int phy_write_mmd(struct phy_device *phy
- EXPORT_SYMBOL(phy_write_mmd);
-
- /**
-+ * __phy_package_read_mmd - read MMD reg relative to PHY package base addr
-+ * @phydev: The phy_device struct
-+ * @addr_offset: The offset to be added to PHY package base_addr
-+ * @devad: The MMD to read from
-+ * @regnum: The register on the MMD to read
-+ *
-+ * Convenience helper for reading a register of an MMD on a given PHY
-+ * using the PHY package base address. The base address is added to
-+ * the addr_offset value.
-+ *
-+ * Same calling rules as for __phy_read();
-+ *
-+ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
-+ */
-+int __phy_package_read_mmd(struct phy_device *phydev,
-+ unsigned int addr_offset, int devad,
-+ u32 regnum)
-+{
-+ int addr = phy_package_address(phydev, addr_offset);
-+
-+ if (addr < 0)
-+ return addr;
-+
-+ if (regnum > (u16)~0 || devad > 32)
-+ return -EINVAL;
-+
-+ return mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad,
-+ regnum);
-+}
-+EXPORT_SYMBOL(__phy_package_read_mmd);
-+
-+/**
-+ * phy_package_read_mmd - read MMD reg relative to PHY package base addr
-+ * @phydev: The phy_device struct
-+ * @addr_offset: The offset to be added to PHY package base_addr
-+ * @devad: The MMD to read from
-+ * @regnum: The register on the MMD to read
-+ *
-+ * Convenience helper for reading a register of an MMD on a given PHY
-+ * using the PHY package base address. The base address is added to
-+ * the addr_offset value.
-+ *
-+ * Same calling rules as for phy_read();
-+ *
-+ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
-+ */
-+int phy_package_read_mmd(struct phy_device *phydev,
-+ unsigned int addr_offset, int devad,
-+ u32 regnum)
-+{
-+ int addr = phy_package_address(phydev, addr_offset);
-+ int val;
-+
-+ if (addr < 0)
-+ return addr;
-+
-+ if (regnum > (u16)~0 || devad > 32)
-+ return -EINVAL;
-+
-+ phy_lock_mdio_bus(phydev);
-+ val = mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad,
-+ regnum);
-+ phy_unlock_mdio_bus(phydev);
-+
-+ return val;
-+}
-+EXPORT_SYMBOL(phy_package_read_mmd);
-+
-+/**
-+ * __phy_package_write_mmd - write MMD reg relative to PHY package base addr
-+ * @phydev: The phy_device struct
-+ * @addr_offset: The offset to be added to PHY package base_addr
-+ * @devad: The MMD to write to
-+ * @regnum: The register on the MMD to write
-+ * @val: value to write to @regnum
-+ *
-+ * Convenience helper for writing a register of an MMD on a given PHY
-+ * using the PHY package base address. The base address is added to
-+ * the addr_offset value.
-+ *
-+ * Same calling rules as for __phy_write();
-+ *
-+ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
-+ */
-+int __phy_package_write_mmd(struct phy_device *phydev,
-+ unsigned int addr_offset, int devad,
-+ u32 regnum, u16 val)
-+{
-+ int addr = phy_package_address(phydev, addr_offset);
-+
-+ if (addr < 0)
-+ return addr;
-+
-+ if (regnum > (u16)~0 || devad > 32)
-+ return -EINVAL;
-+
-+ return mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad,
-+ regnum, val);
-+}
-+EXPORT_SYMBOL(__phy_package_write_mmd);
-+
-+/**
-+ * phy_package_write_mmd - write MMD reg relative to PHY package base addr
-+ * @phydev: The phy_device struct
-+ * @addr_offset: The offset to be added to PHY package base_addr
-+ * @devad: The MMD to write to
-+ * @regnum: The register on the MMD to write
-+ * @val: value to write to @regnum
-+ *
-+ * Convenience helper for writing a register of an MMD on a given PHY
-+ * using the PHY package base address. The base address is added to
-+ * the addr_offset value.
-+ *
-+ * Same calling rules as for phy_write();
-+ *
-+ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
-+ */
-+int phy_package_write_mmd(struct phy_device *phydev,
-+ unsigned int addr_offset, int devad,
-+ u32 regnum, u16 val)
-+{
-+ int addr = phy_package_address(phydev, addr_offset);
-+ int ret;
-+
-+ if (addr < 0)
-+ return addr;
-+
-+ if (regnum > (u16)~0 || devad > 32)
-+ return -EINVAL;
-+
-+ phy_lock_mdio_bus(phydev);
-+ ret = mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad,
-+ regnum, val);
-+ phy_unlock_mdio_bus(phydev);
-+
-+ return ret;
-+}
-+EXPORT_SYMBOL(phy_package_write_mmd);
-+
-+/**
- * phy_modify_changed - Function for modifying a PHY register
- * @phydev: the phy_device struct
- * @regnum: register number to modify
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -2057,6 +2057,22 @@ static inline int __phy_package_write(st
- return __mdiobus_write(phydev->mdio.bus, addr, regnum, val);
- }
-
-+int __phy_package_read_mmd(struct phy_device *phydev,
-+ unsigned int addr_offset, int devad,
-+ u32 regnum);
-+
-+int phy_package_read_mmd(struct phy_device *phydev,
-+ unsigned int addr_offset, int devad,
-+ u32 regnum);
-+
-+int __phy_package_write_mmd(struct phy_device *phydev,
-+ unsigned int addr_offset, int devad,
-+ u32 regnum, u16 val);
-+
-+int phy_package_write_mmd(struct phy_device *phydev,
-+ unsigned int addr_offset, int devad,
-+ u32 regnum, u16 val);
-+
- static inline bool __phy_package_set_once(struct phy_device *phydev,
- unsigned int b)
- {
+++ /dev/null
-From f2ec98566775dd4341ec1dcf93aa5859c60de826 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 1 Feb 2024 14:46:00 +0100
-Subject: [PATCH 1/2] net: phy: qcom: qca808x: fix logic error in LED
- brightness set
-
-In switching to using phy_modify_mmd and a more short version of the
-LED ON/OFF condition in later revision, it was made a logic error where
-
-value ? QCA808X_LED_FORCE_ON : QCA808X_LED_FORCE_OFF is always true as
-value is always OR with QCA808X_LED_FORCE_EN due to missing ()
-resulting in the testing condition being QCA808X_LED_FORCE_EN | value.
-
-Add the () to apply the correct condition and restore correct
-functionality of the brightness ON/OFF.
-
-Fixes: 7196062b64ee ("net: phy: at803x: add LED support for qca808x")
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/qcom/qca808x.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/net/phy/qcom/qca808x.c
-+++ b/drivers/net/phy/qcom/qca808x.c
-@@ -820,8 +820,8 @@ static int qca808x_led_brightness_set(st
-
- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-- QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
-- QCA808X_LED_FORCE_OFF);
-+ QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
-+ QCA808X_LED_FORCE_OFF));
- }
-
- static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
+++ /dev/null
-From f203c8c77c7616c099647636f4c67d59a45fe8a2 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 1 Feb 2024 14:46:01 +0100
-Subject: [PATCH 2/2] net: phy: qcom: qca808x: default to LED active High if
- not set
-
-qca808x PHY provide support for the led_polarity_set OP to configure
-and apply the active-low property but on PHY reset, the Active High bit
-is not set resulting in the LED driven as active-low.
-
-To fix this, check if active-low is not set in DT and enable Active High
-polarity by default to restore correct funcionality of the LED.
-
-Fixes: 7196062b64ee ("net: phy: at803x: add LED support for qca808x")
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/qcom/qca808x.c | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
---- a/drivers/net/phy/qcom/qca808x.c
-+++ b/drivers/net/phy/qcom/qca808x.c
-@@ -290,8 +290,18 @@ static int qca808x_probe(struct phy_devi
-
- static int qca808x_config_init(struct phy_device *phydev)
- {
-+ struct qca808x_priv *priv = phydev->priv;
- int ret;
-
-+ /* Default to LED Active High if active-low not in DT */
-+ if (priv->led_polarity_mode == -1) {
-+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN,
-+ QCA808X_MMD7_LED_POLARITY_CTRL,
-+ QCA808X_LED_ACTIVE_HIGH);
-+ if (ret)
-+ return ret;
-+ }
-+
- /* Active adc&vga on 802.3az for the link 1000M and 100M */
- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
+++ /dev/null
-From 385ef48f468696d6d172eb367656a3466fa0408d Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 6 Feb 2024 18:31:05 +0100
-Subject: [PATCH 02/10] net: phy: add support for scanning PHY in PHY packages
- nodes
-
-Add support for scanning PHY in PHY package nodes. PHY packages nodes
-are just container for actual PHY on the MDIO bus.
-
-Their PHY address defined in the PHY package node are absolute and
-reflect the address on the MDIO bus.
-
-mdio_bus.c and of_mdio.c is updated to now support and parse also
-PHY package subnode by checking if the node name match
-"ethernet-phy-package".
-
-As PHY package reg is mandatory and each PHY in the PHY package must
-have a reg, every invalid PHY Package node is ignored and will be
-skipped by the autoscan fallback.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/mdio/of_mdio.c | 79 +++++++++++++++++++++++++++-----------
- drivers/net/phy/mdio_bus.c | 44 +++++++++++++++++----
- 2 files changed, 92 insertions(+), 31 deletions(-)
-
---- a/drivers/net/mdio/of_mdio.c
-+++ b/drivers/net/mdio/of_mdio.c
-@@ -138,6 +138,53 @@ bool of_mdiobus_child_is_phy(struct devi
- }
- EXPORT_SYMBOL(of_mdiobus_child_is_phy);
-
-+static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np,
-+ bool *scanphys)
-+{
-+ struct device_node *child;
-+ int addr, rc = 0;
-+
-+ /* Loop over the child nodes and register a phy_device for each phy */
-+ for_each_available_child_of_node(np, child) {
-+ if (of_node_name_eq(child, "ethernet-phy-package")) {
-+ /* Ignore invalid ethernet-phy-package node */
-+ if (!of_find_property(child, "reg", NULL))
-+ continue;
-+
-+ rc = __of_mdiobus_parse_phys(mdio, child, NULL);
-+ if (rc && rc != -ENODEV)
-+ goto exit;
-+
-+ continue;
-+ }
-+
-+ addr = of_mdio_parse_addr(&mdio->dev, child);
-+ if (addr < 0) {
-+ /* Skip scanning for invalid ethernet-phy-package node */
-+ if (scanphys)
-+ *scanphys = true;
-+ continue;
-+ }
-+
-+ if (of_mdiobus_child_is_phy(child))
-+ rc = of_mdiobus_register_phy(mdio, child, addr);
-+ else
-+ rc = of_mdiobus_register_device(mdio, child, addr);
-+
-+ if (rc == -ENODEV)
-+ dev_err(&mdio->dev,
-+ "MDIO device at address %d is missing.\n",
-+ addr);
-+ else if (rc)
-+ goto exit;
-+ }
-+
-+ return 0;
-+exit:
-+ of_node_put(child);
-+ return rc;
-+}
-+
- /**
- * __of_mdiobus_register - Register mii_bus and create PHYs from the device tree
- * @mdio: pointer to mii_bus structure
-@@ -179,33 +226,18 @@ int __of_mdiobus_register(struct mii_bus
- return rc;
-
- /* Loop over the child nodes and register a phy_device for each phy */
-- for_each_available_child_of_node(np, child) {
-- addr = of_mdio_parse_addr(&mdio->dev, child);
-- if (addr < 0) {
-- scanphys = true;
-- continue;
-- }
--
-- if (of_mdiobus_child_is_phy(child))
-- rc = of_mdiobus_register_phy(mdio, child, addr);
-- else
-- rc = of_mdiobus_register_device(mdio, child, addr);
--
-- if (rc == -ENODEV)
-- dev_err(&mdio->dev,
-- "MDIO device at address %d is missing.\n",
-- addr);
-- else if (rc)
-- goto unregister;
-- }
-+ rc = __of_mdiobus_parse_phys(mdio, np, &scanphys);
-+ if (rc)
-+ goto unregister;
-
- if (!scanphys)
- return 0;
-
- /* auto scan for PHYs with empty reg property */
- for_each_available_child_of_node(np, child) {
-- /* Skip PHYs with reg property set */
-- if (of_property_present(child, "reg"))
-+ /* Skip PHYs with reg property set or ethernet-phy-package node */
-+ if (of_property_present(child, "reg") ||
-+ of_node_name_eq(child, "ethernet-phy-package"))
- continue;
-
- for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
-@@ -226,15 +258,16 @@ int __of_mdiobus_register(struct mii_bus
- if (!rc)
- break;
- if (rc != -ENODEV)
-- goto unregister;
-+ goto put_unregister;
- }
- }
- }
-
- return 0;
-
--unregister:
-+put_unregister:
- of_node_put(child);
-+unregister:
- mdiobus_unregister(mdio);
- return rc;
- }
---- a/drivers/net/phy/mdio_bus.c
-+++ b/drivers/net/phy/mdio_bus.c
-@@ -455,19 +455,34 @@ EXPORT_SYMBOL(of_mdio_find_bus);
- * found, set the of_node pointer for the mdio device. This allows
- * auto-probed phy devices to be supplied with information passed in
- * via DT.
-+ * If a PHY package is found, PHY is searched also there.
- */
--static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
-- struct mdio_device *mdiodev)
-+static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev,
-+ struct device_node *np)
- {
-- struct device *dev = &mdiodev->dev;
- struct device_node *child;
-
-- if (dev->of_node || !bus->dev.of_node)
-- return;
--
-- for_each_available_child_of_node(bus->dev.of_node, child) {
-+ for_each_available_child_of_node(np, child) {
- int addr;
-
-+ if (of_node_name_eq(child, "ethernet-phy-package")) {
-+ /* Validate PHY package reg presence */
-+ if (!of_find_property(child, "reg", NULL)) {
-+ of_node_put(child);
-+ return -EINVAL;
-+ }
-+
-+ if (!of_mdiobus_find_phy(dev, mdiodev, child)) {
-+ /* The refcount for the PHY package will be
-+ * incremented later when PHY join the Package.
-+ */
-+ of_node_put(child);
-+ return 0;
-+ }
-+
-+ continue;
-+ }
-+
- addr = of_mdio_parse_addr(dev, child);
- if (addr < 0)
- continue;
-@@ -477,9 +492,22 @@ static void of_mdiobus_link_mdiodev(stru
- /* The refcount on "child" is passed to the mdio
- * device. Do _not_ use of_node_put(child) here.
- */
-- return;
-+ return 0;
- }
- }
-+
-+ return -ENODEV;
-+}
-+
-+static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
-+ struct mdio_device *mdiodev)
-+{
-+ struct device *dev = &mdiodev->dev;
-+
-+ if (dev->of_node || !bus->dev.of_node)
-+ return;
-+
-+ of_mdiobus_find_phy(dev, mdiodev, bus->dev.of_node);
- }
- #else /* !IS_ENABLED(CONFIG_OF_MDIO) */
- static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio,
+++ /dev/null
-From 471e8fd3afcef5a9f9089f0bd21965ad9ba35c91 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 6 Feb 2024 18:31:06 +0100
-Subject: [PATCH 03/10] net: phy: add devm/of_phy_package_join helper
-
-Add devm/of_phy_package_join helper to join PHYs in a PHY package. These
-are variant of the manual phy_package_join with the difference that
-these will use DT nodes to derive the base_addr instead of manually
-passing an hardcoded value.
-
-An additional value is added in phy_package_shared, "np" to reference
-the PHY package node pointer in specific PHY driver probe_once and
-config_init_once functions to make use of additional specific properties
-defined in the PHY package node in DT.
-
-The np value is filled only with of_phy_package_join if a valid PHY
-package node is found. A valid PHY package node must have the node name
-set to "ethernet-phy-package".
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phy_device.c | 96 ++++++++++++++++++++++++++++++++++++
- include/linux/phy.h | 6 +++
- 2 files changed, 102 insertions(+)
-
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -1706,6 +1706,7 @@ int phy_package_join(struct phy_device *
- shared->priv_size = priv_size;
- }
- shared->base_addr = base_addr;
-+ shared->np = NULL;
- refcount_set(&shared->refcnt, 1);
- bus->shared[base_addr] = shared;
- } else {
-@@ -1729,6 +1730,63 @@ err_unlock:
- EXPORT_SYMBOL_GPL(phy_package_join);
-
- /**
-+ * of_phy_package_join - join a common PHY group in PHY package
-+ * @phydev: target phy_device struct
-+ * @priv_size: if non-zero allocate this amount of bytes for private data
-+ *
-+ * This is a variant of phy_package_join for PHY package defined in DT.
-+ *
-+ * The parent node of the @phydev is checked as a valid PHY package node
-+ * structure (by matching the node name "ethernet-phy-package") and the
-+ * base_addr for the PHY package is passed to phy_package_join.
-+ *
-+ * With this configuration the shared struct will also have the np value
-+ * filled to use additional DT defined properties in PHY specific
-+ * probe_once and config_init_once PHY package OPs.
-+ *
-+ * Returns < 0 on error, 0 on success. Esp. calling phy_package_join()
-+ * with the same cookie but a different priv_size is an error. Or a parent
-+ * node is not detected or is not valid or doesn't match the expected node
-+ * name for PHY package.
-+ */
-+int of_phy_package_join(struct phy_device *phydev, size_t priv_size)
-+{
-+ struct device_node *node = phydev->mdio.dev.of_node;
-+ struct device_node *package_node;
-+ u32 base_addr;
-+ int ret;
-+
-+ if (!node)
-+ return -EINVAL;
-+
-+ package_node = of_get_parent(node);
-+ if (!package_node)
-+ return -EINVAL;
-+
-+ if (!of_node_name_eq(package_node, "ethernet-phy-package")) {
-+ ret = -EINVAL;
-+ goto exit;
-+ }
-+
-+ if (of_property_read_u32(package_node, "reg", &base_addr)) {
-+ ret = -EINVAL;
-+ goto exit;
-+ }
-+
-+ ret = phy_package_join(phydev, base_addr, priv_size);
-+ if (ret)
-+ goto exit;
-+
-+ phydev->shared->np = package_node;
-+
-+ return 0;
-+exit:
-+ of_node_put(package_node);
-+ return ret;
-+}
-+EXPORT_SYMBOL_GPL(of_phy_package_join);
-+
-+/**
- * phy_package_leave - leave a common PHY group
- * @phydev: target phy_device struct
- *
-@@ -1744,6 +1802,10 @@ void phy_package_leave(struct phy_device
- if (!shared)
- return;
-
-+ /* Decrease the node refcount on leave if present */
-+ if (shared->np)
-+ of_node_put(shared->np);
-+
- if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
- bus->shared[shared->base_addr] = NULL;
- mutex_unlock(&bus->shared_lock);
-@@ -1797,6 +1859,40 @@ int devm_phy_package_join(struct device
- EXPORT_SYMBOL_GPL(devm_phy_package_join);
-
- /**
-+ * devm_of_phy_package_join - resource managed of_phy_package_join()
-+ * @dev: device that is registering this PHY package
-+ * @phydev: target phy_device struct
-+ * @priv_size: if non-zero allocate this amount of bytes for private data
-+ *
-+ * Managed of_phy_package_join(). Shared storage fetched by this function,
-+ * phy_package_leave() is automatically called on driver detach. See
-+ * of_phy_package_join() for more information.
-+ */
-+int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
-+ size_t priv_size)
-+{
-+ struct phy_device **ptr;
-+ int ret;
-+
-+ ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
-+ GFP_KERNEL);
-+ if (!ptr)
-+ return -ENOMEM;
-+
-+ ret = of_phy_package_join(phydev, priv_size);
-+
-+ if (!ret) {
-+ *ptr = phydev;
-+ devres_add(dev, ptr);
-+ } else {
-+ devres_free(ptr);
-+ }
-+
-+ return ret;
-+}
-+EXPORT_SYMBOL_GPL(devm_of_phy_package_join);
-+
-+/**
- * phy_detach - detach a PHY device from its network device
- * @phydev: target phy_device struct
- *
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -329,6 +329,7 @@ struct mdio_bus_stats {
- * struct phy_package_shared - Shared information in PHY packages
- * @base_addr: Base PHY address of PHY package used to combine PHYs
- * in one package and for offset calculation of phy_package_read/write
-+ * @np: Pointer to the Device Node if PHY package defined in DT
- * @refcnt: Number of PHYs connected to this shared data
- * @flags: Initialization of PHY package
- * @priv_size: Size of the shared private data @priv
-@@ -340,6 +341,8 @@ struct mdio_bus_stats {
- */
- struct phy_package_shared {
- u8 base_addr;
-+ /* With PHY package defined in DT this points to the PHY package node */
-+ struct device_node *np;
- refcount_t refcnt;
- unsigned long flags;
- size_t priv_size;
-@@ -1971,9 +1974,12 @@ int phy_ethtool_set_link_ksettings(struc
- const struct ethtool_link_ksettings *cmd);
- int phy_ethtool_nway_reset(struct net_device *ndev);
- int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size);
-+int of_phy_package_join(struct phy_device *phydev, size_t priv_size);
- void phy_package_leave(struct phy_device *phydev);
- int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
- int base_addr, size_t priv_size);
-+int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
-+ size_t priv_size);
-
- int __init mdio_bus_init(void);
- void mdio_bus_exit(void);
+++ /dev/null
-From 737eb75a815f9c08dcbb6631db57f4f4b0540a5b Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 6 Feb 2024 18:31:07 +0100
-Subject: [PATCH 04/10] net: phy: qcom: move more function to shared library
-
-Move more function to shared library in preparation for introduction of
-new PHY Family qca807x that will make use of both functions from at803x
-and qca808x as it's a transition PHY with some implementation of at803x
-and some from the new qca808x.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/qcom/at803x.c | 35 -----
- drivers/net/phy/qcom/qca808x.c | 205 ----------------------------
- drivers/net/phy/qcom/qcom-phy-lib.c | 193 ++++++++++++++++++++++++++
- drivers/net/phy/qcom/qcom.h | 51 +++++++
- 4 files changed, 244 insertions(+), 240 deletions(-)
-
---- a/drivers/net/phy/qcom/at803x.c
-+++ b/drivers/net/phy/qcom/at803x.c
-@@ -504,41 +504,6 @@ static void at803x_link_change_notify(st
- }
- }
-
--static int at803x_read_status(struct phy_device *phydev)
--{
-- struct at803x_ss_mask ss_mask = { 0 };
-- int err, old_link = phydev->link;
--
-- /* Update the link, but return if there was an error */
-- err = genphy_update_link(phydev);
-- if (err)
-- return err;
--
-- /* why bother the PHY if nothing can have changed */
-- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
-- return 0;
--
-- phydev->speed = SPEED_UNKNOWN;
-- phydev->duplex = DUPLEX_UNKNOWN;
-- phydev->pause = 0;
-- phydev->asym_pause = 0;
--
-- err = genphy_read_lpa(phydev);
-- if (err < 0)
-- return err;
--
-- ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
-- ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
-- err = at803x_read_specific_status(phydev, ss_mask);
-- if (err < 0)
-- return err;
--
-- if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
-- phy_resolve_aneg_pause(phydev);
--
-- return 0;
--}
--
- static int at803x_config_aneg(struct phy_device *phydev)
- {
- struct at803x_priv *priv = phydev->priv;
---- a/drivers/net/phy/qcom/qca808x.c
-+++ b/drivers/net/phy/qcom/qca808x.c
-@@ -2,7 +2,6 @@
-
- #include <linux/phy.h>
- #include <linux/module.h>
--#include <linux/ethtool_netlink.h>
-
- #include "qcom.h"
-
-@@ -63,55 +62,6 @@
- #define QCA808X_DBG_AN_TEST 0xb
- #define QCA808X_HIBERNATION_EN BIT(15)
-
--#define QCA808X_CDT_ENABLE_TEST BIT(15)
--#define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
--#define QCA808X_CDT_STATUS BIT(11)
--#define QCA808X_CDT_LENGTH_UNIT BIT(10)
--
--#define QCA808X_MMD3_CDT_STATUS 0x8064
--#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065
--#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
--#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
--#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
--#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
--#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
--
--#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
--#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
--#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
--#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
--
--#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
--#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
--#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
--#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
--#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
--
--#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
--#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
--#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
--#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
--
--/* NORMAL are MDI with type set to 0 */
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-- QCA808X_CDT_STATUS_STAT_MDI1)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-- QCA808X_CDT_STATUS_STAT_MDI1)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-- QCA808X_CDT_STATUS_STAT_MDI2)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-- QCA808X_CDT_STATUS_STAT_MDI2)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-- QCA808X_CDT_STATUS_STAT_MDI3)
--#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-- QCA808X_CDT_STATUS_STAT_MDI3)
--
--/* Added for reference of existence but should be handled by wait_for_completion already */
--#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
--
- #define QCA808X_MMD7_LED_GLOBAL 0x8073
- #define QCA808X_LED_BLINK_1 GENMASK(11, 6)
- #define QCA808X_LED_BLINK_2 GENMASK(5, 0)
-@@ -406,86 +356,6 @@ static int qca808x_soft_reset(struct phy
- return ret;
- }
-
--static bool qca808x_cdt_fault_length_valid(int cdt_code)
--{
-- switch (cdt_code) {
-- case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-- return true;
-- default:
-- return false;
-- }
--}
--
--static int qca808x_cable_test_result_trans(int cdt_code)
--{
-- switch (cdt_code) {
-- case QCA808X_CDT_STATUS_STAT_NORMAL:
-- return ETHTOOL_A_CABLE_RESULT_CODE_OK;
-- case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
-- case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-- return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
-- case QCA808X_CDT_STATUS_STAT_FAIL:
-- default:
-- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
-- }
--}
--
--static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
-- int result)
--{
-- int val;
-- u32 cdt_length_reg = 0;
--
-- switch (pair) {
-- case ETHTOOL_A_CABLE_PAIR_A:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
-- break;
-- case ETHTOOL_A_CABLE_PAIR_B:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
-- break;
-- case ETHTOOL_A_CABLE_PAIR_C:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
-- break;
-- case ETHTOOL_A_CABLE_PAIR_D:
-- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
-- break;
-- default:
-- return -EINVAL;
-- }
--
-- val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
-- if (val < 0)
-- return val;
--
-- if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
-- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
-- else
-- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
--
-- return at803x_cdt_fault_length(val);
--}
--
- static int qca808x_cable_test_start(struct phy_device *phydev)
- {
- int ret;
-@@ -526,81 +396,6 @@ static int qca808x_cable_test_start(stru
-
- return 0;
- }
--
--static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
-- u16 status)
--{
-- int length, result;
-- u16 pair_code;
--
-- switch (pair) {
-- case ETHTOOL_A_CABLE_PAIR_A:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
-- break;
-- case ETHTOOL_A_CABLE_PAIR_B:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
-- break;
-- case ETHTOOL_A_CABLE_PAIR_C:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
-- break;
-- case ETHTOOL_A_CABLE_PAIR_D:
-- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
-- break;
-- default:
-- return -EINVAL;
-- }
--
-- result = qca808x_cable_test_result_trans(pair_code);
-- ethnl_cable_test_result(phydev, pair, result);
--
-- if (qca808x_cdt_fault_length_valid(pair_code)) {
-- length = qca808x_cdt_fault_length(phydev, pair, result);
-- ethnl_cable_test_fault_length(phydev, pair, length);
-- }
--
-- return 0;
--}
--
--static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
--{
-- int ret, val;
--
-- *finished = false;
--
-- val = QCA808X_CDT_ENABLE_TEST |
-- QCA808X_CDT_LENGTH_UNIT;
-- ret = at803x_cdt_start(phydev, val);
-- if (ret)
-- return ret;
--
-- ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
-- if (ret)
-- return ret;
--
-- val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
-- if (val < 0)
-- return val;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
-- if (ret)
-- return ret;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
-- if (ret)
-- return ret;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
-- if (ret)
-- return ret;
--
-- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
-- if (ret)
-- return ret;
--
-- *finished = true;
--
-- return 0;
--}
-
- static int qca808x_get_features(struct phy_device *phydev)
- {
---- a/drivers/net/phy/qcom/qcom-phy-lib.c
-+++ b/drivers/net/phy/qcom/qcom-phy-lib.c
-@@ -5,6 +5,7 @@
-
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
-+#include <linux/ethtool_netlink.h>
-
- #include "qcom.h"
-
-@@ -311,6 +312,42 @@ int at803x_prepare_config_aneg(struct ph
- }
- EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);
-
-+int at803x_read_status(struct phy_device *phydev)
-+{
-+ struct at803x_ss_mask ss_mask = { 0 };
-+ int err, old_link = phydev->link;
-+
-+ /* Update the link, but return if there was an error */
-+ err = genphy_update_link(phydev);
-+ if (err)
-+ return err;
-+
-+ /* why bother the PHY if nothing can have changed */
-+ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
-+ return 0;
-+
-+ phydev->speed = SPEED_UNKNOWN;
-+ phydev->duplex = DUPLEX_UNKNOWN;
-+ phydev->pause = 0;
-+ phydev->asym_pause = 0;
-+
-+ err = genphy_read_lpa(phydev);
-+ if (err < 0)
-+ return err;
-+
-+ ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
-+ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
-+ err = at803x_read_specific_status(phydev, ss_mask);
-+ if (err < 0)
-+ return err;
-+
-+ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
-+ phy_resolve_aneg_pause(phydev);
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(at803x_read_status);
-+
- static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
- {
- int val;
-@@ -427,3 +464,159 @@ int at803x_cdt_wait_for_completion(struc
- return ret < 0 ? ret : 0;
- }
- EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);
-+
-+static bool qca808x_cdt_fault_length_valid(int cdt_code)
-+{
-+ switch (cdt_code) {
-+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-+ return true;
-+ default:
-+ return false;
-+ }
-+}
-+
-+static int qca808x_cable_test_result_trans(int cdt_code)
-+{
-+ switch (cdt_code) {
-+ case QCA808X_CDT_STATUS_STAT_NORMAL:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
-+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
-+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
-+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
-+ case QCA808X_CDT_STATUS_STAT_FAIL:
-+ default:
-+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
-+ }
-+}
-+
-+static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
-+ int result)
-+{
-+ int val;
-+ u32 cdt_length_reg = 0;
-+
-+ switch (pair) {
-+ case ETHTOOL_A_CABLE_PAIR_A:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_B:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_C:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_D:
-+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
-+ if (val < 0)
-+ return val;
-+
-+ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
-+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
-+ else
-+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
-+
-+ return at803x_cdt_fault_length(val);
-+}
-+
-+static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
-+ u16 status)
-+{
-+ int length, result;
-+ u16 pair_code;
-+
-+ switch (pair) {
-+ case ETHTOOL_A_CABLE_PAIR_A:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_B:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_C:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
-+ break;
-+ case ETHTOOL_A_CABLE_PAIR_D:
-+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ result = qca808x_cable_test_result_trans(pair_code);
-+ ethnl_cable_test_result(phydev, pair, result);
-+
-+ if (qca808x_cdt_fault_length_valid(pair_code)) {
-+ length = qca808x_cdt_fault_length(phydev, pair, result);
-+ ethnl_cable_test_fault_length(phydev, pair, length);
-+ }
-+
-+ return 0;
-+}
-+
-+int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
-+{
-+ int ret, val;
-+
-+ *finished = false;
-+
-+ val = QCA808X_CDT_ENABLE_TEST |
-+ QCA808X_CDT_LENGTH_UNIT;
-+ ret = at803x_cdt_start(phydev, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
-+ if (ret)
-+ return ret;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
-+ if (val < 0)
-+ return val;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
-+ if (ret)
-+ return ret;
-+
-+ *finished = true;
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
---- a/drivers/net/phy/qcom/qcom.h
-+++ b/drivers/net/phy/qcom/qcom.h
-@@ -54,6 +54,55 @@
- #define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8)
- #define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0)
-
-+#define QCA808X_CDT_ENABLE_TEST BIT(15)
-+#define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
-+#define QCA808X_CDT_STATUS BIT(11)
-+#define QCA808X_CDT_LENGTH_UNIT BIT(10)
-+
-+#define QCA808X_MMD3_CDT_STATUS 0x8064
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
-+#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
-+#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
-+#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
-+
-+#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
-+#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
-+#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
-+#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
-+
-+#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
-+#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
-+#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
-+#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
-+#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
-+
-+#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
-+#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
-+#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
-+#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
-+
-+/* NORMAL are MDI with type set to 0 */
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI1)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI1)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI2)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI2)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
-+ QCA808X_CDT_STATUS_STAT_MDI3)
-+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
-+ QCA808X_CDT_STATUS_STAT_MDI3)
-+
-+/* Added for reference of existence but should be handled by wait_for_completion already */
-+#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
-+
- #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
- #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B
- #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
-@@ -110,6 +159,7 @@ int at803x_read_specific_status(struct p
- struct at803x_ss_mask ss_mask);
- int at803x_config_mdix(struct phy_device *phydev, u8 ctrl);
- int at803x_prepare_config_aneg(struct phy_device *phydev);
-+int at803x_read_status(struct phy_device *phydev);
- int at803x_get_tunable(struct phy_device *phydev,
- struct ethtool_tunable *tuna, void *data);
- int at803x_set_tunable(struct phy_device *phydev,
-@@ -118,3 +168,4 @@ int at803x_cdt_fault_length(int dt);
- int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start);
- int at803x_cdt_wait_for_completion(struct phy_device *phydev,
- u32 cdt_en);
-+int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished);
+++ /dev/null
-From 9b1d5e055508393561e26bd1720f4c2639b03b1a Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 6 Feb 2024 18:31:09 +0100
-Subject: [PATCH 06/10] net: phy: provide whether link has changed in
- c37_read_status
-
-Some PHY driver might require additional regs call after
-genphy_c37_read_status() is called.
-
-Expand genphy_c37_read_status to provide a bool wheather the link has
-changed or not to permit PHY driver to skip additional regs call if
-nothing has changed.
-
-Every user of genphy_c37_read_status() is updated with the new
-additional bool.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/broadcom.c | 3 ++-
- drivers/net/phy/phy_device.c | 11 +++++++++--
- drivers/net/phy/qcom/at803x.c | 3 ++-
- include/linux/phy.h | 2 +-
- 4 files changed, 14 insertions(+), 5 deletions(-)
-
---- a/drivers/net/phy/broadcom.c
-+++ b/drivers/net/phy/broadcom.c
-@@ -665,10 +665,11 @@ static int bcm54616s_config_aneg(struct
- static int bcm54616s_read_status(struct phy_device *phydev)
- {
- struct bcm54616s_phy_priv *priv = phydev->priv;
-+ bool changed;
- int err;
-
- if (priv->mode_1000bx_en)
-- err = genphy_c37_read_status(phydev);
-+ err = genphy_c37_read_status(phydev, &changed);
- else
- err = genphy_read_status(phydev);
-
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -2615,12 +2615,15 @@ EXPORT_SYMBOL(genphy_read_status);
- /**
- * genphy_c37_read_status - check the link status and update current link state
- * @phydev: target phy_device struct
-+ * @changed: pointer where to store if link changed
- *
- * Description: Check the link, then figure out the current state
- * by comparing what we advertise with what the link partner
- * advertises. This function is for Clause 37 1000Base-X mode.
-+ *
-+ * If link has changed, @changed is set to true, false otherwise.
- */
--int genphy_c37_read_status(struct phy_device *phydev)
-+int genphy_c37_read_status(struct phy_device *phydev, bool *changed)
- {
- int lpa, err, old_link = phydev->link;
-
-@@ -2630,9 +2633,13 @@ int genphy_c37_read_status(struct phy_de
- return err;
-
- /* why bother the PHY if nothing can have changed */
-- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
-+ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) {
-+ *changed = false;
- return 0;
-+ }
-
-+ /* Signal link has changed */
-+ *changed = true;
- phydev->duplex = DUPLEX_UNKNOWN;
- phydev->pause = 0;
- phydev->asym_pause = 0;
---- a/drivers/net/phy/qcom/at803x.c
-+++ b/drivers/net/phy/qcom/at803x.c
-@@ -912,9 +912,10 @@ static int at8031_config_intr(struct phy
- static int at8031_read_status(struct phy_device *phydev)
- {
- struct at803x_priv *priv = phydev->priv;
-+ bool changed;
-
- if (priv->is_1000basex)
-- return genphy_c37_read_status(phydev);
-+ return genphy_c37_read_status(phydev, &changed);
-
- return at803x_read_status(phydev);
- }
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -1849,7 +1849,7 @@ int genphy_write_mmd_unsupported(struct
-
- /* Clause 37 */
- int genphy_c37_config_aneg(struct phy_device *phydev);
--int genphy_c37_read_status(struct phy_device *phydev);
-+int genphy_c37_read_status(struct phy_device *phydev, bool *changed);
-
- /* Clause 45 PHY */
- int genphy_c45_restart_aneg(struct phy_device *phydev);
+++ /dev/null
-From d1cb613efbd3cd7d0c000167816beb3f248f5eb8 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robert.marko@sartura.hr>
-Date: Tue, 6 Feb 2024 18:31:10 +0100
-Subject: [PATCH 07/10] net: phy: qcom: add support for QCA807x PHY Family
-
-This adds driver for the Qualcomm QCA8072 and QCA8075 PHY-s.
-
-They are 2 or 5 port IEEE 802.3 clause 22 compliant 10BASE-Te,
-100BASE-TX and 1000BASE-T PHY-s.
-
-They feature 2 SerDes, one for PSGMII or QSGMII connection with
-MAC, while second one is SGMII for connection to MAC or fiber.
-
-Both models have a combo port that supports 1000BASE-X and
-100BASE-FX fiber.
-
-PHY package can be configured in 3 mode following this table:
-
- First Serdes mode Second Serdes mode
-Option 1 PSGMII for copper Disabled
- ports 0-4
-Option 2 PSGMII for copper 1000BASE-X / 100BASE-FX
- ports 0-4
-Option 3 QSGMII for copper SGMII for
- ports 0-3 copper port 4
-
-Each PHY inside of QCA807x series has 4 digitally controlled
-output only pins that natively drive LED-s.
-But some vendors used these to driver generic LED-s controlled
-by userspace, so lets enable registering each PHY as GPIO
-controller and add driver for it.
-
-These are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x
-boards.
-
-Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: Robert Marko <robert.marko@sartura.hr>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/qcom/Kconfig | 8 +
- drivers/net/phy/qcom/Makefile | 1 +
- drivers/net/phy/qcom/qca807x.c | 597 +++++++++++++++++++++++++++++++++
- 3 files changed, 606 insertions(+)
- create mode 100644 drivers/net/phy/qcom/qca807x.c
-
---- a/drivers/net/phy/qcom/Kconfig
-+++ b/drivers/net/phy/qcom/Kconfig
-@@ -20,3 +20,11 @@ config QCA808X_PHY
- select QCOM_NET_PHYLIB
- help
- Currently supports the QCA8081 model
-+
-+config QCA807X_PHY
-+ tristate "Qualcomm QCA807x PHYs"
-+ select QCOM_NET_PHYLIB
-+ depends on OF_MDIO
-+ help
-+ Currently supports the Qualcomm QCA8072, QCA8075 and the PSGMII
-+ control PHY.
---- a/drivers/net/phy/qcom/Makefile
-+++ b/drivers/net/phy/qcom/Makefile
-@@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-ph
- obj-$(CONFIG_AT803X_PHY) += at803x.o
- obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o
- obj-$(CONFIG_QCA808X_PHY) += qca808x.o
-+obj-$(CONFIG_QCA807X_PHY) += qca807x.o
---- /dev/null
-+++ b/drivers/net/phy/qcom/qca807x.c
-@@ -0,0 +1,597 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+/*
-+ * Copyright (c) 2023 Sartura Ltd.
-+ *
-+ * Author: Robert Marko <robert.marko@sartura.hr>
-+ * Christian Marangi <ansuelsmth@gmail.com>
-+ *
-+ * Qualcomm QCA8072 and QCA8075 PHY driver
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/of.h>
-+#include <linux/phy.h>
-+#include <linux/bitfield.h>
-+#include <linux/gpio/driver.h>
-+#include <linux/sfp.h>
-+
-+#include "qcom.h"
-+
-+#define QCA807X_CHIP_CONFIGURATION 0x1f
-+#define QCA807X_BT_BX_REG_SEL BIT(15)
-+#define QCA807X_BT_BX_REG_SEL_FIBER 0
-+#define QCA807X_BT_BX_REG_SEL_COPPER 1
-+#define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK GENMASK(3, 0)
-+#define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII 4
-+#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER 3
-+#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER 0
-+
-+#define QCA807X_MEDIA_SELECT_STATUS 0x1a
-+#define QCA807X_MEDIA_DETECTED_COPPER BIT(5)
-+#define QCA807X_MEDIA_DETECTED_1000_BASE_X BIT(4)
-+#define QCA807X_MEDIA_DETECTED_100_BASE_FX BIT(3)
-+
-+#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION 0x807e
-+#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN BIT(0)
-+
-+#define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH 0x801a
-+#define QCA807X_CONTROL_DAC_MASK GENMASK(2, 0)
-+/* List of tweaks enabled by this bit:
-+ * - With both FULL amplitude and FULL bias current: bias current
-+ * is set to half.
-+ * - With only DSP amplitude: bias current is set to half and
-+ * is set to 1/4 with cable < 10m.
-+ * - With DSP bias current (included both DSP amplitude and
-+ * DSP bias current): bias current is half the detected current
-+ * with cable < 10m.
-+ */
-+#define QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK BIT(2)
-+#define QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT BIT(1)
-+#define QCA807X_CONTROL_DAC_DSP_AMPLITUDE BIT(0)
-+
-+#define QCA807X_MMD7_LED_100N_1 0x8074
-+#define QCA807X_MMD7_LED_100N_2 0x8075
-+#define QCA807X_MMD7_LED_1000N_1 0x8076
-+#define QCA807X_MMD7_LED_1000N_2 0x8077
-+
-+#define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2))
-+#define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2))
-+
-+#define QCA807X_GPIO_FORCE_EN BIT(15)
-+#define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13)
-+
-+#define QCA807X_FUNCTION_CONTROL 0x10
-+#define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5)
-+#define QCA807X_FC_MDI_CROSSOVER_AUTO 3
-+#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX 1
-+#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI 0
-+
-+/* PQSGMII Analog PHY specific */
-+#define PQSGMII_CTRL_REG 0x0
-+#define PQSGMII_ANALOG_SW_RESET BIT(6)
-+#define PQSGMII_DRIVE_CONTROL_1 0xb
-+#define PQSGMII_TX_DRIVER_MASK GENMASK(7, 4)
-+#define PQSGMII_TX_DRIVER_140MV 0x0
-+#define PQSGMII_TX_DRIVER_160MV 0x1
-+#define PQSGMII_TX_DRIVER_180MV 0x2
-+#define PQSGMII_TX_DRIVER_200MV 0x3
-+#define PQSGMII_TX_DRIVER_220MV 0x4
-+#define PQSGMII_TX_DRIVER_240MV 0x5
-+#define PQSGMII_TX_DRIVER_260MV 0x6
-+#define PQSGMII_TX_DRIVER_280MV 0x7
-+#define PQSGMII_TX_DRIVER_300MV 0x8
-+#define PQSGMII_TX_DRIVER_320MV 0x9
-+#define PQSGMII_TX_DRIVER_400MV 0xa
-+#define PQSGMII_TX_DRIVER_500MV 0xb
-+#define PQSGMII_TX_DRIVER_600MV 0xc
-+#define PQSGMII_MODE_CTRL 0x6d
-+#define PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK BIT(0)
-+#define PQSGMII_MMD3_SERDES_CONTROL 0x805a
-+
-+#define PHY_ID_QCA8072 0x004dd0b2
-+#define PHY_ID_QCA8075 0x004dd0b1
-+
-+#define QCA807X_COMBO_ADDR_OFFSET 4
-+#define QCA807X_PQSGMII_ADDR_OFFSET 5
-+#define SERDES_RESET_SLEEP 100
-+
-+enum qca807x_global_phy {
-+ QCA807X_COMBO_ADDR = 4,
-+ QCA807X_PQSGMII_ADDR = 5,
-+};
-+
-+struct qca807x_shared_priv {
-+ unsigned int package_mode;
-+ u32 tx_drive_strength;
-+};
-+
-+struct qca807x_gpio_priv {
-+ struct phy_device *phy;
-+};
-+
-+struct qca807x_priv {
-+ bool dac_full_amplitude;
-+ bool dac_full_bias_current;
-+ bool dac_disable_bias_current_tweak;
-+};
-+
-+static int qca807x_cable_test_start(struct phy_device *phydev)
-+{
-+ /* we do all the (time consuming) work later */
-+ return 0;
-+}
-+
-+#ifdef CONFIG_GPIOLIB
-+static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
-+{
-+ return GPIO_LINE_DIRECTION_OUT;
-+}
-+
-+static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset)
-+{
-+ struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
-+ u16 reg;
-+ int val;
-+
-+ reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);
-+ val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);
-+
-+ return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val);
-+}
-+
-+static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
-+{
-+ struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
-+ u16 reg;
-+ int val;
-+
-+ reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);
-+
-+ val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);
-+ val &= ~QCA807X_GPIO_FORCE_MODE_MASK;
-+ val |= QCA807X_GPIO_FORCE_EN;
-+ val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value);
-+
-+ phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val);
-+}
-+
-+static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value)
-+{
-+ qca807x_gpio_set(gc, offset, value);
-+
-+ return 0;
-+}
-+
-+static int qca807x_gpio(struct phy_device *phydev)
-+{
-+ struct device *dev = &phydev->mdio.dev;
-+ struct qca807x_gpio_priv *priv;
-+ struct gpio_chip *gc;
-+
-+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ priv->phy = phydev;
-+
-+ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
-+ if (!gc)
-+ return -ENOMEM;
-+
-+ gc->label = dev_name(dev);
-+ gc->base = -1;
-+ gc->ngpio = 2;
-+ gc->parent = dev;
-+ gc->owner = THIS_MODULE;
-+ gc->can_sleep = true;
-+ gc->get_direction = qca807x_gpio_get_direction;
-+ gc->direction_output = qca807x_gpio_dir_out;
-+ gc->get = qca807x_gpio_get;
-+ gc->set = qca807x_gpio_set;
-+
-+ return devm_gpiochip_add_data(dev, gc, priv);
-+}
-+#endif
-+
-+static int qca807x_read_fiber_status(struct phy_device *phydev)
-+{
-+ bool changed;
-+ int ss, err;
-+
-+ err = genphy_c37_read_status(phydev, &changed);
-+ if (err || !changed)
-+ return err;
-+
-+ /* Read the QCA807x PHY-Specific Status register fiber page,
-+ * which indicates the speed and duplex that the PHY is actually
-+ * using, irrespective of whether we are in autoneg mode or not.
-+ */
-+ ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
-+ if (ss < 0)
-+ return ss;
-+
-+ phydev->speed = SPEED_UNKNOWN;
-+ phydev->duplex = DUPLEX_UNKNOWN;
-+ if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
-+ switch (FIELD_GET(AT803X_SS_SPEED_MASK, ss)) {
-+ case AT803X_SS_SPEED_100:
-+ phydev->speed = SPEED_100;
-+ break;
-+ case AT803X_SS_SPEED_1000:
-+ phydev->speed = SPEED_1000;
-+ break;
-+ }
-+
-+ if (ss & AT803X_SS_DUPLEX)
-+ phydev->duplex = DUPLEX_FULL;
-+ else
-+ phydev->duplex = DUPLEX_HALF;
-+ }
-+
-+ return 0;
-+}
-+
-+static int qca807x_read_status(struct phy_device *phydev)
-+{
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
-+ switch (phydev->port) {
-+ case PORT_FIBRE:
-+ return qca807x_read_fiber_status(phydev);
-+ case PORT_TP:
-+ return at803x_read_status(phydev);
-+ default:
-+ return -EINVAL;
-+ }
-+ }
-+
-+ return at803x_read_status(phydev);
-+}
-+
-+static int qca807x_phy_package_probe_once(struct phy_device *phydev)
-+{
-+ struct phy_package_shared *shared = phydev->shared;
-+ struct qca807x_shared_priv *priv = shared->priv;
-+ unsigned int tx_drive_strength;
-+ const char *package_mode_name;
-+
-+ /* Default to 600mw if not defined */
-+ if (of_property_read_u32(shared->np, "qcom,tx-drive-strength-milliwatt",
-+ &tx_drive_strength))
-+ tx_drive_strength = 600;
-+
-+ switch (tx_drive_strength) {
-+ case 140:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_140MV;
-+ break;
-+ case 160:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_160MV;
-+ break;
-+ case 180:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_180MV;
-+ break;
-+ case 200:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_200MV;
-+ break;
-+ case 220:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_220MV;
-+ break;
-+ case 240:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_240MV;
-+ break;
-+ case 260:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_260MV;
-+ break;
-+ case 280:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_280MV;
-+ break;
-+ case 300:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_300MV;
-+ break;
-+ case 320:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_320MV;
-+ break;
-+ case 400:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_400MV;
-+ break;
-+ case 500:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_500MV;
-+ break;
-+ case 600:
-+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_600MV;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ priv->package_mode = PHY_INTERFACE_MODE_NA;
-+ if (!of_property_read_string(shared->np, "qcom,package-mode",
-+ &package_mode_name)) {
-+ if (!strcasecmp(package_mode_name,
-+ phy_modes(PHY_INTERFACE_MODE_PSGMII)))
-+ priv->package_mode = PHY_INTERFACE_MODE_PSGMII;
-+ else if (!strcasecmp(package_mode_name,
-+ phy_modes(PHY_INTERFACE_MODE_QSGMII)))
-+ priv->package_mode = PHY_INTERFACE_MODE_QSGMII;
-+ else
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int qca807x_phy_package_config_init_once(struct phy_device *phydev)
-+{
-+ struct phy_package_shared *shared = phydev->shared;
-+ struct qca807x_shared_priv *priv = shared->priv;
-+ int val, ret;
-+
-+ phy_lock_mdio_bus(phydev);
-+
-+ /* Set correct PHY package mode */
-+ val = __phy_package_read(phydev, QCA807X_COMBO_ADDR,
-+ QCA807X_CHIP_CONFIGURATION);
-+ val &= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK;
-+ /* package_mode can be QSGMII or PSGMII and we validate
-+ * this in probe_once.
-+ * With package_mode to NA, we default to PSGMII.
-+ */
-+ switch (priv->package_mode) {
-+ case PHY_INTERFACE_MODE_QSGMII:
-+ val |= QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII;
-+ break;
-+ case PHY_INTERFACE_MODE_PSGMII:
-+ default:
-+ val |= QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER;
-+ }
-+ ret = __phy_package_write(phydev, QCA807X_COMBO_ADDR,
-+ QCA807X_CHIP_CONFIGURATION, val);
-+ if (ret)
-+ goto exit;
-+
-+ /* After mode change Serdes reset is required */
-+ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
-+ PQSGMII_CTRL_REG);
-+ val &= ~PQSGMII_ANALOG_SW_RESET;
-+ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
-+ PQSGMII_CTRL_REG, val);
-+ if (ret)
-+ goto exit;
-+
-+ msleep(SERDES_RESET_SLEEP);
-+
-+ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
-+ PQSGMII_CTRL_REG);
-+ val |= PQSGMII_ANALOG_SW_RESET;
-+ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
-+ PQSGMII_CTRL_REG, val);
-+ if (ret)
-+ goto exit;
-+
-+ /* Workaround to enable AZ transmitting ability */
-+ val = __phy_package_read_mmd(phydev, QCA807X_PQSGMII_ADDR,
-+ MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL);
-+ val &= ~PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK;
-+ ret = __phy_package_write_mmd(phydev, QCA807X_PQSGMII_ADDR,
-+ MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL, val);
-+ if (ret)
-+ goto exit;
-+
-+ /* Set PQSGMII TX AMP strength */
-+ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
-+ PQSGMII_DRIVE_CONTROL_1);
-+ val &= ~PQSGMII_TX_DRIVER_MASK;
-+ val |= FIELD_PREP(PQSGMII_TX_DRIVER_MASK, priv->tx_drive_strength);
-+ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
-+ PQSGMII_DRIVE_CONTROL_1, val);
-+ if (ret)
-+ goto exit;
-+
-+ /* Prevent PSGMII going into hibernation via PSGMII self test */
-+ val = __phy_package_read_mmd(phydev, QCA807X_COMBO_ADDR,
-+ MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL);
-+ val &= ~BIT(1);
-+ ret = __phy_package_write_mmd(phydev, QCA807X_COMBO_ADDR,
-+ MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL, val);
-+
-+exit:
-+ phy_unlock_mdio_bus(phydev);
-+
-+ return ret;
-+}
-+
-+static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
-+{
-+ struct phy_device *phydev = upstream;
-+ __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
-+ phy_interface_t iface;
-+ int ret;
-+ DECLARE_PHY_INTERFACE_MASK(interfaces);
-+
-+ sfp_parse_support(phydev->sfp_bus, id, support, interfaces);
-+ iface = sfp_select_interface(phydev->sfp_bus, support);
-+
-+ dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface));
-+
-+ switch (iface) {
-+ case PHY_INTERFACE_MODE_1000BASEX:
-+ case PHY_INTERFACE_MODE_100BASEX:
-+ /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */
-+ ret = phy_modify(phydev,
-+ QCA807X_CHIP_CONFIGURATION,
-+ QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK,
-+ QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER);
-+ /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */
-+ ret = phy_set_bits_mmd(phydev,
-+ MDIO_MMD_AN,
-+ QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION,
-+ QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN);
-+ /* Select fiber page */
-+ ret = phy_clear_bits(phydev,
-+ QCA807X_CHIP_CONFIGURATION,
-+ QCA807X_BT_BX_REG_SEL);
-+
-+ phydev->port = PORT_FIBRE;
-+ break;
-+ default:
-+ dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n");
-+ return -EINVAL;
-+ }
-+
-+ return ret;
-+}
-+
-+static void qca807x_sfp_remove(void *upstream)
-+{
-+ struct phy_device *phydev = upstream;
-+
-+ /* Select copper page */
-+ phy_set_bits(phydev,
-+ QCA807X_CHIP_CONFIGURATION,
-+ QCA807X_BT_BX_REG_SEL);
-+
-+ phydev->port = PORT_TP;
-+}
-+
-+static const struct sfp_upstream_ops qca807x_sfp_ops = {
-+ .attach = phy_sfp_attach,
-+ .detach = phy_sfp_detach,
-+ .module_insert = qca807x_sfp_insert,
-+ .module_remove = qca807x_sfp_remove,
-+};
-+
-+static int qca807x_probe(struct phy_device *phydev)
-+{
-+ struct device_node *node = phydev->mdio.dev.of_node;
-+ struct qca807x_shared_priv *shared_priv;
-+ struct device *dev = &phydev->mdio.dev;
-+ struct phy_package_shared *shared;
-+ struct qca807x_priv *priv;
-+ int ret;
-+
-+ ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv));
-+ if (ret)
-+ return ret;
-+
-+ if (phy_package_probe_once(phydev)) {
-+ ret = qca807x_phy_package_probe_once(phydev);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ shared = phydev->shared;
-+ shared_priv = shared->priv;
-+
-+ /* Make sure PHY follow PHY package mode if enforced */
-+ if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA &&
-+ phydev->interface != shared_priv->package_mode)
-+ return -EINVAL;
-+
-+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ priv->dac_full_amplitude = of_property_read_bool(node, "qcom,dac-full-amplitude");
-+ priv->dac_full_bias_current = of_property_read_bool(node, "qcom,dac-full-bias-current");
-+ priv->dac_disable_bias_current_tweak = of_property_read_bool(node,
-+ "qcom,dac-disable-bias-current-tweak");
-+
-+ if (IS_ENABLED(CONFIG_GPIOLIB)) {
-+ /* Do not register a GPIO controller unless flagged for it */
-+ if (of_property_read_bool(node, "gpio-controller")) {
-+ ret = qca807x_gpio(phydev);
-+ if (ret)
-+ return ret;
-+ }
-+ }
-+
-+ /* Attach SFP bus on combo port*/
-+ if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
-+ ret = phy_sfp_probe(phydev, &qca807x_sfp_ops);
-+ if (ret)
-+ return ret;
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising);
-+ }
-+
-+ phydev->priv = priv;
-+
-+ return 0;
-+}
-+
-+static int qca807x_config_init(struct phy_device *phydev)
-+{
-+ struct qca807x_priv *priv = phydev->priv;
-+ u16 control_dac;
-+ int ret;
-+
-+ if (phy_package_init_once(phydev)) {
-+ ret = qca807x_phy_package_config_init_once(phydev);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ control_dac = phy_read_mmd(phydev, MDIO_MMD_AN,
-+ QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH);
-+ control_dac &= ~QCA807X_CONTROL_DAC_MASK;
-+ if (!priv->dac_full_amplitude)
-+ control_dac |= QCA807X_CONTROL_DAC_DSP_AMPLITUDE;
-+ if (!priv->dac_full_amplitude)
-+ control_dac |= QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT;
-+ if (!priv->dac_disable_bias_current_tweak)
-+ control_dac |= QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK;
-+ return phy_write_mmd(phydev, MDIO_MMD_AN,
-+ QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH,
-+ control_dac);
-+}
-+
-+static struct phy_driver qca807x_drivers[] = {
-+ {
-+ PHY_ID_MATCH_EXACT(PHY_ID_QCA8072),
-+ .name = "Qualcomm QCA8072",
-+ .flags = PHY_POLL_CABLE_TEST,
-+ /* PHY_GBIT_FEATURES */
-+ .probe = qca807x_probe,
-+ .config_init = qca807x_config_init,
-+ .read_status = qca807x_read_status,
-+ .config_intr = at803x_config_intr,
-+ .handle_interrupt = at803x_handle_interrupt,
-+ .soft_reset = genphy_soft_reset,
-+ .get_tunable = at803x_get_tunable,
-+ .set_tunable = at803x_set_tunable,
-+ .resume = genphy_resume,
-+ .suspend = genphy_suspend,
-+ .cable_test_start = qca807x_cable_test_start,
-+ .cable_test_get_status = qca808x_cable_test_get_status,
-+ },
-+ {
-+ PHY_ID_MATCH_EXACT(PHY_ID_QCA8075),
-+ .name = "Qualcomm QCA8075",
-+ .flags = PHY_POLL_CABLE_TEST,
-+ /* PHY_GBIT_FEATURES */
-+ .probe = qca807x_probe,
-+ .config_init = qca807x_config_init,
-+ .read_status = qca807x_read_status,
-+ .config_intr = at803x_config_intr,
-+ .handle_interrupt = at803x_handle_interrupt,
-+ .soft_reset = genphy_soft_reset,
-+ .get_tunable = at803x_get_tunable,
-+ .set_tunable = at803x_set_tunable,
-+ .resume = genphy_resume,
-+ .suspend = genphy_suspend,
-+ .cable_test_start = qca807x_cable_test_start,
-+ .cable_test_get_status = qca808x_cable_test_get_status,
-+ },
-+};
-+module_phy_driver(qca807x_drivers);
-+
-+static struct mdio_device_id __maybe_unused qca807x_tbl[] = {
-+ { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072) },
-+ { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075) },
-+ { }
-+};
-+
-+MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>");
-+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
-+MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver");
-+MODULE_DEVICE_TABLE(mdio, qca807x_tbl);
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From ee9d9807bee0e6af8ca2a4db6f0d1dc0e5b41f44 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 6 Feb 2024 18:31:11 +0100
-Subject: [PATCH 08/10] net: phy: qcom: move common qca808x LED define to
- shared header
-
-The LED implementation of qca808x and qca807x is the same but qca807x
-supports also Fiber port and have different hw control bits for Fiber
-port.
-
-In preparation for qca807x introduction, move all the common define to
-shared header.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/qcom/qca808x.c | 65 ----------------------------------
- drivers/net/phy/qcom/qcom.h | 65 ++++++++++++++++++++++++++++++++++
- 2 files changed, 65 insertions(+), 65 deletions(-)
-
---- a/drivers/net/phy/qcom/qca808x.c
-+++ b/drivers/net/phy/qcom/qca808x.c
-@@ -62,29 +62,6 @@
- #define QCA808X_DBG_AN_TEST 0xb
- #define QCA808X_HIBERNATION_EN BIT(15)
-
--#define QCA808X_MMD7_LED_GLOBAL 0x8073
--#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
--#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
--/* Values are the same for both BLINK_1 and BLINK_2 */
--#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
--#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
--#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
--#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
--#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
--#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
--#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
--#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
--#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
--#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
--#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
--#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
--#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
--#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
--#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
--#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
--#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
--#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
--
- #define QCA808X_MMD7_LED2_CTRL 0x8074
- #define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075
- #define QCA808X_MMD7_LED1_CTRL 0x8076
-@@ -92,51 +69,9 @@
- #define QCA808X_MMD7_LED0_CTRL 0x8078
- #define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))
-
--/* LED hw control pattern is the same for every LED */
--#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
--#define QCA808X_LED_SPEED2500_ON BIT(15)
--#define QCA808X_LED_SPEED2500_BLINK BIT(14)
--/* Follow blink trigger even if duplex or speed condition doesn't match */
--#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
--#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
--#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
--#define QCA808X_LED_TX_BLINK BIT(10)
--#define QCA808X_LED_RX_BLINK BIT(9)
--#define QCA808X_LED_TX_ON_10MS BIT(8)
--#define QCA808X_LED_RX_ON_10MS BIT(7)
--#define QCA808X_LED_SPEED1000_ON BIT(6)
--#define QCA808X_LED_SPEED100_ON BIT(5)
--#define QCA808X_LED_SPEED10_ON BIT(4)
--#define QCA808X_LED_COLLISION_BLINK BIT(3)
--#define QCA808X_LED_SPEED1000_BLINK BIT(2)
--#define QCA808X_LED_SPEED100_BLINK BIT(1)
--#define QCA808X_LED_SPEED10_BLINK BIT(0)
--
- #define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079
- #define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))
-
--/* LED force ctrl is the same for every LED
-- * No documentation exist for this, not even internal one
-- * with NDA as QCOM gives only info about configuring
-- * hw control pattern rules and doesn't indicate any way
-- * to force the LED to specific mode.
-- * These define comes from reverse and testing and maybe
-- * lack of some info or some info are not entirely correct.
-- * For the basic LED control and hw control these finding
-- * are enough to support LED control in all the required APIs.
-- *
-- * On doing some comparison with implementation with qca807x,
-- * it was found that it's 1:1 equal to it and confirms all the
-- * reverse done. It was also found further specification with the
-- * force mode and the blink modes.
-- */
--#define QCA808X_LED_FORCE_EN BIT(15)
--#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
--#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
--#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
--#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
--#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
--
- #define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a
- /* QSDK sets by default 0x46 to this reg that sets BIT 6 for
- * LED to active high. It's not clear what BIT 3 and BIT 4 does.
---- a/drivers/net/phy/qcom/qcom.h
-+++ b/drivers/net/phy/qcom/qcom.h
-@@ -103,6 +103,71 @@
- /* Added for reference of existence but should be handled by wait_for_completion already */
- #define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
-
-+#define QCA808X_MMD7_LED_GLOBAL 0x8073
-+#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
-+#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
-+/* Values are the same for both BLINK_1 and BLINK_2 */
-+#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
-+#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
-+#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
-+#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
-+#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
-+#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
-+#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
-+#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
-+#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
-+#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
-+#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
-+#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
-+#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
-+#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
-+#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
-+#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
-+#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
-+#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
-+
-+/* LED hw control pattern is the same for every LED */
-+#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
-+#define QCA808X_LED_SPEED2500_ON BIT(15)
-+#define QCA808X_LED_SPEED2500_BLINK BIT(14)
-+/* Follow blink trigger even if duplex or speed condition doesn't match */
-+#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
-+#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
-+#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
-+#define QCA808X_LED_TX_BLINK BIT(10)
-+#define QCA808X_LED_RX_BLINK BIT(9)
-+#define QCA808X_LED_TX_ON_10MS BIT(8)
-+#define QCA808X_LED_RX_ON_10MS BIT(7)
-+#define QCA808X_LED_SPEED1000_ON BIT(6)
-+#define QCA808X_LED_SPEED100_ON BIT(5)
-+#define QCA808X_LED_SPEED10_ON BIT(4)
-+#define QCA808X_LED_COLLISION_BLINK BIT(3)
-+#define QCA808X_LED_SPEED1000_BLINK BIT(2)
-+#define QCA808X_LED_SPEED100_BLINK BIT(1)
-+#define QCA808X_LED_SPEED10_BLINK BIT(0)
-+
-+/* LED force ctrl is the same for every LED
-+ * No documentation exist for this, not even internal one
-+ * with NDA as QCOM gives only info about configuring
-+ * hw control pattern rules and doesn't indicate any way
-+ * to force the LED to specific mode.
-+ * These define comes from reverse and testing and maybe
-+ * lack of some info or some info are not entirely correct.
-+ * For the basic LED control and hw control these finding
-+ * are enough to support LED control in all the required APIs.
-+ *
-+ * On doing some comparison with implementation with qca807x,
-+ * it was found that it's 1:1 equal to it and confirms all the
-+ * reverse done. It was also found further specification with the
-+ * force mode and the blink modes.
-+ */
-+#define QCA808X_LED_FORCE_EN BIT(15)
-+#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
-+#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
-+#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
-+#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
-+#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
-+
- #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
- #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B
- #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
+++ /dev/null
-From 47b930d0dd437af927145dba50a2e2ea1ba97c67 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 6 Feb 2024 18:31:12 +0100
-Subject: [PATCH 09/10] net: phy: qcom: generalize some qca808x LED functions
-
-Generalize some qca808x LED functions in preparation for qca807x LED
-support.
-
-The LED implementation of qca808x and qca807x is the same but qca807x
-supports also Fiber port and have different hw control bits for Fiber
-port. To limit code duplication introduce micro functions that takes reg
-instead of LED index to tweak all the supported LED modes.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/qcom/qca808x.c | 38 +++-----------------
- drivers/net/phy/qcom/qcom-phy-lib.c | 54 +++++++++++++++++++++++++++++
- drivers/net/phy/qcom/qcom.h | 7 ++++
- 3 files changed, 65 insertions(+), 34 deletions(-)
-
---- a/drivers/net/phy/qcom/qca808x.c
-+++ b/drivers/net/phy/qcom/qca808x.c
-@@ -437,9 +437,7 @@ static int qca808x_led_hw_control_enable
- return -EINVAL;
-
- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_FORCE_EN);
-+ return qca808x_led_reg_hw_control_enable(phydev, reg);
- }
-
- static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
-@@ -480,16 +478,12 @@ static int qca808x_led_hw_control_set(st
- static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
- {
- u16 reg;
-- int val;
-
- if (index > 2)
- return false;
-
- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
--
-- return !(val & QCA808X_LED_FORCE_EN);
-+ return qca808x_led_reg_hw_control_status(phydev, reg);
- }
-
- static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
-@@ -557,44 +551,20 @@ static int qca808x_led_brightness_set(st
- }
-
- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-- QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
-- QCA808X_LED_FORCE_OFF));
-+ return qca808x_led_reg_brightness_set(phydev, reg, value);
- }
-
- static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
- unsigned long *delay_on,
- unsigned long *delay_off)
- {
-- int ret;
- u16 reg;
-
- if (index > 2)
- return -EINVAL;
-
- reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
--
-- /* Set blink to 50% off, 50% on at 4Hz by default */
-- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
-- QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
-- QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
-- if (ret)
-- return ret;
--
-- /* We use BLINK_1 for normal blinking */
-- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
-- if (ret)
-- return ret;
--
-- /* We set blink to 4Hz, aka 250ms */
-- *delay_on = 250 / 2;
-- *delay_off = 250 / 2;
--
-- return 0;
-+ return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);
- }
-
- static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
---- a/drivers/net/phy/qcom/qcom-phy-lib.c
-+++ b/drivers/net/phy/qcom/qcom-phy-lib.c
-@@ -620,3 +620,57 @@ int qca808x_cable_test_get_status(struct
- return 0;
- }
- EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
-+
-+int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg)
-+{
-+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN);
-+}
-+EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable);
-+
-+bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg)
-+{
-+ int val;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
-+ return !(val & QCA808X_LED_FORCE_EN);
-+}
-+EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status);
-+
-+int qca808x_led_reg_brightness_set(struct phy_device *phydev,
-+ u16 reg, enum led_brightness value)
-+{
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-+ QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
-+ QCA808X_LED_FORCE_OFF));
-+}
-+EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set);
-+
-+int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ int ret;
-+
-+ /* Set blink to 50% off, 50% on at 4Hz by default */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
-+ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
-+ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
-+ if (ret)
-+ return ret;
-+
-+ /* We use BLINK_1 for normal blinking */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
-+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
-+ if (ret)
-+ return ret;
-+
-+ /* We set blink to 4Hz, aka 250ms */
-+ *delay_on = 250 / 2;
-+ *delay_off = 250 / 2;
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set);
---- a/drivers/net/phy/qcom/qcom.h
-+++ b/drivers/net/phy/qcom/qcom.h
-@@ -234,3 +234,10 @@ int at803x_cdt_start(struct phy_device *
- int at803x_cdt_wait_for_completion(struct phy_device *phydev,
- u32 cdt_en);
- int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished);
-+int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg);
-+bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg);
-+int qca808x_led_reg_brightness_set(struct phy_device *phydev,
-+ u16 reg, enum led_brightness value);
-+int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off);
+++ /dev/null
-From f508a226b517a6a8afd78a317de46bc83e3e3d51 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 6 Feb 2024 18:31:13 +0100
-Subject: [PATCH 10/10] net: phy: qca807x: add support for configurable LED
-
-QCA8072/5 have up to 2 LEDs attached for PHY.
-
-LEDs can be configured to be ON/hw blink or be set to HW control.
-
-Hw blink mode is set to blink at 4Hz or 250ms.
-
-PHY can support both copper (TP) or fiber (FIBRE) kind and supports
-different HW control modes based on the port type.
-
-HW control modes supported for netdev trigger for copper ports are:
-- LINK_10
-- LINK_100
-- LINK_1000
-- TX
-- RX
-- FULL_DUPLEX
-- HALF_DUPLEX
-
-HW control modes supported for netdev trigger for fiber ports are:
-- LINK_100
-- LINK_1000
-- TX
-- RX
-- FULL_DUPLEX
-- HALF_DUPLEX
-
-LED support conflicts with GPIO controller feature and must be disabled
-if gpio-controller is used for the PHY.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/qcom/qca807x.c | 256 ++++++++++++++++++++++++++++++++-
- 1 file changed, 254 insertions(+), 2 deletions(-)
-
---- a/drivers/net/phy/qcom/qca807x.c
-+++ b/drivers/net/phy/qcom/qca807x.c
-@@ -57,8 +57,18 @@
- #define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2))
- #define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2))
-
--#define QCA807X_GPIO_FORCE_EN BIT(15)
--#define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13)
-+/* LED hw control pattern for fiber port */
-+#define QCA807X_LED_FIBER_PATTERN_MASK GENMASK(11, 1)
-+#define QCA807X_LED_FIBER_TXACT_BLK_EN BIT(10)
-+#define QCA807X_LED_FIBER_RXACT_BLK_EN BIT(9)
-+#define QCA807X_LED_FIBER_FDX_ON_EN BIT(6)
-+#define QCA807X_LED_FIBER_HDX_ON_EN BIT(5)
-+#define QCA807X_LED_FIBER_1000BX_ON_EN BIT(2)
-+#define QCA807X_LED_FIBER_100FX_ON_EN BIT(1)
-+
-+/* Some device repurpose the LED as GPIO out */
-+#define QCA807X_GPIO_FORCE_EN QCA808X_LED_FORCE_EN
-+#define QCA807X_GPIO_FORCE_MODE_MASK QCA808X_LED_FORCE_MODE_MASK
-
- #define QCA807X_FUNCTION_CONTROL 0x10
- #define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5)
-@@ -121,6 +131,233 @@ static int qca807x_cable_test_start(stru
- return 0;
- }
-
-+static int qca807x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
-+ u16 *offload_trigger)
-+{
-+ /* Parsing specific to netdev trigger */
-+ switch (phydev->port) {
-+ case PORT_TP:
-+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
-+ *offload_trigger |= QCA808X_LED_TX_BLINK;
-+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
-+ *offload_trigger |= QCA808X_LED_RX_BLINK;
-+ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED10_ON;
-+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED100_ON;
-+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
-+ *offload_trigger |= QCA808X_LED_SPEED1000_ON;
-+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
-+ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
-+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
-+ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
-+ break;
-+ case PORT_FIBRE:
-+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
-+ *offload_trigger |= QCA807X_LED_FIBER_TXACT_BLK_EN;
-+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
-+ *offload_trigger |= QCA807X_LED_FIBER_RXACT_BLK_EN;
-+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
-+ *offload_trigger |= QCA807X_LED_FIBER_100FX_ON_EN;
-+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
-+ *offload_trigger |= QCA807X_LED_FIBER_1000BX_ON_EN;
-+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
-+ *offload_trigger |= QCA807X_LED_FIBER_HDX_ON_EN;
-+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
-+ *offload_trigger |= QCA807X_LED_FIBER_FDX_ON_EN;
-+ break;
-+ default:
-+ return -EOPNOTSUPP;
-+ }
-+
-+ if (rules && !*offload_trigger)
-+ return -EOPNOTSUPP;
-+
-+ return 0;
-+}
-+
-+static int qca807x_led_hw_control_enable(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg;
-+
-+ if (index > 1)
-+ return -EINVAL;
-+
-+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
-+ return qca808x_led_reg_hw_control_enable(phydev, reg);
-+}
-+
-+static int qca807x_led_hw_is_supported(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ u16 offload_trigger = 0;
-+
-+ if (index > 1)
-+ return -EINVAL;
-+
-+ return qca807x_led_parse_netdev(phydev, rules, &offload_trigger);
-+}
-+
-+static int qca807x_led_hw_control_set(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ u16 reg, mask, offload_trigger = 0;
-+ int ret;
-+
-+ if (index > 1)
-+ return -EINVAL;
-+
-+ ret = qca807x_led_parse_netdev(phydev, rules, &offload_trigger);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca807x_led_hw_control_enable(phydev, index);
-+ if (ret)
-+ return ret;
-+
-+ switch (phydev->port) {
-+ case PORT_TP:
-+ reg = QCA807X_MMD7_LED_CTRL(index);
-+ mask = QCA808X_LED_PATTERN_MASK;
-+ break;
-+ case PORT_FIBRE:
-+ /* HW control pattern bits are in LED FORCE reg */
-+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
-+ mask = QCA807X_LED_FIBER_PATTERN_MASK;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, mask,
-+ offload_trigger);
-+}
-+
-+static bool qca807x_led_hw_control_status(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg;
-+
-+ if (index > 1)
-+ return false;
-+
-+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
-+ return qca808x_led_reg_hw_control_status(phydev, reg);
-+}
-+
-+static int qca807x_led_hw_control_get(struct phy_device *phydev, u8 index,
-+ unsigned long *rules)
-+{
-+ u16 reg;
-+ int val;
-+
-+ if (index > 1)
-+ return -EINVAL;
-+
-+ /* Check if we have hw control enabled */
-+ if (qca807x_led_hw_control_status(phydev, index))
-+ return -EINVAL;
-+
-+ /* Parsing specific to netdev trigger */
-+ switch (phydev->port) {
-+ case PORT_TP:
-+ reg = QCA807X_MMD7_LED_CTRL(index);
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
-+ if (val & QCA808X_LED_TX_BLINK)
-+ set_bit(TRIGGER_NETDEV_TX, rules);
-+ if (val & QCA808X_LED_RX_BLINK)
-+ set_bit(TRIGGER_NETDEV_RX, rules);
-+ if (val & QCA808X_LED_SPEED10_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_10, rules);
-+ if (val & QCA808X_LED_SPEED100_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
-+ if (val & QCA808X_LED_SPEED1000_ON)
-+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
-+ if (val & QCA808X_LED_HALF_DUPLEX_ON)
-+ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
-+ if (val & QCA808X_LED_FULL_DUPLEX_ON)
-+ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
-+ break;
-+ case PORT_FIBRE:
-+ /* HW control pattern bits are in LED FORCE reg */
-+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
-+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
-+ if (val & QCA807X_LED_FIBER_TXACT_BLK_EN)
-+ set_bit(TRIGGER_NETDEV_TX, rules);
-+ if (val & QCA807X_LED_FIBER_RXACT_BLK_EN)
-+ set_bit(TRIGGER_NETDEV_RX, rules);
-+ if (val & QCA807X_LED_FIBER_100FX_ON_EN)
-+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
-+ if (val & QCA807X_LED_FIBER_1000BX_ON_EN)
-+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
-+ if (val & QCA807X_LED_FIBER_HDX_ON_EN)
-+ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
-+ if (val & QCA807X_LED_FIBER_FDX_ON_EN)
-+ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int qca807x_led_hw_control_reset(struct phy_device *phydev, u8 index)
-+{
-+ u16 reg, mask;
-+
-+ if (index > 1)
-+ return -EINVAL;
-+
-+ switch (phydev->port) {
-+ case PORT_TP:
-+ reg = QCA807X_MMD7_LED_CTRL(index);
-+ mask = QCA808X_LED_PATTERN_MASK;
-+ break;
-+ case PORT_FIBRE:
-+ /* HW control pattern bits are in LED FORCE reg */
-+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
-+ mask = QCA807X_LED_FIBER_PATTERN_MASK;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, mask);
-+}
-+
-+static int qca807x_led_brightness_set(struct phy_device *phydev,
-+ u8 index, enum led_brightness value)
-+{
-+ u16 reg;
-+ int ret;
-+
-+ if (index > 1)
-+ return -EINVAL;
-+
-+ /* If we are setting off the LED reset any hw control rule */
-+ if (!value) {
-+ ret = qca807x_led_hw_control_reset(phydev, index);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
-+ return qca808x_led_reg_brightness_set(phydev, reg, value);
-+}
-+
-+static int qca807x_led_blink_set(struct phy_device *phydev, u8 index,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ u16 reg;
-+
-+ if (index > 1)
-+ return -EINVAL;
-+
-+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
-+ return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);
-+}
-+
- #ifdef CONFIG_GPIOLIB
- static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
- {
-@@ -496,6 +733,16 @@ static int qca807x_probe(struct phy_devi
- "qcom,dac-disable-bias-current-tweak");
-
- if (IS_ENABLED(CONFIG_GPIOLIB)) {
-+ /* Make sure we don't have mixed leds node and gpio-controller
-+ * to prevent registering leds and having gpio-controller usage
-+ * conflicting with them.
-+ */
-+ if (of_find_property(node, "leds", NULL) &&
-+ of_find_property(node, "gpio-controller", NULL)) {
-+ phydev_err(phydev, "Invalid property detected. LEDs and gpio-controller are mutually exclusive.");
-+ return -EINVAL;
-+ }
-+
- /* Do not register a GPIO controller unless flagged for it */
- if (of_property_read_bool(node, "gpio-controller")) {
- ret = qca807x_gpio(phydev);
-@@ -580,6 +827,11 @@ static struct phy_driver qca807x_drivers
- .suspend = genphy_suspend,
- .cable_test_start = qca807x_cable_test_start,
- .cable_test_get_status = qca808x_cable_test_get_status,
-+ .led_brightness_set = qca807x_led_brightness_set,
-+ .led_blink_set = qca807x_led_blink_set,
-+ .led_hw_is_supported = qca807x_led_hw_is_supported,
-+ .led_hw_control_set = qca807x_led_hw_control_set,
-+ .led_hw_control_get = qca807x_led_hw_control_get,
- },
- };
- module_phy_driver(qca807x_drivers);
+++ /dev/null
-From 3be0d950b62852a693182cb678948f481de02825 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Mon, 12 Feb 2024 12:49:34 +0100
-Subject: [PATCH] net: phy: qca807x: move interface mode check to
- .config_init_once
-
-Currently, we are checking whether the PHY package mode matches the
-individual PHY interface modes at PHY package probe time, but at that time
-we only know the PHY package mode and not the individual PHY interface
-modes as of_get_phy_mode() that populates it will only get called once the
-netdev to which PHY-s are attached to is being probed and thus this check
-will always fail and return -EINVAL.
-
-So, lets move this check to .config_init_once as at that point individual
-PHY interface modes should be populated.
-
-Fixes: d1cb613efbd3 ("net: phy: qcom: add support for QCA807x PHY Family")
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240212115043.1725918-1-robimarko@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/phy/qcom/qca807x.c | 10 +++++-----
- 1 file changed, 5 insertions(+), 5 deletions(-)
-
---- a/drivers/net/phy/qcom/qca807x.c
-+++ b/drivers/net/phy/qcom/qca807x.c
-@@ -562,6 +562,11 @@ static int qca807x_phy_package_config_in
- struct qca807x_shared_priv *priv = shared->priv;
- int val, ret;
-
-+ /* Make sure PHY follow PHY package mode if enforced */
-+ if (priv->package_mode != PHY_INTERFACE_MODE_NA &&
-+ phydev->interface != priv->package_mode)
-+ return -EINVAL;
-+
- phy_lock_mdio_bus(phydev);
-
- /* Set correct PHY package mode */
-@@ -718,11 +723,6 @@ static int qca807x_probe(struct phy_devi
- shared = phydev->shared;
- shared_priv = shared->priv;
-
-- /* Make sure PHY follow PHY package mode if enforced */
-- if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA &&
-- phydev->interface != shared_priv->package_mode)
-- return -EINVAL;
--
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
+++ /dev/null
-From 6a4aee277740d04ac0fd54cfa17cc28261932ddc Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 25 Mar 2024 20:06:19 +0100
-Subject: [PATCH] net: phy: qcom: at803x: fix kernel panic with at8031_probe
-
-On reworking and splitting the at803x driver, in splitting function of
-at803x PHYs it was added a NULL dereference bug where priv is referenced
-before it's actually allocated and then is tried to write to for the
-is_1000basex and is_fiber variables in the case of at8031, writing on
-the wrong address.
-
-Fix this by correctly setting priv local variable only after
-at803x_probe is called and actually allocates priv in the phydev struct.
-
-Reported-by: William Wortel <wwortel@dorpstraat.com>
-Cc: <stable@vger.kernel.org>
-Fixes: 25d2ba94005f ("net: phy: at803x: move specific at8031 probe mode check to dedicated probe")
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240325190621.2665-1-ansuelsmth@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/phy/qcom/at803x.c | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
---- a/drivers/net/phy/qcom/at803x.c
-+++ b/drivers/net/phy/qcom/at803x.c
-@@ -797,7 +797,7 @@ static int at8031_parse_dt(struct phy_de
-
- static int at8031_probe(struct phy_device *phydev)
- {
-- struct at803x_priv *priv = phydev->priv;
-+ struct at803x_priv *priv;
- int mode_cfg;
- int ccr;
- int ret;
-@@ -806,6 +806,8 @@ static int at8031_probe(struct phy_devic
- if (ret)
- return ret;
-
-+ priv = phydev->priv;
-+
- /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
- * options.
- */
+++ /dev/null
-From bdce82e960d1205d118662f575cec39379984e34 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 31 Jan 2024 03:26:04 +0100
-Subject: [PATCH] net: mdio: ipq4019: add support for clock-frequency property
-
-The IPQ4019 MDIO internally divide the clock feed by AHB based on the
-MDIO_MODE reg. On reset or power up, the default value for the
-divider is 0xff that reflect the divider set to /256.
-
-This makes the MDC run at a very low rate, that is, considering AHB is
-always fixed to 100Mhz, a value of 390KHz.
-
-This hasn't have been a problem as MDIO wasn't used for time sensitive
-operation, it is now that on IPQ807x is usually mounted with PHY that
-requires MDIO to load their firmware (example Aquantia PHY).
-
-To handle this problem and permit to set the correct designed MDC
-frequency for the SoC add support for the standard "clock-frequency"
-property for the MDIO node.
-
-The divider supports value from /1 to /256 and the common value are to
-set it to /16 to reflect 6.25Mhz or to /8 on newer platform to reflect
-12.5Mhz.
-
-To scan if the requested rate is supported by the divider, loop with
-each supported divider and stop when the requested rate match the final
-rate with the current divider. An error is returned if the rate doesn't
-match any value.
-
-On MDIO reset, the divider is restored to the requested value to prevent
-any kind of downclocking caused by the divider reverting to a default
-value.
-
-To follow 802.3 spec of 2.5MHz of default value, if divider is set at
-/256 and "clock-frequency" is not set in DT, assume nobody set the
-divider and try to find the closest MDC rate to 2.5MHz. (in the case of
-AHB set to 100MHz, it's 1.5625MHz)
-
-While at is also document other bits of the MDIO_MODE reg to have a
-clear idea of what is actually applied there.
-
-Documentation of some BITs is skipped as they are marked as reserved and
-their usage is not clear (RES 11:9 GENPHY 16:13 RES1 19:17)
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/mdio/mdio-ipq4019.c | 109 ++++++++++++++++++++++++++++++--
- 1 file changed, 103 insertions(+), 6 deletions(-)
-
---- a/drivers/net/mdio/mdio-ipq4019.c
-+++ b/drivers/net/mdio/mdio-ipq4019.c
-@@ -14,6 +14,20 @@
- #include <linux/clk.h>
-
- #define MDIO_MODE_REG 0x40
-+#define MDIO_MODE_MDC_MODE BIT(12)
-+/* 0 = Clause 22, 1 = Clause 45 */
-+#define MDIO_MODE_C45 BIT(8)
-+#define MDIO_MODE_DIV_MASK GENMASK(7, 0)
-+#define MDIO_MODE_DIV(x) FIELD_PREP(MDIO_MODE_DIV_MASK, (x) - 1)
-+#define MDIO_MODE_DIV_1 0x0
-+#define MDIO_MODE_DIV_2 0x1
-+#define MDIO_MODE_DIV_4 0x3
-+#define MDIO_MODE_DIV_8 0x7
-+#define MDIO_MODE_DIV_16 0xf
-+#define MDIO_MODE_DIV_32 0x1f
-+#define MDIO_MODE_DIV_64 0x3f
-+#define MDIO_MODE_DIV_128 0x7f
-+#define MDIO_MODE_DIV_256 0xff
- #define MDIO_ADDR_REG 0x44
- #define MDIO_DATA_WRITE_REG 0x48
- #define MDIO_DATA_READ_REG 0x4c
-@@ -26,9 +40,6 @@
- #define MDIO_CMD_ACCESS_CODE_C45_WRITE 1
- #define MDIO_CMD_ACCESS_CODE_C45_READ 2
-
--/* 0 = Clause 22, 1 = Clause 45 */
--#define MDIO_MODE_C45 BIT(8)
--
- #define IPQ4019_MDIO_TIMEOUT 10000
- #define IPQ4019_MDIO_SLEEP 10
-
-@@ -41,6 +52,7 @@ struct ipq4019_mdio_data {
- void __iomem *membase;
- void __iomem *eth_ldo_rdy;
- struct clk *mdio_clk;
-+ unsigned int mdc_rate;
- };
-
- static int ipq4019_mdio_wait_busy(struct mii_bus *bus)
-@@ -203,6 +215,38 @@ static int ipq4019_mdio_write_c22(struct
- return 0;
- }
-
-+static int ipq4019_mdio_set_div(struct ipq4019_mdio_data *priv)
-+{
-+ unsigned long ahb_rate;
-+ int div;
-+ u32 val;
-+
-+ /* If we don't have a clock for AHB use the fixed value */
-+ ahb_rate = IPQ_MDIO_CLK_RATE;
-+ if (priv->mdio_clk)
-+ ahb_rate = clk_get_rate(priv->mdio_clk);
-+
-+ /* MDC rate is ahb_rate/(MDIO_MODE_DIV + 1)
-+ * While supported, internal documentation doesn't
-+ * assure correct functionality of the MDIO bus
-+ * with divider of 1, 2 or 4.
-+ */
-+ for (div = 8; div <= 256; div *= 2) {
-+ /* The requested rate is supported by the div */
-+ if (priv->mdc_rate == DIV_ROUND_UP(ahb_rate, div)) {
-+ val = readl(priv->membase + MDIO_MODE_REG);
-+ val &= ~MDIO_MODE_DIV_MASK;
-+ val |= MDIO_MODE_DIV(div);
-+ writel(val, priv->membase + MDIO_MODE_REG);
-+
-+ return 0;
-+ }
-+ }
-+
-+ /* The requested rate is not supported */
-+ return -EINVAL;
-+}
-+
- static int ipq_mdio_reset(struct mii_bus *bus)
- {
- struct ipq4019_mdio_data *priv = bus->priv;
-@@ -225,10 +269,58 @@ static int ipq_mdio_reset(struct mii_bus
- return ret;
-
- ret = clk_prepare_enable(priv->mdio_clk);
-- if (ret == 0)
-- mdelay(10);
-+ if (ret)
-+ return ret;
-+
-+ mdelay(10);
-
-- return ret;
-+ /* Restore MDC rate */
-+ return ipq4019_mdio_set_div(priv);
-+}
-+
-+static void ipq4019_mdio_select_mdc_rate(struct platform_device *pdev,
-+ struct ipq4019_mdio_data *priv)
-+{
-+ unsigned long ahb_rate;
-+ int div;
-+ u32 val;
-+
-+ /* MDC rate defined in DT, we don't have to decide a default value */
-+ if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency",
-+ &priv->mdc_rate))
-+ return;
-+
-+ /* If we don't have a clock for AHB use the fixed value */
-+ ahb_rate = IPQ_MDIO_CLK_RATE;
-+ if (priv->mdio_clk)
-+ ahb_rate = clk_get_rate(priv->mdio_clk);
-+
-+ /* Check what is the current div set */
-+ val = readl(priv->membase + MDIO_MODE_REG);
-+ div = FIELD_GET(MDIO_MODE_DIV_MASK, val);
-+
-+ /* div is not set to the default value of /256
-+ * Probably someone changed that (bootloader, other drivers)
-+ * Keep this and don't overwrite it.
-+ */
-+ if (div != MDIO_MODE_DIV_256) {
-+ priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div + 1);
-+ return;
-+ }
-+
-+ /* If div is /256 assume nobody have set this value and
-+ * try to find one MDC rate that is close the 802.3 spec of
-+ * 2.5MHz
-+ */
-+ for (div = 256; div >= 8; div /= 2) {
-+ /* Stop as soon as we found a divider that
-+ * reached the closest value to 2.5MHz
-+ */
-+ if (DIV_ROUND_UP(ahb_rate, div) > 2500000)
-+ break;
-+
-+ priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div);
-+ }
- }
-
- static int ipq4019_mdio_probe(struct platform_device *pdev)
-@@ -252,6 +344,11 @@ static int ipq4019_mdio_probe(struct pla
- if (IS_ERR(priv->mdio_clk))
- return PTR_ERR(priv->mdio_clk);
-
-+ ipq4019_mdio_select_mdc_rate(pdev, priv);
-+ ret = ipq4019_mdio_set_div(priv);
-+ if (ret)
-+ return ret;
-+
- /* The platform resource is provided on the chipset IPQ5018 */
- /* This resource is optional */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+++ /dev/null
-From 7edce370d87a23e8ed46af5b76a9fef1e341b67b Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 28 Nov 2023 14:59:28 +0100
-Subject: [PATCH] net: phy: aquantia: drop wrong endianness conversion for addr
- and CRC
-
-On further testing on BE target with kernel test robot, it was notice
-that the endianness conversion for addr and CRC in fw_load_memory was
-wrong.
-
-Drop the cpu_to_le32 conversion for addr load as it's not needed.
-
-Use get_unaligned_le32 instead of get_unaligned for FW data word load to
-correctly convert data in the correct order to follow system endian.
-
-Also drop the cpu_to_be32 for CRC calculation as it's wrong and would
-cause different CRC on BE system.
-The loaded word is swapped internally and MAILBOX calculates the CRC on
-the swapped word. To correctly calculate the CRC to be later matched
-with the one from MAILBOX, use an u8 struct and swap the word there to
-keep the same order on both LE and BE for crc_ccitt_false function.
-Also add additional comments on how the CRC verification for the loaded
-section works.
-
-CRC is calculated as we load the section and verified with the MAILBOX
-only after the entire section is loaded to skip additional slowdown by
-loop the section data again.
-
-Reported-by: kernel test robot <lkp@intel.com>
-Closes: https://lore.kernel.org/oe-kbuild-all/202311210414.sEJZjlcD-lkp@intel.com/
-Fixes: e93984ebc1c8 ("net: phy: aquantia: add firmware load support")
-Tested-by: Robert Marko <robimarko@gmail.com> # ipq8072 LE device
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Link: https://lore.kernel.org/r/20231128135928.9841-1-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/aquantia/aquantia_firmware.c | 24 ++++++++++++--------
- 1 file changed, 14 insertions(+), 10 deletions(-)
-
---- a/drivers/net/phy/aquantia/aquantia_firmware.c
-+++ b/drivers/net/phy/aquantia/aquantia_firmware.c
-@@ -93,9 +93,6 @@ static int aqr_fw_load_memory(struct phy
- u16 crc = 0, up_crc;
- size_t pos;
-
-- /* PHY expect addr in LE */
-- addr = (__force u32)cpu_to_le32(addr);
--
- phy_write_mmd(phydev, MDIO_MMD_VEND1,
- VEND1_GLOBAL_MAILBOX_INTERFACE1,
- VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET);
-@@ -110,10 +107,11 @@ static int aqr_fw_load_memory(struct phy
- * If a firmware that is not word aligned is found, please report upstream.
- */
- for (pos = 0; pos < len; pos += sizeof(u32)) {
-+ u8 crc_data[4];
- u32 word;
-
- /* FW data is always stored in little-endian */
-- word = get_unaligned((const u32 *)(data + pos));
-+ word = get_unaligned_le32((const u32 *)(data + pos));
-
- phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5,
- VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word));
-@@ -124,15 +122,21 @@ static int aqr_fw_load_memory(struct phy
- VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE |
- VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE);
-
-- /* calculate CRC as we load data to the mailbox.
-- * We convert word to big-endian as PHY is BE and mailbox will
-- * return a BE CRC.
-+ /* Word is swapped internally and MAILBOX CRC is calculated
-+ * using big-endian order. Mimic what the PHY does to have a
-+ * matching CRC...
- */
-- word = (__force u32)cpu_to_be32(word);
-- crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word));
-- }
-+ crc_data[0] = word >> 24;
-+ crc_data[1] = word >> 16;
-+ crc_data[2] = word >> 8;
-+ crc_data[3] = word;
-
-+ /* ...calculate CRC as we load data... */
-+ crc = crc_ccitt_false(crc, crc_data, sizeof(crc_data));
-+ }
-+ /* ...gets CRC from MAILBOX after we have loaded the entire section... */
- up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2);
-+ /* ...and make sure it does match our calculated CRC */
- if (crc != up_crc) {
- phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n",
- crc, up_crc);
+++ /dev/null
-From 27c42e925323b975a64429e313b0cf5c0c02a411 Mon Sep 17 00:00:00 2001
-From: Kathiravan Thirumoorthy <quic_kathirav@quicinc.com>
-Date: Mon, 25 Mar 2024 21:19:48 +0530
-Subject: dt-bindings: arm: qcom,ids: Add SoC ID for IPQ5321
-
-Add the ID for the Qualcomm IPQ5321 SoC.
-
-Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Reviewed-by: Mukesh Ojha <quic_mojha@quicinc.com>
-Signed-off-by: Kathiravan Thirumoorthy <quic_kathirav@quicinc.com>
-Link: https://lore.kernel.org/r/20240325-ipq5321-sku-support-v2-1-f30ce244732f@quicinc.com
-Signed-off-by: Bjorn Andersson <andersson@kernel.org>
----
- include/dt-bindings/arm/qcom,ids.h | 1 +
- 1 file changed, 1 insertion(+)
-
-(limited to 'include/dt-bindings/arm/qcom,ids.h')
-
---- a/include/dt-bindings/arm/qcom,ids.h
-+++ b/include/dt-bindings/arm/qcom,ids.h
-@@ -260,6 +260,7 @@
- #define QCOM_ID_IPQ5312 594
- #define QCOM_ID_IPQ5302 595
- #define QCOM_ID_IPQ5300 624
-+#define QCOM_ID_IPQ5321 650
-
- /*
- * The board type and revision information, used by Qualcomm bootloaders and
+++ /dev/null
-From cb77d0ad460e2c97a00c02ed78afdf45476e5e5f Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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(+)
-
---- 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 e184e8609f8c1cd9fef703f667245b6ebd89c2ed Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Tue, 3 Oct 2023 14:34:24 +0100
-Subject: [PATCH] net: sfp: re-implement ignoring the hardware TX_FAULT signal
-
-Re-implement how we ignore the hardware TX_FAULT signal. Rather than
-having a separate boolean for this, use a bitmask of the hardware
-signals that we wish to ignore. This gives more flexibility in the
-future to ignore other signals such as RX_LOS.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Tested-by: Christian Marangi <ansuelsmth@gmail.com>
-Link: https://lore.kernel.org/r/E1qnfXc-008UDY-91@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/sfp.c | 16 ++++++++--------
- 1 file changed, 8 insertions(+), 8 deletions(-)
-
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -257,6 +257,7 @@ struct sfp {
- unsigned int state_hw_drive;
- unsigned int state_hw_mask;
- unsigned int state_soft_mask;
-+ unsigned int state_ignore_mask;
- unsigned int state;
-
- struct delayed_work poll;
-@@ -280,7 +281,6 @@ struct sfp {
- unsigned int rs_state_mask;
-
- bool have_a2;
-- bool tx_fault_ignore;
-
- const struct sfp_quirk *quirk;
-
-@@ -347,7 +347,7 @@ static void sfp_fixup_long_startup(struc
-
- static void sfp_fixup_ignore_tx_fault(struct sfp *sfp)
- {
-- sfp->tx_fault_ignore = true;
-+ sfp->state_ignore_mask |= SFP_F_TX_FAULT;
- }
-
- // For 10GBASE-T short-reach modules
-@@ -796,7 +796,8 @@ static void sfp_soft_start_poll(struct s
-
- mutex_lock(&sfp->st_mutex);
- // Poll the soft state for hardware pins we want to ignore
-- sfp->state_soft_mask = ~sfp->state_hw_mask & mask;
-+ sfp->state_soft_mask = ~sfp->state_hw_mask & ~sfp->state_ignore_mask &
-+ mask;
-
- if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) &&
- !sfp->need_poll)
-@@ -2321,7 +2322,7 @@ static int sfp_sm_mod_probe(struct sfp *
- sfp->module_t_start_up = T_START_UP;
- sfp->module_t_wait = T_WAIT;
-
-- sfp->tx_fault_ignore = false;
-+ sfp->state_ignore_mask = 0;
-
- if (sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SFI ||
- sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SR ||
-@@ -2344,6 +2345,8 @@ static int sfp_sm_mod_probe(struct sfp *
-
- if (sfp->quirk && sfp->quirk->fixup)
- sfp->quirk->fixup(sfp);
-+
-+ sfp->state_hw_mask &= ~sfp->state_ignore_mask;
- mutex_unlock(&sfp->st_mutex);
-
- return 0;
-@@ -2844,10 +2847,7 @@ static void sfp_check_state(struct sfp *
- mutex_lock(&sfp->st_mutex);
- state = sfp_get_state(sfp);
- changed = state ^ sfp->state;
-- if (sfp->tx_fault_ignore)
-- changed &= SFP_F_PRESENT | SFP_F_LOS;
-- else
-- changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT;
-+ changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT;
-
- for (i = 0; i < GPIO_MAX; i++)
- if (changed & BIT(i))
+++ /dev/null
-From 380b50ae3a04222334a3779b3787eba844b1177f Mon Sep 17 00:00:00 2001
-From: Marco von Rosenberg <marcovr@selfnet.de>
-Date: Thu, 16 Nov 2023 20:32:31 +0100
-Subject: net: phy: broadcom: Wire suspend/resume for BCM54612E
-
-The BCM54612E ethernet PHY supports IDDQ-SR.
-Therefore wire-up the suspend and resume callbacks
-to point to bcm54xx_suspend() and bcm54xx_resume().
-
-Signed-off-by: Marco von Rosenberg <marcovr@selfnet.de>
-Acked-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/broadcom.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/drivers/net/phy/broadcom.c
-+++ b/drivers/net/phy/broadcom.c
-@@ -1061,6 +1061,8 @@ static struct phy_driver broadcom_driver
- .handle_interrupt = bcm_phy_handle_interrupt,
- .link_change_notify = bcm54xx_link_change_notify,
- .led_brightness_set = bcm_phy_led_brightness_set,
-+ .suspend = bcm54xx_suspend,
-+ .resume = bcm54xx_resume,
- }, {
- .phy_id = PHY_ID_BCM54616S,
- .phy_id_mask = 0xfffffff0,
+++ /dev/null
-From abb45a2477f533cd4aab3085defdff131e2e8c4f Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 6 May 2024 14:32:46 +0200
-Subject: [PATCH] net: stmmac: dwmac-ipq806x: account for rgmii-txid/rxid/id
- phy-mode
-
-Currently the ipq806x dwmac driver is almost always used attached to the
-CPU port of a switch and phy-mode was always set to "rgmii" or "sgmii".
-
-Some device came up with a special configuration where the PHY is
-directly attached to the GMAC port and in those case phy-mode needs to
-be set to "rgmii-id" to make the PHY correctly work and receive packets.
-
-Since the driver supports only "rgmii" and "sgmii" mode, when "rgmii-id"
-(or variants) mode is set, the mode is rejected and probe fails.
-
-Add support also for these phy-modes to correctly setup PHYs that requires
-delay applied to tx/rx.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c | 12 ++++++++++++
- 1 file changed, 12 insertions(+)
-
---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
-+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
-@@ -171,6 +171,9 @@ static int ipq806x_gmac_set_speed(struct
-
- switch (gmac->phy_mode) {
- case PHY_INTERFACE_MODE_RGMII:
-+ case PHY_INTERFACE_MODE_RGMII_ID:
-+ case PHY_INTERFACE_MODE_RGMII_RXID:
-+ case PHY_INTERFACE_MODE_RGMII_TXID:
- div = get_clk_div_rgmii(gmac, speed);
- clk_bits = NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) |
- NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id);
-@@ -412,6 +415,9 @@ static int ipq806x_gmac_probe(struct pla
- val |= NSS_COMMON_GMAC_CTL_CSYS_REQ;
- switch (gmac->phy_mode) {
- case PHY_INTERFACE_MODE_RGMII:
-+ case PHY_INTERFACE_MODE_RGMII_ID:
-+ case PHY_INTERFACE_MODE_RGMII_RXID:
-+ case PHY_INTERFACE_MODE_RGMII_TXID:
- val |= NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL;
- break;
- case PHY_INTERFACE_MODE_SGMII:
-@@ -427,6 +433,9 @@ static int ipq806x_gmac_probe(struct pla
- val &= ~(1 << NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id));
- switch (gmac->phy_mode) {
- case PHY_INTERFACE_MODE_RGMII:
-+ case PHY_INTERFACE_MODE_RGMII_ID:
-+ case PHY_INTERFACE_MODE_RGMII_RXID:
-+ case PHY_INTERFACE_MODE_RGMII_TXID:
- val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) <<
- NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id);
- break;
-@@ -444,6 +453,9 @@ static int ipq806x_gmac_probe(struct pla
- val |= NSS_COMMON_CLK_GATE_PTP_EN(gmac->id);
- switch (gmac->phy_mode) {
- case PHY_INTERFACE_MODE_RGMII:
-+ case PHY_INTERFACE_MODE_RGMII_ID:
-+ case PHY_INTERFACE_MODE_RGMII_RXID:
-+ case PHY_INTERFACE_MODE_RGMII_TXID:
- val |= NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) |
- NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id);
- break;
+++ /dev/null
-From 5d0fad48d2dec175ecb999974b94203c577973ef Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Wed, 8 May 2024 11:43:34 +0100
-Subject: [PATCH] net: ethernet: mediatek: split tx and rx fields in
- mtk_soc_data struct
-
-Split tx and rx fields in mtk_soc_data struct. This is a preliminary
-patch to roll back to ADMAv1 for MT7986 and MT7981 SoC in order to fix a
-hw hang if the device receives a corrupted packet when using ADMAv2.0.
-
-Fixes: 197c9e9b17b1 ("net: ethernet: mtk_eth_soc: introduce support for mt7986 chipset")
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
-Link: https://lore.kernel.org/r/70a799b1f060ec2f57883e88ccb420ac0fb0abb5.1715164770.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 210 ++++++++++++--------
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 29 +--
- 2 files changed, 139 insertions(+), 100 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1138,7 +1138,7 @@ static int mtk_init_fq_dma(struct mtk_et
- eth->scratch_ring = eth->sram_base;
- else
- eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
-- cnt * soc->txrx.txd_size,
-+ cnt * soc->tx.desc_size,
- ð->phy_scratch_ring,
- GFP_KERNEL);
- if (unlikely(!eth->scratch_ring))
-@@ -1154,16 +1154,16 @@ static int mtk_init_fq_dma(struct mtk_et
- if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
- return -ENOMEM;
-
-- phy_ring_tail = eth->phy_scratch_ring + soc->txrx.txd_size * (cnt - 1);
-+ phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1);
-
- for (i = 0; i < cnt; i++) {
- struct mtk_tx_dma_v2 *txd;
-
-- txd = eth->scratch_ring + i * soc->txrx.txd_size;
-+ txd = eth->scratch_ring + i * soc->tx.desc_size;
- txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
- if (i < cnt - 1)
- txd->txd2 = eth->phy_scratch_ring +
-- (i + 1) * soc->txrx.txd_size;
-+ (i + 1) * soc->tx.desc_size;
-
- txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
- txd->txd4 = 0;
-@@ -1412,7 +1412,7 @@ static int mtk_tx_map(struct sk_buff *sk
- if (itxd == ring->last_free)
- return -ENOMEM;
-
-- itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size);
-+ itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->tx.desc_size);
- memset(itx_buf, 0, sizeof(*itx_buf));
-
- txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size,
-@@ -1453,7 +1453,7 @@ static int mtk_tx_map(struct sk_buff *sk
-
- memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
- txd_info.size = min_t(unsigned int, frag_size,
-- soc->txrx.dma_max_len);
-+ soc->tx.dma_max_len);
- txd_info.qid = queue;
- txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 &&
- !(frag_size - txd_info.size);
-@@ -1466,7 +1466,7 @@ static int mtk_tx_map(struct sk_buff *sk
- mtk_tx_set_dma_desc(dev, txd, &txd_info);
-
- tx_buf = mtk_desc_to_tx_buf(ring, txd,
-- soc->txrx.txd_size);
-+ soc->tx.desc_size);
- if (new_desc)
- memset(tx_buf, 0, sizeof(*tx_buf));
- tx_buf->data = (void *)MTK_DMA_DUMMY_DESC;
-@@ -1509,7 +1509,7 @@ static int mtk_tx_map(struct sk_buff *sk
- } else {
- int next_idx;
-
-- next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->txrx.txd_size),
-+ next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->tx.desc_size),
- ring->dma_size);
- mtk_w32(eth, next_idx, MT7628_TX_CTX_IDX0);
- }
-@@ -1518,7 +1518,7 @@ static int mtk_tx_map(struct sk_buff *sk
-
- err_dma:
- do {
-- tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size);
-+ tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->tx.desc_size);
-
- /* unmap dma */
- mtk_tx_unmap(eth, tx_buf, NULL, false);
-@@ -1543,7 +1543,7 @@ static int mtk_cal_txd_req(struct mtk_et
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- frag = &skb_shinfo(skb)->frags[i];
- nfrags += DIV_ROUND_UP(skb_frag_size(frag),
-- eth->soc->txrx.dma_max_len);
-+ eth->soc->tx.dma_max_len);
- }
- } else {
- nfrags += skb_shinfo(skb)->nr_frags;
-@@ -1650,7 +1650,7 @@ static struct mtk_rx_ring *mtk_get_rx_ri
-
- ring = ð->rx_ring[i];
- idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
-- rxd = ring->dma + idx * eth->soc->txrx.rxd_size;
-+ rxd = ring->dma + idx * eth->soc->rx.desc_size;
- if (rxd->rxd2 & RX_DMA_DONE) {
- ring->calc_idx_update = true;
- return ring;
-@@ -1818,7 +1818,7 @@ static int mtk_xdp_submit_frame(struct m
- }
- htxd = txd;
-
-- tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size);
-+ tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->tx.desc_size);
- memset(tx_buf, 0, sizeof(*tx_buf));
- htx_buf = tx_buf;
-
-@@ -1837,7 +1837,7 @@ static int mtk_xdp_submit_frame(struct m
- goto unmap;
-
- tx_buf = mtk_desc_to_tx_buf(ring, txd,
-- soc->txrx.txd_size);
-+ soc->tx.desc_size);
- memset(tx_buf, 0, sizeof(*tx_buf));
- n_desc++;
- }
-@@ -1875,7 +1875,7 @@ static int mtk_xdp_submit_frame(struct m
- } else {
- int idx;
-
-- idx = txd_to_idx(ring, txd, soc->txrx.txd_size);
-+ idx = txd_to_idx(ring, txd, soc->tx.desc_size);
- mtk_w32(eth, NEXT_DESP_IDX(idx, ring->dma_size),
- MT7628_TX_CTX_IDX0);
- }
-@@ -1886,7 +1886,7 @@ static int mtk_xdp_submit_frame(struct m
-
- unmap:
- while (htxd != txd) {
-- tx_buf = mtk_desc_to_tx_buf(ring, htxd, soc->txrx.txd_size);
-+ tx_buf = mtk_desc_to_tx_buf(ring, htxd, soc->tx.desc_size);
- mtk_tx_unmap(eth, tx_buf, NULL, false);
-
- htxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
-@@ -2017,7 +2017,7 @@ static int mtk_poll_rx(struct napi_struc
- goto rx_done;
-
- idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
-- rxd = ring->dma + idx * eth->soc->txrx.rxd_size;
-+ rxd = ring->dma + idx * eth->soc->rx.desc_size;
- data = ring->data[idx];
-
- if (!mtk_rx_get_desc(eth, &trxd, rxd))
-@@ -2152,7 +2152,7 @@ static int mtk_poll_rx(struct napi_struc
- rxdcsum = &trxd.rxd4;
- }
-
-- if (*rxdcsum & eth->soc->txrx.rx_dma_l4_valid)
-+ if (*rxdcsum & eth->soc->rx.dma_l4_valid)
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- else
- skb_checksum_none_assert(skb);
-@@ -2276,7 +2276,7 @@ static int mtk_poll_tx_qdma(struct mtk_e
- break;
-
- tx_buf = mtk_desc_to_tx_buf(ring, desc,
-- eth->soc->txrx.txd_size);
-+ eth->soc->tx.desc_size);
- if (!tx_buf->data)
- break;
-
-@@ -2327,7 +2327,7 @@ static int mtk_poll_tx_pdma(struct mtk_e
- }
- mtk_tx_unmap(eth, tx_buf, &bq, true);
-
-- desc = ring->dma + cpu * eth->soc->txrx.txd_size;
-+ desc = ring->dma + cpu * eth->soc->tx.desc_size;
- ring->last_free = desc;
- atomic_inc(&ring->free_count);
-
-@@ -2417,7 +2417,7 @@ static int mtk_napi_rx(struct napi_struc
- do {
- int rx_done;
-
-- mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask,
-+ mtk_w32(eth, eth->soc->rx.irq_done_mask,
- reg_map->pdma.irq_status);
- rx_done = mtk_poll_rx(napi, budget - rx_done_total, eth);
- rx_done_total += rx_done;
-@@ -2433,10 +2433,10 @@ static int mtk_napi_rx(struct napi_struc
- return budget;
-
- } while (mtk_r32(eth, reg_map->pdma.irq_status) &
-- eth->soc->txrx.rx_irq_done_mask);
-+ eth->soc->rx.irq_done_mask);
-
- if (napi_complete_done(napi, rx_done_total))
-- mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask);
-+ mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask);
-
- return rx_done_total;
- }
-@@ -2445,7 +2445,7 @@ static int mtk_tx_alloc(struct mtk_eth *
- {
- const struct mtk_soc_data *soc = eth->soc;
- struct mtk_tx_ring *ring = ð->tx_ring;
-- int i, sz = soc->txrx.txd_size;
-+ int i, sz = soc->tx.desc_size;
- struct mtk_tx_dma_v2 *txd;
- int ring_size;
- u32 ofs, val;
-@@ -2568,14 +2568,14 @@ static void mtk_tx_clean(struct mtk_eth
- }
- if (!MTK_HAS_CAPS(soc->caps, MTK_SRAM) && ring->dma) {
- dma_free_coherent(eth->dma_dev,
-- ring->dma_size * soc->txrx.txd_size,
-+ ring->dma_size * soc->tx.desc_size,
- ring->dma, ring->phys);
- ring->dma = NULL;
- }
-
- if (ring->dma_pdma) {
- dma_free_coherent(eth->dma_dev,
-- ring->dma_size * soc->txrx.txd_size,
-+ ring->dma_size * soc->tx.desc_size,
- ring->dma_pdma, ring->phys_pdma);
- ring->dma_pdma = NULL;
- }
-@@ -2630,15 +2630,15 @@ static int mtk_rx_alloc(struct mtk_eth *
- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM) ||
- rx_flag != MTK_RX_FLAGS_NORMAL) {
- ring->dma = dma_alloc_coherent(eth->dma_dev,
-- rx_dma_size * eth->soc->txrx.rxd_size,
-- &ring->phys, GFP_KERNEL);
-+ rx_dma_size * eth->soc->rx.desc_size,
-+ &ring->phys, GFP_KERNEL);
- } else {
- struct mtk_tx_ring *tx_ring = ð->tx_ring;
-
- ring->dma = tx_ring->dma + tx_ring_size *
-- eth->soc->txrx.txd_size * (ring_no + 1);
-+ eth->soc->tx.desc_size * (ring_no + 1);
- ring->phys = tx_ring->phys + tx_ring_size *
-- eth->soc->txrx.txd_size * (ring_no + 1);
-+ eth->soc->tx.desc_size * (ring_no + 1);
- }
-
- if (!ring->dma)
-@@ -2649,7 +2649,7 @@ static int mtk_rx_alloc(struct mtk_eth *
- dma_addr_t dma_addr;
- void *data;
-
-- rxd = ring->dma + i * eth->soc->txrx.rxd_size;
-+ rxd = ring->dma + i * eth->soc->rx.desc_size;
- if (ring->page_pool) {
- data = mtk_page_pool_get_buff(ring->page_pool,
- &dma_addr, GFP_KERNEL);
-@@ -2740,7 +2740,7 @@ static void mtk_rx_clean(struct mtk_eth
- if (!ring->data[i])
- continue;
-
-- rxd = ring->dma + i * eth->soc->txrx.rxd_size;
-+ rxd = ring->dma + i * eth->soc->rx.desc_size;
- if (!rxd->rxd1)
- continue;
-
-@@ -2757,7 +2757,7 @@ static void mtk_rx_clean(struct mtk_eth
-
- if (!in_sram && ring->dma) {
- dma_free_coherent(eth->dma_dev,
-- ring->dma_size * eth->soc->txrx.rxd_size,
-+ ring->dma_size * eth->soc->rx.desc_size,
- ring->dma, ring->phys);
- ring->dma = NULL;
- }
-@@ -3120,7 +3120,7 @@ static void mtk_dma_free(struct mtk_eth
- netdev_reset_queue(eth->netdev[i]);
- if (!MTK_HAS_CAPS(soc->caps, MTK_SRAM) && eth->scratch_ring) {
- dma_free_coherent(eth->dma_dev,
-- MTK_QDMA_RING_SIZE * soc->txrx.txd_size,
-+ MTK_QDMA_RING_SIZE * soc->tx.desc_size,
- eth->scratch_ring, eth->phy_scratch_ring);
- eth->scratch_ring = NULL;
- eth->phy_scratch_ring = 0;
-@@ -3170,7 +3170,7 @@ static irqreturn_t mtk_handle_irq_rx(int
-
- eth->rx_events++;
- if (likely(napi_schedule_prep(ð->rx_napi))) {
-- mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
-+ mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
- __napi_schedule(ð->rx_napi);
- }
-
-@@ -3196,9 +3196,9 @@ static irqreturn_t mtk_handle_irq(int ir
- const struct mtk_reg_map *reg_map = eth->soc->reg_map;
-
- if (mtk_r32(eth, reg_map->pdma.irq_mask) &
-- eth->soc->txrx.rx_irq_done_mask) {
-+ eth->soc->rx.irq_done_mask) {
- if (mtk_r32(eth, reg_map->pdma.irq_status) &
-- eth->soc->txrx.rx_irq_done_mask)
-+ eth->soc->rx.irq_done_mask)
- mtk_handle_irq_rx(irq, _eth);
- }
- if (mtk_r32(eth, reg_map->tx_irq_mask) & MTK_TX_DONE_INT) {
-@@ -3216,10 +3216,10 @@ static void mtk_poll_controller(struct n
- struct mtk_eth *eth = mac->hw;
-
- mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
-- mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
-+ mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
- mtk_handle_irq_rx(eth->irq[2], dev);
- mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
-- mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask);
-+ mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask);
- }
- #endif
-
-@@ -3383,7 +3383,7 @@ static int mtk_open(struct net_device *d
- napi_enable(ð->tx_napi);
- napi_enable(ð->rx_napi);
- mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
-- mtk_rx_irq_enable(eth, soc->txrx.rx_irq_done_mask);
-+ mtk_rx_irq_enable(eth, soc->rx.irq_done_mask);
- refcount_set(ð->dma_refcnt, 1);
- }
- else
-@@ -3467,7 +3467,7 @@ static int mtk_stop(struct net_device *d
- mtk_gdm_config(eth, MTK_GDMA_DROP_ALL);
-
- mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
-- mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
-+ mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
- napi_disable(ð->tx_napi);
- napi_disable(ð->rx_napi);
-
-@@ -3943,9 +3943,9 @@ static int mtk_hw_init(struct mtk_eth *e
-
- /* FE int grouping */
- mtk_w32(eth, MTK_TX_DONE_INT, reg_map->pdma.int_grp);
-- mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->pdma.int_grp + 4);
-+ mtk_w32(eth, eth->soc->rx.irq_done_mask, reg_map->pdma.int_grp + 4);
- mtk_w32(eth, MTK_TX_DONE_INT, reg_map->qdma.int_grp);
-- mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->qdma.int_grp + 4);
-+ mtk_w32(eth, eth->soc->rx.irq_done_mask, reg_map->qdma.int_grp + 4);
- mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
-
- if (mtk_is_netsys_v3_or_greater(eth)) {
-@@ -5037,11 +5037,15 @@ static const struct mtk_soc_data mt2701_
- .required_clks = MT7623_CLKS_BITMAP,
- .required_pctl = true,
- .version = 1,
-- .txrx = {
-- .txd_size = sizeof(struct mtk_tx_dma),
-- .rxd_size = sizeof(struct mtk_rx_dma),
-- .rx_irq_done_mask = MTK_RX_DONE_INT,
-- .rx_dma_l4_valid = RX_DMA_L4_VALID,
-+ .tx = {
-+ .desc_size = sizeof(struct mtk_tx_dma),
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN,
-+ .dma_len_offset = 16,
-+ },
-+ .rx = {
-+ .desc_size = sizeof(struct mtk_rx_dma),
-+ .irq_done_mask = MTK_RX_DONE_INT,
-+ .dma_l4_valid = RX_DMA_L4_VALID,
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
- },
-@@ -5057,11 +5061,15 @@ static const struct mtk_soc_data mt7621_
- .offload_version = 1,
- .hash_offset = 2,
- .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
-- .txrx = {
-- .txd_size = sizeof(struct mtk_tx_dma),
-- .rxd_size = sizeof(struct mtk_rx_dma),
-- .rx_irq_done_mask = MTK_RX_DONE_INT,
-- .rx_dma_l4_valid = RX_DMA_L4_VALID,
-+ .tx = {
-+ .desc_size = sizeof(struct mtk_tx_dma),
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN,
-+ .dma_len_offset = 16,
-+ },
-+ .rx = {
-+ .desc_size = sizeof(struct mtk_rx_dma),
-+ .irq_done_mask = MTK_RX_DONE_INT,
-+ .dma_l4_valid = RX_DMA_L4_VALID,
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
- },
-@@ -5079,11 +5087,15 @@ static const struct mtk_soc_data mt7622_
- .hash_offset = 2,
- .has_accounting = true,
- .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
-- .txrx = {
-- .txd_size = sizeof(struct mtk_tx_dma),
-- .rxd_size = sizeof(struct mtk_rx_dma),
-- .rx_irq_done_mask = MTK_RX_DONE_INT,
-- .rx_dma_l4_valid = RX_DMA_L4_VALID,
-+ .tx = {
-+ .desc_size = sizeof(struct mtk_tx_dma),
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN,
-+ .dma_len_offset = 16,
-+ },
-+ .rx = {
-+ .desc_size = sizeof(struct mtk_rx_dma),
-+ .irq_done_mask = MTK_RX_DONE_INT,
-+ .dma_l4_valid = RX_DMA_L4_VALID,
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
- },
-@@ -5100,11 +5112,15 @@ static const struct mtk_soc_data mt7623_
- .hash_offset = 2,
- .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
- .disable_pll_modes = true,
-- .txrx = {
-- .txd_size = sizeof(struct mtk_tx_dma),
-- .rxd_size = sizeof(struct mtk_rx_dma),
-- .rx_irq_done_mask = MTK_RX_DONE_INT,
-- .rx_dma_l4_valid = RX_DMA_L4_VALID,
-+ .tx = {
-+ .desc_size = sizeof(struct mtk_tx_dma),
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN,
-+ .dma_len_offset = 16,
-+ },
-+ .rx = {
-+ .desc_size = sizeof(struct mtk_rx_dma),
-+ .irq_done_mask = MTK_RX_DONE_INT,
-+ .dma_l4_valid = RX_DMA_L4_VALID,
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
- },
-@@ -5119,11 +5135,15 @@ static const struct mtk_soc_data mt7629_
- .required_pctl = false,
- .has_accounting = true,
- .version = 1,
-- .txrx = {
-- .txd_size = sizeof(struct mtk_tx_dma),
-- .rxd_size = sizeof(struct mtk_rx_dma),
-- .rx_irq_done_mask = MTK_RX_DONE_INT,
-- .rx_dma_l4_valid = RX_DMA_L4_VALID,
-+ .tx = {
-+ .desc_size = sizeof(struct mtk_tx_dma),
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN,
-+ .dma_len_offset = 16,
-+ },
-+ .rx = {
-+ .desc_size = sizeof(struct mtk_rx_dma),
-+ .irq_done_mask = MTK_RX_DONE_INT,
-+ .dma_l4_valid = RX_DMA_L4_VALID,
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
- },
-@@ -5141,11 +5161,15 @@ static const struct mtk_soc_data mt7981_
- .hash_offset = 4,
- .has_accounting = true,
- .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
-- .txrx = {
-- .txd_size = sizeof(struct mtk_tx_dma_v2),
-- .rxd_size = sizeof(struct mtk_rx_dma_v2),
-- .rx_irq_done_mask = MTK_RX_DONE_INT_V2,
-- .rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
-+ .tx = {
-+ .desc_size = sizeof(struct mtk_tx_dma_v2),
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
-+ .dma_len_offset = 8,
-+ },
-+ .rx = {
-+ .desc_size = sizeof(struct mtk_rx_dma_v2),
-+ .irq_done_mask = MTK_RX_DONE_INT_V2,
-+ .dma_l4_valid = RX_DMA_L4_VALID_V2,
- .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
- .dma_len_offset = 8,
- },
-@@ -5163,11 +5187,15 @@ static const struct mtk_soc_data mt7986_
- .hash_offset = 4,
- .has_accounting = true,
- .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
-- .txrx = {
-- .txd_size = sizeof(struct mtk_tx_dma_v2),
-- .rxd_size = sizeof(struct mtk_rx_dma_v2),
-- .rx_irq_done_mask = MTK_RX_DONE_INT_V2,
-- .rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
-+ .tx = {
-+ .desc_size = sizeof(struct mtk_tx_dma_v2),
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
-+ .dma_len_offset = 8,
-+ },
-+ .rx = {
-+ .desc_size = sizeof(struct mtk_rx_dma_v2),
-+ .irq_done_mask = MTK_RX_DONE_INT_V2,
-+ .dma_l4_valid = RX_DMA_L4_VALID_V2,
- .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
- .dma_len_offset = 8,
- },
-@@ -5185,11 +5213,15 @@ static const struct mtk_soc_data mt7988_
- .hash_offset = 4,
- .has_accounting = true,
- .foe_entry_size = MTK_FOE_ENTRY_V3_SIZE,
-- .txrx = {
-- .txd_size = sizeof(struct mtk_tx_dma_v2),
-- .rxd_size = sizeof(struct mtk_rx_dma_v2),
-- .rx_irq_done_mask = MTK_RX_DONE_INT_V2,
-- .rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
-+ .tx = {
-+ .desc_size = sizeof(struct mtk_tx_dma_v2),
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
-+ .dma_len_offset = 8,
-+ },
-+ .rx = {
-+ .desc_size = sizeof(struct mtk_rx_dma_v2),
-+ .irq_done_mask = MTK_RX_DONE_INT_V2,
-+ .dma_l4_valid = RX_DMA_L4_VALID_V2,
- .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
- .dma_len_offset = 8,
- },
-@@ -5202,11 +5234,15 @@ static const struct mtk_soc_data rt5350_
- .required_clks = MT7628_CLKS_BITMAP,
- .required_pctl = false,
- .version = 1,
-- .txrx = {
-- .txd_size = sizeof(struct mtk_tx_dma),
-- .rxd_size = sizeof(struct mtk_rx_dma),
-- .rx_irq_done_mask = MTK_RX_DONE_INT,
-- .rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA,
-+ .tx = {
-+ .desc_size = sizeof(struct mtk_tx_dma),
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN,
-+ .dma_len_offset = 16,
-+ },
-+ .rx = {
-+ .desc_size = sizeof(struct mtk_rx_dma),
-+ .irq_done_mask = MTK_RX_DONE_INT,
-+ .dma_l4_valid = RX_DMA_L4_VALID_PDMA,
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
- },
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -327,8 +327,8 @@
- /* QDMA descriptor txd3 */
- #define TX_DMA_OWNER_CPU BIT(31)
- #define TX_DMA_LS0 BIT(30)
--#define TX_DMA_PLEN0(x) (((x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset)
--#define TX_DMA_PLEN1(x) ((x) & eth->soc->txrx.dma_max_len)
-+#define TX_DMA_PLEN0(x) (((x) & eth->soc->tx.dma_max_len) << eth->soc->tx.dma_len_offset)
-+#define TX_DMA_PLEN1(x) ((x) & eth->soc->tx.dma_max_len)
- #define TX_DMA_SWC BIT(14)
- #define TX_DMA_PQID GENMASK(3, 0)
- #define TX_DMA_ADDR64_MASK GENMASK(3, 0)
-@@ -348,8 +348,8 @@
- /* QDMA descriptor rxd2 */
- #define RX_DMA_DONE BIT(31)
- #define RX_DMA_LSO BIT(30)
--#define RX_DMA_PREP_PLEN0(x) (((x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset)
--#define RX_DMA_GET_PLEN0(x) (((x) >> eth->soc->txrx.dma_len_offset) & eth->soc->txrx.dma_max_len)
-+#define RX_DMA_PREP_PLEN0(x) (((x) & eth->soc->rx.dma_max_len) << eth->soc->rx.dma_len_offset)
-+#define RX_DMA_GET_PLEN0(x) (((x) >> eth->soc->rx.dma_len_offset) & eth->soc->rx.dma_max_len)
- #define RX_DMA_VTAG BIT(15)
- #define RX_DMA_ADDR64_MASK GENMASK(3, 0)
- #if IS_ENABLED(CONFIG_64BIT)
-@@ -1153,10 +1153,9 @@ struct mtk_reg_map {
- * @foe_entry_size Foe table entry size.
- * @has_accounting Bool indicating support for accounting of
- * offloaded flows.
-- * @txd_size Tx DMA descriptor size.
-- * @rxd_size Rx DMA descriptor size.
-- * @rx_irq_done_mask Rx irq done register mask.
-- * @rx_dma_l4_valid Rx DMA valid register mask.
-+ * @desc_size Tx/Rx DMA descriptor size.
-+ * @irq_done_mask Rx irq done register mask.
-+ * @dma_l4_valid Rx DMA valid register mask.
- * @dma_max_len Max DMA tx/rx buffer length.
- * @dma_len_offset Tx/Rx DMA length field offset.
- */
-@@ -1174,13 +1173,17 @@ struct mtk_soc_data {
- bool has_accounting;
- bool disable_pll_modes;
- struct {
-- u32 txd_size;
-- u32 rxd_size;
-- u32 rx_irq_done_mask;
-- u32 rx_dma_l4_valid;
-+ u32 desc_size;
- u32 dma_max_len;
- u32 dma_len_offset;
-- } txrx;
-+ } tx;
-+ struct {
-+ u32 desc_size;
-+ u32 irq_done_mask;
-+ u32 dma_l4_valid;
-+ u32 dma_max_len;
-+ u32 dma_len_offset;
-+ } rx;
- };
-
- #define MTK_DMA_MONITOR_TIMEOUT msecs_to_jiffies(1000)
+++ /dev/null
-From 4d572e867bdb372bb4add39a0fa495c6a9c9a8da Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Wed, 8 May 2024 11:43:56 +0100
-Subject: [PATCH] net: ethernet: mediatek: use ADMAv1 instead of ADMAv2.0 on
- MT7981 and MT7986
-
-ADMAv2.0 is plagued by RX hangs which can't easily detected and happen upon
-receival of a corrupted Ethernet frame.
-
-Use ADMAv1 instead which is also still present and usable, and doesn't
-suffer from that problem.
-
-Fixes: 197c9e9b17b1 ("net: ethernet: mtk_eth_soc: introduce support for mt7986 chipset")
-Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/57cef74bbd0c243366ad1ff4221e3f72f437ec80.1715164770.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 46 ++++++++++-----------
- 1 file changed, 23 insertions(+), 23 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -110,16 +110,16 @@ static const struct mtk_reg_map mt7986_r
- .tx_irq_mask = 0x461c,
- .tx_irq_status = 0x4618,
- .pdma = {
-- .rx_ptr = 0x6100,
-- .rx_cnt_cfg = 0x6104,
-- .pcrx_ptr = 0x6108,
-- .glo_cfg = 0x6204,
-- .rst_idx = 0x6208,
-- .delay_irq = 0x620c,
-- .irq_status = 0x6220,
-- .irq_mask = 0x6228,
-- .adma_rx_dbg0 = 0x6238,
-- .int_grp = 0x6250,
-+ .rx_ptr = 0x4100,
-+ .rx_cnt_cfg = 0x4104,
-+ .pcrx_ptr = 0x4108,
-+ .glo_cfg = 0x4204,
-+ .rst_idx = 0x4208,
-+ .delay_irq = 0x420c,
-+ .irq_status = 0x4220,
-+ .irq_mask = 0x4228,
-+ .adma_rx_dbg0 = 0x4238,
-+ .int_grp = 0x4250,
- },
- .qdma = {
- .qtx_cfg = 0x4400,
-@@ -1106,7 +1106,7 @@ static bool mtk_rx_get_desc(struct mtk_e
- rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
- rxd->rxd3 = READ_ONCE(dma_rxd->rxd3);
- rxd->rxd4 = READ_ONCE(dma_rxd->rxd4);
-- if (mtk_is_netsys_v2_or_greater(eth)) {
-+ if (mtk_is_netsys_v3_or_greater(eth)) {
- rxd->rxd5 = READ_ONCE(dma_rxd->rxd5);
- rxd->rxd6 = READ_ONCE(dma_rxd->rxd6);
- }
-@@ -2024,7 +2024,7 @@ static int mtk_poll_rx(struct napi_struc
- break;
-
- /* find out which mac the packet come from. values start at 1 */
-- if (mtk_is_netsys_v2_or_greater(eth)) {
-+ if (mtk_is_netsys_v3_or_greater(eth)) {
- u32 val = RX_DMA_GET_SPORT_V2(trxd.rxd5);
-
- switch (val) {
-@@ -2136,7 +2136,7 @@ static int mtk_poll_rx(struct napi_struc
- skb->dev = netdev;
- bytes += skb->len;
-
-- if (mtk_is_netsys_v2_or_greater(eth)) {
-+ if (mtk_is_netsys_v3_or_greater(eth)) {
- reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON, trxd.rxd5);
- hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY;
- if (hash != MTK_RXD5_FOE_ENTRY)
-@@ -2686,7 +2686,7 @@ static int mtk_rx_alloc(struct mtk_eth *
-
- rxd->rxd3 = 0;
- rxd->rxd4 = 0;
-- if (mtk_is_netsys_v2_or_greater(eth)) {
-+ if (mtk_is_netsys_v3_or_greater(eth)) {
- rxd->rxd5 = 0;
- rxd->rxd6 = 0;
- rxd->rxd7 = 0;
-@@ -3889,7 +3889,7 @@ static int mtk_hw_init(struct mtk_eth *e
- else
- mtk_hw_reset(eth);
-
-- if (mtk_is_netsys_v2_or_greater(eth)) {
-+ if (mtk_is_netsys_v3_or_greater(eth)) {
- /* Set FE to PDMAv2 if necessary */
- val = mtk_r32(eth, MTK_FE_GLO_MISC);
- mtk_w32(eth, val | BIT(4), MTK_FE_GLO_MISC);
-@@ -5167,11 +5167,11 @@ static const struct mtk_soc_data mt7981_
- .dma_len_offset = 8,
- },
- .rx = {
-- .desc_size = sizeof(struct mtk_rx_dma_v2),
-- .irq_done_mask = MTK_RX_DONE_INT_V2,
-+ .desc_size = sizeof(struct mtk_rx_dma),
-+ .irq_done_mask = MTK_RX_DONE_INT,
- .dma_l4_valid = RX_DMA_L4_VALID_V2,
-- .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
-- .dma_len_offset = 8,
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN,
-+ .dma_len_offset = 16,
- },
- };
-
-@@ -5193,11 +5193,11 @@ static const struct mtk_soc_data mt7986_
- .dma_len_offset = 8,
- },
- .rx = {
-- .desc_size = sizeof(struct mtk_rx_dma_v2),
-- .irq_done_mask = MTK_RX_DONE_INT_V2,
-+ .desc_size = sizeof(struct mtk_rx_dma),
-+ .irq_done_mask = MTK_RX_DONE_INT,
- .dma_l4_valid = RX_DMA_L4_VALID_V2,
-- .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
-- .dma_len_offset = 8,
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN,
-+ .dma_len_offset = 16,
- },
- };
-
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 12 Sep 2023 10:22:56 +0200
-Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on mtk_pse_port definitions
- in mtk_flow_set_output_device
-
-Similar to ethernet ports, rely on mtk_pse_port definitions for
-pse wdma ports as well.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://lore.kernel.org/r/b86bdb717e963e3246c1dec5f736c810703cf056.1694506814.git.lorenzo@kernel.org
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -196,10 +196,10 @@ mtk_flow_set_output_device(struct mtk_et
- if (mtk_is_netsys_v2_or_greater(eth)) {
- switch (info.wdma_idx) {
- case 0:
-- pse_port = 8;
-+ pse_port = PSE_WDMA0_PORT;
- break;
- case 1:
-- pse_port = 9;
-+ pse_port = PSE_WDMA1_PORT;
- break;
- default:
- return -EINVAL;
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Wed, 13 Sep 2023 20:42:47 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: do not assume offload callbacks are
- always set
-
-Check if wlan.offload_enable and wlan.offload_disable callbacks are set
-in mtk_wed_flow_add/mtk_wed_flow_remove since mt7996 will not rely
-on them.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -1709,19 +1709,20 @@ mtk_wed_irq_set_mask(struct mtk_wed_devi
- int mtk_wed_flow_add(int index)
- {
- struct mtk_wed_hw *hw = hw_list[index];
-- int ret;
-+ int ret = 0;
-
-- if (!hw || !hw->wed_dev)
-- return -ENODEV;
-+ mutex_lock(&hw_lock);
-
-- if (hw->num_flows) {
-- hw->num_flows++;
-- return 0;
-+ if (!hw || !hw->wed_dev) {
-+ ret = -ENODEV;
-+ goto out;
- }
-
-- mutex_lock(&hw_lock);
-- if (!hw->wed_dev) {
-- ret = -ENODEV;
-+ if (!hw->wed_dev->wlan.offload_enable)
-+ goto out;
-+
-+ if (hw->num_flows) {
-+ hw->num_flows++;
- goto out;
- }
-
-@@ -1740,14 +1741,15 @@ void mtk_wed_flow_remove(int index)
- {
- struct mtk_wed_hw *hw = hw_list[index];
-
-- if (!hw)
-- return;
-+ mutex_lock(&hw_lock);
-
-- if (--hw->num_flows)
-- return;
-+ if (!hw || !hw->wed_dev)
-+ goto out;
-
-- mutex_lock(&hw_lock);
-- if (!hw->wed_dev)
-+ if (!hw->wed_dev->wlan.offload_disable)
-+ goto out;
-+
-+ if (--hw->num_flows)
- goto out;
-
- hw->wed_dev->wlan.offload_disable(hw->wed_dev);
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Mon, 18 Sep 2023 12:29:05 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: introduce versioning utility routines
-
-Similar to mtk_eth_soc, introduce the following wed versioning
-utility routines:
-- mtk_wed_is_v1
-- mtk_wed_is_v2
-
-This is a preliminary patch to introduce WED support for MT7988 SoC
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -278,7 +278,7 @@ mtk_wed_assign(struct mtk_wed_device *de
- if (!hw->wed_dev)
- goto out;
-
-- if (hw->version == 1)
-+ if (mtk_wed_is_v1(hw))
- return NULL;
-
- /* MT7986 WED devices do not have any pcie slot restrictions */
-@@ -359,7 +359,7 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
- desc->buf0 = cpu_to_le32(buf_phys);
- desc->buf1 = cpu_to_le32(buf_phys + txd_size);
-
-- if (dev->hw->version == 1)
-+ if (mtk_wed_is_v1(dev->hw))
- ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) |
- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
- MTK_WED_BUF_SIZE - txd_size) |
-@@ -498,7 +498,7 @@ mtk_wed_set_ext_int(struct mtk_wed_devic
- {
- u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
-
-- if (dev->hw->version == 1)
-+ if (mtk_wed_is_v1(dev->hw))
- mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR;
- else
- mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH |
-@@ -577,7 +577,7 @@ mtk_wed_dma_disable(struct mtk_wed_devic
- MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
- MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
-
-- if (dev->hw->version == 1) {
-+ if (mtk_wed_is_v1(dev->hw)) {
- regmap_write(dev->hw->mirror, dev->hw->index * 4, 0);
- wdma_clr(dev, MTK_WDMA_GLO_CFG,
- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
-@@ -606,7 +606,7 @@ mtk_wed_stop(struct mtk_wed_device *dev)
- wdma_w32(dev, MTK_WDMA_INT_MASK, 0);
- wdma_w32(dev, MTK_WDMA_INT_GRP2, 0);
-
-- if (dev->hw->version == 1)
-+ if (mtk_wed_is_v1(dev->hw))
- return;
-
- wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0);
-@@ -624,7 +624,7 @@ mtk_wed_deinit(struct mtk_wed_device *de
- MTK_WED_CTRL_WED_TX_BM_EN |
- MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
-
-- if (dev->hw->version == 1)
-+ if (mtk_wed_is_v1(dev->hw))
- return;
-
- wed_clr(dev, MTK_WED_CTRL,
-@@ -730,7 +730,7 @@ mtk_wed_bus_init(struct mtk_wed_device *
- static void
- mtk_wed_set_wpdma(struct mtk_wed_device *dev)
- {
-- if (dev->hw->version == 1) {
-+ if (mtk_wed_is_v1(dev->hw)) {
- wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys);
- } else {
- mtk_wed_bus_init(dev);
-@@ -761,7 +761,7 @@ mtk_wed_hw_init_early(struct mtk_wed_dev
- MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY;
- wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set);
-
-- if (dev->hw->version == 1) {
-+ if (mtk_wed_is_v1(dev->hw)) {
- u32 offset = dev->hw->index ? 0x04000400 : 0;
-
- wdma_set(dev, MTK_WDMA_GLO_CFG,
-@@ -934,7 +934,7 @@ mtk_wed_hw_init(struct mtk_wed_device *d
-
- wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
-
-- if (dev->hw->version == 1) {
-+ if (mtk_wed_is_v1(dev->hw)) {
- wed_w32(dev, MTK_WED_TX_BM_TKID,
- FIELD_PREP(MTK_WED_TX_BM_TKID_START,
- dev->wlan.token_start) |
-@@ -967,7 +967,7 @@ mtk_wed_hw_init(struct mtk_wed_device *d
-
- mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
-
-- if (dev->hw->version == 1) {
-+ if (mtk_wed_is_v1(dev->hw)) {
- wed_set(dev, MTK_WED_CTRL,
- MTK_WED_CTRL_WED_TX_BM_EN |
- MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
-@@ -1217,7 +1217,7 @@ mtk_wed_reset_dma(struct mtk_wed_device
- }
-
- dev->init_done = false;
-- if (dev->hw->version == 1)
-+ if (mtk_wed_is_v1(dev->hw))
- return;
-
- if (!busy) {
-@@ -1343,7 +1343,7 @@ mtk_wed_configure_irq(struct mtk_wed_dev
- MTK_WED_CTRL_WED_TX_BM_EN |
- MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
-
-- if (dev->hw->version == 1) {
-+ if (mtk_wed_is_v1(dev->hw)) {
- wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER,
- MTK_WED_PCIE_INT_TRIGGER_STATUS);
-
-@@ -1416,7 +1416,7 @@ mtk_wed_dma_enable(struct mtk_wed_device
- MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
- MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
-
-- if (dev->hw->version == 1) {
-+ if (mtk_wed_is_v1(dev->hw)) {
- wdma_set(dev, MTK_WDMA_GLO_CFG,
- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
- } else {
-@@ -1465,7 +1465,7 @@ mtk_wed_start(struct mtk_wed_device *dev
-
- mtk_wed_set_ext_int(dev, true);
-
-- if (dev->hw->version == 1) {
-+ if (mtk_wed_is_v1(dev->hw)) {
- u32 val = dev->wlan.wpdma_phys | MTK_PCIE_MIRROR_MAP_EN |
- FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID,
- dev->hw->index);
-@@ -1550,7 +1550,7 @@ mtk_wed_attach(struct mtk_wed_device *de
- }
-
- mtk_wed_hw_init_early(dev);
-- if (hw->version == 1) {
-+ if (mtk_wed_is_v1(hw)) {
- regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
- BIT(hw->index), 0);
- } else {
-@@ -1618,7 +1618,7 @@ static int
- mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
- {
- struct mtk_wed_ring *ring = &dev->txfree_ring;
-- int i, index = dev->hw->version == 1;
-+ int i, index = mtk_wed_is_v1(dev->hw);
-
- /*
- * For txfree event handling, the same DMA ring is shared between WED
-@@ -1676,7 +1676,7 @@ mtk_wed_irq_get(struct mtk_wed_device *d
- {
- u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
-
-- if (dev->hw->version == 1)
-+ if (mtk_wed_is_v1(dev->hw))
- ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR;
- else
- ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH |
-@@ -1842,7 +1842,7 @@ mtk_wed_setup_tc(struct mtk_wed_device *
- {
- struct mtk_wed_hw *hw = wed->hw;
-
-- if (hw->version < 2)
-+ if (mtk_wed_is_v1(hw))
- return -EOPNOTSUPP;
-
- switch (type) {
-@@ -1916,9 +1916,9 @@ void mtk_wed_add_hw(struct device_node *
- hw->wdma = wdma;
- hw->index = index;
- hw->irq = irq;
-- hw->version = mtk_is_netsys_v1(eth) ? 1 : 2;
-+ hw->version = eth->soc->version;
-
-- if (hw->version == 1) {
-+ if (mtk_wed_is_v1(hw)) {
- hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
- "mediatek,pcie-mirror");
- hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np,
---- a/drivers/net/ethernet/mediatek/mtk_wed.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.h
-@@ -40,6 +40,16 @@ struct mtk_wdma_info {
- };
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-+static inline bool mtk_wed_is_v1(struct mtk_wed_hw *hw)
-+{
-+ return hw->version == 1;
-+}
-+
-+static inline bool mtk_wed_is_v2(struct mtk_wed_hw *hw)
-+{
-+ return hw->version == 2;
-+}
-+
- static inline void
- wed_w32(struct mtk_wed_device *dev, u32 reg, u32 val)
- {
---- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-@@ -261,7 +261,7 @@ void mtk_wed_hw_add_debugfs(struct mtk_w
- debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg);
- debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval);
- debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops);
-- if (hw->version != 1)
-+ if (!mtk_wed_is_v1(hw))
- debugfs_create_file_unsafe("rxinfo", 0400, dir, hw,
- &wed_rxinfo_fops);
- }
---- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -207,7 +207,7 @@ int mtk_wed_mcu_msg_update(struct mtk_we
- {
- struct mtk_wed_wo *wo = dev->hw->wed_wo;
-
-- if (dev->hw->version == 1)
-+ if (mtk_wed_is_v1(dev->hw))
- return 0;
-
- if (WARN_ON(!wo))
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Mon, 18 Sep 2023 12:29:06 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: do not configure rx offload if not
- supported
-
-Check if rx offload is supported running mtk_wed_get_rx_capa routine
-before configuring it. This is a preliminary patch to introduce Wireless
-Ethernet Dispatcher (WED) support for MT7988 SoC.
-
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -606,7 +606,7 @@ mtk_wed_stop(struct mtk_wed_device *dev)
- wdma_w32(dev, MTK_WDMA_INT_MASK, 0);
- wdma_w32(dev, MTK_WDMA_INT_GRP2, 0);
-
-- if (mtk_wed_is_v1(dev->hw))
-+ if (!mtk_wed_get_rx_capa(dev))
- return;
-
- wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0);
-@@ -732,16 +732,21 @@ mtk_wed_set_wpdma(struct mtk_wed_device
- {
- if (mtk_wed_is_v1(dev->hw)) {
- wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys);
-- } else {
-- mtk_wed_bus_init(dev);
--
-- wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int);
-- wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask);
-- wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx);
-- wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree);
-- wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
-- wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx);
-+ return;
- }
-+
-+ mtk_wed_bus_init(dev);
-+
-+ wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int);
-+ wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask);
-+ wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx);
-+ wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree);
-+
-+ if (!mtk_wed_get_rx_capa(dev))
-+ return;
-+
-+ wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
-+ wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx);
- }
-
- static void
-@@ -973,15 +978,17 @@ mtk_wed_hw_init(struct mtk_wed_device *d
- MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
- } else {
- wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE);
-- /* rx hw init */
-- wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
-- MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
-- MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
-- wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
--
-- mtk_wed_rx_buffer_hw_init(dev);
-- mtk_wed_rro_hw_init(dev);
-- mtk_wed_route_qm_hw_init(dev);
-+ if (mtk_wed_get_rx_capa(dev)) {
-+ /* rx hw init */
-+ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
-+ MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
-+ MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
-+ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
-+
-+ mtk_wed_rx_buffer_hw_init(dev);
-+ mtk_wed_rro_hw_init(dev);
-+ mtk_wed_route_qm_hw_init(dev);
-+ }
- }
-
- wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE);
-@@ -1353,8 +1360,6 @@ mtk_wed_configure_irq(struct mtk_wed_dev
-
- wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
- } else {
-- wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE,
-- GENMASK(1, 0));
- /* initail tx interrupt trigger */
- wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX,
- MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN |
-@@ -1373,15 +1378,20 @@ mtk_wed_configure_irq(struct mtk_wed_dev
- FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG,
- dev->wlan.txfree_tbit));
-
-- wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX,
-- MTK_WED_WPDMA_INT_CTRL_RX0_EN |
-- MTK_WED_WPDMA_INT_CTRL_RX0_CLR |
-- MTK_WED_WPDMA_INT_CTRL_RX1_EN |
-- MTK_WED_WPDMA_INT_CTRL_RX1_CLR |
-- FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG,
-- dev->wlan.rx_tbit[0]) |
-- FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG,
-- dev->wlan.rx_tbit[1]));
-+ if (mtk_wed_get_rx_capa(dev)) {
-+ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX,
-+ MTK_WED_WPDMA_INT_CTRL_RX0_EN |
-+ MTK_WED_WPDMA_INT_CTRL_RX0_CLR |
-+ MTK_WED_WPDMA_INT_CTRL_RX1_EN |
-+ MTK_WED_WPDMA_INT_CTRL_RX1_CLR |
-+ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG,
-+ dev->wlan.rx_tbit[0]) |
-+ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG,
-+ dev->wlan.rx_tbit[1]));
-+
-+ wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE,
-+ GENMASK(1, 0));
-+ }
-
- wed_w32(dev, MTK_WED_WDMA_INT_CLR, wdma_mask);
- wed_set(dev, MTK_WED_WDMA_INT_CTRL,
-@@ -1400,6 +1410,8 @@ mtk_wed_configure_irq(struct mtk_wed_dev
- static void
- mtk_wed_dma_enable(struct mtk_wed_device *dev)
- {
-+ int i;
-+
- wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
-
- wed_set(dev, MTK_WED_GLO_CFG,
-@@ -1419,33 +1431,33 @@ mtk_wed_dma_enable(struct mtk_wed_device
- if (mtk_wed_is_v1(dev->hw)) {
- wdma_set(dev, MTK_WDMA_GLO_CFG,
- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
-- } else {
-- int i;
-+ return;
-+ }
-
-- wed_set(dev, MTK_WED_WPDMA_CTRL,
-- MTK_WED_WPDMA_CTRL_SDL1_FIXED);
-+ wed_set(dev, MTK_WED_WPDMA_CTRL,
-+ MTK_WED_WPDMA_CTRL_SDL1_FIXED);
-+ wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
-+ MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
-+ MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
-+ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
-+ MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
-+ MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
-
-- wed_set(dev, MTK_WED_WDMA_GLO_CFG,
-- MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
-- MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
-+ if (!mtk_wed_get_rx_capa(dev))
-+ return;
-
-- wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
-- MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
-- MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
--
-- wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
-- MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
-- MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
-+ wed_set(dev, MTK_WED_WDMA_GLO_CFG,
-+ MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
-+ MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
-
-- wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
-- MTK_WED_WPDMA_RX_D_RX_DRV_EN |
-- FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) |
-- FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL,
-- 0x2));
-+ wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
-+ MTK_WED_WPDMA_RX_D_RX_DRV_EN |
-+ FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) |
-+ FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL,
-+ 0x2));
-
-- for (i = 0; i < MTK_WED_RX_QUEUES; i++)
-- mtk_wed_check_wfdma_rx_fill(dev, i);
-- }
-+ for (i = 0; i < MTK_WED_RX_QUEUES; i++)
-+ mtk_wed_check_wfdma_rx_fill(dev, i);
- }
-
- static void
-@@ -1472,7 +1484,7 @@ mtk_wed_start(struct mtk_wed_device *dev
-
- val |= BIT(0) | (BIT(1) * !!dev->hw->index);
- regmap_write(dev->hw->mirror, dev->hw->index * 4, val);
-- } else {
-+ } else if (mtk_wed_get_rx_capa(dev)) {
- /* driver set mid ready and only once */
- wed_w32(dev, MTK_WED_EXT_INT_MASK1,
- MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
-@@ -1484,7 +1496,6 @@ mtk_wed_start(struct mtk_wed_device *dev
-
- if (mtk_wed_rro_cfg(dev))
- return;
--
- }
-
- mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
-@@ -1550,13 +1561,14 @@ mtk_wed_attach(struct mtk_wed_device *de
- }
-
- mtk_wed_hw_init_early(dev);
-- if (mtk_wed_is_v1(hw)) {
-+ if (mtk_wed_is_v1(hw))
- regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
- BIT(hw->index), 0);
-- } else {
-+ else
- dev->rev_id = wed_r32(dev, MTK_WED_REV_ID);
-+
-+ if (mtk_wed_get_rx_capa(dev))
- ret = mtk_wed_wo_init(hw);
-- }
- out:
- if (ret) {
- dev_err(dev->hw->dev, "failed to attach wed device\n");
---- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -207,7 +207,7 @@ int mtk_wed_mcu_msg_update(struct mtk_we
- {
- struct mtk_wed_wo *wo = dev->hw->wed_wo;
-
-- if (mtk_wed_is_v1(dev->hw))
-+ if (!mtk_wed_get_rx_capa(dev))
- return 0;
-
- if (WARN_ON(!wo))
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Mon, 18 Sep 2023 12:29:07 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: rename mtk_rxbm_desc in
- mtk_wed_bm_desc
-
-Rename mtk_rxbm_desc structure in mtk_wed_bm_desc since it will be used
-even on tx side by MT7988 SoC.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -422,7 +422,7 @@ free_pagelist:
- static int
- mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev)
- {
-- struct mtk_rxbm_desc *desc;
-+ struct mtk_wed_bm_desc *desc;
- dma_addr_t desc_phys;
-
- dev->rx_buf_ring.size = dev->wlan.rx_nbuf;
-@@ -442,7 +442,7 @@ mtk_wed_rx_buffer_alloc(struct mtk_wed_d
- static void
- mtk_wed_free_rx_buffer(struct mtk_wed_device *dev)
- {
-- struct mtk_rxbm_desc *desc = dev->rx_buf_ring.desc;
-+ struct mtk_wed_bm_desc *desc = dev->rx_buf_ring.desc;
-
- if (!desc)
- return;
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -45,7 +45,7 @@ enum mtk_wed_wo_cmd {
- MTK_WED_WO_CMD_WED_END
- };
-
--struct mtk_rxbm_desc {
-+struct mtk_wed_bm_desc {
- __le32 buf0;
- __le32 token;
- } __packed __aligned(4);
-@@ -104,7 +104,7 @@ struct mtk_wed_device {
-
- struct {
- int size;
-- struct mtk_rxbm_desc *desc;
-+ struct mtk_wed_bm_desc *desc;
- dma_addr_t desc_phys;
- } rx_buf_ring;
-
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Mon, 18 Sep 2023 12:29:08 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: introduce mtk_wed_buf structure
-
-Introduce mtk_wed_buf structure to store both virtual and physical
-addresses allocated in mtk_wed_tx_buffer_alloc() routine. This is a
-preliminary patch to add WED support for MT7988 SoC since it relies on a
-different dma descriptor layout not storing page dma addresses.
-
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -300,9 +300,9 @@ out:
- static int
- mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev)
- {
-+ struct mtk_wed_buf *page_list;
- struct mtk_wdma_desc *desc;
- dma_addr_t desc_phys;
-- void **page_list;
- int token = dev->wlan.token_start;
- int ring_size;
- int n_pages;
-@@ -343,7 +343,8 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
- return -ENOMEM;
- }
-
-- page_list[page_idx++] = page;
-+ page_list[page_idx].p = page;
-+ page_list[page_idx++].phy_addr = page_phys;
- dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE,
- DMA_BIDIRECTIONAL);
-
-@@ -387,8 +388,8 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
- static void
- mtk_wed_free_tx_buffer(struct mtk_wed_device *dev)
- {
-+ struct mtk_wed_buf *page_list = dev->tx_buf_ring.pages;
- struct mtk_wdma_desc *desc = dev->tx_buf_ring.desc;
-- void **page_list = dev->tx_buf_ring.pages;
- int page_idx;
- int i;
-
-@@ -400,13 +401,12 @@ mtk_wed_free_tx_buffer(struct mtk_wed_de
-
- for (i = 0, page_idx = 0; i < dev->tx_buf_ring.size;
- i += MTK_WED_BUF_PER_PAGE) {
-- void *page = page_list[page_idx++];
-- dma_addr_t buf_addr;
-+ dma_addr_t buf_addr = page_list[page_idx].phy_addr;
-+ void *page = page_list[page_idx++].p;
-
- if (!page)
- break;
-
-- buf_addr = le32_to_cpu(desc[i].buf0);
- dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE,
- DMA_BIDIRECTIONAL);
- __free_page(page);
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -76,6 +76,11 @@ struct mtk_wed_wo_rx_stats {
- __le32 rx_drop_cnt;
- };
-
-+struct mtk_wed_buf {
-+ void *p;
-+ dma_addr_t phy_addr;
-+};
-+
- struct mtk_wed_device {
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
- const struct mtk_wed_ops *ops;
-@@ -97,7 +102,7 @@ struct mtk_wed_device {
-
- struct {
- int size;
-- void **pages;
-+ struct mtk_wed_buf *pages;
- struct mtk_wdma_desc *desc;
- dma_addr_t desc_phys;
- } tx_buf_ring;
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Mon, 18 Sep 2023 12:29:09 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: move mem_region array out of
- mtk_wed_mcu_load_firmware
-
-Remove mtk_wed_wo_memory_region boot structure in mtk_wed_wo.
-This is a preliminary patch to introduce WED support for MT7988 SoC.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -16,14 +16,30 @@
- #include "mtk_wed_wo.h"
- #include "mtk_wed.h"
-
-+static struct mtk_wed_wo_memory_region mem_region[] = {
-+ [MTK_WED_WO_REGION_EMI] = {
-+ .name = "wo-emi",
-+ },
-+ [MTK_WED_WO_REGION_ILM] = {
-+ .name = "wo-ilm",
-+ },
-+ [MTK_WED_WO_REGION_DATA] = {
-+ .name = "wo-data",
-+ .shared = true,
-+ },
-+ [MTK_WED_WO_REGION_BOOT] = {
-+ .name = "wo-boot",
-+ },
-+};
-+
- static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg)
- {
-- return readl(wo->boot.addr + reg);
-+ return readl(mem_region[MTK_WED_WO_REGION_BOOT].addr + reg);
- }
-
- static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
- {
-- writel(val, wo->boot.addr + reg);
-+ writel(val, mem_region[MTK_WED_WO_REGION_BOOT].addr + reg);
- }
-
- static struct sk_buff *
-@@ -294,18 +310,6 @@ next:
- static int
- mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo)
- {
-- static struct mtk_wed_wo_memory_region mem_region[] = {
-- [MTK_WED_WO_REGION_EMI] = {
-- .name = "wo-emi",
-- },
-- [MTK_WED_WO_REGION_ILM] = {
-- .name = "wo-ilm",
-- },
-- [MTK_WED_WO_REGION_DATA] = {
-- .name = "wo-data",
-- .shared = true,
-- },
-- };
- const struct mtk_wed_fw_trailer *trailer;
- const struct firmware *fw;
- const char *fw_name;
-@@ -319,11 +323,6 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
- return ret;
- }
-
-- wo->boot.name = "wo-boot";
-- ret = mtk_wed_get_memory_region(wo, &wo->boot);
-- if (ret)
-- return ret;
--
- /* set dummy cr */
- wed_w32(wo->hw->wed_dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL,
- wo->hw->index + 1);
---- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-@@ -228,7 +228,6 @@ struct mtk_wed_wo_queue {
-
- struct mtk_wed_wo {
- struct mtk_wed_hw *hw;
-- struct mtk_wed_wo_memory_region boot;
-
- struct mtk_wed_wo_queue q_tx;
- struct mtk_wed_wo_queue q_rx;
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Mon, 18 Sep 2023 12:29:10 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: make memory region optional
-
-Make mtk_wed_wo_memory_region optionals.
-This is a preliminary patch to introduce Wireless Ethernet Dispatcher
-support for MT7988 SoC since MT7988 WED fw image will have a different
-layout.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -234,19 +234,13 @@ int mtk_wed_mcu_msg_update(struct mtk_we
- }
-
- static int
--mtk_wed_get_memory_region(struct mtk_wed_wo *wo,
-+mtk_wed_get_memory_region(struct mtk_wed_hw *hw, int index,
- struct mtk_wed_wo_memory_region *region)
- {
- struct reserved_mem *rmem;
- struct device_node *np;
-- int index;
-
-- index = of_property_match_string(wo->hw->node, "memory-region-names",
-- region->name);
-- if (index < 0)
-- return index;
--
-- np = of_parse_phandle(wo->hw->node, "memory-region", index);
-+ np = of_parse_phandle(hw->node, "memory-region", index);
- if (!np)
- return -ENODEV;
-
-@@ -258,7 +252,7 @@ mtk_wed_get_memory_region(struct mtk_wed
-
- region->phy_addr = rmem->base;
- region->size = rmem->size;
-- region->addr = devm_ioremap(wo->hw->dev, region->phy_addr, region->size);
-+ region->addr = devm_ioremap(hw->dev, region->phy_addr, region->size);
-
- return !region->addr ? -EINVAL : 0;
- }
-@@ -271,6 +265,9 @@ mtk_wed_mcu_run_firmware(struct mtk_wed_
- const struct mtk_wed_fw_trailer *trailer;
- const struct mtk_wed_fw_region *fw_region;
-
-+ if (!region->phy_addr || !region->size)
-+ return 0;
-+
- trailer_ptr = fw->data + fw->size - sizeof(*trailer);
- trailer = (const struct mtk_wed_fw_trailer *)trailer_ptr;
- region_ptr = trailer_ptr - trailer->num_region * sizeof(*fw_region);
-@@ -318,7 +315,13 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
-
- /* load firmware region metadata */
- for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
-- ret = mtk_wed_get_memory_region(wo, &mem_region[i]);
-+ int index = of_property_match_string(wo->hw->node,
-+ "memory-region-names",
-+ mem_region[i].name);
-+ if (index < 0)
-+ continue;
-+
-+ ret = mtk_wed_get_memory_region(wo->hw, index, &mem_region[i]);
- if (ret)
- return ret;
- }
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Mon, 18 Sep 2023 12:29:12 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: add mtk_wed_soc_data structure
-
-Introduce mtk_wed_soc_data utility structure to contain per-SoC
-definitions.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -49,6 +49,26 @@ struct mtk_wed_flow_block_priv {
- struct net_device *dev;
- };
-
-+static const struct mtk_wed_soc_data mt7622_data = {
-+ .regmap = {
-+ .tx_bm_tkid = 0x088,
-+ .wpdma_rx_ring0 = 0x770,
-+ .reset_idx_tx_mask = GENMASK(3, 0),
-+ .reset_idx_rx_mask = GENMASK(17, 16),
-+ },
-+ .wdma_desc_size = sizeof(struct mtk_wdma_desc),
-+};
-+
-+static const struct mtk_wed_soc_data mt7986_data = {
-+ .regmap = {
-+ .tx_bm_tkid = 0x0c8,
-+ .wpdma_rx_ring0 = 0x770,
-+ .reset_idx_tx_mask = GENMASK(1, 0),
-+ .reset_idx_rx_mask = GENMASK(7, 6),
-+ },
-+ .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc),
-+};
-+
- static void
- wed_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val)
- {
-@@ -746,7 +766,7 @@ mtk_wed_set_wpdma(struct mtk_wed_device
- return;
-
- wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
-- wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx);
-+ wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring0, dev->wlan.wpdma_rx);
- }
-
- static void
-@@ -940,22 +960,10 @@ mtk_wed_hw_init(struct mtk_wed_device *d
- wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
-
- if (mtk_wed_is_v1(dev->hw)) {
-- wed_w32(dev, MTK_WED_TX_BM_TKID,
-- FIELD_PREP(MTK_WED_TX_BM_TKID_START,
-- dev->wlan.token_start) |
-- FIELD_PREP(MTK_WED_TX_BM_TKID_END,
-- dev->wlan.token_start +
-- dev->wlan.nbuf - 1));
- wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
- FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) |
- MTK_WED_TX_BM_DYN_THR_HI);
- } else {
-- wed_w32(dev, MTK_WED_TX_BM_TKID_V2,
-- FIELD_PREP(MTK_WED_TX_BM_TKID_START,
-- dev->wlan.token_start) |
-- FIELD_PREP(MTK_WED_TX_BM_TKID_END,
-- dev->wlan.token_start +
-- dev->wlan.nbuf - 1));
- wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
- FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) |
- MTK_WED_TX_BM_DYN_THR_HI_V2);
-@@ -970,6 +978,11 @@ mtk_wed_hw_init(struct mtk_wed_device *d
- MTK_WED_TX_TKID_DYN_THR_HI);
- }
-
-+ wed_w32(dev, dev->hw->soc->regmap.tx_bm_tkid,
-+ FIELD_PREP(MTK_WED_TX_BM_TKID_START, dev->wlan.token_start) |
-+ FIELD_PREP(MTK_WED_TX_BM_TKID_END,
-+ dev->wlan.token_start + dev->wlan.nbuf - 1));
-+
- mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
-
- if (mtk_wed_is_v1(dev->hw)) {
-@@ -1104,13 +1117,8 @@ mtk_wed_rx_reset(struct mtk_wed_device *
- if (ret) {
- mtk_wed_reset(dev, MTK_WED_RESET_WED_RX_DMA);
- } else {
-- struct mtk_eth *eth = dev->hw->eth;
--
-- if (mtk_is_netsys_v2_or_greater(eth))
-- wed_set(dev, MTK_WED_RESET_IDX,
-- MTK_WED_RESET_IDX_RX_V2);
-- else
-- wed_set(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_RX);
-+ wed_set(dev, MTK_WED_RESET_IDX,
-+ dev->hw->soc->regmap.reset_idx_rx_mask);
- wed_w32(dev, MTK_WED_RESET_IDX, 0);
- }
-
-@@ -1163,7 +1171,8 @@ mtk_wed_reset_dma(struct mtk_wed_device
- if (busy) {
- mtk_wed_reset(dev, MTK_WED_RESET_WED_TX_DMA);
- } else {
-- wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_TX);
-+ wed_w32(dev, MTK_WED_RESET_IDX,
-+ dev->hw->soc->regmap.reset_idx_tx_mask);
- wed_w32(dev, MTK_WED_RESET_IDX, 0);
- }
-
-@@ -1255,7 +1264,6 @@ static int
- mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
- bool reset)
- {
-- u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
- struct mtk_wed_ring *wdma;
-
- if (idx >= ARRAY_SIZE(dev->rx_wdma))
-@@ -1263,7 +1271,7 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_we
-
- wdma = &dev->rx_wdma[idx];
- if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE,
-- desc_size, true))
-+ dev->hw->soc->wdma_desc_size, true))
- return -ENOMEM;
-
- wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
-@@ -1284,7 +1292,6 @@ static int
- mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
- bool reset)
- {
-- u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
- struct mtk_wed_ring *wdma;
-
- if (idx >= ARRAY_SIZE(dev->tx_wdma))
-@@ -1292,7 +1299,7 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we
-
- wdma = &dev->tx_wdma[idx];
- if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE,
-- desc_size, true))
-+ dev->hw->soc->wdma_desc_size, true))
- return -ENOMEM;
-
- wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE,
-@@ -1930,7 +1937,12 @@ void mtk_wed_add_hw(struct device_node *
- hw->irq = irq;
- hw->version = eth->soc->version;
-
-- if (mtk_wed_is_v1(hw)) {
-+ switch (hw->version) {
-+ case 2:
-+ hw->soc = &mt7986_data;
-+ break;
-+ default:
-+ case 1:
- hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
- "mediatek,pcie-mirror");
- hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np,
-@@ -1944,6 +1956,8 @@ void mtk_wed_add_hw(struct device_node *
- regmap_write(hw->mirror, 0, 0);
- regmap_write(hw->mirror, 4, 0);
- }
-+ hw->soc = &mt7622_data;
-+ break;
- }
-
- mtk_wed_hw_add_debugfs(hw);
---- a/drivers/net/ethernet/mediatek/mtk_wed.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.h
-@@ -12,7 +12,18 @@
- struct mtk_eth;
- struct mtk_wed_wo;
-
-+struct mtk_wed_soc_data {
-+ struct {
-+ u32 tx_bm_tkid;
-+ u32 wpdma_rx_ring0;
-+ u32 reset_idx_tx_mask;
-+ u32 reset_idx_rx_mask;
-+ } regmap;
-+ u32 wdma_desc_size;
-+};
-+
- struct mtk_wed_hw {
-+ const struct mtk_wed_soc_data *soc;
- struct device_node *node;
- struct mtk_eth *eth;
- struct regmap *regs;
---- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-@@ -100,8 +100,6 @@ struct mtk_wdma_desc {
-
- #define MTK_WED_TX_BM_BASE 0x084
-
--#define MTK_WED_TX_BM_TKID 0x088
--#define MTK_WED_TX_BM_TKID_V2 0x0c8
- #define MTK_WED_TX_BM_TKID_START GENMASK(15, 0)
- #define MTK_WED_TX_BM_TKID_END GENMASK(31, 16)
-
-@@ -160,9 +158,6 @@ struct mtk_wdma_desc {
- #define MTK_WED_GLO_CFG_RX_2B_OFFSET BIT(31)
-
- #define MTK_WED_RESET_IDX 0x20c
--#define MTK_WED_RESET_IDX_TX GENMASK(3, 0)
--#define MTK_WED_RESET_IDX_RX GENMASK(17, 16)
--#define MTK_WED_RESET_IDX_RX_V2 GENMASK(7, 6)
- #define MTK_WED_RESET_WPDMA_IDX_RX GENMASK(31, 30)
-
- #define MTK_WED_TX_MIB(_n) (0x2a0 + (_n) * 4)
-@@ -286,7 +281,6 @@ struct mtk_wdma_desc {
- #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX GENMASK(25, 24)
-
- #define MTK_WED_WPDMA_RX_GLO_CFG 0x76c
--#define MTK_WED_WPDMA_RX_RING 0x770
-
- #define MTK_WED_WPDMA_RX_D_MIB(_n) (0x774 + (_n) * 4)
- #define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n) (0x784 + (_n) * 4)
+++ /dev/null
-From: Sujuan Chen <sujuan.chen@mediatek.com>
-Date: Mon, 18 Sep 2023 12:29:13 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: introduce WED support for MT7988
-
-Similar to MT7986 and MT7622, enable Wireless Ethernet Ditpatcher for
-MT7988 in order to offload traffic forwarded from LAN/WLAN to WLAN/LAN
-
-Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -197,6 +197,7 @@ static const struct mtk_reg_map mt7988_r
- .wdma_base = {
- [0] = 0x4800,
- [1] = 0x4c00,
-+ [2] = 0x5000,
- },
- .pse_iq_sta = 0x0180,
- .pse_oq_sta = 0x01a0,
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -1132,7 +1132,7 @@ struct mtk_reg_map {
- u32 gdm1_cnt;
- u32 gdma_to_ppe;
- u32 ppe_base;
-- u32 wdma_base[2];
-+ u32 wdma_base[3];
- u32 pse_iq_sta;
- u32 pse_oq_sta;
- };
---- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -201,6 +201,9 @@ mtk_flow_set_output_device(struct mtk_et
- case 1:
- pse_port = PSE_WDMA1_PORT;
- break;
-+ case 2:
-+ pse_port = PSE_WDMA2_PORT;
-+ break;
- default:
- return -EINVAL;
- }
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -17,17 +17,19 @@
- #include <net/flow_offload.h>
- #include <net/pkt_cls.h>
- #include "mtk_eth_soc.h"
--#include "mtk_wed_regs.h"
- #include "mtk_wed.h"
- #include "mtk_ppe.h"
- #include "mtk_wed_wo.h"
-
- #define MTK_PCIE_BASE(n) (0x1a143000 + (n) * 0x2000)
-
--#define MTK_WED_PKT_SIZE 1900
-+#define MTK_WED_PKT_SIZE 1920
- #define MTK_WED_BUF_SIZE 2048
-+#define MTK_WED_PAGE_BUF_SIZE 128
- #define MTK_WED_BUF_PER_PAGE (PAGE_SIZE / 2048)
-+#define MTK_WED_RX_PAGE_BUF_PER_PAGE (PAGE_SIZE / 128)
- #define MTK_WED_RX_RING_SIZE 1536
-+#define MTK_WED_RX_PG_BM_CNT 8192
-
- #define MTK_WED_TX_RING_SIZE 2048
- #define MTK_WED_WDMA_RING_SIZE 1024
-@@ -41,7 +43,10 @@
- #define MTK_WED_RRO_QUE_CNT 8192
- #define MTK_WED_MIOD_ENTRY_CNT 128
-
--static struct mtk_wed_hw *hw_list[2];
-+#define MTK_WED_TX_BM_DMA_SIZE 65536
-+#define MTK_WED_TX_BM_PKT_CNT 32768
-+
-+static struct mtk_wed_hw *hw_list[3];
- static DEFINE_MUTEX(hw_lock);
-
- struct mtk_wed_flow_block_priv {
-@@ -56,6 +61,7 @@ static const struct mtk_wed_soc_data mt7
- .reset_idx_tx_mask = GENMASK(3, 0),
- .reset_idx_rx_mask = GENMASK(17, 16),
- },
-+ .tx_ring_desc_size = sizeof(struct mtk_wdma_desc),
- .wdma_desc_size = sizeof(struct mtk_wdma_desc),
- };
-
-@@ -66,6 +72,18 @@ static const struct mtk_wed_soc_data mt7
- .reset_idx_tx_mask = GENMASK(1, 0),
- .reset_idx_rx_mask = GENMASK(7, 6),
- },
-+ .tx_ring_desc_size = sizeof(struct mtk_wdma_desc),
-+ .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc),
-+};
-+
-+static const struct mtk_wed_soc_data mt7988_data = {
-+ .regmap = {
-+ .tx_bm_tkid = 0x0c8,
-+ .wpdma_rx_ring0 = 0x7d0,
-+ .reset_idx_tx_mask = GENMASK(1, 0),
-+ .reset_idx_rx_mask = GENMASK(7, 6),
-+ },
-+ .tx_ring_desc_size = sizeof(struct mtk_wed_bm_desc),
- .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc),
- };
-
-@@ -320,33 +338,38 @@ out:
- static int
- mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev)
- {
-+ u32 desc_size = dev->hw->soc->tx_ring_desc_size;
-+ int i, page_idx = 0, n_pages, ring_size;
-+ int token = dev->wlan.token_start;
- struct mtk_wed_buf *page_list;
-- struct mtk_wdma_desc *desc;
- dma_addr_t desc_phys;
-- int token = dev->wlan.token_start;
-- int ring_size;
-- int n_pages;
-- int i, page_idx;
-+ void *desc_ptr;
-
-- ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
-- n_pages = ring_size / MTK_WED_BUF_PER_PAGE;
-+ if (!mtk_wed_is_v3_or_greater(dev->hw)) {
-+ ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
-+ dev->tx_buf_ring.size = ring_size;
-+ } else {
-+ dev->tx_buf_ring.size = MTK_WED_TX_BM_DMA_SIZE;
-+ ring_size = MTK_WED_TX_BM_PKT_CNT;
-+ }
-+ n_pages = dev->tx_buf_ring.size / MTK_WED_BUF_PER_PAGE;
-
- page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL);
- if (!page_list)
- return -ENOMEM;
-
-- dev->tx_buf_ring.size = ring_size;
- dev->tx_buf_ring.pages = page_list;
-
-- desc = dma_alloc_coherent(dev->hw->dev, ring_size * sizeof(*desc),
-- &desc_phys, GFP_KERNEL);
-- if (!desc)
-+ desc_ptr = dma_alloc_coherent(dev->hw->dev,
-+ dev->tx_buf_ring.size * desc_size,
-+ &desc_phys, GFP_KERNEL);
-+ if (!desc_ptr)
- return -ENOMEM;
-
-- dev->tx_buf_ring.desc = desc;
-+ dev->tx_buf_ring.desc = desc_ptr;
- dev->tx_buf_ring.desc_phys = desc_phys;
-
-- for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) {
-+ for (i = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) {
- dma_addr_t page_phys, buf_phys;
- struct page *page;
- void *buf;
-@@ -372,28 +395,31 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
- buf_phys = page_phys;
-
- for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) {
-- u32 txd_size;
-- u32 ctrl;
--
-- txd_size = dev->wlan.init_buf(buf, buf_phys, token++);
-+ struct mtk_wdma_desc *desc = desc_ptr;
-
- desc->buf0 = cpu_to_le32(buf_phys);
-- desc->buf1 = cpu_to_le32(buf_phys + txd_size);
-+ if (!mtk_wed_is_v3_or_greater(dev->hw)) {
-+ u32 txd_size, ctrl;
-
-- if (mtk_wed_is_v1(dev->hw))
-- ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) |
-- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
-- MTK_WED_BUF_SIZE - txd_size) |
-- MTK_WDMA_DESC_CTRL_LAST_SEG1;
-- else
-- ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) |
-- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2,
-- MTK_WED_BUF_SIZE - txd_size) |
-- MTK_WDMA_DESC_CTRL_LAST_SEG0;
-- desc->ctrl = cpu_to_le32(ctrl);
-- desc->info = 0;
-- desc++;
-+ txd_size = dev->wlan.init_buf(buf, buf_phys,
-+ token++);
-+ desc->buf1 = cpu_to_le32(buf_phys + txd_size);
-+ ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size);
-+ if (mtk_wed_is_v1(dev->hw))
-+ ctrl |= MTK_WDMA_DESC_CTRL_LAST_SEG1 |
-+ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
-+ MTK_WED_BUF_SIZE - txd_size);
-+ else
-+ ctrl |= MTK_WDMA_DESC_CTRL_LAST_SEG0 |
-+ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2,
-+ MTK_WED_BUF_SIZE - txd_size);
-+ desc->ctrl = cpu_to_le32(ctrl);
-+ desc->info = 0;
-+ } else {
-+ desc->ctrl = cpu_to_le32(token << 16);
-+ }
-
-+ desc_ptr += desc_size;
- buf += MTK_WED_BUF_SIZE;
- buf_phys += MTK_WED_BUF_SIZE;
- }
-@@ -409,31 +435,31 @@ static void
- mtk_wed_free_tx_buffer(struct mtk_wed_device *dev)
- {
- struct mtk_wed_buf *page_list = dev->tx_buf_ring.pages;
-- struct mtk_wdma_desc *desc = dev->tx_buf_ring.desc;
-- int page_idx;
-- int i;
-+ struct mtk_wed_hw *hw = dev->hw;
-+ int i, page_idx = 0;
-
- if (!page_list)
- return;
-
-- if (!desc)
-+ if (!dev->tx_buf_ring.desc)
- goto free_pagelist;
-
-- for (i = 0, page_idx = 0; i < dev->tx_buf_ring.size;
-- i += MTK_WED_BUF_PER_PAGE) {
-- dma_addr_t buf_addr = page_list[page_idx].phy_addr;
-+ for (i = 0; i < dev->tx_buf_ring.size; i += MTK_WED_BUF_PER_PAGE) {
-+ dma_addr_t page_phy = page_list[page_idx].phy_addr;
- void *page = page_list[page_idx++].p;
-
- if (!page)
- break;
-
-- dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE,
-+ dma_unmap_page(dev->hw->dev, page_phy, PAGE_SIZE,
- DMA_BIDIRECTIONAL);
- __free_page(page);
- }
-
-- dma_free_coherent(dev->hw->dev, dev->tx_buf_ring.size * sizeof(*desc),
-- desc, dev->tx_buf_ring.desc_phys);
-+ dma_free_coherent(dev->hw->dev,
-+ dev->tx_buf_ring.size * hw->soc->tx_ring_desc_size,
-+ dev->tx_buf_ring.desc,
-+ dev->tx_buf_ring.desc_phys);
-
- free_pagelist:
- kfree(page_list);
-@@ -518,13 +544,23 @@ mtk_wed_set_ext_int(struct mtk_wed_devic
- {
- u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
-
-- if (mtk_wed_is_v1(dev->hw))
-+ switch (dev->hw->version) {
-+ case 1:
- mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR;
-- else
-+ break;
-+ case 2:
- mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH |
- MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH |
- MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT |
- MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR;
-+ break;
-+ case 3:
-+ mask = MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT |
-+ MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
-+ break;
-+ default:
-+ break;
-+ }
-
- if (!dev->hw->num_flows)
- mask &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
-@@ -536,6 +572,9 @@ mtk_wed_set_ext_int(struct mtk_wed_devic
- static void
- mtk_wed_set_512_support(struct mtk_wed_device *dev, bool enable)
- {
-+ if (!mtk_wed_is_v2(dev->hw))
-+ return;
-+
- if (enable) {
- wed_w32(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR);
- wed_w32(dev, MTK_WED_TXP_DW1,
-@@ -610,6 +649,14 @@ mtk_wed_dma_disable(struct mtk_wed_devic
- MTK_WED_WPDMA_RX_D_RX_DRV_EN);
- wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
- MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
-+
-+ if (mtk_wed_is_v3_or_greater(dev->hw) &&
-+ mtk_wed_get_rx_capa(dev)) {
-+ wdma_clr(dev, MTK_WDMA_PREF_TX_CFG,
-+ MTK_WDMA_PREF_TX_CFG_PREF_EN);
-+ wdma_clr(dev, MTK_WDMA_PREF_RX_CFG,
-+ MTK_WDMA_PREF_RX_CFG_PREF_EN);
-+ }
- }
-
- mtk_wed_set_512_support(dev, false);
-@@ -651,6 +698,14 @@ mtk_wed_deinit(struct mtk_wed_device *de
- MTK_WED_CTRL_RX_ROUTE_QM_EN |
- MTK_WED_CTRL_WED_RX_BM_EN |
- MTK_WED_CTRL_RX_RRO_QM_EN);
-+
-+ if (mtk_wed_is_v3_or_greater(dev->hw)) {
-+ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN);
-+ wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_TX_AMSDU);
-+ wed_clr(dev, MTK_WED_PCIE_INT_CTRL,
-+ MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA |
-+ MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER);
-+ }
- }
-
- static void
-@@ -700,21 +755,37 @@ mtk_wed_detach(struct mtk_wed_device *de
- mutex_unlock(&hw_lock);
- }
-
--#define PCIE_BASE_ADDR0 0x11280000
- static void
- mtk_wed_bus_init(struct mtk_wed_device *dev)
- {
- switch (dev->wlan.bus_type) {
- case MTK_WED_BUS_PCIE: {
- struct device_node *np = dev->hw->eth->dev->of_node;
-- struct regmap *regs;
-
-- regs = syscon_regmap_lookup_by_phandle(np,
-- "mediatek,wed-pcie");
-- if (IS_ERR(regs))
-- break;
-+ if (mtk_wed_is_v2(dev->hw)) {
-+ struct regmap *regs;
-+
-+ regs = syscon_regmap_lookup_by_phandle(np,
-+ "mediatek,wed-pcie");
-+ if (IS_ERR(regs))
-+ break;
-
-- regmap_update_bits(regs, 0, BIT(0), BIT(0));
-+ regmap_update_bits(regs, 0, BIT(0), BIT(0));
-+ }
-+
-+ if (dev->wlan.msi) {
-+ wed_w32(dev, MTK_WED_PCIE_CFG_INTM,
-+ dev->hw->pcie_base | 0xc08);
-+ wed_w32(dev, MTK_WED_PCIE_CFG_BASE,
-+ dev->hw->pcie_base | 0xc04);
-+ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(8));
-+ } else {
-+ wed_w32(dev, MTK_WED_PCIE_CFG_INTM,
-+ dev->hw->pcie_base | 0x180);
-+ wed_w32(dev, MTK_WED_PCIE_CFG_BASE,
-+ dev->hw->pcie_base | 0x184);
-+ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24));
-+ }
-
- wed_w32(dev, MTK_WED_PCIE_INT_CTRL,
- FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2));
-@@ -722,19 +793,9 @@ mtk_wed_bus_init(struct mtk_wed_device *
- /* pcie interrupt control: pola/source selection */
- wed_set(dev, MTK_WED_PCIE_INT_CTRL,
- MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA |
-- FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1));
-- wed_r32(dev, MTK_WED_PCIE_INT_CTRL);
--
-- wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180);
-- wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184);
--
-- /* pcie interrupt status trigger register */
-- wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24));
-- wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER);
--
-- /* pola setting */
-- wed_set(dev, MTK_WED_PCIE_INT_CTRL,
-- MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA);
-+ MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER |
-+ FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL,
-+ dev->hw->index));
- break;
- }
- case MTK_WED_BUS_AXI:
-@@ -772,18 +833,19 @@ mtk_wed_set_wpdma(struct mtk_wed_device
- static void
- mtk_wed_hw_init_early(struct mtk_wed_device *dev)
- {
-- u32 mask, set;
-+ u32 set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2);
-+ u32 mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE;
-
- mtk_wed_deinit(dev);
- mtk_wed_reset(dev, MTK_WED_RESET_WED);
- mtk_wed_set_wpdma(dev);
-
-- mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE |
-- MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE |
-- MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE;
-- set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2) |
-- MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP |
-- MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY;
-+ if (!mtk_wed_is_v3_or_greater(dev->hw)) {
-+ mask |= MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE |
-+ MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE;
-+ set |= MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP |
-+ MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY;
-+ }
- wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set);
-
- if (mtk_wed_is_v1(dev->hw)) {
-@@ -931,11 +993,18 @@ mtk_wed_route_qm_hw_init(struct mtk_wed_
- }
-
- /* configure RX_ROUTE_QM */
-- wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
-- wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_TXDMAD_FPORT);
-- wed_set(dev, MTK_WED_RTQM_GLO_CFG,
-- FIELD_PREP(MTK_WED_RTQM_TXDMAD_FPORT, 0x3 + dev->hw->index));
-- wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
-+ if (mtk_wed_is_v2(dev->hw)) {
-+ wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
-+ wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_TXDMAD_FPORT);
-+ wed_set(dev, MTK_WED_RTQM_GLO_CFG,
-+ FIELD_PREP(MTK_WED_RTQM_TXDMAD_FPORT,
-+ 0x3 + dev->hw->index));
-+ wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
-+ } else {
-+ wed_set(dev, MTK_WED_RTQM_ENQ_CFG0,
-+ FIELD_PREP(MTK_WED_RTQM_ENQ_CFG_TXDMAD_FPORT,
-+ 0x3 + dev->hw->index));
-+ }
- /* enable RX_ROUTE_QM */
- wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN);
- }
-@@ -948,22 +1017,30 @@ mtk_wed_hw_init(struct mtk_wed_device *d
-
- dev->init_done = true;
- mtk_wed_set_ext_int(dev, false);
-- wed_w32(dev, MTK_WED_TX_BM_CTRL,
-- MTK_WED_TX_BM_CTRL_PAUSE |
-- FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM,
-- dev->tx_buf_ring.size / 128) |
-- FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM,
-- MTK_WED_TX_RING_SIZE / 256));
-
- wed_w32(dev, MTK_WED_TX_BM_BASE, dev->tx_buf_ring.desc_phys);
--
- wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
-
- if (mtk_wed_is_v1(dev->hw)) {
-+ wed_w32(dev, MTK_WED_TX_BM_CTRL,
-+ MTK_WED_TX_BM_CTRL_PAUSE |
-+ FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM,
-+ dev->tx_buf_ring.size / 128) |
-+ FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM,
-+ MTK_WED_TX_RING_SIZE / 256));
- wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
- FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) |
- MTK_WED_TX_BM_DYN_THR_HI);
-- } else {
-+ } else if (mtk_wed_is_v2(dev->hw)) {
-+ wed_w32(dev, MTK_WED_TX_BM_CTRL,
-+ MTK_WED_TX_BM_CTRL_PAUSE |
-+ FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM,
-+ dev->tx_buf_ring.size / 128) |
-+ FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM,
-+ MTK_WED_TX_RING_SIZE / 256));
-+ wed_w32(dev, MTK_WED_TX_TKID_DYN_THR,
-+ FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) |
-+ MTK_WED_TX_TKID_DYN_THR_HI);
- wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
- FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) |
- MTK_WED_TX_BM_DYN_THR_HI_V2);
-@@ -973,9 +1050,6 @@ mtk_wed_hw_init(struct mtk_wed_device *d
- dev->tx_buf_ring.size / 128) |
- FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM,
- dev->tx_buf_ring.size / 128));
-- wed_w32(dev, MTK_WED_TX_TKID_DYN_THR,
-- FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) |
-- MTK_WED_TX_TKID_DYN_THR_HI);
- }
-
- wed_w32(dev, dev->hw->soc->regmap.tx_bm_tkid,
-@@ -985,26 +1059,62 @@ mtk_wed_hw_init(struct mtk_wed_device *d
-
- mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
-
-+ if (mtk_wed_is_v3_or_greater(dev->hw)) {
-+ /* switch to new bm architecture */
-+ wed_clr(dev, MTK_WED_TX_BM_CTRL,
-+ MTK_WED_TX_BM_CTRL_LEGACY_EN);
-+
-+ wed_w32(dev, MTK_WED_TX_TKID_CTRL,
-+ MTK_WED_TX_TKID_CTRL_PAUSE |
-+ FIELD_PREP(MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3,
-+ dev->wlan.nbuf / 128) |
-+ FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3,
-+ dev->wlan.nbuf / 128));
-+ /* return SKBID + SDP back to bm */
-+ wed_set(dev, MTK_WED_TX_TKID_CTRL,
-+ MTK_WED_TX_TKID_CTRL_FREE_FORMAT);
-+
-+ wed_w32(dev, MTK_WED_TX_BM_INIT_PTR,
-+ MTK_WED_TX_BM_PKT_CNT |
-+ MTK_WED_TX_BM_INIT_SW_TAIL_IDX);
-+ }
-+
- if (mtk_wed_is_v1(dev->hw)) {
- wed_set(dev, MTK_WED_CTRL,
- MTK_WED_CTRL_WED_TX_BM_EN |
- MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
-- } else {
-- wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE);
-- if (mtk_wed_get_rx_capa(dev)) {
-- /* rx hw init */
-- wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
-- MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
-- MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
-- wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
--
-- mtk_wed_rx_buffer_hw_init(dev);
-- mtk_wed_rro_hw_init(dev);
-- mtk_wed_route_qm_hw_init(dev);
-- }
-+ } else if (mtk_wed_get_rx_capa(dev)) {
-+ /* rx hw init */
-+ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
-+ MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
-+ MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
-+ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
-+
-+ /* reset prefetch index of ring */
-+ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX,
-+ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
-+ wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX,
-+ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
-+
-+ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX,
-+ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
-+ wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX,
-+ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
-+
-+ /* reset prefetch FIFO of ring */
-+ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG,
-+ MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R0_CLR |
-+ MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R1_CLR);
-+ wed_w32(dev, MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG, 0);
-+
-+ mtk_wed_rx_buffer_hw_init(dev);
-+ mtk_wed_rro_hw_init(dev);
-+ mtk_wed_route_qm_hw_init(dev);
- }
-
- wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE);
-+ if (!mtk_wed_is_v1(dev->hw))
-+ wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE);
- }
-
- static void
-@@ -1302,6 +1412,24 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we
- dev->hw->soc->wdma_desc_size, true))
- return -ENOMEM;
-
-+ if (mtk_wed_is_v3_or_greater(dev->hw)) {
-+ struct mtk_wdma_desc *desc = wdma->desc;
-+ int i;
-+
-+ for (i = 0; i < MTK_WED_WDMA_RING_SIZE; i++) {
-+ desc->buf0 = 0;
-+ desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE);
-+ desc->buf1 = 0;
-+ desc->info = cpu_to_le32(MTK_WDMA_TXD0_DESC_INFO_DMA_DONE);
-+ desc++;
-+ desc->buf0 = 0;
-+ desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE);
-+ desc->buf1 = 0;
-+ desc->info = cpu_to_le32(MTK_WDMA_TXD1_DESC_INFO_DMA_DONE);
-+ desc++;
-+ }
-+ }
-+
- wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE,
- wdma->desc_phys);
- wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_COUNT,
-@@ -1367,6 +1495,9 @@ mtk_wed_configure_irq(struct mtk_wed_dev
-
- wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
- } else {
-+ if (mtk_wed_is_v3_or_greater(dev->hw))
-+ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_TKID_ALI_EN);
-+
- /* initail tx interrupt trigger */
- wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX,
- MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN |
-@@ -1419,33 +1550,60 @@ mtk_wed_dma_enable(struct mtk_wed_device
- {
- int i;
-
-- wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
-+ if (!mtk_wed_is_v3_or_greater(dev->hw)) {
-+ wed_set(dev, MTK_WED_WPDMA_INT_CTRL,
-+ MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
-+ wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
-+ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
-+ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
-+ wdma_set(dev, MTK_WDMA_GLO_CFG,
-+ MTK_WDMA_GLO_CFG_TX_DMA_EN |
-+ MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
-+ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
-+ wed_set(dev, MTK_WED_WPDMA_CTRL, MTK_WED_WPDMA_CTRL_SDL1_FIXED);
-+ } else {
-+ wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
-+ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
-+ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN |
-+ MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR);
-+ wdma_set(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
-+ }
-
- wed_set(dev, MTK_WED_GLO_CFG,
- MTK_WED_GLO_CFG_TX_DMA_EN |
- MTK_WED_GLO_CFG_RX_DMA_EN);
-- wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
-- MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
-- MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
-+
- wed_set(dev, MTK_WED_WDMA_GLO_CFG,
- MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
-
-- wdma_set(dev, MTK_WDMA_GLO_CFG,
-- MTK_WDMA_GLO_CFG_TX_DMA_EN |
-- MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
-- MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
--
- if (mtk_wed_is_v1(dev->hw)) {
- wdma_set(dev, MTK_WDMA_GLO_CFG,
- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
- return;
- }
-
-- wed_set(dev, MTK_WED_WPDMA_CTRL,
-- MTK_WED_WPDMA_CTRL_SDL1_FIXED);
- wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
- MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
- MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
-+
-+ if (mtk_wed_is_v3_or_greater(dev->hw)) {
-+ wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG,
-+ FIELD_PREP(MTK_WED_WDMA_RX_PREF_BURST_SIZE, 0x10) |
-+ FIELD_PREP(MTK_WED_WDMA_RX_PREF_LOW_THRES, 0x8));
-+ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
-+ MTK_WED_WDMA_RX_PREF_DDONE2_EN);
-+ wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG, MTK_WED_WDMA_RX_PREF_EN);
-+
-+ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
-+ MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK_LAST);
-+ wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
-+ MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK |
-+ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_CHK |
-+ MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4);
-+
-+ wdma_set(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
-+ }
-+
- wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
- MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
- MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
-@@ -1457,11 +1615,22 @@ mtk_wed_dma_enable(struct mtk_wed_device
- MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
- MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
-
-+ wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RXD_READ_LEN);
- wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
- MTK_WED_WPDMA_RX_D_RX_DRV_EN |
- FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) |
-- FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL,
-- 0x2));
-+ FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL, 0x2));
-+
-+ if (mtk_wed_is_v3_or_greater(dev->hw)) {
-+ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
-+ MTK_WED_WPDMA_RX_D_PREF_EN |
-+ FIELD_PREP(MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE, 0x10) |
-+ FIELD_PREP(MTK_WED_WPDMA_RX_D_PREF_LOW_THRES, 0x8));
-+
-+ wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN);
-+ wdma_set(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN);
-+ wdma_set(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
-+ }
-
- for (i = 0; i < MTK_WED_RX_QUEUES; i++)
- mtk_wed_check_wfdma_rx_fill(dev, i);
-@@ -1501,6 +1670,12 @@ mtk_wed_start(struct mtk_wed_device *dev
- wed_r32(dev, MTK_WED_EXT_INT_MASK1);
- wed_r32(dev, MTK_WED_EXT_INT_MASK2);
-
-+ if (mtk_wed_is_v3_or_greater(dev->hw)) {
-+ wed_w32(dev, MTK_WED_EXT_INT_MASK3,
-+ MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
-+ wed_r32(dev, MTK_WED_EXT_INT_MASK3);
-+ }
-+
- if (mtk_wed_rro_cfg(dev))
- return;
- }
-@@ -1552,6 +1727,7 @@ mtk_wed_attach(struct mtk_wed_device *de
- dev->irq = hw->irq;
- dev->wdma_idx = hw->index;
- dev->version = hw->version;
-+ dev->hw->pcie_base = mtk_wed_get_pcie_base(dev);
-
- if (hw->eth->dma_dev == hw->eth->dev &&
- of_dma_is_coherent(hw->eth->dev->of_node))
-@@ -1619,6 +1795,23 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev
- ring->reg_base = MTK_WED_RING_TX(idx);
- ring->wpdma = regs;
-
-+ if (mtk_wed_is_v3_or_greater(dev->hw) && idx == 1) {
-+ /* reset prefetch index */
-+ wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG,
-+ MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR |
-+ MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR);
-+
-+ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
-+ MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR |
-+ MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR);
-+
-+ /* reset prefetch FIFO */
-+ wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG,
-+ MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR |
-+ MTK_WED_WDMA_RX_PREF_FIFO_RX1_CLR);
-+ wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG, 0);
-+ }
-+
- /* WED -> WPDMA */
- wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_BASE, ring->desc_phys);
- wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_COUNT, MTK_WED_TX_RING_SIZE);
-@@ -1693,15 +1886,13 @@ mtk_wed_rx_ring_setup(struct mtk_wed_dev
- static u32
- mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
- {
-- u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
-+ u32 val, ext_mask;
-
-- if (mtk_wed_is_v1(dev->hw))
-- ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR;
-+ if (mtk_wed_is_v3_or_greater(dev->hw))
-+ ext_mask = MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT |
-+ MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
- else
-- ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH |
-- MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH |
-- MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT |
-- MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR;
-+ ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
-
- val = wed_r32(dev, MTK_WED_EXT_INT_STATUS);
- wed_w32(dev, MTK_WED_EXT_INT_STATUS, val);
-@@ -1941,6 +2132,9 @@ void mtk_wed_add_hw(struct device_node *
- case 2:
- hw->soc = &mt7986_data;
- break;
-+ case 3:
-+ hw->soc = &mt7988_data;
-+ break;
- default:
- case 1:
- hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
---- a/drivers/net/ethernet/mediatek/mtk_wed.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.h
-@@ -9,6 +9,8 @@
- #include <linux/regmap.h>
- #include <linux/netdevice.h>
-
-+#include "mtk_wed_regs.h"
-+
- struct mtk_eth;
- struct mtk_wed_wo;
-
-@@ -19,6 +21,7 @@ struct mtk_wed_soc_data {
- u32 reset_idx_tx_mask;
- u32 reset_idx_rx_mask;
- } regmap;
-+ u32 tx_ring_desc_size;
- u32 wdma_desc_size;
- };
-
-@@ -35,6 +38,7 @@ struct mtk_wed_hw {
- struct dentry *debugfs_dir;
- struct mtk_wed_device *wed_dev;
- struct mtk_wed_wo *wed_wo;
-+ u32 pcie_base;
- u32 debugfs_reg;
- u32 num_flows;
- u8 version;
-@@ -61,6 +65,16 @@ static inline bool mtk_wed_is_v2(struct
- return hw->version == 2;
- }
-
-+static inline bool mtk_wed_is_v3(struct mtk_wed_hw *hw)
-+{
-+ return hw->version == 3;
-+}
-+
-+static inline bool mtk_wed_is_v3_or_greater(struct mtk_wed_hw *hw)
-+{
-+ return hw->version > 2;
-+}
-+
- static inline void
- wed_w32(struct mtk_wed_device *dev, u32 reg, u32 val)
- {
-@@ -143,6 +157,21 @@ wpdma_txfree_w32(struct mtk_wed_device *
- writel(val, dev->txfree_ring.wpdma + reg);
- }
-
-+static inline u32 mtk_wed_get_pcie_base(struct mtk_wed_device *dev)
-+{
-+ if (!mtk_wed_is_v3_or_greater(dev->hw))
-+ return MTK_WED_PCIE_BASE;
-+
-+ switch (dev->hw->index) {
-+ case 1:
-+ return MTK_WED_PCIE_BASE1;
-+ case 2:
-+ return MTK_WED_PCIE_BASE2;
-+ default:
-+ return MTK_WED_PCIE_BASE0;
-+ }
-+}
-+
- void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
- void __iomem *wdma, phys_addr_t wdma_phy,
- int index);
---- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -331,10 +331,22 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
- wo->hw->index + 1);
-
- /* load firmware */
-- if (of_device_is_compatible(wo->hw->node, "mediatek,mt7981-wed"))
-- fw_name = MT7981_FIRMWARE_WO;
-- else
-- fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0;
-+ switch (wo->hw->version) {
-+ case 2:
-+ if (of_device_is_compatible(wo->hw->node,
-+ "mediatek,mt7981-wed"))
-+ fw_name = MT7981_FIRMWARE_WO;
-+ else
-+ fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1
-+ : MT7986_FIRMWARE_WO0;
-+ break;
-+ case 3:
-+ fw_name = wo->hw->index ? MT7988_FIRMWARE_WO1
-+ : MT7988_FIRMWARE_WO0;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-
- ret = request_firmware(&fw, fw_name, wo->hw->dev);
- if (ret)
-@@ -355,15 +367,16 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
- }
-
- /* set the start address */
-- boot_cr = wo->hw->index ? MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR
-- : MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
-+ if (!mtk_wed_is_v3_or_greater(wo->hw) && wo->hw->index)
-+ boot_cr = MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR;
-+ else
-+ boot_cr = MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
- wo_w32(wo, boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16);
- /* wo firmware reset */
- wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00);
-
-- val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR);
-- val |= wo->hw->index ? MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK
-- : MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK;
-+ val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR) |
-+ MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK;
- wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val);
- out:
- release_firmware(fw);
-@@ -398,3 +411,5 @@ int mtk_wed_mcu_init(struct mtk_wed_wo *
- MODULE_FIRMWARE(MT7981_FIRMWARE_WO);
- MODULE_FIRMWARE(MT7986_FIRMWARE_WO0);
- MODULE_FIRMWARE(MT7986_FIRMWARE_WO1);
-+MODULE_FIRMWARE(MT7988_FIRMWARE_WO0);
-+MODULE_FIRMWARE(MT7988_FIRMWARE_WO1);
---- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-@@ -13,6 +13,9 @@
- #define MTK_WDMA_DESC_CTRL_LAST_SEG0 BIT(30)
- #define MTK_WDMA_DESC_CTRL_DMA_DONE BIT(31)
-
-+#define MTK_WDMA_TXD0_DESC_INFO_DMA_DONE BIT(29)
-+#define MTK_WDMA_TXD1_DESC_INFO_DMA_DONE BIT(31)
-+
- struct mtk_wdma_desc {
- __le32 buf0;
- __le32 ctrl;
-@@ -37,6 +40,7 @@ struct mtk_wdma_desc {
- #define MTK_WED_RESET_WDMA_INT_AGENT BIT(19)
- #define MTK_WED_RESET_RX_RRO_QM BIT(20)
- #define MTK_WED_RESET_RX_ROUTE_QM BIT(21)
-+#define MTK_WED_RESET_TX_AMSDU BIT(22)
- #define MTK_WED_RESET_WED BIT(31)
-
- #define MTK_WED_CTRL 0x00c
-@@ -44,6 +48,9 @@ struct mtk_wdma_desc {
- #define MTK_WED_CTRL_WPDMA_INT_AGENT_BUSY BIT(1)
- #define MTK_WED_CTRL_WDMA_INT_AGENT_EN BIT(2)
- #define MTK_WED_CTRL_WDMA_INT_AGENT_BUSY BIT(3)
-+#define MTK_WED_CTRL_WED_RX_IND_CMD_EN BIT(5)
-+#define MTK_WED_CTRL_WED_RX_PG_BM_EN BIT(6)
-+#define MTK_WED_CTRL_WED_RX_PG_BM_BUSY BIT(7)
- #define MTK_WED_CTRL_WED_TX_BM_EN BIT(8)
- #define MTK_WED_CTRL_WED_TX_BM_BUSY BIT(9)
- #define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN BIT(10)
-@@ -54,9 +61,14 @@ struct mtk_wdma_desc {
- #define MTK_WED_CTRL_RX_RRO_QM_BUSY BIT(15)
- #define MTK_WED_CTRL_RX_ROUTE_QM_EN BIT(16)
- #define MTK_WED_CTRL_RX_ROUTE_QM_BUSY BIT(17)
-+#define MTK_WED_CTRL_TX_TKID_ALI_EN BIT(20)
-+#define MTK_WED_CTRL_TX_TKID_ALI_BUSY BIT(21)
-+#define MTK_WED_CTRL_TX_AMSDU_EN BIT(22)
-+#define MTK_WED_CTRL_TX_AMSDU_BUSY BIT(23)
- #define MTK_WED_CTRL_FINAL_DIDX_READ BIT(24)
- #define MTK_WED_CTRL_ETH_DMAD_FMT BIT(25)
- #define MTK_WED_CTRL_MIB_READ_CLEAR BIT(28)
-+#define MTK_WED_CTRL_FLD_MIB_RD_CLR BIT(28)
-
- #define MTK_WED_EXT_INT_STATUS 0x020
- #define MTK_WED_EXT_INT_STATUS_TF_LEN_ERR BIT(0)
-@@ -89,6 +101,7 @@ struct mtk_wdma_desc {
- #define MTK_WED_EXT_INT_MASK 0x028
- #define MTK_WED_EXT_INT_MASK1 0x02c
- #define MTK_WED_EXT_INT_MASK2 0x030
-+#define MTK_WED_EXT_INT_MASK3 0x034
-
- #define MTK_WED_STATUS 0x060
- #define MTK_WED_STATUS_TX GENMASK(15, 8)
-@@ -96,9 +109,14 @@ struct mtk_wdma_desc {
- #define MTK_WED_TX_BM_CTRL 0x080
- #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM GENMASK(6, 0)
- #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM GENMASK(22, 16)
-+#define MTK_WED_TX_BM_CTRL_LEGACY_EN BIT(26)
-+#define MTK_WED_TX_TKID_CTRL_FREE_FORMAT BIT(27)
- #define MTK_WED_TX_BM_CTRL_PAUSE BIT(28)
-
- #define MTK_WED_TX_BM_BASE 0x084
-+#define MTK_WED_TX_BM_INIT_PTR 0x088
-+#define MTK_WED_TX_BM_SW_TAIL_IDX GENMASK(16, 0)
-+#define MTK_WED_TX_BM_INIT_SW_TAIL_IDX BIT(16)
-
- #define MTK_WED_TX_BM_TKID_START GENMASK(15, 0)
- #define MTK_WED_TX_BM_TKID_END GENMASK(31, 16)
-@@ -122,6 +140,9 @@ struct mtk_wdma_desc {
- #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM GENMASK(22, 16)
- #define MTK_WED_TX_TKID_CTRL_PAUSE BIT(28)
-
-+#define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3 GENMASK(7, 0)
-+#define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3 GENMASK(23, 16)
-+
- #define MTK_WED_TX_TKID_DYN_THR 0x0e0
- #define MTK_WED_TX_TKID_DYN_THR_LO GENMASK(6, 0)
- #define MTK_WED_TX_TKID_DYN_THR_HI GENMASK(22, 16)
-@@ -199,12 +220,15 @@ struct mtk_wdma_desc {
- #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_PKT_PROC BIT(5)
- #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC BIT(6)
- #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_CRX_SYNC BIT(7)
--#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER GENMASK(18, 16)
-+#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER GENMASK(15, 12)
-+#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4 BIT(18)
- #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNSUPPORT_FMT BIT(19)
--#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UEVENT_PKT_FMT_CHK BIT(20)
-+#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_CHK BIT(20)
- #define MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR BIT(21)
- #define MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP BIT(24)
-+#define MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK_LAST BIT(25)
- #define MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV BIT(28)
-+#define MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK BIT(30)
-
- #define MTK_WED_WPDMA_RESET_IDX 0x50c
- #define MTK_WED_WPDMA_RESET_IDX_TX GENMASK(3, 0)
-@@ -250,9 +274,10 @@ struct mtk_wdma_desc {
- #define MTK_WED_PCIE_INT_TRIGGER_STATUS BIT(16)
-
- #define MTK_WED_PCIE_INT_CTRL 0x57c
--#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA BIT(20)
--#define MTK_WED_PCIE_INT_CTRL_SRC_SEL GENMASK(17, 16)
- #define MTK_WED_PCIE_INT_CTRL_POLL_EN GENMASK(13, 12)
-+#define MTK_WED_PCIE_INT_CTRL_SRC_SEL GENMASK(17, 16)
-+#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA BIT(20)
-+#define MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER BIT(21)
-
- #define MTK_WED_WPDMA_CFG_BASE 0x580
- #define MTK_WED_WPDMA_CFG_INT_MASK 0x584
-@@ -286,6 +311,20 @@ struct mtk_wdma_desc {
- #define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n) (0x784 + (_n) * 4)
- #define MTK_WED_WPDMA_RX_D_COHERENT_MIB 0x78c
-
-+#define MTK_WED_WPDMA_RX_D_PREF_CFG 0x7b4
-+#define MTK_WED_WPDMA_RX_D_PREF_EN BIT(0)
-+#define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE GENMASK(12, 8)
-+#define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES GENMASK(21, 16)
-+
-+#define MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX 0x7b8
-+#define MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR BIT(15)
-+
-+#define MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX 0x7bc
-+
-+#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG 0x7c0
-+#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R0_CLR BIT(0)
-+#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R1_CLR BIT(16)
-+
- #define MTK_WED_WDMA_RING_TX 0x800
-
- #define MTK_WED_WDMA_TX_MIB 0x810
-@@ -293,6 +332,18 @@ struct mtk_wdma_desc {
- #define MTK_WED_WDMA_RING_RX(_n) (0x900 + (_n) * 0x10)
- #define MTK_WED_WDMA_RX_THRES(_n) (0x940 + (_n) * 0x4)
-
-+#define MTK_WED_WDMA_RX_PREF_CFG 0x950
-+#define MTK_WED_WDMA_RX_PREF_EN BIT(0)
-+#define MTK_WED_WDMA_RX_PREF_BURST_SIZE GENMASK(12, 8)
-+#define MTK_WED_WDMA_RX_PREF_LOW_THRES GENMASK(21, 16)
-+#define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR BIT(24)
-+#define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR BIT(25)
-+#define MTK_WED_WDMA_RX_PREF_DDONE2_EN BIT(26)
-+
-+#define MTK_WED_WDMA_RX_PREF_FIFO_CFG 0x95C
-+#define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR BIT(0)
-+#define MTK_WED_WDMA_RX_PREF_FIFO_RX1_CLR BIT(16)
-+
- #define MTK_WED_WDMA_GLO_CFG 0xa04
- #define MTK_WED_WDMA_GLO_CFG_TX_DRV_EN BIT(0)
- #define MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK BIT(1)
-@@ -325,6 +376,7 @@ struct mtk_wdma_desc {
- #define MTK_WED_WDMA_INT_TRIGGER_RX_DONE GENMASK(17, 16)
-
- #define MTK_WED_WDMA_INT_CTRL 0xa2c
-+#define MTK_WED_WDMA_INT_POLL_PRD GENMASK(7, 0)
- #define MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL GENMASK(17, 16)
-
- #define MTK_WED_WDMA_CFG_BASE 0xaa0
-@@ -388,6 +440,18 @@ struct mtk_wdma_desc {
- #define MTK_WDMA_INT_GRP1 0x250
- #define MTK_WDMA_INT_GRP2 0x254
-
-+#define MTK_WDMA_PREF_TX_CFG 0x2d0
-+#define MTK_WDMA_PREF_TX_CFG_PREF_EN BIT(0)
-+
-+#define MTK_WDMA_PREF_RX_CFG 0x2dc
-+#define MTK_WDMA_PREF_RX_CFG_PREF_EN BIT(0)
-+
-+#define MTK_WDMA_WRBK_TX_CFG 0x300
-+#define MTK_WDMA_WRBK_TX_CFG_WRBK_EN BIT(30)
-+
-+#define MTK_WDMA_WRBK_RX_CFG 0x344
-+#define MTK_WDMA_WRBK_RX_CFG_WRBK_EN BIT(30)
-+
- #define MTK_PCIE_MIRROR_MAP(n) ((n) ? 0x4 : 0x0)
- #define MTK_PCIE_MIRROR_MAP_EN BIT(0)
- #define MTK_PCIE_MIRROR_MAP_WED_ID BIT(1)
-@@ -401,6 +465,30 @@ struct mtk_wdma_desc {
- #define MTK_WED_RTQM_Q_DBG_BYPASS BIT(5)
- #define MTK_WED_RTQM_TXDMAD_FPORT GENMASK(23, 20)
-
-+#define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT 0xb1c
-+#define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n) (0xb20 + (_n) * 0x4)
-+#define MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT 0xb28
-+#define MTK_WED_RTQM_IGRS0_I2H_PKT_CNT(_n) (0xb2c + (_n) * 0x4)
-+#define MTK_WED_RTQM_IGRS0_FDROP_CNT 0xb34
-+
-+#define MTK_WED_RTQM_IGRS1_I2HW_DMAD_CNT 0xb44
-+#define MTK_WED_RTQM_IGRS1_I2H_DMAD_CNT(_n) (0xb48 + (_n) * 0x4)
-+#define MTK_WED_RTQM_IGRS1_I2HW_PKT_CNT 0xb50
-+#define MTK_WED_RTQM_IGRS1_I2H_PKT_CNT(_n) (0xb54 + (_n) * 0x4)
-+#define MTK_WED_RTQM_IGRS1_FDROP_CNT 0xb5c
-+
-+#define MTK_WED_RTQM_IGRS2_I2HW_DMAD_CNT 0xb6c
-+#define MTK_WED_RTQM_IGRS2_I2H_DMAD_CNT(_n) (0xb70 + (_n) * 0x4)
-+#define MTK_WED_RTQM_IGRS2_I2HW_PKT_CNT 0xb78
-+#define MTK_WED_RTQM_IGRS2_I2H_PKT_CNT(_n) (0xb7c + (_n) * 0x4)
-+#define MTK_WED_RTQM_IGRS2_FDROP_CNT 0xb84
-+
-+#define MTK_WED_RTQM_IGRS3_I2HW_DMAD_CNT 0xb94
-+#define MTK_WED_RTQM_IGRS3_I2H_DMAD_CNT(_n) (0xb98 + (_n) * 0x4)
-+#define MTK_WED_RTQM_IGRS3_I2HW_PKT_CNT 0xba0
-+#define MTK_WED_RTQM_IGRS3_I2H_PKT_CNT(_n) (0xba4 + (_n) * 0x4)
-+#define MTK_WED_RTQM_IGRS3_FDROP_CNT 0xbac
-+
- #define MTK_WED_RTQM_R2H_MIB(_n) (0xb70 + (_n) * 0x4)
- #define MTK_WED_RTQM_R2Q_MIB(_n) (0xb78 + (_n) * 0x4)
- #define MTK_WED_RTQM_Q2N_MIB 0xb80
-@@ -409,6 +497,24 @@ struct mtk_wdma_desc {
- #define MTK_WED_RTQM_Q2B_MIB 0xb8c
- #define MTK_WED_RTQM_PFDBK_MIB 0xb90
-
-+#define MTK_WED_RTQM_ENQ_CFG0 0xbb8
-+#define MTK_WED_RTQM_ENQ_CFG_TXDMAD_FPORT GENMASK(15, 12)
-+
-+#define MTK_WED_RTQM_FDROP_MIB 0xb84
-+#define MTK_WED_RTQM_ENQ_I2Q_DMAD_CNT 0xbbc
-+#define MTK_WED_RTQM_ENQ_I2N_DMAD_CNT 0xbc0
-+#define MTK_WED_RTQM_ENQ_I2Q_PKT_CNT 0xbc4
-+#define MTK_WED_RTQM_ENQ_I2N_PKT_CNT 0xbc8
-+#define MTK_WED_RTQM_ENQ_USED_ENTRY_CNT 0xbcc
-+#define MTK_WED_RTQM_ENQ_ERR_CNT 0xbd0
-+
-+#define MTK_WED_RTQM_DEQ_DMAD_CNT 0xbd8
-+#define MTK_WED_RTQM_DEQ_Q2I_DMAD_CNT 0xbdc
-+#define MTK_WED_RTQM_DEQ_PKT_CNT 0xbe0
-+#define MTK_WED_RTQM_DEQ_Q2I_PKT_CNT 0xbe4
-+#define MTK_WED_RTQM_DEQ_USED_PFDBK_CNT 0xbe8
-+#define MTK_WED_RTQM_DEQ_ERR_CNT 0xbec
-+
- #define MTK_WED_RROQM_GLO_CFG 0xc04
- #define MTK_WED_RROQM_RST_IDX 0xc08
- #define MTK_WED_RROQM_RST_IDX_MIOD BIT(0)
-@@ -458,7 +564,116 @@ struct mtk_wdma_desc {
- #define MTK_WED_RX_BM_INTF 0xd9c
- #define MTK_WED_RX_BM_ERR_STS 0xda8
-
-+#define MTK_RRO_IND_CMD_SIGNATURE 0xe00
-+#define MTK_RRO_IND_CMD_DMA_IDX GENMASK(11, 0)
-+#define MTK_RRO_IND_CMD_MAGIC_CNT GENMASK(30, 28)
-+
-+#define MTK_WED_IND_CMD_RX_CTRL0 0xe04
-+#define MTK_WED_IND_CMD_PROC_IDX GENMASK(11, 0)
-+#define MTK_WED_IND_CMD_PREFETCH_FREE_CNT GENMASK(19, 16)
-+#define MTK_WED_IND_CMD_MAGIC_CNT GENMASK(30, 28)
-+
-+#define MTK_WED_IND_CMD_RX_CTRL1 0xe08
-+#define MTK_WED_IND_CMD_RX_CTRL2 0xe0c
-+#define MTK_WED_IND_CMD_MAX_CNT GENMASK(11, 0)
-+#define MTK_WED_IND_CMD_BASE_M GENMASK(19, 16)
-+
-+#define MTK_WED_RRO_CFG0 0xe10
-+#define MTK_WED_RRO_CFG1 0xe14
-+#define MTK_WED_RRO_CFG1_MAX_WIN_SZ GENMASK(31, 29)
-+#define MTK_WED_RRO_CFG1_ACK_SN_BASE_M GENMASK(19, 16)
-+#define MTK_WED_RRO_CFG1_PARTICL_SE_ID GENMASK(11, 0)
-+
-+#define MTK_WED_ADDR_ELEM_CFG0 0xe18
-+#define MTK_WED_ADDR_ELEM_CFG1 0xe1c
-+#define MTK_WED_ADDR_ELEM_PREFETCH_FREE_CNT GENMASK(19, 16)
-+
-+#define MTK_WED_ADDR_ELEM_TBL_CFG 0xe20
-+#define MTK_WED_ADDR_ELEM_TBL_OFFSET GENMASK(6, 0)
-+#define MTK_WED_ADDR_ELEM_TBL_RD_RDY BIT(28)
-+#define MTK_WED_ADDR_ELEM_TBL_WR_RDY BIT(29)
-+#define MTK_WED_ADDR_ELEM_TBL_RD BIT(30)
-+#define MTK_WED_ADDR_ELEM_TBL_WR BIT(31)
-+
-+#define MTK_WED_RADDR_ELEM_TBL_WDATA 0xe24
-+#define MTK_WED_RADDR_ELEM_TBL_RDATA 0xe28
-+
-+#define MTK_WED_PN_CHECK_CFG 0xe30
-+#define MTK_WED_PN_CHECK_SE_ID GENMASK(11, 0)
-+#define MTK_WED_PN_CHECK_RD_RDY BIT(28)
-+#define MTK_WED_PN_CHECK_WR_RDY BIT(29)
-+#define MTK_WED_PN_CHECK_RD BIT(30)
-+#define MTK_WED_PN_CHECK_WR BIT(31)
-+
-+#define MTK_WED_PN_CHECK_WDATA_M 0xe38
-+#define MTK_WED_PN_CHECK_IS_FIRST BIT(17)
-+
-+#define MTK_WED_RRO_MSDU_PG_RING_CFG(_n) (0xe44 + (_n) * 0x8)
-+
-+#define MTK_WED_RRO_MSDU_PG_RING2_CFG 0xe58
-+#define MTK_WED_RRO_MSDU_PG_DRV_CLR BIT(26)
-+#define MTK_WED_RRO_MSDU_PG_DRV_EN BIT(31)
-+
-+#define MTK_WED_RRO_MSDU_PG_CTRL0(_n) (0xe5c + (_n) * 0xc)
-+#define MTK_WED_RRO_MSDU_PG_CTRL1(_n) (0xe60 + (_n) * 0xc)
-+#define MTK_WED_RRO_MSDU_PG_CTRL2(_n) (0xe64 + (_n) * 0xc)
-+
-+#define MTK_WED_RRO_RX_D_RX(_n) (0xe80 + (_n) * 0x10)
-+
-+#define MTK_WED_RRO_RX_MAGIC_CNT BIT(13)
-+
-+#define MTK_WED_RRO_RX_D_CFG(_n) (0xea0 + (_n) * 0x4)
-+#define MTK_WED_RRO_RX_D_DRV_CLR BIT(26)
-+#define MTK_WED_RRO_RX_D_DRV_EN BIT(31)
-+
-+#define MTK_WED_RRO_PG_BM_RX_DMAM 0xeb0
-+#define MTK_WED_RRO_PG_BM_RX_SDL0 GENMASK(13, 0)
-+
-+#define MTK_WED_RRO_PG_BM_BASE 0xeb4
-+#define MTK_WED_RRO_PG_BM_INIT_PTR 0xeb8
-+#define MTK_WED_RRO_PG_BM_SW_TAIL_IDX GENMASK(15, 0)
-+#define MTK_WED_RRO_PG_BM_INIT_SW_TAIL_IDX BIT(16)
-+
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_RX 0xeec
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_EN BIT(0)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_CLR BIT(1)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_DONE_TRIG GENMASK(6, 2)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_EN BIT(8)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_CLR BIT(9)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_DONE_TRIG GENMASK(14, 10)
-+
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_MSDU_PG 0xef4
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_EN BIT(0)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_CLR BIT(1)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_DONE_TRIG GENMASK(6, 2)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_EN BIT(8)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_CLR BIT(9)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_DONE_TRIG GENMASK(14, 10)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_EN BIT(16)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR BIT(17)
-+#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG GENMASK(22, 18)
-+
-+#define MTK_WED_RX_IND_CMD_CNT0 0xf20
-+#define MTK_WED_RX_IND_CMD_DBG_CNT_EN BIT(31)
-+
-+#define MTK_WED_RX_IND_CMD_CNT(_n) (0xf20 + (_n) * 0x4)
-+#define MTK_WED_IND_CMD_MAGIC_CNT_FAIL_CNT GENMASK(15, 0)
-+
-+#define MTK_WED_RX_ADDR_ELEM_CNT(_n) (0xf48 + (_n) * 0x4)
-+#define MTK_WED_ADDR_ELEM_SIG_FAIL_CNT GENMASK(15, 0)
-+#define MTK_WED_ADDR_ELEM_FIRST_SIG_FAIL_CNT GENMASK(31, 16)
-+#define MTK_WED_ADDR_ELEM_ACKSN_CNT GENMASK(27, 0)
-+
-+#define MTK_WED_RX_MSDU_PG_CNT(_n) (0xf5c + (_n) * 0x4)
-+
-+#define MTK_WED_RX_PN_CHK_CNT 0xf70
-+#define MTK_WED_PN_CHK_FAIL_CNT GENMASK(15, 0)
-+
- #define MTK_WED_WOCPU_VIEW_MIOD_BASE 0x8000
- #define MTK_WED_PCIE_INT_MASK 0x0
-
-+#define MTK_WED_PCIE_BASE 0x11280000
-+#define MTK_WED_PCIE_BASE0 0x11300000
-+#define MTK_WED_PCIE_BASE1 0x11310000
-+#define MTK_WED_PCIE_BASE2 0x11290000
- #endif
---- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-@@ -91,6 +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 MTK_WO_MCU_CFG_LS_BASE 0
- #define MTK_WO_MCU_CFG_LS_HW_VER_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x000)
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -138,6 +138,8 @@ struct mtk_wed_device {
- u32 wpdma_rx;
-
- bool wcid_512;
-+ bool hw_rro;
-+ bool msi;
-
- u16 token_start;
- unsigned int nbuf;
-@@ -211,10 +213,12 @@ mtk_wed_device_attach(struct mtk_wed_dev
- return ret;
- }
-
--static inline bool
--mtk_wed_get_rx_capa(struct mtk_wed_device *dev)
-+static inline bool mtk_wed_get_rx_capa(struct mtk_wed_device *dev)
- {
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-+ if (dev->version == 3)
-+ return dev->wlan.hw_rro;
-+
- return dev->version != 1;
- #else
- return false;
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Mon, 18 Sep 2023 12:29:14 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: refactor mtk_wed_check_wfdma_rx_fill
- routine
-
-Refactor mtk_wed_check_wfdma_rx_fill() in order to be reused adding HW
-receive offload support for MT7988 SoC.
-
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -586,22 +586,15 @@ mtk_wed_set_512_support(struct mtk_wed_d
- }
- }
-
--#define MTK_WFMDA_RX_DMA_EN BIT(2)
--static void
--mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, int idx)
-+static int
-+mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev,
-+ struct mtk_wed_ring *ring)
- {
-- u32 val;
- int i;
-
-- if (!(dev->rx_ring[idx].flags & MTK_WED_RING_CONFIGURED))
-- return; /* queue is not configured by mt76 */
--
- for (i = 0; i < 3; i++) {
-- u32 cur_idx;
-+ u32 cur_idx = readl(ring->wpdma + MTK_WED_RING_OFS_CPU_IDX);
-
-- cur_idx = wed_r32(dev,
-- MTK_WED_WPDMA_RING_RX_DATA(idx) +
-- MTK_WED_RING_OFS_CPU_IDX);
- if (cur_idx == MTK_WED_RX_RING_SIZE - 1)
- break;
-
-@@ -610,12 +603,10 @@ mtk_wed_check_wfdma_rx_fill(struct mtk_w
-
- if (i == 3) {
- dev_err(dev->hw->dev, "rx dma enable failed\n");
-- return;
-+ return -ETIMEDOUT;
- }
-
-- val = wifi_r32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base) |
-- MTK_WFMDA_RX_DMA_EN;
-- wifi_w32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base, val);
-+ return 0;
- }
-
- static void
-@@ -1545,6 +1536,7 @@ mtk_wed_configure_irq(struct mtk_wed_dev
- wed_w32(dev, MTK_WED_INT_MASK, irq_mask);
- }
-
-+#define MTK_WFMDA_RX_DMA_EN BIT(2)
- static void
- mtk_wed_dma_enable(struct mtk_wed_device *dev)
- {
-@@ -1632,8 +1624,26 @@ mtk_wed_dma_enable(struct mtk_wed_device
- wdma_set(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
- }
-
-- for (i = 0; i < MTK_WED_RX_QUEUES; i++)
-- mtk_wed_check_wfdma_rx_fill(dev, i);
-+ for (i = 0; i < MTK_WED_RX_QUEUES; i++) {
-+ struct mtk_wed_ring *ring = &dev->rx_ring[i];
-+ u32 val;
-+
-+ if (!(ring->flags & MTK_WED_RING_CONFIGURED))
-+ continue; /* queue is not configured by mt76 */
-+
-+ if (mtk_wed_check_wfdma_rx_fill(dev, ring)) {
-+ dev_err(dev->hw->dev,
-+ "rx_ring(%d) dma enable failed\n", i);
-+ continue;
-+ }
-+
-+ val = wifi_r32(dev,
-+ dev->wlan.wpdma_rx_glo -
-+ dev->wlan.phy_base) | MTK_WFMDA_RX_DMA_EN;
-+ wifi_w32(dev,
-+ dev->wlan.wpdma_rx_glo - dev->wlan.phy_base,
-+ val);
-+ }
- }
-
- static void
+++ /dev/null
-From: Sujuan Chen <sujuan.chen@mediatek.com>
-Date: Mon, 18 Sep 2023 12:29:15 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: introduce partial AMSDU offload
- support for MT7988
-
-Introduce partial AMSDU offload support for MT7988 SoC in order to merge
-in hw packets belonging to the same AMSDU before passing them to the
-WLAN nic.
-
-Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -425,7 +425,8 @@ int mtk_foe_entry_set_pppoe(struct mtk_e
- }
-
- int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
-- int wdma_idx, int txq, int bss, int wcid)
-+ int wdma_idx, int txq, int bss, int wcid,
-+ bool amsdu_en)
- {
- struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
- u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
-@@ -437,6 +438,7 @@ int mtk_foe_entry_set_wdma(struct mtk_et
- MTK_FOE_IB2_WDMA_WINFO_V2;
- l2->w3info = FIELD_PREP(MTK_FOE_WINFO_WCID_V3, wcid) |
- FIELD_PREP(MTK_FOE_WINFO_BSS_V3, bss);
-+ l2->amsdu = FIELD_PREP(MTK_FOE_WINFO_AMSDU_EN, amsdu_en);
- break;
- case 2:
- *ib2 &= ~MTK_FOE_IB2_PORT_MG_V2;
---- a/drivers/net/ethernet/mediatek/mtk_ppe.h
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -88,13 +88,13 @@ enum {
- #define MTK_FOE_WINFO_BSS_V3 GENMASK(23, 16)
- #define MTK_FOE_WINFO_WCID_V3 GENMASK(15, 0)
-
--#define MTK_FOE_WINFO_PAO_USR_INFO GENMASK(15, 0)
--#define MTK_FOE_WINFO_PAO_TID GENMASK(19, 16)
--#define MTK_FOE_WINFO_PAO_IS_FIXEDRATE BIT(20)
--#define MTK_FOE_WINFO_PAO_IS_PRIOR BIT(21)
--#define MTK_FOE_WINFO_PAO_IS_SP BIT(22)
--#define MTK_FOE_WINFO_PAO_HF BIT(23)
--#define MTK_FOE_WINFO_PAO_AMSDU_EN BIT(24)
-+#define MTK_FOE_WINFO_AMSDU_USR_INFO GENMASK(15, 0)
-+#define MTK_FOE_WINFO_AMSDU_TID GENMASK(19, 16)
-+#define MTK_FOE_WINFO_AMSDU_IS_FIXEDRATE BIT(20)
-+#define MTK_FOE_WINFO_AMSDU_IS_PRIOR BIT(21)
-+#define MTK_FOE_WINFO_AMSDU_IS_SP BIT(22)
-+#define MTK_FOE_WINFO_AMSDU_HF BIT(23)
-+#define MTK_FOE_WINFO_AMSDU_EN BIT(24)
-
- enum {
- MTK_FOE_STATE_INVALID,
-@@ -123,7 +123,7 @@ struct mtk_foe_mac_info {
-
- /* netsys_v3 */
- u32 w3info;
-- u32 wpao;
-+ u32 amsdu;
- };
-
- /* software-only entry type */
-@@ -392,7 +392,8 @@ int mtk_foe_entry_set_vlan(struct mtk_et
- int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry,
- int sid);
- int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
-- int wdma_idx, int txq, int bss, int wcid);
-+ int wdma_idx, int txq, int bss, int wcid,
-+ bool amsdu_en);
- int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry,
- unsigned int queue);
- int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
---- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -111,6 +111,7 @@ mtk_flow_get_wdma_info(struct net_device
- info->queue = path->mtk_wdma.queue;
- info->bss = path->mtk_wdma.bss;
- info->wcid = path->mtk_wdma.wcid;
-+ info->amsdu = path->mtk_wdma.amsdu;
-
- return 0;
- }
-@@ -192,7 +193,7 @@ mtk_flow_set_output_device(struct mtk_et
-
- if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
- mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue,
-- info.bss, info.wcid);
-+ info.bss, info.wcid, info.amsdu);
- if (mtk_is_netsys_v2_or_greater(eth)) {
- switch (info.wdma_idx) {
- case 0:
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -30,6 +30,8 @@
- #define MTK_WED_RX_PAGE_BUF_PER_PAGE (PAGE_SIZE / 128)
- #define MTK_WED_RX_RING_SIZE 1536
- #define MTK_WED_RX_PG_BM_CNT 8192
-+#define MTK_WED_AMSDU_BUF_SIZE (PAGE_SIZE << 4)
-+#define MTK_WED_AMSDU_NPAGES 32
-
- #define MTK_WED_TX_RING_SIZE 2048
- #define MTK_WED_WDMA_RING_SIZE 1024
-@@ -173,6 +175,23 @@ mtk_wdma_rx_reset(struct mtk_wed_device
- return ret;
- }
-
-+static u32
-+mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
-+{
-+ return !!(wed_r32(dev, reg) & mask);
-+}
-+
-+static int
-+mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
-+{
-+ int sleep = 15000;
-+ int timeout = 100 * sleep;
-+ u32 val;
-+
-+ return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
-+ timeout, false, dev, reg, mask);
-+}
-+
- static void
- mtk_wdma_tx_reset(struct mtk_wed_device *dev)
- {
-@@ -336,6 +355,118 @@ out:
- }
-
- static int
-+mtk_wed_amsdu_buffer_alloc(struct mtk_wed_device *dev)
-+{
-+ struct mtk_wed_hw *hw = dev->hw;
-+ struct mtk_wed_amsdu *wed_amsdu;
-+ int i;
-+
-+ if (!mtk_wed_is_v3_or_greater(hw))
-+ return 0;
-+
-+ wed_amsdu = devm_kcalloc(hw->dev, MTK_WED_AMSDU_NPAGES,
-+ sizeof(*wed_amsdu), GFP_KERNEL);
-+ if (!wed_amsdu)
-+ return -ENOMEM;
-+
-+ for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) {
-+ void *ptr;
-+
-+ /* each segment is 64K */
-+ ptr = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN |
-+ __GFP_ZERO | __GFP_COMP |
-+ GFP_DMA32,
-+ get_order(MTK_WED_AMSDU_BUF_SIZE));
-+ if (!ptr)
-+ goto error;
-+
-+ wed_amsdu[i].txd = ptr;
-+ wed_amsdu[i].txd_phy = dma_map_single(hw->dev, ptr,
-+ MTK_WED_AMSDU_BUF_SIZE,
-+ DMA_TO_DEVICE);
-+ if (dma_mapping_error(hw->dev, wed_amsdu[i].txd_phy))
-+ goto error;
-+ }
-+ dev->hw->wed_amsdu = wed_amsdu;
-+
-+ return 0;
-+
-+error:
-+ for (i--; i >= 0; i--)
-+ dma_unmap_single(hw->dev, wed_amsdu[i].txd_phy,
-+ MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE);
-+ return -ENOMEM;
-+}
-+
-+static void
-+mtk_wed_amsdu_free_buffer(struct mtk_wed_device *dev)
-+{
-+ struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu;
-+ int i;
-+
-+ if (!wed_amsdu)
-+ return;
-+
-+ for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) {
-+ dma_unmap_single(dev->hw->dev, wed_amsdu[i].txd_phy,
-+ MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE);
-+ free_pages((unsigned long)wed_amsdu[i].txd,
-+ get_order(MTK_WED_AMSDU_BUF_SIZE));
-+ }
-+}
-+
-+static int
-+mtk_wed_amsdu_init(struct mtk_wed_device *dev)
-+{
-+ struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu;
-+ int i, ret;
-+
-+ if (!wed_amsdu)
-+ return 0;
-+
-+ for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++)
-+ wed_w32(dev, MTK_WED_AMSDU_HIFTXD_BASE_L(i),
-+ wed_amsdu[i].txd_phy);
-+
-+ /* init all sta parameter */
-+ wed_w32(dev, MTK_WED_AMSDU_STA_INFO_INIT, MTK_WED_AMSDU_STA_RMVL |
-+ MTK_WED_AMSDU_STA_WTBL_HDRT_MODE |
-+ FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_LEN,
-+ dev->wlan.amsdu_max_len >> 8) |
-+ FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_NUM,
-+ dev->wlan.amsdu_max_subframes));
-+
-+ wed_w32(dev, MTK_WED_AMSDU_STA_INFO, MTK_WED_AMSDU_STA_INFO_DO_INIT);
-+
-+ ret = mtk_wed_poll_busy(dev, MTK_WED_AMSDU_STA_INFO,
-+ MTK_WED_AMSDU_STA_INFO_DO_INIT);
-+ if (ret) {
-+ dev_err(dev->hw->dev, "amsdu initialization failed\n");
-+ return ret;
-+ }
-+
-+ /* init partial amsdu offload txd src */
-+ wed_set(dev, MTK_WED_AMSDU_HIFTXD_CFG,
-+ FIELD_PREP(MTK_WED_AMSDU_HIFTXD_SRC, dev->hw->index));
-+
-+ /* init qmem */
-+ wed_set(dev, MTK_WED_AMSDU_PSE, MTK_WED_AMSDU_PSE_RESET);
-+ ret = mtk_wed_poll_busy(dev, MTK_WED_MON_AMSDU_QMEM_STS1, BIT(29));
-+ if (ret) {
-+ pr_info("%s: amsdu qmem initialization failed\n", __func__);
-+ return ret;
-+ }
-+
-+ /* eagle E1 PCIE1 tx ring 22 flow control issue */
-+ if (dev->wlan.id == 0x7991)
-+ wed_clr(dev, MTK_WED_AMSDU_FIFO, MTK_WED_AMSDU_IS_PRIOR0_RING);
-+
-+ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN);
-+
-+ return 0;
-+}
-+
-+static int
- mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev)
- {
- u32 desc_size = dev->hw->soc->tx_ring_desc_size;
-@@ -708,6 +839,7 @@ __mtk_wed_detach(struct mtk_wed_device *
-
- mtk_wdma_rx_reset(dev);
- mtk_wed_reset(dev, MTK_WED_RESET_WED);
-+ mtk_wed_amsdu_free_buffer(dev);
- mtk_wed_free_tx_buffer(dev);
- mtk_wed_free_tx_rings(dev);
-
-@@ -1128,23 +1260,6 @@ mtk_wed_ring_reset(struct mtk_wed_ring *
- }
- }
-
--static u32
--mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
--{
-- return !!(wed_r32(dev, reg) & mask);
--}
--
--static int
--mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
--{
-- int sleep = 15000;
-- int timeout = 100 * sleep;
-- u32 val;
--
-- return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
-- timeout, false, dev, reg, mask);
--}
--
- static int
- mtk_wed_rx_reset(struct mtk_wed_device *dev)
- {
-@@ -1691,6 +1806,7 @@ mtk_wed_start(struct mtk_wed_device *dev
- }
-
- mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
-+ mtk_wed_amsdu_init(dev);
-
- mtk_wed_dma_enable(dev);
- dev->running = true;
-@@ -1747,6 +1863,10 @@ mtk_wed_attach(struct mtk_wed_device *de
- if (ret)
- goto out;
-
-+ ret = mtk_wed_amsdu_buffer_alloc(dev);
-+ if (ret)
-+ goto out;
-+
- if (mtk_wed_get_rx_capa(dev)) {
- ret = mtk_wed_rro_alloc(dev);
- if (ret)
---- a/drivers/net/ethernet/mediatek/mtk_wed.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.h
-@@ -25,6 +25,11 @@ struct mtk_wed_soc_data {
- u32 wdma_desc_size;
- };
-
-+struct mtk_wed_amsdu {
-+ void *txd;
-+ dma_addr_t txd_phy;
-+};
-+
- struct mtk_wed_hw {
- const struct mtk_wed_soc_data *soc;
- struct device_node *node;
-@@ -38,6 +43,7 @@ struct mtk_wed_hw {
- struct dentry *debugfs_dir;
- struct mtk_wed_device *wed_dev;
- struct mtk_wed_wo *wed_wo;
-+ struct mtk_wed_amsdu *wed_amsdu;
- u32 pcie_base;
- u32 debugfs_reg;
- u32 num_flows;
-@@ -52,6 +58,7 @@ struct mtk_wdma_info {
- u8 queue;
- u16 wcid;
- u8 bss;
-+ u8 amsdu;
- };
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
---- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-@@ -672,6 +672,82 @@ struct mtk_wdma_desc {
- #define MTK_WED_WOCPU_VIEW_MIOD_BASE 0x8000
- #define MTK_WED_PCIE_INT_MASK 0x0
-
-+#define MTK_WED_AMSDU_FIFO 0x1800
-+#define MTK_WED_AMSDU_IS_PRIOR0_RING BIT(10)
-+
-+#define MTK_WED_AMSDU_STA_INFO 0x01810
-+#define MTK_WED_AMSDU_STA_INFO_DO_INIT BIT(0)
-+#define MTK_WED_AMSDU_STA_INFO_SET_INIT BIT(1)
-+
-+#define MTK_WED_AMSDU_STA_INFO_INIT 0x01814
-+#define MTK_WED_AMSDU_STA_WTBL_HDRT_MODE BIT(0)
-+#define MTK_WED_AMSDU_STA_RMVL BIT(1)
-+#define MTK_WED_AMSDU_STA_MAX_AMSDU_LEN GENMASK(7, 2)
-+#define MTK_WED_AMSDU_STA_MAX_AMSDU_NUM GENMASK(11, 8)
-+
-+#define MTK_WED_AMSDU_HIFTXD_BASE_L(_n) (0x1980 + (_n) * 0x4)
-+
-+#define MTK_WED_AMSDU_PSE 0x1910
-+#define MTK_WED_AMSDU_PSE_RESET BIT(16)
-+
-+#define MTK_WED_AMSDU_HIFTXD_CFG 0x1968
-+#define MTK_WED_AMSDU_HIFTXD_SRC GENMASK(16, 15)
-+
-+#define MTK_WED_MON_AMSDU_FIFO_DMAD 0x1a34
-+
-+#define MTK_WED_MON_AMSDU_ENG_DMAD(_n) (0x1a80 + (_n) * 0x50)
-+#define MTK_WED_MON_AMSDU_ENG_QFPL(_n) (0x1a84 + (_n) * 0x50)
-+#define MTK_WED_MON_AMSDU_ENG_QENI(_n) (0x1a88 + (_n) * 0x50)
-+#define MTK_WED_MON_AMSDU_ENG_QENO(_n) (0x1a8c + (_n) * 0x50)
-+#define MTK_WED_MON_AMSDU_ENG_MERG(_n) (0x1a90 + (_n) * 0x50)
-+
-+#define MTK_WED_MON_AMSDU_ENG_CNT8(_n) (0x1a94 + (_n) * 0x50)
-+#define MTK_WED_AMSDU_ENG_MAX_QGPP_CNT GENMASK(10, 0)
-+#define MTK_WED_AMSDU_ENG_MAX_PL_CNT GENMASK(27, 16)
-+
-+#define MTK_WED_MON_AMSDU_ENG_CNT9(_n) (0x1a98 + (_n) * 0x50)
-+#define MTK_WED_AMSDU_ENG_CUR_ENTRY GENMASK(10, 0)
-+#define MTK_WED_AMSDU_ENG_MAX_BUF_MERGED GENMASK(20, 16)
-+#define MTK_WED_AMSDU_ENG_MAX_MSDU_MERGED GENMASK(28, 24)
-+
-+#define MTK_WED_MON_AMSDU_QMEM_STS1 0x1e04
-+
-+#define MTK_WED_MON_AMSDU_QMEM_CNT(_n) (0x1e0c + (_n) * 0x4)
-+#define MTK_WED_AMSDU_QMEM_FQ_CNT GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_SP_QCNT GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID0_QCNT GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID1_QCNT GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID2_QCNT GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID3_QCNT GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID4_QCNT GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID5_QCNT GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID6_QCNT GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID7_QCNT GENMASK(11, 0)
-+
-+#define MTK_WED_MON_AMSDU_QMEM_PTR(_n) (0x1e20 + (_n) * 0x4)
-+#define MTK_WED_AMSDU_QMEM_FQ_HEAD GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_SP_QHEAD GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID0_QHEAD GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID1_QHEAD GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID2_QHEAD GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID3_QHEAD GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID4_QHEAD GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID5_QHEAD GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID6_QHEAD GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID7_QHEAD GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_FQ_TAIL GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_SP_QTAIL GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID0_QTAIL GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID1_QTAIL GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID2_QTAIL GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID3_QTAIL GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID4_QTAIL GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID5_QTAIL GENMASK(11, 0)
-+#define MTK_WED_AMSDU_QMEM_TID6_QTAIL GENMASK(27, 16)
-+#define MTK_WED_AMSDU_QMEM_TID7_QTAIL GENMASK(11, 0)
-+
-+#define MTK_WED_MON_AMSDU_HIFTXD_FETCH_MSDU(_n) (0x1ec4 + (_n) * 0x4)
-+
- #define MTK_WED_PCIE_BASE 0x11280000
- #define MTK_WED_PCIE_BASE0 0x11300000
- #define MTK_WED_PCIE_BASE1 0x11310000
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -917,6 +917,7 @@ struct net_device_path {
- u8 queue;
- u16 wcid;
- u8 bss;
-+ u8 amsdu;
- } mtk_wdma;
- };
- };
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -128,6 +128,7 @@ struct mtk_wed_device {
- enum mtk_wed_bus_tye bus_type;
- void __iomem *base;
- u32 phy_base;
-+ u32 id;
-
- u32 wpdma_phys;
- u32 wpdma_int;
-@@ -146,10 +147,12 @@ struct mtk_wed_device {
- unsigned int rx_nbuf;
- unsigned int rx_npkt;
- unsigned int rx_size;
-+ unsigned int amsdu_max_len;
-
- u8 tx_tbit[MTK_WED_TX_QUEUES];
- u8 rx_tbit[MTK_WED_RX_QUEUES];
- u8 txfree_tbit;
-+ u8 amsdu_max_subframes;
-
- u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id);
- int (*offload_enable)(struct mtk_wed_device *wed);
-@@ -223,6 +226,15 @@ static inline bool mtk_wed_get_rx_capa(s
- #else
- return false;
- #endif
-+}
-+
-+static inline bool mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev)
-+{
-+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
-+ return dev->version == 3;
-+#else
-+ return false;
-+#endif
- }
-
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+++ /dev/null
-From: Sujuan Chen <sujuan.chen@mediatek.com>
-Date: Mon, 18 Sep 2023 12:29:16 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: introduce hw_rro support for MT7988
-
-MT7988 SoC support 802.11 receive reordering offload in hw while
-MT7986 SoC implements it through the firmware running on the mcu.
-
-Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -27,7 +27,7 @@
- #define MTK_WED_BUF_SIZE 2048
- #define MTK_WED_PAGE_BUF_SIZE 128
- #define MTK_WED_BUF_PER_PAGE (PAGE_SIZE / 2048)
--#define MTK_WED_RX_PAGE_BUF_PER_PAGE (PAGE_SIZE / 128)
-+#define MTK_WED_RX_BUF_PER_PAGE (PAGE_SIZE / MTK_WED_PAGE_BUF_SIZE)
- #define MTK_WED_RX_RING_SIZE 1536
- #define MTK_WED_RX_PG_BM_CNT 8192
- #define MTK_WED_AMSDU_BUF_SIZE (PAGE_SIZE << 4)
-@@ -597,6 +597,68 @@ free_pagelist:
- }
-
- static int
-+mtk_wed_hwrro_buffer_alloc(struct mtk_wed_device *dev)
-+{
-+ int n_pages = MTK_WED_RX_PG_BM_CNT / MTK_WED_RX_BUF_PER_PAGE;
-+ struct mtk_wed_buf *page_list;
-+ struct mtk_wed_bm_desc *desc;
-+ dma_addr_t desc_phys;
-+ int i, page_idx = 0;
-+
-+ if (!dev->wlan.hw_rro)
-+ return 0;
-+
-+ page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL);
-+ if (!page_list)
-+ return -ENOMEM;
-+
-+ dev->hw_rro.size = dev->wlan.rx_nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
-+ dev->hw_rro.pages = page_list;
-+ desc = dma_alloc_coherent(dev->hw->dev,
-+ dev->wlan.rx_nbuf * sizeof(*desc),
-+ &desc_phys, GFP_KERNEL);
-+ if (!desc)
-+ return -ENOMEM;
-+
-+ dev->hw_rro.desc = desc;
-+ dev->hw_rro.desc_phys = desc_phys;
-+
-+ for (i = 0; i < MTK_WED_RX_PG_BM_CNT; i += MTK_WED_RX_BUF_PER_PAGE) {
-+ dma_addr_t page_phys, buf_phys;
-+ struct page *page;
-+ int s;
-+
-+ page = __dev_alloc_page(GFP_KERNEL);
-+ if (!page)
-+ return -ENOMEM;
-+
-+ page_phys = dma_map_page(dev->hw->dev, page, 0, PAGE_SIZE,
-+ DMA_BIDIRECTIONAL);
-+ if (dma_mapping_error(dev->hw->dev, page_phys)) {
-+ __free_page(page);
-+ return -ENOMEM;
-+ }
-+
-+ page_list[page_idx].p = page;
-+ page_list[page_idx++].phy_addr = page_phys;
-+ dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE,
-+ DMA_BIDIRECTIONAL);
-+
-+ buf_phys = page_phys;
-+ for (s = 0; s < MTK_WED_RX_BUF_PER_PAGE; s++) {
-+ desc->buf0 = cpu_to_le32(buf_phys);
-+ buf_phys += MTK_WED_PAGE_BUF_SIZE;
-+ desc++;
-+ }
-+
-+ dma_sync_single_for_device(dev->hw->dev, page_phys, PAGE_SIZE,
-+ DMA_BIDIRECTIONAL);
-+ }
-+
-+ return 0;
-+}
-+
-+static int
- mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev)
- {
- struct mtk_wed_bm_desc *desc;
-@@ -613,7 +675,42 @@ mtk_wed_rx_buffer_alloc(struct mtk_wed_d
- dev->rx_buf_ring.desc_phys = desc_phys;
- dev->wlan.init_rx_buf(dev, dev->wlan.rx_npkt);
-
-- return 0;
-+ return mtk_wed_hwrro_buffer_alloc(dev);
-+}
-+
-+static void
-+mtk_wed_hwrro_free_buffer(struct mtk_wed_device *dev)
-+{
-+ struct mtk_wed_buf *page_list = dev->hw_rro.pages;
-+ struct mtk_wed_bm_desc *desc = dev->hw_rro.desc;
-+ int i, page_idx = 0;
-+
-+ if (!dev->wlan.hw_rro)
-+ return;
-+
-+ if (!page_list)
-+ return;
-+
-+ if (!desc)
-+ goto free_pagelist;
-+
-+ for (i = 0; i < MTK_WED_RX_PG_BM_CNT; i += MTK_WED_RX_BUF_PER_PAGE) {
-+ dma_addr_t buf_addr = page_list[page_idx].phy_addr;
-+ void *page = page_list[page_idx++].p;
-+
-+ if (!page)
-+ break;
-+
-+ dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE,
-+ DMA_BIDIRECTIONAL);
-+ __free_page(page);
-+ }
-+
-+ dma_free_coherent(dev->hw->dev, dev->hw_rro.size * sizeof(*desc),
-+ desc, dev->hw_rro.desc_phys);
-+
-+free_pagelist:
-+ kfree(page_list);
- }
-
- static void
-@@ -627,6 +724,28 @@ mtk_wed_free_rx_buffer(struct mtk_wed_de
- dev->wlan.release_rx_buf(dev);
- dma_free_coherent(dev->hw->dev, dev->rx_buf_ring.size * sizeof(*desc),
- desc, dev->rx_buf_ring.desc_phys);
-+
-+ mtk_wed_hwrro_free_buffer(dev);
-+}
-+
-+static void
-+mtk_wed_hwrro_init(struct mtk_wed_device *dev)
-+{
-+ if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro)
-+ return;
-+
-+ wed_set(dev, MTK_WED_RRO_PG_BM_RX_DMAM,
-+ FIELD_PREP(MTK_WED_RRO_PG_BM_RX_SDL0, 128));
-+
-+ wed_w32(dev, MTK_WED_RRO_PG_BM_BASE, dev->hw_rro.desc_phys);
-+
-+ wed_w32(dev, MTK_WED_RRO_PG_BM_INIT_PTR,
-+ MTK_WED_RRO_PG_BM_INIT_SW_TAIL_IDX |
-+ FIELD_PREP(MTK_WED_RRO_PG_BM_SW_TAIL_IDX,
-+ MTK_WED_RX_PG_BM_CNT));
-+
-+ /* enable rx_page_bm to fetch dmad */
-+ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN);
- }
-
- static void
-@@ -640,6 +759,8 @@ mtk_wed_rx_buffer_hw_init(struct mtk_wed
- wed_w32(dev, MTK_WED_RX_BM_DYN_ALLOC_TH,
- FIELD_PREP(MTK_WED_RX_BM_DYN_ALLOC_TH_H, 0xffff));
- wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN);
-+
-+ mtk_wed_hwrro_init(dev);
- }
-
- static void
-@@ -934,6 +1055,8 @@ mtk_wed_bus_init(struct mtk_wed_device *
- static void
- mtk_wed_set_wpdma(struct mtk_wed_device *dev)
- {
-+ int i;
-+
- if (mtk_wed_is_v1(dev->hw)) {
- wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys);
- return;
-@@ -951,6 +1074,15 @@ mtk_wed_set_wpdma(struct mtk_wed_device
-
- wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
- wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring0, dev->wlan.wpdma_rx);
-+
-+ if (!dev->wlan.hw_rro)
-+ return;
-+
-+ wed_w32(dev, MTK_WED_RRO_RX_D_CFG(0), dev->wlan.wpdma_rx_rro[0]);
-+ wed_w32(dev, MTK_WED_RRO_RX_D_CFG(1), dev->wlan.wpdma_rx_rro[1]);
-+ for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++)
-+ wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING_CFG(i),
-+ dev->wlan.wpdma_rx_pg + i * 0x10);
- }
-
- static void
-@@ -1762,6 +1894,165 @@ mtk_wed_dma_enable(struct mtk_wed_device
- }
-
- static void
-+mtk_wed_start_hw_rro(struct mtk_wed_device *dev, u32 irq_mask, bool reset)
-+{
-+ int i;
-+
-+ wed_w32(dev, MTK_WED_WPDMA_INT_MASK, irq_mask);
-+ wed_w32(dev, MTK_WED_INT_MASK, irq_mask);
-+
-+ if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro)
-+ return;
-+
-+ wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR);
-+ wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
-+ MTK_WED_RRO_MSDU_PG_DRV_CLR);
-+
-+ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_RX,
-+ MTK_WED_WPDMA_INT_CTRL_RRO_RX0_EN |
-+ MTK_WED_WPDMA_INT_CTRL_RRO_RX0_CLR |
-+ MTK_WED_WPDMA_INT_CTRL_RRO_RX1_EN |
-+ MTK_WED_WPDMA_INT_CTRL_RRO_RX1_CLR |
-+ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX0_DONE_TRIG,
-+ dev->wlan.rro_rx_tbit[0]) |
-+ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX1_DONE_TRIG,
-+ dev->wlan.rro_rx_tbit[1]));
-+
-+ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_MSDU_PG,
-+ MTK_WED_WPDMA_INT_CTRL_RRO_PG0_EN |
-+ MTK_WED_WPDMA_INT_CTRL_RRO_PG0_CLR |
-+ MTK_WED_WPDMA_INT_CTRL_RRO_PG1_EN |
-+ MTK_WED_WPDMA_INT_CTRL_RRO_PG1_CLR |
-+ MTK_WED_WPDMA_INT_CTRL_RRO_PG2_EN |
-+ MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR |
-+ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG0_DONE_TRIG,
-+ dev->wlan.rx_pg_tbit[0]) |
-+ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG1_DONE_TRIG,
-+ dev->wlan.rx_pg_tbit[1]) |
-+ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG,
-+ dev->wlan.rx_pg_tbit[2]));
-+
-+ /* RRO_MSDU_PG_RING2_CFG1_FLD_DRV_EN should be enabled after
-+ * WM FWDL completed, otherwise RRO_MSDU_PG ring may broken
-+ */
-+ wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
-+ MTK_WED_RRO_MSDU_PG_DRV_EN);
-+
-+ for (i = 0; i < MTK_WED_RX_QUEUES; i++) {
-+ struct mtk_wed_ring *ring = &dev->rx_rro_ring[i];
-+
-+ if (!(ring->flags & MTK_WED_RING_CONFIGURED))
-+ continue;
-+
-+ if (mtk_wed_check_wfdma_rx_fill(dev, ring))
-+ dev_err(dev->hw->dev,
-+ "rx_rro_ring(%d) initialization failed\n", i);
-+ }
-+
-+ for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++) {
-+ struct mtk_wed_ring *ring = &dev->rx_page_ring[i];
-+
-+ if (!(ring->flags & MTK_WED_RING_CONFIGURED))
-+ continue;
-+
-+ if (mtk_wed_check_wfdma_rx_fill(dev, ring))
-+ dev_err(dev->hw->dev,
-+ "rx_page_ring(%d) initialization failed\n", i);
-+ }
-+}
-+
-+static void
-+mtk_wed_rro_rx_ring_setup(struct mtk_wed_device *dev, int idx,
-+ void __iomem *regs)
-+{
-+ struct mtk_wed_ring *ring = &dev->rx_rro_ring[idx];
-+
-+ ring->wpdma = regs;
-+ wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_BASE,
-+ readl(regs));
-+ wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_COUNT,
-+ readl(regs + MTK_WED_RING_OFS_COUNT));
-+ ring->flags |= MTK_WED_RING_CONFIGURED;
-+}
-+
-+static void
-+mtk_wed_msdu_pg_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
-+{
-+ struct mtk_wed_ring *ring = &dev->rx_page_ring[idx];
-+
-+ ring->wpdma = regs;
-+ wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_BASE,
-+ readl(regs));
-+ wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_COUNT,
-+ readl(regs + MTK_WED_RING_OFS_COUNT));
-+ ring->flags |= MTK_WED_RING_CONFIGURED;
-+}
-+
-+static int
-+mtk_wed_ind_rx_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
-+{
-+ struct mtk_wed_ring *ring = &dev->ind_cmd_ring;
-+ u32 val = readl(regs + MTK_WED_RING_OFS_COUNT);
-+ int i, count = 0;
-+
-+ ring->wpdma = regs;
-+ wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_BASE,
-+ readl(regs) & 0xfffffff0);
-+
-+ wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_COUNT,
-+ readl(regs + MTK_WED_RING_OFS_COUNT));
-+
-+ /* ack sn cr */
-+ wed_w32(dev, MTK_WED_RRO_CFG0, dev->wlan.phy_base +
-+ dev->wlan.ind_cmd.ack_sn_addr);
-+ wed_w32(dev, MTK_WED_RRO_CFG1,
-+ FIELD_PREP(MTK_WED_RRO_CFG1_MAX_WIN_SZ,
-+ dev->wlan.ind_cmd.win_size) |
-+ FIELD_PREP(MTK_WED_RRO_CFG1_PARTICL_SE_ID,
-+ dev->wlan.ind_cmd.particular_sid));
-+
-+ /* particular session addr element */
-+ wed_w32(dev, MTK_WED_ADDR_ELEM_CFG0,
-+ dev->wlan.ind_cmd.particular_se_phys);
-+
-+ for (i = 0; i < dev->wlan.ind_cmd.se_group_nums; i++) {
-+ wed_w32(dev, MTK_WED_RADDR_ELEM_TBL_WDATA,
-+ dev->wlan.ind_cmd.addr_elem_phys[i] >> 4);
-+ wed_w32(dev, MTK_WED_ADDR_ELEM_TBL_CFG,
-+ MTK_WED_ADDR_ELEM_TBL_WR | (i & 0x7f));
-+
-+ val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG);
-+ while (!(val & MTK_WED_ADDR_ELEM_TBL_WR_RDY) && count++ < 100)
-+ val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG);
-+ if (count >= 100)
-+ dev_err(dev->hw->dev,
-+ "write ba session base failed\n");
-+ }
-+
-+ /* pn check init */
-+ for (i = 0; i < dev->wlan.ind_cmd.particular_sid; i++) {
-+ wed_w32(dev, MTK_WED_PN_CHECK_WDATA_M,
-+ MTK_WED_PN_CHECK_IS_FIRST);
-+
-+ wed_w32(dev, MTK_WED_PN_CHECK_CFG, MTK_WED_PN_CHECK_WR |
-+ FIELD_PREP(MTK_WED_PN_CHECK_SE_ID, i));
-+
-+ count = 0;
-+ val = wed_r32(dev, MTK_WED_PN_CHECK_CFG);
-+ while (!(val & MTK_WED_PN_CHECK_WR_RDY) && count++ < 100)
-+ val = wed_r32(dev, MTK_WED_PN_CHECK_CFG);
-+ if (count >= 100)
-+ dev_err(dev->hw->dev,
-+ "session(%d) initialization failed\n", i);
-+ }
-+
-+ wed_w32(dev, MTK_WED_RX_IND_CMD_CNT0, MTK_WED_RX_IND_CMD_DBG_CNT_EN);
-+ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN);
-+
-+ return 0;
-+}
-+
-+static void
- mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
- {
- int i;
-@@ -2214,6 +2505,10 @@ void mtk_wed_add_hw(struct device_node *
- .detach = mtk_wed_detach,
- .ppe_check = mtk_wed_ppe_check,
- .setup_tc = mtk_wed_setup_tc,
-+ .start_hw_rro = mtk_wed_start_hw_rro,
-+ .rro_rx_ring_setup = mtk_wed_rro_rx_ring_setup,
-+ .msdu_pg_rx_ring_setup = mtk_wed_msdu_pg_rx_ring_setup,
-+ .ind_rx_ring_setup = mtk_wed_ind_rx_ring_setup,
- };
- struct device_node *eth_np = eth->dev->of_node;
- struct platform_device *pdev;
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -10,6 +10,7 @@
-
- #define MTK_WED_TX_QUEUES 2
- #define MTK_WED_RX_QUEUES 2
-+#define MTK_WED_RX_PAGE_QUEUES 3
-
- #define WED_WO_STA_REC 0x6
-
-@@ -99,6 +100,9 @@ struct mtk_wed_device {
- struct mtk_wed_ring txfree_ring;
- struct mtk_wed_ring tx_wdma[MTK_WED_TX_QUEUES];
- struct mtk_wed_ring rx_wdma[MTK_WED_RX_QUEUES];
-+ struct mtk_wed_ring rx_rro_ring[MTK_WED_RX_QUEUES];
-+ struct mtk_wed_ring rx_page_ring[MTK_WED_RX_PAGE_QUEUES];
-+ struct mtk_wed_ring ind_cmd_ring;
-
- struct {
- int size;
-@@ -119,6 +123,13 @@ struct mtk_wed_device {
- dma_addr_t fdbk_phys;
- } rro;
-
-+ struct {
-+ int size;
-+ struct mtk_wed_buf *pages;
-+ struct mtk_wed_bm_desc *desc;
-+ dma_addr_t desc_phys;
-+ } hw_rro;
-+
- /* filled by driver: */
- struct {
- union {
-@@ -137,6 +148,8 @@ struct mtk_wed_device {
- u32 wpdma_txfree;
- u32 wpdma_rx_glo;
- u32 wpdma_rx;
-+ u32 wpdma_rx_rro[MTK_WED_RX_QUEUES];
-+ u32 wpdma_rx_pg;
-
- bool wcid_512;
- bool hw_rro;
-@@ -151,9 +164,20 @@ struct mtk_wed_device {
-
- u8 tx_tbit[MTK_WED_TX_QUEUES];
- u8 rx_tbit[MTK_WED_RX_QUEUES];
-+ u8 rro_rx_tbit[MTK_WED_RX_QUEUES];
-+ u8 rx_pg_tbit[MTK_WED_RX_PAGE_QUEUES];
- u8 txfree_tbit;
- u8 amsdu_max_subframes;
-
-+ struct {
-+ u8 se_group_nums;
-+ u16 win_size;
-+ u16 particular_sid;
-+ u32 ack_sn_addr;
-+ dma_addr_t particular_se_phys;
-+ dma_addr_t addr_elem_phys[1024];
-+ } ind_cmd;
-+
- u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id);
- int (*offload_enable)(struct mtk_wed_device *wed);
- void (*offload_disable)(struct mtk_wed_device *wed);
-@@ -192,6 +216,14 @@ struct mtk_wed_ops {
- void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask);
- int (*setup_tc)(struct mtk_wed_device *wed, struct net_device *dev,
- enum tc_setup_type type, void *type_data);
-+ void (*start_hw_rro)(struct mtk_wed_device *dev, u32 irq_mask,
-+ bool reset);
-+ void (*rro_rx_ring_setup)(struct mtk_wed_device *dev, int ring,
-+ void __iomem *regs);
-+ void (*msdu_pg_rx_ring_setup)(struct mtk_wed_device *dev, int ring,
-+ void __iomem *regs);
-+ int (*ind_rx_ring_setup)(struct mtk_wed_device *dev,
-+ void __iomem *regs);
- };
-
- extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops;
-@@ -263,6 +295,15 @@ static inline bool mtk_wed_is_amsdu_supp
- #define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev)
- #define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) \
- (_dev)->ops->setup_tc(_dev, _netdev, _type, _type_data)
-+#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) \
-+ (_dev)->ops->start_hw_rro(_dev, _mask, _reset)
-+#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) \
-+ (_dev)->ops->rro_rx_ring_setup(_dev, _ring, _regs)
-+#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) \
-+ (_dev)->ops->msdu_pg_rx_ring_setup(_dev, _ring, _regs)
-+#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs) \
-+ (_dev)->ops->ind_rx_ring_setup(_dev, _regs)
-+
- #else
- static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
- {
-@@ -282,6 +323,10 @@ static inline bool mtk_wed_device_active
- #define mtk_wed_device_stop(_dev) do {} while (0)
- #define mtk_wed_device_dma_reset(_dev) do {} while (0)
- #define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) -EOPNOTSUPP
-+#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) do {} while (0)
-+#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) -ENODEV
-+#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) -ENODEV
-+#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs) -ENODEV
- #endif
-
- #endif
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Mon, 18 Sep 2023 12:29:17 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: debugfs: move wed_v2 specific regs
- out of regs array
-
-Move specific WED2.0 debugfs entries out of regs array. This is a
-preliminary patch to introduce WED 3.0 debugfs info.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-@@ -151,7 +151,7 @@ DEFINE_SHOW_ATTRIBUTE(wed_txinfo);
- static int
- wed_rxinfo_show(struct seq_file *s, void *data)
- {
-- static const struct reg_dump regs[] = {
-+ static const struct reg_dump regs_common[] = {
- DUMP_STR("WPDMA RX"),
- DUMP_WPDMA_RX_RING(0),
- DUMP_WPDMA_RX_RING(1),
-@@ -169,7 +169,7 @@ wed_rxinfo_show(struct seq_file *s, void
- DUMP_WED_RING(WED_RING_RX_DATA(0)),
- DUMP_WED_RING(WED_RING_RX_DATA(1)),
-
-- DUMP_STR("WED RRO"),
-+ DUMP_STR("WED WO RRO"),
- DUMP_WED_RRO_RING(WED_RROQM_MIOD_CTRL0),
- DUMP_WED(WED_RROQM_MID_MIB),
- DUMP_WED(WED_RROQM_MOD_MIB),
-@@ -180,17 +180,6 @@ wed_rxinfo_show(struct seq_file *s, void
- DUMP_WED(WED_RROQM_FDBK_ANC_MIB),
- DUMP_WED(WED_RROQM_FDBK_ANC2H_MIB),
-
-- DUMP_STR("WED Route QM"),
-- DUMP_WED(WED_RTQM_R2H_MIB(0)),
-- DUMP_WED(WED_RTQM_R2Q_MIB(0)),
-- DUMP_WED(WED_RTQM_Q2H_MIB(0)),
-- DUMP_WED(WED_RTQM_R2H_MIB(1)),
-- DUMP_WED(WED_RTQM_R2Q_MIB(1)),
-- DUMP_WED(WED_RTQM_Q2H_MIB(1)),
-- DUMP_WED(WED_RTQM_Q2N_MIB),
-- DUMP_WED(WED_RTQM_Q2B_MIB),
-- DUMP_WED(WED_RTQM_PFDBK_MIB),
--
- DUMP_STR("WED WDMA TX"),
- DUMP_WED(WED_WDMA_TX_MIB),
- DUMP_WED_RING(WED_WDMA_RING_TX),
-@@ -211,11 +200,25 @@ wed_rxinfo_show(struct seq_file *s, void
- DUMP_WED(WED_RX_BM_INTF),
- DUMP_WED(WED_RX_BM_ERR_STS),
- };
-+ static const struct reg_dump regs_wed_v2[] = {
-+ DUMP_STR("WED Route QM"),
-+ DUMP_WED(WED_RTQM_R2H_MIB(0)),
-+ DUMP_WED(WED_RTQM_R2Q_MIB(0)),
-+ DUMP_WED(WED_RTQM_Q2H_MIB(0)),
-+ DUMP_WED(WED_RTQM_R2H_MIB(1)),
-+ DUMP_WED(WED_RTQM_R2Q_MIB(1)),
-+ DUMP_WED(WED_RTQM_Q2H_MIB(1)),
-+ DUMP_WED(WED_RTQM_Q2N_MIB),
-+ DUMP_WED(WED_RTQM_Q2B_MIB),
-+ DUMP_WED(WED_RTQM_PFDBK_MIB),
-+ };
- struct mtk_wed_hw *hw = s->private;
- struct mtk_wed_device *dev = hw->wed_dev;
-
-- if (dev)
-- dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
-+ if (dev) {
-+ dump_wed_regs(s, dev, regs_common, ARRAY_SIZE(regs_common));
-+ dump_wed_regs(s, dev, regs_wed_v2, ARRAY_SIZE(regs_wed_v2));
-+ }
-
- return 0;
- }
+++ /dev/null
-From: Sujuan Chen <sujuan.chen@mediatek.com>
-Date: Mon, 18 Sep 2023 12:29:18 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: debugfs: add WED 3.0 debugfs entries
-
-Introduce WED3.0 debugfs entries useful for debugging.
-
-Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-@@ -11,6 +11,7 @@ struct reg_dump {
- u16 offset;
- u8 type;
- u8 base;
-+ u32 mask;
- };
-
- enum {
-@@ -25,6 +26,8 @@ enum {
-
- #define DUMP_STR(_str) { _str, 0, DUMP_TYPE_STRING }
- #define DUMP_REG(_reg, ...) { #_reg, MTK_##_reg, __VA_ARGS__ }
-+#define DUMP_REG_MASK(_reg, _mask) \
-+ { #_mask, MTK_##_reg, DUMP_TYPE_WED, 0, MTK_##_mask }
- #define DUMP_RING(_prefix, _base, ...) \
- { _prefix " BASE", _base, __VA_ARGS__ }, \
- { _prefix " CNT", _base + 0x4, __VA_ARGS__ }, \
-@@ -32,6 +35,7 @@ enum {
- { _prefix " DIDX", _base + 0xc, __VA_ARGS__ }
-
- #define DUMP_WED(_reg) DUMP_REG(_reg, DUMP_TYPE_WED)
-+#define DUMP_WED_MASK(_reg, _mask) DUMP_REG_MASK(_reg, _mask)
- #define DUMP_WED_RING(_base) DUMP_RING(#_base, MTK_##_base, DUMP_TYPE_WED)
-
- #define DUMP_WDMA(_reg) DUMP_REG(_reg, DUMP_TYPE_WDMA)
-@@ -212,12 +216,58 @@ wed_rxinfo_show(struct seq_file *s, void
- DUMP_WED(WED_RTQM_Q2B_MIB),
- DUMP_WED(WED_RTQM_PFDBK_MIB),
- };
-+ static const struct reg_dump regs_wed_v3[] = {
-+ DUMP_STR("WED RX RRO DATA"),
-+ DUMP_WED_RING(WED_RRO_RX_D_RX(0)),
-+ DUMP_WED_RING(WED_RRO_RX_D_RX(1)),
-+
-+ DUMP_STR("WED RX MSDU PAGE"),
-+ DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(0)),
-+ DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(1)),
-+ DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(2)),
-+
-+ DUMP_STR("WED RX IND CMD"),
-+ DUMP_WED(WED_IND_CMD_RX_CTRL1),
-+ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL2, WED_IND_CMD_MAX_CNT),
-+ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_PROC_IDX),
-+ DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_DMA_IDX),
-+ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_MAGIC_CNT),
-+ DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_MAGIC_CNT),
-+ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0,
-+ WED_IND_CMD_PREFETCH_FREE_CNT),
-+ DUMP_WED_MASK(WED_RRO_CFG1, WED_RRO_CFG1_PARTICL_SE_ID),
-+
-+ DUMP_STR("WED ADDR ELEM"),
-+ DUMP_WED(WED_ADDR_ELEM_CFG0),
-+ DUMP_WED_MASK(WED_ADDR_ELEM_CFG1,
-+ WED_ADDR_ELEM_PREFETCH_FREE_CNT),
-+
-+ DUMP_STR("WED Route QM"),
-+ DUMP_WED(WED_RTQM_ENQ_I2Q_DMAD_CNT),
-+ DUMP_WED(WED_RTQM_ENQ_I2N_DMAD_CNT),
-+ DUMP_WED(WED_RTQM_ENQ_I2Q_PKT_CNT),
-+ DUMP_WED(WED_RTQM_ENQ_I2N_PKT_CNT),
-+ DUMP_WED(WED_RTQM_ENQ_USED_ENTRY_CNT),
-+ DUMP_WED(WED_RTQM_ENQ_ERR_CNT),
-+
-+ DUMP_WED(WED_RTQM_DEQ_DMAD_CNT),
-+ DUMP_WED(WED_RTQM_DEQ_Q2I_DMAD_CNT),
-+ DUMP_WED(WED_RTQM_DEQ_PKT_CNT),
-+ DUMP_WED(WED_RTQM_DEQ_Q2I_PKT_CNT),
-+ DUMP_WED(WED_RTQM_DEQ_USED_PFDBK_CNT),
-+ DUMP_WED(WED_RTQM_DEQ_ERR_CNT),
-+ };
- struct mtk_wed_hw *hw = s->private;
- struct mtk_wed_device *dev = hw->wed_dev;
-
- if (dev) {
- dump_wed_regs(s, dev, regs_common, ARRAY_SIZE(regs_common));
-- dump_wed_regs(s, dev, regs_wed_v2, ARRAY_SIZE(regs_wed_v2));
-+ if (mtk_wed_is_v2(hw))
-+ dump_wed_regs(s, dev,
-+ regs_wed_v2, ARRAY_SIZE(regs_wed_v2));
-+ else
-+ dump_wed_regs(s, dev,
-+ regs_wed_v3, ARRAY_SIZE(regs_wed_v3));
- }
-
- return 0;
-@@ -225,6 +275,314 @@ wed_rxinfo_show(struct seq_file *s, void
- DEFINE_SHOW_ATTRIBUTE(wed_rxinfo);
-
- static int
-+wed_amsdu_show(struct seq_file *s, void *data)
-+{
-+ static const struct reg_dump regs[] = {
-+ DUMP_STR("WED AMDSU INFO"),
-+ DUMP_WED(WED_MON_AMSDU_FIFO_DMAD),
-+
-+ DUMP_STR("WED AMDSU ENG0 INFO"),
-+ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(0)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(0)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENI(0)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENO(0)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_MERG(0)),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(0),
-+ WED_AMSDU_ENG_MAX_PL_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(0),
-+ WED_AMSDU_ENG_MAX_QGPP_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0),
-+ WED_AMSDU_ENG_CUR_ENTRY),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0),
-+ WED_AMSDU_ENG_MAX_BUF_MERGED),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0),
-+ WED_AMSDU_ENG_MAX_MSDU_MERGED),
-+
-+ DUMP_STR("WED AMDSU ENG1 INFO"),
-+ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(1)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(1)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENI(1)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENO(1)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_MERG(1)),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(1),
-+ WED_AMSDU_ENG_MAX_PL_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(1),
-+ WED_AMSDU_ENG_MAX_QGPP_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1),
-+ WED_AMSDU_ENG_CUR_ENTRY),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
-+ WED_AMSDU_ENG_MAX_BUF_MERGED),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
-+ WED_AMSDU_ENG_MAX_MSDU_MERGED),
-+
-+ DUMP_STR("WED AMDSU ENG2 INFO"),
-+ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(2)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(2)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENI(2)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENO(2)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_MERG(2)),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(2),
-+ WED_AMSDU_ENG_MAX_PL_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(2),
-+ WED_AMSDU_ENG_MAX_QGPP_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
-+ WED_AMSDU_ENG_CUR_ENTRY),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
-+ WED_AMSDU_ENG_MAX_BUF_MERGED),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
-+ WED_AMSDU_ENG_MAX_MSDU_MERGED),
-+
-+ DUMP_STR("WED AMDSU ENG3 INFO"),
-+ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(3)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(3)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENI(3)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENO(3)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_MERG(3)),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(3),
-+ WED_AMSDU_ENG_MAX_PL_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(3),
-+ WED_AMSDU_ENG_MAX_QGPP_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3),
-+ WED_AMSDU_ENG_CUR_ENTRY),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3),
-+ WED_AMSDU_ENG_MAX_BUF_MERGED),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3),
-+ WED_AMSDU_ENG_MAX_MSDU_MERGED),
-+
-+ DUMP_STR("WED AMDSU ENG4 INFO"),
-+ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(4)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(4)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENI(4)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENO(4)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_MERG(4)),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(4),
-+ WED_AMSDU_ENG_MAX_PL_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(4),
-+ WED_AMSDU_ENG_MAX_QGPP_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
-+ WED_AMSDU_ENG_CUR_ENTRY),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
-+ WED_AMSDU_ENG_MAX_BUF_MERGED),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
-+ WED_AMSDU_ENG_MAX_MSDU_MERGED),
-+
-+ DUMP_STR("WED AMDSU ENG5 INFO"),
-+ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(5)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(5)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENI(5)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENO(5)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_MERG(5)),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(5),
-+ WED_AMSDU_ENG_MAX_PL_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(5),
-+ WED_AMSDU_ENG_MAX_QGPP_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5),
-+ WED_AMSDU_ENG_CUR_ENTRY),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5),
-+ WED_AMSDU_ENG_MAX_BUF_MERGED),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5),
-+ WED_AMSDU_ENG_MAX_MSDU_MERGED),
-+
-+ DUMP_STR("WED AMDSU ENG6 INFO"),
-+ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(6)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(6)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENI(6)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENO(6)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_MERG(6)),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(6),
-+ WED_AMSDU_ENG_MAX_PL_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(6),
-+ WED_AMSDU_ENG_MAX_QGPP_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6),
-+ WED_AMSDU_ENG_CUR_ENTRY),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6),
-+ WED_AMSDU_ENG_MAX_BUF_MERGED),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6),
-+ WED_AMSDU_ENG_MAX_MSDU_MERGED),
-+
-+ DUMP_STR("WED AMDSU ENG7 INFO"),
-+ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(7)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(7)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENI(7)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENO(7)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_MERG(7)),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(7),
-+ WED_AMSDU_ENG_MAX_PL_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(7),
-+ WED_AMSDU_ENG_MAX_QGPP_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7),
-+ WED_AMSDU_ENG_CUR_ENTRY),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7),
-+ WED_AMSDU_ENG_MAX_BUF_MERGED),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
-+ WED_AMSDU_ENG_MAX_MSDU_MERGED),
-+
-+ DUMP_STR("WED AMDSU ENG8 INFO"),
-+ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(8)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(8)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENI(8)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_QENO(8)),
-+ DUMP_WED(WED_MON_AMSDU_ENG_MERG(8)),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(8),
-+ WED_AMSDU_ENG_MAX_PL_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(8),
-+ WED_AMSDU_ENG_MAX_QGPP_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8),
-+ WED_AMSDU_ENG_CUR_ENTRY),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8),
-+ WED_AMSDU_ENG_MAX_BUF_MERGED),
-+ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8),
-+ WED_AMSDU_ENG_MAX_MSDU_MERGED),
-+
-+ DUMP_STR("WED QMEM INFO"),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(0), WED_AMSDU_QMEM_FQ_CNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(0), WED_AMSDU_QMEM_SP_QCNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(1), WED_AMSDU_QMEM_TID0_QCNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(1), WED_AMSDU_QMEM_TID1_QCNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(2), WED_AMSDU_QMEM_TID2_QCNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(2), WED_AMSDU_QMEM_TID3_QCNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(3), WED_AMSDU_QMEM_TID4_QCNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(3), WED_AMSDU_QMEM_TID5_QCNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(4), WED_AMSDU_QMEM_TID6_QCNT),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(4), WED_AMSDU_QMEM_TID7_QCNT),
-+
-+ DUMP_STR("WED QMEM HEAD INFO"),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(0), WED_AMSDU_QMEM_FQ_HEAD),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(0), WED_AMSDU_QMEM_SP_QHEAD),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(1), WED_AMSDU_QMEM_TID0_QHEAD),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(1), WED_AMSDU_QMEM_TID1_QHEAD),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(2), WED_AMSDU_QMEM_TID2_QHEAD),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(2), WED_AMSDU_QMEM_TID3_QHEAD),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(3), WED_AMSDU_QMEM_TID4_QHEAD),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(3), WED_AMSDU_QMEM_TID5_QHEAD),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(4), WED_AMSDU_QMEM_TID6_QHEAD),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(4), WED_AMSDU_QMEM_TID7_QHEAD),
-+
-+ DUMP_STR("WED QMEM TAIL INFO"),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(5), WED_AMSDU_QMEM_FQ_TAIL),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(5), WED_AMSDU_QMEM_SP_QTAIL),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(6), WED_AMSDU_QMEM_TID0_QTAIL),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(6), WED_AMSDU_QMEM_TID1_QTAIL),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(7), WED_AMSDU_QMEM_TID2_QTAIL),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(7), WED_AMSDU_QMEM_TID3_QTAIL),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(8), WED_AMSDU_QMEM_TID4_QTAIL),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(8), WED_AMSDU_QMEM_TID5_QTAIL),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(9), WED_AMSDU_QMEM_TID6_QTAIL),
-+ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(9), WED_AMSDU_QMEM_TID7_QTAIL),
-+
-+ DUMP_STR("WED HIFTXD MSDU INFO"),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(1)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(2)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(3)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(4)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(5)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(6)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(7)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(8)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(9)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(10)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(11)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(12)),
-+ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(13)),
-+ };
-+ struct mtk_wed_hw *hw = s->private;
-+ struct mtk_wed_device *dev = hw->wed_dev;
-+
-+ if (dev)
-+ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
-+
-+ return 0;
-+}
-+DEFINE_SHOW_ATTRIBUTE(wed_amsdu);
-+
-+static int
-+wed_rtqm_show(struct seq_file *s, void *data)
-+{
-+ static const struct reg_dump regs[] = {
-+ DUMP_STR("WED Route QM IGRS0(N2H + Recycle)"),
-+ DUMP_WED(WED_RTQM_IGRS0_I2HW_DMAD_CNT),
-+ DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(0)),
-+ DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(1)),
-+ DUMP_WED(WED_RTQM_IGRS0_I2HW_PKT_CNT),
-+ DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)),
-+ DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)),
-+ DUMP_WED(WED_RTQM_IGRS0_FDROP_CNT),
-+
-+ DUMP_STR("WED Route QM IGRS1(Legacy)"),
-+ DUMP_WED(WED_RTQM_IGRS1_I2HW_DMAD_CNT),
-+ DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(0)),
-+ DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(1)),
-+ DUMP_WED(WED_RTQM_IGRS1_I2HW_PKT_CNT),
-+ DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(0)),
-+ DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(1)),
-+ DUMP_WED(WED_RTQM_IGRS1_FDROP_CNT),
-+
-+ DUMP_STR("WED Route QM IGRS2(RRO3.0)"),
-+ DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT),
-+ DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(0)),
-+ DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(1)),
-+ DUMP_WED(WED_RTQM_IGRS2_I2HW_PKT_CNT),
-+ DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(0)),
-+ DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(1)),
-+ DUMP_WED(WED_RTQM_IGRS2_FDROP_CNT),
-+
-+ DUMP_STR("WED Route QM IGRS3(DEBUG)"),
-+ DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT),
-+ DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(0)),
-+ DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(1)),
-+ DUMP_WED(WED_RTQM_IGRS3_I2HW_PKT_CNT),
-+ DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(0)),
-+ DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(1)),
-+ DUMP_WED(WED_RTQM_IGRS3_FDROP_CNT),
-+ };
-+ struct mtk_wed_hw *hw = s->private;
-+ struct mtk_wed_device *dev = hw->wed_dev;
-+
-+ if (dev)
-+ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
-+
-+ return 0;
-+}
-+DEFINE_SHOW_ATTRIBUTE(wed_rtqm);
-+
-+static int
-+wed_rro_show(struct seq_file *s, void *data)
-+{
-+ static const struct reg_dump regs[] = {
-+ DUMP_STR("RRO/IND CMD CNT"),
-+ DUMP_WED(WED_RX_IND_CMD_CNT(1)),
-+ DUMP_WED(WED_RX_IND_CMD_CNT(2)),
-+ DUMP_WED(WED_RX_IND_CMD_CNT(3)),
-+ DUMP_WED(WED_RX_IND_CMD_CNT(4)),
-+ DUMP_WED(WED_RX_IND_CMD_CNT(5)),
-+ DUMP_WED(WED_RX_IND_CMD_CNT(6)),
-+ DUMP_WED(WED_RX_IND_CMD_CNT(7)),
-+ DUMP_WED(WED_RX_IND_CMD_CNT(8)),
-+ DUMP_WED_MASK(WED_RX_IND_CMD_CNT(9),
-+ WED_IND_CMD_MAGIC_CNT_FAIL_CNT),
-+
-+ DUMP_WED(WED_RX_ADDR_ELEM_CNT(0)),
-+ DUMP_WED_MASK(WED_RX_ADDR_ELEM_CNT(1),
-+ WED_ADDR_ELEM_SIG_FAIL_CNT),
-+ DUMP_WED(WED_RX_MSDU_PG_CNT(1)),
-+ DUMP_WED(WED_RX_MSDU_PG_CNT(2)),
-+ DUMP_WED(WED_RX_MSDU_PG_CNT(3)),
-+ DUMP_WED(WED_RX_MSDU_PG_CNT(4)),
-+ DUMP_WED(WED_RX_MSDU_PG_CNT(5)),
-+ DUMP_WED_MASK(WED_RX_PN_CHK_CNT,
-+ WED_PN_CHK_FAIL_CNT),
-+ };
-+ struct mtk_wed_hw *hw = s->private;
-+ struct mtk_wed_device *dev = hw->wed_dev;
-+
-+ if (dev)
-+ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
-+
-+ return 0;
-+}
-+DEFINE_SHOW_ATTRIBUTE(wed_rro);
-+
-+static int
- mtk_wed_reg_set(void *data, u64 val)
- {
- struct mtk_wed_hw *hw = data;
-@@ -264,7 +622,16 @@ void mtk_wed_hw_add_debugfs(struct mtk_w
- debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg);
- debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval);
- debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops);
-- if (!mtk_wed_is_v1(hw))
-+ if (!mtk_wed_is_v1(hw)) {
- debugfs_create_file_unsafe("rxinfo", 0400, dir, hw,
- &wed_rxinfo_fops);
-+ if (mtk_wed_is_v3_or_greater(hw)) {
-+ debugfs_create_file_unsafe("amsdu", 0400, dir, hw,
-+ &wed_amsdu_fops);
-+ debugfs_create_file_unsafe("rtqm", 0400, dir, hw,
-+ &wed_rtqm_fops);
-+ debugfs_create_file_unsafe("rro", 0400, dir, hw,
-+ &wed_rro_fops);
-+ }
-+ }
- }
+++ /dev/null
-From: Sujuan Chen <sujuan.chen@mediatek.com>
-Date: Mon, 18 Sep 2023 12:29:19 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: add wed 3.0 reset support
-
-Introduce support for resetting Wireless Ethernet Dispatcher 3.0
-available on MT988 SoC.
-
-Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -149,6 +149,90 @@ mtk_wdma_read_reset(struct mtk_wed_devic
- return wdma_r32(dev, MTK_WDMA_GLO_CFG);
- }
-
-+static void
-+mtk_wdma_v3_rx_reset(struct mtk_wed_device *dev)
-+{
-+ u32 status;
-+
-+ if (!mtk_wed_is_v3_or_greater(dev->hw))
-+ return;
-+
-+ wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN);
-+ wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
-+
-+ if (read_poll_timeout(wdma_r32, status,
-+ !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY),
-+ 0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG))
-+ dev_err(dev->hw->dev, "rx reset failed\n");
-+
-+ if (read_poll_timeout(wdma_r32, status,
-+ !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY),
-+ 0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG))
-+ dev_err(dev->hw->dev, "rx reset failed\n");
-+
-+ wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
-+ wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
-+
-+ if (read_poll_timeout(wdma_r32, status,
-+ !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY),
-+ 0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG))
-+ dev_err(dev->hw->dev, "rx reset failed\n");
-+
-+ if (read_poll_timeout(wdma_r32, status,
-+ !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY),
-+ 0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG))
-+ dev_err(dev->hw->dev, "rx reset failed\n");
-+
-+ /* prefetch FIFO */
-+ wdma_w32(dev, MTK_WDMA_PREF_RX_FIFO_CFG,
-+ MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR |
-+ MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR);
-+ wdma_clr(dev, MTK_WDMA_PREF_RX_FIFO_CFG,
-+ MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR |
-+ MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR);
-+
-+ /* core FIFO */
-+ wdma_w32(dev, MTK_WDMA_XDMA_RX_FIFO_CFG,
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR);
-+ wdma_clr(dev, MTK_WDMA_XDMA_RX_FIFO_CFG,
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR);
-+
-+ /* writeback FIFO */
-+ wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0),
-+ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
-+ wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1),
-+ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
-+
-+ wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0),
-+ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
-+ wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1),
-+ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
-+
-+ /* prefetch ring status */
-+ wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG,
-+ MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR);
-+ wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG,
-+ MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR);
-+
-+ /* writeback ring status */
-+ wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG,
-+ MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR);
-+ wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG,
-+ MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR);
-+}
-+
- static int
- mtk_wdma_rx_reset(struct mtk_wed_device *dev)
- {
-@@ -161,6 +245,7 @@ mtk_wdma_rx_reset(struct mtk_wed_device
- if (ret)
- dev_err(dev->hw->dev, "rx reset failed\n");
-
-+ mtk_wdma_v3_rx_reset(dev);
- wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
- wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
-
-@@ -193,6 +278,84 @@ mtk_wed_poll_busy(struct mtk_wed_device
- }
-
- static void
-+mtk_wdma_v3_tx_reset(struct mtk_wed_device *dev)
-+{
-+ u32 status;
-+
-+ if (!mtk_wed_is_v3_or_greater(dev->hw))
-+ return;
-+
-+ wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN);
-+ wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
-+
-+ if (read_poll_timeout(wdma_r32, status,
-+ !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY),
-+ 0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG))
-+ dev_err(dev->hw->dev, "tx reset failed\n");
-+
-+ if (read_poll_timeout(wdma_r32, status,
-+ !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY),
-+ 0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG))
-+ dev_err(dev->hw->dev, "tx reset failed\n");
-+
-+ wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
-+ wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
-+
-+ if (read_poll_timeout(wdma_r32, status,
-+ !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY),
-+ 0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG))
-+ dev_err(dev->hw->dev, "tx reset failed\n");
-+
-+ if (read_poll_timeout(wdma_r32, status,
-+ !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY),
-+ 0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG))
-+ dev_err(dev->hw->dev, "tx reset failed\n");
-+
-+ /* prefetch FIFO */
-+ wdma_w32(dev, MTK_WDMA_PREF_TX_FIFO_CFG,
-+ MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR |
-+ MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR);
-+ wdma_clr(dev, MTK_WDMA_PREF_TX_FIFO_CFG,
-+ MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR |
-+ MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR);
-+
-+ /* core FIFO */
-+ wdma_w32(dev, MTK_WDMA_XDMA_TX_FIFO_CFG,
-+ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR);
-+ wdma_clr(dev, MTK_WDMA_XDMA_TX_FIFO_CFG,
-+ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR |
-+ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR);
-+
-+ /* writeback FIFO */
-+ wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0),
-+ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
-+ wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1),
-+ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
-+
-+ wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0),
-+ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
-+ wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1),
-+ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
-+
-+ /* prefetch ring status */
-+ wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG,
-+ MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR);
-+ wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG,
-+ MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR);
-+
-+ /* writeback ring status */
-+ wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG,
-+ MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR);
-+ wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG,
-+ MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR);
-+}
-+
-+static void
- mtk_wdma_tx_reset(struct mtk_wed_device *dev)
- {
- u32 status, mask = MTK_WDMA_GLO_CFG_TX_DMA_BUSY;
-@@ -203,6 +366,7 @@ mtk_wdma_tx_reset(struct mtk_wed_device
- !(status & mask), 0, 10000))
- dev_err(dev->hw->dev, "tx reset failed\n");
-
-+ mtk_wdma_v3_tx_reset(dev);
- wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX);
- wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
-
-@@ -1405,13 +1569,33 @@ mtk_wed_rx_reset(struct mtk_wed_device *
- if (ret)
- return ret;
-
-+ if (dev->wlan.hw_rro) {
-+ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN);
-+ mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_HW_STS,
-+ MTK_WED_RX_IND_CMD_BUSY);
-+ mtk_wed_reset(dev, MTK_WED_RESET_RRO_RX_TO_PG);
-+ }
-+
- wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RX_DRV_EN);
- ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
- MTK_WED_WPDMA_RX_D_RX_DRV_BUSY);
-+ if (!ret && mtk_wed_is_v3_or_greater(dev->hw))
-+ ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
-+ MTK_WED_WPDMA_RX_D_PREF_BUSY);
- if (ret) {
- mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
- mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_D_DRV);
- } else {
-+ if (mtk_wed_is_v3_or_greater(dev->hw)) {
-+ /* 1.a. disable prefetch HW */
-+ wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
-+ MTK_WED_WPDMA_RX_D_PREF_EN);
-+ mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
-+ MTK_WED_WPDMA_RX_D_PREF_BUSY);
-+ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
-+ MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL);
-+ }
-+
- wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
- MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
- MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
-@@ -1439,23 +1623,52 @@ mtk_wed_rx_reset(struct mtk_wed_device *
- wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0);
- }
-
-+ if (dev->wlan.hw_rro) {
-+ /* disable rro msdu page drv */
-+ wed_clr(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
-+ MTK_WED_RRO_MSDU_PG_DRV_EN);
-+
-+ /* disable rro data drv */
-+ wed_clr(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN);
-+
-+ /* rro msdu page drv reset */
-+ wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
-+ MTK_WED_RRO_MSDU_PG_DRV_CLR);
-+ mtk_wed_poll_busy(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
-+ MTK_WED_RRO_MSDU_PG_DRV_CLR);
-+
-+ /* rro data drv reset */
-+ wed_w32(dev, MTK_WED_RRO_RX_D_CFG(2),
-+ MTK_WED_RRO_RX_D_DRV_CLR);
-+ mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_D_CFG(2),
-+ MTK_WED_RRO_RX_D_DRV_CLR);
-+ }
-+
- /* reset route qm */
- wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN);
- ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL,
- MTK_WED_CTRL_RX_ROUTE_QM_BUSY);
-- if (ret)
-+ if (ret) {
- mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM);
-- else
-- wed_set(dev, MTK_WED_RTQM_GLO_CFG,
-- MTK_WED_RTQM_Q_RST);
-+ } else if (mtk_wed_is_v3_or_greater(dev->hw)) {
-+ wed_set(dev, MTK_WED_RTQM_RST, BIT(0));
-+ wed_clr(dev, MTK_WED_RTQM_RST, BIT(0));
-+ mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM);
-+ } else {
-+ wed_set(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
-+ }
-
- /* reset tx wdma */
- mtk_wdma_tx_reset(dev);
-
- /* reset tx wdma drv */
- wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_TX_DRV_EN);
-- mtk_wed_poll_busy(dev, MTK_WED_CTRL,
-- MTK_WED_CTRL_WDMA_INT_AGENT_BUSY);
-+ if (mtk_wed_is_v3_or_greater(dev->hw))
-+ mtk_wed_poll_busy(dev, MTK_WED_WPDMA_STATUS,
-+ MTK_WED_WPDMA_STATUS_TX_DRV);
-+ else
-+ mtk_wed_poll_busy(dev, MTK_WED_CTRL,
-+ MTK_WED_CTRL_WDMA_INT_AGENT_BUSY);
- mtk_wed_reset(dev, MTK_WED_RESET_WDMA_TX_DRV);
-
- /* reset wed rx dma */
-@@ -1476,6 +1689,14 @@ mtk_wed_rx_reset(struct mtk_wed_device *
- MTK_WED_CTRL_WED_RX_BM_BUSY);
- mtk_wed_reset(dev, MTK_WED_RESET_RX_BM);
-
-+ if (dev->wlan.hw_rro) {
-+ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN);
-+ mtk_wed_poll_busy(dev, MTK_WED_CTRL,
-+ MTK_WED_CTRL_WED_RX_PG_BM_BUSY);
-+ wed_set(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM);
-+ wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM);
-+ }
-+
- /* wo change to enable state */
- val = MTK_WED_WO_STATE_ENABLE;
- ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
-@@ -1493,6 +1714,7 @@ mtk_wed_rx_reset(struct mtk_wed_device *
- false);
- }
- mtk_wed_free_rx_buffer(dev);
-+ mtk_wed_hwrro_free_buffer(dev);
-
- return 0;
- }
-@@ -1526,15 +1748,41 @@ mtk_wed_reset_dma(struct mtk_wed_device
-
- /* 2. reset WDMA rx DMA */
- busy = !!mtk_wdma_rx_reset(dev);
-- wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
-+ if (mtk_wed_is_v3_or_greater(dev->hw)) {
-+ val = MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE |
-+ wed_r32(dev, MTK_WED_WDMA_GLO_CFG);
-+ val &= ~MTK_WED_WDMA_GLO_CFG_RX_DRV_EN;
-+ wed_w32(dev, MTK_WED_WDMA_GLO_CFG, val);
-+ } else {
-+ wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
-+ MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
-+ }
-+
- if (!busy)
- busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_GLO_CFG,
- MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY);
-+ if (!busy && mtk_wed_is_v3_or_greater(dev->hw))
-+ busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG,
-+ MTK_WED_WDMA_RX_PREF_BUSY);
-
- if (busy) {
- mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT);
- mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV);
- } else {
-+ if (mtk_wed_is_v3_or_greater(dev->hw)) {
-+ /* 1.a. disable prefetch HW */
-+ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
-+ MTK_WED_WDMA_RX_PREF_EN);
-+ mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG,
-+ MTK_WED_WDMA_RX_PREF_BUSY);
-+ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
-+ MTK_WED_WDMA_RX_PREF_DDONE2_EN);
-+
-+ /* 2. Reset dma index */
-+ wed_w32(dev, MTK_WED_WDMA_RESET_IDX,
-+ MTK_WED_WDMA_RESET_IDX_RX_ALL);
-+ }
-+
- wed_w32(dev, MTK_WED_WDMA_RESET_IDX,
- MTK_WED_WDMA_RESET_IDX_RX | MTK_WED_WDMA_RESET_IDX_DRV);
- wed_w32(dev, MTK_WED_WDMA_RESET_IDX, 0);
-@@ -1550,8 +1798,13 @@ mtk_wed_reset_dma(struct mtk_wed_device
- wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
-
- for (i = 0; i < 100; i++) {
-- val = wed_r32(dev, MTK_WED_TX_BM_INTF);
-- if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40)
-+ if (mtk_wed_is_v1(dev->hw))
-+ val = FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP,
-+ wed_r32(dev, MTK_WED_TX_BM_INTF));
-+ else
-+ val = FIELD_GET(MTK_WED_TX_TKID_INTF_TKFIFO_FDEP,
-+ wed_r32(dev, MTK_WED_TX_TKID_INTF));
-+ if (val == 0x40)
- break;
- }
-
-@@ -1573,6 +1826,8 @@ mtk_wed_reset_dma(struct mtk_wed_device
- mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
- mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV);
- mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_DRV);
-+ if (mtk_wed_is_v3_or_greater(dev->hw))
-+ wed_w32(dev, MTK_WED_RX1_CTRL2, 0);
- } else {
- wed_w32(dev, MTK_WED_WPDMA_RESET_IDX,
- MTK_WED_WPDMA_RESET_IDX_TX |
-@@ -1589,7 +1844,14 @@ mtk_wed_reset_dma(struct mtk_wed_device
- wed_w32(dev, MTK_WED_RESET_IDX, 0);
- }
-
-- mtk_wed_rx_reset(dev);
-+ if (mtk_wed_is_v3_or_greater(dev->hw)) {
-+ /* reset amsdu engine */
-+ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN);
-+ mtk_wed_reset(dev, MTK_WED_RESET_TX_AMSDU);
-+ }
-+
-+ if (mtk_wed_get_rx_capa(dev))
-+ mtk_wed_rx_reset(dev);
- }
-
- static int
-@@ -1841,6 +2103,7 @@ mtk_wed_dma_enable(struct mtk_wed_device
- MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4);
-
- wdma_set(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
-+ wdma_set(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
- }
-
- wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
-@@ -1904,6 +2167,12 @@ mtk_wed_start_hw_rro(struct mtk_wed_devi
- if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro)
- return;
-
-+ if (reset) {
-+ wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
-+ MTK_WED_RRO_MSDU_PG_DRV_EN);
-+ return;
-+ }
-+
- wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR);
- wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
- MTK_WED_RRO_MSDU_PG_DRV_CLR);
---- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-@@ -28,6 +28,8 @@ struct mtk_wdma_desc {
- #define MTK_WED_RESET 0x008
- #define MTK_WED_RESET_TX_BM BIT(0)
- #define MTK_WED_RESET_RX_BM BIT(1)
-+#define MTK_WED_RESET_RX_PG_BM BIT(2)
-+#define MTK_WED_RESET_RRO_RX_TO_PG BIT(3)
- #define MTK_WED_RESET_TX_FREE_AGENT BIT(4)
- #define MTK_WED_RESET_WPDMA_TX_DRV BIT(8)
- #define MTK_WED_RESET_WPDMA_RX_DRV BIT(9)
-@@ -106,6 +108,9 @@ struct mtk_wdma_desc {
- #define MTK_WED_STATUS 0x060
- #define MTK_WED_STATUS_TX GENMASK(15, 8)
-
-+#define MTK_WED_WPDMA_STATUS 0x068
-+#define MTK_WED_WPDMA_STATUS_TX_DRV GENMASK(15, 8)
-+
- #define MTK_WED_TX_BM_CTRL 0x080
- #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM GENMASK(6, 0)
- #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM GENMASK(22, 16)
-@@ -140,6 +145,9 @@ struct mtk_wdma_desc {
- #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM GENMASK(22, 16)
- #define MTK_WED_TX_TKID_CTRL_PAUSE BIT(28)
-
-+#define MTK_WED_TX_TKID_INTF 0x0dc
-+#define MTK_WED_TX_TKID_INTF_TKFIFO_FDEP GENMASK(25, 16)
-+
- #define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3 GENMASK(7, 0)
- #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3 GENMASK(23, 16)
-
-@@ -190,6 +198,7 @@ struct mtk_wdma_desc {
- #define MTK_WED_RING_RX_DATA(_n) (0x420 + (_n) * 0x10)
-
- #define MTK_WED_SCR0 0x3c0
-+#define MTK_WED_RX1_CTRL2 0x418
- #define MTK_WED_WPDMA_INT_TRIGGER 0x504
- #define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE BIT(1)
- #define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE GENMASK(5, 4)
-@@ -303,6 +312,7 @@ struct mtk_wdma_desc {
-
- #define MTK_WED_WPDMA_RX_D_RST_IDX 0x760
- #define MTK_WED_WPDMA_RX_D_RST_CRX_IDX GENMASK(17, 16)
-+#define MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL BIT(20)
- #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX GENMASK(25, 24)
-
- #define MTK_WED_WPDMA_RX_GLO_CFG 0x76c
-@@ -313,6 +323,7 @@ struct mtk_wdma_desc {
-
- #define MTK_WED_WPDMA_RX_D_PREF_CFG 0x7b4
- #define MTK_WED_WPDMA_RX_D_PREF_EN BIT(0)
-+#define MTK_WED_WPDMA_RX_D_PREF_BUSY BIT(1)
- #define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE GENMASK(12, 8)
- #define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES GENMASK(21, 16)
-
-@@ -334,11 +345,13 @@ struct mtk_wdma_desc {
-
- #define MTK_WED_WDMA_RX_PREF_CFG 0x950
- #define MTK_WED_WDMA_RX_PREF_EN BIT(0)
-+#define MTK_WED_WDMA_RX_PREF_BUSY BIT(1)
- #define MTK_WED_WDMA_RX_PREF_BURST_SIZE GENMASK(12, 8)
- #define MTK_WED_WDMA_RX_PREF_LOW_THRES GENMASK(21, 16)
- #define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR BIT(24)
- #define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR BIT(25)
- #define MTK_WED_WDMA_RX_PREF_DDONE2_EN BIT(26)
-+#define MTK_WED_WDMA_RX_PREF_DDONE2_BUSY BIT(27)
-
- #define MTK_WED_WDMA_RX_PREF_FIFO_CFG 0x95C
- #define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR BIT(0)
-@@ -367,6 +380,7 @@ struct mtk_wdma_desc {
-
- #define MTK_WED_WDMA_RESET_IDX 0xa08
- #define MTK_WED_WDMA_RESET_IDX_RX GENMASK(17, 16)
-+#define MTK_WED_WDMA_RESET_IDX_RX_ALL BIT(20)
- #define MTK_WED_WDMA_RESET_IDX_DRV GENMASK(25, 24)
-
- #define MTK_WED_WDMA_INT_CLR 0xa24
-@@ -437,21 +451,62 @@ struct mtk_wdma_desc {
- #define MTK_WDMA_INT_MASK_RX_DELAY BIT(30)
- #define MTK_WDMA_INT_MASK_RX_COHERENT BIT(31)
-
-+#define MTK_WDMA_XDMA_TX_FIFO_CFG 0x238
-+#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR BIT(0)
-+#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR BIT(4)
-+#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR BIT(8)
-+#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR BIT(12)
-+
-+#define MTK_WDMA_XDMA_RX_FIFO_CFG 0x23c
-+#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR BIT(0)
-+#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR BIT(4)
-+#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR BIT(8)
-+#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR BIT(12)
-+#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR BIT(15)
-+#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR BIT(18)
-+#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR BIT(21)
-+
- #define MTK_WDMA_INT_GRP1 0x250
- #define MTK_WDMA_INT_GRP2 0x254
-
- #define MTK_WDMA_PREF_TX_CFG 0x2d0
- #define MTK_WDMA_PREF_TX_CFG_PREF_EN BIT(0)
-+#define MTK_WDMA_PREF_TX_CFG_PREF_BUSY BIT(1)
-
- #define MTK_WDMA_PREF_RX_CFG 0x2dc
- #define MTK_WDMA_PREF_RX_CFG_PREF_EN BIT(0)
-+#define MTK_WDMA_PREF_RX_CFG_PREF_BUSY BIT(1)
-+
-+#define MTK_WDMA_PREF_RX_FIFO_CFG 0x2e0
-+#define MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR BIT(0)
-+#define MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR BIT(16)
-+
-+#define MTK_WDMA_PREF_TX_FIFO_CFG 0x2d4
-+#define MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR BIT(0)
-+#define MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR BIT(16)
-+
-+#define MTK_WDMA_PREF_SIDX_CFG 0x2e4
-+#define MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR GENMASK(3, 0)
-+#define MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR GENMASK(5, 4)
-
- #define MTK_WDMA_WRBK_TX_CFG 0x300
-+#define MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY BIT(0)
- #define MTK_WDMA_WRBK_TX_CFG_WRBK_EN BIT(30)
-
-+#define MTK_WDMA_WRBK_TX_FIFO_CFG(_n) (0x304 + (_n) * 0x4)
-+#define MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR BIT(0)
-+
- #define MTK_WDMA_WRBK_RX_CFG 0x344
-+#define MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY BIT(0)
- #define MTK_WDMA_WRBK_RX_CFG_WRBK_EN BIT(30)
-
-+#define MTK_WDMA_WRBK_RX_FIFO_CFG(_n) (0x348 + (_n) * 0x4)
-+#define MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR BIT(0)
-+
-+#define MTK_WDMA_WRBK_SIDX_CFG 0x388
-+#define MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR GENMASK(3, 0)
-+#define MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR GENMASK(5, 4)
-+
- #define MTK_PCIE_MIRROR_MAP(n) ((n) ? 0x4 : 0x0)
- #define MTK_PCIE_MIRROR_MAP_EN BIT(0)
- #define MTK_PCIE_MIRROR_MAP_WED_ID BIT(1)
-@@ -465,6 +520,8 @@ struct mtk_wdma_desc {
- #define MTK_WED_RTQM_Q_DBG_BYPASS BIT(5)
- #define MTK_WED_RTQM_TXDMAD_FPORT GENMASK(23, 20)
-
-+#define MTK_WED_RTQM_RST 0xb04
-+
- #define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT 0xb1c
- #define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n) (0xb20 + (_n) * 0x4)
- #define MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT 0xb28
-@@ -653,6 +710,9 @@ struct mtk_wdma_desc {
- #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR BIT(17)
- #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG GENMASK(22, 18)
-
-+#define MTK_WED_RRO_RX_HW_STS 0xf00
-+#define MTK_WED_RX_IND_CMD_BUSY GENMASK(31, 0)
-+
- #define MTK_WED_RX_IND_CMD_CNT0 0xf20
- #define MTK_WED_RX_IND_CMD_DBG_CNT_EN BIT(31)
-
+++ /dev/null
-From 52ea72ad0daa0f29535b4cef39257616c5a211d3 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 24 Oct 2023 00:00:19 +0200
-Subject: [PATCH 1/5] net: ethernet: mtk_wed: fix firmware loading for MT7986
- SoC
-
-The WED mcu firmware does not contain all the memory regions defined in
-the dts reserved_memory node (e.g. MT7986 WED firmware does not contain
-cpu-boot region).
-Reverse the mtk_wed_mcu_run_firmware() logic to check all the fw
-sections are defined in the dts reserved_memory node.
-
-Fixes: c6d961aeaa77 ("net: ethernet: mtk_wed: move mem_region array out of mtk_wed_mcu_load_firmware")
-Tested-by: Frank Wunderlich <frank-w@public-files.de>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
-Link: https://lore.kernel.org/r/d983cbfe8ea562fef9264de8f0c501f7d5705bd5.1698098381.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 48 +++++++++++----------
- 1 file changed, 25 insertions(+), 23 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -258,16 +258,12 @@ mtk_wed_get_memory_region(struct mtk_wed
- }
-
- static int
--mtk_wed_mcu_run_firmware(struct mtk_wed_wo *wo, const struct firmware *fw,
-- struct mtk_wed_wo_memory_region *region)
-+mtk_wed_mcu_run_firmware(struct mtk_wed_wo *wo, const struct firmware *fw)
- {
- const u8 *first_region_ptr, *region_ptr, *trailer_ptr, *ptr = fw->data;
- const struct mtk_wed_fw_trailer *trailer;
- const struct mtk_wed_fw_region *fw_region;
-
-- if (!region->phy_addr || !region->size)
-- return 0;
--
- trailer_ptr = fw->data + fw->size - sizeof(*trailer);
- trailer = (const struct mtk_wed_fw_trailer *)trailer_ptr;
- region_ptr = trailer_ptr - trailer->num_region * sizeof(*fw_region);
-@@ -275,33 +271,41 @@ mtk_wed_mcu_run_firmware(struct mtk_wed_
-
- while (region_ptr < trailer_ptr) {
- u32 length;
-+ int i;
-
- fw_region = (const struct mtk_wed_fw_region *)region_ptr;
- length = le32_to_cpu(fw_region->len);
--
-- if (region->phy_addr != le32_to_cpu(fw_region->addr))
-- goto next;
--
-- if (region->size < length)
-- goto next;
--
- if (first_region_ptr < ptr + length)
- goto next;
-
-- if (region->shared && region->consumed)
-- return 0;
-+ for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
-+ struct mtk_wed_wo_memory_region *region;
-
-- if (!region->shared || !region->consumed) {
-- memcpy_toio(region->addr, ptr, length);
-- region->consumed = true;
-- return 0;
-+ region = &mem_region[i];
-+ if (region->phy_addr != le32_to_cpu(fw_region->addr))
-+ continue;
-+
-+ if (region->size < length)
-+ continue;
-+
-+ if (region->shared && region->consumed)
-+ break;
-+
-+ if (!region->shared || !region->consumed) {
-+ memcpy_toio(region->addr, ptr, length);
-+ region->consumed = true;
-+ break;
-+ }
- }
-+
-+ if (i == ARRAY_SIZE(mem_region))
-+ return -EINVAL;
- next:
- region_ptr += sizeof(*fw_region);
- ptr += length;
- }
-
-- return -EINVAL;
-+ return 0;
- }
-
- static int
-@@ -360,11 +364,9 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
- dev_info(wo->hw->dev, "MTK WED WO Chip ID %02x Region %d\n",
- trailer->chip_id, trailer->num_region);
-
-- for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
-- ret = mtk_wed_mcu_run_firmware(wo, fw, &mem_region[i]);
-- if (ret)
-- goto out;
-- }
-+ ret = mtk_wed_mcu_run_firmware(wo, fw);
-+ if (ret)
-+ goto out;
-
- /* set the start address */
- if (!mtk_wed_is_v3_or_greater(wo->hw) && wo->hw->index)
+++ /dev/null
-From 7aa8defd3495208289abcc629946af26a2af3391 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 24 Oct 2023 00:01:30 +0200
-Subject: [PATCH 2/5] net: ethernet: mtk_wed: remove wo pointer in
- wo_r32/wo_w32 signature
-
-wo pointer is no longer used in wo_r32 and wo_w32 routines so get rid of
-it.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/530537db0872f7523deff21f0a5dfdd9b75fdc9d.1698098459.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -32,12 +32,12 @@ static struct mtk_wed_wo_memory_region m
- },
- };
-
--static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg)
-+static u32 wo_r32(u32 reg)
- {
- return readl(mem_region[MTK_WED_WO_REGION_BOOT].addr + reg);
- }
-
--static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
-+static void wo_w32(u32 reg, u32 val)
- {
- writel(val, mem_region[MTK_WED_WO_REGION_BOOT].addr + reg);
- }
-@@ -373,13 +373,13 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
- boot_cr = MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR;
- else
- boot_cr = MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
-- wo_w32(wo, boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16);
-+ wo_w32(boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16);
- /* wo firmware reset */
-- wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00);
-+ wo_w32(MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00);
-
-- val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR) |
-+ val = wo_r32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR) |
- MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK;
-- wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val);
-+ wo_w32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val);
- out:
- release_firmware(fw);
-
+++ /dev/null
-From 65aacd457eaf5d0c958ed8030ec46f99ea808dd9 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Fri, 17 Nov 2023 17:39:22 +0100
-Subject: [PATCH 3/5] net: ethernet: mtk_wed: rely on __dev_alloc_page in
- mtk_wed_tx_buffer_alloc
-
-Simplify the code and use __dev_alloc_page() instead of __dev_alloc_pages()
-with order 0 in mtk_wed_tx_buffer_alloc routine
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/mediatek/mtk_wed.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -670,7 +670,7 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
- void *buf;
- int s;
-
-- page = __dev_alloc_pages(GFP_KERNEL, 0);
-+ page = __dev_alloc_page(GFP_KERNEL);
- if (!page)
- return -ENOMEM;
-
+++ /dev/null
-From 5f5997322584b6257543d4d103f81484b8006d84 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Fri, 17 Nov 2023 17:42:59 +0100
-Subject: [PATCH 4/5] net: ethernet: mtk_wed: add support for devices with more
- than 4GB of dram
-
-Introduce WED offloading support for boards with more than 4GB of
-memory.
-
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/1c7efdf5d384ea7af3c0209723e40b2ee0f956bf.1700239272.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 ++++-
- drivers/net/ethernet/mediatek/mtk_wed.c | 8 +++++---
- drivers/net/ethernet/mediatek/mtk_wed_wo.c | 3 ++-
- 3 files changed, 11 insertions(+), 5 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1158,15 +1158,18 @@ static int mtk_init_fq_dma(struct mtk_et
- phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1);
-
- for (i = 0; i < cnt; i++) {
-+ dma_addr_t addr = dma_addr + i * MTK_QDMA_PAGE_SIZE;
- struct mtk_tx_dma_v2 *txd;
-
- txd = eth->scratch_ring + i * soc->tx.desc_size;
-- txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
-+ txd->txd1 = addr;
- if (i < cnt - 1)
- txd->txd2 = eth->phy_scratch_ring +
- (i + 1) * soc->tx.desc_size;
-
- txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
-+ if (MTK_HAS_CAPS(soc->caps, MTK_36BIT_DMA))
-+ txd->txd3 |= TX_DMA_PREP_ADDR64(addr);
- txd->txd4 = 0;
- if (mtk_is_netsys_v2_or_greater(eth)) {
- txd->txd5 = 0;
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -691,10 +691,11 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
-
- for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) {
- struct mtk_wdma_desc *desc = desc_ptr;
-+ u32 ctrl;
-
- desc->buf0 = cpu_to_le32(buf_phys);
- if (!mtk_wed_is_v3_or_greater(dev->hw)) {
-- u32 txd_size, ctrl;
-+ u32 txd_size;
-
- txd_size = dev->wlan.init_buf(buf, buf_phys,
- token++);
-@@ -708,11 +709,11 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
- ctrl |= MTK_WDMA_DESC_CTRL_LAST_SEG0 |
- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2,
- MTK_WED_BUF_SIZE - txd_size);
-- desc->ctrl = cpu_to_le32(ctrl);
- desc->info = 0;
- } else {
-- desc->ctrl = cpu_to_le32(token << 16);
-+ ctrl = token << 16 | TX_DMA_PREP_ADDR64(buf_phys);
- }
-+ desc->ctrl = cpu_to_le32(ctrl);
-
- desc_ptr += desc_size;
- buf += MTK_WED_BUF_SIZE;
-@@ -811,6 +812,7 @@ mtk_wed_hwrro_buffer_alloc(struct mtk_we
- buf_phys = page_phys;
- for (s = 0; s < MTK_WED_RX_BUF_PER_PAGE; s++) {
- desc->buf0 = cpu_to_le32(buf_phys);
-+ desc->token = cpu_to_le32(RX_DMA_PREP_ADDR64(buf_phys));
- buf_phys += MTK_WED_PAGE_BUF_SIZE;
- desc++;
- }
---- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-@@ -142,7 +142,8 @@ mtk_wed_wo_queue_refill(struct mtk_wed_w
- dma_addr_t addr;
- void *buf;
-
-- buf = page_frag_alloc(&q->cache, q->buf_size, GFP_ATOMIC);
-+ buf = page_frag_alloc(&q->cache, q->buf_size,
-+ GFP_ATOMIC | GFP_DMA32);
- if (!buf)
- break;
-
+++ /dev/null
-From c57e558194430d10d5e5f4acd8a8655b68dade13 Mon Sep 17 00:00:00 2001
-From: Frank Wunderlich <frank-w@public-files.de>
-Date: Mon, 3 Jun 2024 21:25:05 +0200
-Subject: [PATCH] net: ethernet: mtk_eth_soc: handle dma buffer size soc
- specific
-
-The mainline MTK ethernet driver suffers long time from rarly but
-annoying tx queue timeouts. We think that this is caused by fixed
-dma sizes hardcoded for all SoCs.
-
-We suspect this problem arises from a low level of free TX DMADs,
-the TX Ring alomost full.
-
-The transmit timeout is caused by the Tx queue not waking up. The
-Tx queue stops when the free counter is less than ring->thres, and
-it will wake up once the free counter is greater than ring->thres.
-If the CPU is too late to wake up the Tx queues, it may cause a
-transmit timeout.
-Therefore, we increased the TX and RX DMADs to improve this error
-situation.
-
-Use the dma-size implementation from SDK in a per SoC manner. In
-difference to SDK we have no RSS feature yet, so all RX/TX sizes
-should be raised from 512 to 2048 byte except fqdma on mt7988 to
-avoid the tx timeout issue.
-
-Fixes: 656e705243fd ("net-next: mediatek: add support for MT7623 ethernet")
-Suggested-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
-Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 104 +++++++++++++-------
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 9 +-
- 2 files changed, 77 insertions(+), 36 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1131,9 +1131,9 @@ static int mtk_init_fq_dma(struct mtk_et
- {
- const struct mtk_soc_data *soc = eth->soc;
- dma_addr_t phy_ring_tail;
-- int cnt = MTK_QDMA_RING_SIZE;
-+ int cnt = soc->tx.fq_dma_size;
- dma_addr_t dma_addr;
-- int i;
-+ int i, j, len;
-
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM))
- eth->scratch_ring = eth->sram_base;
-@@ -1142,40 +1142,46 @@ static int mtk_init_fq_dma(struct mtk_et
- cnt * soc->tx.desc_size,
- ð->phy_scratch_ring,
- GFP_KERNEL);
-+
- if (unlikely(!eth->scratch_ring))
- return -ENOMEM;
-
-- eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE, GFP_KERNEL);
-- if (unlikely(!eth->scratch_head))
-- return -ENOMEM;
-+ phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1);
-
-- dma_addr = dma_map_single(eth->dma_dev,
-- eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
-- DMA_FROM_DEVICE);
-- if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
-- return -ENOMEM;
-+ for (j = 0; j < DIV_ROUND_UP(soc->tx.fq_dma_size, MTK_FQ_DMA_LENGTH); j++) {
-+ len = min_t(int, cnt - j * MTK_FQ_DMA_LENGTH, MTK_FQ_DMA_LENGTH);
-+ eth->scratch_head[j] = kcalloc(len, MTK_QDMA_PAGE_SIZE, GFP_KERNEL);
-
-- phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1);
-+ if (unlikely(!eth->scratch_head[j]))
-+ return -ENOMEM;
-
-- for (i = 0; i < cnt; i++) {
-- dma_addr_t addr = dma_addr + i * MTK_QDMA_PAGE_SIZE;
-- struct mtk_tx_dma_v2 *txd;
--
-- txd = eth->scratch_ring + i * soc->tx.desc_size;
-- txd->txd1 = addr;
-- if (i < cnt - 1)
-- txd->txd2 = eth->phy_scratch_ring +
-- (i + 1) * soc->tx.desc_size;
--
-- txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
-- if (MTK_HAS_CAPS(soc->caps, MTK_36BIT_DMA))
-- txd->txd3 |= TX_DMA_PREP_ADDR64(addr);
-- txd->txd4 = 0;
-- if (mtk_is_netsys_v2_or_greater(eth)) {
-- txd->txd5 = 0;
-- txd->txd6 = 0;
-- txd->txd7 = 0;
-- txd->txd8 = 0;
-+ dma_addr = dma_map_single(eth->dma_dev,
-+ eth->scratch_head[j], len * MTK_QDMA_PAGE_SIZE,
-+ DMA_FROM_DEVICE);
-+
-+ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
-+ return -ENOMEM;
-+
-+ for (i = 0; i < cnt; i++) {
-+ struct mtk_tx_dma_v2 *txd;
-+
-+ txd = eth->scratch_ring + (j * MTK_FQ_DMA_LENGTH + i) * soc->tx.desc_size;
-+ txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
-+ if (j * MTK_FQ_DMA_LENGTH + i < cnt)
-+ txd->txd2 = eth->phy_scratch_ring +
-+ (j * MTK_FQ_DMA_LENGTH + i + 1) * soc->tx.desc_size;
-+
-+ txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
-+ if (MTK_HAS_CAPS(soc->caps, MTK_36BIT_DMA))
-+ txd->txd3 |= TX_DMA_PREP_ADDR64(dma_addr + i * MTK_QDMA_PAGE_SIZE);
-+
-+ txd->txd4 = 0;
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
-+ txd->txd5 = 0;
-+ txd->txd6 = 0;
-+ txd->txd7 = 0;
-+ txd->txd8 = 0;
-+ }
- }
- }
-
-@@ -2457,7 +2463,7 @@ static int mtk_tx_alloc(struct mtk_eth *
- if (MTK_HAS_CAPS(soc->caps, MTK_QDMA))
- ring_size = MTK_QDMA_RING_SIZE;
- else
-- ring_size = MTK_DMA_SIZE;
-+ ring_size = soc->tx.dma_size;
-
- ring->buf = kcalloc(ring_size, sizeof(*ring->buf),
- GFP_KERNEL);
-@@ -2465,8 +2471,8 @@ static int mtk_tx_alloc(struct mtk_eth *
- goto no_tx_mem;
-
- if (MTK_HAS_CAPS(soc->caps, MTK_SRAM)) {
-- ring->dma = eth->sram_base + ring_size * sz;
-- ring->phys = eth->phy_scratch_ring + ring_size * (dma_addr_t)sz;
-+ ring->dma = eth->sram_base + soc->tx.fq_dma_size * sz;
-+ ring->phys = eth->phy_scratch_ring + soc->tx.fq_dma_size * (dma_addr_t)sz;
- } else {
- ring->dma = dma_alloc_coherent(eth->dma_dev, ring_size * sz,
- &ring->phys, GFP_KERNEL);
-@@ -2588,6 +2594,7 @@ static void mtk_tx_clean(struct mtk_eth
- static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
- {
- const struct mtk_reg_map *reg_map = eth->soc->reg_map;
-+ const struct mtk_soc_data *soc = eth->soc;
- struct mtk_rx_ring *ring;
- int rx_data_len, rx_dma_size, tx_ring_size;
- int i;
-@@ -2595,7 +2602,7 @@ static int mtk_rx_alloc(struct mtk_eth *
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
- tx_ring_size = MTK_QDMA_RING_SIZE;
- else
-- tx_ring_size = MTK_DMA_SIZE;
-+ tx_ring_size = soc->tx.dma_size;
-
- if (rx_flag == MTK_RX_FLAGS_QDMA) {
- if (ring_no)
-@@ -2610,7 +2617,7 @@ static int mtk_rx_alloc(struct mtk_eth *
- rx_dma_size = MTK_HW_LRO_DMA_SIZE;
- } else {
- rx_data_len = ETH_DATA_LEN;
-- rx_dma_size = MTK_DMA_SIZE;
-+ rx_dma_size = soc->rx.dma_size;
- }
-
- ring->frag_size = mtk_max_frag_size(rx_data_len);
-@@ -3139,7 +3146,10 @@ static void mtk_dma_free(struct mtk_eth
- mtk_rx_clean(eth, ð->rx_ring[i], false);
- }
-
-- kfree(eth->scratch_head);
-+ for (i = 0; i < DIV_ROUND_UP(soc->tx.fq_dma_size, MTK_FQ_DMA_LENGTH); i++) {
-+ kfree(eth->scratch_head[i]);
-+ eth->scratch_head[i] = NULL;
-+ }
- }
-
- static bool mtk_hw_reset_check(struct mtk_eth *eth)
-@@ -5045,11 +5055,14 @@ static const struct mtk_soc_data mt2701_
- .desc_size = sizeof(struct mtk_tx_dma),
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
-+ .dma_size = MTK_DMA_SIZE(2K),
-+ .fq_dma_size = MTK_DMA_SIZE(2K),
- },
- .rx = {
- .desc_size = sizeof(struct mtk_rx_dma),
- .irq_done_mask = MTK_RX_DONE_INT,
- .dma_l4_valid = RX_DMA_L4_VALID,
-+ .dma_size = MTK_DMA_SIZE(2K),
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
- },
-@@ -5069,11 +5082,14 @@ static const struct mtk_soc_data mt7621_
- .desc_size = sizeof(struct mtk_tx_dma),
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
-+ .dma_size = MTK_DMA_SIZE(2K),
-+ .fq_dma_size = MTK_DMA_SIZE(2K),
- },
- .rx = {
- .desc_size = sizeof(struct mtk_rx_dma),
- .irq_done_mask = MTK_RX_DONE_INT,
- .dma_l4_valid = RX_DMA_L4_VALID,
-+ .dma_size = MTK_DMA_SIZE(2K),
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
- },
-@@ -5095,11 +5111,14 @@ static const struct mtk_soc_data mt7622_
- .desc_size = sizeof(struct mtk_tx_dma),
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
-+ .dma_size = MTK_DMA_SIZE(2K),
-+ .fq_dma_size = MTK_DMA_SIZE(2K),
- },
- .rx = {
- .desc_size = sizeof(struct mtk_rx_dma),
- .irq_done_mask = MTK_RX_DONE_INT,
- .dma_l4_valid = RX_DMA_L4_VALID,
-+ .dma_size = MTK_DMA_SIZE(2K),
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
- },
-@@ -5120,11 +5139,14 @@ static const struct mtk_soc_data mt7623_
- .desc_size = sizeof(struct mtk_tx_dma),
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
-+ .dma_size = MTK_DMA_SIZE(2K),
-+ .fq_dma_size = MTK_DMA_SIZE(2K),
- },
- .rx = {
- .desc_size = sizeof(struct mtk_rx_dma),
- .irq_done_mask = MTK_RX_DONE_INT,
- .dma_l4_valid = RX_DMA_L4_VALID,
-+ .dma_size = MTK_DMA_SIZE(2K),
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
- },
-@@ -5143,11 +5165,14 @@ static const struct mtk_soc_data mt7629_
- .desc_size = sizeof(struct mtk_tx_dma),
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
-+ .dma_size = MTK_DMA_SIZE(2K),
-+ .fq_dma_size = MTK_DMA_SIZE(2K),
- },
- .rx = {
- .desc_size = sizeof(struct mtk_rx_dma),
- .irq_done_mask = MTK_RX_DONE_INT,
- .dma_l4_valid = RX_DMA_L4_VALID,
-+ .dma_size = MTK_DMA_SIZE(2K),
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
- },
-@@ -5169,6 +5194,8 @@ static const struct mtk_soc_data mt7981_
- .desc_size = sizeof(struct mtk_tx_dma_v2),
- .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
- .dma_len_offset = 8,
-+ .dma_size = MTK_DMA_SIZE(2K),
-+ .fq_dma_size = MTK_DMA_SIZE(2K),
- },
- .rx = {
- .desc_size = sizeof(struct mtk_rx_dma),
-@@ -5176,6 +5203,7 @@ static const struct mtk_soc_data mt7981_
- .dma_l4_valid = RX_DMA_L4_VALID_V2,
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
-+ .dma_size = MTK_DMA_SIZE(2K),
- },
- };
-
-@@ -5195,6 +5223,8 @@ static const struct mtk_soc_data mt7986_
- .desc_size = sizeof(struct mtk_tx_dma_v2),
- .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
- .dma_len_offset = 8,
-+ .dma_size = MTK_DMA_SIZE(2K),
-+ .fq_dma_size = MTK_DMA_SIZE(2K),
- },
- .rx = {
- .desc_size = sizeof(struct mtk_rx_dma),
-@@ -5202,6 +5232,7 @@ static const struct mtk_soc_data mt7986_
- .dma_l4_valid = RX_DMA_L4_VALID_V2,
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
-+ .dma_size = MTK_DMA_SIZE(2K),
- },
- };
-
-@@ -5221,6 +5252,8 @@ static const struct mtk_soc_data mt7988_
- .desc_size = sizeof(struct mtk_tx_dma_v2),
- .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
- .dma_len_offset = 8,
-+ .dma_size = MTK_DMA_SIZE(2K),
-+ .fq_dma_size = MTK_DMA_SIZE(4K),
- },
- .rx = {
- .desc_size = sizeof(struct mtk_rx_dma_v2),
-@@ -5228,6 +5261,7 @@ static const struct mtk_soc_data mt7988_
- .dma_l4_valid = RX_DMA_L4_VALID_V2,
- .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
- .dma_len_offset = 8,
-+ .dma_size = MTK_DMA_SIZE(2K),
- },
- };
-
-@@ -5242,6 +5276,7 @@ static const struct mtk_soc_data rt5350_
- .desc_size = sizeof(struct mtk_tx_dma),
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
-+ .dma_size = MTK_DMA_SIZE(2K),
- },
- .rx = {
- .desc_size = sizeof(struct mtk_rx_dma),
-@@ -5249,6 +5284,7 @@ static const struct mtk_soc_data rt5350_
- .dma_l4_valid = RX_DMA_L4_VALID_PDMA,
- .dma_max_len = MTK_TX_DMA_BUF_LEN,
- .dma_len_offset = 16,
-+ .dma_size = MTK_DMA_SIZE(2K),
- },
- };
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -32,7 +32,9 @@
- #define MTK_TX_DMA_BUF_LEN 0x3fff
- #define MTK_TX_DMA_BUF_LEN_V2 0xffff
- #define MTK_QDMA_RING_SIZE 2048
--#define MTK_DMA_SIZE 512
-+#define MTK_DMA_SIZE(x) (SZ_##x)
-+#define MTK_FQ_DMA_HEAD 32
-+#define MTK_FQ_DMA_LENGTH 2048
- #define MTK_RX_ETH_HLEN (ETH_HLEN + ETH_FCS_LEN)
- #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
- #define MTK_DMA_DUMMY_DESC 0xffffffff
-@@ -1176,6 +1178,8 @@ struct mtk_soc_data {
- u32 desc_size;
- u32 dma_max_len;
- u32 dma_len_offset;
-+ u32 dma_size;
-+ u32 fq_dma_size;
- } tx;
- struct {
- u32 desc_size;
-@@ -1183,6 +1187,7 @@ struct mtk_soc_data {
- u32 dma_l4_valid;
- u32 dma_max_len;
- u32 dma_len_offset;
-+ u32 dma_size;
- } rx;
- };
-
-@@ -1264,7 +1269,7 @@ struct mtk_eth {
- struct napi_struct rx_napi;
- void *scratch_ring;
- dma_addr_t phy_scratch_ring;
-- void *scratch_head;
-+ void *scratch_head[MTK_FQ_DMA_HEAD];
- struct clk *clks[MTK_CLK_MAX];
-
- struct mii_bus *mii_bus;
+++ /dev/null
-From dee4dd10c79aaca192b73520d8fb64628468ae0f Mon Sep 17 00:00:00 2001
-From: Elad Yifee <eladwf@gmail.com>
-Date: Fri, 7 Jun 2024 11:21:50 +0300
-Subject: [PATCH] net: ethernet: mtk_eth_soc: ppe: add support for multiple
- PPEs
-
-Add the missing pieces to allow multiple PPEs units, one for each GMAC.
-mtk_gdm_config has been modified to work on targted mac ID,
-the inner loop moved outside of the function to allow unrelated
-operations like setting the MAC's PPE index.
-Introduce a sanity check in flow_offload_replace to account for
-non-MTK ingress devices.
-Additional field 'ppe_idx' was added to struct mtk_mac in order
-to keep track on the assigned PPE unit.
-
-Signed-off-by: Elad Yifee <eladwf@gmail.com>
-Link: https://lore.kernel.org/r/20240607082155.20021-1-eladwf@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 112 +++++++++++-------
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 8 +-
- .../net/ethernet/mediatek/mtk_ppe_offload.c | 17 ++-
- 3 files changed, 92 insertions(+), 45 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -80,7 +80,9 @@ static const struct mtk_reg_map mtk_reg_
- .fq_blen = 0x1b2c,
- },
- .gdm1_cnt = 0x2400,
-- .gdma_to_ppe = 0x4444,
-+ .gdma_to_ppe = {
-+ [0] = 0x4444,
-+ },
- .ppe_base = 0x0c00,
- .wdma_base = {
- [0] = 0x2800,
-@@ -144,7 +146,10 @@ static const struct mtk_reg_map mt7986_r
- .tx_sch_rate = 0x4798,
- },
- .gdm1_cnt = 0x1c00,
-- .gdma_to_ppe = 0x3333,
-+ .gdma_to_ppe = {
-+ [0] = 0x3333,
-+ [1] = 0x4444,
-+ },
- .ppe_base = 0x2000,
- .wdma_base = {
- [0] = 0x4800,
-@@ -192,7 +197,11 @@ static const struct mtk_reg_map mt7988_r
- .tx_sch_rate = 0x4798,
- },
- .gdm1_cnt = 0x1c00,
-- .gdma_to_ppe = 0x3333,
-+ .gdma_to_ppe = {
-+ [0] = 0x3333,
-+ [1] = 0x4444,
-+ [2] = 0xcccc,
-+ },
- .ppe_base = 0x2000,
- .wdma_base = {
- [0] = 0x4800,
-@@ -2015,6 +2024,7 @@ static int mtk_poll_rx(struct napi_struc
- struct mtk_rx_dma_v2 *rxd, trxd;
- int done = 0, bytes = 0;
- dma_addr_t dma_addr = DMA_MAPPING_ERROR;
-+ int ppe_idx = 0;
-
- while (done < budget) {
- unsigned int pktlen, *rxdcsum;
-@@ -2058,6 +2068,7 @@ static int mtk_poll_rx(struct napi_struc
- goto release_desc;
-
- netdev = eth->netdev[mac];
-+ ppe_idx = eth->mac[mac]->ppe_idx;
-
- if (unlikely(test_bit(MTK_RESETTING, ð->state)))
- goto release_desc;
-@@ -2181,7 +2192,7 @@ static int mtk_poll_rx(struct napi_struc
- }
-
- if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
-- mtk_ppe_check_skb(eth->ppe[0], skb, hash);
-+ mtk_ppe_check_skb(eth->ppe[ppe_idx], skb, hash);
-
- skb_record_rx_queue(skb, 0);
- napi_gro_receive(napi, skb);
-@@ -3276,37 +3287,27 @@ static int mtk_start_dma(struct mtk_eth
- return 0;
- }
-
--static void mtk_gdm_config(struct mtk_eth *eth, u32 config)
-+static void mtk_gdm_config(struct mtk_eth *eth, u32 id, u32 config)
- {
-- int i;
-+ u32 val;
-
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
- return;
-
-- for (i = 0; i < MTK_MAX_DEVS; i++) {
-- u32 val;
--
-- if (!eth->netdev[i])
-- continue;
-+ val = mtk_r32(eth, MTK_GDMA_FWD_CFG(id));
-
-- val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i));
-+ /* default setup the forward port to send frame to PDMA */
-+ val &= ~0xffff;
-
-- /* default setup the forward port to send frame to PDMA */
-- val &= ~0xffff;
-+ /* Enable RX checksum */
-+ val |= MTK_GDMA_ICS_EN | MTK_GDMA_TCS_EN | MTK_GDMA_UCS_EN;
-
-- /* Enable RX checksum */
-- val |= MTK_GDMA_ICS_EN | MTK_GDMA_TCS_EN | MTK_GDMA_UCS_EN;
-+ val |= config;
-
-- val |= config;
-+ if (eth->netdev[id] && netdev_uses_dsa(eth->netdev[id]))
-+ val |= MTK_GDMA_SPECIAL_TAG;
-
-- if (netdev_uses_dsa(eth->netdev[i]))
-- val |= MTK_GDMA_SPECIAL_TAG;
--
-- mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
-- }
-- /* Reset and enable PSE */
-- mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
-- mtk_w32(eth, 0, MTK_RST_GL);
-+ mtk_w32(eth, val, MTK_GDMA_FWD_CFG(id));
- }
-
-
-@@ -3366,7 +3367,10 @@ static int mtk_open(struct net_device *d
- {
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
-- int i, err;
-+ struct mtk_mac *target_mac;
-+ int i, err, ppe_num;
-+
-+ ppe_num = eth->soc->ppe_num;
-
- err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0);
- if (err) {
-@@ -3390,18 +3394,38 @@ static int mtk_open(struct net_device *d
- for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
- mtk_ppe_start(eth->ppe[i]);
-
-- gdm_config = soc->offload_version ? soc->reg_map->gdma_to_ppe
-- : MTK_GDMA_TO_PDMA;
-- mtk_gdm_config(eth, gdm_config);
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
-+ if (!eth->netdev[i])
-+ break;
-+
-+ target_mac = netdev_priv(eth->netdev[i]);
-+ if (!soc->offload_version) {
-+ target_mac->ppe_idx = 0;
-+ gdm_config = MTK_GDMA_TO_PDMA;
-+ } else if (ppe_num >= 3 && target_mac->id == 2) {
-+ target_mac->ppe_idx = 2;
-+ gdm_config = soc->reg_map->gdma_to_ppe[2];
-+ } else if (ppe_num >= 2 && target_mac->id == 1) {
-+ target_mac->ppe_idx = 1;
-+ gdm_config = soc->reg_map->gdma_to_ppe[1];
-+ } else {
-+ target_mac->ppe_idx = 0;
-+ gdm_config = soc->reg_map->gdma_to_ppe[0];
-+ }
-+ mtk_gdm_config(eth, target_mac->id, gdm_config);
-+ }
-+ /* Reset and enable PSE */
-+ mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
-+ mtk_w32(eth, 0, MTK_RST_GL);
-
- napi_enable(ð->tx_napi);
- napi_enable(ð->rx_napi);
- mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
- mtk_rx_irq_enable(eth, soc->rx.irq_done_mask);
- refcount_set(ð->dma_refcnt, 1);
-- }
-- else
-+ } else {
- refcount_inc(ð->dma_refcnt);
-+ }
-
- phylink_start(mac->phylink);
- netif_tx_start_all_queues(dev);
-@@ -3478,7 +3502,8 @@ static int mtk_stop(struct net_device *d
- if (!refcount_dec_and_test(ð->dma_refcnt))
- return 0;
-
-- mtk_gdm_config(eth, MTK_GDMA_DROP_ALL);
-+ for (i = 0; i < MTK_MAX_DEVS; i++)
-+ mtk_gdm_config(eth, i, MTK_GDMA_DROP_ALL);
-
- mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
- mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
-@@ -4957,23 +4982,24 @@ static int mtk_probe(struct platform_dev
- }
-
- if (eth->soc->offload_version) {
-- u32 num_ppe = mtk_is_netsys_v2_or_greater(eth) ? 2 : 1;
-+ u8 ppe_num = eth->soc->ppe_num;
-
-- num_ppe = min_t(u32, ARRAY_SIZE(eth->ppe), num_ppe);
-- for (i = 0; i < num_ppe; i++) {
-- u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400;
-+ ppe_num = min_t(u8, ARRAY_SIZE(eth->ppe), ppe_num);
-+ for (i = 0; i < ppe_num; i++) {
-+ u32 ppe_addr = eth->soc->reg_map->ppe_base;
-
-+ ppe_addr += (i == 2 ? 0xc00 : i * 0x400);
- eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr, i);
-
- if (!eth->ppe[i]) {
- err = -ENOMEM;
- goto err_deinit_ppe;
- }
-- }
-+ err = mtk_eth_offload_init(eth, i);
-
-- err = mtk_eth_offload_init(eth);
-- if (err)
-- goto err_deinit_ppe;
-+ if (err)
-+ goto err_deinit_ppe;
-+ }
- }
-
- for (i = 0; i < MTK_MAX_DEVS; i++) {
-@@ -5076,6 +5102,7 @@ static const struct mtk_soc_data mt7621_
- .required_pctl = false,
- .version = 1,
- .offload_version = 1,
-+ .ppe_num = 1,
- .hash_offset = 2,
- .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
- .tx = {
-@@ -5104,6 +5131,7 @@ static const struct mtk_soc_data mt7622_
- .required_pctl = false,
- .version = 1,
- .offload_version = 2,
-+ .ppe_num = 1,
- .hash_offset = 2,
- .has_accounting = true,
- .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
-@@ -5132,6 +5160,7 @@ static const struct mtk_soc_data mt7623_
- .required_pctl = true,
- .version = 1,
- .offload_version = 1,
-+ .ppe_num = 1,
- .hash_offset = 2,
- .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
- .disable_pll_modes = true,
-@@ -5187,6 +5216,7 @@ static const struct mtk_soc_data mt7981_
- .required_pctl = false,
- .version = 2,
- .offload_version = 2,
-+ .ppe_num = 2,
- .hash_offset = 4,
- .has_accounting = true,
- .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
-@@ -5216,6 +5246,7 @@ static const struct mtk_soc_data mt7986_
- .required_pctl = false,
- .version = 2,
- .offload_version = 2,
-+ .ppe_num = 2,
- .hash_offset = 4,
- .has_accounting = true,
- .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
-@@ -5245,6 +5276,7 @@ static const struct mtk_soc_data mt7988_
- .required_pctl = false,
- .version = 3,
- .offload_version = 2,
-+ .ppe_num = 3,
- .hash_offset = 4,
- .has_accounting = true,
- .foe_entry_size = MTK_FOE_ENTRY_V3_SIZE,
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -1132,7 +1132,7 @@ struct mtk_reg_map {
- u32 tx_sch_rate; /* tx scheduler rate control registers */
- } qdma;
- u32 gdm1_cnt;
-- u32 gdma_to_ppe;
-+ u32 gdma_to_ppe[3];
- u32 ppe_base;
- u32 wdma_base[3];
- u32 pse_iq_sta;
-@@ -1170,6 +1170,7 @@ struct mtk_soc_data {
- u8 offload_version;
- u8 hash_offset;
- u8 version;
-+ u8 ppe_num;
- u16 foe_entry_size;
- netdev_features_t hw_features;
- bool has_accounting;
-@@ -1294,7 +1295,7 @@ struct mtk_eth {
-
- struct metadata_dst *dsa_meta[MTK_MAX_DSA_PORTS];
-
-- struct mtk_ppe *ppe[2];
-+ struct mtk_ppe *ppe[3];
- struct rhashtable flow_table;
-
- struct bpf_prog __rcu *prog;
-@@ -1319,6 +1320,7 @@ struct mtk_eth {
- struct mtk_mac {
- int id;
- phy_interface_t interface;
-+ u8 ppe_idx;
- int speed;
- struct device_node *of_node;
- struct phylink *phylink;
-@@ -1440,7 +1442,7 @@ int mtk_gmac_sgmii_path_setup(struct mtk
- int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
- int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
-
--int mtk_eth_offload_init(struct mtk_eth *eth);
-+int mtk_eth_offload_init(struct mtk_eth *eth, u8 id);
- int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
- void *type_data);
- int mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls,
---- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -245,10 +245,10 @@ mtk_flow_offload_replace(struct mtk_eth
- int ppe_index)
- {
- struct flow_rule *rule = flow_cls_offload_flow_rule(f);
-+ struct net_device *idev = NULL, *odev = NULL;
- struct flow_action_entry *act;
- struct mtk_flow_data data = {};
- struct mtk_foe_entry foe;
-- struct net_device *odev = NULL;
- struct mtk_flow_entry *entry;
- int offload_type = 0;
- int wed_index = -1;
-@@ -264,6 +264,17 @@ mtk_flow_offload_replace(struct mtk_eth
- struct flow_match_meta match;
-
- flow_rule_match_meta(rule, &match);
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
-+ idev = __dev_get_by_index(&init_net, match.key->ingress_ifindex);
-+ if (idev) {
-+ struct mtk_mac *mac = netdev_priv(idev);
-+
-+ if (WARN_ON(mac->ppe_idx >= eth->soc->ppe_num))
-+ return -EINVAL;
-+
-+ ppe_index = mac->ppe_idx;
-+ }
-+ }
- } else {
- return -EOPNOTSUPP;
- }
-@@ -633,7 +644,9 @@ int mtk_eth_setup_tc(struct net_device *
- }
- }
-
--int mtk_eth_offload_init(struct mtk_eth *eth)
-+int mtk_eth_offload_init(struct mtk_eth *eth, u8 id)
- {
-+ if (!eth->ppe[id] || !eth->ppe[id]->foe_table)
-+ return 0;
- return rhashtable_init(ð->flow_table, &mtk_flow_ht_params);
- }
+++ /dev/null
-From 73cfd947dbdb25ef9863ac49c4596a7d53ad4025 Mon Sep 17 00:00:00 2001
-From: Elad Yifee <eladwf@gmail.com>
-Date: Sun, 23 Jun 2024 20:51:09 +0300
-Subject: [PATCH] net: ethernet: mtk_eth_soc: ppe: prevent ppe update for
- non-mtk devices
-
-Introduce an additional validation to ensure that the PPE index
-is modified exclusively for mtk_eth ingress devices.
-This primarily addresses the issue related
-to WED operation with multiple PPEs.
-
-Fixes: dee4dd10c79a ("net: ethernet: mtk_eth_soc: ppe: add support for multiple PPEs")
-Signed-off-by: Elad Yifee <eladwf@gmail.com>
-Link: https://lore.kernel.org/r/20240623175113.24437-1-eladwf@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/ethernet/mediatek/mtk_ppe_offload.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -266,7 +266,7 @@ mtk_flow_offload_replace(struct mtk_eth
- flow_rule_match_meta(rule, &match);
- if (mtk_is_netsys_v2_or_greater(eth)) {
- idev = __dev_get_by_index(&init_net, match.key->ingress_ifindex);
-- if (idev) {
-+ if (idev && idev->netdev_ops == eth->netdev[0]->netdev_ops) {
- struct mtk_mac *mac = netdev_priv(idev);
-
- if (WARN_ON(mac->ppe_idx >= eth->soc->ppe_num))
+++ /dev/null
-From 3b2aef99221d395ce37efa426d7b50e7dcd621d6 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 1 Jul 2024 20:28:14 +0100
-Subject: [PATCH] net: ethernet: mediatek: Allow gaps in MAC allocation
-
-Some devices with MediaTek SoCs don't use the first but only the second
-MAC in the chip. Especially with MT7981 which got a built-in 1GE PHY
-connected to the second MAC this is quite common.
-Make sure to reset and enable PSE also in those cases by skipping gaps
-using 'continue' instead of aborting the loop using 'break'.
-
-Fixes: dee4dd10c79a ("net: ethernet: mtk_eth_soc: ppe: add support for multiple PPEs")
-Suggested-by: Elad Yifee <eladwf@gmail.com>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
-Link: https://patch.msgid.link/379ae584cea112db60f4ada79c7e5ba4f3364a64.1719862038.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
-@@ -3396,7 +3396,7 @@ static int mtk_open(struct net_device *d
-
- for (i = 0; i < MTK_MAX_DEVS; i++) {
- if (!eth->netdev[i])
-- break;
-+ continue;
-
- target_mac = netdev_priv(eth->netdev[i]);
- if (!soc->offload_version) {
+++ /dev/null
-From ca18300e00d584d5693127eb60c108b84883b8ac Mon Sep 17 00:00:00 2001
-From: Shengyu Qu <wiagn233@outlook.com>
-Date: Fri, 5 Jul 2024 01:26:26 +0800
-Subject: [PATCH] net: ethernet: mtk_ppe: Change PPE entries number to 16K
-
-MT7981,7986 and 7988 all supports 32768 PPE entries, and MT7621/MT7620
-supports 16384 PPE entries, but only set to 8192 entries in driver. So
-incrase max entries to 16384 instead.
-
-Signed-off-by: Elad Yifee <eladwf@gmail.com>
-Signed-off-by: Shengyu Qu <wiagn233@outlook.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/TY3P286MB261103F937DE4EEB0F88437D98DE2@TY3P286MB2611.JPNP286.PROD.OUTLOOK.COM
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_ppe.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe.h
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -8,7 +8,7 @@
- #include <linux/bitfield.h>
- #include <linux/rhashtable.h>
-
--#define MTK_PPE_ENTRIES_SHIFT 3
-+#define MTK_PPE_ENTRIES_SHIFT 4
- #define MTK_PPE_ENTRIES (1024 << MTK_PPE_ENTRIES_SHIFT)
- #define MTK_PPE_HASH_MASK (MTK_PPE_ENTRIES - 1)
- #define MTK_PPE_WAIT_TIMEOUT_US 1000000
+++ /dev/null
-From 064fbc4e9b5a6dbda7fe7b67dc7e9e95d31f8d75 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Thu, 4 Jul 2024 11:14:55 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: implement .{get,set}_pauseparam
- ethtool ops
-
-Implement operations to get and set flow-control link parameters.
-Both is done by simply calling phylink_ethtool_{get,set}_pauseparam().
-Fix whitespace in mtk_ethtool_ops while at it.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Michal Kubiak <michal.kubiak@intel.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Tested-by: Rui Salvaterra <rsalvaterra@gmail.com>
-Link: https://patch.msgid.link/e3ece47323444631d6cb479f32af0dfd6d145be0.1720088047.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 18 +++++++++++++++++-
- 1 file changed, 17 insertions(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -4462,6 +4462,20 @@ static int mtk_set_rxnfc(struct net_devi
- return ret;
- }
-
-+static void mtk_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause)
-+{
-+ struct mtk_mac *mac = netdev_priv(dev);
-+
-+ phylink_ethtool_get_pauseparam(mac->phylink, pause);
-+}
-+
-+static int mtk_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause)
-+{
-+ struct mtk_mac *mac = netdev_priv(dev);
-+
-+ return phylink_ethtool_set_pauseparam(mac->phylink, pause);
-+}
-+
- static u16 mtk_select_queue(struct net_device *dev, struct sk_buff *skb,
- struct net_device *sb_dev)
- {
-@@ -4490,8 +4504,10 @@ static const struct ethtool_ops mtk_etht
- .get_strings = mtk_get_strings,
- .get_sset_count = mtk_get_sset_count,
- .get_ethtool_stats = mtk_get_ethtool_stats,
-+ .get_pauseparam = mtk_get_pauseparam,
-+ .set_pauseparam = mtk_set_pauseparam,
- .get_rxnfc = mtk_get_rxnfc,
-- .set_rxnfc = mtk_set_rxnfc,
-+ .set_rxnfc = mtk_set_rxnfc,
- };
-
- static const struct net_device_ops mtk_netdev_ops = {
+++ /dev/null
-From 88806efc034a9830f483963326b99930ad519af1 Mon Sep 17 00:00:00 2001
-From: Felix Fietkau <nbd@nbd.name>
-Date: Tue, 15 Oct 2024 10:17: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>
-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
-@@ -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;
-
-- 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 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");
+++ /dev/null
-From 038ba1dc4e54d51d953f5618d8eb5dd39bd9de25 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 13 Feb 2024 14:35:51 +0100
-Subject: [PATCH] net: phy: aquantia: add AQR111 and AQR111B0 PHY ID
-
-Add Aquantia AQR111 and AQR111B0 PHY ID. These PHY advertise 10G speed
-but actually supports up to 5G speed, hence some manual fixup is needed.
-
-The Aquantia AQR111B0 PHY is just a variant of the AQR111 with smaller
-chip size.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240213133558.1836-1-ansuelsmth@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/phy/aquantia/aquantia_main.c | 52 ++++++++++++++++++++++++
- 1 file changed, 52 insertions(+)
-
---- a/drivers/net/phy/aquantia/aquantia_main.c
-+++ b/drivers/net/phy/aquantia/aquantia_main.c
-@@ -22,6 +22,8 @@
- #define PHY_ID_AQR107 0x03a1b4e0
- #define PHY_ID_AQCS109 0x03a1b5c2
- #define PHY_ID_AQR405 0x03a1b4b0
-+#define PHY_ID_AQR111 0x03a1b610
-+#define PHY_ID_AQR111B0 0x03a1b612
- #define PHY_ID_AQR112 0x03a1b662
- #define PHY_ID_AQR412 0x03a1b712
- #define PHY_ID_AQR113C 0x31c31c12
-@@ -672,6 +674,16 @@ static int aqr107_probe(struct phy_devic
- return aqr_hwmon_probe(phydev);
- }
-
-+static int aqr111_config_init(struct phy_device *phydev)
-+{
-+ /* AQR111 reports supporting speed up to 10G,
-+ * however only speeds up to 5G are supported.
-+ */
-+ phy_set_max_speed(phydev, SPEED_5000);
-+
-+ return aqr107_config_init(phydev);
-+}
-+
- static struct phy_driver aqr_driver[] = {
- {
- PHY_ID_MATCH_MODEL(PHY_ID_AQ1202),
-@@ -746,6 +758,44 @@ static struct phy_driver aqr_driver[] =
- .link_change_notify = aqr107_link_change_notify,
- },
- {
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR111),
-+ .name = "Aquantia AQR111",
-+ .probe = aqr107_probe,
-+ .get_rate_matching = aqr107_get_rate_matching,
-+ .config_init = aqr111_config_init,
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr107_read_status,
-+ .get_tunable = aqr107_get_tunable,
-+ .set_tunable = aqr107_set_tunable,
-+ .suspend = aqr107_suspend,
-+ .resume = aqr107_resume,
-+ .get_sset_count = aqr107_get_sset_count,
-+ .get_strings = aqr107_get_strings,
-+ .get_stats = aqr107_get_stats,
-+ .link_change_notify = aqr107_link_change_notify,
-+},
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0),
-+ .name = "Aquantia AQR111B0",
-+ .probe = aqr107_probe,
-+ .get_rate_matching = aqr107_get_rate_matching,
-+ .config_init = aqr111_config_init,
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr107_read_status,
-+ .get_tunable = aqr107_get_tunable,
-+ .set_tunable = aqr107_set_tunable,
-+ .suspend = aqr107_suspend,
-+ .resume = aqr107_resume,
-+ .get_sset_count = aqr107_get_sset_count,
-+ .get_strings = aqr107_get_strings,
-+ .get_stats = aqr107_get_stats,
-+ .link_change_notify = aqr107_link_change_notify,
-+},
-+{
- PHY_ID_MATCH_MODEL(PHY_ID_AQR405),
- .name = "Aquantia AQR405",
- .config_aneg = aqr_config_aneg,
-@@ -820,6 +870,8 @@ static struct mdio_device_id __maybe_unu
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR111) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
+++ /dev/null
-From 71b605d32017e5b8d257db7344bc2f8e8fcc973e Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 15 Feb 2024 16:30:05 +0100
-Subject: [PATCH] net: phy: aquantia: add AQR113 PHY ID
-
-Add Aquantia AQR113 PHY ID. Aquantia AQR113 is just a chip size variant of
-the already supported AQR133C where the only difference is the PHY ID
-and the hw chip size.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/aquantia/aquantia_main.c | 21 +++++++++++++++++++++
- 1 file changed, 21 insertions(+)
-
---- a/drivers/net/phy/aquantia/aquantia_main.c
-+++ b/drivers/net/phy/aquantia/aquantia_main.c
-@@ -26,6 +26,7 @@
- #define PHY_ID_AQR111B0 0x03a1b612
- #define PHY_ID_AQR112 0x03a1b662
- #define PHY_ID_AQR412 0x03a1b712
-+#define PHY_ID_AQR113 0x31c31c40
- #define PHY_ID_AQR113C 0x31c31c12
-
- #define MDIO_PHYXS_VEND_IF_STATUS 0xe812
-@@ -840,6 +841,25 @@ static struct phy_driver aqr_driver[] =
- .link_change_notify = aqr107_link_change_notify,
- },
- {
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
-+ .name = "Aquantia AQR113",
-+ .probe = aqr107_probe,
-+ .get_rate_matching = aqr107_get_rate_matching,
-+ .config_init = aqr107_config_init,
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr107_read_status,
-+ .get_tunable = aqr107_get_tunable,
-+ .set_tunable = aqr107_set_tunable,
-+ .suspend = aqr107_suspend,
-+ .resume = aqr107_resume,
-+ .get_sset_count = aqr107_get_sset_count,
-+ .get_strings = aqr107_get_strings,
-+ .get_stats = aqr107_get_stats,
-+ .link_change_notify = aqr107_link_change_notify,
-+},
-+{
- PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
- .name = "Aquantia AQR113C",
- .probe = aqr107_probe,
-@@ -874,6 +894,7 @@ static struct mdio_device_id __maybe_unu
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
- { }
- };
+++ /dev/null
-From 6d47302a3f0ba31445478d518d98bd55918bc8ab Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 15 Feb 2024 22:43:30 +0100
-Subject: [PATCH] net: phy: aquantia: add AQR813 PHY ID
-
-Aquantia AQR813 is the Octal Port variant of the AQR113. Add PHY ID for
-it to provide support for it.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/aquantia/aquantia_main.c | 21 +++++++++++++++++++++
- 1 file changed, 21 insertions(+)
-
---- a/drivers/net/phy/aquantia/aquantia_main.c
-+++ b/drivers/net/phy/aquantia/aquantia_main.c
-@@ -28,6 +28,7 @@
- #define PHY_ID_AQR412 0x03a1b712
- #define PHY_ID_AQR113 0x31c31c40
- #define PHY_ID_AQR113C 0x31c31c12
-+#define PHY_ID_AQR813 0x31c31cb2
-
- #define MDIO_PHYXS_VEND_IF_STATUS 0xe812
- #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3)
-@@ -878,6 +879,25 @@ static struct phy_driver aqr_driver[] =
- .get_stats = aqr107_get_stats,
- .link_change_notify = aqr107_link_change_notify,
- },
-+{
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR813),
-+ .name = "Aquantia AQR813",
-+ .probe = aqr107_probe,
-+ .get_rate_matching = aqr107_get_rate_matching,
-+ .config_init = aqr107_config_init,
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr107_read_status,
-+ .get_tunable = aqr107_get_tunable,
-+ .set_tunable = aqr107_set_tunable,
-+ .suspend = aqr107_suspend,
-+ .resume = aqr107_resume,
-+ .get_sset_count = aqr107_get_sset_count,
-+ .get_strings = aqr107_get_strings,
-+ .get_stats = aqr107_get_stats,
-+ .link_change_notify = aqr107_link_change_notify,
-+},
- };
-
- module_phy_driver(aqr_driver);
-@@ -896,6 +916,7 @@ static struct mdio_device_id __maybe_unu
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
- { }
- };
-
+++ /dev/null
-From f13b2b33c7674fa0988dfaa9adb95d7d912b489f Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Wed, 10 Apr 2024 20:42:38 +0100
-Subject: [PATCH 1/2] net: dsa: introduce dsa_phylink_to_port()
-
-We convert from a phylink_config struct to a dsa_port struct in many
-places, let's provide a helper for this.
-
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Link: https://lore.kernel.org/r/E1rudqA-006K9B-85@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- include/net/dsa.h | 6 ++++++
- net/dsa/port.c | 12 ++++++------
- 2 files changed, 12 insertions(+), 6 deletions(-)
-
---- a/include/net/dsa.h
-+++ b/include/net/dsa.h
-@@ -327,6 +327,12 @@ struct dsa_port {
- };
- };
-
-+static inline struct dsa_port *
-+dsa_phylink_to_port(struct phylink_config *config)
-+{
-+ return container_of(config, struct dsa_port, pl_config);
-+}
-+
- /* TODO: ideally DSA ports would have a single dp->link_dp member,
- * and no dst->rtable nor this struct dsa_link would be needed,
- * but this would require some more complex tree walking,
---- a/net/dsa/port.c
-+++ b/net/dsa/port.c
-@@ -1572,7 +1572,7 @@ static struct phylink_pcs *
- dsa_port_phylink_mac_select_pcs(struct phylink_config *config,
- phy_interface_t interface)
- {
-- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
- struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP);
- struct dsa_switch *ds = dp->ds;
-
-@@ -1586,7 +1586,7 @@ static int dsa_port_phylink_mac_prepare(
- unsigned int mode,
- phy_interface_t interface)
- {
-- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
- struct dsa_switch *ds = dp->ds;
- int err = 0;
-
-@@ -1601,7 +1601,7 @@ static void dsa_port_phylink_mac_config(
- unsigned int mode,
- const struct phylink_link_state *state)
- {
-- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
- struct dsa_switch *ds = dp->ds;
-
- if (!ds->ops->phylink_mac_config)
-@@ -1614,7 +1614,7 @@ static int dsa_port_phylink_mac_finish(s
- unsigned int mode,
- phy_interface_t interface)
- {
-- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
- struct dsa_switch *ds = dp->ds;
- int err = 0;
-
-@@ -1629,7 +1629,7 @@ static void dsa_port_phylink_mac_link_do
- unsigned int mode,
- phy_interface_t interface)
- {
-- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
- struct phy_device *phydev = NULL;
- struct dsa_switch *ds = dp->ds;
-
-@@ -1652,7 +1652,7 @@ static void dsa_port_phylink_mac_link_up
- int speed, int duplex,
- bool tx_pause, bool rx_pause)
- {
-- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
- struct dsa_switch *ds = dp->ds;
-
- if (!ds->ops->phylink_mac_link_up) {
+++ /dev/null
-From c22d8240fcd73a1c3ec8dcb055bd583fb970c375 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Wed, 10 Apr 2024 20:42:43 +0100
-Subject: [PATCH 2/2] net: dsa: allow DSA switch drivers to provide their own
- phylink mac ops
-
-Rather than having a shim for each and every phylink MAC operation,
-allow DSA switch drivers to provide their own ops structure. When a
-DSA driver provides the phylink MAC operations, the shimmed ops must
-not be provided, so fail an attempt to register a switch with both
-the phylink_mac_ops in struct dsa_switch and the phylink_mac_*
-operations populated in dsa_switch_ops populated.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://lore.kernel.org/r/E1rudqF-006K9H-Cc@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- include/net/dsa.h | 5 +++++
- net/dsa/dsa.c | 11 +++++++++++
- net/dsa/port.c | 26 ++++++++++++++++++++------
- 3 files changed, 36 insertions(+), 6 deletions(-)
-
---- a/include/net/dsa.h
-+++ b/include/net/dsa.h
-@@ -458,6 +458,11 @@ struct dsa_switch {
- const struct dsa_switch_ops *ops;
-
- /*
-+ * Allow a DSA switch driver to override the phylink MAC ops
-+ */
-+ const struct phylink_mac_ops *phylink_mac_ops;
-+
-+ /*
- * Slave mii_bus and devices for the individual ports.
- */
- u32 phys_mii_mask;
---- a/net/dsa/dsa.c
-+++ b/net/dsa/dsa.c
-@@ -1510,6 +1510,17 @@ static int dsa_switch_probe(struct dsa_s
- if (!ds->num_ports)
- return -EINVAL;
-
-+ if (ds->phylink_mac_ops) {
-+ if (ds->ops->phylink_mac_select_pcs ||
-+ ds->ops->phylink_mac_prepare ||
-+ ds->ops->phylink_mac_config ||
-+ ds->ops->phylink_mac_finish ||
-+ ds->ops->phylink_mac_link_down ||
-+ ds->ops->phylink_mac_link_up ||
-+ ds->ops->adjust_link)
-+ return -EINVAL;
-+ }
-+
- if (np) {
- err = dsa_switch_parse_of(ds, np);
- if (err)
---- a/net/dsa/port.c
-+++ b/net/dsa/port.c
-@@ -1677,6 +1677,7 @@ static const struct phylink_mac_ops dsa_
-
- int dsa_port_phylink_create(struct dsa_port *dp)
- {
-+ const struct phylink_mac_ops *mac_ops;
- struct dsa_switch *ds = dp->ds;
- phy_interface_t mode;
- struct phylink *pl;
-@@ -1700,8 +1701,12 @@ int dsa_port_phylink_create(struct dsa_p
- }
- }
-
-- pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn),
-- mode, &dsa_port_phylink_mac_ops);
-+ mac_ops = &dsa_port_phylink_mac_ops;
-+ if (ds->phylink_mac_ops)
-+ mac_ops = ds->phylink_mac_ops;
-+
-+ pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), mode,
-+ mac_ops);
- if (IS_ERR(pl)) {
- pr_err("error creating PHYLINK: %ld\n", PTR_ERR(pl));
- return PTR_ERR(pl);
-@@ -1967,12 +1972,23 @@ static void dsa_shared_port_validate_of(
- dn, dsa_port_is_cpu(dp) ? "CPU" : "DSA", dp->index);
- }
-
-+static void dsa_shared_port_link_down(struct dsa_port *dp)
-+{
-+ struct dsa_switch *ds = dp->ds;
-+
-+ if (ds->phylink_mac_ops && ds->phylink_mac_ops->mac_link_down)
-+ ds->phylink_mac_ops->mac_link_down(&dp->pl_config, MLO_AN_FIXED,
-+ PHY_INTERFACE_MODE_NA);
-+ else if (ds->ops->phylink_mac_link_down)
-+ ds->ops->phylink_mac_link_down(ds, dp->index, MLO_AN_FIXED,
-+ PHY_INTERFACE_MODE_NA);
-+}
-+
- int dsa_shared_port_link_register_of(struct dsa_port *dp)
- {
- struct dsa_switch *ds = dp->ds;
- bool missing_link_description;
- bool missing_phy_mode;
-- int port = dp->index;
-
- dsa_shared_port_validate_of(dp, &missing_phy_mode,
- &missing_link_description);
-@@ -1988,9 +2004,7 @@ int dsa_shared_port_link_register_of(str
- "Skipping phylink registration for %s port %d\n",
- dsa_port_is_cpu(dp) ? "CPU" : "DSA", dp->index);
- } else {
-- if (ds->ops->phylink_mac_link_down)
-- ds->ops->phylink_mac_link_down(ds, port,
-- MLO_AN_FIXED, PHY_INTERFACE_MODE_NA);
-+ dsa_shared_port_link_down(dp);
-
- return dsa_shared_port_phylink_register(dp);
- }
+++ /dev/null
-From c278ec644377249aba5b1e1ca2b5705fd1c0132c Mon Sep 17 00:00:00 2001
-From: Paweł Owoc <frut3k7@gmail.com>
-Date: Mon, 1 Apr 2024 16:51:06 +0200
-Subject: [PATCH net-next v2] net: phy: aquantia: add support for AQR114C PHY ID
-
-Add support for AQR114C PHY ID. This PHY advertise 10G speed:
-SPEED(0x04): 0x6031
- capabilities: -400g +5g +2.5g -200g -25g -10g-xr -100g -40g -10g/1g -10
- +100 +1000 -10-ts -2-tl +10g
-EXTABLE(0x0B): 0x40fc
- capabilities: -10g-cx4 -10g-lrm +10g-t +10g-kx4 +10g-kr +1000-t +1000-kx
- +100-tx -10-t -p2mp -40g/100g -1000/100-t1 -25g -200g/400g
- +2.5g/5g -1000-h
-
-but supports only up to 5G speed (as with AQR111/111B0).
-AQR111 init config is used to set max speed 5G.
-
-Signed-off-by: Paweł Owoc <frut3k7@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240401145114.1699451-1-frut3k7@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/aquantia/aquantia_main.c | 21 +++++++++++++++++++++
- 1 file changed, 21 insertions(+)
-
---- a/drivers/net/phy/aquantia/aquantia_main.c
-+++ b/drivers/net/phy/aquantia/aquantia_main.c
-@@ -28,6 +28,7 @@
- #define PHY_ID_AQR412 0x03a1b712
- #define PHY_ID_AQR113 0x31c31c40
- #define PHY_ID_AQR113C 0x31c31c12
-+#define PHY_ID_AQR114C 0x31c31c22
- #define PHY_ID_AQR813 0x31c31cb2
-
- #define MDIO_PHYXS_VEND_IF_STATUS 0xe812
-@@ -880,6 +881,25 @@ static struct phy_driver aqr_driver[] =
- .link_change_notify = aqr107_link_change_notify,
- },
- {
-+ PHY_ID_MATCH_MODEL(PHY_ID_AQR114C),
-+ .name = "Aquantia AQR114C",
-+ .probe = aqr107_probe,
-+ .get_rate_matching = aqr107_get_rate_matching,
-+ .config_init = aqr111_config_init,
-+ .config_aneg = aqr_config_aneg,
-+ .config_intr = aqr_config_intr,
-+ .handle_interrupt = aqr_handle_interrupt,
-+ .read_status = aqr107_read_status,
-+ .get_tunable = aqr107_get_tunable,
-+ .set_tunable = aqr107_set_tunable,
-+ .suspend = aqr107_suspend,
-+ .resume = aqr107_resume,
-+ .get_sset_count = aqr107_get_sset_count,
-+ .get_strings = aqr107_get_strings,
-+ .get_stats = aqr107_get_stats,
-+ .link_change_notify = aqr107_link_change_notify,
-+},
-+{
- PHY_ID_MATCH_MODEL(PHY_ID_AQR813),
- .name = "Aquantia AQR813",
- .probe = aqr107_probe,
-@@ -916,6 +936,7 @@ static struct mdio_device_id __maybe_unu
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
-+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR114C) },
- { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
- { }
- };
+++ /dev/null
-From 7f3eb2174512fe6c9c0f062e96eccb0d3cc6d5cd Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 18 Oct 2023 14:35:47 +0200
-Subject: [PATCH] net: introduce napi_is_scheduled helper
-
-We currently have napi_if_scheduled_mark_missed that can be used to
-check if napi is scheduled but that does more thing than simply checking
-it and return a bool. Some driver already implement custom function to
-check if napi is scheduled.
-
-Drop these custom function and introduce napi_is_scheduled that simply
-check if napi is scheduled atomically.
-
-Update any driver and code that implement a similar check and instead
-use this new helper.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/ethernet/chelsio/cxgb3/sge.c | 8 --------
- drivers/net/wireless/realtek/rtw89/core.c | 2 +-
- include/linux/netdevice.h | 23 +++++++++++++++++++++++
- net/core/dev.c | 2 +-
- 4 files changed, 25 insertions(+), 10 deletions(-)
-
---- a/drivers/net/ethernet/chelsio/cxgb3/sge.c
-+++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c
-@@ -2501,14 +2501,6 @@ static int napi_rx_handler(struct napi_s
- return work_done;
- }
-
--/*
-- * Returns true if the device is already scheduled for polling.
-- */
--static inline int napi_is_scheduled(struct napi_struct *napi)
--{
-- return test_bit(NAPI_STATE_SCHED, &napi->state);
--}
--
- /**
- * process_pure_responses - process pure responses from a response queue
- * @adap: the adapter
---- a/drivers/net/wireless/realtek/rtw89/core.c
-+++ b/drivers/net/wireless/realtek/rtw89/core.c
-@@ -1744,7 +1744,7 @@ static void rtw89_core_rx_to_mac80211(st
- struct napi_struct *napi = &rtwdev->napi;
-
- /* In low power mode, napi isn't scheduled. Receive it to netif. */
-- if (unlikely(!test_bit(NAPI_STATE_SCHED, &napi->state)))
-+ if (unlikely(!napi_is_scheduled(napi)))
- napi = NULL;
-
- rtw89_core_hw_to_sband_rate(rx_status);
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -480,6 +480,29 @@ static inline bool napi_prefer_busy_poll
- return test_bit(NAPI_STATE_PREFER_BUSY_POLL, &n->state);
- }
-
-+/**
-+ * napi_is_scheduled - test if NAPI is scheduled
-+ * @n: NAPI context
-+ *
-+ * This check is "best-effort". With no locking implemented,
-+ * a NAPI can be scheduled or terminate right after this check
-+ * and produce not precise results.
-+ *
-+ * NAPI_STATE_SCHED is an internal state, napi_is_scheduled
-+ * should not be used normally and napi_schedule should be
-+ * used instead.
-+ *
-+ * Use only if the driver really needs to check if a NAPI
-+ * is scheduled for example in the context of delayed timer
-+ * that can be skipped if a NAPI is already scheduled.
-+ *
-+ * Return True if NAPI is scheduled, False otherwise.
-+ */
-+static inline bool napi_is_scheduled(struct napi_struct *n)
-+{
-+ return test_bit(NAPI_STATE_SCHED, &n->state);
-+}
-+
- bool napi_schedule_prep(struct napi_struct *n);
-
- /**
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -6643,7 +6643,7 @@ static int __napi_poll(struct napi_struc
- * accidentally calling ->poll() when NAPI is not scheduled.
- */
- work = 0;
-- if (test_bit(NAPI_STATE_SCHED, &n->state)) {
-+ if (napi_is_scheduled(n)) {
- work = n->poll(n, weight);
- trace_napi_poll(n, work, weight);
- }
+++ /dev/null
-From 2d1a42cf7f77cda54dbbee18d00b1200e7bc22aa Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 18 Oct 2023 14:35:48 +0200
-Subject: [PATCH 1/3] net: stmmac: improve TX timer arm logic
-
-There is currently a problem with the TX timer getting armed multiple
-unnecessary times causing big performance regression on some device that
-suffer from heavy handling of hrtimer rearm.
-
-The use of the TX timer is an old implementation that predates the napi
-implementation and the interrupt enable/disable handling.
-
-Due to stmmac being a very old code, the TX timer was never evaluated
-again with this new implementation and was kept there causing
-performance regression. The performance regression started to appear
-with kernel version 4.19 with 8fce33317023 ("net: stmmac: Rework coalesce
-timer and fix multi-queue races") where the timer was reduced to 1ms
-causing it to be armed 40 times more than before.
-
-Decreasing the timer made the problem more present and caused the
-regression in the other of 600-700mbps on some device (regression where
-this was notice is ipq806x).
-
-The problem is in the fact that handling the hrtimer on some target is
-expensive and recent kernel made the timer armed much more times.
-A solution that was proposed was reverting the hrtimer change and use
-mod_timer but such solution would still hide the real problem in the
-current implementation.
-
-To fix the regression, apply some additional logic and skip arming the
-timer when not needed.
-
-Arm the timer ONLY if a napi is not already scheduled. Running the timer
-is redundant since the same function (stmmac_tx_clean) will run in the
-napi TX poll. Also try to cancel any timer if a napi is scheduled to
-prevent redundant run of TX call.
-
-With the following new logic the original performance are restored while
-keeping using the hrtimer.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 18 +++++++++++++++---
- 1 file changed, 15 insertions(+), 3 deletions(-)
-
---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
-+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
-@@ -2988,13 +2988,25 @@ static void stmmac_tx_timer_arm(struct s
- {
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
- u32 tx_coal_timer = priv->tx_coal_timer[queue];
-+ struct stmmac_channel *ch;
-+ struct napi_struct *napi;
-
- if (!tx_coal_timer)
- return;
-
-- hrtimer_start(&tx_q->txtimer,
-- STMMAC_COAL_TIMER(tx_coal_timer),
-- HRTIMER_MODE_REL);
-+ ch = &priv->channel[tx_q->queue_index];
-+ napi = tx_q->xsk_pool ? &ch->rxtx_napi : &ch->tx_napi;
-+
-+ /* Arm timer only if napi is not already scheduled.
-+ * Try to cancel any timer if napi is scheduled, timer will be armed
-+ * again in the next scheduled napi.
-+ */
-+ if (unlikely(!napi_is_scheduled(napi)))
-+ hrtimer_start(&tx_q->txtimer,
-+ STMMAC_COAL_TIMER(tx_coal_timer),
-+ HRTIMER_MODE_REL);
-+ else
-+ hrtimer_try_to_cancel(&tx_q->txtimer);
- }
-
- /**
+++ /dev/null
-From a594166387fe08e6f5a32130c400249a35b298f9 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 18 Oct 2023 14:35:49 +0200
-Subject: [PATCH 2/3] net: stmmac: move TX timer arm after DMA enable
-
-Move TX timer arm call after DMA interrupt is enabled again.
-
-The TX timer arm function changed logic and now is skipped if a napi is
-already scheduled. By moving the TX timer arm call after DMA is enabled,
-we permit to correctly skip if a DMA interrupt has been fired and a napi
-has been scheduled again.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 22 +++++++++++++++----
- 1 file changed, 18 insertions(+), 4 deletions(-)
-
---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
-+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
-@@ -2536,9 +2536,13 @@ static void stmmac_bump_dma_threshold(st
- * @priv: driver private structure
- * @budget: napi budget limiting this functions packet handling
- * @queue: TX queue index
-+ * @pending_packets: signal to arm the TX coal timer
- * Description: it reclaims the transmit resources after transmission completes.
-+ * If some packets still needs to be handled, due to TX coalesce, set
-+ * pending_packets to true to make NAPI arm the TX coal timer.
- */
--static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue)
-+static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
-+ bool *pending_packets)
- {
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
- struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue];
-@@ -2698,7 +2702,7 @@ static int stmmac_tx_clean(struct stmmac
-
- /* We still have pending packets, let's call for a new scheduling */
- if (tx_q->dirty_tx != tx_q->cur_tx)
-- stmmac_tx_timer_arm(priv, queue);
-+ *pending_packets = true;
-
- u64_stats_update_begin(&txq_stats->napi_syncp);
- u64_stats_add(&txq_stats->napi.tx_packets, tx_packets);
-@@ -5604,6 +5608,7 @@ static int stmmac_napi_poll_tx(struct na
- container_of(napi, struct stmmac_channel, tx_napi);
- struct stmmac_priv *priv = ch->priv_data;
- struct stmmac_txq_stats *txq_stats;
-+ bool pending_packets = false;
- u32 chan = ch->index;
- int work_done;
-
-@@ -5612,7 +5617,7 @@ static int stmmac_napi_poll_tx(struct na
- u64_stats_inc(&txq_stats->napi.poll);
- u64_stats_update_end(&txq_stats->napi_syncp);
-
-- work_done = stmmac_tx_clean(priv, budget, chan);
-+ work_done = stmmac_tx_clean(priv, budget, chan, &pending_packets);
- work_done = min(work_done, budget);
-
- if (work_done < budget && napi_complete_done(napi, work_done)) {
-@@ -5623,6 +5628,10 @@ static int stmmac_napi_poll_tx(struct na
- spin_unlock_irqrestore(&ch->lock, flags);
- }
-
-+ /* TX still have packet to handle, check if we need to arm tx timer */
-+ if (pending_packets)
-+ stmmac_tx_timer_arm(priv, chan);
-+
- return work_done;
- }
-
-@@ -5631,6 +5640,7 @@ static int stmmac_napi_poll_rxtx(struct
- struct stmmac_channel *ch =
- container_of(napi, struct stmmac_channel, rxtx_napi);
- struct stmmac_priv *priv = ch->priv_data;
-+ bool tx_pending_packets = false;
- int rx_done, tx_done, rxtx_done;
- struct stmmac_rxq_stats *rxq_stats;
- struct stmmac_txq_stats *txq_stats;
-@@ -5646,7 +5656,7 @@ static int stmmac_napi_poll_rxtx(struct
- u64_stats_inc(&txq_stats->napi.poll);
- u64_stats_update_end(&txq_stats->napi_syncp);
-
-- tx_done = stmmac_tx_clean(priv, budget, chan);
-+ tx_done = stmmac_tx_clean(priv, budget, chan, &tx_pending_packets);
- tx_done = min(tx_done, budget);
-
- rx_done = stmmac_rx_zc(priv, budget, chan);
-@@ -5671,6 +5681,10 @@ static int stmmac_napi_poll_rxtx(struct
- spin_unlock_irqrestore(&ch->lock, flags);
- }
-
-+ /* TX still have packet to handle, check if we need to arm tx timer */
-+ if (tx_pending_packets)
-+ stmmac_tx_timer_arm(priv, chan);
-+
- return min(rxtx_done, budget - 1);
- }
-
+++ /dev/null
-From 039550960a2235cfe2dfaa773df9f98f8da31a0c Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 18 Oct 2023 14:35:50 +0200
-Subject: [PATCH 3/3] net: stmmac: increase TX coalesce timer to 5ms
-
-Commit 8fce33317023 ("net: stmmac: Rework coalesce timer and fix
-multi-queue races") decreased the TX coalesce timer from 40ms to 1ms.
-
-This caused some performance regression on some target (regression was
-reported at least on ipq806x) in the order of 600mbps dropping from
-gigabit handling to only 200mbps.
-
-The problem was identified in the TX timer getting armed too much time.
-While this was fixed and improved in another commit, performance can be
-improved even further by increasing the timer delay a bit moving from
-1ms to 5ms.
-
-The value is a good balance between battery saving by prevending too
-much interrupt to be generated and permitting good performance for
-internet oriented devices.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/ethernet/stmicro/stmmac/common.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/stmicro/stmmac/common.h
-+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
-@@ -318,7 +318,7 @@ struct stmmac_safety_stats {
- #define MIN_DMA_RIWT 0x10
- #define DEF_DMA_RIWT 0xa0
- /* Tx coalesce parameters */
--#define STMMAC_COAL_TX_TIMER 1000
-+#define STMMAC_COAL_TX_TIMER 5000
- #define STMMAC_MAX_COAL_TX_TICK 100000
- #define STMMAC_TX_MAX_FRAMES 256
- #define STMMAC_TX_FRAMES 25
+++ /dev/null
-From 055dd7511f675d26fa283b35bb3dadfc7f77ed97 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Mon, 13 Nov 2023 20:13:26 +0100
-Subject: [PATCH] r8169: improve RTL8411b phy-down fixup
-
-Mirsad proposed a patch to reduce the number of spinlock lock/unlock
-operations and the function code size. This can be further improved
-because the function sets a consecutive register block.
-
-Suggested-by: Mirsad Todorovac <mirsad.todorovac@alu.unizg.hr>
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Reviewed-by: Mirsad Todorovac <mirsad.todorovac@alu.unizg.hr>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/realtek/r8169_main.c | 139 +++++-----------------
- 1 file changed, 28 insertions(+), 111 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -3156,6 +3156,33 @@ static void rtl_hw_start_8168g_2(struct
- rtl_ephy_init(tp, e_info_8168g_2);
- }
-
-+static void rtl8411b_fix_phy_down(struct rtl8169_private *tp)
-+{
-+ static const u16 fix_data[] = {
-+/* 0xf800 */ 0xe008, 0xe00a, 0xe00c, 0xe00e, 0xe027, 0xe04f, 0xe05e, 0xe065,
-+/* 0xf810 */ 0xc602, 0xbe00, 0x0000, 0xc502, 0xbd00, 0x074c, 0xc302, 0xbb00,
-+/* 0xf820 */ 0x080a, 0x6420, 0x48c2, 0x8c20, 0xc516, 0x64a4, 0x49c0, 0xf009,
-+/* 0xf830 */ 0x74a2, 0x8ca5, 0x74a0, 0xc50e, 0x9ca2, 0x1c11, 0x9ca0, 0xe006,
-+/* 0xf840 */ 0x74f8, 0x48c4, 0x8cf8, 0xc404, 0xbc00, 0xc403, 0xbc00, 0x0bf2,
-+/* 0xf850 */ 0x0c0a, 0xe434, 0xd3c0, 0x49d9, 0xf01f, 0xc526, 0x64a5, 0x1400,
-+/* 0xf860 */ 0xf007, 0x0c01, 0x8ca5, 0x1c15, 0xc51b, 0x9ca0, 0xe013, 0xc519,
-+/* 0xf870 */ 0x74a0, 0x48c4, 0x8ca0, 0xc516, 0x74a4, 0x48c8, 0x48ca, 0x9ca4,
-+/* 0xf880 */ 0xc512, 0x1b00, 0x9ba0, 0x1b1c, 0x483f, 0x9ba2, 0x1b04, 0xc508,
-+/* 0xf890 */ 0x9ba0, 0xc505, 0xbd00, 0xc502, 0xbd00, 0x0300, 0x051e, 0xe434,
-+/* 0xf8a0 */ 0xe018, 0xe092, 0xde20, 0xd3c0, 0xc50f, 0x76a4, 0x49e3, 0xf007,
-+/* 0xf8b0 */ 0x49c0, 0xf103, 0xc607, 0xbe00, 0xc606, 0xbe00, 0xc602, 0xbe00,
-+/* 0xf8c0 */ 0x0c4c, 0x0c28, 0x0c2c, 0xdc00, 0xc707, 0x1d00, 0x8de2, 0x48c1,
-+/* 0xf8d0 */ 0xc502, 0xbd00, 0x00aa, 0xe0c0, 0xc502, 0xbd00, 0x0132
-+ };
-+ unsigned long flags;
-+ int i;
-+
-+ raw_spin_lock_irqsave(&tp->mac_ocp_lock, flags);
-+ for (i = 0; i < ARRAY_SIZE(fix_data); i++)
-+ __r8168_mac_ocp_write(tp, 0xf800 + 2 * i, fix_data[i]);
-+ raw_spin_unlock_irqrestore(&tp->mac_ocp_lock, flags);
-+}
-+
- static void rtl_hw_start_8411_2(struct rtl8169_private *tp)
- {
- static const struct ephy_info e_info_8411_2[] = {
-@@ -3189,117 +3216,7 @@ static void rtl_hw_start_8411_2(struct r
- mdelay(3);
- r8168_mac_ocp_write(tp, 0xFC26, 0x0000);
-
-- r8168_mac_ocp_write(tp, 0xF800, 0xE008);
-- r8168_mac_ocp_write(tp, 0xF802, 0xE00A);
-- r8168_mac_ocp_write(tp, 0xF804, 0xE00C);
-- r8168_mac_ocp_write(tp, 0xF806, 0xE00E);
-- r8168_mac_ocp_write(tp, 0xF808, 0xE027);
-- r8168_mac_ocp_write(tp, 0xF80A, 0xE04F);
-- r8168_mac_ocp_write(tp, 0xF80C, 0xE05E);
-- r8168_mac_ocp_write(tp, 0xF80E, 0xE065);
-- r8168_mac_ocp_write(tp, 0xF810, 0xC602);
-- r8168_mac_ocp_write(tp, 0xF812, 0xBE00);
-- r8168_mac_ocp_write(tp, 0xF814, 0x0000);
-- r8168_mac_ocp_write(tp, 0xF816, 0xC502);
-- r8168_mac_ocp_write(tp, 0xF818, 0xBD00);
-- r8168_mac_ocp_write(tp, 0xF81A, 0x074C);
-- r8168_mac_ocp_write(tp, 0xF81C, 0xC302);
-- r8168_mac_ocp_write(tp, 0xF81E, 0xBB00);
-- r8168_mac_ocp_write(tp, 0xF820, 0x080A);
-- r8168_mac_ocp_write(tp, 0xF822, 0x6420);
-- r8168_mac_ocp_write(tp, 0xF824, 0x48C2);
-- r8168_mac_ocp_write(tp, 0xF826, 0x8C20);
-- r8168_mac_ocp_write(tp, 0xF828, 0xC516);
-- r8168_mac_ocp_write(tp, 0xF82A, 0x64A4);
-- r8168_mac_ocp_write(tp, 0xF82C, 0x49C0);
-- r8168_mac_ocp_write(tp, 0xF82E, 0xF009);
-- r8168_mac_ocp_write(tp, 0xF830, 0x74A2);
-- r8168_mac_ocp_write(tp, 0xF832, 0x8CA5);
-- r8168_mac_ocp_write(tp, 0xF834, 0x74A0);
-- r8168_mac_ocp_write(tp, 0xF836, 0xC50E);
-- r8168_mac_ocp_write(tp, 0xF838, 0x9CA2);
-- r8168_mac_ocp_write(tp, 0xF83A, 0x1C11);
-- r8168_mac_ocp_write(tp, 0xF83C, 0x9CA0);
-- r8168_mac_ocp_write(tp, 0xF83E, 0xE006);
-- r8168_mac_ocp_write(tp, 0xF840, 0x74F8);
-- r8168_mac_ocp_write(tp, 0xF842, 0x48C4);
-- r8168_mac_ocp_write(tp, 0xF844, 0x8CF8);
-- r8168_mac_ocp_write(tp, 0xF846, 0xC404);
-- r8168_mac_ocp_write(tp, 0xF848, 0xBC00);
-- r8168_mac_ocp_write(tp, 0xF84A, 0xC403);
-- r8168_mac_ocp_write(tp, 0xF84C, 0xBC00);
-- r8168_mac_ocp_write(tp, 0xF84E, 0x0BF2);
-- r8168_mac_ocp_write(tp, 0xF850, 0x0C0A);
-- r8168_mac_ocp_write(tp, 0xF852, 0xE434);
-- r8168_mac_ocp_write(tp, 0xF854, 0xD3C0);
-- r8168_mac_ocp_write(tp, 0xF856, 0x49D9);
-- r8168_mac_ocp_write(tp, 0xF858, 0xF01F);
-- r8168_mac_ocp_write(tp, 0xF85A, 0xC526);
-- r8168_mac_ocp_write(tp, 0xF85C, 0x64A5);
-- r8168_mac_ocp_write(tp, 0xF85E, 0x1400);
-- r8168_mac_ocp_write(tp, 0xF860, 0xF007);
-- r8168_mac_ocp_write(tp, 0xF862, 0x0C01);
-- r8168_mac_ocp_write(tp, 0xF864, 0x8CA5);
-- r8168_mac_ocp_write(tp, 0xF866, 0x1C15);
-- r8168_mac_ocp_write(tp, 0xF868, 0xC51B);
-- r8168_mac_ocp_write(tp, 0xF86A, 0x9CA0);
-- r8168_mac_ocp_write(tp, 0xF86C, 0xE013);
-- r8168_mac_ocp_write(tp, 0xF86E, 0xC519);
-- r8168_mac_ocp_write(tp, 0xF870, 0x74A0);
-- r8168_mac_ocp_write(tp, 0xF872, 0x48C4);
-- r8168_mac_ocp_write(tp, 0xF874, 0x8CA0);
-- r8168_mac_ocp_write(tp, 0xF876, 0xC516);
-- r8168_mac_ocp_write(tp, 0xF878, 0x74A4);
-- r8168_mac_ocp_write(tp, 0xF87A, 0x48C8);
-- r8168_mac_ocp_write(tp, 0xF87C, 0x48CA);
-- r8168_mac_ocp_write(tp, 0xF87E, 0x9CA4);
-- r8168_mac_ocp_write(tp, 0xF880, 0xC512);
-- r8168_mac_ocp_write(tp, 0xF882, 0x1B00);
-- r8168_mac_ocp_write(tp, 0xF884, 0x9BA0);
-- r8168_mac_ocp_write(tp, 0xF886, 0x1B1C);
-- r8168_mac_ocp_write(tp, 0xF888, 0x483F);
-- r8168_mac_ocp_write(tp, 0xF88A, 0x9BA2);
-- r8168_mac_ocp_write(tp, 0xF88C, 0x1B04);
-- r8168_mac_ocp_write(tp, 0xF88E, 0xC508);
-- r8168_mac_ocp_write(tp, 0xF890, 0x9BA0);
-- r8168_mac_ocp_write(tp, 0xF892, 0xC505);
-- r8168_mac_ocp_write(tp, 0xF894, 0xBD00);
-- r8168_mac_ocp_write(tp, 0xF896, 0xC502);
-- r8168_mac_ocp_write(tp, 0xF898, 0xBD00);
-- r8168_mac_ocp_write(tp, 0xF89A, 0x0300);
-- r8168_mac_ocp_write(tp, 0xF89C, 0x051E);
-- r8168_mac_ocp_write(tp, 0xF89E, 0xE434);
-- r8168_mac_ocp_write(tp, 0xF8A0, 0xE018);
-- r8168_mac_ocp_write(tp, 0xF8A2, 0xE092);
-- r8168_mac_ocp_write(tp, 0xF8A4, 0xDE20);
-- r8168_mac_ocp_write(tp, 0xF8A6, 0xD3C0);
-- r8168_mac_ocp_write(tp, 0xF8A8, 0xC50F);
-- r8168_mac_ocp_write(tp, 0xF8AA, 0x76A4);
-- r8168_mac_ocp_write(tp, 0xF8AC, 0x49E3);
-- r8168_mac_ocp_write(tp, 0xF8AE, 0xF007);
-- r8168_mac_ocp_write(tp, 0xF8B0, 0x49C0);
-- r8168_mac_ocp_write(tp, 0xF8B2, 0xF103);
-- r8168_mac_ocp_write(tp, 0xF8B4, 0xC607);
-- r8168_mac_ocp_write(tp, 0xF8B6, 0xBE00);
-- r8168_mac_ocp_write(tp, 0xF8B8, 0xC606);
-- r8168_mac_ocp_write(tp, 0xF8BA, 0xBE00);
-- r8168_mac_ocp_write(tp, 0xF8BC, 0xC602);
-- r8168_mac_ocp_write(tp, 0xF8BE, 0xBE00);
-- r8168_mac_ocp_write(tp, 0xF8C0, 0x0C4C);
-- r8168_mac_ocp_write(tp, 0xF8C2, 0x0C28);
-- r8168_mac_ocp_write(tp, 0xF8C4, 0x0C2C);
-- r8168_mac_ocp_write(tp, 0xF8C6, 0xDC00);
-- r8168_mac_ocp_write(tp, 0xF8C8, 0xC707);
-- r8168_mac_ocp_write(tp, 0xF8CA, 0x1D00);
-- r8168_mac_ocp_write(tp, 0xF8CC, 0x8DE2);
-- r8168_mac_ocp_write(tp, 0xF8CE, 0x48C1);
-- r8168_mac_ocp_write(tp, 0xF8D0, 0xC502);
-- r8168_mac_ocp_write(tp, 0xF8D2, 0xBD00);
-- r8168_mac_ocp_write(tp, 0xF8D4, 0x00AA);
-- r8168_mac_ocp_write(tp, 0xF8D6, 0xE0C0);
-- r8168_mac_ocp_write(tp, 0xF8D8, 0xC502);
-- r8168_mac_ocp_write(tp, 0xF8DA, 0xBD00);
-- r8168_mac_ocp_write(tp, 0xF8DC, 0x0132);
-+ rtl8411b_fix_phy_down(tp);
-
- r8168_mac_ocp_write(tp, 0xFC26, 0x8000);
-
+++ /dev/null
-From 3a767b482cacd9bfeac786837fcac419af315995 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Thu, 23 Nov 2023 10:53:26 +0100
-Subject: [PATCH] r8169: remove not needed check in rtl_fw_write_firmware
-
-This check can never be true for a firmware file with a correct format.
-Existing checks in rtl_fw_data_ok() are sufficient, no problems with
-invalid firmware files are known.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/realtek/r8169_firmware.c | 3 ---
- 1 file changed, 3 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169_firmware.c
-+++ b/drivers/net/ethernet/realtek/r8169_firmware.c
-@@ -151,9 +151,6 @@ void rtl_fw_write_firmware(struct rtl816
- u32 regno = (action & 0x0fff0000) >> 16;
- enum rtl_fw_opcode opcode = action >> 28;
-
-- if (!action)
-- break;
--
- switch (opcode) {
- case PHY_READ:
- predata = fw_read(tp, regno);
+++ /dev/null
-From cd04b44bf055c4cd6bcee2ebfa6932fb20ef369d Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Mon, 27 Nov 2023 21:16:10 +0100
-Subject: [PATCH] r8169: remove multicast filter limit
-
-Once upon a time, when r8169 was new, the multicast filter limit code
-was copied from RTL8139 driver. There the filter limit is even
-user-configurable.
-The filtering is hash-based and we don't have perfect filtering.
-Actually the mc filtering on RTL8125 still seems to be the same
-as used on 8390/NE2000. So it's not clear to me which benefit it
-should bring when switching to all-multi mode once a certain number
-of filter bits is set. More the opposite: Filtering out at least
-some unwanted mc traffic is better than no filtering.
-Also the available chip documentation doesn't mention any restriction.
-Therefore remove the filter limit.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://lore.kernel.org/r/57076c05-3730-40d1-ab9a-5334b263e41a@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169_main.c | 7 +------
- 1 file changed, 1 insertion(+), 6 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -56,10 +56,6 @@
- #define FIRMWARE_8125A_3 "rtl_nic/rtl8125a-3.fw"
- #define FIRMWARE_8125B_2 "rtl_nic/rtl8125b-2.fw"
-
--/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
-- The RTL chips use a 64 element hash table based on the Ethernet CRC. */
--#define MC_FILTER_LIMIT 32
--
- #define TX_DMA_BURST 7 /* Maximum PCI burst, '7' is unlimited */
- #define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */
-
-@@ -2653,8 +2649,7 @@ static void rtl_set_rx_mode(struct net_d
- rx_mode |= AcceptAllPhys;
- } else if (!(dev->flags & IFF_MULTICAST)) {
- rx_mode &= ~AcceptMulticast;
-- } else if (netdev_mc_count(dev) > MC_FILTER_LIMIT ||
-- dev->flags & IFF_ALLMULTI ||
-+ } else if (dev->flags & IFF_ALLMULTI ||
- tp->mac_version == RTL_GIGA_MAC_VER_35) {
- /* accept all multicasts */
- } else if (netdev_mc_empty(dev)) {
+++ /dev/null
-From 127532cd0f060ebc3c4cbca81b6438728ad5896e Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Mon, 27 Nov 2023 18:20:11 +0100
-Subject: [PATCH] r8169: improve handling task scheduling
-
-If we know that the task is going to be a no-op, don't even schedule it.
-And remove the check for netif_running() in the worker function, the
-check for flag RTL_FLAG_TASK_ENABLED is sufficient. Note that we can't
-remove the check for flag RTL_FLAG_TASK_ENABLED in the worker function
-because we have no guarantee when it will be executed.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
-Link: https://lore.kernel.org/r/c65873a3-7394-4107-99a7-83f20030779c@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169_main.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -2279,6 +2279,9 @@ u16 rtl8168h_2_get_adc_bias_ioffset(stru
-
- static void rtl_schedule_task(struct rtl8169_private *tp, enum rtl_flag flag)
- {
-+ if (!test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags))
-+ return;
-+
- set_bit(flag, tp->wk.flags);
- schedule_work(&tp->wk.work);
- }
-@@ -4526,8 +4529,7 @@ static void rtl_task(struct work_struct
-
- rtnl_lock();
-
-- if (!netif_running(tp->dev) ||
-- !test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags))
-+ if (!test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags))
- goto out_unlock;
-
- if (test_and_clear_bit(RTL_FLAG_TASK_TX_TIMEOUT, tp->wk.flags)) {
+++ /dev/null
-From 18764b883e157e28126b54e7d4ba9dd487d5bf54 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Sat, 16 Dec 2023 20:58:10 +0100
-Subject: [PATCH] r8169: add support for LED's on RTL8168/RTL8101
-
-This adds support for the LED's on most chip versions. Excluded are
-the old non-PCIe versions and RTL8125. RTL8125 has a different LED
-register layout, support for it will follow later.
-
-LED's can be controlled from userspace using the netdev LED trigger.
-
-Tested on RTL8168h.
-
-Note: The driver can't know which LED's are actually physically
-wired. Therefore not every LED device may represent a physically
-available LED.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/realtek/Makefile | 3 +
- drivers/net/ethernet/realtek/r8169.h | 7 +
- drivers/net/ethernet/realtek/r8169_leds.c | 157 ++++++++++++++++++++++
- drivers/net/ethernet/realtek/r8169_main.c | 65 +++++++++
- 4 files changed, 232 insertions(+)
- create mode 100644 drivers/net/ethernet/realtek/r8169_leds.c
-
---- a/drivers/net/ethernet/realtek/Makefile
-+++ b/drivers/net/ethernet/realtek/Makefile
-@@ -7,4 +7,7 @@ obj-$(CONFIG_8139CP) += 8139cp.o
- obj-$(CONFIG_8139TOO) += 8139too.o
- obj-$(CONFIG_ATP) += atp.o
- r8169-objs += r8169_main.o r8169_firmware.o r8169_phy_config.o
-+ifdef CONFIG_LEDS_TRIGGER_NETDEV
-+r8169-objs += r8169_leds.o
-+endif
- obj-$(CONFIG_R8169) += r8169.o
---- a/drivers/net/ethernet/realtek/r8169.h
-+++ b/drivers/net/ethernet/realtek/r8169.h
-@@ -8,6 +8,7 @@
- * See MAINTAINERS file for support contact information.
- */
-
-+#include <linux/netdevice.h>
- #include <linux/types.h>
- #include <linux/phy.h>
-
-@@ -77,3 +78,9 @@ u16 rtl8168h_2_get_adc_bias_ioffset(stru
- u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr);
- void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev,
- enum mac_version ver);
-+
-+void r8169_get_led_name(struct rtl8169_private *tp, int idx,
-+ char *buf, int buf_len);
-+int rtl8168_get_led_mode(struct rtl8169_private *tp);
-+int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
-+void rtl8168_init_leds(struct net_device *ndev);
---- /dev/null
-+++ b/drivers/net/ethernet/realtek/r8169_leds.c
-@@ -0,0 +1,157 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* r8169_leds.c: Realtek 8169/8168/8101/8125 ethernet driver.
-+ *
-+ * Copyright (c) 2023 Heiner Kallweit <hkallweit1@gmail.com>
-+ *
-+ * See MAINTAINERS file for support contact information.
-+ */
-+
-+#include <linux/leds.h>
-+#include <linux/netdevice.h>
-+#include <uapi/linux/uleds.h>
-+
-+#include "r8169.h"
-+
-+#define RTL8168_LED_CTRL_OPTION2 BIT(15)
-+#define RTL8168_LED_CTRL_ACT BIT(3)
-+#define RTL8168_LED_CTRL_LINK_1000 BIT(2)
-+#define RTL8168_LED_CTRL_LINK_100 BIT(1)
-+#define RTL8168_LED_CTRL_LINK_10 BIT(0)
-+
-+#define RTL8168_NUM_LEDS 3
-+
-+#define RTL8168_SUPPORTED_MODES \
-+ (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK_100) | \
-+ BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_RX) | \
-+ BIT(TRIGGER_NETDEV_TX))
-+
-+struct r8169_led_classdev {
-+ struct led_classdev led;
-+ struct net_device *ndev;
-+ int index;
-+};
-+
-+#define lcdev_to_r8169_ldev(lcdev) container_of(lcdev, struct r8169_led_classdev, led)
-+
-+static int rtl8168_led_hw_control_is_supported(struct led_classdev *led_cdev,
-+ unsigned long flags)
-+{
-+ struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
-+ struct rtl8169_private *tp = netdev_priv(ldev->ndev);
-+ int shift = ldev->index * 4;
-+ bool rx, tx;
-+
-+ if (flags & ~RTL8168_SUPPORTED_MODES)
-+ goto nosupp;
-+
-+ rx = flags & BIT(TRIGGER_NETDEV_RX);
-+ tx = flags & BIT(TRIGGER_NETDEV_TX);
-+ if (rx != tx)
-+ goto nosupp;
-+
-+ return 0;
-+
-+nosupp:
-+ /* Switch LED off to indicate that mode isn't supported */
-+ rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0);
-+ return -EOPNOTSUPP;
-+}
-+
-+static int rtl8168_led_hw_control_set(struct led_classdev *led_cdev,
-+ unsigned long flags)
-+{
-+ struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
-+ struct rtl8169_private *tp = netdev_priv(ldev->ndev);
-+ int shift = ldev->index * 4;
-+ u16 mode = 0;
-+
-+ if (flags & BIT(TRIGGER_NETDEV_LINK_10))
-+ mode |= RTL8168_LED_CTRL_LINK_10;
-+ if (flags & BIT(TRIGGER_NETDEV_LINK_100))
-+ mode |= RTL8168_LED_CTRL_LINK_100;
-+ if (flags & BIT(TRIGGER_NETDEV_LINK_1000))
-+ mode |= RTL8168_LED_CTRL_LINK_1000;
-+ if (flags & BIT(TRIGGER_NETDEV_TX))
-+ mode |= RTL8168_LED_CTRL_ACT;
-+
-+ return rtl8168_led_mod_ctrl(tp, 0x000f << shift, mode << shift);
-+}
-+
-+static int rtl8168_led_hw_control_get(struct led_classdev *led_cdev,
-+ unsigned long *flags)
-+{
-+ struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
-+ struct rtl8169_private *tp = netdev_priv(ldev->ndev);
-+ int shift = ldev->index * 4;
-+ int mode;
-+
-+ mode = rtl8168_get_led_mode(tp);
-+ if (mode < 0)
-+ return mode;
-+
-+ if (mode & RTL8168_LED_CTRL_OPTION2) {
-+ rtl8168_led_mod_ctrl(tp, RTL8168_LED_CTRL_OPTION2, 0);
-+ netdev_notice(ldev->ndev, "Deactivating unsupported Option2 LED mode\n");
-+ }
-+
-+ mode = (mode >> shift) & 0x000f;
-+
-+ if (mode & RTL8168_LED_CTRL_ACT)
-+ *flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
-+
-+ if (mode & RTL8168_LED_CTRL_LINK_10)
-+ *flags |= BIT(TRIGGER_NETDEV_LINK_10);
-+ if (mode & RTL8168_LED_CTRL_LINK_100)
-+ *flags |= BIT(TRIGGER_NETDEV_LINK_100);
-+ if (mode & RTL8168_LED_CTRL_LINK_1000)
-+ *flags |= BIT(TRIGGER_NETDEV_LINK_1000);
-+
-+ return 0;
-+}
-+
-+static struct device *
-+ r8169_led_hw_control_get_device(struct led_classdev *led_cdev)
-+{
-+ struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
-+
-+ return &ldev->ndev->dev;
-+}
-+
-+static void rtl8168_setup_ldev(struct r8169_led_classdev *ldev,
-+ struct net_device *ndev, int index)
-+{
-+ struct rtl8169_private *tp = netdev_priv(ndev);
-+ struct led_classdev *led_cdev = &ldev->led;
-+ char led_name[LED_MAX_NAME_SIZE];
-+
-+ ldev->ndev = ndev;
-+ ldev->index = index;
-+
-+ r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE);
-+ led_cdev->name = led_name;
-+ led_cdev->default_trigger = "netdev";
-+ led_cdev->hw_control_trigger = "netdev";
-+ led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
-+ led_cdev->hw_control_is_supported = rtl8168_led_hw_control_is_supported;
-+ led_cdev->hw_control_set = rtl8168_led_hw_control_set;
-+ led_cdev->hw_control_get = rtl8168_led_hw_control_get;
-+ led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
-+
-+ /* ignore errors */
-+ devm_led_classdev_register(&ndev->dev, led_cdev);
-+}
-+
-+void rtl8168_init_leds(struct net_device *ndev)
-+{
-+ /* bind resource mgmt to netdev */
-+ struct device *dev = &ndev->dev;
-+ struct r8169_led_classdev *leds;
-+ int i;
-+
-+ leds = devm_kcalloc(dev, RTL8168_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
-+ if (!leds)
-+ return;
-+
-+ for (i = 0; i < RTL8168_NUM_LEDS; i++)
-+ rtl8168_setup_ldev(leds + i, ndev, i);
-+}
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -285,6 +285,7 @@ enum rtl8168_8101_registers {
- };
-
- enum rtl8168_registers {
-+ LED_CTRL = 0x18,
- LED_FREQ = 0x1a,
- EEE_LED = 0x1b,
- ERIDR = 0x70,
-@@ -643,6 +644,7 @@ struct rtl8169_private {
-
- raw_spinlock_t config25_lock;
- raw_spinlock_t mac_ocp_lock;
-+ struct mutex led_lock; /* serialize LED ctrl RMW access */
-
- raw_spinlock_t cfg9346_usage_lock;
- int cfg9346_usage_count;
-@@ -815,6 +817,62 @@ static const struct rtl_cond name = {
- \
- static bool name ## _check(struct rtl8169_private *tp)
-
-+int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val)
-+{
-+ struct device *dev = tp_to_dev(tp);
-+ int ret;
-+
-+ ret = pm_runtime_resume_and_get(dev);
-+ if (ret < 0)
-+ return ret;
-+
-+ mutex_lock(&tp->led_lock);
-+ RTL_W16(tp, LED_CTRL, (RTL_R16(tp, LED_CTRL) & ~mask) | val);
-+ mutex_unlock(&tp->led_lock);
-+
-+ pm_runtime_put_sync(dev);
-+
-+ return 0;
-+}
-+
-+int rtl8168_get_led_mode(struct rtl8169_private *tp)
-+{
-+ struct device *dev = tp_to_dev(tp);
-+ int ret;
-+
-+ ret = pm_runtime_resume_and_get(dev);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = RTL_R16(tp, LED_CTRL);
-+
-+ pm_runtime_put_sync(dev);
-+
-+ return ret;
-+}
-+
-+void r8169_get_led_name(struct rtl8169_private *tp, int idx,
-+ char *buf, int buf_len)
-+{
-+ struct pci_dev *pdev = tp->pci_dev;
-+ char pdom[8], pfun[8];
-+ int domain;
-+
-+ domain = pci_domain_nr(pdev->bus);
-+ if (domain)
-+ snprintf(pdom, sizeof(pdom), "P%d", domain);
-+ else
-+ pdom[0] = '\0';
-+
-+ if (pdev->multifunction)
-+ snprintf(pfun, sizeof(pfun), "f%d", PCI_FUNC(pdev->devfn));
-+ else
-+ pfun[0] = '\0';
-+
-+ snprintf(buf, buf_len, "en%sp%ds%d%s-%d::lan", pdom, pdev->bus->number,
-+ PCI_SLOT(pdev->devfn), pfun, idx);
-+}
-+
- static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type)
- {
- /* based on RTL8168FP_OOBMAC_BASE in vendor driver */
-@@ -5203,6 +5261,7 @@ static int rtl_init_one(struct pci_dev *
- raw_spin_lock_init(&tp->cfg9346_usage_lock);
- raw_spin_lock_init(&tp->config25_lock);
- raw_spin_lock_init(&tp->mac_ocp_lock);
-+ mutex_init(&tp->led_lock);
-
- dev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev,
- struct pcpu_sw_netstats);
-@@ -5359,6 +5418,12 @@ static int rtl_init_one(struct pci_dev *
- if (rc)
- return rc;
-
-+#if IS_REACHABLE(CONFIG_LEDS_CLASS) && IS_ENABLED(CONFIG_LEDS_TRIGGER_NETDEV)
-+ if (tp->mac_version > RTL_GIGA_MAC_VER_06 &&
-+ tp->mac_version < RTL_GIGA_MAC_VER_61)
-+ rtl8168_init_leds(dev);
-+#endif
-+
- netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
- rtl_chip_infos[chipset].name, dev->dev_addr, xid, tp->irq);
-
+++ /dev/null
-From a2634a5ffcafc31c343c6153ae487eb184c433a6 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Wed, 3 Jan 2024 16:52:04 +0100
-Subject: [PATCH] r8169: fix building with CONFIG_LEDS_CLASS=m
-
-When r8169 is built-in but LED support is a loadable module, the new
-code to drive the LED causes a link failure:
-
-ld: drivers/net/ethernet/realtek/r8169_leds.o: in function `rtl8168_init_leds':
-r8169_leds.c:(.text+0x36c): undefined reference to `devm_led_classdev_register_ext'
-
-LED support is an optional feature, so fix this issue by adding a Kconfig
-symbol R8169_LEDS that is guaranteed to be false if r8169 is built-in
-and LED core support is a module. As a positive side effect of this change
-r8169_leds.o no longer is built under this configuration.
-
-Fixes: 18764b883e15 ("r8169: add support for LED's on RTL8168/RTL8101")
-Reported-by: kernel test robot <lkp@intel.com>
-Closes: https://lore.kernel.org/oe-kbuild-all/202312281159.9TPeXbNd-lkp@intel.com/
-Suggested-by: Arnd Bergmann <arnd@arndb.de>
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Tested-by: Simon Horman <horms@kernel.org> # build-tested
-Tested-by: Arnd Bergmann <arnd@arndb.de>
-Link: https://lore.kernel.org/r/d055aeb5-fe5c-4ccf-987f-5af93a17537b@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/Kconfig | 7 +++++++
- drivers/net/ethernet/realtek/Makefile | 6 ++----
- drivers/net/ethernet/realtek/r8169_main.c | 5 ++---
- 3 files changed, 11 insertions(+), 7 deletions(-)
-
---- a/drivers/net/ethernet/realtek/Kconfig
-+++ b/drivers/net/ethernet/realtek/Kconfig
-@@ -113,4 +113,11 @@ config R8169
- To compile this driver as a module, choose M here: the module
- will be called r8169. This is recommended.
-
-+config R8169_LEDS
-+ def_bool R8169 && LEDS_TRIGGER_NETDEV
-+ depends on !(R8169=y && LEDS_CLASS=m)
-+ help
-+ Optional support for controlling the NIC LED's with the netdev
-+ LED trigger.
-+
- endif # NET_VENDOR_REALTEK
---- a/drivers/net/ethernet/realtek/Makefile
-+++ b/drivers/net/ethernet/realtek/Makefile
-@@ -6,8 +6,6 @@
- obj-$(CONFIG_8139CP) += 8139cp.o
- obj-$(CONFIG_8139TOO) += 8139too.o
- obj-$(CONFIG_ATP) += atp.o
--r8169-objs += r8169_main.o r8169_firmware.o r8169_phy_config.o
--ifdef CONFIG_LEDS_TRIGGER_NETDEV
--r8169-objs += r8169_leds.o
--endif
-+r8169-y += r8169_main.o r8169_firmware.o r8169_phy_config.o
-+r8169-$(CONFIG_R8169_LEDS) += r8169_leds.o
- obj-$(CONFIG_R8169) += r8169.o
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -5418,11 +5418,10 @@ static int rtl_init_one(struct pci_dev *
- if (rc)
- return rc;
-
--#if IS_REACHABLE(CONFIG_LEDS_CLASS) && IS_ENABLED(CONFIG_LEDS_TRIGGER_NETDEV)
-- if (tp->mac_version > RTL_GIGA_MAC_VER_06 &&
-+ if (IS_ENABLED(CONFIG_R8169_LEDS) &&
-+ tp->mac_version > RTL_GIGA_MAC_VER_06 &&
- tp->mac_version < RTL_GIGA_MAC_VER_61)
- rtl8168_init_leds(dev);
--#endif
-
- netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
- rtl_chip_infos[chipset].name, dev->dev_addr, xid, tp->irq);
+++ /dev/null
-From f5d59230ec26aa5e8b59e9f4a4d288703a737479 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Wed, 31 Jan 2024 21:31:01 +0100
-Subject: [PATCH] r8169: simplify EEE handling
-
-We don't have to store the EEE modes to be advertised in the driver,
-phylib does this for us and stores it in phydev->advertising_eee.
-phylib also takes care of properly handling the EEE advertisement.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://lore.kernel.org/r/27c336a8-ea47-483d-815b-02c45ae41da2@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169_main.c | 32 +++--------------------
- 1 file changed, 4 insertions(+), 28 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -656,7 +656,6 @@ struct rtl8169_private {
- struct rtl8169_counters *counters;
- struct rtl8169_tc_offsets tc_offset;
- u32 saved_wolopts;
-- int eee_adv;
-
- const char *fw_name;
- struct rtl_fw *rtl_fw;
-@@ -2037,17 +2036,11 @@ static int rtl8169_get_eee(struct net_de
- static int rtl8169_set_eee(struct net_device *dev, struct ethtool_eee *data)
- {
- struct rtl8169_private *tp = netdev_priv(dev);
-- int ret;
-
- if (!rtl_supports_eee(tp))
- return -EOPNOTSUPP;
-
-- ret = phy_ethtool_set_eee(tp->phydev, data);
--
-- if (!ret)
-- tp->eee_adv = phy_read_mmd(dev->phydev, MDIO_MMD_AN,
-- MDIO_AN_EEE_ADV);
-- return ret;
-+ return phy_ethtool_set_eee(tp->phydev, data);
- }
-
- static void rtl8169_get_ringparam(struct net_device *dev,
-@@ -2112,21 +2105,6 @@ static const struct ethtool_ops rtl8169_
- .set_pauseparam = rtl8169_set_pauseparam,
- };
-
--static void rtl_enable_eee(struct rtl8169_private *tp)
--{
-- struct phy_device *phydev = tp->phydev;
-- int adv;
--
-- /* respect EEE advertisement the user may have set */
-- if (tp->eee_adv >= 0)
-- adv = tp->eee_adv;
-- else
-- adv = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
--
-- if (adv >= 0)
-- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv);
--}
--
- static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii)
- {
- /*
-@@ -2363,9 +2341,6 @@ static void rtl8169_init_phy(struct rtl8
- /* We may have called phy_speed_down before */
- phy_speed_up(tp->phydev);
-
-- if (rtl_supports_eee(tp))
-- rtl_enable_eee(tp);
--
- genphy_soft_reset(tp->phydev);
- }
-
-@@ -5120,7 +5095,9 @@ static int r8169_mdio_register(struct rt
- }
-
- tp->phydev->mac_managed_pm = true;
--
-+ if (rtl_supports_eee(tp))
-+ linkmode_copy(tp->phydev->advertising_eee,
-+ tp->phydev->supported_eee);
- phy_support_asym_pause(tp->phydev);
-
- /* PHY will be woken up in rtl_open() */
-@@ -5255,7 +5232,6 @@ static int rtl_init_one(struct pci_dev *
- tp->dev = dev;
- tp->pci_dev = pdev;
- tp->supports_gmii = ent->driver_data == RTL_CFG_NO_GBIT ? 0 : 1;
-- tp->eee_adv = -1;
- tp->ocp_base = OCP_STD_PHY_BASE;
-
- raw_spin_lock_init(&tp->cfg9346_usage_lock);
+++ /dev/null
-From 3907f1ffc0ecf466d5c04aadc44c4b9203f3ec9a Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Thu, 1 Feb 2024 22:38:01 +0100
-Subject: [PATCH] r8169: add support for RTL8126A
-
-This adds support for the RTL8126A found on Asus z790 Maximus Formula.
-It was successfully tested w/o the firmware at 1000Mbps. Firmware file
-has been provided by Realtek and submitted to linux-firmware.
-2.5G and 5G modes are untested.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/realtek/r8169.h | 1 +
- drivers/net/ethernet/realtek/r8169_main.c | 105 ++++++++++++++----
- .../net/ethernet/realtek/r8169_phy_config.c | 7 ++
- 3 files changed, 89 insertions(+), 24 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169.h
-+++ b/drivers/net/ethernet/realtek/r8169.h
-@@ -68,6 +68,7 @@ enum mac_version {
- /* support for RTL_GIGA_MAC_VER_60 has been removed */
- RTL_GIGA_MAC_VER_61,
- RTL_GIGA_MAC_VER_63,
-+ RTL_GIGA_MAC_VER_65,
- RTL_GIGA_MAC_NONE
- };
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -55,6 +55,7 @@
- #define FIRMWARE_8107E_2 "rtl_nic/rtl8107e-2.fw"
- #define FIRMWARE_8125A_3 "rtl_nic/rtl8125a-3.fw"
- #define FIRMWARE_8125B_2 "rtl_nic/rtl8125b-2.fw"
-+#define FIRMWARE_8126A_2 "rtl_nic/rtl8126a-2.fw"
-
- #define TX_DMA_BURST 7 /* Maximum PCI burst, '7' is unlimited */
- #define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */
-@@ -136,6 +137,7 @@ static const struct {
- [RTL_GIGA_MAC_VER_61] = {"RTL8125A", FIRMWARE_8125A_3},
- /* reserve 62 for CFG_METHOD_4 in the vendor driver */
- [RTL_GIGA_MAC_VER_63] = {"RTL8125B", FIRMWARE_8125B_2},
-+ [RTL_GIGA_MAC_VER_65] = {"RTL8126A", FIRMWARE_8126A_2},
- };
-
- static const struct pci_device_id rtl8169_pci_tbl[] = {
-@@ -158,6 +160,7 @@ static const struct pci_device_id rtl816
- { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0024 },
- { 0x0001, 0x8168, PCI_ANY_ID, 0x2410 },
- { PCI_VDEVICE(REALTEK, 0x8125) },
-+ { PCI_VDEVICE(REALTEK, 0x8126) },
- { PCI_VDEVICE(REALTEK, 0x3000) },
- {}
- };
-@@ -327,8 +330,12 @@ enum rtl8168_registers {
- };
-
- enum rtl8125_registers {
-+ INT_CFG0_8125 = 0x34,
-+#define INT_CFG0_ENABLE_8125 BIT(0)
-+#define INT_CFG0_CLKREQEN BIT(3)
- IntrMask_8125 = 0x38,
- IntrStatus_8125 = 0x3c,
-+ INT_CFG1_8125 = 0x7a,
- TxPoll_8125 = 0x90,
- MAC0_BKP = 0x19e0,
- EEE_TXIDLE_TIMER_8125 = 0x6048,
-@@ -1166,7 +1173,7 @@ static void rtl_writephy(struct rtl8169_
- case RTL_GIGA_MAC_VER_31:
- r8168dp_2_mdio_write(tp, location, val);
- break;
-- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
- r8168g_mdio_write(tp, location, val);
- break;
- default:
-@@ -1181,7 +1188,7 @@ static int rtl_readphy(struct rtl8169_pr
- case RTL_GIGA_MAC_VER_28:
- case RTL_GIGA_MAC_VER_31:
- return r8168dp_2_mdio_read(tp, location);
-- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
- return r8168g_mdio_read(tp, location);
- default:
- return r8169_mdio_read(tp, location);
-@@ -1390,7 +1397,7 @@ static void rtl_set_d3_pll_down(struct r
- case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26:
- case RTL_GIGA_MAC_VER_29 ... RTL_GIGA_MAC_VER_30:
- case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_37:
-- case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_65:
- if (enable)
- RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~D3_NO_PLL_DOWN);
- else
-@@ -1557,7 +1564,7 @@ static void __rtl8169_set_wol(struct rtl
- break;
- case RTL_GIGA_MAC_VER_34:
- case RTL_GIGA_MAC_VER_37:
-- case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_65:
- if (wolopts)
- rtl_mod_config2(tp, 0, PME_SIGNAL);
- else
-@@ -2123,6 +2130,9 @@ static enum mac_version rtl8169_get_mac_
- u16 val;
- enum mac_version ver;
- } mac_info[] = {
-+ /* 8126A family. */
-+ { 0x7cf, 0x649, RTL_GIGA_MAC_VER_65 },
-+
- /* 8125B family. */
- { 0x7cf, 0x641, RTL_GIGA_MAC_VER_63 },
-
-@@ -2393,6 +2403,7 @@ static void rtl_init_rxcfg(struct rtl816
- RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST);
- break;
- case RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_65:
- RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST |
- RX_PAUSE_SLOT_ON);
- break;
-@@ -2579,7 +2590,7 @@ static void rtl_wait_txrx_fifo_empty(str
- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_61:
- rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42);
- break;
-- case RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_65:
- RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
- rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42);
- rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond_2, 100, 42);
-@@ -2822,7 +2833,7 @@ static void rtl_enable_exit_l1(struct rt
- case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_38:
- rtl_eri_set_bits(tp, 0xd4, 0x0c00);
- break;
-- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
- r8168_mac_ocp_modify(tp, 0xc0ac, 0, 0x1f80);
- break;
- default:
-@@ -2836,7 +2847,7 @@ static void rtl_disable_exit_l1(struct r
- case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38:
- rtl_eri_clear_bits(tp, 0xd4, 0x1f00);
- break;
-- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
- r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0);
- break;
- default:
-@@ -2846,6 +2857,8 @@ static void rtl_disable_exit_l1(struct r
-
- static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable)
- {
-+ u8 val8;
-+
- if (tp->mac_version < RTL_GIGA_MAC_VER_32)
- return;
-
-@@ -2859,11 +2872,19 @@ static void rtl_hw_aspm_clkreq_enable(st
- return;
-
- rtl_mod_config5(tp, 0, ASPM_en);
-- rtl_mod_config2(tp, 0, ClkReqEn);
-+ switch (tp->mac_version) {
-+ case RTL_GIGA_MAC_VER_65:
-+ val8 = RTL_R8(tp, INT_CFG0_8125) | INT_CFG0_CLKREQEN;
-+ RTL_W8(tp, INT_CFG0_8125, val8);
-+ break;
-+ default:
-+ rtl_mod_config2(tp, 0, ClkReqEn);
-+ break;
-+ }
-
- switch (tp->mac_version) {
- case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
-- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
- /* reset ephy tx/rx disable timer */
- r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0);
- /* chip can trigger L1.2 */
-@@ -2875,14 +2896,22 @@ static void rtl_hw_aspm_clkreq_enable(st
- } else {
- switch (tp->mac_version) {
- case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
-- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
- r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0);
- break;
- default:
- break;
- }
-
-- rtl_mod_config2(tp, ClkReqEn, 0);
-+ switch (tp->mac_version) {
-+ case RTL_GIGA_MAC_VER_65:
-+ val8 = RTL_R8(tp, INT_CFG0_8125) & ~INT_CFG0_CLKREQEN;
-+ RTL_W8(tp, INT_CFG0_8125, val8);
-+ break;
-+ default:
-+ rtl_mod_config2(tp, ClkReqEn, 0);
-+ break;
-+ }
- rtl_mod_config5(tp, ASPM_en, 0);
- }
- }
-@@ -3595,10 +3624,15 @@ static void rtl_hw_start_8125_common(str
- /* disable new tx descriptor format */
- r8168_mac_ocp_modify(tp, 0xeb58, 0x0001, 0x0000);
-
-- if (tp->mac_version == RTL_GIGA_MAC_VER_63)
-+ if (tp->mac_version == RTL_GIGA_MAC_VER_65)
-+ RTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) & ~0x02);
-+
-+ if (tp->mac_version == RTL_GIGA_MAC_VER_65)
-+ r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400);
-+ else if (tp->mac_version == RTL_GIGA_MAC_VER_63)
- r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200);
- else
-- r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400);
-+ r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0300);
-
- if (tp->mac_version == RTL_GIGA_MAC_VER_63)
- r8168_mac_ocp_modify(tp, 0xe63e, 0x0c30, 0x0000);
-@@ -3611,6 +3645,10 @@ static void rtl_hw_start_8125_common(str
- r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0030);
- r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000);
- r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001);
-+ if (tp->mac_version == RTL_GIGA_MAC_VER_65)
-+ r8168_mac_ocp_modify(tp, 0xea1c, 0x0300, 0x0000);
-+ else
-+ r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000);
- r8168_mac_ocp_modify(tp, 0xe0c0, 0x4f0f, 0x4403);
- r8168_mac_ocp_modify(tp, 0xe052, 0x0080, 0x0068);
- r8168_mac_ocp_modify(tp, 0xd430, 0x0fff, 0x047f);
-@@ -3625,10 +3663,10 @@ static void rtl_hw_start_8125_common(str
-
- rtl_loop_wait_low(tp, &rtl_mac_ocp_e00e_cond, 1000, 10);
-
-- if (tp->mac_version == RTL_GIGA_MAC_VER_63)
-- rtl8125b_config_eee_mac(tp);
-- else
-+ if (tp->mac_version == RTL_GIGA_MAC_VER_61)
- rtl8125a_config_eee_mac(tp);
-+ else
-+ rtl8125b_config_eee_mac(tp);
-
- rtl_disable_rxdvgate(tp);
- }
-@@ -3672,6 +3710,12 @@ static void rtl_hw_start_8125b(struct rt
- rtl_hw_start_8125_common(tp);
- }
-
-+static void rtl_hw_start_8126a(struct rtl8169_private *tp)
-+{
-+ rtl_set_def_aspm_entry_latency(tp);
-+ rtl_hw_start_8125_common(tp);
-+}
-+
- static void rtl_hw_config(struct rtl8169_private *tp)
- {
- static const rtl_generic_fct hw_configs[] = {
-@@ -3714,6 +3758,7 @@ static void rtl_hw_config(struct rtl8169
- [RTL_GIGA_MAC_VER_53] = rtl_hw_start_8117,
- [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2,
- [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b,
-+ [RTL_GIGA_MAC_VER_65] = rtl_hw_start_8126a,
- };
-
- if (hw_configs[tp->mac_version])
-@@ -3724,9 +3769,23 @@ static void rtl_hw_start_8125(struct rtl
- {
- int i;
-
-+ RTL_W8(tp, INT_CFG0_8125, 0x00);
-+
- /* disable interrupt coalescing */
-- for (i = 0xa00; i < 0xb00; i += 4)
-- RTL_W32(tp, i, 0);
-+ switch (tp->mac_version) {
-+ case RTL_GIGA_MAC_VER_61:
-+ for (i = 0xa00; i < 0xb00; i += 4)
-+ RTL_W32(tp, i, 0);
-+ break;
-+ case RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_65:
-+ for (i = 0xa00; i < 0xa80; i += 4)
-+ RTL_W32(tp, i, 0);
-+ RTL_W16(tp, INT_CFG1_8125, 0x0000);
-+ break;
-+ default:
-+ break;
-+ }
-
- rtl_hw_config(tp);
- }
-@@ -3804,8 +3863,7 @@ static int rtl8169_change_mtu(struct net
- rtl_jumbo_config(tp);
-
- switch (tp->mac_version) {
-- case RTL_GIGA_MAC_VER_61:
-- case RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
- rtl8125_set_eee_txidle_timer(tp);
- break;
- default:
-@@ -3954,7 +4012,7 @@ static void rtl8169_cleanup(struct rtl81
- RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
- rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666);
- break;
-- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
- rtl_enable_rxdvgate(tp);
- fsleep(2000);
- break;
-@@ -4111,8 +4169,7 @@ static unsigned int rtl_quirk_packet_pad
-
- switch (tp->mac_version) {
- case RTL_GIGA_MAC_VER_34:
-- case RTL_GIGA_MAC_VER_61:
-- case RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
- padto = max_t(unsigned int, padto, ETH_ZLEN);
- break;
- default:
-@@ -5147,7 +5204,7 @@ static void rtl_hw_initialize(struct rtl
- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48:
- rtl_hw_init_8168g(tp);
- break;
-- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
- rtl_hw_init_8125(tp);
- break;
- default:
---- a/drivers/net/ethernet/realtek/r8169_phy_config.c
-+++ b/drivers/net/ethernet/realtek/r8169_phy_config.c
-@@ -1104,6 +1104,12 @@ static void rtl8125b_hw_phy_config(struc
- rtl8125b_config_eee_phy(phydev);
- }
-
-+static void rtl8126a_hw_phy_config(struct rtl8169_private *tp,
-+ struct phy_device *phydev)
-+{
-+ r8169_apply_firmware(tp);
-+}
-+
- void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev,
- enum mac_version ver)
- {
-@@ -1154,6 +1160,7 @@ void r8169_hw_phy_config(struct rtl8169_
- [RTL_GIGA_MAC_VER_53] = rtl8117_hw_phy_config,
- [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config,
- [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config,
-+ [RTL_GIGA_MAC_VER_65] = rtl8126a_hw_phy_config,
- };
-
- if (phy_configs[ver])
+++ /dev/null
-From 4c49b6824a607af4760fac4f5c0b9954ab902cef Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Wed, 7 Feb 2024 08:16:40 +0100
-Subject: [PATCH] r8169: improve checking for valid LED modes
-
-After 3a2746320403 ("leds: trigger: netdev: Display only supported link
-speed attribute") the check for valid link modes can be simplified.
-In addition factor it out, so that it can be re-used by the upcoming
-LED support for RTL8125.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://lore.kernel.org/r/8876a9f4-7a2d-48c3-8eae-0d834f5c27c5@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169_leds.c | 38 ++++++++++++-----------
- 1 file changed, 20 insertions(+), 18 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169_leds.c
-+++ b/drivers/net/ethernet/realtek/r8169_leds.c
-@@ -20,11 +20,6 @@
-
- #define RTL8168_NUM_LEDS 3
-
--#define RTL8168_SUPPORTED_MODES \
-- (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK_100) | \
-- BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_RX) | \
-- BIT(TRIGGER_NETDEV_TX))
--
- struct r8169_led_classdev {
- struct led_classdev led;
- struct net_device *ndev;
-@@ -33,28 +28,35 @@ struct r8169_led_classdev {
-
- #define lcdev_to_r8169_ldev(lcdev) container_of(lcdev, struct r8169_led_classdev, led)
-
-+static bool r8169_trigger_mode_is_valid(unsigned long flags)
-+{
-+ bool rx, tx;
-+
-+ if (flags & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
-+ return false;
-+ if (flags & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
-+ return false;
-+
-+ rx = flags & BIT(TRIGGER_NETDEV_RX);
-+ tx = flags & BIT(TRIGGER_NETDEV_TX);
-+
-+ return rx == tx;
-+}
-+
- static int rtl8168_led_hw_control_is_supported(struct led_classdev *led_cdev,
- unsigned long flags)
- {
- struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
- struct rtl8169_private *tp = netdev_priv(ldev->ndev);
- int shift = ldev->index * 4;
-- bool rx, tx;
-
-- if (flags & ~RTL8168_SUPPORTED_MODES)
-- goto nosupp;
--
-- rx = flags & BIT(TRIGGER_NETDEV_RX);
-- tx = flags & BIT(TRIGGER_NETDEV_TX);
-- if (rx != tx)
-- goto nosupp;
-+ if (!r8169_trigger_mode_is_valid(flags)) {
-+ /* Switch LED off to indicate that mode isn't supported */
-+ rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0);
-+ return -EOPNOTSUPP;
-+ }
-
- return 0;
--
--nosupp:
-- /* Switch LED off to indicate that mode isn't supported */
-- rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0);
-- return -EOPNOTSUPP;
- }
-
- static int rtl8168_led_hw_control_set(struct led_classdev *led_cdev,
+++ /dev/null
-From 400909df6e6543cb5cce3db9bbcd413d59125327 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Sat, 10 Feb 2024 17:58:29 +0100
-Subject: [PATCH] r8169: simplify code by using core-provided pcpu stats
- allocation
-
-Use core-provided pcpu stats allocation instead of open-coding it in
-the driver.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://lore.kernel.org/r/03f5bb3b-d7f4-48be-ae8a-54862ec4566c@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/ethernet/realtek/r8169_main.c | 7 ++-----
- 1 file changed, 2 insertions(+), 5 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -5296,11 +5296,6 @@ static int rtl_init_one(struct pci_dev *
- raw_spin_lock_init(&tp->mac_ocp_lock);
- mutex_init(&tp->led_lock);
-
-- dev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev,
-- struct pcpu_sw_netstats);
-- if (!dev->tstats)
-- return -ENOMEM;
--
- /* Get the *optional* external "ether_clk" used on some boards */
- tp->clk = devm_clk_get_optional_enabled(&pdev->dev, "ether_clk");
- if (IS_ERR(tp->clk))
-@@ -5415,6 +5410,8 @@ static int rtl_init_one(struct pci_dev *
- dev->hw_features |= NETIF_F_RXALL;
- dev->hw_features |= NETIF_F_RXFCS;
-
-+ dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
-+
- netdev_sw_irq_coalesce_default_on(dev);
-
- /* configure chip for default features */
+++ /dev/null
-From be51ed104ba9929c741afb718ef7198dbcecef94 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Mon, 12 Feb 2024 19:44:11 +0100
-Subject: [PATCH] r8169: add LED support for RTL8125/RTL8126
-
-This adds LED support for RTL8125/RTL8126.
-
-Note: Due to missing datasheets changing the 5Gbps link mode isn't
-supported for RTL8126.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/f982602c-9de3-4ca6-85a3-2c1d118dcb15@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169.h | 3 +
- drivers/net/ethernet/realtek/r8169_leds.c | 106 ++++++++++++++++++++++
- drivers/net/ethernet/realtek/r8169_main.c | 61 ++++++++++++-
- 3 files changed, 166 insertions(+), 4 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169.h
-+++ b/drivers/net/ethernet/realtek/r8169.h
-@@ -85,3 +85,6 @@ void r8169_get_led_name(struct rtl8169_p
- int rtl8168_get_led_mode(struct rtl8169_private *tp);
- int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
- void rtl8168_init_leds(struct net_device *ndev);
-+int rtl8125_get_led_mode(struct rtl8169_private *tp, int index);
-+int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode);
-+void rtl8125_init_leds(struct net_device *ndev);
---- a/drivers/net/ethernet/realtek/r8169_leds.c
-+++ b/drivers/net/ethernet/realtek/r8169_leds.c
-@@ -18,7 +18,14 @@
- #define RTL8168_LED_CTRL_LINK_100 BIT(1)
- #define RTL8168_LED_CTRL_LINK_10 BIT(0)
-
-+#define RTL8125_LED_CTRL_ACT BIT(9)
-+#define RTL8125_LED_CTRL_LINK_2500 BIT(5)
-+#define RTL8125_LED_CTRL_LINK_1000 BIT(3)
-+#define RTL8125_LED_CTRL_LINK_100 BIT(1)
-+#define RTL8125_LED_CTRL_LINK_10 BIT(0)
-+
- #define RTL8168_NUM_LEDS 3
-+#define RTL8125_NUM_LEDS 4
-
- struct r8169_led_classdev {
- struct led_classdev led;
-@@ -157,3 +164,102 @@ void rtl8168_init_leds(struct net_device
- for (i = 0; i < RTL8168_NUM_LEDS; i++)
- rtl8168_setup_ldev(leds + i, ndev, i);
- }
-+
-+static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev,
-+ unsigned long flags)
-+{
-+ struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
-+ struct rtl8169_private *tp = netdev_priv(ldev->ndev);
-+
-+ if (!r8169_trigger_mode_is_valid(flags)) {
-+ /* Switch LED off to indicate that mode isn't supported */
-+ rtl8125_set_led_mode(tp, ldev->index, 0);
-+ return -EOPNOTSUPP;
-+ }
-+
-+ return 0;
-+}
-+
-+static int rtl8125_led_hw_control_set(struct led_classdev *led_cdev,
-+ unsigned long flags)
-+{
-+ struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
-+ struct rtl8169_private *tp = netdev_priv(ldev->ndev);
-+ u16 mode = 0;
-+
-+ if (flags & BIT(TRIGGER_NETDEV_LINK_10))
-+ mode |= RTL8125_LED_CTRL_LINK_10;
-+ if (flags & BIT(TRIGGER_NETDEV_LINK_100))
-+ mode |= RTL8125_LED_CTRL_LINK_100;
-+ if (flags & BIT(TRIGGER_NETDEV_LINK_1000))
-+ mode |= RTL8125_LED_CTRL_LINK_1000;
-+ if (flags & BIT(TRIGGER_NETDEV_LINK_2500))
-+ mode |= RTL8125_LED_CTRL_LINK_2500;
-+ if (flags & (BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX)))
-+ mode |= RTL8125_LED_CTRL_ACT;
-+
-+ return rtl8125_set_led_mode(tp, ldev->index, mode);
-+}
-+
-+static int rtl8125_led_hw_control_get(struct led_classdev *led_cdev,
-+ unsigned long *flags)
-+{
-+ struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
-+ struct rtl8169_private *tp = netdev_priv(ldev->ndev);
-+ int mode;
-+
-+ mode = rtl8125_get_led_mode(tp, ldev->index);
-+ if (mode < 0)
-+ return mode;
-+
-+ if (mode & RTL8125_LED_CTRL_LINK_10)
-+ *flags |= BIT(TRIGGER_NETDEV_LINK_10);
-+ if (mode & RTL8125_LED_CTRL_LINK_100)
-+ *flags |= BIT(TRIGGER_NETDEV_LINK_100);
-+ if (mode & RTL8125_LED_CTRL_LINK_1000)
-+ *flags |= BIT(TRIGGER_NETDEV_LINK_1000);
-+ if (mode & RTL8125_LED_CTRL_LINK_2500)
-+ *flags |= BIT(TRIGGER_NETDEV_LINK_2500);
-+ if (mode & RTL8125_LED_CTRL_ACT)
-+ *flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
-+
-+ return 0;
-+}
-+
-+static void rtl8125_setup_led_ldev(struct r8169_led_classdev *ldev,
-+ struct net_device *ndev, int index)
-+{
-+ struct rtl8169_private *tp = netdev_priv(ndev);
-+ struct led_classdev *led_cdev = &ldev->led;
-+ char led_name[LED_MAX_NAME_SIZE];
-+
-+ ldev->ndev = ndev;
-+ ldev->index = index;
-+
-+ r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE);
-+ led_cdev->name = led_name;
-+ led_cdev->hw_control_trigger = "netdev";
-+ led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
-+ led_cdev->hw_control_is_supported = rtl8125_led_hw_control_is_supported;
-+ led_cdev->hw_control_set = rtl8125_led_hw_control_set;
-+ led_cdev->hw_control_get = rtl8125_led_hw_control_get;
-+ led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
-+
-+ /* ignore errors */
-+ devm_led_classdev_register(&ndev->dev, led_cdev);
-+}
-+
-+void rtl8125_init_leds(struct net_device *ndev)
-+{
-+ /* bind resource mgmt to netdev */
-+ struct device *dev = &ndev->dev;
-+ struct r8169_led_classdev *leds;
-+ int i;
-+
-+ leds = devm_kcalloc(dev, RTL8125_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
-+ if (!leds)
-+ return;
-+
-+ for (i = 0; i < RTL8125_NUM_LEDS; i++)
-+ rtl8125_setup_led_ldev(leds + i, ndev, i);
-+}
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -330,17 +330,23 @@ enum rtl8168_registers {
- };
-
- enum rtl8125_registers {
-+ LEDSEL0 = 0x18,
- INT_CFG0_8125 = 0x34,
- #define INT_CFG0_ENABLE_8125 BIT(0)
- #define INT_CFG0_CLKREQEN BIT(3)
- IntrMask_8125 = 0x38,
- IntrStatus_8125 = 0x3c,
- INT_CFG1_8125 = 0x7a,
-+ LEDSEL2 = 0x84,
-+ LEDSEL1 = 0x86,
- TxPoll_8125 = 0x90,
-+ LEDSEL3 = 0x96,
- MAC0_BKP = 0x19e0,
- EEE_TXIDLE_TIMER_8125 = 0x6048,
- };
-
-+#define LEDSEL_MASK_8125 0x23f
-+
- #define RX_VLAN_INNER_8125 BIT(22)
- #define RX_VLAN_OUTER_8125 BIT(23)
- #define RX_VLAN_8125 (RX_VLAN_INNER_8125 | RX_VLAN_OUTER_8125)
-@@ -857,6 +863,51 @@ int rtl8168_get_led_mode(struct rtl8169_
- return ret;
- }
-
-+static int rtl8125_get_led_reg(int index)
-+{
-+ static const int led_regs[] = { LEDSEL0, LEDSEL1, LEDSEL2, LEDSEL3 };
-+
-+ return led_regs[index];
-+}
-+
-+int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode)
-+{
-+ int reg = rtl8125_get_led_reg(index);
-+ struct device *dev = tp_to_dev(tp);
-+ int ret;
-+ u16 val;
-+
-+ ret = pm_runtime_resume_and_get(dev);
-+ if (ret < 0)
-+ return ret;
-+
-+ mutex_lock(&tp->led_lock);
-+ val = RTL_R16(tp, reg) & ~LEDSEL_MASK_8125;
-+ RTL_W16(tp, reg, val | mode);
-+ mutex_unlock(&tp->led_lock);
-+
-+ pm_runtime_put_sync(dev);
-+
-+ return 0;
-+}
-+
-+int rtl8125_get_led_mode(struct rtl8169_private *tp, int index)
-+{
-+ int reg = rtl8125_get_led_reg(index);
-+ struct device *dev = tp_to_dev(tp);
-+ int ret;
-+
-+ ret = pm_runtime_resume_and_get(dev);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = RTL_R16(tp, reg);
-+
-+ pm_runtime_put_sync(dev);
-+
-+ return ret;
-+}
-+
- void r8169_get_led_name(struct rtl8169_private *tp, int idx,
- char *buf, int buf_len)
- {
-@@ -5448,10 +5499,12 @@ static int rtl_init_one(struct pci_dev *
- if (rc)
- return rc;
-
-- if (IS_ENABLED(CONFIG_R8169_LEDS) &&
-- tp->mac_version > RTL_GIGA_MAC_VER_06 &&
-- tp->mac_version < RTL_GIGA_MAC_VER_61)
-- rtl8168_init_leds(dev);
-+ if (IS_ENABLED(CONFIG_R8169_LEDS)) {
-+ if (rtl_is_8125(tp))
-+ rtl8125_init_leds(dev);
-+ else if (tp->mac_version > RTL_GIGA_MAC_VER_06)
-+ rtl8168_init_leds(dev);
-+ }
-
- netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
- rtl_chip_infos[chipset].name, dev->dev_addr, xid, tp->irq);
+++ /dev/null
-From 2ce30993831041b9dcd31eb12896be6611e8b7e2 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Mon, 12 Feb 2024 19:57:46 +0100
-Subject: [PATCH] r8169: add generic rtl_set_eee_txidle_timer function
-
-Add a generic setter for the EEE tx idle timer and use it with all
-RTL8125/RTL8126 chip versions, in line with the vendor driver.
-This prepares for adding EEE tx idle timer support for additional
-chip versions.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/39beed72-0dc4-4c45-8899-b72c43ab62a7@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169_main.c | 34 +++++++++++++----------
- 1 file changed, 20 insertions(+), 14 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -646,6 +646,7 @@ struct rtl8169_private {
- struct page *Rx_databuff[NUM_RX_DESC]; /* Rx data buffers */
- struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */
- u16 cp_cmd;
-+ u16 tx_lpi_timer;
- u32 irq_mask;
- int irq;
- struct clk *clk;
-@@ -2081,6 +2082,22 @@ static int rtl_set_coalesce(struct net_d
- return 0;
- }
-
-+static void rtl_set_eee_txidle_timer(struct rtl8169_private *tp)
-+{
-+ unsigned int timer_val = READ_ONCE(tp->dev->mtu) + ETH_HLEN + 0x20;
-+
-+ switch (tp->mac_version) {
-+ case RTL_GIGA_MAC_VER_61:
-+ case RTL_GIGA_MAC_VER_63:
-+ case RTL_GIGA_MAC_VER_65:
-+ tp->tx_lpi_timer = timer_val;
-+ RTL_W16(tp, EEE_TXIDLE_TIMER_8125, timer_val);
-+ break;
-+ default:
-+ break;
-+ }
-+}
-+
- static int rtl8169_get_eee(struct net_device *dev, struct ethtool_eee *data)
- {
- struct rtl8169_private *tp = netdev_priv(dev);
-@@ -2339,14 +2356,8 @@ static void rtl8125a_config_eee_mac(stru
- r8168_mac_ocp_modify(tp, 0xeb62, 0, BIT(2) | BIT(1));
- }
-
--static void rtl8125_set_eee_txidle_timer(struct rtl8169_private *tp)
--{
-- RTL_W16(tp, EEE_TXIDLE_TIMER_8125, tp->dev->mtu + ETH_HLEN + 0x20);
--}
--
- static void rtl8125b_config_eee_mac(struct rtl8169_private *tp)
- {
-- rtl8125_set_eee_txidle_timer(tp);
- r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0));
- }
-
-@@ -3879,6 +3890,8 @@ static void rtl_hw_start(struct rtl8169
- rtl_hw_aspm_clkreq_enable(tp, false);
- RTL_W16(tp, CPlusCmd, tp->cp_cmd);
-
-+ rtl_set_eee_txidle_timer(tp);
-+
- if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
- rtl_hw_start_8169(tp);
- else if (rtl_is_8125(tp))
-@@ -3912,14 +3925,7 @@ static int rtl8169_change_mtu(struct net
- dev->mtu = new_mtu;
- netdev_update_features(dev);
- rtl_jumbo_config(tp);
--
-- switch (tp->mac_version) {
-- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
-- rtl8125_set_eee_txidle_timer(tp);
-- break;
-- default:
-- break;
-- }
-+ rtl_set_eee_txidle_timer(tp);
-
- return 0;
- }
+++ /dev/null
-From 57d2d2c8f132c830565058a5cdd8138350e068ec Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Mon, 12 Feb 2024 19:58:47 +0100
-Subject: [PATCH] r8169: support setting the EEE tx idle timer on
- RTL8168h
-
-Support setting the EEE tx idle timer also on RTL8168h.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/cfb69ec9-24c4-4aad-9909-fdae3088add4@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169_main.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -2087,6 +2087,11 @@ static void rtl_set_eee_txidle_timer(str
- unsigned int timer_val = READ_ONCE(tp->dev->mtu) + ETH_HLEN + 0x20;
-
- switch (tp->mac_version) {
-+ case RTL_GIGA_MAC_VER_46:
-+ case RTL_GIGA_MAC_VER_48:
-+ tp->tx_lpi_timer = timer_val;
-+ r8168_mac_ocp_write(tp, 0xe048, timer_val);
-+ break;
- case RTL_GIGA_MAC_VER_61:
- case RTL_GIGA_MAC_VER_63:
- case RTL_GIGA_MAC_VER_65:
+++ /dev/null
-From 9c50139727265c088f936e496777bf588850e9f1 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Mon, 12 Feb 2024 19:59:26 +0100
-Subject: [PATCH] r8169: add support for returning tx_lpi_timer in
- ethtool get_eee
-
-Add support for returning the tx_lpi_timer value to userspace.
-This is supported by few chip versions only: RTL8168h/RTL8125/RTL8126
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/4eee9c34-c5d6-4c96-9b05-455896dea59a@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169_main.c | 22 +++++++++++++++++++++-
- 1 file changed, 21 insertions(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -2103,14 +2103,34 @@ static void rtl_set_eee_txidle_timer(str
- }
- }
-
-+static unsigned int r8169_get_tx_lpi_timer_us(struct rtl8169_private *tp)
-+{
-+ unsigned int speed = tp->phydev->speed;
-+ unsigned int timer = tp->tx_lpi_timer;
-+
-+ if (!timer || speed == SPEED_UNKNOWN)
-+ return 0;
-+
-+ /* tx_lpi_timer value is in bytes */
-+ return DIV_ROUND_CLOSEST(timer * BITS_PER_BYTE, speed);
-+}
-+
- static int rtl8169_get_eee(struct net_device *dev, struct ethtool_eee *data)
- {
- struct rtl8169_private *tp = netdev_priv(dev);
-+ int ret;
-
- if (!rtl_supports_eee(tp))
- return -EOPNOTSUPP;
-
-- return phy_ethtool_get_eee(tp->phydev, data);
-+ ret = phy_ethtool_get_eee(tp->phydev, data);
-+ if (ret)
-+ return ret;
-+
-+ data->tx_lpi_timer = r8169_get_tx_lpi_timer_us(tp);
-+ data->tx_lpi_enabled = data->tx_lpi_timer ? data->eee_enabled : false;
-+
-+ return 0;
- }
-
- static int rtl8169_set_eee(struct net_device *dev, struct ethtool_eee *data)
+++ /dev/null
-From f4d3e595c0000ce39dec7e4799ea42ce42ab6867 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Sat, 17 Feb 2024 15:48:23 +0100
-Subject: [PATCH] r8169: add MODULE_FIRMWARE entry for RTL8126A
-
-Add the missing MODULE_FIRMWARE entry for RTL8126A.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://lore.kernel.org/r/47ef79d2-59c4-4d44-9595-366c70c4ad87@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169_main.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -703,6 +703,7 @@ MODULE_FIRMWARE(FIRMWARE_8168FP_3);
- MODULE_FIRMWARE(FIRMWARE_8107E_2);
- MODULE_FIRMWARE(FIRMWARE_8125A_3);
- MODULE_FIRMWARE(FIRMWARE_8125B_2);
-+MODULE_FIRMWARE(FIRMWARE_8126A_2);
-
- static inline struct device *tp_to_dev(struct rtl8169_private *tp)
- {
+++ /dev/null
-From 19fa4f2a85d777a8052e869c1b892a2f7556569d Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Mon, 8 Apr 2024 20:47:40 +0200
-Subject: [PATCH] r8169: fix LED-related deadlock on module removal
-
-Binding devm_led_classdev_register() to the netdev is problematic
-because on module removal we get a RTNL-related deadlock. Fix this
-by avoiding the device-managed LED functions.
-
-Note: We can safely call led_classdev_unregister() for a LED even
-if registering it failed, because led_classdev_unregister() detects
-this and is a no-op in this case.
-
-Fixes: 18764b883e15 ("r8169: add support for LED's on RTL8168/RTL8101")
-Cc: stable@vger.kernel.org
-Reported-by: Lukas Wunner <lukas@wunner.de>
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/realtek/r8169.h | 6 ++--
- drivers/net/ethernet/realtek/r8169_leds.c | 35 +++++++++++++++--------
- drivers/net/ethernet/realtek/r8169_main.c | 8 ++++--
- 3 files changed, 33 insertions(+), 16 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169.h
-+++ b/drivers/net/ethernet/realtek/r8169.h
-@@ -73,6 +73,7 @@ enum mac_version {
- };
-
- struct rtl8169_private;
-+struct r8169_led_classdev;
-
- void r8169_apply_firmware(struct rtl8169_private *tp);
- u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp);
-@@ -84,7 +85,8 @@ void r8169_get_led_name(struct rtl8169_p
- char *buf, int buf_len);
- int rtl8168_get_led_mode(struct rtl8169_private *tp);
- int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
--void rtl8168_init_leds(struct net_device *ndev);
-+struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev);
- int rtl8125_get_led_mode(struct rtl8169_private *tp, int index);
- int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode);
--void rtl8125_init_leds(struct net_device *ndev);
-+struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev);
-+void r8169_remove_leds(struct r8169_led_classdev *leds);
---- a/drivers/net/ethernet/realtek/r8169_leds.c
-+++ b/drivers/net/ethernet/realtek/r8169_leds.c
-@@ -147,22 +147,22 @@ static void rtl8168_setup_ldev(struct r8
- led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
-
- /* ignore errors */
-- devm_led_classdev_register(&ndev->dev, led_cdev);
-+ led_classdev_register(&ndev->dev, led_cdev);
- }
-
--void rtl8168_init_leds(struct net_device *ndev)
-+struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev)
- {
-- /* bind resource mgmt to netdev */
-- struct device *dev = &ndev->dev;
- struct r8169_led_classdev *leds;
- int i;
-
-- leds = devm_kcalloc(dev, RTL8168_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
-+ leds = kcalloc(RTL8168_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
- if (!leds)
-- return;
-+ return NULL;
-
- for (i = 0; i < RTL8168_NUM_LEDS; i++)
- rtl8168_setup_ldev(leds + i, ndev, i);
-+
-+ return leds;
- }
-
- static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev,
-@@ -246,20 +246,31 @@ static void rtl8125_setup_led_ldev(struc
- led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
-
- /* ignore errors */
-- devm_led_classdev_register(&ndev->dev, led_cdev);
-+ led_classdev_register(&ndev->dev, led_cdev);
- }
-
--void rtl8125_init_leds(struct net_device *ndev)
-+struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev)
- {
-- /* bind resource mgmt to netdev */
-- struct device *dev = &ndev->dev;
- struct r8169_led_classdev *leds;
- int i;
-
-- leds = devm_kcalloc(dev, RTL8125_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
-+ leds = kcalloc(RTL8125_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
- if (!leds)
-- return;
-+ return NULL;
-
- for (i = 0; i < RTL8125_NUM_LEDS; i++)
- rtl8125_setup_led_ldev(leds + i, ndev, i);
-+
-+ return leds;
-+}
-+
-+void r8169_remove_leds(struct r8169_led_classdev *leds)
-+{
-+ if (!leds)
-+ return;
-+
-+ for (struct r8169_led_classdev *l = leds; l->ndev; l++)
-+ led_classdev_unregister(&l->led);
-+
-+ kfree(leds);
- }
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -674,6 +674,8 @@ struct rtl8169_private {
- const char *fw_name;
- struct rtl_fw *rtl_fw;
-
-+ struct r8169_led_classdev *leds;
-+
- u32 ocp_base;
- };
-
-@@ -5075,6 +5077,8 @@ static void rtl_remove_one(struct pci_de
-
- cancel_work_sync(&tp->wk.work);
-
-+ r8169_remove_leds(tp->leds);
-+
- unregister_netdev(tp->dev);
-
- if (tp->dash_type != RTL_DASH_NONE)
-@@ -5533,9 +5537,9 @@ static int rtl_init_one(struct pci_dev *
-
- if (IS_ENABLED(CONFIG_R8169_LEDS)) {
- if (rtl_is_8125(tp))
-- rtl8125_init_leds(dev);
-+ tp->leds = rtl8125_init_leds(dev);
- else if (tp->mac_version > RTL_GIGA_MAC_VER_06)
-- rtl8168_init_leds(dev);
-+ tp->leds = rtl8168_init_leds(dev);
- }
-
- netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
+++ /dev/null
-From 97e176fcbbf3c0f2bd410c9b241177c051f57176 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Wed, 10 Apr 2024 15:11:28 +0200
-Subject: [PATCH] r8169: add missing conditional compiling for call to
- r8169_remove_leds
-
-Add missing dependency on CONFIG_R8169_LEDS. As-is a link error occurs
-if config option CONFIG_R8169_LEDS isn't enabled.
-
-Fixes: 19fa4f2a85d7 ("r8169: fix LED-related deadlock on module removal")
-Reported-by: Venkat Rao Bagalkote <venkat88@linux.vnet.ibm.com>
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Tested-By: Venkat Rao Bagalkote <venkat88@linux.vnet.ibm.com>
-Link: https://lore.kernel.org/r/d080038c-eb6b-45ac-9237-b8c1cdd7870f@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169_main.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -5077,7 +5077,8 @@ static void rtl_remove_one(struct pci_de
-
- cancel_work_sync(&tp->wk.work);
-
-- r8169_remove_leds(tp->leds);
-+ if (IS_ENABLED(CONFIG_R8169_LEDS))
-+ r8169_remove_leds(tp->leds);
-
- unregister_netdev(tp->dev);
-
+++ /dev/null
-From 39f59c72ad3a1eaab9a60f0671bc94d2bc826d21 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Sun, 7 Apr 2024 23:19:25 +0200
-Subject: [PATCH] r8169: add support for RTL8168M
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-A user reported an unknown chip version. According to the r8168 vendor
-driver it's called RTL8168M, but handling is identical to RTL8168H.
-So let's simply treat it as RTL8168H.
-
-Tested-by: Евгений <octobergun@gmail.com>
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/realtek/r8169_main.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -2257,6 +2257,8 @@ static enum mac_version rtl8169_get_mac_
- * the wild. Let's disable detection.
- * { 0x7cf, 0x540, RTL_GIGA_MAC_VER_45 },
- */
-+ /* Realtek calls it RTL8168M, but it's handled like RTL8168H */
-+ { 0x7cf, 0x6c0, RTL_GIGA_MAC_VER_46 },
-
- /* 8168G family. */
- { 0x7cf, 0x5c8, RTL_GIGA_MAC_VER_44 },
+++ /dev/null
-From 1eb2cded45b35816085c1f962933c187d970f9dc Mon Sep 17 00:00:00 2001
-From: Eric Dumazet <edumazet@google.com>
-Date: Mon, 6 May 2024 10:28:12 +0000
-Subject: [PATCH] net: annotate writes on dev->mtu from ndo_change_mtu()
-
-Simon reported that ndo_change_mtu() methods were never
-updated to use WRITE_ONCE(dev->mtu, new_mtu) as hinted
-in commit 501a90c94510 ("inet: protect against too small
-mtu values.")
-
-We read dev->mtu without holding RTNL in many places,
-with READ_ONCE() annotations.
-
-It is time to take care of ndo_change_mtu() methods
-to use corresponding WRITE_ONCE()
-
-Signed-off-by: Eric Dumazet <edumazet@google.com>
-Reported-by: Simon Horman <horms@kernel.org>
-Closes: https://lore.kernel.org/netdev/20240505144608.GB67882@kernel.org/
-Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
-Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Acked-by: Shannon Nelson <shannon.nelson@amd.com>
-Link: https://lore.kernel.org/r/20240506102812.3025432-1-edumazet@google.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/8139cp.c | 4 ++--
- drivers/net/ethernet/realtek/r8169_main.c | 2 +-
- 2 files changed, 3 insertions(+), 3 deletions(-)
-
---- a/drivers/net/ethernet/realtek/8139cp.c
-+++ b/drivers/net/ethernet/realtek/8139cp.c
-@@ -1277,14 +1277,14 @@ static int cp_change_mtu(struct net_devi
-
- /* if network interface not up, no need for complexity */
- if (!netif_running(dev)) {
-- dev->mtu = new_mtu;
-+ WRITE_ONCE(dev->mtu, new_mtu);
- cp_set_rxbufsize(cp); /* set new rx buf size */
- return 0;
- }
-
- /* network IS up, close it, reset MTU, and come up again. */
- cp_close(dev);
-- dev->mtu = new_mtu;
-+ WRITE_ONCE(dev->mtu, new_mtu);
- cp_set_rxbufsize(cp);
- return cp_open(dev);
- }
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -3952,7 +3952,7 @@ static int rtl8169_change_mtu(struct net
- {
- struct rtl8169_private *tp = netdev_priv(dev);
-
-- dev->mtu = new_mtu;
-+ WRITE_ONCE(dev->mtu, new_mtu);
- netdev_update_features(dev);
- rtl_jumbo_config(tp);
- rtl_set_eee_txidle_timer(tp);
+++ /dev/null
-From 6994520a332887f1688464f250c9ec8002a89a8e Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Mon, 27 May 2024 21:16:56 +0200
-Subject: [PATCH] r8169: disable interrupt source RxOverflow
-
-Vendor driver calls this bit RxDescUnavail. All we do in the interrupt
-handler in this case is scheduling NAPI. If we should be out of
-RX descriptors, then NAPI is scheduled anyway. Therefore remove this
-interrupt source. Tested on RTL8168h.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Sunil Goutham <sgoutham@marvell.com>
-Link: https://lore.kernel.org/r/9b2054b2-0548-4f48-bf91-b646572093b4@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169_main.c | 4 +---
- 1 file changed, 1 insertion(+), 3 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -5118,12 +5118,10 @@ static void rtl_set_irq_mask(struct rtl8
- tp->irq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg;
-
- if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
-- tp->irq_mask |= SYSErr | RxOverflow | RxFIFOOver;
-+ tp->irq_mask |= SYSErr | RxFIFOOver;
- else if (tp->mac_version == RTL_GIGA_MAC_VER_11)
- /* special workaround needed */
- tp->irq_mask |= RxFIFOOver;
-- else
-- tp->irq_mask |= RxOverflow;
- }
-
- static int rtl_alloc_irq(struct rtl8169_private *tp)
+++ /dev/null
-From 982300c115d229565d7af8e8b38aa1ee7bb1f5bd Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Mon, 27 May 2024 21:20:16 +0200
-Subject: [PATCH] r8169: remove detection of chip version 11 (early
- RTL8168b)
-
-This early RTL8168b version was the first PCIe chip version, and it's
-quite quirky. Last sign of life is from more than 15 yrs ago.
-Let's remove detection of this chip version, we'll see whether anybody
-complains. If not, support for this chip version can be removed a few
-kernel versions later.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://lore.kernel.org/r/875cdcf4-843c-420a-ad5d-417447b68572@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169_main.c | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -2302,7 +2302,9 @@ static enum mac_version rtl8169_get_mac_
-
- /* 8168B family. */
- { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17 },
-- { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11 },
-+ /* This one is very old and rare, let's see if anybody complains.
-+ * { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11 },
-+ */
-
- /* 8101 family. */
- { 0x7c8, 0x448, RTL_GIGA_MAC_VER_39 },
+++ /dev/null
-From 69cb89981c7a181d857b634c0740e914d5df79ea Mon Sep 17 00:00:00 2001
-From: ChunHao Lin <hau@realtek.com>
-Date: Fri, 30 Aug 2024 10:18:10 +0800
-Subject: [PATCH] r8169: add support for RTL8126A rev.b
-
-Add support for RTL8126A rev.b. Its XID is 0x64a. It is basically
-based on the one with XID 0x649, but with different firmware file.
-
-Signed-off-by: ChunHao Lin <hau@realtek.com>
-Reviewed-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/20240830021810.11993-1-hau@realtek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/realtek/r8169.h | 1 +
- drivers/net/ethernet/realtek/r8169_main.c | 42 ++++++++++++-------
- .../net/ethernet/realtek/r8169_phy_config.c | 1 +
- 3 files changed, 29 insertions(+), 15 deletions(-)
-
---- a/drivers/net/ethernet/realtek/r8169.h
-+++ b/drivers/net/ethernet/realtek/r8169.h
-@@ -69,6 +69,7 @@ enum mac_version {
- RTL_GIGA_MAC_VER_61,
- RTL_GIGA_MAC_VER_63,
- RTL_GIGA_MAC_VER_65,
-+ RTL_GIGA_MAC_VER_66,
- RTL_GIGA_MAC_NONE
- };
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -56,6 +56,7 @@
- #define FIRMWARE_8125A_3 "rtl_nic/rtl8125a-3.fw"
- #define FIRMWARE_8125B_2 "rtl_nic/rtl8125b-2.fw"
- #define FIRMWARE_8126A_2 "rtl_nic/rtl8126a-2.fw"
-+#define FIRMWARE_8126A_3 "rtl_nic/rtl8126a-3.fw"
-
- #define TX_DMA_BURST 7 /* Maximum PCI burst, '7' is unlimited */
- #define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */
-@@ -138,6 +139,7 @@ static const struct {
- /* reserve 62 for CFG_METHOD_4 in the vendor driver */
- [RTL_GIGA_MAC_VER_63] = {"RTL8125B", FIRMWARE_8125B_2},
- [RTL_GIGA_MAC_VER_65] = {"RTL8126A", FIRMWARE_8126A_2},
-+ [RTL_GIGA_MAC_VER_66] = {"RTL8126A", FIRMWARE_8126A_3},
- };
-
- static const struct pci_device_id rtl8169_pci_tbl[] = {
-@@ -1228,7 +1230,7 @@ static void rtl_writephy(struct rtl8169_
- case RTL_GIGA_MAC_VER_31:
- r8168dp_2_mdio_write(tp, location, val);
- break;
-- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66:
- r8168g_mdio_write(tp, location, val);
- break;
- default:
-@@ -1243,7 +1245,7 @@ static int rtl_readphy(struct rtl8169_pr
- case RTL_GIGA_MAC_VER_28:
- case RTL_GIGA_MAC_VER_31:
- return r8168dp_2_mdio_read(tp, location);
-- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66:
- return r8168g_mdio_read(tp, location);
- default:
- return r8169_mdio_read(tp, location);
-@@ -1452,7 +1454,7 @@ static void rtl_set_d3_pll_down(struct r
- case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26:
- case RTL_GIGA_MAC_VER_29 ... RTL_GIGA_MAC_VER_30:
- case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_37:
-- case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_66:
- if (enable)
- RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~D3_NO_PLL_DOWN);
- else
-@@ -1619,7 +1621,7 @@ static void __rtl8169_set_wol(struct rtl
- break;
- case RTL_GIGA_MAC_VER_34:
- case RTL_GIGA_MAC_VER_37:
-- case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_66:
- if (wolopts)
- rtl_mod_config2(tp, 0, PME_SIGNAL);
- else
-@@ -2098,6 +2100,7 @@ static void rtl_set_eee_txidle_timer(str
- case RTL_GIGA_MAC_VER_61:
- case RTL_GIGA_MAC_VER_63:
- case RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_66:
- tp->tx_lpi_timer = timer_val;
- RTL_W16(tp, EEE_TXIDLE_TIMER_8125, timer_val);
- break;
-@@ -2227,6 +2230,7 @@ static enum mac_version rtl8169_get_mac_
- enum mac_version ver;
- } mac_info[] = {
- /* 8126A family. */
-+ { 0x7cf, 0x64a, RTL_GIGA_MAC_VER_66 },
- { 0x7cf, 0x649, RTL_GIGA_MAC_VER_65 },
-
- /* 8125B family. */
-@@ -2498,6 +2502,7 @@ static void rtl_init_rxcfg(struct rtl816
- break;
- case RTL_GIGA_MAC_VER_63:
- case RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_66:
- RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST |
- RX_PAUSE_SLOT_ON);
- break;
-@@ -2684,7 +2689,7 @@ static void rtl_wait_txrx_fifo_empty(str
- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_61:
- rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42);
- break;
-- case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_66:
- RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
- rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42);
- rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond_2, 100, 42);
-@@ -2927,7 +2932,7 @@ static void rtl_enable_exit_l1(struct rt
- case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_38:
- rtl_eri_set_bits(tp, 0xd4, 0x0c00);
- break;
-- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66:
- r8168_mac_ocp_modify(tp, 0xc0ac, 0, 0x1f80);
- break;
- default:
-@@ -2941,7 +2946,7 @@ static void rtl_disable_exit_l1(struct r
- case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38:
- rtl_eri_clear_bits(tp, 0xd4, 0x1f00);
- break;
-- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66:
- r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0);
- break;
- default:
-@@ -2968,6 +2973,7 @@ static void rtl_hw_aspm_clkreq_enable(st
- rtl_mod_config5(tp, 0, ASPM_en);
- switch (tp->mac_version) {
- case RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_66:
- val8 = RTL_R8(tp, INT_CFG0_8125) | INT_CFG0_CLKREQEN;
- RTL_W8(tp, INT_CFG0_8125, val8);
- break;
-@@ -2978,7 +2984,7 @@ static void rtl_hw_aspm_clkreq_enable(st
-
- switch (tp->mac_version) {
- case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
-- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66:
- /* reset ephy tx/rx disable timer */
- r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0);
- /* chip can trigger L1.2 */
-@@ -2990,7 +2996,7 @@ static void rtl_hw_aspm_clkreq_enable(st
- } else {
- switch (tp->mac_version) {
- case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
-- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66:
- r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0);
- break;
- default:
-@@ -2999,6 +3005,7 @@ static void rtl_hw_aspm_clkreq_enable(st
-
- switch (tp->mac_version) {
- case RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_66:
- val8 = RTL_R8(tp, INT_CFG0_8125) & ~INT_CFG0_CLKREQEN;
- RTL_W8(tp, INT_CFG0_8125, val8);
- break;
-@@ -3718,10 +3725,12 @@ static void rtl_hw_start_8125_common(str
- /* disable new tx descriptor format */
- r8168_mac_ocp_modify(tp, 0xeb58, 0x0001, 0x0000);
-
-- if (tp->mac_version == RTL_GIGA_MAC_VER_65)
-+ if (tp->mac_version == RTL_GIGA_MAC_VER_65 ||
-+ tp->mac_version == RTL_GIGA_MAC_VER_66)
- RTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) & ~0x02);
-
-- if (tp->mac_version == RTL_GIGA_MAC_VER_65)
-+ if (tp->mac_version == RTL_GIGA_MAC_VER_65 ||
-+ tp->mac_version == RTL_GIGA_MAC_VER_66)
- r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400);
- else if (tp->mac_version == RTL_GIGA_MAC_VER_63)
- r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200);
-@@ -3739,7 +3748,8 @@ static void rtl_hw_start_8125_common(str
- r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0030);
- r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000);
- r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001);
-- if (tp->mac_version == RTL_GIGA_MAC_VER_65)
-+ if (tp->mac_version == RTL_GIGA_MAC_VER_65 ||
-+ tp->mac_version == RTL_GIGA_MAC_VER_66)
- r8168_mac_ocp_modify(tp, 0xea1c, 0x0300, 0x0000);
- else
- r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000);
-@@ -3853,6 +3863,7 @@ static void rtl_hw_config(struct rtl8169
- [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2,
- [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b,
- [RTL_GIGA_MAC_VER_65] = rtl_hw_start_8126a,
-+ [RTL_GIGA_MAC_VER_66] = rtl_hw_start_8126a,
- };
-
- if (hw_configs[tp->mac_version])
-@@ -3873,6 +3884,7 @@ static void rtl_hw_start_8125(struct rtl
- break;
- case RTL_GIGA_MAC_VER_63:
- case RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_66:
- for (i = 0xa00; i < 0xa80; i += 4)
- RTL_W32(tp, i, 0);
- RTL_W16(tp, INT_CFG1_8125, 0x0000);
-@@ -4101,7 +4113,7 @@ static void rtl8169_cleanup(struct rtl81
- RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
- rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666);
- break;
-- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66:
- rtl_enable_rxdvgate(tp);
- fsleep(2000);
- break;
-@@ -4258,7 +4270,7 @@ static unsigned int rtl_quirk_packet_pad
-
- switch (tp->mac_version) {
- case RTL_GIGA_MAC_VER_34:
-- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66:
- padto = max_t(unsigned int, padto, ETH_ZLEN);
- break;
- default:
-@@ -5294,7 +5306,7 @@ static void rtl_hw_initialize(struct rtl
- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48:
- rtl_hw_init_8168g(tp);
- break;
-- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
-+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66:
- rtl_hw_init_8125(tp);
- break;
- default:
---- a/drivers/net/ethernet/realtek/r8169_phy_config.c
-+++ b/drivers/net/ethernet/realtek/r8169_phy_config.c
-@@ -1161,6 +1161,7 @@ void r8169_hw_phy_config(struct rtl8169_
- [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config,
- [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config,
- [RTL_GIGA_MAC_VER_65] = rtl8126a_hw_phy_config,
-+ [RTL_GIGA_MAC_VER_66] = rtl8126a_hw_phy_config,
- };
-
- if (phy_configs[ver])
+++ /dev/null
-From 3b067536daa4842adbf685accf47c899a26367d3 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Wed, 18 Sep 2024 20:45:15 +0200
-Subject: [PATCH] r8169: add missing MODULE_FIRMWARE entry for RTL8126A rev.b
-
-Add a missing MODULE_FIRMWARE entry.
-
-Fixes: 69cb89981c7a ("r8169: add support for RTL8126A rev.b")
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/bb307611-d129-43f5-a7ff-bdb6b4044fce@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/ethernet/realtek/r8169_main.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/net/ethernet/realtek/r8169_main.c
-+++ b/drivers/net/ethernet/realtek/r8169_main.c
-@@ -708,6 +708,7 @@ MODULE_FIRMWARE(FIRMWARE_8107E_2);
- MODULE_FIRMWARE(FIRMWARE_8125A_3);
- MODULE_FIRMWARE(FIRMWARE_8125B_2);
- MODULE_FIRMWARE(FIRMWARE_8126A_2);
-+MODULE_FIRMWARE(FIRMWARE_8126A_3);
-
- static inline struct device *tp_to_dev(struct rtl8169_private *tp)
- {
+++ /dev/null
-From 5befa3728b855e9f75b29bb0069a1ca7f5bab2f7 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Wed, 31 Jan 2024 21:24:29 +0100
-Subject: [PATCH] net: phy: realtek: add support for RTL8126A-integrated 5Gbps
- PHY
-
-A user reported that first consumer mainboards show up with a RTL8126A
-5Gbps MAC/PHY. This adds support for the integrated PHY, which is also
-available stand-alone. From a PHY driver perspective it's treated the
-same as the 2.5Gbps PHY's, we just have to support the new PHY ID.
-
-Reported-by: Joe Salmeri <jmscdba@gmail.com>
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Tested-by: Joe Salmeri <jmscdba@gmail.com>
-Link: https://lore.kernel.org/r/0c8e67ea-6505-43d1-bd51-94e7ecd6e222@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/realtek.c | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -1050,6 +1050,16 @@ static struct phy_driver realtek_drvs[]
- .read_page = rtl821x_read_page,
- .write_page = rtl821x_write_page,
- }, {
-+ PHY_ID_MATCH_EXACT(0x001cc862),
-+ .name = "RTL8251B 5Gbps PHY",
-+ .get_features = rtl822x_get_features,
-+ .config_aneg = rtl822x_config_aneg,
-+ .read_status = rtl822x_read_status,
-+ .suspend = genphy_suspend,
-+ .resume = rtlgen_resume,
-+ .read_page = rtl821x_read_page,
-+ .write_page = rtl821x_write_page,
-+ }, {
- PHY_ID_MATCH_EXACT(0x001cc961),
- .name = "RTL8366RB Gigabit Ethernet",
- .config_init = &rtl8366rb_config_init,
+++ /dev/null
-From 2b9ec5dfb8255656ca731ab9d9bf59d94566d377 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Sun, 4 Feb 2024 15:17:53 +0100
-Subject: [PATCH] net: phy: realtek: use generic MDIO constants
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Drop the ad-hoc MDIO constants used in the driver and use generic
-constants instead.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/732a70d6-4191-4aae-8862-3716b062aa9e@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/realtek.c | 30 +++++++++++++-----------------
- 1 file changed, 13 insertions(+), 17 deletions(-)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -57,14 +57,6 @@
- #define RTL8366RB_POWER_SAVE 0x15
- #define RTL8366RB_POWER_SAVE_ON BIT(12)
-
--#define RTL_SUPPORTS_5000FULL BIT(14)
--#define RTL_SUPPORTS_2500FULL BIT(13)
--#define RTL_SUPPORTS_10000FULL BIT(0)
--#define RTL_ADV_2500FULL BIT(7)
--#define RTL_LPADV_10000FULL BIT(11)
--#define RTL_LPADV_5000FULL BIT(6)
--#define RTL_LPADV_2500FULL BIT(5)
--
- #define RTL9000A_GINMR 0x14
- #define RTL9000A_GINMR_LINK_STATUS BIT(4)
-
-@@ -676,11 +668,11 @@ static int rtl822x_get_features(struct p
- return val;
-
- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
-- phydev->supported, val & RTL_SUPPORTS_2500FULL);
-+ phydev->supported, val & MDIO_PMA_SPEED_2_5G);
- linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
-- phydev->supported, val & RTL_SUPPORTS_5000FULL);
-+ phydev->supported, val & MDIO_PMA_SPEED_5G);
- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
-- phydev->supported, val & RTL_SUPPORTS_10000FULL);
-+ phydev->supported, val & MDIO_SPEED_10G);
-
- return genphy_read_abilities(phydev);
- }
-@@ -694,10 +686,11 @@ static int rtl822x_config_aneg(struct ph
-
- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
- phydev->advertising))
-- adv2500 = RTL_ADV_2500FULL;
-+ adv2500 = MDIO_AN_10GBT_CTRL_ADV2_5G;
-
- ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12,
-- RTL_ADV_2500FULL, adv2500);
-+ MDIO_AN_10GBT_CTRL_ADV2_5G,
-+ adv2500);
- if (ret < 0)
- return ret;
- }
-@@ -716,11 +709,14 @@ static int rtl822x_read_status(struct ph
- return lpadv;
-
- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
-- phydev->lp_advertising, lpadv & RTL_LPADV_10000FULL);
-+ phydev->lp_advertising,
-+ lpadv & MDIO_AN_10GBT_STAT_LP10G);
- linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
-- phydev->lp_advertising, lpadv & RTL_LPADV_5000FULL);
-+ phydev->lp_advertising,
-+ lpadv & MDIO_AN_10GBT_STAT_LP5G);
- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
-- phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL);
-+ phydev->lp_advertising,
-+ lpadv & MDIO_AN_10GBT_STAT_LP2_5G);
- }
-
- ret = genphy_read_status(phydev);
-@@ -738,7 +734,7 @@ static bool rtlgen_supports_2_5gbps(stru
- val = phy_read(phydev, 0x13);
- phy_write(phydev, RTL821x_PAGE_SELECT, 0);
-
-- return val >= 0 && val & RTL_SUPPORTS_2500FULL;
-+ return val >= 0 && val & MDIO_PMA_SPEED_2_5G;
- }
-
- static int rtlgen_match_phy_device(struct phy_device *phydev)
+++ /dev/null
-From db1bb7741ff29bf2cefcbc0ca567644e9ed1caa9 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Sun, 4 Feb 2024 15:18:50 +0100
-Subject: [PATCH] net: phy: realtek: add 5Gbps support to rtl822x_config_aneg()
-
-RTL8126 as an evolution of RTL8125 supports 5Gbps. rtl822x_config_aneg()
-is used by the PHY driver for the integrated PHY, therefore add 5Gbps
-support to it.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://lore.kernel.org/r/5644ab50-e3e9-477c-96db-05cd5bdc2563@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/realtek.c | 12 ++++++++----
- 1 file changed, 8 insertions(+), 4 deletions(-)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -682,15 +682,19 @@ static int rtl822x_config_aneg(struct ph
- int ret = 0;
-
- if (phydev->autoneg == AUTONEG_ENABLE) {
-- u16 adv2500 = 0;
-+ u16 adv = 0;
-
- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
- phydev->advertising))
-- adv2500 = MDIO_AN_10GBT_CTRL_ADV2_5G;
-+ adv |= MDIO_AN_10GBT_CTRL_ADV2_5G;
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
-+ phydev->advertising))
-+ adv |= MDIO_AN_10GBT_CTRL_ADV5G;
-
- ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12,
-- MDIO_AN_10GBT_CTRL_ADV2_5G,
-- adv2500);
-+ MDIO_AN_10GBT_CTRL_ADV2_5G |
-+ MDIO_AN_10GBT_CTRL_ADV5G,
-+ adv);
- if (ret < 0)
- return ret;
- }
+++ /dev/null
-From b63cc73341e076961d564a74cc3d29b2fd444079 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Thu, 8 Feb 2024 07:59:18 +0100
-Subject: [PATCH] net: phy: realtek: use generic MDIO helpers to simplify the
- code
-
-Use generic MDIO helpers to simplify the code.
-
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/422ae70f-7305-45fd-ab3e-0dd604b9fd6c@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/realtek.c | 20 +++-----------------
- 1 file changed, 3 insertions(+), 17 deletions(-)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -682,14 +682,7 @@ static int rtl822x_config_aneg(struct ph
- int ret = 0;
-
- if (phydev->autoneg == AUTONEG_ENABLE) {
-- u16 adv = 0;
--
-- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
-- phydev->advertising))
-- adv |= MDIO_AN_10GBT_CTRL_ADV2_5G;
-- if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
-- phydev->advertising))
-- adv |= MDIO_AN_10GBT_CTRL_ADV5G;
-+ u16 adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising);
-
- ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12,
- MDIO_AN_10GBT_CTRL_ADV2_5G |
-@@ -712,15 +705,8 @@ static int rtl822x_read_status(struct ph
- if (lpadv < 0)
- return lpadv;
-
-- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
-- phydev->lp_advertising,
-- lpadv & MDIO_AN_10GBT_STAT_LP10G);
-- linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
-- phydev->lp_advertising,
-- lpadv & MDIO_AN_10GBT_STAT_LP5G);
-- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
-- phydev->lp_advertising,
-- lpadv & MDIO_AN_10GBT_STAT_LP2_5G);
-+ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising,
-+ lpadv);
- }
-
- ret = genphy_read_status(phydev);
+++ /dev/null
-From deb8af5243504e379878ae3f9a091b21422d65b2 Mon Sep 17 00:00:00 2001
-From: Alexander Couzens <lynxis@fe80.eu>
-Date: Tue, 9 Apr 2024 09:30:11 +0200
-Subject: [PATCH] net: phy: realtek: configure SerDes mode for rtl822xb PHYs
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The rtl8221b and rtl8226b series support switching SerDes mode between
-2500base-x and sgmii based on the negotiated copper speed.
-
-Configure this switching mode according to SerDes modes supported by
-host.
-
-There is an additional datasheet for RTL8226B/RTL8221B called
-"SERDES MODE SETTING FLOW APPLICATION NOTE" where a sequence is
-described to setup interface and rate adapter mode.
-
-However, there is no documentation about the meaning of registers
-and bits, it's literally just magic numbers and pseudo-code.
-
-Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
-[ refactored, dropped HiSGMII mode and changed commit message ]
-Signed-off-by: Marek Behún <kabel@kernel.org>
-[ changed rtl822x_update_interface() to use vendor register ]
-[ always fill in possible interfaces ]
-[ only apply to rtl8221b and rtl8226b phy's ]
-[ set phydev->rate_matching in .config_init() ]
-Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
-
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: should come before them, without any blank lines. As the
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/realtek.c | 114 ++++++++++++++++++++++++++++++++++++--
- 1 file changed, 110 insertions(+), 4 deletions(-)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -54,6 +54,16 @@
- RTL8201F_ISR_LINK)
- #define RTL8201F_IER 0x13
-
-+#define RTL822X_VND1_SERDES_OPTION 0x697a
-+#define RTL822X_VND1_SERDES_OPTION_MODE_MASK GENMASK(5, 0)
-+#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII 0
-+#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX 2
-+
-+#define RTL822X_VND1_SERDES_CTRL3 0x7580
-+#define RTL822X_VND1_SERDES_CTRL3_MODE_MASK GENMASK(5, 0)
-+#define RTL822X_VND1_SERDES_CTRL3_MODE_SGMII 0x02
-+#define RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX 0x16
-+
- #define RTL8366RB_POWER_SAVE 0x15
- #define RTL8366RB_POWER_SAVE_ON BIT(12)
-
-@@ -659,6 +669,63 @@ static int rtl822x_write_mmd(struct phy_
- return ret;
- }
-
-+static int rtl822xb_config_init(struct phy_device *phydev)
-+{
-+ bool has_2500, has_sgmii;
-+ u16 mode;
-+ int ret;
-+
-+ has_2500 = test_bit(PHY_INTERFACE_MODE_2500BASEX,
-+ phydev->host_interfaces) ||
-+ phydev->interface == PHY_INTERFACE_MODE_2500BASEX;
-+
-+ has_sgmii = test_bit(PHY_INTERFACE_MODE_SGMII,
-+ phydev->host_interfaces) ||
-+ phydev->interface == PHY_INTERFACE_MODE_SGMII;
-+
-+ /* fill in possible interfaces */
-+ __assign_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->possible_interfaces,
-+ has_2500);
-+ __assign_bit(PHY_INTERFACE_MODE_SGMII, phydev->possible_interfaces,
-+ has_sgmii);
-+
-+ if (!has_2500 && !has_sgmii)
-+ return 0;
-+
-+ /* determine SerDes option mode */
-+ if (has_2500 && !has_sgmii) {
-+ mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX;
-+ phydev->rate_matching = RATE_MATCH_PAUSE;
-+ } else {
-+ mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII;
-+ phydev->rate_matching = RATE_MATCH_NONE;
-+ }
-+
-+ /* the following sequence with magic numbers sets up the SerDes
-+ * option mode
-+ */
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x75f3, 0);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1,
-+ RTL822X_VND1_SERDES_OPTION,
-+ RTL822X_VND1_SERDES_OPTION_MODE_MASK,
-+ mode);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6a04, 0x0503);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f10, 0xd455);
-+ if (ret < 0)
-+ return ret;
-+
-+ return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
-+}
-+
- static int rtl822x_get_features(struct phy_device *phydev)
- {
- int val;
-@@ -695,6 +762,28 @@ static int rtl822x_config_aneg(struct ph
- return __genphy_config_aneg(phydev, ret);
- }
-
-+static void rtl822xb_update_interface(struct phy_device *phydev)
-+{
-+ int val;
-+
-+ if (!phydev->link)
-+ return;
-+
-+ /* Change interface according to serdes mode */
-+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_CTRL3);
-+ if (val < 0)
-+ return;
-+
-+ switch (val & RTL822X_VND1_SERDES_CTRL3_MODE_MASK) {
-+ case RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX:
-+ phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
-+ break;
-+ case RTL822X_VND1_SERDES_CTRL3_MODE_SGMII:
-+ phydev->interface = PHY_INTERFACE_MODE_SGMII;
-+ break;
-+ }
-+}
-+
- static int rtl822x_read_status(struct phy_device *phydev)
- {
- int ret;
-@@ -716,6 +805,19 @@ static int rtl822x_read_status(struct ph
- return rtlgen_get_speed(phydev);
- }
-
-+static int rtl822xb_read_status(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = rtl822x_read_status(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ rtl822xb_update_interface(phydev);
-+
-+ return 0;
-+}
-+
- static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
- {
- int val;
-@@ -988,7 +1090,8 @@ static struct phy_driver realtek_drvs[]
- .name = "RTL8226B_RTL8221B 2.5Gbps PHY",
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
-- .read_status = rtl822x_read_status,
-+ .config_init = rtl822xb_config_init,
-+ .read_status = rtl822xb_read_status,
- .suspend = genphy_suspend,
- .resume = rtlgen_resume,
- .read_page = rtl821x_read_page,
-@@ -1010,7 +1113,8 @@ static struct phy_driver realtek_drvs[]
- .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
-- .read_status = rtl822x_read_status,
-+ .config_init = rtl822xb_config_init,
-+ .read_status = rtl822xb_read_status,
- .suspend = genphy_suspend,
- .resume = rtlgen_resume,
- .read_page = rtl821x_read_page,
-@@ -1020,7 +1124,8 @@ static struct phy_driver realtek_drvs[]
- .name = "RTL8221B-VB-CG 2.5Gbps PHY",
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
-- .read_status = rtl822x_read_status,
-+ .config_init = rtl822xb_config_init,
-+ .read_status = rtl822xb_read_status,
- .suspend = genphy_suspend,
- .resume = rtlgen_resume,
- .read_page = rtl821x_read_page,
-@@ -1030,7 +1135,8 @@ static struct phy_driver realtek_drvs[]
- .name = "RTL8221B-VM-CG 2.5Gbps PHY",
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
-- .read_status = rtl822x_read_status,
-+ .config_init = rtl822xb_config_init,
-+ .read_status = rtl822xb_read_status,
- .suspend = genphy_suspend,
- .resume = rtlgen_resume,
- .read_page = rtl821x_read_page,
+++ /dev/null
-From c189dbd738243be6775bb6878366bf63e27bfd05 Mon Sep 17 00:00:00 2001
-From: Eric Woudstra <ericwouds@gmail.com>
-Date: Tue, 9 Apr 2024 09:30:12 +0200
-Subject: [PATCH] net: phy: realtek: add get_rate_matching() for rtl822xb PHYs
-
-Uses vendor register to determine if SerDes is setup in rate-matching mode.
-
-Rate-matching only supported when SerDes is set to 2500base-x.
-
-Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/realtek.c | 25 +++++++++++++++++++++++++
- 1 file changed, 25 insertions(+)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -726,6 +726,27 @@ static int rtl822xb_config_init(struct p
- return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
- }
-
-+static int rtl822xb_get_rate_matching(struct phy_device *phydev,
-+ phy_interface_t iface)
-+{
-+ int val;
-+
-+ /* Only rate matching at 2500base-x */
-+ if (iface != PHY_INTERFACE_MODE_2500BASEX)
-+ return RATE_MATCH_NONE;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_OPTION);
-+ if (val < 0)
-+ return val;
-+
-+ if ((val & RTL822X_VND1_SERDES_OPTION_MODE_MASK) ==
-+ RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX)
-+ return RATE_MATCH_PAUSE;
-+
-+ /* RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII */
-+ return RATE_MATCH_NONE;
-+}
-+
- static int rtl822x_get_features(struct phy_device *phydev)
- {
- int val;
-@@ -1091,6 +1112,7 @@ static struct phy_driver realtek_drvs[]
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
- .config_init = rtl822xb_config_init,
-+ .get_rate_matching = rtl822xb_get_rate_matching,
- .read_status = rtl822xb_read_status,
- .suspend = genphy_suspend,
- .resume = rtlgen_resume,
-@@ -1114,6 +1136,7 @@ static struct phy_driver realtek_drvs[]
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
- .config_init = rtl822xb_config_init,
-+ .get_rate_matching = rtl822xb_get_rate_matching,
- .read_status = rtl822xb_read_status,
- .suspend = genphy_suspend,
- .resume = rtlgen_resume,
-@@ -1125,6 +1148,7 @@ static struct phy_driver realtek_drvs[]
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
- .config_init = rtl822xb_config_init,
-+ .get_rate_matching = rtl822xb_get_rate_matching,
- .read_status = rtl822xb_read_status,
- .suspend = genphy_suspend,
- .resume = rtlgen_resume,
-@@ -1136,6 +1160,7 @@ static struct phy_driver realtek_drvs[]
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
- .config_init = rtl822xb_config_init,
-+ .get_rate_matching = rtl822xb_get_rate_matching,
- .read_status = rtl822xb_read_status,
- .suspend = genphy_suspend,
- .resume = rtlgen_resume,
+++ /dev/null
-From ad5ce743a6b0329f642d80be50ef7b534e908fba Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Tue, 9 Apr 2024 09:30:13 +0200
-Subject: [PATCH] net: phy: realtek: Add driver instances for rtl8221b via
- Clause 45
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Collected from several commits in [PATCH net-next]
-"Realtek RTL822x PHY rework to c45 and SerDes interface switching"
-
-The instances are used by Clause 45 only accessible PHY's on several sfp
-modules, which are using RollBall protocol.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-[ Added matching functions to differentiate C45 instances ]
-Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
-
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/realtek.c | 135 ++++++++++++++++++++++++++++++++++++--
- 1 file changed, 131 insertions(+), 4 deletions(-)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -64,6 +64,13 @@
- #define RTL822X_VND1_SERDES_CTRL3_MODE_SGMII 0x02
- #define RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX 0x16
-
-+/* RTL822X_VND2_XXXXX registers are only accessible when phydev->is_c45
-+ * is set, they cannot be accessed by C45-over-C22.
-+ */
-+#define RTL822X_VND2_GBCR 0xa412
-+
-+#define RTL822X_VND2_GANLPAR 0xa414
-+
- #define RTL8366RB_POWER_SAVE 0x15
- #define RTL8366RB_POWER_SAVE_ON BIT(12)
-
-@@ -74,6 +81,9 @@
-
- #define RTL_GENERIC_PHYID 0x001cc800
- #define RTL_8211FVD_PHYID 0x001cc878
-+#define RTL_8221B_VB_CG 0x001cc849
-+#define RTL_8221B_VN_CG 0x001cc84a
-+#define RTL_8251B 0x001cc862
-
- MODULE_DESCRIPTION("Realtek PHY driver");
- MODULE_AUTHOR("Johnson Leung");
-@@ -839,6 +849,67 @@ static int rtl822xb_read_status(struct p
- return 0;
- }
-
-+static int rtl822x_c45_config_aneg(struct phy_device *phydev)
-+{
-+ bool changed = false;
-+ int ret, val;
-+
-+ if (phydev->autoneg == AUTONEG_DISABLE)
-+ return genphy_c45_pma_setup_forced(phydev);
-+
-+ ret = genphy_c45_an_config_aneg(phydev);
-+ if (ret < 0)
-+ return ret;
-+ if (ret > 0)
-+ changed = true;
-+
-+ val = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
-+
-+ /* Vendor register as C45 has no standardized support for 1000BaseT */
-+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL822X_VND2_GBCR,
-+ ADVERTISE_1000FULL, val);
-+ if (ret < 0)
-+ return ret;
-+ if (ret > 0)
-+ changed = true;
-+
-+ return genphy_c45_check_and_restart_aneg(phydev, changed);
-+}
-+
-+static int rtl822x_c45_read_status(struct phy_device *phydev)
-+{
-+ int ret, val;
-+
-+ ret = genphy_c45_read_status(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Vendor register as C45 has no standardized support for 1000BaseT */
-+ if (phydev->autoneg == AUTONEG_ENABLE) {
-+ val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
-+ RTL822X_VND2_GANLPAR);
-+ if (val < 0)
-+ return val;
-+
-+ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
-+ }
-+
-+ return 0;
-+}
-+
-+static int rtl822xb_c45_read_status(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = rtl822x_c45_read_status(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ rtl822xb_update_interface(phydev);
-+
-+ return 0;
-+}
-+
- static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
- {
- int val;
-@@ -862,6 +933,35 @@ static int rtl8226_match_phy_device(stru
- rtlgen_supports_2_5gbps(phydev);
- }
-
-+static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
-+ bool is_c45)
-+{
-+ if (phydev->is_c45)
-+ return is_c45 && (id == phydev->c45_ids.device_ids[1]);
-+ else
-+ return !is_c45 && (id == phydev->phy_id);
-+}
-+
-+static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev)
-+{
-+ return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false);
-+}
-+
-+static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev)
-+{
-+ return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true);
-+}
-+
-+static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev)
-+{
-+ return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false);
-+}
-+
-+static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev)
-+{
-+ return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
-+}
-+
- static int rtlgen_resume(struct phy_device *phydev)
- {
- int ret = genphy_resume(phydev);
-@@ -872,6 +972,15 @@ static int rtlgen_resume(struct phy_devi
- return ret;
- }
-
-+static int rtlgen_c45_resume(struct phy_device *phydev)
-+{
-+ int ret = genphy_c45_pma_resume(phydev);
-+
-+ msleep(20);
-+
-+ return ret;
-+}
-+
- static int rtl9000a_config_init(struct phy_device *phydev)
- {
- phydev->autoneg = AUTONEG_DISABLE;
-@@ -1143,8 +1252,8 @@ static struct phy_driver realtek_drvs[]
- .read_page = rtl821x_read_page,
- .write_page = rtl821x_write_page,
- }, {
-- PHY_ID_MATCH_EXACT(0x001cc849),
-- .name = "RTL8221B-VB-CG 2.5Gbps PHY",
-+ .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
-+ .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
- .config_init = rtl822xb_config_init,
-@@ -1155,8 +1264,17 @@ static struct phy_driver realtek_drvs[]
- .read_page = rtl821x_read_page,
- .write_page = rtl821x_write_page,
- }, {
-- PHY_ID_MATCH_EXACT(0x001cc84a),
-- .name = "RTL8221B-VM-CG 2.5Gbps PHY",
-+ .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
-+ .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
-+ .config_init = rtl822xb_config_init,
-+ .get_rate_matching = rtl822xb_get_rate_matching,
-+ .config_aneg = rtl822x_c45_config_aneg,
-+ .read_status = rtl822xb_c45_read_status,
-+ .suspend = genphy_c45_pma_suspend,
-+ .resume = rtlgen_c45_resume,
-+ }, {
-+ .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
-+ .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
- .config_init = rtl822xb_config_init,
-@@ -1167,6 +1285,15 @@ static struct phy_driver realtek_drvs[]
- .read_page = rtl821x_read_page,
- .write_page = rtl821x_write_page,
- }, {
-+ .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
-+ .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
-+ .config_init = rtl822xb_config_init,
-+ .get_rate_matching = rtl822xb_get_rate_matching,
-+ .config_aneg = rtl822x_c45_config_aneg,
-+ .read_status = rtl822xb_c45_read_status,
-+ .suspend = genphy_c45_pma_suspend,
-+ .resume = rtlgen_c45_resume,
-+ }, {
- PHY_ID_MATCH_EXACT(0x001cc862),
- .name = "RTL8251B 5Gbps PHY",
- .get_features = rtl822x_get_features,
+++ /dev/null
-From 2e4ea707c7e04eb83e58c43e0e744bbdf6b23ff2 Mon Sep 17 00:00:00 2001
-From: Eric Woudstra <ericwouds@gmail.com>
-Date: Tue, 9 Apr 2024 09:30:14 +0200
-Subject: [PATCH] net: phy: realtek: Change rtlgen_get_speed() to
- rtlgen_decode_speed()
-
-The value of the register to determine the speed, is retrieved
-differently when using Clause 45 only. To use the rtlgen_get_speed()
-function in this case, pass the value of the register as argument to
-rtlgen_get_speed(). The function would then always return 0, so change it
-to void. A better name for this function now is rtlgen_decode_speed().
-
-Replace a call to genphy_read_status() followed by rtlgen_get_speed()
-with a call to rtlgen_read_status() in rtl822x_read_status().
-
-Add reading speed to rtl822x_c45_read_status().
-
-Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
-
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/realtek.c | 46 +++++++++++++++++++++------------------
- 1 file changed, 25 insertions(+), 21 deletions(-)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -71,6 +71,8 @@
-
- #define RTL822X_VND2_GANLPAR 0xa414
-
-+#define RTL822X_VND2_PHYSR 0xa434
-+
- #define RTL8366RB_POWER_SAVE 0x15
- #define RTL8366RB_POWER_SAVE_ON BIT(12)
-
-@@ -551,17 +553,8 @@ static int rtl8366rb_config_init(struct
- }
-
- /* get actual speed to cover the downshift case */
--static int rtlgen_get_speed(struct phy_device *phydev)
-+static void rtlgen_decode_speed(struct phy_device *phydev, int val)
- {
-- int val;
--
-- if (!phydev->link)
-- return 0;
--
-- val = phy_read_paged(phydev, 0xa43, 0x12);
-- if (val < 0)
-- return val;
--
- switch (val & RTLGEN_SPEED_MASK) {
- case 0x0000:
- phydev->speed = SPEED_10;
-@@ -584,19 +577,26 @@ static int rtlgen_get_speed(struct phy_d
- default:
- break;
- }
--
-- return 0;
- }
-
- static int rtlgen_read_status(struct phy_device *phydev)
- {
-- int ret;
-+ int ret, val;
-
- ret = genphy_read_status(phydev);
- if (ret < 0)
- return ret;
-
-- return rtlgen_get_speed(phydev);
-+ if (!phydev->link)
-+ return 0;
-+
-+ val = phy_read_paged(phydev, 0xa43, 0x12);
-+ if (val < 0)
-+ return val;
-+
-+ rtlgen_decode_speed(phydev, val);
-+
-+ return 0;
- }
-
- static int rtlgen_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
-@@ -817,8 +817,6 @@ static void rtl822xb_update_interface(st
-
- static int rtl822x_read_status(struct phy_device *phydev)
- {
-- int ret;
--
- if (phydev->autoneg == AUTONEG_ENABLE) {
- int lpadv = phy_read_paged(phydev, 0xa5d, 0x13);
-
-@@ -829,11 +827,7 @@ static int rtl822x_read_status(struct ph
- lpadv);
- }
-
-- ret = genphy_read_status(phydev);
-- if (ret < 0)
-- return ret;
--
-- return rtlgen_get_speed(phydev);
-+ return rtlgen_read_status(phydev);
- }
-
- static int rtl822xb_read_status(struct phy_device *phydev)
-@@ -894,6 +888,16 @@ static int rtl822x_c45_read_status(struc
- mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
- }
-
-+ if (!phydev->link)
-+ return 0;
-+
-+ /* Read actual speed from vendor register. */
-+ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_PHYSR);
-+ if (val < 0)
-+ return val;
-+
-+ rtlgen_decode_speed(phydev, val);
-+
- return 0;
- }
-
+++ /dev/null
-From 2d9ce64862705b33397d54dafecc5f51d8b1bb06 Mon Sep 17 00:00:00 2001
-From: Eric Woudstra <ericwouds@gmail.com>
-Date: Tue, 9 Apr 2024 09:30:15 +0200
-Subject: [PATCH] net: phy: realtek: add rtl822x_c45_get_features() to set
- supported port
-
-Sets ETHTOOL_LINK_MODE_TP_BIT in phydev->supported.
-
-Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/realtek.c | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -843,6 +843,14 @@ static int rtl822xb_read_status(struct p
- return 0;
- }
-
-+static int rtl822x_c45_get_features(struct phy_device *phydev)
-+{
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT,
-+ phydev->supported);
-+
-+ return genphy_c45_pma_read_abilities(phydev);
-+}
-+
- static int rtl822x_c45_config_aneg(struct phy_device *phydev)
- {
- bool changed = false;
-@@ -1272,6 +1280,7 @@ static struct phy_driver realtek_drvs[]
- .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
- .config_init = rtl822xb_config_init,
- .get_rate_matching = rtl822xb_get_rate_matching,
-+ .get_features = rtl822x_c45_get_features,
- .config_aneg = rtl822x_c45_config_aneg,
- .read_status = rtl822xb_c45_read_status,
- .suspend = genphy_c45_pma_suspend,
-@@ -1293,6 +1302,7 @@ static struct phy_driver realtek_drvs[]
- .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
- .config_init = rtl822xb_config_init,
- .get_rate_matching = rtl822xb_get_rate_matching,
-+ .get_features = rtl822x_c45_get_features,
- .config_aneg = rtl822x_c45_config_aneg,
- .read_status = rtl822xb_c45_read_status,
- .suspend = genphy_c45_pma_suspend,
+++ /dev/null
-From 9e42a2ea7f6703e2092c39171c2bf1fd7eec0bd3 Mon Sep 17 00:00:00 2001
-From: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Date: Tue, 11 Jun 2024 17:34:14 +1200
-Subject: [PATCH] net: phy: realtek: add support for rtl8224 2.5Gbps PHY
-
-The Realtek RTL8224 PHY is a 2.5Gbps capable PHY. It only uses the
-clause 45 MDIO interface and can leverage the support that has already
-been added for the other 822x PHYs.
-
-Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Link: https://lore.kernel.org/r/20240611053415.2111723-1-chris.packham@alliedtelesis.co.nz
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/realtek.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -1318,6 +1318,14 @@ static struct phy_driver realtek_drvs[]
- .read_page = rtl821x_read_page,
- .write_page = rtl821x_write_page,
- }, {
-+ PHY_ID_MATCH_EXACT(0x001ccad0),
-+ .name = "RTL8224 2.5Gbps PHY",
-+ .get_features = rtl822x_c45_get_features,
-+ .config_aneg = rtl822x_c45_config_aneg,
-+ .read_status = rtl822x_c45_read_status,
-+ .suspend = genphy_c45_pma_suspend,
-+ .resume = rtlgen_c45_resume,
-+ }, {
- PHY_ID_MATCH_EXACT(0x001cc961),
- .name = "RTL8366RB Gigabit Ethernet",
- .config_init = &rtl8366rb_config_init,
+++ /dev/null
-From 17784801d888238571a0c4101b9ac4401fffeaa0 Mon Sep 17 00:00:00 2001
-From: Marek Vasut <marex@denx.de>
-Date: Tue, 25 Jun 2024 22:42:17 +0200
-Subject: [PATCH] net: phy: realtek: Add support for PHY LEDs on RTL8211F
-
-Realtek RTL8211F Ethernet PHY supports 3 LED pins which are used to
-indicate link status and activity. Add minimal LED controller driver
-supporting the most common uses with the 'netdev' trigger.
-
-Signed-off-by: Marek Vasut <marex@denx.de>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/realtek.c | 106 ++++++++++++++++++++++++++++++++++++++
- 1 file changed, 106 insertions(+)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -32,6 +32,15 @@
- #define RTL8211F_PHYCR2 0x19
- #define RTL8211F_INSR 0x1d
-
-+#define RTL8211F_LEDCR 0x10
-+#define RTL8211F_LEDCR_MODE BIT(15)
-+#define RTL8211F_LEDCR_ACT_TXRX BIT(4)
-+#define RTL8211F_LEDCR_LINK_1000 BIT(3)
-+#define RTL8211F_LEDCR_LINK_100 BIT(1)
-+#define RTL8211F_LEDCR_LINK_10 BIT(0)
-+#define RTL8211F_LEDCR_MASK GENMASK(4, 0)
-+#define RTL8211F_LEDCR_SHIFT 5
-+
- #define RTL8211F_TX_DELAY BIT(8)
- #define RTL8211F_RX_DELAY BIT(3)
-
-@@ -87,6 +96,8 @@
- #define RTL_8221B_VN_CG 0x001cc84a
- #define RTL_8251B 0x001cc862
-
-+#define RTL8211F_LED_COUNT 3
-+
- MODULE_DESCRIPTION("Realtek PHY driver");
- MODULE_AUTHOR("Johnson Leung");
- MODULE_LICENSE("GPL");
-@@ -476,6 +487,98 @@ static int rtl821x_resume(struct phy_dev
- return 0;
- }
-
-+static int rtl8211f_led_hw_is_supported(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ const unsigned long mask = BIT(TRIGGER_NETDEV_LINK_10) |
-+ BIT(TRIGGER_NETDEV_LINK_100) |
-+ BIT(TRIGGER_NETDEV_LINK_1000) |
-+ BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX);
-+
-+ /* The RTL8211F PHY supports these LED settings on up to three LEDs:
-+ * - Link: Configurable subset of 10/100/1000 link rates
-+ * - Active: Blink on activity, RX or TX is not differentiated
-+ * The Active option has two modes, A and B:
-+ * - A: Link and Active indication at configurable, but matching,
-+ * subset of 10/100/1000 link rates
-+ * - B: Link indication at configurable subset of 10/100/1000 link
-+ * rates and Active indication always at all three 10+100+1000
-+ * link rates.
-+ * This code currently uses mode B only.
-+ */
-+
-+ if (index >= RTL8211F_LED_COUNT)
-+ return -EINVAL;
-+
-+ /* Filter out any other unsupported triggers. */
-+ if (rules & ~mask)
-+ return -EOPNOTSUPP;
-+
-+ /* RX and TX are not differentiated, either both are set or not set. */
-+ if (!(rules & BIT(TRIGGER_NETDEV_RX)) ^ !(rules & BIT(TRIGGER_NETDEV_TX)))
-+ return -EOPNOTSUPP;
-+
-+ return 0;
-+}
-+
-+static int rtl8211f_led_hw_control_get(struct phy_device *phydev, u8 index,
-+ unsigned long *rules)
-+{
-+ int val;
-+
-+ val = phy_read_paged(phydev, 0xd04, RTL8211F_LEDCR);
-+ if (val < 0)
-+ return val;
-+
-+ val >>= RTL8211F_LEDCR_SHIFT * index;
-+ val &= RTL8211F_LEDCR_MASK;
-+
-+ if (val & RTL8211F_LEDCR_LINK_10)
-+ set_bit(TRIGGER_NETDEV_LINK_10, rules);
-+
-+ if (val & RTL8211F_LEDCR_LINK_100)
-+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
-+
-+ if (val & RTL8211F_LEDCR_LINK_1000)
-+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
-+
-+ if (val & RTL8211F_LEDCR_ACT_TXRX) {
-+ set_bit(TRIGGER_NETDEV_RX, rules);
-+ set_bit(TRIGGER_NETDEV_TX, rules);
-+ }
-+
-+ return 0;
-+}
-+
-+static int rtl8211f_led_hw_control_set(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ const u16 mask = RTL8211F_LEDCR_MASK << (RTL8211F_LEDCR_SHIFT * index);
-+ u16 reg = RTL8211F_LEDCR_MODE; /* Mode B */
-+
-+ if (index >= RTL8211F_LED_COUNT)
-+ return -EINVAL;
-+
-+ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
-+ reg |= RTL8211F_LEDCR_LINK_10;
-+
-+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
-+ reg |= RTL8211F_LEDCR_LINK_100;
-+
-+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
-+ reg |= RTL8211F_LEDCR_LINK_1000;
-+
-+ if (test_bit(TRIGGER_NETDEV_RX, &rules) ||
-+ test_bit(TRIGGER_NETDEV_TX, &rules)) {
-+ reg |= RTL8211F_LEDCR_ACT_TXRX;
-+ }
-+
-+ reg <<= RTL8211F_LEDCR_SHIFT * index;
-+
-+ return phy_modify_paged(phydev, 0xd04, RTL8211F_LEDCR, mask, reg);
-+}
-+
- static int rtl8211e_config_init(struct phy_device *phydev)
- {
- int ret = 0, oldpage;
-@@ -1192,6 +1295,9 @@ static struct phy_driver realtek_drvs[]
- .read_page = rtl821x_read_page,
- .write_page = rtl821x_write_page,
- .flags = PHY_ALWAYS_CALL_SUSPEND,
-+ .led_hw_is_supported = rtl8211f_led_hw_is_supported,
-+ .led_hw_control_get = rtl8211f_led_hw_control_get,
-+ .led_hw_control_set = rtl8211f_led_hw_control_set,
- }, {
- PHY_ID_MATCH_EXACT(RTL_8211FVD_PHYID),
- .name = "RTL8211F-VD Gigabit Ethernet",
+++ /dev/null
-From a2f5c505b4378cd6fc7c4a44ff3665ccef2037db Mon Sep 17 00:00:00 2001
-From: Sava Jakovljev <savaj@meyersound.com>
-Date: Wed, 21 Aug 2024 04:16:57 +0200
-Subject: [PATCH] net: phy: realtek: Fix setting of PHY LEDs Mode B bit on
- RTL8211F
-
-The current implementation incorrectly sets the mode bit of the PHY chip.
-Bit 15 (RTL8211F_LEDCR_MODE) should not be shifted together with the
-configuration nibble of a LED- it should be set independently of the
-index of the LED being configured.
-As a consequence, the RTL8211F LED control is actually operating in Mode A.
-Fix the error by or-ing final register value to write with a const-value of
-RTL8211F_LEDCR_MODE, thus setting Mode bit explicitly.
-
-Fixes: 17784801d888 ("net: phy: realtek: Add support for PHY LEDs on RTL8211F")
-Signed-off-by: Sava Jakovljev <savaj@meyersound.com>
-Reviewed-by: Marek Vasut <marex@denx.de>
-Link: https://patch.msgid.link/PAWP192MB21287372F30C4E55B6DF6158C38E2@PAWP192MB2128.EURP192.PROD.OUTLOOK.COM
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/phy/realtek.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -555,7 +555,7 @@ static int rtl8211f_led_hw_control_set(s
- unsigned long rules)
- {
- const u16 mask = RTL8211F_LEDCR_MASK << (RTL8211F_LEDCR_SHIFT * index);
-- u16 reg = RTL8211F_LEDCR_MODE; /* Mode B */
-+ u16 reg = 0;
-
- if (index >= RTL8211F_LED_COUNT)
- return -EINVAL;
-@@ -575,6 +575,7 @@ static int rtl8211f_led_hw_control_set(s
- }
-
- reg <<= RTL8211F_LEDCR_SHIFT * index;
-+ reg |= RTL8211F_LEDCR_MODE; /* Mode B */
-
- return phy_modify_paged(phydev, 0xd04, RTL8211F_LEDCR, mask, reg);
- }
+++ /dev/null
-From c283782fc5d60c4d8169137c6f955aa3553d3b3d Mon Sep 17 00:00:00 2001
-From: Hui Wang <hui.wang@canonical.com>
-Date: Fri, 27 Sep 2024 19:46:10 +0800
-Subject: [PATCH] net: phy: realtek: Check the index value in
- led_hw_control_get
-
-Just like rtl8211f_led_hw_is_supported() and
-rtl8211f_led_hw_control_set(), the rtl8211f_led_hw_control_get() also
-needs to check the index value, otherwise the caller is likely to get
-an incorrect rules.
-
-Fixes: 17784801d888 ("net: phy: realtek: Add support for PHY LEDs on RTL8211F")
-Signed-off-by: Hui Wang <hui.wang@canonical.com>
-Reviewed-by: Marek Vasut <marex@denx.de>
-Link: https://patch.msgid.link/20240927114610.1278935-1-hui.wang@canonical.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/realtek.c | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -527,6 +527,9 @@ static int rtl8211f_led_hw_control_get(s
- {
- int val;
-
-+ if (index >= RTL8211F_LED_COUNT)
-+ return -EINVAL;
-+
- val = phy_read_paged(phydev, 0xd04, RTL8211F_LEDCR);
- if (val < 0)
- return val;
+++ /dev/null
-From a6ad589c1d118f9d5b1bc4c6888d42919f830340 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Mon, 7 Oct 2024 11:57:41 +0200
-Subject: [PATCH] net: phy: realtek: Fix MMD access on RTL8126A-integrated PHY
-
-All MMD reads return 0 for the RTL8126A-integrated PHY. Therefore phylib
-assumes it doesn't support EEE, what results in higher power consumption,
-and a significantly higher chip temperature in my case.
-To fix this split out the PHY driver for the RTL8126A-integrated PHY
-and set the read_mmd/write_mmd callbacks to read from vendor-specific
-registers.
-
-Fixes: 5befa3728b85 ("net: phy: realtek: add support for RTL8126A-integrated 5Gbps PHY")
-Cc: stable@vger.kernel.org
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/realtek.c | 24 +++++++++++++++++++++++-
- 1 file changed, 23 insertions(+), 1 deletion(-)
-
---- a/drivers/net/phy/realtek.c
-+++ b/drivers/net/phy/realtek.c
-@@ -1081,6 +1081,16 @@ static int rtl8221b_vn_cg_c45_match_phy_
- return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
- }
-
-+static int rtl8251b_c22_match_phy_device(struct phy_device *phydev)
-+{
-+ return rtlgen_is_c45_match(phydev, RTL_8251B, false);
-+}
-+
-+static int rtl8251b_c45_match_phy_device(struct phy_device *phydev)
-+{
-+ return rtlgen_is_c45_match(phydev, RTL_8251B, true);
-+}
-+
- static int rtlgen_resume(struct phy_device *phydev)
- {
- int ret = genphy_resume(phydev);
-@@ -1418,7 +1428,7 @@ static struct phy_driver realtek_drvs[]
- .suspend = genphy_c45_pma_suspend,
- .resume = rtlgen_c45_resume,
- }, {
-- PHY_ID_MATCH_EXACT(0x001cc862),
-+ .match_phy_device = rtl8251b_c45_match_phy_device,
- .name = "RTL8251B 5Gbps PHY",
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
-@@ -1428,6 +1438,18 @@ static struct phy_driver realtek_drvs[]
- .read_page = rtl821x_read_page,
- .write_page = rtl821x_write_page,
- }, {
-+ .match_phy_device = rtl8251b_c22_match_phy_device,
-+ .name = "RTL8126A-internal 5Gbps PHY",
-+ .get_features = rtl822x_get_features,
-+ .config_aneg = rtl822x_config_aneg,
-+ .read_status = rtl822x_read_status,
-+ .suspend = genphy_suspend,
-+ .resume = rtlgen_resume,
-+ .read_page = rtl821x_read_page,
-+ .write_page = rtl821x_write_page,
-+ .read_mmd = rtl822x_read_mmd,
-+ .write_mmd = rtl822x_write_mmd,
-+ }, {
- PHY_ID_MATCH_EXACT(0x001ccad0),
- .name = "RTL8224 2.5Gbps PHY",
- .get_features = rtl822x_c45_get_features,
+++ /dev/null
-From 2f3ce7a56c6e02bc0b258507f3a82b7511f62f9e Mon Sep 17 00:00:00 2001
-From: Marek Behún <kabel@kernel.org>
-Date: Tue, 21 Nov 2023 18:20:24 +0100
-Subject: net: sfp: rework the RollBall PHY waiting code
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-RollBall SFP modules allow the access to PHY registers only after a
-certain time has passed. Until then, the registers read 0xffff.
-
-Currently we have quirks for modules where we need to wait either 25
-seconds or 4 seconds, but recently I got hands on another module where
-the wait is even shorter.
-
-Instead of hardcoding different wait times, lets rework the code:
-- increase the PHY retry count to 25
-- when RollBall module is detected, increase the PHY retry time from
- 50ms to 1s
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/sfp.c | 41 +++++++++++++++++++++--------------------
- 1 file changed, 21 insertions(+), 20 deletions(-)
-
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -191,7 +191,7 @@ static const enum gpiod_flags gpio_flags
- * R_PHY_RETRY is the number of attempts.
- */
- #define T_PHY_RETRY msecs_to_jiffies(50)
--#define R_PHY_RETRY 12
-+#define R_PHY_RETRY 25
-
- /* SFP module presence detection is poor: the three MOD DEF signals are
- * the same length on the PCB, which means it's possible for MOD DEF 0 to
-@@ -274,7 +274,7 @@ struct sfp {
- struct sfp_eeprom_id id;
- unsigned int module_power_mW;
- unsigned int module_t_start_up;
-- unsigned int module_t_wait;
-+ unsigned int phy_t_retry;
-
- unsigned int rate_kbd;
- unsigned int rs_threshold_kbd;
-@@ -357,18 +357,22 @@ static void sfp_fixup_10gbaset_30m(struc
- sfp->id.base.extended_cc = SFF8024_ECC_10GBASE_T_SR;
- }
-
--static void sfp_fixup_rollball_proto(struct sfp *sfp, unsigned int secs)
-+static void sfp_fixup_rollball(struct sfp *sfp)
- {
- sfp->mdio_protocol = MDIO_I2C_ROLLBALL;
-- sfp->module_t_wait = msecs_to_jiffies(secs * 1000);
-+
-+ /* RollBall modules may disallow access to PHY registers for up to 25
-+ * seconds, and the reads return 0xffff before that. Increase the time
-+ * between PHY probe retries from 50ms to 1s so that we will wait for
-+ * the PHY for a sufficient amount of time.
-+ */
-+ sfp->phy_t_retry = msecs_to_jiffies(1000);
- }
-
- static void sfp_fixup_fs_10gt(struct sfp *sfp)
- {
- sfp_fixup_10gbaset_30m(sfp);
--
-- // These SFPs need 4 seconds before the PHY can be accessed
-- sfp_fixup_rollball_proto(sfp, 4);
-+ sfp_fixup_rollball(sfp);
- }
-
- static void sfp_fixup_halny_gsfp(struct sfp *sfp)
-@@ -380,12 +384,6 @@ static void sfp_fixup_halny_gsfp(struct
- sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS);
- }
-
--static void sfp_fixup_rollball(struct sfp *sfp)
--{
-- // Rollball SFPs need 25 seconds before the PHY can be accessed
-- sfp_fixup_rollball_proto(sfp, 25);
--}
--
- static void sfp_fixup_rollball_cc(struct sfp *sfp)
- {
- sfp_fixup_rollball(sfp);
-@@ -2320,7 +2318,7 @@ static int sfp_sm_mod_probe(struct sfp *
- mask |= SFP_F_RS1;
-
- sfp->module_t_start_up = T_START_UP;
-- sfp->module_t_wait = T_WAIT;
-+ sfp->phy_t_retry = T_PHY_RETRY;
-
- sfp->state_ignore_mask = 0;
-
-@@ -2556,10 +2554,9 @@ static void sfp_sm_main(struct sfp *sfp,
-
- /* We need to check the TX_FAULT state, which is not defined
- * while TX_DISABLE is asserted. The earliest we want to do
-- * anything (such as probe for a PHY) is 50ms (or more on
-- * specific modules).
-+ * anything (such as probe for a PHY) is 50ms.
- */
-- sfp_sm_next(sfp, SFP_S_WAIT, sfp->module_t_wait);
-+ sfp_sm_next(sfp, SFP_S_WAIT, T_WAIT);
- break;
-
- case SFP_S_WAIT:
-@@ -2573,8 +2570,8 @@ static void sfp_sm_main(struct sfp *sfp,
- * deasserting.
- */
- timeout = sfp->module_t_start_up;
-- if (timeout > sfp->module_t_wait)
-- timeout -= sfp->module_t_wait;
-+ if (timeout > T_WAIT)
-+ timeout -= T_WAIT;
- else
- timeout = 1;
-
-@@ -2617,7 +2614,11 @@ static void sfp_sm_main(struct sfp *sfp,
- ret = sfp_sm_probe_for_phy(sfp);
- if (ret == -ENODEV) {
- if (--sfp->sm_phy_retries) {
-- sfp_sm_next(sfp, SFP_S_INIT_PHY, T_PHY_RETRY);
-+ sfp_sm_next(sfp, SFP_S_INIT_PHY,
-+ sfp->phy_t_retry);
-+ dev_dbg(sfp->dev,
-+ "no PHY detected, %u tries left\n",
-+ sfp->sm_phy_retries);
- break;
- } else {
- dev_info(sfp->dev, "no PHY detected\n");
+++ /dev/null
-From e9301af385e7864dea353f5e58cad7339dd6c718 Mon Sep 17 00:00:00 2001
-From: Marek Behún <kabel@kernel.org>
-Date: Tue, 19 Dec 2023 17:24:15 +0100
-Subject: net: sfp: fix PHY discovery for FS SFP-10G-T module
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Commit 2f3ce7a56c6e ("net: sfp: rework the RollBall PHY waiting code")
-changed the long wait before accessing RollBall / FS modules into
-probing for PHY every 1 second, and trying 25 times.
-
-Wei Lei reports that this does not work correctly on FS modules: when
-initializing, they may report values different from 0xffff in PHY ID
-registers for some MMDs, causing get_phy_c45_ids() to find some bogus
-MMD.
-
-Fix this by adding the module_t_wait member back, and setting it to 4
-seconds for FS modules.
-
-Fixes: 2f3ce7a56c6e ("net: sfp: rework the RollBall PHY waiting code")
-Reported-by: Wei Lei <quic_leiwei@quicinc.com>
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Tested-by: Lei Wei <quic_leiwei@quicinc.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/sfp.c | 17 +++++++++++++----
- 1 file changed, 13 insertions(+), 4 deletions(-)
-
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -274,6 +274,7 @@ struct sfp {
- struct sfp_eeprom_id id;
- unsigned int module_power_mW;
- unsigned int module_t_start_up;
-+ unsigned int module_t_wait;
- unsigned int phy_t_retry;
-
- unsigned int rate_kbd;
-@@ -373,6 +374,12 @@ static void sfp_fixup_fs_10gt(struct sfp
- {
- sfp_fixup_10gbaset_30m(sfp);
- sfp_fixup_rollball(sfp);
-+
-+ /* The RollBall fixup is not enough for FS modules, the AQR chip inside
-+ * them does not return 0xffff for PHY ID registers in all MMDs for the
-+ * while initializing. They need a 4 second wait before accessing PHY.
-+ */
-+ sfp->module_t_wait = msecs_to_jiffies(4000);
- }
-
- static void sfp_fixup_halny_gsfp(struct sfp *sfp)
-@@ -2318,6 +2325,7 @@ static int sfp_sm_mod_probe(struct sfp *
- mask |= SFP_F_RS1;
-
- sfp->module_t_start_up = T_START_UP;
-+ sfp->module_t_wait = T_WAIT;
- sfp->phy_t_retry = T_PHY_RETRY;
-
- sfp->state_ignore_mask = 0;
-@@ -2554,9 +2562,10 @@ static void sfp_sm_main(struct sfp *sfp,
-
- /* We need to check the TX_FAULT state, which is not defined
- * while TX_DISABLE is asserted. The earliest we want to do
-- * anything (such as probe for a PHY) is 50ms.
-+ * anything (such as probe for a PHY) is 50ms (or more on
-+ * specific modules).
- */
-- sfp_sm_next(sfp, SFP_S_WAIT, T_WAIT);
-+ sfp_sm_next(sfp, SFP_S_WAIT, sfp->module_t_wait);
- break;
-
- case SFP_S_WAIT:
-@@ -2570,8 +2579,8 @@ static void sfp_sm_main(struct sfp *sfp,
- * deasserting.
- */
- timeout = sfp->module_t_start_up;
-- if (timeout > T_WAIT)
-- timeout -= T_WAIT;
-+ if (timeout > sfp->module_t_wait)
-+ timeout -= sfp->module_t_wait;
- else
- timeout = 1;
-
+++ /dev/null
-From 6999e0fc9a552ce98fcc66bee3dca7e55fba0ed3 Mon Sep 17 00:00:00 2001
-From: Marek Behún <kabel@kernel.org>
-Date: Tue, 23 Apr 2024 10:50:38 +0200
-Subject: net: sfp: update comment for FS SFP-10G-T quirk
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Update the comment for the Fibrestore SFP-10G-T module: since commit
-e9301af385e7 ("net: sfp: fix PHY discovery for FS SFP-10G-T module")
-we also do a 4 second wait before probing the PHY.
-
-Fixes: e9301af385e7 ("net: sfp: fix PHY discovery for FS SFP-10G-T module")
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Jiri Pirko <jiri@nvidia.com>
-Link: https://lore.kernel.org/r/20240423085039.26957-1-kabel@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/sfp.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -454,8 +454,9 @@ static const struct sfp_quirk sfp_quirks
- SFP_QUIRK("ALCATELLUCENT", "3FE46541AA", sfp_quirk_2500basex,
- sfp_fixup_long_startup),
-
-- // Fiberstore SFP-10G-T doesn't identify as copper, and uses the
-- // Rollball protocol to talk to the PHY.
-+ // Fiberstore SFP-10G-T doesn't identify as copper, uses the Rollball
-+ // protocol to talk to the PHY and needs 4 sec wait before probing the
-+ // PHY.
- SFP_QUIRK_F("FS", "SFP-10G-T", sfp_fixup_fs_10gt),
-
- // Fiberstore GPON-ONU-34-20BI can operate at 2500base-X, but report 1.2GBd
+++ /dev/null
-From cd4a32e60061789676f7f018a94fcc9ec56732a0 Mon Sep 17 00:00:00 2001
-From: Marek Behún <kabel@kernel.org>
-Date: Tue, 23 Apr 2024 10:50:39 +0200
-Subject: net: sfp: enhance quirk for Fibrestore 2.5G copper SFP module
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Enhance the quirk for Fibrestore 2.5G copper SFP module. The original
-commit e27aca3760c0 ("net: sfp: add quirk for FS's 2.5G copper SFP")
-introducing the quirk says that the PHY is inaccessible, but that is
-not true.
-
-The module uses Rollball protocol to talk to the PHY, and needs a 4
-second wait before probing it, same as FS 10G module.
-
-The PHY inside the module is Realtek RTL8221B-VB-CG PHY. The realtek
-driver recently gained support to set it up via clause 45 accesses.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Reviewed-by: Jiri Pirko <jiri@nvidia.com>
-Link: https://lore.kernel.org/r/20240423085039.26957-2-kabel@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/sfp.c | 18 ++++++++++++------
- 1 file changed, 12 insertions(+), 6 deletions(-)
-
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -370,18 +370,23 @@ static void sfp_fixup_rollball(struct sf
- sfp->phy_t_retry = msecs_to_jiffies(1000);
- }
-
--static void sfp_fixup_fs_10gt(struct sfp *sfp)
-+static void sfp_fixup_fs_2_5gt(struct sfp *sfp)
- {
-- sfp_fixup_10gbaset_30m(sfp);
- sfp_fixup_rollball(sfp);
-
-- /* The RollBall fixup is not enough for FS modules, the AQR chip inside
-+ /* The RollBall fixup is not enough for FS modules, the PHY chip inside
- * them does not return 0xffff for PHY ID registers in all MMDs for the
- * while initializing. They need a 4 second wait before accessing PHY.
- */
- sfp->module_t_wait = msecs_to_jiffies(4000);
- }
-
-+static void sfp_fixup_fs_10gt(struct sfp *sfp)
-+{
-+ sfp_fixup_10gbaset_30m(sfp);
-+ sfp_fixup_fs_2_5gt(sfp);
-+}
-+
- static void sfp_fixup_halny_gsfp(struct sfp *sfp)
- {
- /* Ignore the TX_FAULT and LOS signals on this module.
-@@ -459,6 +464,10 @@ static const struct sfp_quirk sfp_quirks
- // PHY.
- SFP_QUIRK_F("FS", "SFP-10G-T", sfp_fixup_fs_10gt),
-
-+ // Fiberstore SFP-2.5G-T uses Rollball protocol to talk to the PHY and
-+ // needs 4 sec wait before probing the PHY.
-+ SFP_QUIRK_F("FS", "SFP-2.5G-T", sfp_fixup_fs_2_5gt),
-+
- // Fiberstore GPON-ONU-34-20BI can operate at 2500base-X, but report 1.2GBd
- // NRZ in their EEPROM
- SFP_QUIRK("FS", "GPON-ONU-34-20BI", sfp_quirk_2500basex,
-@@ -475,9 +484,6 @@ static const struct sfp_quirk sfp_quirks
- SFP_QUIRK("HUAWEI", "MA5671A", sfp_quirk_2500basex,
- sfp_fixup_ignore_tx_fault),
-
-- // FS 2.5G Base-T
-- SFP_QUIRK_M("FS", "SFP-2.5G-T", sfp_quirk_oem_2_5g),
--
- // Lantech 8330-262D-E can operate at 2500base-X, but incorrectly report
- // 2500MBd NRZ in their EEPROM
- SFP_QUIRK_M("Lantech", "8330-262D-E", sfp_quirk_2500basex),
+++ /dev/null
-From 05ec5c085eb7ae044d49e04a3cff194a0b2a3251 Mon Sep 17 00:00:00 2001
-From: Martin Schiller <ms@dev.tdt.de>
-Date: Thu, 27 Feb 2025 08:10:58 +0100
-Subject: net: sfp: add quirk for FS SFP-10GM-T copper SFP+ module
-
-Add quirk for a copper SFP that identifies itself as "FS" "SFP-10GM-T".
-It uses RollBall protocol to talk to the PHY and needs 4 sec wait before
-probing the PHY.
-
-Signed-off-by: Martin Schiller <ms@dev.tdt.de>
-Link: https://patch.msgid.link/20250227071058.1520027-1-ms@dev.tdt.de
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/sfp.c | 11 ++++++-----
- 1 file changed, 6 insertions(+), 5 deletions(-)
-
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -370,7 +370,7 @@ static void sfp_fixup_rollball(struct sf
- sfp->phy_t_retry = msecs_to_jiffies(1000);
- }
-
--static void sfp_fixup_fs_2_5gt(struct sfp *sfp)
-+static void sfp_fixup_rollball_wait4s(struct sfp *sfp)
- {
- sfp_fixup_rollball(sfp);
-
-@@ -384,7 +384,7 @@ static void sfp_fixup_fs_2_5gt(struct sf
- static void sfp_fixup_fs_10gt(struct sfp *sfp)
- {
- sfp_fixup_10gbaset_30m(sfp);
-- sfp_fixup_fs_2_5gt(sfp);
-+ sfp_fixup_rollball_wait4s(sfp);
- }
-
- static void sfp_fixup_halny_gsfp(struct sfp *sfp)
-@@ -464,9 +464,10 @@ static const struct sfp_quirk sfp_quirks
- // PHY.
- SFP_QUIRK_F("FS", "SFP-10G-T", sfp_fixup_fs_10gt),
-
-- // Fiberstore SFP-2.5G-T uses Rollball protocol to talk to the PHY and
-- // needs 4 sec wait before probing the PHY.
-- SFP_QUIRK_F("FS", "SFP-2.5G-T", sfp_fixup_fs_2_5gt),
-+ // Fiberstore SFP-2.5G-T and SFP-10GM-T uses Rollball protocol to talk
-+ // to the PHY and needs 4 sec wait before probing the PHY.
-+ SFP_QUIRK_F("FS", "SFP-2.5G-T", sfp_fixup_rollball_wait4s),
-+ SFP_QUIRK_F("FS", "SFP-10GM-T", sfp_fixup_rollball_wait4s),
-
- // Fiberstore GPON-ONU-34-20BI can operate at 2500base-X, but report 1.2GBd
- // NRZ in their EEPROM
+++ /dev/null
-From b91ef50f70e7c092c50c1b92e63ef3fb0041cdd4 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de>
-Date: Mon, 18 Sep 2023 21:19:12 +0200
-Subject: [PATCH 01/30] net: dsa: mt7530: Convert to platform remove callback
- returning void
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The .remove() callback for a platform driver returns an int which makes
-many driver authors wrongly assume it's possible to do error handling by
-returning an error code. However the value returned is ignored (apart
-from emitting a warning) and this typically results in resource leaks.
-To improve here there is a quest to make the remove callback return
-void. In the first step of this quest all drivers are converted to
-.remove_new() which already returns void. Eventually after all drivers
-are converted, .remove_new() is renamed to .remove().
-
-Trivially convert this driver from always returning zero in the remove
-callback to the void returning variant.
-
-Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530-mmio.c | 7 ++-----
- 1 file changed, 2 insertions(+), 5 deletions(-)
-
---- a/drivers/net/dsa/mt7530-mmio.c
-+++ b/drivers/net/dsa/mt7530-mmio.c
-@@ -63,15 +63,12 @@ mt7988_probe(struct platform_device *pde
- return dsa_register_switch(priv->ds);
- }
-
--static int
--mt7988_remove(struct platform_device *pdev)
-+static void mt7988_remove(struct platform_device *pdev)
- {
- struct mt7530_priv *priv = platform_get_drvdata(pdev);
-
- if (priv)
- mt7530_remove_common(priv);
--
-- return 0;
- }
-
- static void mt7988_shutdown(struct platform_device *pdev)
-@@ -88,7 +85,7 @@ static void mt7988_shutdown(struct platf
-
- static struct platform_driver mt7988_platform_driver = {
- .probe = mt7988_probe,
-- .remove = mt7988_remove,
-+ .remove_new = mt7988_remove,
- .shutdown = mt7988_shutdown,
- .driver = {
- .name = "mt7530-mmio",
+++ /dev/null
-From d22c85764665af931c5c61bbe282b4116a88e792 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Wed, 27 Sep 2023 13:13:56 +0100
-Subject: [PATCH 02/30] net: dsa: mt753x: remove mt753x_phylink_pcs_link_up()
-
-Remove the mt753x_phylink_pcs_link_up() function for two reasons:
-
-1) priv->pcs[i].pcs.neg_mode is set true, meaning it doesn't take a
- MLO_AN_FIXED anymore, but one of PHYLINK_PCS_NEG_*. However, this
- is inconsequential due to...
-2) priv->pcs[port].pcs.ops is always initialised to point at
- mt7530_pcs_ops, which does not have a pcs_link_up() member.
-
-So, let's remove mt753x_phylink_pcs_link_up() entirely.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://lore.kernel.org/r/E1qlTQS-008BWe-Va@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 11 -----------
- 1 file changed, 11 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -3033,15 +3033,6 @@ static void mt753x_phylink_mac_link_down
- mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
- }
-
--static void mt753x_phylink_pcs_link_up(struct phylink_pcs *pcs,
-- unsigned int mode,
-- phy_interface_t interface,
-- int speed, int duplex)
--{
-- if (pcs->ops->pcs_link_up)
-- pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
--}
--
- static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,
- unsigned int mode,
- phy_interface_t interface,
-@@ -3129,8 +3120,6 @@ mt7531_cpu_port_config(struct dsa_switch
- return ret;
- mt7530_write(priv, MT7530_PMCR_P(port),
- PMCR_CPU_PORT_SETTING(priv->id));
-- mt753x_phylink_pcs_link_up(&priv->pcs[port].pcs, MLO_AN_FIXED,
-- interface, speed, DUPLEX_FULL);
- mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL,
- speed, DUPLEX_FULL, true, true);
-
+++ /dev/null
-From 9b4f1f5a0801652056670a38503b4049eb413caf Mon Sep 17 00:00:00 2001
-From: Justin Stitt <justinstitt@google.com>
-Date: Mon, 9 Oct 2023 18:29:19 +0000
-Subject: [PATCH 03/30] net: dsa: mt7530: replace deprecated strncpy with
- ethtool_sprintf
-
-`strncpy` is deprecated for use on NUL-terminated destination strings
-[1] and as such we should prefer more robust and less ambiguous string
-interfaces.
-
-ethtool_sprintf() is designed specifically for get_strings() usage.
-Let's replace strncpy in favor of this more robust and easier to
-understand interface.
-
-Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1]
-Link: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html [2]
-Link: https://github.com/KSPP/linux/issues/90
-Signed-off-by: Justin Stitt <justinstitt@google.com>
-Reviewed-by: Kees Cook <keescook@chromium.org>
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
-Link: https://lore.kernel.org/r/20231009-strncpy-drivers-net-dsa-mt7530-c-v1-1-ec6677a6436a@google.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -836,8 +836,7 @@ mt7530_get_strings(struct dsa_switch *ds
- return;
-
- for (i = 0; i < ARRAY_SIZE(mt7530_mib); i++)
-- strncpy(data + i * ETH_GSTRING_LEN, mt7530_mib[i].name,
-- ETH_GSTRING_LEN);
-+ ethtool_sprintf(&data, "%s", mt7530_mib[i].name);
- }
-
- static void
+++ /dev/null
-From af26b0d1bf934bbaa7cafb871a51e95087a088a0 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Jan 2024 08:34:31 +0300
-Subject: [PATCH 04/30] net: dsa: mt7530: support OF-based registration of
- switch MDIO bus
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Currently the MDIO bus of the switches the MT7530 DSA subdriver controls
-can only be registered as non-OF-based. Bring support for registering the
-bus OF-based.
-
-The subdrivers that control switches [with MDIO bus] probed on OF must
-follow this logic to support all cases properly:
-
-No switch MDIO bus defined: Populate ds->user_mii_bus, register the MDIO
-bus, set the interrupts for PHYs if "interrupt-controller" is defined at
-the switch node. This case should only be covered for the switches which
-their dt-bindings documentation didn't document the MDIO bus from the
-start. This is to keep supporting the device trees that do not describe the
-MDIO bus on the device tree but the MDIO bus is being used nonetheless.
-
-Switch MDIO bus defined: Don't populate ds->user_mii_bus, register the MDIO
-bus, set the interrupts for PHYs if ["interrupt-controller" is defined at
-the switch node and "interrupts" is defined at the PHY nodes under the
-switch MDIO bus node].
-
-Switch MDIO bus defined but explicitly disabled: If the device tree says
-status = "disabled" for the MDIO bus, we shouldn't need an MDIO bus at all.
-Instead, just exit as early as possible and do not call any MDIO API.
-
-The use of ds->user_mii_bus is inappropriate when the MDIO bus of the
-switch is described on the device tree [1], which is why we don't populate
-ds->user_mii_bus in that case.
-
-Link: https://lore.kernel.org/netdev/20231213120656.x46fyad6ls7sqyzv@skbuf/ [1]
-Suggested-by: David Bauer <mail@david-bauer.net>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Link: https://lore.kernel.org/r/20240122053431.7751-1-arinc.unal@arinc9.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 34 ++++++++++++++++++++++++++--------
- 1 file changed, 26 insertions(+), 8 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2345,24 +2345,40 @@ mt7530_free_irq_common(struct mt7530_pri
- static void
- mt7530_free_irq(struct mt7530_priv *priv)
- {
-- mt7530_free_mdio_irq(priv);
-+ struct device_node *mnp, *np = priv->dev->of_node;
-+
-+ mnp = of_get_child_by_name(np, "mdio");
-+ if (!mnp)
-+ mt7530_free_mdio_irq(priv);
-+ of_node_put(mnp);
-+
- mt7530_free_irq_common(priv);
- }
-
- static int
- mt7530_setup_mdio(struct mt7530_priv *priv)
- {
-+ struct device_node *mnp, *np = priv->dev->of_node;
- struct dsa_switch *ds = priv->ds;
- struct device *dev = priv->dev;
- struct mii_bus *bus;
- static int idx;
-- int ret;
-+ int ret = 0;
-+
-+ mnp = of_get_child_by_name(np, "mdio");
-+
-+ if (mnp && !of_device_is_available(mnp))
-+ goto out;
-
- bus = devm_mdiobus_alloc(dev);
-- if (!bus)
-- return -ENOMEM;
-+ if (!bus) {
-+ ret = -ENOMEM;
-+ goto out;
-+ }
-+
-+ if (!mnp)
-+ ds->slave_mii_bus = bus;
-
-- ds->slave_mii_bus = bus;
- bus->priv = priv;
- bus->name = KBUILD_MODNAME "-mii";
- snprintf(bus->id, MII_BUS_ID_SIZE, KBUILD_MODNAME "-%d", idx++);
-@@ -2373,16 +2389,18 @@ mt7530_setup_mdio(struct mt7530_priv *pr
- bus->parent = dev;
- bus->phy_mask = ~ds->phys_mii_mask;
-
-- if (priv->irq)
-+ if (priv->irq && !mnp)
- mt7530_setup_mdio_irq(priv);
-
-- ret = devm_mdiobus_register(dev, bus);
-+ ret = devm_of_mdiobus_register(dev, bus, mnp);
- if (ret) {
- dev_err(dev, "failed to register MDIO bus: %d\n", ret);
-- if (priv->irq)
-+ if (priv->irq && !mnp)
- mt7530_free_mdio_irq(priv);
- }
-
-+out:
-+ of_node_put(mnp);
- return ret;
- }
-
+++ /dev/null
-From 617b07e08bcb1f69a72a085a7d847d1ca2999830 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Jan 2024 08:35:52 +0300
-Subject: [PATCH 05/30] net: dsa: mt7530: always trap frames to active CPU port
- on MT7530
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-On the MT7530 switch, the CPU_PORT field indicates which CPU port to trap
-frames to, regardless of the affinity of the inbound user port.
-
-When multiple CPU ports are in use, if the DSA conduit interface is down,
-trapped frames won't be passed to the conduit interface.
-
-To make trapping frames work including this case, implement
-ds->ops->conduit_state_change() on this subdriver and set the CPU_PORT
-field to the numerically smallest CPU port whose conduit interface is up.
-Introduce the active_cpu_ports field to store the information of the active
-CPU ports. Correct the macros, CPU_PORT is bits 4 through 6 of the
-register.
-
-Add a comment to explain frame trapping for this switch.
-
-Currently, the driver doesn't support the use of multiple CPU ports so this
-is not necessarily a bug fix.
-
-Suggested-by: Vladimir Oltean <olteanv@gmail.com>
-Suggested-by: Russell King (Oracle) <linux@armlinux.org.uk>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-1-042401f2b279@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 35 +++++++++++++++++++++++++++++++----
- drivers/net/dsa/mt7530.h | 6 ++++--
- 2 files changed, 35 insertions(+), 6 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -1232,10 +1232,6 @@ mt753x_cpu_port_enable(struct dsa_switch
- mt7530_set(priv, MT7530_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) |
- UNU_FFP(BIT(port)));
-
-- /* Set CPU port number */
-- if (priv->id == ID_MT7530 || priv->id == ID_MT7621)
-- mt7530_rmw(priv, MT7530_MFC, CPU_MASK, CPU_EN | CPU_PORT(port));
--
- /* Add the CPU port to the CPU port bitmap for MT7531 and the switch on
- * the MT7988 SoC. Trapped frames will be forwarded to the CPU port that
- * is affine to the inbound user port.
-@@ -3301,6 +3297,36 @@ static int mt753x_set_mac_eee(struct dsa
- return 0;
- }
-
-+static void
-+mt753x_conduit_state_change(struct dsa_switch *ds,
-+ const struct net_device *conduit,
-+ bool operational)
-+{
-+ struct dsa_port *cpu_dp = conduit->dsa_ptr;
-+ struct mt7530_priv *priv = ds->priv;
-+ int val = 0;
-+ u8 mask;
-+
-+ /* Set the CPU port to trap frames to for MT7530. Trapped frames will be
-+ * forwarded to the numerically smallest CPU port whose conduit
-+ * interface is up.
-+ */
-+ if (priv->id != ID_MT7530 && priv->id != ID_MT7621)
-+ return;
-+
-+ mask = BIT(cpu_dp->index);
-+
-+ if (operational)
-+ priv->active_cpu_ports |= mask;
-+ else
-+ priv->active_cpu_ports &= ~mask;
-+
-+ if (priv->active_cpu_ports)
-+ val = CPU_EN | CPU_PORT(__ffs(priv->active_cpu_ports));
-+
-+ mt7530_rmw(priv, MT7530_MFC, CPU_EN | CPU_PORT_MASK, val);
-+}
-+
- static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
- {
- return 0;
-@@ -3356,6 +3382,7 @@ const struct dsa_switch_ops mt7530_switc
- .phylink_mac_link_up = mt753x_phylink_mac_link_up,
- .get_mac_eee = mt753x_get_mac_eee,
- .set_mac_eee = mt753x_set_mac_eee,
-+ .master_state_change = mt753x_conduit_state_change,
- };
- EXPORT_SYMBOL_GPL(mt7530_switch_ops);
-
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -45,8 +45,8 @@ enum mt753x_id {
- #define UNU_FFP(x) (((x) & 0xff) << 8)
- #define UNU_FFP_MASK UNU_FFP(~0)
- #define CPU_EN BIT(7)
--#define CPU_PORT(x) ((x) << 4)
--#define CPU_MASK (0xf << 4)
-+#define CPU_PORT_MASK GENMASK(6, 4)
-+#define CPU_PORT(x) FIELD_PREP(CPU_PORT_MASK, x)
- #define MIRROR_EN BIT(3)
- #define MIRROR_PORT(x) ((x) & 0x7)
- #define MIRROR_MASK 0x7
-@@ -790,6 +790,7 @@ struct mt753x_info {
- * @irq_domain: IRQ domain of the switch irq_chip
- * @irq_enable: IRQ enable bits, synced to SYS_INT_EN
- * @create_sgmii: Pointer to function creating SGMII PCS instance(s)
-+ * @active_cpu_ports: Holding the active CPU ports
- */
- struct mt7530_priv {
- struct device *dev;
-@@ -816,6 +817,7 @@ struct mt7530_priv {
- struct irq_domain *irq_domain;
- u32 irq_enable;
- int (*create_sgmii)(struct mt7530_priv *priv, bool dual_sgmii);
-+ u8 active_cpu_ports;
- };
-
- struct mt7530_hw_vlan_entry {
+++ /dev/null
-From 07f411e26f82d75723df1c0c072e5602d06f4e30 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Jan 2024 08:35:53 +0300
-Subject: [PATCH 06/30] net: dsa: mt7530: use p5_interface_select as data type
- for p5_intf_sel
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Use the p5_interface_select enumeration as the data type for the
-p5_intf_sel field. This ensures p5_intf_sel can only take the values
-defined in the p5_interface_select enumeration.
-
-Remove the explicit assignment of 0 to P5_DISABLED as the first enum item
-is automatically assigned 0.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-2-042401f2b279@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.h | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -713,7 +713,7 @@ struct mt7530_port {
-
- /* Port 5 interface select definitions */
- enum p5_interface_select {
-- P5_DISABLED = 0,
-+ P5_DISABLED,
- P5_INTF_SEL_PHY_P0,
- P5_INTF_SEL_PHY_P4,
- P5_INTF_SEL_GMAC5,
-@@ -806,7 +806,7 @@ struct mt7530_priv {
- bool mcm;
- phy_interface_t p6_interface;
- phy_interface_t p5_interface;
-- unsigned int p5_intf_sel;
-+ enum p5_interface_select p5_intf_sel;
- u8 mirror_rx;
- u8 mirror_tx;
- struct mt7530_port ports[MT7530_NUM_PORTS];
+++ /dev/null
-From 8f7db12efc189eedd196ed8d053236ce27add484 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Jan 2024 08:35:54 +0300
-Subject: [PATCH 07/30] net: dsa: mt7530: store port 5 SGMII capability of
- MT7531
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Introduce the p5_sgmii field to store the information for whether port 5
-has got SGMII or not. Instead of reading the MT7531_TOP_SIG_SR register
-multiple times, the register will be read once and the value will be
-stored on the p5_sgmii field. This saves unnecessary reads of the
-register.
-
-Move the comment about MT7531AE and MT7531BE to mt7531_setup(), where the
-switch is identified.
-
-Get rid of mt7531_dual_sgmii_supported() now that priv->p5_sgmii stores the
-information. Address the code where mt7531_dual_sgmii_supported() is used.
-
-Get rid of mt7531_is_rgmii_port() which just prints the opposite of
-priv->p5_sgmii.
-
-Instead of calling mt7531_pll_setup() then returning, do not call it if
-port 5 is SGMII.
-
-Remove P5_INTF_SEL_GMAC5_SGMII. The p5_interface_select enum is supposed to
-represent the mode that port 5 is being used in, not the hardware
-information of port 5. Set p5_intf_sel to P5_INTF_SEL_GMAC5 instead, if
-port 5 is not dsa_is_unused_port().
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-3-042401f2b279@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530-mdio.c | 7 ++---
- drivers/net/dsa/mt7530.c | 48 ++++++++++++-----------------------
- drivers/net/dsa/mt7530.h | 6 +++--
- 3 files changed, 22 insertions(+), 39 deletions(-)
-
---- a/drivers/net/dsa/mt7530-mdio.c
-+++ b/drivers/net/dsa/mt7530-mdio.c
-@@ -81,17 +81,14 @@ static const struct regmap_bus mt7530_re
- };
-
- static int
--mt7531_create_sgmii(struct mt7530_priv *priv, bool dual_sgmii)
-+mt7531_create_sgmii(struct mt7530_priv *priv)
- {
- struct regmap_config *mt7531_pcs_config[2] = {};
- struct phylink_pcs *pcs;
- struct regmap *regmap;
- int i, ret = 0;
-
-- /* MT7531AE has two SGMII units for port 5 and port 6
-- * MT7531BE has only one SGMII unit for port 6
-- */
-- for (i = dual_sgmii ? 0 : 1; i < 2; i++) {
-+ for (i = priv->p5_sgmii ? 0 : 1; i < 2; i++) {
- mt7531_pcs_config[i] = devm_kzalloc(priv->dev,
- sizeof(struct regmap_config),
- GFP_KERNEL);
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -487,15 +487,6 @@ mt7530_pad_clk_setup(struct dsa_switch *
- return 0;
- }
-
--static bool mt7531_dual_sgmii_supported(struct mt7530_priv *priv)
--{
-- u32 val;
--
-- val = mt7530_read(priv, MT7531_TOP_SIG_SR);
--
-- return (val & PAD_DUAL_SGMII_EN) != 0;
--}
--
- static int
- mt7531_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
- {
-@@ -510,9 +501,6 @@ mt7531_pll_setup(struct mt7530_priv *pri
- u32 xtal;
- u32 val;
-
-- if (mt7531_dual_sgmii_supported(priv))
-- return;
--
- val = mt7530_read(priv, MT7531_CREV);
- top_sig = mt7530_read(priv, MT7531_TOP_SIG_SR);
- hwstrap = mt7530_read(priv, MT7531_HWTRAP);
-@@ -920,8 +908,6 @@ static const char *p5_intf_modes(unsigne
- return "PHY P4";
- case P5_INTF_SEL_GMAC5:
- return "GMAC5";
-- case P5_INTF_SEL_GMAC5_SGMII:
-- return "GMAC5_SGMII";
- default:
- return "unknown";
- }
-@@ -2695,6 +2681,12 @@ mt7531_setup(struct dsa_switch *ds)
- return -ENODEV;
- }
-
-+ /* MT7531AE has got two SGMII units. One for port 5, one for port 6.
-+ * MT7531BE has got only one SGMII unit which is for port 6.
-+ */
-+ val = mt7530_read(priv, MT7531_TOP_SIG_SR);
-+ priv->p5_sgmii = !!(val & PAD_DUAL_SGMII_EN);
-+
- /* all MACs must be forced link-down before sw reset */
- for (i = 0; i < MT7530_NUM_PORTS; i++)
- mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK);
-@@ -2704,21 +2696,18 @@ mt7531_setup(struct dsa_switch *ds)
- SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
- SYS_CTRL_REG_RST);
-
-- mt7531_pll_setup(priv);
--
-- if (mt7531_dual_sgmii_supported(priv)) {
-- priv->p5_intf_sel = P5_INTF_SEL_GMAC5_SGMII;
--
-+ if (!priv->p5_sgmii) {
-+ mt7531_pll_setup(priv);
-+ } else {
- /* Let ds->slave_mii_bus be able to access external phy. */
- mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO11_RG_RXD2_MASK,
- MT7531_EXT_P_MDC_11);
- mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO12_RG_RXD3_MASK,
- MT7531_EXT_P_MDIO_12);
-- } else {
-- priv->p5_intf_sel = P5_INTF_SEL_GMAC5;
- }
-- dev_dbg(ds->dev, "P5 support %s interface\n",
-- p5_intf_modes(priv->p5_intf_sel));
-+
-+ if (!dsa_is_unused_port(ds, 5))
-+ priv->p5_intf_sel = P5_INTF_SEL_GMAC5;
-
- mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK,
- MT7531_GPIO0_INTERRUPT);
-@@ -2780,11 +2769,6 @@ static void mt7530_mac_port_get_caps(str
- }
- }
-
--static bool mt7531_is_rgmii_port(struct mt7530_priv *priv, u32 port)
--{
-- return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII);
--}
--
- static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port,
- struct phylink_config *config)
- {
-@@ -2797,7 +2781,7 @@ static void mt7531_mac_port_get_caps(str
- break;
-
- case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */
-- if (mt7531_is_rgmii_port(priv, port)) {
-+ if (!priv->p5_sgmii) {
- phy_interface_set_rgmii(config->supported_interfaces);
- break;
- }
-@@ -2864,7 +2848,7 @@ static int mt7531_rgmii_setup(struct mt7
- {
- u32 val;
-
-- if (!mt7531_is_rgmii_port(priv, port)) {
-+ if (priv->p5_sgmii) {
- dev_err(priv->dev, "RGMII mode is not available for port %d\n",
- port);
- return -EINVAL;
-@@ -3107,7 +3091,7 @@ mt7531_cpu_port_config(struct dsa_switch
-
- switch (port) {
- case 5:
-- if (mt7531_is_rgmii_port(priv, port))
-+ if (!priv->p5_sgmii)
- interface = PHY_INTERFACE_MODE_RGMII;
- else
- interface = PHY_INTERFACE_MODE_2500BASEX;
-@@ -3259,7 +3243,7 @@ mt753x_setup(struct dsa_switch *ds)
- mt7530_free_irq_common(priv);
-
- if (priv->create_sgmii) {
-- ret = priv->create_sgmii(priv, mt7531_dual_sgmii_supported(priv));
-+ ret = priv->create_sgmii(priv);
- if (ret && priv->irq)
- mt7530_free_irq(priv);
- }
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -717,7 +717,6 @@ enum p5_interface_select {
- P5_INTF_SEL_PHY_P0,
- P5_INTF_SEL_PHY_P4,
- P5_INTF_SEL_GMAC5,
-- P5_INTF_SEL_GMAC5_SGMII,
- };
-
- struct mt7530_priv;
-@@ -786,6 +785,8 @@ struct mt753x_info {
- * registers
- * @p6_interface Holding the current port 6 interface
- * @p5_intf_sel: Holding the current port 5 interface select
-+ * @p5_sgmii: Flag for distinguishing if port 5 of the MT7531 switch
-+ * has got SGMII
- * @irq: IRQ number of the switch
- * @irq_domain: IRQ domain of the switch irq_chip
- * @irq_enable: IRQ enable bits, synced to SYS_INT_EN
-@@ -807,6 +808,7 @@ struct mt7530_priv {
- phy_interface_t p6_interface;
- phy_interface_t p5_interface;
- enum p5_interface_select p5_intf_sel;
-+ bool p5_sgmii;
- u8 mirror_rx;
- u8 mirror_tx;
- struct mt7530_port ports[MT7530_NUM_PORTS];
-@@ -816,7 +818,7 @@ struct mt7530_priv {
- int irq;
- struct irq_domain *irq_domain;
- u32 irq_enable;
-- int (*create_sgmii)(struct mt7530_priv *priv, bool dual_sgmii);
-+ int (*create_sgmii)(struct mt7530_priv *priv);
- u8 active_cpu_ports;
- };
-
+++ /dev/null
-From c91b7fb8fbb2e18ebb497e67f4252cec78e3a29b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Jan 2024 08:35:55 +0300
-Subject: [PATCH 08/30] net: dsa: mt7530: improve comments regarding switch
- ports
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There's no logic to numerically order the CPU ports. Just state the port
-number instead.
-
-Remove the irrelevant PHY muxing information from
-mt7530_mac_port_get_caps(). Explain the supported MII modes instead.
-
-Remove the out of place PHY muxing information from
-mt753x_phylink_mac_config(). The function is for MT7530, MT7531, and the
-switch on the MT7988 SoC but there's no PHY muxing on MT7531 or the switch
-on the MT7988 SoC.
-
-These comments were gradually introduced with the commits below.
-commit ca366d6c889b ("net: dsa: mt7530: Convert to PHYLINK API")
-commit 38f790a80560 ("net: dsa: mt7530: Add support for port 5")
-commit 88bdef8be9f6 ("net: dsa: mt7530: Extend device data ready for adding
-a new hardware")
-commit c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch")
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-4-042401f2b279@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 30 ++++++++++++++++++++----------
- 1 file changed, 20 insertions(+), 10 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2747,12 +2747,14 @@ static void mt7530_mac_port_get_caps(str
- struct phylink_config *config)
- {
- switch (port) {
-- case 0 ... 4: /* Internal phy */
-+ /* Ports which are connected to switch PHYs. There is no MII pinout. */
-+ case 0 ... 4:
- __set_bit(PHY_INTERFACE_MODE_GMII,
- config->supported_interfaces);
- break;
-
-- case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */
-+ /* Port 5 supports rgmii with delays, mii, and gmii. */
-+ case 5:
- phy_interface_set_rgmii(config->supported_interfaces);
- __set_bit(PHY_INTERFACE_MODE_MII,
- config->supported_interfaces);
-@@ -2760,7 +2762,8 @@ static void mt7530_mac_port_get_caps(str
- config->supported_interfaces);
- break;
-
-- case 6: /* 1st cpu port */
-+ /* Port 6 supports rgmii and trgmii. */
-+ case 6:
- __set_bit(PHY_INTERFACE_MODE_RGMII,
- config->supported_interfaces);
- __set_bit(PHY_INTERFACE_MODE_TRGMII,
-@@ -2775,19 +2778,24 @@ static void mt7531_mac_port_get_caps(str
- struct mt7530_priv *priv = ds->priv;
-
- switch (port) {
-- case 0 ... 4: /* Internal phy */
-+ /* Ports which are connected to switch PHYs. There is no MII pinout. */
-+ case 0 ... 4:
- __set_bit(PHY_INTERFACE_MODE_GMII,
- config->supported_interfaces);
- break;
-
-- case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */
-+ /* Port 5 supports rgmii with delays on MT7531BE, sgmii/802.3z on
-+ * MT7531AE.
-+ */
-+ case 5:
- if (!priv->p5_sgmii) {
- phy_interface_set_rgmii(config->supported_interfaces);
- break;
- }
- fallthrough;
-
-- case 6: /* 1st cpu port supports sgmii/8023z only */
-+ /* Port 6 supports sgmii/802.3z. */
-+ case 6:
- __set_bit(PHY_INTERFACE_MODE_SGMII,
- config->supported_interfaces);
- __set_bit(PHY_INTERFACE_MODE_1000BASEX,
-@@ -2806,11 +2814,13 @@ static void mt7988_mac_port_get_caps(str
- phy_interface_zero(config->supported_interfaces);
-
- switch (port) {
-- case 0 ... 4: /* Internal phy */
-+ /* Ports which are connected to switch PHYs. There is no MII pinout. */
-+ case 0 ... 4:
- __set_bit(PHY_INTERFACE_MODE_INTERNAL,
- config->supported_interfaces);
- break;
-
-+ /* Port 6 is connected to SoC's XGMII MAC. There is no MII pinout. */
- case 6:
- __set_bit(PHY_INTERFACE_MODE_INTERNAL,
- config->supported_interfaces);
-@@ -2974,12 +2984,12 @@ mt753x_phylink_mac_config(struct dsa_swi
- u32 mcr_cur, mcr_new;
-
- switch (port) {
-- case 0 ... 4: /* Internal phy */
-+ case 0 ... 4:
- if (state->interface != PHY_INTERFACE_MODE_GMII &&
- state->interface != PHY_INTERFACE_MODE_INTERNAL)
- goto unsupported;
- break;
-- case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */
-+ case 5:
- if (priv->p5_interface == state->interface)
- break;
-
-@@ -2989,7 +2999,7 @@ mt753x_phylink_mac_config(struct dsa_swi
- if (priv->p5_intf_sel != P5_DISABLED)
- priv->p5_interface = state->interface;
- break;
-- case 6: /* 1st cpu port */
-+ case 6:
- if (priv->p6_interface == state->interface)
- break;
-
+++ /dev/null
-From c1b2294a9b4b9b6c0cbe58666cb86e0a9cb0abfd Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Jan 2024 08:35:56 +0300
-Subject: [PATCH 09/30] net: dsa: mt7530: improve code path for setting up port
- 5
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There're two code paths for setting up port 5:
-
-mt7530_setup()
--> mt7530_setup_port5()
-
-mt753x_phylink_mac_config()
--> mt753x_mac_config()
- -> mt7530_mac_config()
- -> mt7530_setup_port5()
-
-Currently mt7530_setup_port5() from mt7530_setup() always runs. If port 5
-is used as a CPU, DSA, or user port, mt7530_setup_port5() from
-mt753x_phylink_mac_config() won't run. That is because priv->p5_interface
-set on mt7530_setup_port5() will match state->interface on
-mt753x_phylink_mac_config() which will stop running mt7530_setup_port5()
-again.
-
-Therefore, mt7530_setup_port5() will never run from
-mt753x_phylink_mac_config().
-
-Address this by not running mt7530_setup_port5() from mt7530_setup() if
-port 5 is used as a CPU, DSA, or user port. This driver isn't in the
-dsa_switches_apply_workarounds[] array so phylink will always be present.
-
-To keep the cases where port 5 isn't controlled by phylink working as
-before, preserve the mt7530_setup_port5() call from mt7530_setup().
-
-Do not set priv->p5_intf_sel to P5_DISABLED. It is already set to that when
-"priv" is allocated.
-
-Move setting the interface to a more specific location. It's supposed to be
-overwritten if PHY muxing is detected.
-
-Improve the comment which explains the process.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-5-042401f2b279@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 17 ++++++++---------
- 1 file changed, 8 insertions(+), 9 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2529,16 +2529,15 @@ mt7530_setup(struct dsa_switch *ds)
- return ret;
-
- /* Setup port 5 */
-- priv->p5_intf_sel = P5_DISABLED;
-- interface = PHY_INTERFACE_MODE_NA;
--
- if (!dsa_is_unused_port(ds, 5)) {
- priv->p5_intf_sel = P5_INTF_SEL_GMAC5;
-- ret = of_get_phy_mode(dsa_to_port(ds, 5)->dn, &interface);
-- if (ret && ret != -ENODEV)
-- return ret;
- } else {
-- /* Scan the ethernet nodes. look for GMAC1, lookup used phy */
-+ /* Scan the ethernet nodes. Look for GMAC1, lookup the used PHY.
-+ * Set priv->p5_intf_sel to the appropriate value if PHY muxing
-+ * is detected.
-+ */
-+ interface = PHY_INTERFACE_MODE_NA;
-+
- for_each_child_of_node(dn, mac_np) {
- if (!of_device_is_compatible(mac_np,
- "mediatek,eth-mac"))
-@@ -2569,6 +2568,8 @@ mt7530_setup(struct dsa_switch *ds)
- of_node_put(phy_node);
- break;
- }
-+
-+ mt7530_setup_port5(ds, interface);
- }
-
- #ifdef CONFIG_GPIOLIB
-@@ -2579,8 +2580,6 @@ mt7530_setup(struct dsa_switch *ds)
- }
- #endif /* CONFIG_GPIOLIB */
-
-- mt7530_setup_port5(ds, interface);
--
- /* Flush the FDB table */
- ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL);
- if (ret < 0)
+++ /dev/null
-From cd1cee68e57eedb460a68d1f42abf9f740b17e94 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Jan 2024 08:35:57 +0300
-Subject: [PATCH 10/30] net: dsa: mt7530: do not set priv->p5_interface on
- mt7530_setup_port5()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Running mt7530_setup_port5() from mt7530_setup() used to handle all cases
-of configuring port 5, including phylink.
-
-Setting priv->p5_interface under mt7530_setup_port5() makes sure that
-mt7530_setup_port5() from mt753x_phylink_mac_config() won't run.
-
-The commit ("net: dsa: mt7530: improve code path for setting up port 5")
-makes so that mt7530_setup_port5() from mt7530_setup() runs only on
-non-phylink cases.
-
-Get rid of unnecessarily setting priv->p5_interface under
-mt7530_setup_port5() as port 5 phylink configuration will be done by
-running mt7530_setup_port5() from mt753x_phylink_mac_config() now.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-6-042401f2b279@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 2 --
- 1 file changed, 2 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -978,8 +978,6 @@ static void mt7530_setup_port5(struct ds
- dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, intf_sel=%s, phy-mode=%s\n",
- val, p5_intf_modes(priv->p5_intf_sel), phy_modes(interface));
-
-- priv->p5_interface = interface;
--
- unlock_exit:
- mutex_unlock(&priv->reg_mutex);
- }
+++ /dev/null
-From e55a68aeb0f8b9c74b582b7a5e92b82988832bf8 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Jan 2024 08:35:58 +0300
-Subject: [PATCH 11/30] net: dsa: mt7530: do not run mt7530_setup_port5() if
- port 5 is disabled
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There's no need to run all the code on mt7530_setup_port5() if port 5 is
-disabled. The only case for calling mt7530_setup_port5() from
-mt7530_setup() is when PHY muxing is enabled. That is because port 5 is not
-defined as a port on the devicetree, therefore, it cannot be controlled by
-phylink.
-
-Because of this, run mt7530_setup_port5() if priv->p5_intf_sel is
-P5_INTF_SEL_PHY_P0 or P5_INTF_SEL_PHY_P4. Remove the P5_DISABLED case from
-mt7530_setup_port5().
-
-Stop initialising the interface variable as the remaining cases will always
-call mt7530_setup_port5() with it initialised.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-7-042401f2b279@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 9 +++------
- 1 file changed, 3 insertions(+), 6 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -942,9 +942,6 @@ static void mt7530_setup_port5(struct ds
- /* MT7530_P5_MODE_GMAC: P5 -> External phy or 2nd GMAC */
- val &= ~MHWTRAP_P5_DIS;
- break;
-- case P5_DISABLED:
-- interface = PHY_INTERFACE_MODE_NA;
-- break;
- default:
- dev_err(ds->dev, "Unsupported p5_intf_sel %d\n",
- priv->p5_intf_sel);
-@@ -2534,8 +2531,6 @@ mt7530_setup(struct dsa_switch *ds)
- * Set priv->p5_intf_sel to the appropriate value if PHY muxing
- * is detected.
- */
-- interface = PHY_INTERFACE_MODE_NA;
--
- for_each_child_of_node(dn, mac_np) {
- if (!of_device_is_compatible(mac_np,
- "mediatek,eth-mac"))
-@@ -2567,7 +2562,9 @@ mt7530_setup(struct dsa_switch *ds)
- break;
- }
-
-- mt7530_setup_port5(ds, interface);
-+ if (priv->p5_intf_sel == P5_INTF_SEL_PHY_P0 ||
-+ priv->p5_intf_sel == P5_INTF_SEL_PHY_P4)
-+ mt7530_setup_port5(ds, interface);
- }
-
- #ifdef CONFIG_GPIOLIB
+++ /dev/null
-From 1f538cda24bcb69919da2fcac0211b66281d3d4e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Tue, 6 Feb 2024 01:08:02 +0300
-Subject: [PATCH 12/30] net: dsa: mt7530: empty default case on
- mt7530_setup_port5()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There're two code paths for setting up port 5:
-
-mt7530_setup()
--> mt7530_setup_port5()
-
-mt753x_phylink_mac_config()
--> mt753x_mac_config()
- -> mt7530_mac_config()
- -> mt7530_setup_port5()
-
-On the first code path, priv->p5_intf_sel is either set to
-P5_INTF_SEL_PHY_P0 or P5_INTF_SEL_PHY_P4 when mt7530_setup_port5() is run.
-
-On the second code path, priv->p5_intf_sel is set to P5_INTF_SEL_GMAC5 when
-mt7530_setup_port5() is run.
-
-Empty the default case which will never run but is needed nonetheless to
-handle all the remaining enumeration values.
-
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-1-d7d92a185cb1@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 5 +----
- 1 file changed, 1 insertion(+), 4 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -943,9 +943,7 @@ static void mt7530_setup_port5(struct ds
- val &= ~MHWTRAP_P5_DIS;
- break;
- default:
-- dev_err(ds->dev, "Unsupported p5_intf_sel %d\n",
-- priv->p5_intf_sel);
-- goto unlock_exit;
-+ break;
- }
-
- /* Setup RGMII settings */
-@@ -975,7 +973,6 @@ static void mt7530_setup_port5(struct ds
- dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, intf_sel=%s, phy-mode=%s\n",
- val, p5_intf_modes(priv->p5_intf_sel), phy_modes(interface));
-
--unlock_exit:
- mutex_unlock(&priv->reg_mutex);
- }
-
+++ /dev/null
-From 12c511cd31c2dc6bd96e4a89f7709d515aa8a76b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Tue, 6 Feb 2024 01:08:03 +0300
-Subject: [PATCH 13/30] net: dsa: mt7530: move XTAL check to mt7530_setup()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The crystal frequency concerns the switch core. The frequency should be
-checked when the switch is being set up so the driver can reject the
-unsupported hardware earlier and without requiring port 6 to be used.
-
-Move it to mt7530_setup(). Drop the unnecessary function printing.
-
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-2-d7d92a185cb1@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 13 ++++++-------
- 1 file changed, 6 insertions(+), 7 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -422,13 +422,6 @@ mt7530_pad_clk_setup(struct dsa_switch *
-
- xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK;
-
-- if (xtal == HWTRAP_XTAL_20MHZ) {
-- dev_err(priv->dev,
-- "%s: MT7530 with a 20MHz XTAL is not supported!\n",
-- __func__);
-- return -EINVAL;
-- }
--
- switch (interface) {
- case PHY_INTERFACE_MODE_RGMII:
- trgint = 0;
-@@ -2458,6 +2451,12 @@ mt7530_setup(struct dsa_switch *ds)
- return -ENODEV;
- }
-
-+ if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_20MHZ) {
-+ dev_err(priv->dev,
-+ "MT7530 with a 20MHz XTAL is not supported!\n");
-+ return -EINVAL;
-+ }
-+
- /* Reset the switch through internal reset */
- mt7530_write(priv, MT7530_SYS_CTRL,
- SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
+++ /dev/null
-From c33899a6a8c1a5723afbfc075600aba2e2bdbea7 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Tue, 6 Feb 2024 01:08:04 +0300
-Subject: [PATCH 14/30] net: dsa: mt7530: simplify mt7530_pad_clk_setup()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This code is from before this driver was converted to phylink API. Phylink
-deals with the unsupported interface cases before mt7530_pad_clk_setup() is
-run. Therefore, the default case would never run. However, it must be
-defined nonetheless to handle all the remaining enumeration values, the
-phy-modes.
-
-Switch to if statement for RGMII and return which simplifies the code and
-saves an indent.
-
-Set P6_INTF_MODE, which is the three least significant bits of the
-MT7530_P6ECR register, to 0 for RGMII even though it will already be 0
-after reset. This is to keep supporting dynamic reconfiguration of the port
-in the case the interface changes from TRGMII to RGMII.
-
-Disable the TRGMII clocks for all cases. They will be enabled if TRGMII is
-being used.
-
-Read XTAL after checking for RGMII as it's only needed for the TRGMII
-interface mode.
-
-Reviewed-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-3-d7d92a185cb1@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 91 ++++++++++++++++++----------------------
- 1 file changed, 40 insertions(+), 51 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -418,65 +418,54 @@ static int
- mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)
- {
- struct mt7530_priv *priv = ds->priv;
-- u32 ncpo1, ssc_delta, trgint, xtal;
-+ u32 ncpo1, ssc_delta, xtal;
-
-- xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK;
-+ /* Disable the MT7530 TRGMII clocks */
-+ core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
-
-- switch (interface) {
-- case PHY_INTERFACE_MODE_RGMII:
-- trgint = 0;
-- break;
-- case PHY_INTERFACE_MODE_TRGMII:
-- trgint = 1;
-- if (xtal == HWTRAP_XTAL_25MHZ)
-- ssc_delta = 0x57;
-- else
-- ssc_delta = 0x87;
-- if (priv->id == ID_MT7621) {
-- /* PLL frequency: 125MHz: 1.0GBit */
-- if (xtal == HWTRAP_XTAL_40MHZ)
-- ncpo1 = 0x0640;
-- if (xtal == HWTRAP_XTAL_25MHZ)
-- ncpo1 = 0x0a00;
-- } else { /* PLL frequency: 250MHz: 2.0Gbit */
-- if (xtal == HWTRAP_XTAL_40MHZ)
-- ncpo1 = 0x0c80;
-- if (xtal == HWTRAP_XTAL_25MHZ)
-- ncpo1 = 0x1400;
-- }
-- break;
-- default:
-- dev_err(priv->dev, "xMII interface %d not supported\n",
-- interface);
-- return -EINVAL;
-+ if (interface == PHY_INTERFACE_MODE_RGMII) {
-+ mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK,
-+ P6_INTF_MODE(0));
-+ return 0;
- }
-
-- mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK,
-- P6_INTF_MODE(trgint));
-+ mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, P6_INTF_MODE(1));
-
-- if (trgint) {
-- /* Disable the MT7530 TRGMII clocks */
-- core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
--
-- /* Setup the MT7530 TRGMII Tx Clock */
-- core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1));
-- core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0));
-- core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta));
-- core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta));
-- core_write(priv, CORE_PLL_GROUP4,
-- RG_SYSPLL_DDSFBK_EN | RG_SYSPLL_BIAS_EN |
-- RG_SYSPLL_BIAS_LPF_EN);
-- core_write(priv, CORE_PLL_GROUP2,
-- RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN |
-- RG_SYSPLL_POSDIV(1));
-- core_write(priv, CORE_PLL_GROUP7,
-- RG_LCDDS_PCW_NCPO_CHG | RG_LCCDS_C(3) |
-- RG_LCDDS_PWDB | RG_LCDDS_ISO_EN);
-+ xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK;
-
-- /* Enable the MT7530 TRGMII clocks */
-- core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
-+ if (xtal == HWTRAP_XTAL_25MHZ)
-+ ssc_delta = 0x57;
-+ else
-+ ssc_delta = 0x87;
-+
-+ if (priv->id == ID_MT7621) {
-+ /* PLL frequency: 125MHz: 1.0GBit */
-+ if (xtal == HWTRAP_XTAL_40MHZ)
-+ ncpo1 = 0x0640;
-+ if (xtal == HWTRAP_XTAL_25MHZ)
-+ ncpo1 = 0x0a00;
-+ } else { /* PLL frequency: 250MHz: 2.0Gbit */
-+ if (xtal == HWTRAP_XTAL_40MHZ)
-+ ncpo1 = 0x0c80;
-+ if (xtal == HWTRAP_XTAL_25MHZ)
-+ ncpo1 = 0x1400;
- }
-
-+ /* Setup the MT7530 TRGMII Tx Clock */
-+ core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1));
-+ core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0));
-+ core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta));
-+ core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta));
-+ core_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN |
-+ RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN);
-+ core_write(priv, CORE_PLL_GROUP2, RG_SYSPLL_EN_NORMAL |
-+ RG_SYSPLL_VODEN | RG_SYSPLL_POSDIV(1));
-+ core_write(priv, CORE_PLL_GROUP7, RG_LCDDS_PCW_NCPO_CHG |
-+ RG_LCCDS_C(3) | RG_LCDDS_PWDB | RG_LCDDS_ISO_EN);
-+
-+ /* Enable the MT7530 TRGMII clocks */
-+ core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
-+
- return 0;
- }
-
+++ /dev/null
-From e612922de7070a28802216650ee88128a57290de Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Tue, 6 Feb 2024 01:08:05 +0300
-Subject: [PATCH 15/30] net: dsa: mt7530: call port 6 setup from
- mt7530_mac_config()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-mt7530_pad_clk_setup() is called if port 6 is enabled. It used to do more
-things than setting up port 6. That part was moved to more appropriate
-locations, mt7530_setup() and mt7530_pll_setup().
-
-Now that all it does is set up port 6, rename it to mt7530_setup_port6(),
-and move it to a more appropriate location, under mt7530_mac_config().
-
-Change mt7530_setup_port6() to void as there're no error cases.
-
-Leave an empty mt7530_pad_clk_setup() to satisfy the pad_setup function
-pointer.
-
-This is the code path for setting up the ports before:
-
-dsa_switch_ops :: phylink_mac_config() -> mt753x_phylink_mac_config()
--> mt753x_mac_config()
- -> mt753x_info :: mac_port_config() -> mt7530_mac_config()
- -> mt7530_setup_port5()
--> mt753x_pad_setup()
- -> mt753x_info :: pad_setup() -> mt7530_pad_clk_setup()
-
-This is after:
-
-dsa_switch_ops :: phylink_mac_config() -> mt753x_phylink_mac_config()
--> mt753x_mac_config()
- -> mt753x_info :: mac_port_config() -> mt7530_mac_config()
- -> mt7530_setup_port5()
- -> mt7530_setup_port6()
-
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-4-d7d92a185cb1@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 19 +++++++++++--------
- 1 file changed, 11 insertions(+), 8 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -414,8 +414,8 @@ mt753x_preferred_default_local_cpu_port(
- }
-
- /* Setup port 6 interface mode and TRGMII TX circuit */
--static int
--mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)
-+static void
-+mt7530_setup_port6(struct dsa_switch *ds, phy_interface_t interface)
- {
- struct mt7530_priv *priv = ds->priv;
- u32 ncpo1, ssc_delta, xtal;
-@@ -426,7 +426,7 @@ mt7530_pad_clk_setup(struct dsa_switch *
- if (interface == PHY_INTERFACE_MODE_RGMII) {
- mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK,
- P6_INTF_MODE(0));
-- return 0;
-+ return;
- }
-
- mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, P6_INTF_MODE(1));
-@@ -465,7 +465,11 @@ mt7530_pad_clk_setup(struct dsa_switch *
-
- /* Enable the MT7530 TRGMII clocks */
- core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
-+}
-
-+static int
-+mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)
-+{
- return 0;
- }
-
-@@ -2822,11 +2826,10 @@ mt7530_mac_config(struct dsa_switch *ds,
- {
- struct mt7530_priv *priv = ds->priv;
-
-- /* Only need to setup port5. */
-- if (port != 5)
-- return 0;
--
-- mt7530_setup_port5(priv->ds, interface);
-+ if (port == 5)
-+ mt7530_setup_port5(priv->ds, interface);
-+ else if (port == 6)
-+ mt7530_setup_port6(priv->ds, interface);
-
- return 0;
- }
+++ /dev/null
-From af83e0c7d766078fcd5580c0c81b9e5b55ff5906 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Tue, 6 Feb 2024 01:08:06 +0300
-Subject: [PATCH 16/30] net: dsa: mt7530: remove pad_setup function pointer
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The pad_setup function pointer was introduced with 88bdef8be9f6 ("net: dsa:
-mt7530: Extend device data ready for adding a new hardware"). It was being
-used to set up the core clock and port 6 of the MT7530 switch, and pll of
-the MT7531 switch.
-
-All of these were moved to more appropriate locations, and it was never
-used for the switch on the MT7988 SoC. Therefore, this function pointer
-hasn't got a use anymore. Remove it.
-
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-5-d7d92a185cb1@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 36 ++----------------------------------
- drivers/net/dsa/mt7530.h | 3 ---
- 2 files changed, 2 insertions(+), 37 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -467,18 +467,6 @@ mt7530_setup_port6(struct dsa_switch *ds
- core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
- }
-
--static int
--mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)
--{
-- return 0;
--}
--
--static int
--mt7531_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
--{
-- return 0;
--}
--
- static void
- mt7531_pll_setup(struct mt7530_priv *priv)
- {
-@@ -2813,14 +2801,6 @@ static void mt7988_mac_port_get_caps(str
- }
-
- static int
--mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state)
--{
-- struct mt7530_priv *priv = ds->priv;
--
-- return priv->info->pad_setup(ds, state->interface);
--}
--
--static int
- mt7530_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
- phy_interface_t interface)
- {
-@@ -2985,8 +2965,6 @@ mt753x_phylink_mac_config(struct dsa_swi
- if (priv->p6_interface == state->interface)
- break;
-
-- mt753x_pad_setup(ds, state);
--
- if (mt753x_mac_config(ds, port, mode, state) < 0)
- goto unsupported;
-
-@@ -3303,11 +3281,6 @@ mt753x_conduit_state_change(struct dsa_s
- mt7530_rmw(priv, MT7530_MFC, CPU_EN | CPU_PORT_MASK, val);
- }
-
--static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
--{
-- return 0;
--}
--
- static int mt7988_setup(struct dsa_switch *ds)
- {
- struct mt7530_priv *priv = ds->priv;
-@@ -3371,7 +3344,6 @@ const struct mt753x_info mt753x_table[]
- .phy_write_c22 = mt7530_phy_write_c22,
- .phy_read_c45 = mt7530_phy_read_c45,
- .phy_write_c45 = mt7530_phy_write_c45,
-- .pad_setup = mt7530_pad_clk_setup,
- .mac_port_get_caps = mt7530_mac_port_get_caps,
- .mac_port_config = mt7530_mac_config,
- },
-@@ -3383,7 +3355,6 @@ const struct mt753x_info mt753x_table[]
- .phy_write_c22 = mt7530_phy_write_c22,
- .phy_read_c45 = mt7530_phy_read_c45,
- .phy_write_c45 = mt7530_phy_write_c45,
-- .pad_setup = mt7530_pad_clk_setup,
- .mac_port_get_caps = mt7530_mac_port_get_caps,
- .mac_port_config = mt7530_mac_config,
- },
-@@ -3395,7 +3366,6 @@ const struct mt753x_info mt753x_table[]
- .phy_write_c22 = mt7531_ind_c22_phy_write,
- .phy_read_c45 = mt7531_ind_c45_phy_read,
- .phy_write_c45 = mt7531_ind_c45_phy_write,
-- .pad_setup = mt7531_pad_setup,
- .cpu_port_config = mt7531_cpu_port_config,
- .mac_port_get_caps = mt7531_mac_port_get_caps,
- .mac_port_config = mt7531_mac_config,
-@@ -3408,7 +3378,6 @@ const struct mt753x_info mt753x_table[]
- .phy_write_c22 = mt7531_ind_c22_phy_write,
- .phy_read_c45 = mt7531_ind_c45_phy_read,
- .phy_write_c45 = mt7531_ind_c45_phy_write,
-- .pad_setup = mt7988_pad_setup,
- .cpu_port_config = mt7988_cpu_port_config,
- .mac_port_get_caps = mt7988_mac_port_get_caps,
- .mac_port_config = mt7988_mac_config,
-@@ -3438,9 +3407,8 @@ mt7530_probe_common(struct mt7530_priv *
- /* Sanity check if these required device operations are filled
- * properly.
- */
-- if (!priv->info->sw_setup || !priv->info->pad_setup ||
-- !priv->info->phy_read_c22 || !priv->info->phy_write_c22 ||
-- !priv->info->mac_port_get_caps ||
-+ if (!priv->info->sw_setup || !priv->info->phy_read_c22 ||
-+ !priv->info->phy_write_c22 || !priv->info->mac_port_get_caps ||
- !priv->info->mac_port_config)
- return -EINVAL;
-
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -734,8 +734,6 @@ struct mt753x_pcs {
- * @phy_write_c22: Holding the way writing PHY port using C22
- * @phy_read_c45: Holding the way reading PHY port using C45
- * @phy_write_c45: Holding the way writing PHY port using C45
-- * @pad_setup: Holding the way setting up the bus pad for a certain
-- * MAC port
- * @phy_mode_supported: Check if the PHY type is being supported on a certain
- * port
- * @mac_port_validate: Holding the way to set addition validate type for a
-@@ -756,7 +754,6 @@ struct mt753x_info {
- int regnum);
- int (*phy_write_c45)(struct mt7530_priv *priv, int port, int devad,
- int regnum, u16 val);
-- int (*pad_setup)(struct dsa_switch *ds, phy_interface_t interface);
- int (*cpu_port_config)(struct dsa_switch *ds, int port);
- void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
- struct phylink_config *config);
+++ /dev/null
-From 9716e3e2c21547c97a9d79119da8fdce5659c2cc Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Tue, 6 Feb 2024 01:08:07 +0300
-Subject: [PATCH 17/30] net: dsa: mt7530: correct port capabilities of MT7988
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-On the switch on the MT7988 SoC, as shown in Block Diagram 8.1.1.3 on page
-125 of "MT7988A Wi-Fi 7 Generation Router Platform: Datasheet (Open
-Version) v0.1", there are only 4 PHYs. That's port 0 to 3. Set the case for
-ports which connect to switch PHYs to '0 ... 3'.
-
-Port 4 and 5 are not used at all in this design.
-
-Link: https://wiki.banana-pi.org/Banana_Pi_BPI-R4#Documents [1]
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-6-d7d92a185cb1@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2786,7 +2786,7 @@ static void mt7988_mac_port_get_caps(str
-
- switch (port) {
- /* Ports which are connected to switch PHYs. There is no MII pinout. */
-- case 0 ... 4:
-+ case 0 ... 3:
- __set_bit(PHY_INTERFACE_MODE_INTERNAL,
- config->supported_interfaces);
- break;
+++ /dev/null
-From 4d7b17712513710778c0f2f83ea5d9b55ed58c36 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Tue, 6 Feb 2024 01:08:08 +0300
-Subject: [PATCH 18/30] net: dsa: mt7530: do not clear
- config->supported_interfaces
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There's no need to clear the config->supported_interfaces bitmap before
-reporting the supported interfaces as all bits in the bitmap will already
-be initialized to zero when the phylink_config structure is allocated. The
-"config" pointer points to &dp->phylink_config, and "dp" is allocated by
-dsa_port_touch() with kzalloc(), so all its fields are filled with zeroes.
-
-There's no code that would change the bitmap beforehand. Remove it.
-
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-7-d7d92a185cb1@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 2 --
- 1 file changed, 2 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2782,8 +2782,6 @@ static void mt7531_mac_port_get_caps(str
- static void mt7988_mac_port_get_caps(struct dsa_switch *ds, int port,
- struct phylink_config *config)
- {
-- phy_interface_zero(config->supported_interfaces);
--
- switch (port) {
- /* Ports which are connected to switch PHYs. There is no MII pinout. */
- case 0 ... 3:
+++ /dev/null
-From 69e689e28191f9a242de6821a85f2c5ae4dbd5ae Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Fri, 1 Mar 2024 12:42:57 +0200
-Subject: [PATCH 19/30] net: dsa: mt7530: remove .mac_port_config for MT7988
- and make it optional
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-For the switch on the MT7988 SoC, the mac_port_config member for ID_MT7988
-in mt753x_table is not needed as the interfaces of all MACs are already
-handled on mt7988_mac_port_get_caps().
-
-Therefore, remove the mac_port_config member from ID_MT7988 in
-mt753x_table. Before calling priv->info->mac_port_config(), if there's no
-mac_port_config member in mt753x_table, exit mt753x_mac_config()
-successfully.
-
-Remove calling priv->info->mac_port_config() from the sanity check as the
-sanity check requires a pointer to a mac_port_config function to be
-non-NULL. This will fail for MT7988 as mac_port_config won't be a member of
-its info table.
-
-Co-developed-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 18 ++++--------------
- 1 file changed, 4 insertions(+), 14 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2865,17 +2865,6 @@ static bool mt753x_is_mac_port(u32 port)
- }
-
- static int
--mt7988_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
-- phy_interface_t interface)
--{
-- if (dsa_is_cpu_port(ds, port) &&
-- interface == PHY_INTERFACE_MODE_INTERNAL)
-- return 0;
--
-- return -EINVAL;
--}
--
--static int
- mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
- phy_interface_t interface)
- {
-@@ -2915,6 +2904,9 @@ mt753x_mac_config(struct dsa_switch *ds,
- {
- struct mt7530_priv *priv = ds->priv;
-
-+ if (!priv->info->mac_port_config)
-+ return 0;
-+
- return priv->info->mac_port_config(ds, port, mode, state->interface);
- }
-
-@@ -3378,7 +3370,6 @@ const struct mt753x_info mt753x_table[]
- .phy_write_c45 = mt7531_ind_c45_phy_write,
- .cpu_port_config = mt7988_cpu_port_config,
- .mac_port_get_caps = mt7988_mac_port_get_caps,
-- .mac_port_config = mt7988_mac_config,
- },
- };
- EXPORT_SYMBOL_GPL(mt753x_table);
-@@ -3406,8 +3397,7 @@ mt7530_probe_common(struct mt7530_priv *
- * properly.
- */
- if (!priv->info->sw_setup || !priv->info->phy_read_c22 ||
-- !priv->info->phy_write_c22 || !priv->info->mac_port_get_caps ||
-- !priv->info->mac_port_config)
-+ !priv->info->phy_write_c22 || !priv->info->mac_port_get_caps)
- return -EINVAL;
-
- priv->id = priv->info->id;
+++ /dev/null
-From f8faa3a04ca860b31f22d7d526c5e3f3de511a8f Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Fri, 1 Mar 2024 12:42:58 +0200
-Subject: [PATCH 20/30] net: dsa: mt7530: set interrupt register only for
- MT7530
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Setting this register related to interrupts is only needed for the MT7530
-switch. Make an exclusive check to ensure this.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2254,7 +2254,7 @@ mt7530_setup_irq(struct mt7530_priv *pri
- }
-
- /* This register must be set for MT7530 to properly fire interrupts */
-- if (priv->id != ID_MT7531)
-+ if (priv->id == ID_MT7530 || priv->id == ID_MT7621)
- mt7530_set(priv, MT7530_TOP_SIG_CTRL, TOP_SIG_CTRL_NORMAL);
-
- ret = request_threaded_irq(priv->irq, NULL, mt7530_irq_thread_fn,
+++ /dev/null
-From 80f4f866d7dad41b12cf37476c38766a89b8b5c4 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Fri, 1 Mar 2024 12:42:59 +0200
-Subject: [PATCH 21/30] net: dsa: mt7530: do not use SW_PHY_RST to reset MT7531
- switch
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-According to the document MT7531 Reference Manual for Development Board
-v1.0, the SW_PHY_RST bit on the SYS_CTRL register doesn't exist for
-MT7531. This is likely why forcing link down on all ports is necessary for
-MT7531.
-
-Therefore, do not set SW_PHY_RST on mt7531_setup().
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 6 ++----
- 1 file changed, 2 insertions(+), 4 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2658,14 +2658,12 @@ mt7531_setup(struct dsa_switch *ds)
- val = mt7530_read(priv, MT7531_TOP_SIG_SR);
- priv->p5_sgmii = !!(val & PAD_DUAL_SGMII_EN);
-
-- /* all MACs must be forced link-down before sw reset */
-+ /* Force link down on all ports before internal reset */
- for (i = 0; i < MT7530_NUM_PORTS; i++)
- mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK);
-
- /* Reset the switch through internal reset */
-- mt7530_write(priv, MT7530_SYS_CTRL,
-- SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
-- SYS_CTRL_REG_RST);
-+ mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
-
- if (!priv->p5_sgmii) {
- mt7531_pll_setup(priv);
+++ /dev/null
-From 58670652cacb7c5752e01f29979d0ca4cdbfcc0a Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Fri, 1 Mar 2024 12:43:00 +0200
-Subject: [PATCH 22/30] net: dsa: mt7530: get rid of useless error returns on
- phylink code path
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Remove error returns on the cases where they are already handled with the
-function the mac_port_get_caps member in mt753x_table points to.
-
-mt7531_mac_config() is also called from mt7531_cpu_port_config() outside of
-phylink but the port and interface modes are already handled there.
-
-Change the functions and the mac_port_config function pointer to void now
-that there're no error returns anymore.
-
-Remove mt753x_is_mac_port() that used to help the said error returns.
-
-On mt7531_mac_config(), switch to if statements to simplify the code.
-
-Remove internal phy cases from mt753x_phylink_mac_config(), there is no
-need to check the interface mode as that's already handled with the
-function the mac_port_get_caps member in mt753x_table points to.
-
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 81 ++++++++--------------------------------
- drivers/net/dsa/mt7530.h | 6 +--
- 2 files changed, 19 insertions(+), 68 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2796,7 +2796,7 @@ static void mt7988_mac_port_get_caps(str
- }
- }
-
--static int
-+static void
- mt7530_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
- phy_interface_t interface)
- {
-@@ -2806,22 +2806,14 @@ mt7530_mac_config(struct dsa_switch *ds,
- mt7530_setup_port5(priv->ds, interface);
- else if (port == 6)
- mt7530_setup_port6(priv->ds, interface);
--
-- return 0;
- }
-
--static int mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port,
-- phy_interface_t interface,
-- struct phy_device *phydev)
-+static void mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port,
-+ phy_interface_t interface,
-+ struct phy_device *phydev)
- {
- u32 val;
-
-- if (priv->p5_sgmii) {
-- dev_err(priv->dev, "RGMII mode is not available for port %d\n",
-- port);
-- return -EINVAL;
-- }
--
- val = mt7530_read(priv, MT7531_CLKGEN_CTRL);
- val |= GP_CLK_EN;
- val &= ~GP_MODE_MASK;
-@@ -2849,20 +2841,14 @@ static int mt7531_rgmii_setup(struct mt7
- case PHY_INTERFACE_MODE_RGMII_ID:
- break;
- default:
-- return -EINVAL;
-+ break;
- }
- }
-- mt7530_write(priv, MT7531_CLKGEN_CTRL, val);
-
-- return 0;
--}
--
--static bool mt753x_is_mac_port(u32 port)
--{
-- return (port == 5 || port == 6);
-+ mt7530_write(priv, MT7531_CLKGEN_CTRL, val);
- }
-
--static int
-+static void
- mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
- phy_interface_t interface)
- {
-@@ -2870,42 +2856,21 @@ mt7531_mac_config(struct dsa_switch *ds,
- struct phy_device *phydev;
- struct dsa_port *dp;
-
-- if (!mt753x_is_mac_port(port)) {
-- dev_err(priv->dev, "port %d is not a MAC port\n", port);
-- return -EINVAL;
-- }
--
-- switch (interface) {
-- case PHY_INTERFACE_MODE_RGMII:
-- case PHY_INTERFACE_MODE_RGMII_ID:
-- case PHY_INTERFACE_MODE_RGMII_RXID:
-- case PHY_INTERFACE_MODE_RGMII_TXID:
-+ if (phy_interface_mode_is_rgmii(interface)) {
- dp = dsa_to_port(ds, port);
- phydev = dp->slave->phydev;
-- return mt7531_rgmii_setup(priv, port, interface, phydev);
-- case PHY_INTERFACE_MODE_SGMII:
-- case PHY_INTERFACE_MODE_NA:
-- case PHY_INTERFACE_MODE_1000BASEX:
-- case PHY_INTERFACE_MODE_2500BASEX:
-- /* handled in SGMII PCS driver */
-- return 0;
-- default:
-- return -EINVAL;
-+ mt7531_rgmii_setup(priv, port, interface, phydev);
- }
--
-- return -EINVAL;
- }
-
--static int
-+static void
- mt753x_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
- const struct phylink_link_state *state)
- {
- struct mt7530_priv *priv = ds->priv;
-
-- if (!priv->info->mac_port_config)
-- return 0;
--
-- return priv->info->mac_port_config(ds, port, mode, state->interface);
-+ if (priv->info->mac_port_config)
-+ priv->info->mac_port_config(ds, port, mode, state->interface);
- }
-
- static struct phylink_pcs *
-@@ -2934,17 +2899,11 @@ mt753x_phylink_mac_config(struct dsa_swi
- u32 mcr_cur, mcr_new;
-
- switch (port) {
-- case 0 ... 4:
-- if (state->interface != PHY_INTERFACE_MODE_GMII &&
-- state->interface != PHY_INTERFACE_MODE_INTERNAL)
-- goto unsupported;
-- break;
- case 5:
- if (priv->p5_interface == state->interface)
- break;
-
-- if (mt753x_mac_config(ds, port, mode, state) < 0)
-- goto unsupported;
-+ mt753x_mac_config(ds, port, mode, state);
-
- if (priv->p5_intf_sel != P5_DISABLED)
- priv->p5_interface = state->interface;
-@@ -2953,16 +2912,10 @@ mt753x_phylink_mac_config(struct dsa_swi
- if (priv->p6_interface == state->interface)
- break;
-
-- if (mt753x_mac_config(ds, port, mode, state) < 0)
-- goto unsupported;
-+ mt753x_mac_config(ds, port, mode, state);
-
- priv->p6_interface = state->interface;
- break;
-- default:
--unsupported:
-- dev_err(ds->dev, "%s: unsupported %s port: %i\n",
-- __func__, phy_modes(state->interface), port);
-- return;
- }
-
- mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
-@@ -3045,7 +2998,6 @@ mt7531_cpu_port_config(struct dsa_switch
- struct mt7530_priv *priv = ds->priv;
- phy_interface_t interface;
- int speed;
-- int ret;
-
- switch (port) {
- case 5:
-@@ -3070,9 +3022,8 @@ mt7531_cpu_port_config(struct dsa_switch
- else
- speed = SPEED_1000;
-
-- ret = mt7531_mac_config(ds, port, MLO_AN_FIXED, interface);
-- if (ret)
-- return ret;
-+ mt7531_mac_config(ds, port, MLO_AN_FIXED, interface);
-+
- mt7530_write(priv, MT7530_PMCR_P(port),
- PMCR_CPU_PORT_SETTING(priv->id));
- mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL,
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -760,9 +760,9 @@ struct mt753x_info {
- void (*mac_port_validate)(struct dsa_switch *ds, int port,
- phy_interface_t interface,
- unsigned long *supported);
-- int (*mac_port_config)(struct dsa_switch *ds, int port,
-- unsigned int mode,
-- phy_interface_t interface);
-+ void (*mac_port_config)(struct dsa_switch *ds, int port,
-+ unsigned int mode,
-+ phy_interface_t interface);
- };
-
- /* struct mt7530_priv - This is the main data structure for holding the state
+++ /dev/null
-From 859df5cf6ff07a9c930be4681284346aa73dd1fb Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Fri, 1 Mar 2024 12:43:01 +0200
-Subject: [PATCH 23/30] net: dsa: mt7530: get rid of
- priv->info->cpu_port_config()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-priv->info->cpu_port_config() is used for MT7531 and the switch on the
-MT7988 SoC. It sets up the ports described as a CPU port earlier than the
-phylink code path would do.
-
-This function is useless as:
-- Configuring the MACs can be done from the phylink_mac_config code path
- instead.
-- All the link configuration it does on the CPU ports are later undone with
- the port_enable, phylink_mac_config, and then phylink_mac_link_up code
- path [1].
-
-priv->p5_interface and priv->p6_interface were being used to prevent
-configuring the MACs from the phylink_mac_config code path. Remove them now
-that they hold no purpose.
-
-Remove priv->info->cpu_port_config(). On mt753x_phylink_mac_config, switch
-to if statements to simplify the code.
-
-Remove the overwriting of the speed and duplex interfaces for certain
-interface modes. Phylink already provides the speed and duplex variables
-with proper values. Phylink already sets the max speed of TRGMII to
-SPEED_1000. Add SPEED_2500 for PHY_INTERFACE_MODE_2500BASEX to where the
-speed and EEE bits are set instead.
-
-On the switch on the MT7988 SoC, PHY_INTERFACE_MODE_INTERNAL is being used
-to describe the interface mode of the 10G MAC, which is of port 6. On
-mt7988_cpu_port_config() PMCR_FORCE_SPEED_1000 was set via the
-PMCR_CPU_PORT_SETTING() mask. Add SPEED_10000 case to where the speed bits
-are set to cover this. No need to add it to where the EEE bits are set as
-the "MT7988A Wi-Fi 7 Generation Router Platform: Datasheet (Open Version)
-v0.1" document shows that these bits don't exist on the MT7530_PMCR_P(6)
-register.
-
-Remove the definition of PMCR_CPU_PORT_SETTING() now that it holds no
-purpose.
-
-Change mt753x_cpu_port_enable() to void now that there're no error cases
-left.
-
-Link: https://lore.kernel.org/netdev/ZHy2jQLesdYFMQtO@shell.armlinux.org.uk/ [1]
-Suggested-by: Russell King (Oracle) <linux@armlinux.org.uk>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 114 +++------------------------------------
- drivers/net/dsa/mt7530.h | 11 ----
- 2 files changed, 7 insertions(+), 118 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -1163,18 +1163,10 @@ mt753x_trap_frames(struct mt7530_priv *p
- MT753X_BPDU_CPU_ONLY);
- }
-
--static int
-+static void
- mt753x_cpu_port_enable(struct dsa_switch *ds, int port)
- {
- struct mt7530_priv *priv = ds->priv;
-- int ret;
--
-- /* Setup max capability of CPU port at first */
-- if (priv->info->cpu_port_config) {
-- ret = priv->info->cpu_port_config(ds, port);
-- if (ret)
-- return ret;
-- }
-
- /* Enable Mediatek header mode on the cpu port */
- mt7530_write(priv, MT7530_PVC_P(port),
-@@ -1200,8 +1192,6 @@ mt753x_cpu_port_enable(struct dsa_switch
- /* Set to fallback mode for independent VLAN learning */
- mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
- MT7530_PORT_FALLBACK_MODE);
--
-- return 0;
- }
-
- static int
-@@ -2458,8 +2448,6 @@ mt7530_setup(struct dsa_switch *ds)
- val |= MHWTRAP_MANUAL;
- mt7530_write(priv, MT7530_MHWTRAP, val);
-
-- priv->p6_interface = PHY_INTERFACE_MODE_NA;
--
- if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_40MHZ)
- mt7530_pll_setup(priv);
-
-@@ -2477,9 +2465,7 @@ mt7530_setup(struct dsa_switch *ds)
- mt7530_set(priv, MT7530_PSC_P(i), SA_DIS);
-
- if (dsa_is_cpu_port(ds, i)) {
-- ret = mt753x_cpu_port_enable(ds, i);
-- if (ret)
-- return ret;
-+ mt753x_cpu_port_enable(ds, i);
- } else {
- mt7530_port_disable(ds, i);
-
-@@ -2586,9 +2572,7 @@ mt7531_setup_common(struct dsa_switch *d
- mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR);
-
- if (dsa_is_cpu_port(ds, i)) {
-- ret = mt753x_cpu_port_enable(ds, i);
-- if (ret)
-- return ret;
-+ mt753x_cpu_port_enable(ds, i);
- } else {
- mt7530_port_disable(ds, i);
-
-@@ -2681,10 +2665,6 @@ mt7531_setup(struct dsa_switch *ds)
- mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK,
- MT7531_GPIO0_INTERRUPT);
-
-- /* Let phylink decide the interface later. */
-- priv->p5_interface = PHY_INTERFACE_MODE_NA;
-- priv->p6_interface = PHY_INTERFACE_MODE_NA;
--
- /* Enable Energy-Efficient Ethernet (EEE) and PHY core PLL, since
- * phy_device has not yet been created provided for
- * phy_[read,write]_mmd_indirect is called, we provide our own
-@@ -2898,26 +2878,9 @@ mt753x_phylink_mac_config(struct dsa_swi
- struct mt7530_priv *priv = ds->priv;
- u32 mcr_cur, mcr_new;
-
-- switch (port) {
-- case 5:
-- if (priv->p5_interface == state->interface)
-- break;
--
-+ if (port == 5 || port == 6)
- mt753x_mac_config(ds, port, mode, state);
-
-- if (priv->p5_intf_sel != P5_DISABLED)
-- priv->p5_interface = state->interface;
-- break;
-- case 6:
-- if (priv->p6_interface == state->interface)
-- break;
--
-- mt753x_mac_config(ds, port, mode, state);
--
-- priv->p6_interface = state->interface;
-- break;
-- }
--
- mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
- mcr_new = mcr_cur;
- mcr_new &= ~PMCR_LINK_SETTINGS_MASK;
-@@ -2953,17 +2916,10 @@ static void mt753x_phylink_mac_link_up(s
-
- mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK;
-
-- /* MT753x MAC works in 1G full duplex mode for all up-clocked
-- * variants.
-- */
-- if (interface == PHY_INTERFACE_MODE_TRGMII ||
-- (phy_interface_mode_is_8023z(interface))) {
-- speed = SPEED_1000;
-- duplex = DUPLEX_FULL;
-- }
--
- switch (speed) {
- case SPEED_1000:
-+ case SPEED_2500:
-+ case SPEED_10000:
- mcr |= PMCR_FORCE_SPEED_1000;
- break;
- case SPEED_100:
-@@ -2981,6 +2937,7 @@ static void mt753x_phylink_mac_link_up(s
- if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) {
- switch (speed) {
- case SPEED_1000:
-+ case SPEED_2500:
- mcr |= PMCR_FORCE_EEE1G;
- break;
- case SPEED_100:
-@@ -2992,61 +2949,6 @@ static void mt753x_phylink_mac_link_up(s
- mt7530_set(priv, MT7530_PMCR_P(port), mcr);
- }
-
--static int
--mt7531_cpu_port_config(struct dsa_switch *ds, int port)
--{
-- struct mt7530_priv *priv = ds->priv;
-- phy_interface_t interface;
-- int speed;
--
-- switch (port) {
-- case 5:
-- if (!priv->p5_sgmii)
-- interface = PHY_INTERFACE_MODE_RGMII;
-- else
-- interface = PHY_INTERFACE_MODE_2500BASEX;
--
-- priv->p5_interface = interface;
-- break;
-- case 6:
-- interface = PHY_INTERFACE_MODE_2500BASEX;
--
-- priv->p6_interface = interface;
-- break;
-- default:
-- return -EINVAL;
-- }
--
-- if (interface == PHY_INTERFACE_MODE_2500BASEX)
-- speed = SPEED_2500;
-- else
-- speed = SPEED_1000;
--
-- mt7531_mac_config(ds, port, MLO_AN_FIXED, interface);
--
-- mt7530_write(priv, MT7530_PMCR_P(port),
-- PMCR_CPU_PORT_SETTING(priv->id));
-- mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL,
-- speed, DUPLEX_FULL, true, true);
--
-- return 0;
--}
--
--static int
--mt7988_cpu_port_config(struct dsa_switch *ds, int port)
--{
-- struct mt7530_priv *priv = ds->priv;
--
-- mt7530_write(priv, MT7530_PMCR_P(port),
-- PMCR_CPU_PORT_SETTING(priv->id));
--
-- mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED,
-- PHY_INTERFACE_MODE_INTERNAL, NULL,
-- SPEED_10000, DUPLEX_FULL, true, true);
--
-- return 0;
--}
--
- static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
- struct phylink_config *config)
- {
-@@ -3305,7 +3207,6 @@ const struct mt753x_info mt753x_table[]
- .phy_write_c22 = mt7531_ind_c22_phy_write,
- .phy_read_c45 = mt7531_ind_c45_phy_read,
- .phy_write_c45 = mt7531_ind_c45_phy_write,
-- .cpu_port_config = mt7531_cpu_port_config,
- .mac_port_get_caps = mt7531_mac_port_get_caps,
- .mac_port_config = mt7531_mac_config,
- },
-@@ -3317,7 +3218,6 @@ const struct mt753x_info mt753x_table[]
- .phy_write_c22 = mt7531_ind_c22_phy_write,
- .phy_read_c45 = mt7531_ind_c45_phy_read,
- .phy_write_c45 = mt7531_ind_c45_phy_write,
-- .cpu_port_config = mt7988_cpu_port_config,
- .mac_port_get_caps = mt7988_mac_port_get_caps,
- },
- };
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -340,13 +340,6 @@ enum mt7530_vlan_port_acc_frm {
- PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
- PMCR_FORCE_FDX | PMCR_FORCE_LNK | \
- PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100)
--#define PMCR_CPU_PORT_SETTING(id) (PMCR_FORCE_MODE_ID((id)) | \
-- PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \
-- PMCR_BACKOFF_EN | PMCR_BACKPR_EN | \
-- PMCR_TX_EN | PMCR_RX_EN | \
-- PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
-- PMCR_FORCE_SPEED_1000 | \
-- PMCR_FORCE_FDX | PMCR_FORCE_LNK)
-
- #define MT7530_PMEEECR_P(x) (0x3004 + (x) * 0x100)
- #define WAKEUP_TIME_1000(x) (((x) & 0xFF) << 24)
-@@ -754,7 +747,6 @@ struct mt753x_info {
- int regnum);
- int (*phy_write_c45)(struct mt7530_priv *priv, int port, int devad,
- int regnum, u16 val);
-- int (*cpu_port_config)(struct dsa_switch *ds, int port);
- void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
- struct phylink_config *config);
- void (*mac_port_validate)(struct dsa_switch *ds, int port,
-@@ -780,7 +772,6 @@ struct mt753x_info {
- * @ports: Holding the state among ports
- * @reg_mutex: The lock for protecting among process accessing
- * registers
-- * @p6_interface Holding the current port 6 interface
- * @p5_intf_sel: Holding the current port 5 interface select
- * @p5_sgmii: Flag for distinguishing if port 5 of the MT7531 switch
- * has got SGMII
-@@ -802,8 +793,6 @@ struct mt7530_priv {
- const struct mt753x_info *info;
- unsigned int id;
- bool mcm;
-- phy_interface_t p6_interface;
-- phy_interface_t p5_interface;
- enum p5_interface_select p5_intf_sel;
- bool p5_sgmii;
- u8 mirror_rx;
+++ /dev/null
-From c74a98baa8d098157975b3f94e496dd3a73e0864 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Fri, 1 Mar 2024 12:43:02 +0200
-Subject: [PATCH 24/30] net: dsa: mt7530: get rid of mt753x_mac_config()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There is no need for a separate function to call
-priv->info->mac_port_config(). Call it from mt753x_phylink_mac_config()
-instead and remove mt753x_mac_config().
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 14 ++------------
- 1 file changed, 2 insertions(+), 12 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2843,16 +2843,6 @@ mt7531_mac_config(struct dsa_switch *ds,
- }
- }
-
--static void
--mt753x_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
-- const struct phylink_link_state *state)
--{
-- struct mt7530_priv *priv = ds->priv;
--
-- if (priv->info->mac_port_config)
-- priv->info->mac_port_config(ds, port, mode, state->interface);
--}
--
- static struct phylink_pcs *
- mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
- phy_interface_t interface)
-@@ -2878,8 +2868,8 @@ mt753x_phylink_mac_config(struct dsa_swi
- struct mt7530_priv *priv = ds->priv;
- u32 mcr_cur, mcr_new;
-
-- if (port == 5 || port == 6)
-- mt753x_mac_config(ds, port, mode, state);
-+ if ((port == 5 || port == 6) && priv->info->mac_port_config)
-+ priv->info->mac_port_config(ds, port, mode, state->interface);
-
- mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
- mcr_new = mcr_cur;
+++ /dev/null
-From ab1ddb241bc1cb3d80aa51207810edd5cb0bbdc5 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Fri, 1 Mar 2024 12:43:03 +0200
-Subject: [PATCH 25/30] net: dsa: mt7530: put initialising PCS devices code
- back to original order
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The commit fae463084032 ("net: dsa: mt753x: fix pcs conversion regression")
-fixes regression caused by cpu_port_config manually calling phylink
-operations. cpu_port_config was deemed useless and was removed. Therefore,
-put initialising PCS devices code back to its original order.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 20 ++++++++++----------
- 1 file changed, 10 insertions(+), 10 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -3021,17 +3021,9 @@ static int
- mt753x_setup(struct dsa_switch *ds)
- {
- struct mt7530_priv *priv = ds->priv;
-- int i, ret;
-+ int ret = priv->info->sw_setup(ds);
-+ int i;
-
-- /* Initialise the PCS devices */
-- for (i = 0; i < priv->ds->num_ports; i++) {
-- priv->pcs[i].pcs.ops = priv->info->pcs_ops;
-- priv->pcs[i].pcs.neg_mode = true;
-- priv->pcs[i].priv = priv;
-- priv->pcs[i].port = i;
-- }
--
-- ret = priv->info->sw_setup(ds);
- if (ret)
- return ret;
-
-@@ -3043,6 +3035,14 @@ mt753x_setup(struct dsa_switch *ds)
- if (ret && priv->irq)
- mt7530_free_irq_common(priv);
-
-+ /* Initialise the PCS devices */
-+ for (i = 0; i < priv->ds->num_ports; i++) {
-+ priv->pcs[i].pcs.ops = priv->info->pcs_ops;
-+ priv->pcs[i].pcs.neg_mode = true;
-+ priv->pcs[i].priv = priv;
-+ priv->pcs[i].port = i;
-+ }
-+
- if (priv->create_sgmii) {
- ret = priv->create_sgmii(priv);
- if (ret && priv->irq)
+++ /dev/null
-From aa474698f75f4790a4de2052dd487736d2361b2e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Fri, 1 Mar 2024 12:43:04 +0200
-Subject: [PATCH 26/30] net: dsa: mt7530: sort link settings ops and force link
- down on all ports
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-port_enable and port_disable clears the link settings. Move that to
-mt7530_setup() and mt7531_setup_common() which set up the switches. This
-way, the link settings are cleared on all ports at setup, and then only
-once with phylink_mac_link_down() when a link goes down.
-
-Enable force mode at setup to apply the force part of the link settings.
-This ensures that disabled ports will have their link down.
-
-Suggested-by: Vladimir Oltean <olteanv@gmail.com>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 14 ++++++++++++--
- 1 file changed, 12 insertions(+), 2 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -1215,7 +1215,6 @@ mt7530_port_enable(struct dsa_switch *ds
- priv->ports[port].enable = true;
- mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
- priv->ports[port].pm);
-- mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
-
- mutex_unlock(&priv->reg_mutex);
-
-@@ -1235,7 +1234,6 @@ mt7530_port_disable(struct dsa_switch *d
- priv->ports[port].enable = false;
- mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
- PCR_MATRIX_CLR);
-- mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
-
- mutex_unlock(&priv->reg_mutex);
- }
-@@ -2457,6 +2455,12 @@ mt7530_setup(struct dsa_switch *ds)
- mt7530_mib_reset(ds);
-
- for (i = 0; i < MT7530_NUM_PORTS; i++) {
-+ /* Clear link settings and enable force mode to force link down
-+ * on all ports until they're enabled later.
-+ */
-+ mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
-+ PMCR_FORCE_MODE, PMCR_FORCE_MODE);
-+
- /* Disable forwarding by default on all ports */
- mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
- PCR_MATRIX_CLR);
-@@ -2562,6 +2566,12 @@ mt7531_setup_common(struct dsa_switch *d
- UNU_FFP_MASK);
-
- for (i = 0; i < MT7530_NUM_PORTS; i++) {
-+ /* Clear link settings and enable force mode to force link down
-+ * on all ports until they're enabled later.
-+ */
-+ mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
-+ MT7531_FORCE_MODE, MT7531_FORCE_MODE);
-+
- /* Disable forwarding by default on all ports */
- mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
- PCR_MATRIX_CLR);
+++ /dev/null
-From 1ca89c2e349d7c5e045911d741dacf4c83d029e7 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Fri, 1 Mar 2024 12:43:05 +0200
-Subject: [PATCH 27/30] net: dsa: mt7530: simplify link operations
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The "MT7621 Giga Switch Programming Guide v0.3", "MT7531 Reference Manual
-for Development Board v1.0", and "MT7988A Wi-Fi 7 Generation Router
-Platform: Datasheet (Open Version) v0.1" documents show that these bits are
-enabled at reset:
-
-PMCR_IFG_XMIT(1) (not part of PMCR_LINK_SETTINGS_MASK)
-PMCR_MAC_MODE (not part of PMCR_LINK_SETTINGS_MASK)
-PMCR_TX_EN
-PMCR_RX_EN
-PMCR_BACKOFF_EN (not part of PMCR_LINK_SETTINGS_MASK)
-PMCR_BACKPR_EN (not part of PMCR_LINK_SETTINGS_MASK)
-PMCR_TX_FC_EN
-PMCR_RX_FC_EN
-
-These bits also don't exist on the MT7530_PMCR_P(6) register of the switch
-on the MT7988 SoC:
-
-PMCR_IFG_XMIT()
-PMCR_MAC_MODE
-PMCR_BACKOFF_EN
-PMCR_BACKPR_EN
-
-Remove the setting of the bits not part of PMCR_LINK_SETTINGS_MASK on
-phylink_mac_config as they're already set.
-
-The bit for setting the port on force mode is already done on
-mt7530_setup() and mt7531_setup_common(). So get rid of
-PMCR_FORCE_MODE_ID() which helped determine which bit to use for the switch
-model.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 12 +-----------
- drivers/net/dsa/mt7530.h | 2 --
- 2 files changed, 1 insertion(+), 13 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2876,23 +2876,13 @@ mt753x_phylink_mac_config(struct dsa_swi
- const struct phylink_link_state *state)
- {
- struct mt7530_priv *priv = ds->priv;
-- u32 mcr_cur, mcr_new;
-
- if ((port == 5 || port == 6) && priv->info->mac_port_config)
- priv->info->mac_port_config(ds, port, mode, state->interface);
-
-- mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
-- mcr_new = mcr_cur;
-- mcr_new &= ~PMCR_LINK_SETTINGS_MASK;
-- mcr_new |= PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | PMCR_BACKOFF_EN |
-- PMCR_BACKPR_EN | PMCR_FORCE_MODE_ID(priv->id);
--
- /* Are we connected to external phy */
- if (port == 5 && dsa_is_user_port(ds, 5))
-- mcr_new |= PMCR_EXT_PHY;
--
-- if (mcr_new != mcr_cur)
-- mt7530_write(priv, MT7530_PMCR_P(port), mcr_new);
-+ mt7530_set(priv, MT7530_PMCR_P(port), PMCR_EXT_PHY);
- }
-
- static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port,
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -333,8 +333,6 @@ enum mt7530_vlan_port_acc_frm {
- MT7531_FORCE_DPX | \
- MT7531_FORCE_RX_FC | \
- MT7531_FORCE_TX_FC)
--#define PMCR_FORCE_MODE_ID(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
-- MT7531_FORCE_MODE : PMCR_FORCE_MODE)
- #define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \
- PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \
- PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
+++ /dev/null
-From de16cf680331cd0bd7db97c3f8d376f5eac39cae Mon Sep 17 00:00:00 2001
-From: Justin Swartz <justin.swartz@risingedge.co.za>
-Date: Tue, 5 Mar 2024 06:39:51 +0200
-Subject: [PATCH 28/30] net: dsa: mt7530: disable LEDs before reset
-
-Disable LEDs just before resetting the MT7530 to avoid
-situations where the ESW_P4_LED_0 and ESW_P3_LED_0 pin
-states may cause an unintended external crystal frequency
-to be selected.
-
-The HT_XTAL_FSEL (External Crystal Frequency Selection)
-field of HWTRAP (the Hardware Trap register) stores a
-2-bit value that represents the state of the ESW_P4_LED_0
-and ESW_P4_LED_0 pins (seemingly) sampled just after the
-MT7530 has been reset, as:
-
- ESW_P4_LED_0 ESW_P3_LED_0 Frequency
- -----------------------------------------
- 0 1 20MHz
- 1 0 40MHz
- 1 1 25MHz
-
-The value of HT_XTAL_FSEL is bootstrapped by pulling
-ESW_P4_LED_0 and ESW_P3_LED_0 up or down accordingly,
-but:
-
- if a 40MHz crystal has been selected and
- the ESW_P3_LED_0 pin is high during reset,
-
- or a 20MHz crystal has been selected and
- the ESW_P4_LED_0 pin is high during reset,
-
- then the value of HT_XTAL_FSEL will indicate
- that a 25MHz crystal is present.
-
-By default, the state of the LED pins is PHY controlled
-to reflect the link state.
-
-To illustrate, if a board has:
-
- 5 ports with active low LED control,
- and HT_XTAL_FSEL bootstrapped for 40MHz.
-
-When the MT7530 is powered up without any external
-connection, only the LED associated with Port 3 is
-illuminated as ESW_P3_LED_0 is low.
-
-In this state, directly after mt7530_setup()'s reset
-is performed, the HWTRAP register (0x7800) reflects
-the intended HT_XTAL_FSEL (HWTRAP bits 10:9) of 40MHz:
-
- mt7530-mdio mdio-bus:1f: mt7530_read: 00007800 == 00007dcf
-
- >>> bin(0x7dcf >> 9 & 0b11)
- '0b10'
-
-But if a cable is connected to Port 3 and the link
-is active before mt7530_setup()'s reset takes place,
-then HT_XTAL_FSEL seems to be set for 25MHz:
-
- mt7530-mdio mdio-bus:1f: mt7530_read: 00007800 == 00007fcf
-
- >>> bin(0x7fcf >> 9 & 0b11)
- '0b11'
-
-Once HT_XTAL_FSEL reflects 25MHz, none of the ports
-are functional until the MT7621 (or MT7530 itself)
-is reset.
-
-By disabling the LED pins just before reset, the chance
-of an unintended HT_XTAL_FSEL value is reduced.
-
-Signed-off-by: Justin Swartz <justin.swartz@risingedge.co.za>
-Link: https://lore.kernel.org/r/20240305043952.21590-1-justin.swartz@risingedge.co.za
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530.c | 6 ++++++
- 1 file changed, 6 insertions(+)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2391,6 +2391,12 @@ mt7530_setup(struct dsa_switch *ds)
- }
- }
-
-+ /* Disable LEDs before reset to prevent the MT7530 sampling a
-+ * potentially incorrect HT_XTAL_FSEL value.
-+ */
-+ mt7530_write(priv, MT7530_LED_EN, 0);
-+ usleep_range(1000, 1100);
-+
- /* Reset whole chip through gpio pin or memory-mapped registers for
- * different type of hardware
- */
+++ /dev/null
-From b9547109205c5e0a27e5bed568b0fc183fff906b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Thu, 14 Mar 2024 12:28:35 +0300
-Subject: [PATCH 30/30] net: dsa: mt7530: prevent possible incorrect XTAL
- frequency selection
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-On MT7530, the HT_XTAL_FSEL field of the HWTRAP register stores a 2-bit
-value that represents the frequency of the crystal oscillator connected to
-the switch IC. The field is populated by the state of the ESW_P4_LED_0 and
-ESW_P4_LED_0 pins, which is done right after reset is deasserted.
-
- ESW_P4_LED_0 ESW_P3_LED_0 Frequency
- -----------------------------------------
- 0 0 Reserved
- 0 1 20MHz
- 1 0 40MHz
- 1 1 25MHz
-
-On MT7531, the XTAL25 bit of the STRAP register stores this. The LAN0LED0
-pin is used to populate the bit. 25MHz when the pin is high, 40MHz when
-it's low.
-
-These pins are also used with LEDs, therefore, their state can be set to
-something other than the bootstrapping configuration. For example, a link
-may be established on port 3 before the DSA subdriver takes control of the
-switch which would set ESW_P3_LED_0 to high.
-
-Currently on mt7530_setup() and mt7531_setup(), 1000 - 1100 usec delay is
-described between reset assertion and deassertion. Some switch ICs in real
-life conditions cannot always have these pins set back to the bootstrapping
-configuration before reset deassertion in this amount of delay. This causes
-wrong crystal frequency to be selected which puts the switch in a
-nonfunctional state after reset deassertion.
-
-The tests below are conducted on an MT7530 with a 40MHz crystal oscillator
-by Justin Swartz.
-
-With a cable from an active peer connected to port 3 before reset, an
-incorrect crystal frequency (0b11 = 25MHz) is selected:
-
- [1] [3] [5]
- : : :
- _____________________________ __________________
-ESW_P4_LED_0 |_______|
- _____________________________
-ESW_P3_LED_0 |__________________________
-
- : : : :
- : : [4]...:
- : :
- [2]................:
-
-[1] Reset is asserted.
-[2] Period of 1000 - 1100 usec.
-[3] Reset is deasserted.
-[4] Period of 315 usec. HWTRAP register is populated with incorrect
- XTAL frequency.
-[5] Signals reflect the bootstrapped configuration.
-
-Increase the delay between reset_control_assert() and
-reset_control_deassert(), and gpiod_set_value_cansleep(priv->reset, 0) and
-gpiod_set_value_cansleep(priv->reset, 1) to 5000 - 5100 usec. This amount
-ensures a higher possibility that the switch IC will have these pins back
-to the bootstrapping configuration before reset deassertion.
-
-With a cable from an active peer connected to port 3 before reset, the
-correct crystal frequency (0b10 = 40MHz) is selected:
-
- [1] [2-1] [3] [5]
- : : : :
- _____________________________ __________________
-ESW_P4_LED_0 |_______|
- ___________________ _______
-ESW_P3_LED_0 |_________| |__________________
-
- : : : : :
- : [2-2]...: [4]...:
- [2]................:
-
-[1] Reset is asserted.
-[2] Period of 5000 - 5100 usec.
-[2-1] ESW_P3_LED_0 goes low.
-[2-2] Remaining period of 5000 - 5100 usec.
-[3] Reset is deasserted.
-[4] Period of 310 usec. HWTRAP register is populated with bootstrapped
- XTAL frequency.
-[5] Signals reflect the bootstrapped configuration.
-
-ESW_P3_LED_0 low period before reset deassertion:
-
- 5000 usec
- - 5100 usec
- TEST RESET HOLD
- # (usec)
- ---------------------
- 1 5410
- 2 5440
- 3 4375
- 4 5490
- 5 5475
- 6 4335
- 7 4370
- 8 5435
- 9 4205
- 10 4335
- 11 3750
- 12 3170
- 13 4395
- 14 4375
- 15 3515
- 16 4335
- 17 4220
- 18 4175
- 19 4175
- 20 4350
-
- Min 3170
- Max 5490
-
- Median 4342.500
- Avg 4466.500
-
-Revert commit 2920dd92b980 ("net: dsa: mt7530: disable LEDs before reset").
-Changing the state of pins via reset assertion is simpler and more
-efficient than doing so by setting the LED controller off.
-
-Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch")
-Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch")
-Co-developed-by: Justin Swartz <justin.swartz@risingedge.co.za>
-Signed-off-by: Justin Swartz <justin.swartz@risingedge.co.za>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 6 ------
- 1 file changed, 6 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2391,12 +2391,6 @@ mt7530_setup(struct dsa_switch *ds)
- }
- }
-
-- /* Disable LEDs before reset to prevent the MT7530 sampling a
-- * potentially incorrect HT_XTAL_FSEL value.
-- */
-- mt7530_write(priv, MT7530_LED_EN, 0);
-- usleep_range(1000, 1100);
--
- /* Reset whole chip through gpio pin or memory-mapped registers for
- * different type of hardware
- */
+++ /dev/null
-From 5754b3bdcd872aa229881b8f07f84a8404c7d72a Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 12 Apr 2024 16:15:34 +0100
-Subject: [PATCH 1/5] net: dsa: mt7530: provide own phylink MAC operations
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Convert mt753x to provide its own phylink MAC operations, thus avoiding
-the shim layer in DSA's port.c
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Tested-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Link: https://lore.kernel.org/r/E1rvIco-006bQu-Fq@rmk-PC.armlinux.org.uk
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 46 +++++++++++++++++++++++++---------------
- 1 file changed, 29 insertions(+), 17 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2854,28 +2854,34 @@ mt7531_mac_config(struct dsa_switch *ds,
- }
-
- static struct phylink_pcs *
--mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
-+mt753x_phylink_mac_select_pcs(struct phylink_config *config,
- phy_interface_t interface)
- {
-- struct mt7530_priv *priv = ds->priv;
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
-+ struct mt7530_priv *priv = dp->ds->priv;
-
- switch (interface) {
- case PHY_INTERFACE_MODE_TRGMII:
-- return &priv->pcs[port].pcs;
-+ return &priv->pcs[dp->index].pcs;
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_1000BASEX:
- case PHY_INTERFACE_MODE_2500BASEX:
-- return priv->ports[port].sgmii_pcs;
-+ return priv->ports[dp->index].sgmii_pcs;
- default:
- return NULL;
- }
- }
-
- static void
--mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
-+mt753x_phylink_mac_config(struct phylink_config *config, unsigned int mode,
- const struct phylink_link_state *state)
- {
-- struct mt7530_priv *priv = ds->priv;
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
-+ struct dsa_switch *ds = dp->ds;
-+ struct mt7530_priv *priv;
-+ int port = dp->index;
-+
-+ priv = ds->priv;
-
- if ((port == 5 || port == 6) && priv->info->mac_port_config)
- priv->info->mac_port_config(ds, port, mode, state->interface);
-@@ -2885,23 +2891,25 @@ mt753x_phylink_mac_config(struct dsa_swi
- mt7530_set(priv, MT7530_PMCR_P(port), PMCR_EXT_PHY);
- }
-
--static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port,
-+static void mt753x_phylink_mac_link_down(struct phylink_config *config,
- unsigned int mode,
- phy_interface_t interface)
- {
-- struct mt7530_priv *priv = ds->priv;
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
-+ struct mt7530_priv *priv = dp->ds->priv;
-
-- mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
-+ mt7530_clear(priv, MT7530_PMCR_P(dp->index), PMCR_LINK_SETTINGS_MASK);
- }
-
--static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,
-+static void mt753x_phylink_mac_link_up(struct phylink_config *config,
-+ struct phy_device *phydev,
- unsigned int mode,
- phy_interface_t interface,
-- struct phy_device *phydev,
- int speed, int duplex,
- bool tx_pause, bool rx_pause)
- {
-- struct mt7530_priv *priv = ds->priv;
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
-+ struct mt7530_priv *priv = dp->ds->priv;
- u32 mcr;
-
- mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK;
-@@ -2936,7 +2944,7 @@ static void mt753x_phylink_mac_link_up(s
- }
- }
-
-- mt7530_set(priv, MT7530_PMCR_P(port), mcr);
-+ mt7530_set(priv, MT7530_PMCR_P(dp->index), mcr);
- }
-
- static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
-@@ -3156,16 +3164,19 @@ const struct dsa_switch_ops mt7530_switc
- .port_mirror_add = mt753x_port_mirror_add,
- .port_mirror_del = mt753x_port_mirror_del,
- .phylink_get_caps = mt753x_phylink_get_caps,
-- .phylink_mac_select_pcs = mt753x_phylink_mac_select_pcs,
-- .phylink_mac_config = mt753x_phylink_mac_config,
-- .phylink_mac_link_down = mt753x_phylink_mac_link_down,
-- .phylink_mac_link_up = mt753x_phylink_mac_link_up,
- .get_mac_eee = mt753x_get_mac_eee,
- .set_mac_eee = mt753x_set_mac_eee,
- .master_state_change = mt753x_conduit_state_change,
- };
- EXPORT_SYMBOL_GPL(mt7530_switch_ops);
-
-+static const struct phylink_mac_ops mt753x_phylink_mac_ops = {
-+ .mac_select_pcs = mt753x_phylink_mac_select_pcs,
-+ .mac_config = mt753x_phylink_mac_config,
-+ .mac_link_down = mt753x_phylink_mac_link_down,
-+ .mac_link_up = mt753x_phylink_mac_link_up,
-+};
-+
- const struct mt753x_info mt753x_table[] = {
- [ID_MT7621] = {
- .id = ID_MT7621,
-@@ -3243,6 +3254,7 @@ mt7530_probe_common(struct mt7530_priv *
- priv->dev = dev;
- priv->ds->priv = priv;
- priv->ds->ops = &mt7530_switch_ops;
-+ priv->ds->phylink_mac_ops = &mt753x_phylink_mac_ops;
- mutex_init(&priv->reg_mutex);
- dev_set_drvdata(dev, priv);
-
+++ /dev/null
-From 5053a6cf1d50d785078562470d2a63695a9f3bf2 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Thu, 18 Apr 2024 08:35:30 +0300
-Subject: [PATCH 4/5] net: dsa: mt7530-mdio: read PHY address of switch from
- device tree
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Read the PHY address the switch listens on from the reg property of the
-switch node on the device tree. This change brings support for MT7530
-switches on boards with such bootstrapping configuration where the switch
-listens on a different PHY address than the hardcoded PHY address on the
-driver, 31.
-
-As described on the "MT7621 Programming Guide v0.4" document, the MT7530
-switch and its PHYs can be configured to listen on the range of 7-12,
-15-20, 23-28, and 31 and 0-4 PHY addresses.
-
-There are operations where the switch PHY registers are used. For the PHY
-address of the control PHY, transform the MT753X_CTRL_PHY_ADDR constant
-into a macro and use it. The PHY address for the control PHY is 0 when the
-switch listens on 31. In any other case, it is one greater than the PHY
-address the switch listens on.
-
-Reviewed-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530-mdio.c | 28 +++++++++++++-------------
- drivers/net/dsa/mt7530.c | 37 +++++++++++++++++++++++------------
- drivers/net/dsa/mt7530.h | 4 +++-
- 3 files changed, 41 insertions(+), 28 deletions(-)
-
---- a/drivers/net/dsa/mt7530-mdio.c
-+++ b/drivers/net/dsa/mt7530-mdio.c
-@@ -18,7 +18,8 @@
- static int
- mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
- {
-- struct mii_bus *bus = context;
-+ struct mt7530_priv *priv = context;
-+ struct mii_bus *bus = priv->bus;
- u16 page, r, lo, hi;
- int ret;
-
-@@ -27,36 +28,35 @@ mt7530_regmap_write(void *context, unsig
- lo = val & 0xffff;
- hi = val >> 16;
-
-- /* MT7530 uses 31 as the pseudo port */
-- ret = bus->write(bus, 0x1f, 0x1f, page);
-+ ret = bus->write(bus, priv->mdiodev->addr, 0x1f, page);
- if (ret < 0)
- return ret;
-
-- ret = bus->write(bus, 0x1f, r, lo);
-+ ret = bus->write(bus, priv->mdiodev->addr, r, lo);
- if (ret < 0)
- return ret;
-
-- ret = bus->write(bus, 0x1f, 0x10, hi);
-+ ret = bus->write(bus, priv->mdiodev->addr, 0x10, hi);
- return ret;
- }
-
- static int
- mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
- {
-- struct mii_bus *bus = context;
-+ struct mt7530_priv *priv = context;
-+ struct mii_bus *bus = priv->bus;
- u16 page, r, lo, hi;
- int ret;
-
- page = (reg >> 6) & 0x3ff;
- r = (reg >> 2) & 0xf;
-
-- /* MT7530 uses 31 as the pseudo port */
-- ret = bus->write(bus, 0x1f, 0x1f, page);
-+ ret = bus->write(bus, priv->mdiodev->addr, 0x1f, page);
- if (ret < 0)
- return ret;
-
-- lo = bus->read(bus, 0x1f, r);
-- hi = bus->read(bus, 0x1f, 0x10);
-+ lo = bus->read(bus, priv->mdiodev->addr, r);
-+ hi = bus->read(bus, priv->mdiodev->addr, 0x10);
-
- *val = (hi << 16) | (lo & 0xffff);
-
-@@ -107,8 +107,7 @@ mt7531_create_sgmii(struct mt7530_priv *
- mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock;
- mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock;
-
-- regmap = devm_regmap_init(priv->dev,
-- &mt7530_regmap_bus, priv->bus,
-+ regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, priv,
- mt7531_pcs_config[i]);
- if (IS_ERR(regmap)) {
- ret = PTR_ERR(regmap);
-@@ -153,6 +152,7 @@ mt7530_probe(struct mdio_device *mdiodev
-
- priv->bus = mdiodev->bus;
- priv->dev = &mdiodev->dev;
-+ priv->mdiodev = mdiodev;
-
- ret = mt7530_probe_common(priv);
- if (ret)
-@@ -203,8 +203,8 @@ mt7530_probe(struct mdio_device *mdiodev
- regmap_config->reg_stride = 4;
- regmap_config->max_register = MT7530_CREV;
- regmap_config->disable_locking = true;
-- priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus,
-- priv->bus, regmap_config);
-+ priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, priv,
-+ regmap_config);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -86,22 +86,26 @@ core_read_mmd_indirect(struct mt7530_pri
- int value, ret;
-
- /* Write the desired MMD Devad */
-- ret = bus->write(bus, 0, MII_MMD_CTRL, devad);
-+ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-+ MII_MMD_CTRL, devad);
- if (ret < 0)
- goto err;
-
- /* Write the desired MMD register address */
-- ret = bus->write(bus, 0, MII_MMD_DATA, prtad);
-+ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-+ MII_MMD_DATA, prtad);
- if (ret < 0)
- goto err;
-
- /* Select the Function : DATA with no post increment */
-- ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
-+ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-+ MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR);
- if (ret < 0)
- goto err;
-
- /* Read the content of the MMD's selected register */
-- value = bus->read(bus, 0, MII_MMD_DATA);
-+ value = bus->read(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-+ MII_MMD_DATA);
-
- return value;
- err:
-@@ -118,22 +122,26 @@ core_write_mmd_indirect(struct mt7530_pr
- int ret;
-
- /* Write the desired MMD Devad */
-- ret = bus->write(bus, 0, MII_MMD_CTRL, devad);
-+ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-+ MII_MMD_CTRL, devad);
- if (ret < 0)
- goto err;
-
- /* Write the desired MMD register address */
-- ret = bus->write(bus, 0, MII_MMD_DATA, prtad);
-+ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-+ MII_MMD_DATA, prtad);
- if (ret < 0)
- goto err;
-
- /* Select the Function : DATA with no post increment */
-- ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
-+ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-+ MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR);
- if (ret < 0)
- goto err;
-
- /* Write the data into MMD's selected register */
-- ret = bus->write(bus, 0, MII_MMD_DATA, data);
-+ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-+ MII_MMD_DATA, data);
- err:
- if (ret < 0)
- dev_err(&bus->dev,
-@@ -2680,16 +2688,19 @@ mt7531_setup(struct dsa_switch *ds)
- * phy_[read,write]_mmd_indirect is called, we provide our own
- * mt7531_ind_mmd_phy_[read,write] to complete this function.
- */
-- val = mt7531_ind_c45_phy_read(priv, MT753X_CTRL_PHY_ADDR,
-+ val = mt7531_ind_c45_phy_read(priv,
-+ MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
- MDIO_MMD_VEND2, CORE_PLL_GROUP4);
- val |= MT7531_RG_SYSPLL_DMY2 | MT7531_PHY_PLL_BYPASS_MODE;
- val &= ~MT7531_PHY_PLL_OFF;
-- mt7531_ind_c45_phy_write(priv, MT753X_CTRL_PHY_ADDR, MDIO_MMD_VEND2,
-- CORE_PLL_GROUP4, val);
-+ mt7531_ind_c45_phy_write(priv,
-+ MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-+ MDIO_MMD_VEND2, CORE_PLL_GROUP4, val);
-
- /* Disable EEE advertisement on the switch PHYs. */
-- for (i = MT753X_CTRL_PHY_ADDR;
-- i < MT753X_CTRL_PHY_ADDR + MT7530_NUM_PHYS; i++) {
-+ for (i = MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr);
-+ i < MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr) + MT7530_NUM_PHYS;
-+ i++) {
- mt7531_ind_c45_phy_write(priv, i, MDIO_MMD_AN, MDIO_AN_EEE_ADV,
- 0);
- }
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -629,7 +629,7 @@ enum mt7531_clk_skew {
- #define MT7531_PHY_PLL_OFF BIT(5)
- #define MT7531_PHY_PLL_BYPASS_MODE BIT(4)
-
--#define MT753X_CTRL_PHY_ADDR 0
-+#define MT753X_CTRL_PHY_ADDR(addr) ((addr + 1) & 0x1f)
-
- #define CORE_PLL_GROUP5 0x404
- #define RG_LCDDS_PCW_NCPO1(x) ((x) & 0xffff)
-@@ -778,6 +778,7 @@ struct mt753x_info {
- * @irq_enable: IRQ enable bits, synced to SYS_INT_EN
- * @create_sgmii: Pointer to function creating SGMII PCS instance(s)
- * @active_cpu_ports: Holding the active CPU ports
-+ * @mdiodev: The pointer to the MDIO device structure
- */
- struct mt7530_priv {
- struct device *dev;
-@@ -804,6 +805,7 @@ struct mt7530_priv {
- u32 irq_enable;
- int (*create_sgmii)(struct mt7530_priv *priv);
- u8 active_cpu_ports;
-+ struct mdio_device *mdiodev;
- };
-
- struct mt7530_hw_vlan_entry {
+++ /dev/null
-From 9764a08b3d260f4e7799d34bbfe64463db940d74 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Thu, 18 Apr 2024 08:35:31 +0300
-Subject: [PATCH 5/5] net: dsa: mt7530: simplify core operations
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The core_rmw() function calls core_read_mmd_indirect() to read the
-requested register, and then calls core_write_mmd_indirect() to write the
-requested value to the register. Because Clause 22 is used to access Clause
-45 registers, some operations on core_write_mmd_indirect() are
-unnecessarily run. Get rid of core_read_mmd_indirect() and
-core_write_mmd_indirect(), and run only the necessary operations on
-core_write() and core_rmw().
-
-Reviewed-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 108 ++++++++++++++++-----------------------
- 1 file changed, 43 insertions(+), 65 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -74,116 +74,94 @@ static const struct mt7530_mib_desc mt75
- MIB_DESC(1, 0xb8, "RxArlDrop"),
- };
-
--/* Since phy_device has not yet been created and
-- * phy_{read,write}_mmd_indirect is not available, we provide our own
-- * core_{read,write}_mmd_indirect with core_{clear,write,set} wrappers
-- * to complete this function.
-- */
--static int
--core_read_mmd_indirect(struct mt7530_priv *priv, int prtad, int devad)
-+static void
-+mt7530_mutex_lock(struct mt7530_priv *priv)
-+{
-+ if (priv->bus)
-+ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
-+}
-+
-+static void
-+mt7530_mutex_unlock(struct mt7530_priv *priv)
-+{
-+ if (priv->bus)
-+ mutex_unlock(&priv->bus->mdio_lock);
-+}
-+
-+static void
-+core_write(struct mt7530_priv *priv, u32 reg, u32 val)
- {
- struct mii_bus *bus = priv->bus;
-- int value, ret;
-+ int ret;
-+
-+ mt7530_mutex_lock(priv);
-
- /* Write the desired MMD Devad */
- ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-- MII_MMD_CTRL, devad);
-+ MII_MMD_CTRL, MDIO_MMD_VEND2);
- if (ret < 0)
- goto err;
-
- /* Write the desired MMD register address */
- ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-- MII_MMD_DATA, prtad);
-+ MII_MMD_DATA, reg);
- if (ret < 0)
- goto err;
-
- /* Select the Function : DATA with no post increment */
- ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-- MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR);
-+ MII_MMD_CTRL, MDIO_MMD_VEND2 | MII_MMD_CTRL_NOINCR);
- if (ret < 0)
- goto err;
-
-- /* Read the content of the MMD's selected register */
-- value = bus->read(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-- MII_MMD_DATA);
--
-- return value;
-+ /* Write the data into MMD's selected register */
-+ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-+ MII_MMD_DATA, val);
- err:
-- dev_err(&bus->dev, "failed to read mmd register\n");
-+ if (ret < 0)
-+ dev_err(&bus->dev, "failed to write mmd register\n");
-
-- return ret;
-+ mt7530_mutex_unlock(priv);
- }
-
--static int
--core_write_mmd_indirect(struct mt7530_priv *priv, int prtad,
-- int devad, u32 data)
-+static void
-+core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set)
- {
- struct mii_bus *bus = priv->bus;
-+ u32 val;
- int ret;
-
-+ mt7530_mutex_lock(priv);
-+
- /* Write the desired MMD Devad */
- ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-- MII_MMD_CTRL, devad);
-+ MII_MMD_CTRL, MDIO_MMD_VEND2);
- if (ret < 0)
- goto err;
-
- /* Write the desired MMD register address */
- ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-- MII_MMD_DATA, prtad);
-+ MII_MMD_DATA, reg);
- if (ret < 0)
- goto err;
-
- /* Select the Function : DATA with no post increment */
- ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-- MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR);
-+ MII_MMD_CTRL, MDIO_MMD_VEND2 | MII_MMD_CTRL_NOINCR);
- if (ret < 0)
- goto err;
-
-+ /* Read the content of the MMD's selected register */
-+ val = bus->read(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-+ MII_MMD_DATA);
-+ val &= ~mask;
-+ val |= set;
- /* Write the data into MMD's selected register */
- ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
-- MII_MMD_DATA, data);
-+ MII_MMD_DATA, val);
- err:
- if (ret < 0)
-- dev_err(&bus->dev,
-- "failed to write mmd register\n");
-- return ret;
--}
--
--static void
--mt7530_mutex_lock(struct mt7530_priv *priv)
--{
-- if (priv->bus)
-- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
--}
--
--static void
--mt7530_mutex_unlock(struct mt7530_priv *priv)
--{
-- if (priv->bus)
-- mutex_unlock(&priv->bus->mdio_lock);
--}
--
--static void
--core_write(struct mt7530_priv *priv, u32 reg, u32 val)
--{
-- mt7530_mutex_lock(priv);
--
-- core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
--
-- mt7530_mutex_unlock(priv);
--}
--
--static void
--core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set)
--{
-- u32 val;
--
-- mt7530_mutex_lock(priv);
--
-- val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2);
-- val &= ~mask;
-- val |= set;
-- core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
-+ dev_err(&bus->dev, "failed to write mmd register\n");
-
- mt7530_mutex_unlock(priv);
- }
+++ /dev/null
-From 856e8954a0a88d1a4d2b43e9002b9249131a156f Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:08 +0300
-Subject: [PATCH 01/15] net: dsa: mt7530: disable EEE abilities on failure on
- MT7531 and MT7988
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The MT7531_FORCE_EEE1G and MT7531_FORCE_EEE100 bits let the
-PMCR_FORCE_EEE1G and PMCR_FORCE_EEE100 bits determine the 1G/100 EEE
-abilities of the MAC. If MT7531_FORCE_EEE1G and MT7531_FORCE_EEE100 are
-unset, the abilities are left to be determined by PHY auto polling.
-
-The commit 40b5d2f15c09 ("net: dsa: mt7530: Add support for EEE features")
-made it so that the PMCR_FORCE_EEE1G and PMCR_FORCE_EEE100 bits are set on
-mt753x_phylink_mac_link_up(). But it did not set the MT7531_FORCE_EEE1G and
-MT7531_FORCE_EEE100 bits. Because of this, the EEE abilities will be
-determined by PHY auto polling, regardless of the result of phy_init_eee().
-
-Define these bits and add them to the MT7531_FORCE_MODE mask which is set
-in mt7531_setup_common(). With this, there won't be any EEE abilities set
-when phy_init_eee() returns a negative value.
-
-Thanks to Russell for explaining when phy_init_eee() could return a
-negative value below.
-
-Looking at phy_init_eee(), it could return a negative value when:
-
-1. phydev->drv is NULL
-2. if genphy_c45_eee_is_active() returns negative
-3. if genphy_c45_eee_is_active() returns zero, it returns -EPROTONOSUPPORT
-4. if phy_set_bits_mmd() fails (e.g. communication error with the PHY)
-
-If we then look at genphy_c45_eee_is_active(), then:
-
-genphy_c45_read_eee_adv() and genphy_c45_read_eee_lpa() propagate their
-non-zero return values, otherwise this function returns zero or positive
-integer.
-
-If we then look at genphy_c45_read_eee_adv(), then a failure of
-phy_read_mmd() would cause a negative value to be returned.
-
-Looking at genphy_c45_read_eee_lpa(), the same is true.
-
-So, it can be summarised as:
-
-- phydev->drv is NULL
-- there is a communication error accessing the PHY
-- EEE is not active
-
-otherwise, it returns zero on success.
-
-If one wishes to determine whether an error occurred vs EEE not being
-supported through negotiation for the negotiated speed, if it returns
--EPROTONOSUPPORT in the latter case. Other error codes mean either the
-driver has been unloaded or communication error.
-
-In conclusion, determining the EEE abilities by PHY auto polling shouldn't
-result in having any EEE abilities enabled, when one of the last two
-situations in the summary happens. And it seems that if phydev->drv is
-NULL, there would be bigger problems with the device than a broken link. So
-this is not a bugfix.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.h | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -328,11 +328,15 @@ enum mt7530_vlan_port_acc_frm {
- #define MT7531_FORCE_DPX BIT(29)
- #define MT7531_FORCE_RX_FC BIT(28)
- #define MT7531_FORCE_TX_FC BIT(27)
-+#define MT7531_FORCE_EEE100 BIT(26)
-+#define MT7531_FORCE_EEE1G BIT(25)
- #define MT7531_FORCE_MODE (MT7531_FORCE_LNK | \
- MT7531_FORCE_SPD | \
- MT7531_FORCE_DPX | \
- MT7531_FORCE_RX_FC | \
-- MT7531_FORCE_TX_FC)
-+ MT7531_FORCE_TX_FC | \
-+ MT7531_FORCE_EEE100 | \
-+ MT7531_FORCE_EEE1G)
- #define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \
- PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \
- PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
+++ /dev/null
-From 712ad00d2f43814c81a7abfcbc339690a05fb6a0 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:09 +0300
-Subject: [PATCH 02/15] net: dsa: mt7530: refactor MT7530_PMCR_P()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The MT7530_PMCR_P() registers are on MT7530, MT7531, and the switch on the
-MT7988 SoC. Rename the definition for them to MT753X_PMCR_P(). Bit 15 is
-for MT7530 only. Add MT7530 prefix to the definition for bit 15.
-
-Use GENMASK and FIELD_PREP for PMCR_IFG_XMIT().
-
-Rename PMCR_TX_EN and PMCR_RX_EN to PMCR_MAC_TX_EN and PMCR_MAC_TX_EN to
-follow the naming on the "MT7621 Giga Switch Programming Guide v0.3",
-"MT7531 Reference Manual for Development Board v1.0", and "MT7988A Wi-Fi 7
-Generation Router Platform: Datasheet (Open Version) v0.1" documents.
-
-These documents show that PMCR_RX_FC_EN is at bit 5. Correct this along
-with renaming it to PMCR_FORCE_RX_FC_EN, and the same for PMCR_TX_FC_EN.
-
-Remove PMCR_SPEED_MASK which doesn't have a use.
-
-Rename the force mode definitions for MT7531 to FORCE_MODE. Add MASK at the
-end for the mask that includes all force mode definitions.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 24 ++++++++---------
- drivers/net/dsa/mt7530.h | 58 +++++++++++++++++++++-------------------
- 2 files changed, 42 insertions(+), 40 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -896,7 +896,7 @@ static void mt7530_setup_port5(struct ds
- val &= ~MHWTRAP_P5_MAC_SEL & ~MHWTRAP_P5_DIS;
-
- /* Setup the MAC by default for the cpu port */
-- mt7530_write(priv, MT7530_PMCR_P(5), 0x56300);
-+ mt7530_write(priv, MT753X_PMCR_P(5), 0x56300);
- break;
- case P5_INTF_SEL_GMAC5:
- /* MT7530_P5_MODE_GMAC: P5 -> External phy or 2nd GMAC */
-@@ -2444,8 +2444,8 @@ mt7530_setup(struct dsa_switch *ds)
- /* Clear link settings and enable force mode to force link down
- * on all ports until they're enabled later.
- */
-- mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
-- PMCR_FORCE_MODE, PMCR_FORCE_MODE);
-+ mt7530_rmw(priv, MT753X_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
-+ MT7530_FORCE_MODE, MT7530_FORCE_MODE);
-
- /* Disable forwarding by default on all ports */
- mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
-@@ -2555,8 +2555,8 @@ mt7531_setup_common(struct dsa_switch *d
- /* Clear link settings and enable force mode to force link down
- * on all ports until they're enabled later.
- */
-- mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
-- MT7531_FORCE_MODE, MT7531_FORCE_MODE);
-+ mt7530_rmw(priv, MT753X_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
-+ MT7531_FORCE_MODE_MASK, MT7531_FORCE_MODE_MASK);
-
- /* Disable forwarding by default on all ports */
- mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
-@@ -2640,7 +2640,7 @@ mt7531_setup(struct dsa_switch *ds)
-
- /* Force link down on all ports before internal reset */
- for (i = 0; i < MT7530_NUM_PORTS; i++)
-- mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK);
-+ mt7530_write(priv, MT753X_PMCR_P(i), MT7531_FORCE_MODE_LNK);
-
- /* Reset the switch through internal reset */
- mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
-@@ -2877,7 +2877,7 @@ mt753x_phylink_mac_config(struct phylink
-
- /* Are we connected to external phy */
- if (port == 5 && dsa_is_user_port(ds, 5))
-- mt7530_set(priv, MT7530_PMCR_P(port), PMCR_EXT_PHY);
-+ mt7530_set(priv, MT753X_PMCR_P(port), PMCR_EXT_PHY);
- }
-
- static void mt753x_phylink_mac_link_down(struct phylink_config *config,
-@@ -2887,7 +2887,7 @@ static void mt753x_phylink_mac_link_down
- struct dsa_port *dp = dsa_phylink_to_port(config);
- struct mt7530_priv *priv = dp->ds->priv;
-
-- mt7530_clear(priv, MT7530_PMCR_P(dp->index), PMCR_LINK_SETTINGS_MASK);
-+ mt7530_clear(priv, MT753X_PMCR_P(dp->index), PMCR_LINK_SETTINGS_MASK);
- }
-
- static void mt753x_phylink_mac_link_up(struct phylink_config *config,
-@@ -2901,7 +2901,7 @@ static void mt753x_phylink_mac_link_up(s
- struct mt7530_priv *priv = dp->ds->priv;
- u32 mcr;
-
-- mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK;
-+ mcr = PMCR_MAC_RX_EN | PMCR_MAC_TX_EN | PMCR_FORCE_LNK;
-
- switch (speed) {
- case SPEED_1000:
-@@ -2916,9 +2916,9 @@ static void mt753x_phylink_mac_link_up(s
- if (duplex == DUPLEX_FULL) {
- mcr |= PMCR_FORCE_FDX;
- if (tx_pause)
-- mcr |= PMCR_TX_FC_EN;
-+ mcr |= PMCR_FORCE_TX_FC_EN;
- if (rx_pause)
-- mcr |= PMCR_RX_FC_EN;
-+ mcr |= PMCR_FORCE_RX_FC_EN;
- }
-
- if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) {
-@@ -2933,7 +2933,7 @@ static void mt753x_phylink_mac_link_up(s
- }
- }
-
-- mt7530_set(priv, MT7530_PMCR_P(dp->index), mcr);
-+ mt7530_set(priv, MT753X_PMCR_P(dp->index), mcr);
- }
-
- static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -304,44 +304,46 @@ enum mt7530_vlan_port_acc_frm {
- #define G0_PORT_VID_DEF G0_PORT_VID(0)
-
- /* Register for port MAC control register */
--#define MT7530_PMCR_P(x) (0x3000 + ((x) * 0x100))
--#define PMCR_IFG_XMIT(x) (((x) & 0x3) << 18)
-+#define MT753X_PMCR_P(x) (0x3000 + ((x) * 0x100))
-+#define PMCR_IFG_XMIT_MASK GENMASK(19, 18)
-+#define PMCR_IFG_XMIT(x) FIELD_PREP(PMCR_IFG_XMIT_MASK, x)
- #define PMCR_EXT_PHY BIT(17)
- #define PMCR_MAC_MODE BIT(16)
--#define PMCR_FORCE_MODE BIT(15)
--#define PMCR_TX_EN BIT(14)
--#define PMCR_RX_EN BIT(13)
-+#define MT7530_FORCE_MODE BIT(15)
-+#define PMCR_MAC_TX_EN BIT(14)
-+#define PMCR_MAC_RX_EN BIT(13)
- #define PMCR_BACKOFF_EN BIT(9)
- #define PMCR_BACKPR_EN BIT(8)
- #define PMCR_FORCE_EEE1G BIT(7)
- #define PMCR_FORCE_EEE100 BIT(6)
--#define PMCR_TX_FC_EN BIT(5)
--#define PMCR_RX_FC_EN BIT(4)
-+#define PMCR_FORCE_RX_FC_EN BIT(5)
-+#define PMCR_FORCE_TX_FC_EN BIT(4)
- #define PMCR_FORCE_SPEED_1000 BIT(3)
- #define PMCR_FORCE_SPEED_100 BIT(2)
- #define PMCR_FORCE_FDX BIT(1)
- #define PMCR_FORCE_LNK BIT(0)
--#define PMCR_SPEED_MASK (PMCR_FORCE_SPEED_100 | \
-- PMCR_FORCE_SPEED_1000)
--#define MT7531_FORCE_LNK BIT(31)
--#define MT7531_FORCE_SPD BIT(30)
--#define MT7531_FORCE_DPX BIT(29)
--#define MT7531_FORCE_RX_FC BIT(28)
--#define MT7531_FORCE_TX_FC BIT(27)
--#define MT7531_FORCE_EEE100 BIT(26)
--#define MT7531_FORCE_EEE1G BIT(25)
--#define MT7531_FORCE_MODE (MT7531_FORCE_LNK | \
-- MT7531_FORCE_SPD | \
-- MT7531_FORCE_DPX | \
-- MT7531_FORCE_RX_FC | \
-- MT7531_FORCE_TX_FC | \
-- MT7531_FORCE_EEE100 | \
-- MT7531_FORCE_EEE1G)
--#define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \
-- PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \
-- PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
-- PMCR_FORCE_FDX | PMCR_FORCE_LNK | \
-- PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100)
-+#define MT7531_FORCE_MODE_LNK BIT(31)
-+#define MT7531_FORCE_MODE_SPD BIT(30)
-+#define MT7531_FORCE_MODE_DPX BIT(29)
-+#define MT7531_FORCE_MODE_RX_FC BIT(28)
-+#define MT7531_FORCE_MODE_TX_FC BIT(27)
-+#define MT7531_FORCE_MODE_EEE100 BIT(26)
-+#define MT7531_FORCE_MODE_EEE1G BIT(25)
-+#define MT7531_FORCE_MODE_MASK (MT7531_FORCE_MODE_LNK | \
-+ MT7531_FORCE_MODE_SPD | \
-+ MT7531_FORCE_MODE_DPX | \
-+ MT7531_FORCE_MODE_RX_FC | \
-+ MT7531_FORCE_MODE_TX_FC | \
-+ MT7531_FORCE_MODE_EEE100 | \
-+ MT7531_FORCE_MODE_EEE1G)
-+#define PMCR_LINK_SETTINGS_MASK (PMCR_MAC_TX_EN | PMCR_MAC_RX_EN | \
-+ PMCR_FORCE_EEE1G | \
-+ PMCR_FORCE_EEE100 | \
-+ PMCR_FORCE_RX_FC_EN | \
-+ PMCR_FORCE_TX_FC_EN | \
-+ PMCR_FORCE_SPEED_1000 | \
-+ PMCR_FORCE_SPEED_100 | \
-+ PMCR_FORCE_FDX | PMCR_FORCE_LNK)
-
- #define MT7530_PMEEECR_P(x) (0x3004 + (x) * 0x100)
- #define WAKEUP_TIME_1000(x) (((x) & 0xFF) << 24)
+++ /dev/null
-From 875ec5b67ab88e969b171e6e9ea803e3ed759614 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:10 +0300
-Subject: [PATCH 03/15] net: dsa: mt7530: rename p5_intf_sel and use only for
- MT7530 switch
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The p5_intf_sel pointer is used to store the information of whether PHY
-muxing is used or not. PHY muxing is a feature specific to port 5 of the
-MT7530 switch. Do not use it for other switch models.
-
-Rename the pointer to p5_mode to store the mode the port is being used in.
-Rename the p5_interface_select enum to mt7530_p5_mode, the string
-representation to mt7530_p5_mode_str, and the enum elements.
-
-If PHY muxing is not detected, the default mode, GMAC5, will be used.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 62 +++++++++++++++++-----------------------
- drivers/net/dsa/mt7530.h | 15 +++++-----
- 2 files changed, 33 insertions(+), 44 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -857,19 +857,15 @@ mt7530_set_ageing_time(struct dsa_switch
- return 0;
- }
-
--static const char *p5_intf_modes(unsigned int p5_interface)
-+static const char *mt7530_p5_mode_str(unsigned int mode)
- {
-- switch (p5_interface) {
-- case P5_DISABLED:
-- return "DISABLED";
-- case P5_INTF_SEL_PHY_P0:
-- return "PHY P0";
-- case P5_INTF_SEL_PHY_P4:
-- return "PHY P4";
-- case P5_INTF_SEL_GMAC5:
-- return "GMAC5";
-+ switch (mode) {
-+ case MUX_PHY_P0:
-+ return "MUX PHY P0";
-+ case MUX_PHY_P4:
-+ return "MUX PHY P4";
- default:
-- return "unknown";
-+ return "GMAC5";
- }
- }
-
-@@ -886,23 +882,23 @@ static void mt7530_setup_port5(struct ds
- val |= MHWTRAP_MANUAL | MHWTRAP_P5_MAC_SEL | MHWTRAP_P5_DIS;
- val &= ~MHWTRAP_P5_RGMII_MODE & ~MHWTRAP_PHY0_SEL;
-
-- switch (priv->p5_intf_sel) {
-- case P5_INTF_SEL_PHY_P0:
-- /* MT7530_P5_MODE_GPHY_P0: 2nd GMAC -> P5 -> P0 */
-+ switch (priv->p5_mode) {
-+ /* MUX_PHY_P0: P0 -> P5 -> SoC MAC */
-+ case MUX_PHY_P0:
- val |= MHWTRAP_PHY0_SEL;
- fallthrough;
-- case P5_INTF_SEL_PHY_P4:
-- /* MT7530_P5_MODE_GPHY_P4: 2nd GMAC -> P5 -> P4 */
-+
-+ /* MUX_PHY_P4: P4 -> P5 -> SoC MAC */
-+ case MUX_PHY_P4:
- val &= ~MHWTRAP_P5_MAC_SEL & ~MHWTRAP_P5_DIS;
-
- /* Setup the MAC by default for the cpu port */
- mt7530_write(priv, MT753X_PMCR_P(5), 0x56300);
- break;
-- case P5_INTF_SEL_GMAC5:
-- /* MT7530_P5_MODE_GMAC: P5 -> External phy or 2nd GMAC */
-- val &= ~MHWTRAP_P5_DIS;
-- break;
-+
-+ /* GMAC5: P5 -> SoC MAC or external PHY */
- default:
-+ val &= ~MHWTRAP_P5_DIS;
- break;
- }
-
-@@ -930,8 +926,8 @@ static void mt7530_setup_port5(struct ds
-
- mt7530_write(priv, MT7530_MHWTRAP, val);
-
-- dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, intf_sel=%s, phy-mode=%s\n",
-- val, p5_intf_modes(priv->p5_intf_sel), phy_modes(interface));
-+ dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, mode=%s, phy-mode=%s\n", val,
-+ mt7530_p5_mode_str(priv->p5_mode), phy_modes(interface));
-
- mutex_unlock(&priv->reg_mutex);
- }
-@@ -2476,13 +2472,11 @@ mt7530_setup(struct dsa_switch *ds)
- if (ret)
- return ret;
-
-- /* Setup port 5 */
-- if (!dsa_is_unused_port(ds, 5)) {
-- priv->p5_intf_sel = P5_INTF_SEL_GMAC5;
-- } else {
-+ /* Check for PHY muxing on port 5 */
-+ if (dsa_is_unused_port(ds, 5)) {
- /* Scan the ethernet nodes. Look for GMAC1, lookup the used PHY.
-- * Set priv->p5_intf_sel to the appropriate value if PHY muxing
-- * is detected.
-+ * Set priv->p5_mode to the appropriate value if PHY muxing is
-+ * detected.
- */
- for_each_child_of_node(dn, mac_np) {
- if (!of_device_is_compatible(mac_np,
-@@ -2506,17 +2500,16 @@ mt7530_setup(struct dsa_switch *ds)
- }
- id = of_mdio_parse_addr(ds->dev, phy_node);
- if (id == 0)
-- priv->p5_intf_sel = P5_INTF_SEL_PHY_P0;
-+ priv->p5_mode = MUX_PHY_P0;
- if (id == 4)
-- priv->p5_intf_sel = P5_INTF_SEL_PHY_P4;
-+ priv->p5_mode = MUX_PHY_P4;
- }
- of_node_put(mac_np);
- of_node_put(phy_node);
- break;
- }
-
-- if (priv->p5_intf_sel == P5_INTF_SEL_PHY_P0 ||
-- priv->p5_intf_sel == P5_INTF_SEL_PHY_P4)
-+ if (priv->p5_mode == MUX_PHY_P0 || priv->p5_mode == MUX_PHY_P4)
- mt7530_setup_port5(ds, interface);
- }
-
-@@ -2655,9 +2648,6 @@ mt7531_setup(struct dsa_switch *ds)
- MT7531_EXT_P_MDIO_12);
- }
-
-- if (!dsa_is_unused_port(ds, 5))
-- priv->p5_intf_sel = P5_INTF_SEL_GMAC5;
--
- mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK,
- MT7531_GPIO0_INTERRUPT);
-
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -708,12 +708,11 @@ struct mt7530_port {
- struct phylink_pcs *sgmii_pcs;
- };
-
--/* Port 5 interface select definitions */
--enum p5_interface_select {
-- P5_DISABLED,
-- P5_INTF_SEL_PHY_P0,
-- P5_INTF_SEL_PHY_P4,
-- P5_INTF_SEL_GMAC5,
-+/* Port 5 mode definitions of the MT7530 switch */
-+enum mt7530_p5_mode {
-+ GMAC5,
-+ MUX_PHY_P0,
-+ MUX_PHY_P4,
- };
-
- struct mt7530_priv;
-@@ -776,7 +775,7 @@ struct mt753x_info {
- * @ports: Holding the state among ports
- * @reg_mutex: The lock for protecting among process accessing
- * registers
-- * @p5_intf_sel: Holding the current port 5 interface select
-+ * @p5_mode: Holding the current mode of port 5 of the MT7530 switch
- * @p5_sgmii: Flag for distinguishing if port 5 of the MT7531 switch
- * has got SGMII
- * @irq: IRQ number of the switch
-@@ -798,7 +797,7 @@ struct mt7530_priv {
- const struct mt753x_info *info;
- unsigned int id;
- bool mcm;
-- enum p5_interface_select p5_intf_sel;
-+ enum mt7530_p5_mode p5_mode;
- bool p5_sgmii;
- u8 mirror_rx;
- u8 mirror_tx;
+++ /dev/null
-From 83fe3df057e641cd0e88425e579d7a5a370ca430 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:11 +0300
-Subject: [PATCH 04/15] net: dsa: mt7530: rename mt753x_bpdu_port_fw enum to
- mt753x_to_cpu_fw
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The mt753x_bpdu_port_fw enum is globally used for manipulating the process
-of deciding the forwardable ports, specifically concerning the CPU port(s).
-Therefore, rename it and the values in it to mt753x_to_cpu_fw.
-
-Change FOLLOW_MFC to SYSTEM_DEFAULT to be on par with the switch documents.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 44 ++++++++++-------------
- drivers/net/dsa/mt7530.h | 76 ++++++++++++++++++++--------------------
- 2 files changed, 56 insertions(+), 64 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -1107,42 +1107,34 @@ mt753x_trap_frames(struct mt7530_priv *p
- * VLAN-untagged.
- */
- mt7530_rmw(priv, MT753X_BPC,
-- MT753X_PAE_BPDU_FR | MT753X_PAE_EG_TAG_MASK |
-- MT753X_PAE_PORT_FW_MASK | MT753X_BPDU_EG_TAG_MASK |
-- MT753X_BPDU_PORT_FW_MASK,
-- MT753X_PAE_BPDU_FR |
-- MT753X_PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-- MT753X_PAE_PORT_FW(MT753X_BPDU_CPU_ONLY) |
-- MT753X_BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-- MT753X_BPDU_CPU_ONLY);
-+ PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK |
-+ BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK,
-+ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-+ PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) |
-+ BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-+ TO_CPU_FW_CPU_ONLY);
-
- /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress
- * them VLAN-untagged.
- */
- mt7530_rmw(priv, MT753X_RGAC1,
-- MT753X_R02_BPDU_FR | MT753X_R02_EG_TAG_MASK |
-- MT753X_R02_PORT_FW_MASK | MT753X_R01_BPDU_FR |
-- MT753X_R01_EG_TAG_MASK | MT753X_R01_PORT_FW_MASK,
-- MT753X_R02_BPDU_FR |
-- MT753X_R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-- MT753X_R02_PORT_FW(MT753X_BPDU_CPU_ONLY) |
-- MT753X_R01_BPDU_FR |
-- MT753X_R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-- MT753X_BPDU_CPU_ONLY);
-+ R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK |
-+ R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK,
-+ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-+ R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR |
-+ R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-+ TO_CPU_FW_CPU_ONLY);
-
- /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress
- * them VLAN-untagged.
- */
- mt7530_rmw(priv, MT753X_RGAC2,
-- MT753X_R0E_BPDU_FR | MT753X_R0E_EG_TAG_MASK |
-- MT753X_R0E_PORT_FW_MASK | MT753X_R03_BPDU_FR |
-- MT753X_R03_EG_TAG_MASK | MT753X_R03_PORT_FW_MASK,
-- MT753X_R0E_BPDU_FR |
-- MT753X_R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-- MT753X_R0E_PORT_FW(MT753X_BPDU_CPU_ONLY) |
-- MT753X_R03_BPDU_FR |
-- MT753X_R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-- MT753X_BPDU_CPU_ONLY);
-+ R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK |
-+ R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK,
-+ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-+ R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR |
-+ R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
-+ TO_CPU_FW_CPU_ONLY);
- }
-
- static void
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -67,47 +67,47 @@ enum mt753x_id {
- #define MT753X_MIRROR_MASK(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
- MT7531_MIRROR_MASK : MIRROR_MASK)
-
--/* Registers for BPDU and PAE frame control*/
-+/* Register for BPDU and PAE frame control */
- #define MT753X_BPC 0x24
--#define MT753X_PAE_BPDU_FR BIT(25)
--#define MT753X_PAE_EG_TAG_MASK GENMASK(24, 22)
--#define MT753X_PAE_EG_TAG(x) FIELD_PREP(MT753X_PAE_EG_TAG_MASK, x)
--#define MT753X_PAE_PORT_FW_MASK GENMASK(18, 16)
--#define MT753X_PAE_PORT_FW(x) FIELD_PREP(MT753X_PAE_PORT_FW_MASK, x)
--#define MT753X_BPDU_EG_TAG_MASK GENMASK(8, 6)
--#define MT753X_BPDU_EG_TAG(x) FIELD_PREP(MT753X_BPDU_EG_TAG_MASK, x)
--#define MT753X_BPDU_PORT_FW_MASK GENMASK(2, 0)
-+#define PAE_BPDU_FR BIT(25)
-+#define PAE_EG_TAG_MASK GENMASK(24, 22)
-+#define PAE_EG_TAG(x) FIELD_PREP(PAE_EG_TAG_MASK, x)
-+#define PAE_PORT_FW_MASK GENMASK(18, 16)
-+#define PAE_PORT_FW(x) FIELD_PREP(PAE_PORT_FW_MASK, x)
-+#define BPDU_EG_TAG_MASK GENMASK(8, 6)
-+#define BPDU_EG_TAG(x) FIELD_PREP(BPDU_EG_TAG_MASK, x)
-+#define BPDU_PORT_FW_MASK GENMASK(2, 0)
-
--/* Register for :01 and :02 MAC DA frame control */
-+/* Register for 01-80-C2-00-00-[01,02] MAC DA frame control */
- #define MT753X_RGAC1 0x28
--#define MT753X_R02_BPDU_FR BIT(25)
--#define MT753X_R02_EG_TAG_MASK GENMASK(24, 22)
--#define MT753X_R02_EG_TAG(x) FIELD_PREP(MT753X_R02_EG_TAG_MASK, x)
--#define MT753X_R02_PORT_FW_MASK GENMASK(18, 16)
--#define MT753X_R02_PORT_FW(x) FIELD_PREP(MT753X_R02_PORT_FW_MASK, x)
--#define MT753X_R01_BPDU_FR BIT(9)
--#define MT753X_R01_EG_TAG_MASK GENMASK(8, 6)
--#define MT753X_R01_EG_TAG(x) FIELD_PREP(MT753X_R01_EG_TAG_MASK, x)
--#define MT753X_R01_PORT_FW_MASK GENMASK(2, 0)
-+#define R02_BPDU_FR BIT(25)
-+#define R02_EG_TAG_MASK GENMASK(24, 22)
-+#define R02_EG_TAG(x) FIELD_PREP(R02_EG_TAG_MASK, x)
-+#define R02_PORT_FW_MASK GENMASK(18, 16)
-+#define R02_PORT_FW(x) FIELD_PREP(R02_PORT_FW_MASK, x)
-+#define R01_BPDU_FR BIT(9)
-+#define R01_EG_TAG_MASK GENMASK(8, 6)
-+#define R01_EG_TAG(x) FIELD_PREP(R01_EG_TAG_MASK, x)
-+#define R01_PORT_FW_MASK GENMASK(2, 0)
-
--/* Register for :03 and :0E MAC DA frame control */
-+/* Register for 01-80-C2-00-00-[03,0E] MAC DA frame control */
- #define MT753X_RGAC2 0x2c
--#define MT753X_R0E_BPDU_FR BIT(25)
--#define MT753X_R0E_EG_TAG_MASK GENMASK(24, 22)
--#define MT753X_R0E_EG_TAG(x) FIELD_PREP(MT753X_R0E_EG_TAG_MASK, x)
--#define MT753X_R0E_PORT_FW_MASK GENMASK(18, 16)
--#define MT753X_R0E_PORT_FW(x) FIELD_PREP(MT753X_R0E_PORT_FW_MASK, x)
--#define MT753X_R03_BPDU_FR BIT(9)
--#define MT753X_R03_EG_TAG_MASK GENMASK(8, 6)
--#define MT753X_R03_EG_TAG(x) FIELD_PREP(MT753X_R03_EG_TAG_MASK, x)
--#define MT753X_R03_PORT_FW_MASK GENMASK(2, 0)
-+#define R0E_BPDU_FR BIT(25)
-+#define R0E_EG_TAG_MASK GENMASK(24, 22)
-+#define R0E_EG_TAG(x) FIELD_PREP(R0E_EG_TAG_MASK, x)
-+#define R0E_PORT_FW_MASK GENMASK(18, 16)
-+#define R0E_PORT_FW(x) FIELD_PREP(R0E_PORT_FW_MASK, x)
-+#define R03_BPDU_FR BIT(9)
-+#define R03_EG_TAG_MASK GENMASK(8, 6)
-+#define R03_EG_TAG(x) FIELD_PREP(R03_EG_TAG_MASK, x)
-+#define R03_PORT_FW_MASK GENMASK(2, 0)
-
--enum mt753x_bpdu_port_fw {
-- MT753X_BPDU_FOLLOW_MFC,
-- MT753X_BPDU_CPU_EXCLUDE = 4,
-- MT753X_BPDU_CPU_INCLUDE = 5,
-- MT753X_BPDU_CPU_ONLY = 6,
-- MT753X_BPDU_DROP = 7,
-+enum mt753x_to_cpu_fw {
-+ TO_CPU_FW_SYSTEM_DEFAULT,
-+ TO_CPU_FW_CPU_EXCLUDE = 4,
-+ TO_CPU_FW_CPU_INCLUDE = 5,
-+ TO_CPU_FW_CPU_ONLY = 6,
-+ TO_CPU_FW_DROP = 7,
- };
-
- /* Registers for address table access */
+++ /dev/null
-From 1dbc1bdc2869e6d2929235c70d64e393aa5a5fa2 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:12 +0300
-Subject: [PATCH 05/15] net: dsa: mt7530: refactor MT7530_MFC and MT7531_CFC,
- add MT7531_QRY_FFP
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The MT7530_MFC register is on MT7530, MT7531, and the switch on the MT7988
-SoC. Rename it to MT753X_MFC. Bit 7 to 0 differs between MT7530 and
-MT7531/MT7988. Add MT7530 prefix to these definitions, and define the
-IGMP/MLD Query Frame Flooding Ports mask for MT7531.
-
-Rename the cases of MIRROR_MASK to MIRROR_PORT_MASK.
-
-Move mt753x_mirror_port_get() and mt753x_port_mirror_set() to mt7530.h as
-macros.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 38 ++++++++--------------
- drivers/net/dsa/mt7530.h | 69 +++++++++++++++++++++++++---------------
- 2 files changed, 57 insertions(+), 50 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -1147,7 +1147,7 @@ mt753x_cpu_port_enable(struct dsa_switch
- PORT_SPEC_TAG);
-
- /* Enable flooding on the CPU port */
-- mt7530_set(priv, MT7530_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) |
-+ mt7530_set(priv, MT753X_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) |
- UNU_FFP(BIT(port)));
-
- /* Add the CPU port to the CPU port bitmap for MT7531 and the switch on
-@@ -1311,15 +1311,15 @@ mt7530_port_bridge_flags(struct dsa_swit
- flags.val & BR_LEARNING ? 0 : SA_DIS);
-
- if (flags.mask & BR_FLOOD)
-- mt7530_rmw(priv, MT7530_MFC, UNU_FFP(BIT(port)),
-+ mt7530_rmw(priv, MT753X_MFC, UNU_FFP(BIT(port)),
- flags.val & BR_FLOOD ? UNU_FFP(BIT(port)) : 0);
-
- if (flags.mask & BR_MCAST_FLOOD)
-- mt7530_rmw(priv, MT7530_MFC, UNM_FFP(BIT(port)),
-+ mt7530_rmw(priv, MT753X_MFC, UNM_FFP(BIT(port)),
- flags.val & BR_MCAST_FLOOD ? UNM_FFP(BIT(port)) : 0);
-
- if (flags.mask & BR_BCAST_FLOOD)
-- mt7530_rmw(priv, MT7530_MFC, BC_FFP(BIT(port)),
-+ mt7530_rmw(priv, MT753X_MFC, BC_FFP(BIT(port)),
- flags.val & BR_BCAST_FLOOD ? BC_FFP(BIT(port)) : 0);
-
- return 0;
-@@ -1855,20 +1855,6 @@ mt7530_port_vlan_del(struct dsa_switch *
- return 0;
- }
-
--static int mt753x_mirror_port_get(unsigned int id, u32 val)
--{
-- return (id == ID_MT7531 || id == ID_MT7988) ?
-- MT7531_MIRROR_PORT_GET(val) :
-- MIRROR_PORT(val);
--}
--
--static int mt753x_mirror_port_set(unsigned int id, u32 val)
--{
-- return (id == ID_MT7531 || id == ID_MT7988) ?
-- MT7531_MIRROR_PORT_SET(val) :
-- MIRROR_PORT(val);
--}
--
- static int mt753x_port_mirror_add(struct dsa_switch *ds, int port,
- struct dsa_mall_mirror_tc_entry *mirror,
- bool ingress, struct netlink_ext_ack *extack)
-@@ -1884,14 +1870,14 @@ static int mt753x_port_mirror_add(struct
- val = mt7530_read(priv, MT753X_MIRROR_REG(priv->id));
-
- /* MT7530 only supports one monitor port */
-- monitor_port = mt753x_mirror_port_get(priv->id, val);
-+ monitor_port = MT753X_MIRROR_PORT_GET(priv->id, val);
- if (val & MT753X_MIRROR_EN(priv->id) &&
- monitor_port != mirror->to_local_port)
- return -EEXIST;
-
- val |= MT753X_MIRROR_EN(priv->id);
-- val &= ~MT753X_MIRROR_MASK(priv->id);
-- val |= mt753x_mirror_port_set(priv->id, mirror->to_local_port);
-+ val &= ~MT753X_MIRROR_PORT_MASK(priv->id);
-+ val |= MT753X_MIRROR_PORT_SET(priv->id, mirror->to_local_port);
- mt7530_write(priv, MT753X_MIRROR_REG(priv->id), val);
-
- val = mt7530_read(priv, MT7530_PCR_P(port));
-@@ -2533,7 +2519,7 @@ mt7531_setup_common(struct dsa_switch *d
- mt7530_mib_reset(ds);
-
- /* Disable flooding on all ports */
-- mt7530_clear(priv, MT7530_MFC, BC_FFP_MASK | UNM_FFP_MASK |
-+ mt7530_clear(priv, MT753X_MFC, BC_FFP_MASK | UNM_FFP_MASK |
- UNU_FFP_MASK);
-
- for (i = 0; i < MT7530_NUM_PORTS; i++) {
-@@ -3085,10 +3071,12 @@ mt753x_conduit_state_change(struct dsa_s
- else
- priv->active_cpu_ports &= ~mask;
-
-- if (priv->active_cpu_ports)
-- val = CPU_EN | CPU_PORT(__ffs(priv->active_cpu_ports));
-+ if (priv->active_cpu_ports) {
-+ val = MT7530_CPU_EN |
-+ MT7530_CPU_PORT(__ffs(priv->active_cpu_ports));
-+ }
-
-- mt7530_rmw(priv, MT7530_MFC, CPU_EN | CPU_PORT_MASK, val);
-+ mt7530_rmw(priv, MT753X_MFC, MT7530_CPU_EN | MT7530_CPU_PORT_MASK, val);
- }
-
- static int mt7988_setup(struct dsa_switch *ds)
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -36,36 +36,55 @@ enum mt753x_id {
- #define MT753X_AGC 0xc
- #define LOCAL_EN BIT(7)
-
--/* Registers to mac forward control for unknown frames */
--#define MT7530_MFC 0x10
--#define BC_FFP(x) (((x) & 0xff) << 24)
--#define BC_FFP_MASK BC_FFP(~0)
--#define UNM_FFP(x) (((x) & 0xff) << 16)
--#define UNM_FFP_MASK UNM_FFP(~0)
--#define UNU_FFP(x) (((x) & 0xff) << 8)
--#define UNU_FFP_MASK UNU_FFP(~0)
--#define CPU_EN BIT(7)
--#define CPU_PORT_MASK GENMASK(6, 4)
--#define CPU_PORT(x) FIELD_PREP(CPU_PORT_MASK, x)
--#define MIRROR_EN BIT(3)
--#define MIRROR_PORT(x) ((x) & 0x7)
--#define MIRROR_MASK 0x7
-+/* Register for MAC forward control */
-+#define MT753X_MFC 0x10
-+#define BC_FFP_MASK GENMASK(31, 24)
-+#define BC_FFP(x) FIELD_PREP(BC_FFP_MASK, x)
-+#define UNM_FFP_MASK GENMASK(23, 16)
-+#define UNM_FFP(x) FIELD_PREP(UNM_FFP_MASK, x)
-+#define UNU_FFP_MASK GENMASK(15, 8)
-+#define UNU_FFP(x) FIELD_PREP(UNU_FFP_MASK, x)
-+#define MT7530_CPU_EN BIT(7)
-+#define MT7530_CPU_PORT_MASK GENMASK(6, 4)
-+#define MT7530_CPU_PORT(x) FIELD_PREP(MT7530_CPU_PORT_MASK, x)
-+#define MT7530_MIRROR_EN BIT(3)
-+#define MT7530_MIRROR_PORT_MASK GENMASK(2, 0)
-+#define MT7530_MIRROR_PORT_GET(x) FIELD_GET(MT7530_MIRROR_PORT_MASK, x)
-+#define MT7530_MIRROR_PORT_SET(x) FIELD_PREP(MT7530_MIRROR_PORT_MASK, x)
-+#define MT7531_QRY_FFP_MASK GENMASK(7, 0)
-+#define MT7531_QRY_FFP(x) FIELD_PREP(MT7531_QRY_FFP_MASK, x)
-
--/* Registers for CPU forward control */
-+/* Register for CPU forward control */
- #define MT7531_CFC 0x4
- #define MT7531_MIRROR_EN BIT(19)
--#define MT7531_MIRROR_MASK (MIRROR_MASK << 16)
--#define MT7531_MIRROR_PORT_GET(x) (((x) >> 16) & MIRROR_MASK)
--#define MT7531_MIRROR_PORT_SET(x) (((x) & MIRROR_MASK) << 16)
-+#define MT7531_MIRROR_PORT_MASK GENMASK(18, 16)
-+#define MT7531_MIRROR_PORT_GET(x) FIELD_GET(MT7531_MIRROR_PORT_MASK, x)
-+#define MT7531_MIRROR_PORT_SET(x) FIELD_PREP(MT7531_MIRROR_PORT_MASK, x)
- #define MT7531_CPU_PMAP_MASK GENMASK(7, 0)
- #define MT7531_CPU_PMAP(x) FIELD_PREP(MT7531_CPU_PMAP_MASK, x)
-
--#define MT753X_MIRROR_REG(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
-- MT7531_CFC : MT7530_MFC)
--#define MT753X_MIRROR_EN(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
-- MT7531_MIRROR_EN : MIRROR_EN)
--#define MT753X_MIRROR_MASK(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
-- MT7531_MIRROR_MASK : MIRROR_MASK)
-+#define MT753X_MIRROR_REG(id) ((id == ID_MT7531 || \
-+ id == ID_MT7988) ? \
-+ MT7531_CFC : MT753X_MFC)
-+
-+#define MT753X_MIRROR_EN(id) ((id == ID_MT7531 || \
-+ id == ID_MT7988) ? \
-+ MT7531_MIRROR_EN : MT7530_MIRROR_EN)
-+
-+#define MT753X_MIRROR_PORT_MASK(id) ((id == ID_MT7531 || \
-+ id == ID_MT7988) ? \
-+ MT7531_MIRROR_PORT_MASK : \
-+ MT7530_MIRROR_PORT_MASK)
-+
-+#define MT753X_MIRROR_PORT_GET(id, val) ((id == ID_MT7531 || \
-+ id == ID_MT7988) ? \
-+ MT7531_MIRROR_PORT_GET(val) : \
-+ MT7530_MIRROR_PORT_GET(val))
-+
-+#define MT753X_MIRROR_PORT_SET(id, val) ((id == ID_MT7531 || \
-+ id == ID_MT7988) ? \
-+ MT7531_MIRROR_PORT_SET(val) : \
-+ MT7530_MIRROR_PORT_SET(val))
-
- /* Register for BPDU and PAE frame control */
- #define MT753X_BPC 0x24
+++ /dev/null
-From 3ccf67597d35c06a7319e407b1c42f78a7966779 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:13 +0300
-Subject: [PATCH 06/15] net: dsa: mt7530: refactor MT7530_HWTRAP and
- MT7530_MHWTRAP
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The MT7530_HWTRAP and MT7530_MHWTRAP registers are on MT7530 and MT7531.
-It's called hardware trap on MT7530, software trap on MT7531. That's
-because some bits of the trap on MT7530 cannot be modified by software
-whilst all bits of the trap on MT7531 can. Rename the definitions for them
-to MT753X_TRAP and MT753X_MTRAP. Add MT7530 and MT7531 prefixes to the
-definitions specific to the switch model.
-
-Remove the extra parentheses from MT7530_XTAL_40MHZ and MT7530_XTAL_20MHZ.
-
-Rename MHWTRAP_PHY0_SEL, MHWTRAP_MANUAL, and MHWTRAP_PHY_ACCESS to be on
-par with the "MT7621 Giga Switch Programming Guide v0.3" document.
-
-Make an enumaration for the XTAL frequency. Set the data type of the xtal
-variable on mt7531_pll_setup() to it.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 59 ++++++++++++++++++++--------------------
- drivers/net/dsa/mt7530.h | 50 ++++++++++++++++------------------
- 2 files changed, 54 insertions(+), 55 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -417,23 +417,23 @@ mt7530_setup_port6(struct dsa_switch *ds
-
- mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, P6_INTF_MODE(1));
-
-- xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK;
-+ xtal = mt7530_read(priv, MT753X_MTRAP) & MT7530_XTAL_MASK;
-
-- if (xtal == HWTRAP_XTAL_25MHZ)
-+ if (xtal == MT7530_XTAL_25MHZ)
- ssc_delta = 0x57;
- else
- ssc_delta = 0x87;
-
- if (priv->id == ID_MT7621) {
- /* PLL frequency: 125MHz: 1.0GBit */
-- if (xtal == HWTRAP_XTAL_40MHZ)
-+ if (xtal == MT7530_XTAL_40MHZ)
- ncpo1 = 0x0640;
-- if (xtal == HWTRAP_XTAL_25MHZ)
-+ if (xtal == MT7530_XTAL_25MHZ)
- ncpo1 = 0x0a00;
- } else { /* PLL frequency: 250MHz: 2.0Gbit */
-- if (xtal == HWTRAP_XTAL_40MHZ)
-+ if (xtal == MT7530_XTAL_40MHZ)
- ncpo1 = 0x0c80;
-- if (xtal == HWTRAP_XTAL_25MHZ)
-+ if (xtal == MT7530_XTAL_25MHZ)
- ncpo1 = 0x1400;
- }
-
-@@ -456,19 +456,20 @@ mt7530_setup_port6(struct dsa_switch *ds
- static void
- mt7531_pll_setup(struct mt7530_priv *priv)
- {
-+ enum mt7531_xtal_fsel xtal;
- u32 top_sig;
- u32 hwstrap;
-- u32 xtal;
- u32 val;
-
- val = mt7530_read(priv, MT7531_CREV);
- top_sig = mt7530_read(priv, MT7531_TOP_SIG_SR);
-- hwstrap = mt7530_read(priv, MT7531_HWTRAP);
-+ hwstrap = mt7530_read(priv, MT753X_TRAP);
- if ((val & CHIP_REV_M) > 0)
-- xtal = (top_sig & PAD_MCM_SMI_EN) ? HWTRAP_XTAL_FSEL_40MHZ :
-- HWTRAP_XTAL_FSEL_25MHZ;
-+ xtal = (top_sig & PAD_MCM_SMI_EN) ? MT7531_XTAL_FSEL_40MHZ :
-+ MT7531_XTAL_FSEL_25MHZ;
- else
-- xtal = hwstrap & HWTRAP_XTAL_FSEL_MASK;
-+ xtal = (hwstrap & MT7531_XTAL25) ? MT7531_XTAL_FSEL_25MHZ :
-+ MT7531_XTAL_FSEL_40MHZ;
-
- /* Step 1 : Disable MT7531 COREPLL */
- val = mt7530_read(priv, MT7531_PLLGP_EN);
-@@ -497,13 +498,13 @@ mt7531_pll_setup(struct mt7530_priv *pri
- usleep_range(25, 35);
-
- switch (xtal) {
-- case HWTRAP_XTAL_FSEL_25MHZ:
-+ case MT7531_XTAL_FSEL_25MHZ:
- val = mt7530_read(priv, MT7531_PLLGP_CR0);
- val &= ~RG_COREPLL_SDM_PCW_M;
- val |= 0x140000 << RG_COREPLL_SDM_PCW_S;
- mt7530_write(priv, MT7531_PLLGP_CR0, val);
- break;
-- case HWTRAP_XTAL_FSEL_40MHZ:
-+ case MT7531_XTAL_FSEL_40MHZ:
- val = mt7530_read(priv, MT7531_PLLGP_CR0);
- val &= ~RG_COREPLL_SDM_PCW_M;
- val |= 0x190000 << RG_COREPLL_SDM_PCW_S;
-@@ -877,20 +878,20 @@ static void mt7530_setup_port5(struct ds
-
- mutex_lock(&priv->reg_mutex);
-
-- val = mt7530_read(priv, MT7530_MHWTRAP);
-+ val = mt7530_read(priv, MT753X_MTRAP);
-
-- val |= MHWTRAP_MANUAL | MHWTRAP_P5_MAC_SEL | MHWTRAP_P5_DIS;
-- val &= ~MHWTRAP_P5_RGMII_MODE & ~MHWTRAP_PHY0_SEL;
-+ val |= MT7530_CHG_TRAP | MT7530_P5_MAC_SEL | MT7530_P5_DIS;
-+ val &= ~MT7530_P5_RGMII_MODE & ~MT7530_P5_PHY0_SEL;
-
- switch (priv->p5_mode) {
- /* MUX_PHY_P0: P0 -> P5 -> SoC MAC */
- case MUX_PHY_P0:
-- val |= MHWTRAP_PHY0_SEL;
-+ val |= MT7530_P5_PHY0_SEL;
- fallthrough;
-
- /* MUX_PHY_P4: P4 -> P5 -> SoC MAC */
- case MUX_PHY_P4:
-- val &= ~MHWTRAP_P5_MAC_SEL & ~MHWTRAP_P5_DIS;
-+ val &= ~MT7530_P5_MAC_SEL & ~MT7530_P5_DIS;
-
- /* Setup the MAC by default for the cpu port */
- mt7530_write(priv, MT753X_PMCR_P(5), 0x56300);
-@@ -898,13 +899,13 @@ static void mt7530_setup_port5(struct ds
-
- /* GMAC5: P5 -> SoC MAC or external PHY */
- default:
-- val &= ~MHWTRAP_P5_DIS;
-+ val &= ~MT7530_P5_DIS;
- break;
- }
-
- /* Setup RGMII settings */
- if (phy_interface_mode_is_rgmii(interface)) {
-- val |= MHWTRAP_P5_RGMII_MODE;
-+ val |= MT7530_P5_RGMII_MODE;
-
- /* P5 RGMII RX Clock Control: delay setting for 1000M */
- mt7530_write(priv, MT7530_P5RGMIIRXCR, CSR_RGMII_EDGE_ALIGN);
-@@ -924,7 +925,7 @@ static void mt7530_setup_port5(struct ds
- P5_IO_CLK_DRV(1) | P5_IO_DATA_DRV(1));
- }
-
-- mt7530_write(priv, MT7530_MHWTRAP, val);
-+ mt7530_write(priv, MT753X_MTRAP, val);
-
- dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, mode=%s, phy-mode=%s\n", val,
- mt7530_p5_mode_str(priv->p5_mode), phy_modes(interface));
-@@ -2365,7 +2366,7 @@ mt7530_setup(struct dsa_switch *ds)
- }
-
- /* Waiting for MT7530 got to stable */
-- INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_HWTRAP);
-+ INIT_MT7530_DUMMY_POLL(&p, priv, MT753X_TRAP);
- ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0,
- 20, 1000000);
- if (ret < 0) {
-@@ -2380,7 +2381,7 @@ mt7530_setup(struct dsa_switch *ds)
- return -ENODEV;
- }
-
-- if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_20MHZ) {
-+ if ((val & MT7530_XTAL_MASK) == MT7530_XTAL_20MHZ) {
- dev_err(priv->dev,
- "MT7530 with a 20MHz XTAL is not supported!\n");
- return -EINVAL;
-@@ -2401,12 +2402,12 @@ mt7530_setup(struct dsa_switch *ds)
- RD_TAP_MASK, RD_TAP(16));
-
- /* Enable port 6 */
-- val = mt7530_read(priv, MT7530_MHWTRAP);
-- val &= ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS;
-- val |= MHWTRAP_MANUAL;
-- mt7530_write(priv, MT7530_MHWTRAP, val);
-+ val = mt7530_read(priv, MT753X_MTRAP);
-+ val &= ~MT7530_P6_DIS & ~MT7530_PHY_INDIRECT_ACCESS;
-+ val |= MT7530_CHG_TRAP;
-+ mt7530_write(priv, MT753X_MTRAP, val);
-
-- if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_40MHZ)
-+ if ((val & MT7530_XTAL_MASK) == MT7530_XTAL_40MHZ)
- mt7530_pll_setup(priv);
-
- mt753x_trap_frames(priv);
-@@ -2587,7 +2588,7 @@ mt7531_setup(struct dsa_switch *ds)
- }
-
- /* Waiting for MT7530 got to stable */
-- INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_HWTRAP);
-+ INIT_MT7530_DUMMY_POLL(&p, priv, MT753X_TRAP);
- ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0,
- 20, 1000000);
- if (ret < 0) {
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -495,32 +495,30 @@ enum mt7531_clk_skew {
- MT7531_CLK_SKEW_REVERSE = 3,
- };
-
--/* Register for hw trap status */
--#define MT7530_HWTRAP 0x7800
--#define HWTRAP_XTAL_MASK (BIT(10) | BIT(9))
--#define HWTRAP_XTAL_25MHZ (BIT(10) | BIT(9))
--#define HWTRAP_XTAL_40MHZ (BIT(10))
--#define HWTRAP_XTAL_20MHZ (BIT(9))
-+/* Register for trap status */
-+#define MT753X_TRAP 0x7800
-+#define MT7530_XTAL_MASK (BIT(10) | BIT(9))
-+#define MT7530_XTAL_25MHZ (BIT(10) | BIT(9))
-+#define MT7530_XTAL_40MHZ BIT(10)
-+#define MT7530_XTAL_20MHZ BIT(9)
-+#define MT7531_XTAL25 BIT(7)
-
--#define MT7531_HWTRAP 0x7800
--#define HWTRAP_XTAL_FSEL_MASK BIT(7)
--#define HWTRAP_XTAL_FSEL_25MHZ BIT(7)
--#define HWTRAP_XTAL_FSEL_40MHZ 0
--/* Unique fields of (M)HWSTRAP for MT7531 */
--#define XTAL_FSEL_S 7
--#define XTAL_FSEL_M BIT(7)
--#define PHY_EN BIT(6)
--#define CHG_STRAP BIT(8)
-+/* Register for trap modification */
-+#define MT753X_MTRAP 0x7804
-+#define MT7530_P5_PHY0_SEL BIT(20)
-+#define MT7530_CHG_TRAP BIT(16)
-+#define MT7530_P5_MAC_SEL BIT(13)
-+#define MT7530_P6_DIS BIT(8)
-+#define MT7530_P5_RGMII_MODE BIT(7)
-+#define MT7530_P5_DIS BIT(6)
-+#define MT7530_PHY_INDIRECT_ACCESS BIT(5)
-+#define MT7531_CHG_STRAP BIT(8)
-+#define MT7531_PHY_EN BIT(6)
-
--/* Register for hw trap modification */
--#define MT7530_MHWTRAP 0x7804
--#define MHWTRAP_PHY0_SEL BIT(20)
--#define MHWTRAP_MANUAL BIT(16)
--#define MHWTRAP_P5_MAC_SEL BIT(13)
--#define MHWTRAP_P6_DIS BIT(8)
--#define MHWTRAP_P5_RGMII_MODE BIT(7)
--#define MHWTRAP_P5_DIS BIT(6)
--#define MHWTRAP_PHY_ACCESS BIT(5)
-+enum mt7531_xtal_fsel {
-+ MT7531_XTAL_FSEL_25MHZ,
-+ MT7531_XTAL_FSEL_40MHZ,
-+};
-
- /* Register for TOP signal control */
- #define MT7530_TOP_SIG_CTRL 0x7808
+++ /dev/null
-From 2982f395c9a513b168f1e685588f70013cba2f5f Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:14 +0300
-Subject: [PATCH 07/15] net: dsa: mt7530: move MT753X_MTRAP operations for
- MT7530
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-On MT7530, the media-independent interfaces of port 5 and 6 are controlled
-by the MT7530_P5_DIS and MT7530_P6_DIS bits of the hardware trap. Deal with
-these bits only when the relevant port is being enabled or disabled. This
-ensures that these ports will be disabled when they are not in use.
-
-Do not set MT7530_CHG_TRAP on mt7530_setup_port5() as that's already being
-done on mt7530_setup().
-
-Instead of globally setting MT7530_P5_MAC_SEL, clear it, then set it only
-on the appropriate case.
-
-If PHY muxing is detected, clear MT7530_P5_DIS before calling
-mt7530_setup_port5().
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 38 +++++++++++++++++++++++++++-----------
- 1 file changed, 27 insertions(+), 11 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -880,8 +880,7 @@ static void mt7530_setup_port5(struct ds
-
- val = mt7530_read(priv, MT753X_MTRAP);
-
-- val |= MT7530_CHG_TRAP | MT7530_P5_MAC_SEL | MT7530_P5_DIS;
-- val &= ~MT7530_P5_RGMII_MODE & ~MT7530_P5_PHY0_SEL;
-+ val &= ~MT7530_P5_PHY0_SEL & ~MT7530_P5_MAC_SEL & ~MT7530_P5_RGMII_MODE;
-
- switch (priv->p5_mode) {
- /* MUX_PHY_P0: P0 -> P5 -> SoC MAC */
-@@ -891,15 +890,13 @@ static void mt7530_setup_port5(struct ds
-
- /* MUX_PHY_P4: P4 -> P5 -> SoC MAC */
- case MUX_PHY_P4:
-- val &= ~MT7530_P5_MAC_SEL & ~MT7530_P5_DIS;
--
- /* Setup the MAC by default for the cpu port */
- mt7530_write(priv, MT753X_PMCR_P(5), 0x56300);
- break;
-
- /* GMAC5: P5 -> SoC MAC or external PHY */
- default:
-- val &= ~MT7530_P5_DIS;
-+ val |= MT7530_P5_MAC_SEL;
- break;
- }
-
-@@ -1193,6 +1190,14 @@ mt7530_port_enable(struct dsa_switch *ds
-
- mutex_unlock(&priv->reg_mutex);
-
-+ if (priv->id != ID_MT7530 && priv->id != ID_MT7621)
-+ return 0;
-+
-+ if (port == 5)
-+ mt7530_clear(priv, MT753X_MTRAP, MT7530_P5_DIS);
-+ else if (port == 6)
-+ mt7530_clear(priv, MT753X_MTRAP, MT7530_P6_DIS);
-+
- return 0;
- }
-
-@@ -1211,6 +1216,14 @@ mt7530_port_disable(struct dsa_switch *d
- PCR_MATRIX_CLR);
-
- mutex_unlock(&priv->reg_mutex);
-+
-+ if (priv->id != ID_MT7530 && priv->id != ID_MT7621)
-+ return;
-+
-+ if (port == 5)
-+ mt7530_set(priv, MT753X_MTRAP, MT7530_P5_DIS);
-+ else if (port == 6)
-+ mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS);
- }
-
- static int
-@@ -2401,11 +2414,11 @@ mt7530_setup(struct dsa_switch *ds)
- mt7530_rmw(priv, MT7530_TRGMII_RD(i),
- RD_TAP_MASK, RD_TAP(16));
-
-- /* Enable port 6 */
-- val = mt7530_read(priv, MT753X_MTRAP);
-- val &= ~MT7530_P6_DIS & ~MT7530_PHY_INDIRECT_ACCESS;
-- val |= MT7530_CHG_TRAP;
-- mt7530_write(priv, MT753X_MTRAP, val);
-+ /* Allow modifying the trap and directly access PHY registers via the
-+ * MDIO bus the switch is on.
-+ */
-+ mt7530_rmw(priv, MT753X_MTRAP, MT7530_CHG_TRAP |
-+ MT7530_PHY_INDIRECT_ACCESS, MT7530_CHG_TRAP);
-
- if ((val & MT7530_XTAL_MASK) == MT7530_XTAL_40MHZ)
- mt7530_pll_setup(priv);
-@@ -2488,8 +2501,11 @@ mt7530_setup(struct dsa_switch *ds)
- break;
- }
-
-- if (priv->p5_mode == MUX_PHY_P0 || priv->p5_mode == MUX_PHY_P4)
-+ if (priv->p5_mode == MUX_PHY_P0 ||
-+ priv->p5_mode == MUX_PHY_P4) {
-+ mt7530_clear(priv, MT753X_MTRAP, MT7530_P5_DIS);
- mt7530_setup_port5(ds, interface);
-+ }
- }
-
- #ifdef CONFIG_GPIOLIB
+++ /dev/null
-From 1f5669efca65564c7533704917f79003c6b36c9c Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:15 +0300
-Subject: [PATCH 08/15] net: dsa: mt7530: return mt7530_setup_mdio &
- mt7531_setup_common on error
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The mt7530_setup_mdio() and mt7531_setup_common() functions should be
-checked for errors. Return if the functions return a non-zero value.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2668,7 +2668,9 @@ mt7531_setup(struct dsa_switch *ds)
- 0);
- }
-
-- mt7531_setup_common(ds);
-+ ret = mt7531_setup_common(ds);
-+ if (ret)
-+ return ret;
-
- ds->assisted_learning_on_cpu_port = true;
- ds->mtu_enforcement_ingress = true;
-@@ -3016,6 +3018,8 @@ mt753x_setup(struct dsa_switch *ds)
- ret = mt7530_setup_mdio(priv);
- if (ret && priv->irq)
- mt7530_free_irq_common(priv);
-+ if (ret)
-+ return ret;
-
- /* Initialise the PCS devices */
- for (i = 0; i < priv->ds->num_ports; i++) {
+++ /dev/null
-From 6cc2d4ccd77509df74b7b8ef46bbc6ba0a571318 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:16 +0300
-Subject: [PATCH 09/15] net: dsa: mt7530: define MAC speed capabilities per
- switch model
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-With the support of the MT7988 SoC switch, the MAC speed capabilities
-defined on mt753x_phylink_get_caps() won't apply to all switch models
-anymore. Move them to more appropriate locations instead of overwriting
-config->mac_capabilities.
-
-Remove the comment on mt753x_phylink_get_caps() as it's become invalid with
-the support of MT7531 and MT7988 SoC switch.
-
-Add break to case 6 of mt7988_mac_port_get_caps() to be explicit.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 15 ++++++++++-----
- 1 file changed, 10 insertions(+), 5 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2681,6 +2681,8 @@ mt7531_setup(struct dsa_switch *ds)
- static void mt7530_mac_port_get_caps(struct dsa_switch *ds, int port,
- struct phylink_config *config)
- {
-+ config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000FD;
-+
- switch (port) {
- /* Ports which are connected to switch PHYs. There is no MII pinout. */
- case 0 ... 4:
-@@ -2712,6 +2714,8 @@ static void mt7531_mac_port_get_caps(str
- {
- struct mt7530_priv *priv = ds->priv;
-
-+ config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000FD;
-+
- switch (port) {
- /* Ports which are connected to switch PHYs. There is no MII pinout. */
- case 0 ... 4:
-@@ -2751,14 +2755,17 @@ static void mt7988_mac_port_get_caps(str
- case 0 ... 3:
- __set_bit(PHY_INTERFACE_MODE_INTERNAL,
- config->supported_interfaces);
-+
-+ config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000FD;
- break;
-
- /* Port 6 is connected to SoC's XGMII MAC. There is no MII pinout. */
- case 6:
- __set_bit(PHY_INTERFACE_MODE_INTERNAL,
- config->supported_interfaces);
-- config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
-- MAC_10000FD;
-+
-+ config->mac_capabilities |= MAC_10000FD;
-+ break;
- }
- }
-
-@@ -2928,9 +2935,7 @@ static void mt753x_phylink_get_caps(stru
- {
- struct mt7530_priv *priv = ds->priv;
-
-- /* This switch only supports full-duplex at 1Gbps */
-- config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
-- MAC_10 | MAC_100 | MAC_1000FD;
-+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE;
-
- priv->info->mac_port_get_caps(ds, port, config);
- }
+++ /dev/null
-From dd0f15fc877c10567699190bce0f55e96f4ad6b5 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:17 +0300
-Subject: [PATCH 10/15] net: dsa: mt7530: get rid of function sanity check
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Get rid of checking whether functions are filled properly. priv->info which
-is an mt753x_info structure is filled and checked for before this check.
-It's unnecessary checking whether it's filled properly.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 7 -------
- 1 file changed, 7 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -3228,13 +3228,6 @@ mt7530_probe_common(struct mt7530_priv *
- if (!priv->info)
- return -EINVAL;
-
-- /* Sanity check if these required device operations are filled
-- * properly.
-- */
-- if (!priv->info->sw_setup || !priv->info->phy_read_c22 ||
-- !priv->info->phy_write_c22 || !priv->info->mac_port_get_caps)
-- return -EINVAL;
--
- priv->id = priv->info->id;
- priv->dev = dev;
- priv->ds->priv = priv;
+++ /dev/null
-From 2dff9759602b069f97ccc939e15a47ca051b2983 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:18 +0300
-Subject: [PATCH 11/15] net: dsa: mt7530: refactor MT7530_PMEEECR_P()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The MT7530_PMEEECR_P() register is on MT7530, MT7531, and the switch on the
-MT7988 SoC. Rename the definition for them to MT753X_PMEEECR_P(). Use the
-FIELD_PREP and FIELD_GET macros. Rename GET_LPI_THRESH() and
-SET_LPI_THRESH() to LPI_THRESH_GET() and LPI_THRESH_SET().
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 8 ++++----
- drivers/net/dsa/mt7530.h | 13 +++++++------
- 2 files changed, 11 insertions(+), 10 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -3047,10 +3047,10 @@ static int mt753x_get_mac_eee(struct dsa
- struct ethtool_eee *e)
- {
- struct mt7530_priv *priv = ds->priv;
-- u32 eeecr = mt7530_read(priv, MT7530_PMEEECR_P(port));
-+ u32 eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port));
-
- e->tx_lpi_enabled = !(eeecr & LPI_MODE_EN);
-- e->tx_lpi_timer = GET_LPI_THRESH(eeecr);
-+ e->tx_lpi_timer = LPI_THRESH_GET(eeecr);
-
- return 0;
- }
-@@ -3064,11 +3064,11 @@ static int mt753x_set_mac_eee(struct dsa
- if (e->tx_lpi_timer > 0xFFF)
- return -EINVAL;
-
-- set = SET_LPI_THRESH(e->tx_lpi_timer);
-+ set = LPI_THRESH_SET(e->tx_lpi_timer);
- if (!e->tx_lpi_enabled)
- /* Force LPI Mode without a delay */
- set |= LPI_MODE_EN;
-- mt7530_rmw(priv, MT7530_PMEEECR_P(port), mask, set);
-+ mt7530_rmw(priv, MT753X_PMEEECR_P(port), mask, set);
-
- return 0;
- }
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -364,13 +364,14 @@ enum mt7530_vlan_port_acc_frm {
- PMCR_FORCE_SPEED_100 | \
- PMCR_FORCE_FDX | PMCR_FORCE_LNK)
-
--#define MT7530_PMEEECR_P(x) (0x3004 + (x) * 0x100)
--#define WAKEUP_TIME_1000(x) (((x) & 0xFF) << 24)
--#define WAKEUP_TIME_100(x) (((x) & 0xFF) << 16)
-+#define MT753X_PMEEECR_P(x) (0x3004 + (x) * 0x100)
-+#define WAKEUP_TIME_1000_MASK GENMASK(31, 24)
-+#define WAKEUP_TIME_1000(x) FIELD_PREP(WAKEUP_TIME_1000_MASK, x)
-+#define WAKEUP_TIME_100_MASK GENMASK(23, 16)
-+#define WAKEUP_TIME_100(x) FIELD_PREP(WAKEUP_TIME_100_MASK, x)
- #define LPI_THRESH_MASK GENMASK(15, 4)
--#define LPI_THRESH_SHT 4
--#define SET_LPI_THRESH(x) (((x) << LPI_THRESH_SHT) & LPI_THRESH_MASK)
--#define GET_LPI_THRESH(x) (((x) & LPI_THRESH_MASK) >> LPI_THRESH_SHT)
-+#define LPI_THRESH_GET(x) FIELD_GET(LPI_THRESH_MASK, x)
-+#define LPI_THRESH_SET(x) FIELD_PREP(LPI_THRESH_MASK, x)
- #define LPI_MODE_EN BIT(0)
-
- #define MT7530_PMSR_P(x) (0x3008 + (x) * 0x100)
+++ /dev/null
-From 21d67c2fabfe40baf33202d3287b67b6c16f8382 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:19 +0300
-Subject: [PATCH 12/15] net: dsa: mt7530: get rid of mac_port_validate member
- of mt753x_info
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The mac_port_validate member of the mt753x_info structure is not being
-used, remove it. Improve the member description section in the process.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.h | 10 +++-------
- 1 file changed, 3 insertions(+), 7 deletions(-)
-
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -743,15 +743,14 @@ struct mt753x_pcs {
-
- /* struct mt753x_info - This is the main data structure for holding the specific
- * part for each supported device
-+ * @id: Holding the identifier to a switch model
-+ * @pcs_ops: Holding the pointer to the MAC PCS operations structure
- * @sw_setup: Holding the handler to a device initialization
- * @phy_read_c22: Holding the way reading PHY port using C22
- * @phy_write_c22: Holding the way writing PHY port using C22
- * @phy_read_c45: Holding the way reading PHY port using C45
- * @phy_write_c45: Holding the way writing PHY port using C45
-- * @phy_mode_supported: Check if the PHY type is being supported on a certain
-- * port
-- * @mac_port_validate: Holding the way to set addition validate type for a
-- * certan MAC port
-+ * @mac_port_get_caps: Holding the handler that provides MAC capabilities
- * @mac_port_config: Holding the way setting up the PHY attribute to a
- * certain MAC port
- */
-@@ -770,9 +769,6 @@ struct mt753x_info {
- int regnum, u16 val);
- void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
- struct phylink_config *config);
-- void (*mac_port_validate)(struct dsa_switch *ds, int port,
-- phy_interface_t interface,
-- unsigned long *supported);
- void (*mac_port_config)(struct dsa_switch *ds, int port,
- unsigned int mode,
- phy_interface_t interface);
+++ /dev/null
-From 6efc8ae3eb0363328f479191a0cf0dc12a16e090 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:20 +0300
-Subject: [PATCH 13/15] net: dsa: mt7530: use priv->ds->num_ports instead of
- MT7530_NUM_PORTS
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Use priv->ds->num_ports on all for loops which configure the switch
-registers. In the future, the value of MT7530_NUM_PORTS will depend on
-priv->id. Therefore, this change prepares the subdriver for a simpler
-implementation.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -1411,7 +1411,7 @@ mt7530_port_set_vlan_unaware(struct dsa_
- mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
- G0_PORT_VID_DEF);
-
-- for (i = 0; i < MT7530_NUM_PORTS; i++) {
-+ for (i = 0; i < priv->ds->num_ports; i++) {
- if (dsa_is_user_port(ds, i) &&
- dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) {
- all_user_ports_removed = false;
-@@ -2428,7 +2428,7 @@ mt7530_setup(struct dsa_switch *ds)
- /* Enable and reset MIB counters */
- mt7530_mib_reset(ds);
-
-- for (i = 0; i < MT7530_NUM_PORTS; i++) {
-+ for (i = 0; i < priv->ds->num_ports; i++) {
- /* Clear link settings and enable force mode to force link down
- * on all ports until they're enabled later.
- */
-@@ -2539,7 +2539,7 @@ mt7531_setup_common(struct dsa_switch *d
- mt7530_clear(priv, MT753X_MFC, BC_FFP_MASK | UNM_FFP_MASK |
- UNU_FFP_MASK);
-
-- for (i = 0; i < MT7530_NUM_PORTS; i++) {
-+ for (i = 0; i < priv->ds->num_ports; i++) {
- /* Clear link settings and enable force mode to force link down
- * on all ports until they're enabled later.
- */
-@@ -2627,7 +2627,7 @@ mt7531_setup(struct dsa_switch *ds)
- priv->p5_sgmii = !!(val & PAD_DUAL_SGMII_EN);
-
- /* Force link down on all ports before internal reset */
-- for (i = 0; i < MT7530_NUM_PORTS; i++)
-+ for (i = 0; i < priv->ds->num_ports; i++)
- mt7530_write(priv, MT753X_PMCR_P(i), MT7531_FORCE_MODE_LNK);
-
- /* Reset the switch through internal reset */
+++ /dev/null
-From c078ebbf5f6f6d8390035a9f92eeab766b78884d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:21 +0300
-Subject: [PATCH 14/15] net: dsa: mt7530: do not pass port variable to
- mt7531_rgmii_setup()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The mt7531_rgmii_setup() function does not use the port variable, do not
-pass the variable to it.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2781,7 +2781,7 @@ mt7530_mac_config(struct dsa_switch *ds,
- mt7530_setup_port6(priv->ds, interface);
- }
-
--static void mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port,
-+static void mt7531_rgmii_setup(struct mt7530_priv *priv,
- phy_interface_t interface,
- struct phy_device *phydev)
- {
-@@ -2832,7 +2832,7 @@ mt7531_mac_config(struct dsa_switch *ds,
- if (phy_interface_mode_is_rgmii(interface)) {
- dp = dsa_to_port(ds, port);
- phydev = dp->slave->phydev;
-- mt7531_rgmii_setup(priv, port, interface, phydev);
-+ mt7531_rgmii_setup(priv, interface, phydev);
- }
- }
-
+++ /dev/null
-From e7a9cc3cc00b40e0bc2bae40bd2ece0e48fa51d5 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Mon, 22 Apr 2024 10:15:22 +0300
-Subject: [PATCH 15/15] net: dsa: mt7530: explain exposing MDIO bus of MT7531AE
- better
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Unlike MT7531BE, the GPIO 6-12 pins are not used for RGMII on MT7531AE.
-Therefore, the GPIO 11-12 pins are set to function as MDC and MDIO to
-expose the MDIO bus of the switch. Replace the comment with a better
-explanation.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
----
- drivers/net/dsa/mt7530.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2636,7 +2636,10 @@ mt7531_setup(struct dsa_switch *ds)
- if (!priv->p5_sgmii) {
- mt7531_pll_setup(priv);
- } else {
-- /* Let ds->slave_mii_bus be able to access external phy. */
-+ /* Unlike MT7531BE, the GPIO 6-12 pins are not used for RGMII on
-+ * MT7531AE. Set the GPIO 11-12 pins to function as MDC and MDIO
-+ * to expose the MDIO bus of the switch.
-+ */
- mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO11_RG_RXD2_MASK,
- MT7531_EXT_P_MDC_11);
- mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO12_RG_RXD3_MASK,
+++ /dev/null
-From 16e6592cd5c5bd74d8890973489f60176c692614 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Sun, 28 Apr 2024 12:19:58 +0300
-Subject: [PATCH] net: dsa: mt7530: do not set MT7530_P5_DIS when PHY muxing is
- being used
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-DSA initalises the ds->num_ports amount of ports in
-dsa_switch_touch_ports(). When the PHY muxing feature is in use, port 5
-won't be defined in the device tree. Because of this, the type member of
-the dsa_port structure for this port will be assigned DSA_PORT_TYPE_UNUSED.
-The dsa_port_setup() function calls ds->ops->port_disable() when the port
-type is DSA_PORT_TYPE_UNUSED.
-
-The MT7530_P5_DIS bit is unset in mt7530_setup() when PHY muxing is being
-used. mt7530_port_disable() which is assigned to ds->ops->port_disable() is
-called afterwards. Currently, mt7530_port_disable() sets MT7530_P5_DIS
-which breaks network connectivity when PHY muxing is being used.
-
-Therefore, do not set MT7530_P5_DIS when PHY muxing is being used.
-
-Fixes: 377174c5760c ("net: dsa: mt7530: move MT753X_MTRAP operations for MT7530")
-Reported-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240428-for-netnext-mt7530-do-not-disable-port5-when-phy-muxing-v2-1-bb7c37d293f8@arinc9.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -1220,7 +1220,8 @@ mt7530_port_disable(struct dsa_switch *d
- if (priv->id != ID_MT7530 && priv->id != ID_MT7621)
- return;
-
-- if (port == 5)
-+ /* Do not set MT7530_P5_DIS when port 5 is being used for PHY muxing. */
-+ if (port == 5 && priv->p5_mode == GMAC5)
- mt7530_set(priv, MT753X_MTRAP, MT7530_P5_DIS);
- else if (port == 6)
- mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS);
+++ /dev/null
-From d8dcf5bd6d0eace9f7c1daa14b63b3925b09d033 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Tue, 30 Apr 2024 08:01:33 +0300
-Subject: [PATCH] net: dsa: mt7530: detect PHY muxing when PHY is defined on
- switch MDIO bus
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Currently, the MT7530 DSA subdriver configures the MT7530 switch to provide
-direct access to switch PHYs, meaning, the switch PHYs listen on the MDIO
-bus the switch listens on. The PHY muxing feature makes use of this.
-
-This is problematic as the PHY may be attached before the switch is
-initialised, in which case, the PHY will fail to be attached.
-
-Since commit 91374ba537bd ("net: dsa: mt7530: support OF-based registration
-of switch MDIO bus"), we can describe the switch PHYs on the MDIO bus of
-the switch on the device tree. Extend the check to detect PHY muxing when
-the PHY is defined on the MDIO bus of the switch on the device tree.
-
-When the PHY is described this way, the switch will be initialised first,
-then the switch MDIO bus will be registered. Only after these steps, the
-PHY will be attached.
-
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Reviewed-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/20240430-b4-for-netnext-mt7530-use-switch-mdio-bus-for-phy-muxing-v2-1-9104d886d0db@arinc9.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mt7530.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2484,7 +2484,8 @@ mt7530_setup(struct dsa_switch *ds)
- if (!phy_node)
- continue;
-
-- if (phy_node->parent == priv->dev->of_node->parent) {
-+ if (phy_node->parent == priv->dev->of_node->parent ||
-+ phy_node->parent->parent == priv->dev->of_node) {
- ret = of_get_phy_mode(mac_np, &interface);
- if (ret && ret != -ENODEV) {
- of_node_put(mac_np);
+++ /dev/null
-From c25c961fc7f36682f0a530150f1b7453ebc344cd Mon Sep 17 00:00:00 2001
-From: Matthias Schiffer <mschiffer@universe-factory.net>
-Date: Tue, 18 Jun 2024 09:17:12 +0200
-Subject: [PATCH 1/2] net: dsa: mt7530: factor out bridge join/leave logic
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-As preparation for implementing bridge port isolation, move the logic to
-add and remove bits in the port matrix into a new helper
-mt7530_update_port_member(), which is called from
-mt7530_port_bridge_join() and mt7530_port_bridge_leave().
-
-Another part of the preparation is using dsa_port_offloads_bridge_dev()
-instead of dsa_port_offloads_bridge() to check for bridge membership, as
-we don't have a struct dsa_bridge in mt7530_port_bridge_flags().
-
-The port matrix setting is slightly streamlined, now always first setting
-the mt7530_port's pm field and then writing the port matrix from that
-field into the hardware register, instead of duplicating the bit
-manipulation for both the struct field and the register.
-
-mt7530_port_bridge_join() was previously using |= to update the port
-matrix with the port bitmap, which was unnecessary, as pm would only
-have the CPU port set before joining a bridge; a simple assignment can
-be used for both joining and leaving (and will also work when individual
-bits are added/removed in port_bitmap with regard to the previous port
-matrix, which is what happens with port isolation).
-
-No functional change intended.
-
-Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
-Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
-Reviewed-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Tested-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 105 ++++++++++++++++++---------------------
- 1 file changed, 48 insertions(+), 57 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -1302,6 +1302,52 @@ mt7530_stp_state_set(struct dsa_switch *
- FID_PST(FID_BRIDGED, stp_state));
- }
-
-+static void mt7530_update_port_member(struct mt7530_priv *priv, int port,
-+ const struct net_device *bridge_dev,
-+ bool join) __must_hold(&priv->reg_mutex)
-+{
-+ struct dsa_port *dp = dsa_to_port(priv->ds, port), *other_dp;
-+ struct mt7530_port *p = &priv->ports[port], *other_p;
-+ struct dsa_port *cpu_dp = dp->cpu_dp;
-+ u32 port_bitmap = BIT(cpu_dp->index);
-+ int other_port;
-+
-+ dsa_switch_for_each_user_port(other_dp, priv->ds) {
-+ other_port = other_dp->index;
-+ other_p = &priv->ports[other_port];
-+
-+ if (dp == other_dp)
-+ continue;
-+
-+ /* Add/remove this port to/from the port matrix of the other
-+ * ports in the same bridge. If the port is disabled, port
-+ * matrix is kept and not being setup until the port becomes
-+ * enabled.
-+ */
-+ if (!dsa_port_offloads_bridge_dev(other_dp, bridge_dev))
-+ continue;
-+
-+ if (join) {
-+ other_p->pm |= PCR_MATRIX(BIT(port));
-+ port_bitmap |= BIT(other_port);
-+ } else {
-+ other_p->pm &= ~PCR_MATRIX(BIT(port));
-+ }
-+
-+ if (other_p->enable)
-+ mt7530_rmw(priv, MT7530_PCR_P(other_port),
-+ PCR_MATRIX_MASK, other_p->pm);
-+ }
-+
-+ /* Add/remove the all other ports to this port matrix. For !join
-+ * (leaving the bridge), only the CPU port will remain in the port matrix
-+ * of this port.
-+ */
-+ p->pm = PCR_MATRIX(port_bitmap);
-+ if (priv->ports[port].enable)
-+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, p->pm);
-+}
-+
- static int
- mt7530_port_pre_bridge_flags(struct dsa_switch *ds, int port,
- struct switchdev_brport_flags flags,
-@@ -1345,39 +1391,11 @@ mt7530_port_bridge_join(struct dsa_switc
- struct dsa_bridge bridge, bool *tx_fwd_offload,
- struct netlink_ext_ack *extack)
- {
-- struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
-- struct dsa_port *cpu_dp = dp->cpu_dp;
-- u32 port_bitmap = BIT(cpu_dp->index);
- struct mt7530_priv *priv = ds->priv;
-
- mutex_lock(&priv->reg_mutex);
-
-- dsa_switch_for_each_user_port(other_dp, ds) {
-- int other_port = other_dp->index;
--
-- if (dp == other_dp)
-- continue;
--
-- /* Add this port to the port matrix of the other ports in the
-- * same bridge. If the port is disabled, port matrix is kept
-- * and not being setup until the port becomes enabled.
-- */
-- if (!dsa_port_offloads_bridge(other_dp, &bridge))
-- continue;
--
-- if (priv->ports[other_port].enable)
-- mt7530_set(priv, MT7530_PCR_P(other_port),
-- PCR_MATRIX(BIT(port)));
-- priv->ports[other_port].pm |= PCR_MATRIX(BIT(port));
--
-- port_bitmap |= BIT(other_port);
-- }
--
-- /* Add the all other ports to this port matrix. */
-- if (priv->ports[port].enable)
-- mt7530_rmw(priv, MT7530_PCR_P(port),
-- PCR_MATRIX_MASK, PCR_MATRIX(port_bitmap));
-- priv->ports[port].pm |= PCR_MATRIX(port_bitmap);
-+ mt7530_update_port_member(priv, port, bridge.dev, true);
-
- /* Set to fallback mode for independent VLAN learning */
- mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
-@@ -1478,38 +1496,11 @@ static void
- mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
- struct dsa_bridge bridge)
- {
-- struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
-- struct dsa_port *cpu_dp = dp->cpu_dp;
- struct mt7530_priv *priv = ds->priv;
-
- mutex_lock(&priv->reg_mutex);
-
-- dsa_switch_for_each_user_port(other_dp, ds) {
-- int other_port = other_dp->index;
--
-- if (dp == other_dp)
-- continue;
--
-- /* Remove this port from the port matrix of the other ports
-- * in the same bridge. If the port is disabled, port matrix
-- * is kept and not being setup until the port becomes enabled.
-- */
-- if (!dsa_port_offloads_bridge(other_dp, &bridge))
-- continue;
--
-- if (priv->ports[other_port].enable)
-- mt7530_clear(priv, MT7530_PCR_P(other_port),
-- PCR_MATRIX(BIT(port)));
-- priv->ports[other_port].pm &= ~PCR_MATRIX(BIT(port));
-- }
--
-- /* Set the cpu port to be the only one in the port matrix of
-- * this port.
-- */
-- if (priv->ports[port].enable)
-- mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
-- PCR_MATRIX(BIT(cpu_dp->index)));
-- priv->ports[port].pm = PCR_MATRIX(BIT(cpu_dp->index));
-+ mt7530_update_port_member(priv, port, bridge.dev, false);
-
- /* When a port is removed from the bridge, the port would be set up
- * back to the default as is at initial boot which is a VLAN-unaware
+++ /dev/null
-From 3d49ee2127c26fd2c77944fd2e3168c057f99439 Mon Sep 17 00:00:00 2001
-From: Matthias Schiffer <mschiffer@universe-factory.net>
-Date: Tue, 18 Jun 2024 09:17:13 +0200
-Subject: [PATCH 2/2] net: dsa: mt7530: add support for bridge port isolation
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Remove a pair of ports from the port matrix when both ports have the
-isolated flag set.
-
-Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
-Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
-Reviewed-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Tested-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 18 ++++++++++++++++--
- drivers/net/dsa/mt7530.h | 1 +
- 2 files changed, 17 insertions(+), 2 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -1311,6 +1311,7 @@ static void mt7530_update_port_member(st
- struct dsa_port *cpu_dp = dp->cpu_dp;
- u32 port_bitmap = BIT(cpu_dp->index);
- int other_port;
-+ bool isolated;
-
- dsa_switch_for_each_user_port(other_dp, priv->ds) {
- other_port = other_dp->index;
-@@ -1327,7 +1328,9 @@ static void mt7530_update_port_member(st
- if (!dsa_port_offloads_bridge_dev(other_dp, bridge_dev))
- continue;
-
-- if (join) {
-+ isolated = p->isolated && other_p->isolated;
-+
-+ if (join && !isolated) {
- other_p->pm |= PCR_MATRIX(BIT(port));
- port_bitmap |= BIT(other_port);
- } else {
-@@ -1354,7 +1357,7 @@ mt7530_port_pre_bridge_flags(struct dsa_
- struct netlink_ext_ack *extack)
- {
- if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD |
-- BR_BCAST_FLOOD))
-+ BR_BCAST_FLOOD | BR_ISOLATED))
- return -EINVAL;
-
- return 0;
-@@ -1383,6 +1386,17 @@ mt7530_port_bridge_flags(struct dsa_swit
- mt7530_rmw(priv, MT753X_MFC, BC_FFP(BIT(port)),
- flags.val & BR_BCAST_FLOOD ? BC_FFP(BIT(port)) : 0);
-
-+ if (flags.mask & BR_ISOLATED) {
-+ struct dsa_port *dp = dsa_to_port(ds, port);
-+ struct net_device *bridge_dev = dsa_port_bridge_dev_get(dp);
-+
-+ priv->ports[port].isolated = !!(flags.val & BR_ISOLATED);
-+
-+ mutex_lock(&priv->reg_mutex);
-+ mt7530_update_port_member(priv, port, bridge_dev, true);
-+ mutex_unlock(&priv->reg_mutex);
-+ }
-+
- return 0;
- }
-
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -721,6 +721,7 @@ struct mt7530_fdb {
- */
- struct mt7530_port {
- bool enable;
-+ bool isolated;
- u32 pm;
- u16 pvid;
- struct phylink_pcs *sgmii_pcs;
+++ /dev/null
-From c11d5dbbe73fa7b450aaa77bb18df86a9714b422 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Sat, 1 Jun 2024 01:35:02 +0200
-Subject: [PATCH 1/2] net: phy: aquantia: move priv and hw stat to header
-
-In preparation for LEDs support, move priv and hw stat to header to
-reference priv struct also in other .c outside aquantia.main
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/aquantia/aquantia.h | 38 ++++++++++++++++++++++++
- drivers/net/phy/aquantia/aquantia_main.c | 37 -----------------------
- 2 files changed, 38 insertions(+), 37 deletions(-)
-
---- a/drivers/net/phy/aquantia/aquantia.h
-+++ b/drivers/net/phy/aquantia/aquantia.h
-@@ -82,6 +82,18 @@
- #define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0)
- #define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23
-
-+/* MDIO_MMD_C22EXT */
-+#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES 0xd292
-+#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES 0xd294
-+#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER 0xd297
-+#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES 0xd313
-+#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES 0xd315
-+#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER 0xd317
-+#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS 0xd318
-+#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS 0xd319
-+#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a
-+#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b
-+
- #define VEND1_GLOBAL_INT_STD_STATUS 0xfc00
- #define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01
-
-@@ -108,6 +120,32 @@
- #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
- #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
-
-+struct aqr107_hw_stat {
-+ const char *name;
-+ int reg;
-+ int size;
-+};
-+
-+#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s }
-+static const struct aqr107_hw_stat aqr107_hw_stats[] = {
-+ SGMII_STAT("sgmii_rx_good_frames", RX_GOOD_FRAMES, 26),
-+ SGMII_STAT("sgmii_rx_bad_frames", RX_BAD_FRAMES, 26),
-+ SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER, 8),
-+ SGMII_STAT("sgmii_tx_good_frames", TX_GOOD_FRAMES, 26),
-+ SGMII_STAT("sgmii_tx_bad_frames", TX_BAD_FRAMES, 26),
-+ SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER, 8),
-+ SGMII_STAT("sgmii_tx_collisions", TX_COLLISIONS, 8),
-+ SGMII_STAT("sgmii_tx_line_collisions", TX_LINE_COLLISIONS, 8),
-+ SGMII_STAT("sgmii_tx_frame_alignment_err", TX_FRAME_ALIGN_ERR, 16),
-+ SGMII_STAT("sgmii_tx_runt_frames", TX_RUNT_FRAMES, 22),
-+};
-+
-+#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats)
-+
-+struct aqr107_priv {
-+ u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
-+};
-+
- #if IS_REACHABLE(CONFIG_HWMON)
- int aqr_hwmon_probe(struct phy_device *phydev);
- #else
---- a/drivers/net/phy/aquantia/aquantia_main.c
-+++ b/drivers/net/phy/aquantia/aquantia_main.c
-@@ -84,49 +84,12 @@
- #define MDIO_AN_RX_VEND_STAT3 0xe832
- #define MDIO_AN_RX_VEND_STAT3_AFR BIT(0)
-
--/* MDIO_MMD_C22EXT */
--#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES 0xd292
--#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES 0xd294
--#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER 0xd297
--#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES 0xd313
--#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES 0xd315
--#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER 0xd317
--#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS 0xd318
--#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS 0xd319
--#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a
--#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b
--
- /* Sleep and timeout for checking if the Processor-Intensive
- * MDIO operation is finished
- */
- #define AQR107_OP_IN_PROG_SLEEP 1000
- #define AQR107_OP_IN_PROG_TIMEOUT 100000
-
--struct aqr107_hw_stat {
-- const char *name;
-- int reg;
-- int size;
--};
--
--#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s }
--static const struct aqr107_hw_stat aqr107_hw_stats[] = {
-- SGMII_STAT("sgmii_rx_good_frames", RX_GOOD_FRAMES, 26),
-- SGMII_STAT("sgmii_rx_bad_frames", RX_BAD_FRAMES, 26),
-- SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER, 8),
-- SGMII_STAT("sgmii_tx_good_frames", TX_GOOD_FRAMES, 26),
-- SGMII_STAT("sgmii_tx_bad_frames", TX_BAD_FRAMES, 26),
-- SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER, 8),
-- SGMII_STAT("sgmii_tx_collisions", TX_COLLISIONS, 8),
-- SGMII_STAT("sgmii_tx_line_collisions", TX_LINE_COLLISIONS, 8),
-- SGMII_STAT("sgmii_tx_frame_alignment_err", TX_FRAME_ALIGN_ERR, 16),
-- SGMII_STAT("sgmii_tx_runt_frames", TX_RUNT_FRAMES, 22),
--};
--#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats)
--
--struct aqr107_priv {
-- u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
--};
--
- static int aqr107_get_sset_count(struct phy_device *phydev)
- {
- return AQR107_SGMII_STAT_SZ;
+++ /dev/null
-From 61578f67937881abf54c8bd258eb913312dbe4c1 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Sat, 1 Jun 2024 01:35:03 +0200
-Subject: [PATCH 2/2] net: phy: aquantia: add support for PHY LEDs
-
-Aquantia Ethernet PHYs got 3 LED output pins which are typically used
-to indicate link status and activity.
-Add a minimal LED controller driver supporting the most common uses
-with the 'netdev' trigger as well as software-driven forced control of
-the LEDs.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-[ rework indentation, fix checkpatch error and improve some functions ]
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/aquantia/Makefile | 2 +-
- drivers/net/phy/aquantia/aquantia.h | 40 ++++++
- drivers/net/phy/aquantia/aquantia_leds.c | 150 +++++++++++++++++++++++
- drivers/net/phy/aquantia/aquantia_main.c | 63 +++++++++-
- 4 files changed, 252 insertions(+), 3 deletions(-)
- create mode 100644 drivers/net/phy/aquantia/aquantia_leds.c
-
---- a/drivers/net/phy/aquantia/Makefile
-+++ b/drivers/net/phy/aquantia/Makefile
-@@ -1,5 +1,5 @@
- # SPDX-License-Identifier: GPL-2.0
--aquantia-objs += aquantia_main.o aquantia_firmware.o
-+aquantia-objs += aquantia_main.o aquantia_firmware.o aquantia_leds.o
- ifdef CONFIG_HWMON
- aquantia-objs += aquantia_hwmon.o
- endif
---- a/drivers/net/phy/aquantia/aquantia.h
-+++ b/drivers/net/phy/aquantia/aquantia.h
-@@ -58,6 +58,28 @@
- #define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD BIT(6)
- #define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL BIT(0)
-
-+#define VEND1_GLOBAL_LED_PROV 0xc430
-+#define AQR_LED_PROV(x) (VEND1_GLOBAL_LED_PROV + (x))
-+#define VEND1_GLOBAL_LED_PROV_LINK2500 BIT(14)
-+#define VEND1_GLOBAL_LED_PROV_LINK5000 BIT(15)
-+#define VEND1_GLOBAL_LED_PROV_FORCE_ON BIT(8)
-+#define VEND1_GLOBAL_LED_PROV_LINK10000 BIT(7)
-+#define VEND1_GLOBAL_LED_PROV_LINK1000 BIT(6)
-+#define VEND1_GLOBAL_LED_PROV_LINK100 BIT(5)
-+#define VEND1_GLOBAL_LED_PROV_RX_ACT BIT(3)
-+#define VEND1_GLOBAL_LED_PROV_TX_ACT BIT(2)
-+#define VEND1_GLOBAL_LED_PROV_ACT_STRETCH GENMASK(0, 1)
-+
-+#define VEND1_GLOBAL_LED_PROV_LINK_MASK (VEND1_GLOBAL_LED_PROV_LINK100 | \
-+ VEND1_GLOBAL_LED_PROV_LINK1000 | \
-+ VEND1_GLOBAL_LED_PROV_LINK10000 | \
-+ VEND1_GLOBAL_LED_PROV_LINK5000 | \
-+ VEND1_GLOBAL_LED_PROV_LINK2500)
-+
-+#define VEND1_GLOBAL_LED_DRIVE 0xc438
-+#define VEND1_GLOBAL_LED_DRIVE_VDD BIT(1)
-+#define AQR_LED_DRIVE(x) (VEND1_GLOBAL_LED_DRIVE + (x))
-+
- #define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
- #define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
- #define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
-@@ -120,6 +142,8 @@
- #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
- #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
-
-+#define AQR_MAX_LEDS 3
-+
- struct aqr107_hw_stat {
- const char *name;
- int reg;
-@@ -144,6 +168,7 @@ static const struct aqr107_hw_stat aqr10
-
- struct aqr107_priv {
- u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
-+ unsigned long leds_active_low;
- };
-
- #if IS_REACHABLE(CONFIG_HWMON)
-@@ -153,3 +178,18 @@ static inline int aqr_hwmon_probe(struct
- #endif
-
- int aqr_firmware_load(struct phy_device *phydev);
-+
-+int aqr_phy_led_blink_set(struct phy_device *phydev, u8 index,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off);
-+int aqr_phy_led_brightness_set(struct phy_device *phydev,
-+ u8 index, enum led_brightness value);
-+int aqr_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
-+ unsigned long rules);
-+int aqr_phy_led_hw_control_get(struct phy_device *phydev, u8 index,
-+ unsigned long *rules);
-+int aqr_phy_led_hw_control_set(struct phy_device *phydev, u8 index,
-+ unsigned long rules);
-+int aqr_phy_led_active_low_set(struct phy_device *phydev, int index, bool enable);
-+int aqr_phy_led_polarity_set(struct phy_device *phydev, int index,
-+ unsigned long modes);
---- /dev/null
-+++ b/drivers/net/phy/aquantia/aquantia_leds.c
-@@ -0,0 +1,150 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/* LED driver for Aquantia PHY
-+ *
-+ * Author: Daniel Golle <daniel@makrotopia.org>
-+ */
-+
-+#include <linux/phy.h>
-+
-+#include "aquantia.h"
-+
-+int aqr_phy_led_brightness_set(struct phy_device *phydev,
-+ u8 index, enum led_brightness value)
-+{
-+ if (index >= AQR_MAX_LEDS)
-+ return -EINVAL;
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index),
-+ VEND1_GLOBAL_LED_PROV_LINK_MASK |
-+ VEND1_GLOBAL_LED_PROV_FORCE_ON |
-+ VEND1_GLOBAL_LED_PROV_RX_ACT |
-+ VEND1_GLOBAL_LED_PROV_TX_ACT,
-+ value ? VEND1_GLOBAL_LED_PROV_FORCE_ON : 0);
-+}
-+
-+static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) |
-+ BIT(TRIGGER_NETDEV_LINK_100) |
-+ BIT(TRIGGER_NETDEV_LINK_1000) |
-+ BIT(TRIGGER_NETDEV_LINK_2500) |
-+ BIT(TRIGGER_NETDEV_LINK_5000) |
-+ BIT(TRIGGER_NETDEV_LINK_10000) |
-+ BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX));
-+
-+int aqr_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ if (index >= AQR_MAX_LEDS)
-+ return -EINVAL;
-+
-+ /* All combinations of the supported triggers are allowed */
-+ if (rules & ~supported_triggers)
-+ return -EOPNOTSUPP;
-+
-+ return 0;
-+}
-+
-+int aqr_phy_led_hw_control_get(struct phy_device *phydev, u8 index,
-+ unsigned long *rules)
-+{
-+ int val;
-+
-+ if (index >= AQR_MAX_LEDS)
-+ return -EINVAL;
-+
-+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index));
-+ if (val < 0)
-+ return val;
-+
-+ *rules = 0;
-+ if (val & VEND1_GLOBAL_LED_PROV_LINK100)
-+ *rules |= BIT(TRIGGER_NETDEV_LINK_100);
-+
-+ if (val & VEND1_GLOBAL_LED_PROV_LINK1000)
-+ *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
-+
-+ if (val & VEND1_GLOBAL_LED_PROV_LINK2500)
-+ *rules |= BIT(TRIGGER_NETDEV_LINK_2500);
-+
-+ if (val & VEND1_GLOBAL_LED_PROV_LINK5000)
-+ *rules |= BIT(TRIGGER_NETDEV_LINK_5000);
-+
-+ if (val & VEND1_GLOBAL_LED_PROV_LINK10000)
-+ *rules |= BIT(TRIGGER_NETDEV_LINK_10000);
-+
-+ if (val & VEND1_GLOBAL_LED_PROV_RX_ACT)
-+ *rules |= BIT(TRIGGER_NETDEV_RX);
-+
-+ if (val & VEND1_GLOBAL_LED_PROV_TX_ACT)
-+ *rules |= BIT(TRIGGER_NETDEV_TX);
-+
-+ return 0;
-+}
-+
-+int aqr_phy_led_hw_control_set(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ u16 val = 0;
-+
-+ if (index >= AQR_MAX_LEDS)
-+ return -EINVAL;
-+
-+ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
-+ val |= VEND1_GLOBAL_LED_PROV_LINK100;
-+
-+ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
-+ val |= VEND1_GLOBAL_LED_PROV_LINK1000;
-+
-+ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK)))
-+ val |= VEND1_GLOBAL_LED_PROV_LINK2500;
-+
-+ if (rules & (BIT(TRIGGER_NETDEV_LINK_5000) | BIT(TRIGGER_NETDEV_LINK)))
-+ val |= VEND1_GLOBAL_LED_PROV_LINK5000;
-+
-+ if (rules & (BIT(TRIGGER_NETDEV_LINK_10000) | BIT(TRIGGER_NETDEV_LINK)))
-+ val |= VEND1_GLOBAL_LED_PROV_LINK10000;
-+
-+ if (rules & BIT(TRIGGER_NETDEV_RX))
-+ val |= VEND1_GLOBAL_LED_PROV_RX_ACT;
-+
-+ if (rules & BIT(TRIGGER_NETDEV_TX))
-+ val |= VEND1_GLOBAL_LED_PROV_TX_ACT;
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index),
-+ VEND1_GLOBAL_LED_PROV_LINK_MASK |
-+ VEND1_GLOBAL_LED_PROV_FORCE_ON |
-+ VEND1_GLOBAL_LED_PROV_RX_ACT |
-+ VEND1_GLOBAL_LED_PROV_TX_ACT, val);
-+}
-+
-+int aqr_phy_led_active_low_set(struct phy_device *phydev, int index, bool enable)
-+{
-+ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_DRIVE(index),
-+ VEND1_GLOBAL_LED_DRIVE_VDD, enable);
-+}
-+
-+int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long modes)
-+{
-+ struct aqr107_priv *priv = phydev->priv;
-+ bool active_low = false;
-+ u32 mode;
-+
-+ if (index >= AQR_MAX_LEDS)
-+ return -EINVAL;
-+
-+ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
-+ switch (mode) {
-+ case PHY_LED_ACTIVE_LOW:
-+ active_low = true;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+ }
-+
-+ /* Save LED driver vdd state to restore on SW reset */
-+ if (active_low)
-+ priv->leds_active_low |= BIT(index);
-+
-+ return aqr_phy_led_active_low_set(phydev, index, active_low);
-+}
---- a/drivers/net/phy/aquantia/aquantia_main.c
-+++ b/drivers/net/phy/aquantia/aquantia_main.c
-@@ -475,7 +475,9 @@ static void aqr107_chip_info(struct phy_
-
- static int aqr107_config_init(struct phy_device *phydev)
- {
-- int ret;
-+ struct aqr107_priv *priv = phydev->priv;
-+ u32 led_active_low;
-+ int ret, index = 0;
-
- /* Check that the PHY interface type is compatible */
- if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
-@@ -496,7 +498,19 @@ static int aqr107_config_init(struct phy
- if (!ret)
- aqr107_chip_info(phydev);
-
-- return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
-+ ret = aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
-+ if (ret)
-+ return ret;
-+
-+ /* Restore LED polarity state after reset */
-+ for_each_set_bit(led_active_low, &priv->leds_active_low, AQR_MAX_LEDS) {
-+ ret = aqr_phy_led_active_low_set(phydev, index, led_active_low);
-+ if (ret)
-+ return ret;
-+ index++;
-+ }
-+
-+ return 0;
- }
-
- static int aqcs109_config_init(struct phy_device *phydev)
-@@ -703,6 +717,11 @@ static struct phy_driver aqr_driver[] =
- .get_strings = aqr107_get_strings,
- .get_stats = aqr107_get_stats,
- .link_change_notify = aqr107_link_change_notify,
-+ .led_brightness_set = aqr_phy_led_brightness_set,
-+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
-+ .led_hw_control_set = aqr_phy_led_hw_control_set,
-+ .led_hw_control_get = aqr_phy_led_hw_control_get,
-+ .led_polarity_set = aqr_phy_led_polarity_set,
- },
- {
- PHY_ID_MATCH_MODEL(PHY_ID_AQCS109),
-@@ -722,6 +741,11 @@ static struct phy_driver aqr_driver[] =
- .get_strings = aqr107_get_strings,
- .get_stats = aqr107_get_stats,
- .link_change_notify = aqr107_link_change_notify,
-+ .led_brightness_set = aqr_phy_led_brightness_set,
-+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
-+ .led_hw_control_set = aqr_phy_led_hw_control_set,
-+ .led_hw_control_get = aqr_phy_led_hw_control_get,
-+ .led_polarity_set = aqr_phy_led_polarity_set,
- },
- {
- PHY_ID_MATCH_MODEL(PHY_ID_AQR111),
-@@ -741,6 +765,11 @@ static struct phy_driver aqr_driver[] =
- .get_strings = aqr107_get_strings,
- .get_stats = aqr107_get_stats,
- .link_change_notify = aqr107_link_change_notify,
-+ .led_brightness_set = aqr_phy_led_brightness_set,
-+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
-+ .led_hw_control_set = aqr_phy_led_hw_control_set,
-+ .led_hw_control_get = aqr_phy_led_hw_control_get,
-+ .led_polarity_set = aqr_phy_led_polarity_set,
- },
- {
- PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0),
-@@ -760,6 +789,11 @@ static struct phy_driver aqr_driver[] =
- .get_strings = aqr107_get_strings,
- .get_stats = aqr107_get_stats,
- .link_change_notify = aqr107_link_change_notify,
-+ .led_brightness_set = aqr_phy_led_brightness_set,
-+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
-+ .led_hw_control_set = aqr_phy_led_hw_control_set,
-+ .led_hw_control_get = aqr_phy_led_hw_control_get,
-+ .led_polarity_set = aqr_phy_led_polarity_set,
- },
- {
- PHY_ID_MATCH_MODEL(PHY_ID_AQR405),
-@@ -786,6 +820,11 @@ static struct phy_driver aqr_driver[] =
- .get_strings = aqr107_get_strings,
- .get_stats = aqr107_get_stats,
- .link_change_notify = aqr107_link_change_notify,
-+ .led_brightness_set = aqr_phy_led_brightness_set,
-+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
-+ .led_hw_control_set = aqr_phy_led_hw_control_set,
-+ .led_hw_control_get = aqr_phy_led_hw_control_get,
-+ .led_polarity_set = aqr_phy_led_polarity_set,
- },
- {
- PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
-@@ -823,6 +862,11 @@ static struct phy_driver aqr_driver[] =
- .get_strings = aqr107_get_strings,
- .get_stats = aqr107_get_stats,
- .link_change_notify = aqr107_link_change_notify,
-+ .led_brightness_set = aqr_phy_led_brightness_set,
-+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
-+ .led_hw_control_set = aqr_phy_led_hw_control_set,
-+ .led_hw_control_get = aqr_phy_led_hw_control_get,
-+ .led_polarity_set = aqr_phy_led_polarity_set,
- },
- {
- PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
-@@ -842,6 +886,11 @@ static struct phy_driver aqr_driver[] =
- .get_strings = aqr107_get_strings,
- .get_stats = aqr107_get_stats,
- .link_change_notify = aqr107_link_change_notify,
-+ .led_brightness_set = aqr_phy_led_brightness_set,
-+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
-+ .led_hw_control_set = aqr_phy_led_hw_control_set,
-+ .led_hw_control_get = aqr_phy_led_hw_control_get,
-+ .led_polarity_set = aqr_phy_led_polarity_set,
- },
- {
- PHY_ID_MATCH_MODEL(PHY_ID_AQR114C),
-@@ -861,6 +910,11 @@ static struct phy_driver aqr_driver[] =
- .get_strings = aqr107_get_strings,
- .get_stats = aqr107_get_stats,
- .link_change_notify = aqr107_link_change_notify,
-+ .led_brightness_set = aqr_phy_led_brightness_set,
-+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
-+ .led_hw_control_set = aqr_phy_led_hw_control_set,
-+ .led_hw_control_get = aqr_phy_led_hw_control_get,
-+ .led_polarity_set = aqr_phy_led_polarity_set,
- },
- {
- PHY_ID_MATCH_MODEL(PHY_ID_AQR813),
-@@ -880,6 +934,11 @@ static struct phy_driver aqr_driver[] =
- .get_strings = aqr107_get_strings,
- .get_stats = aqr107_get_stats,
- .link_change_notify = aqr107_link_change_notify,
-+ .led_brightness_set = aqr_phy_led_brightness_set,
-+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
-+ .led_hw_control_set = aqr_phy_led_hw_control_set,
-+ .led_hw_control_get = aqr_phy_led_hw_control_get,
-+ .led_polarity_set = aqr_phy_led_polarity_set,
- },
- };
-
+++ /dev/null
-From e85d3e6fea05c8ae21a40809a3c6b7adc97411c7 Mon Sep 17 00:00:00 2001
-Message-ID: <e85d3e6fea05c8ae21a40809a3c6b7adc97411c7.1728674648.git.mschiffer@universe-factory.net>
-From: Matthias Schiffer <mschiffer@universe-factory.net>
-Date: Thu, 20 Jun 2024 19:25:48 +0200
-Subject: [PATCH] net: dsa: qca8k: do not write port mask twice in bridge
- join/leave
-
-qca8k_port_bridge_join() set QCA8K_PORT_LOOKUP_CTRL() for i == port twice,
-once in the loop handling all other port's masks, and finally at the end
-with the accumulated port_mask.
-
-The first time it would incorrectly set the port's own bit in the mask,
-only to correct the mistake a moment later. qca8k_port_bridge_leave() had
-the same issue, but here the regmap_clear_bits() was a no-op rather than
-setting an unintended value.
-
-Remove the duplicate assignment by skipping the whole loop iteration for
-i == port. The unintended bit setting doesn't seem to have any negative
-effects (even when not reverted right away), so the change is submitted
-as a simple cleanup rather than a fix.
-
-Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
-Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-common.c | 7 +++++--
- 1 file changed, 5 insertions(+), 2 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-common.c
-+++ b/drivers/net/dsa/qca/qca8k-common.c
-@@ -654,6 +654,8 @@ int qca8k_port_bridge_join(struct dsa_sw
- port_mask = BIT(cpu_port);
-
- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-+ if (i == port)
-+ continue;
- if (dsa_is_cpu_port(ds, i))
- continue;
- if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
-@@ -666,8 +668,7 @@ int qca8k_port_bridge_join(struct dsa_sw
- BIT(port));
- if (ret)
- return ret;
-- if (i != port)
-- port_mask |= BIT(i);
-+ port_mask |= BIT(i);
- }
-
- /* Add all other ports to this ports portvlan mask */
-@@ -686,6 +687,8 @@ void qca8k_port_bridge_leave(struct dsa_
- cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
-
- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-+ if (i == port)
-+ continue;
- if (dsa_is_cpu_port(ds, i))
- continue;
- if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
+++ /dev/null
-From 412e1775f413c944b8c51bdadb675be957d83dc8 Mon Sep 17 00:00:00 2001
-Message-ID: <412e1775f413c944b8c51bdadb675be957d83dc8.1728674648.git.mschiffer@universe-factory.net>
-In-Reply-To: <e85d3e6fea05c8ae21a40809a3c6b7adc97411c7.1728674648.git.mschiffer@universe-factory.net>
-References: <e85d3e6fea05c8ae21a40809a3c6b7adc97411c7.1728674648.git.mschiffer@universe-factory.net>
-From: Matthias Schiffer <mschiffer@universe-factory.net>
-Date: Thu, 20 Jun 2024 19:25:49 +0200
-Subject: [PATCH] net: dsa: qca8k: factor out bridge join/leave logic
-
-Most of the logic in qca8k_port_bridge_join() and qca8k_port_bridge_leave()
-is the same. Refactor to reduce duplication and prepare for reusing the
-code for implementing bridge port isolation.
-
-dsa_port_offloads_bridge_dev() is used instead of
-dsa_port_offloads_bridge(), passing the bridge in as a struct netdevice *,
-as we won't have a struct dsa_bridge in qca8k_port_bridge_flags().
-
-The error handling is changed slightly in the bridge leave case,
-returning early and emitting an error message when a regmap access fails.
-This shouldn't matter in practice, as there isn't much we can do if
-communication with the switch breaks down in the middle of reconfiguration.
-
-Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
-Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-common.c | 101 ++++++++++++++---------------
- 1 file changed, 50 insertions(+), 51 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-common.c
-+++ b/drivers/net/dsa/qca/qca8k-common.c
-@@ -615,6 +615,49 @@ void qca8k_port_stp_state_set(struct dsa
- qca8k_port_configure_learning(ds, port, learning);
- }
-
-+static int qca8k_update_port_member(struct qca8k_priv *priv, int port,
-+ const struct net_device *bridge_dev,
-+ bool join)
-+{
-+ struct dsa_port *dp = dsa_to_port(priv->ds, port), *other_dp;
-+ u32 port_mask = BIT(dp->cpu_dp->index);
-+ int i, ret;
-+
-+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-+ if (i == port)
-+ continue;
-+ if (dsa_is_cpu_port(priv->ds, i))
-+ continue;
-+
-+ other_dp = dsa_to_port(priv->ds, i);
-+ if (!dsa_port_offloads_bridge_dev(other_dp, bridge_dev))
-+ continue;
-+
-+ /* Add/remove this port to/from the portvlan mask of the other
-+ * ports in the bridge
-+ */
-+ if (join) {
-+ port_mask |= BIT(i);
-+ ret = regmap_set_bits(priv->regmap,
-+ QCA8K_PORT_LOOKUP_CTRL(i),
-+ BIT(port));
-+ } else {
-+ ret = regmap_clear_bits(priv->regmap,
-+ QCA8K_PORT_LOOKUP_CTRL(i),
-+ BIT(port));
-+ }
-+
-+ if (ret)
-+ return ret;
-+ }
-+
-+ /* Add/remove all other ports to/from this port's portvlan mask */
-+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
-+ QCA8K_PORT_LOOKUP_MEMBER, port_mask);
-+
-+ return ret;
-+}
-+
- int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port,
- struct switchdev_brport_flags flags,
- struct netlink_ext_ack *extack)
-@@ -647,65 +690,21 @@ int qca8k_port_bridge_join(struct dsa_sw
- struct netlink_ext_ack *extack)
- {
- struct qca8k_priv *priv = ds->priv;
-- int port_mask, cpu_port;
-- int i, ret;
--
-- cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
-- port_mask = BIT(cpu_port);
-
-- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-- if (i == port)
-- continue;
-- if (dsa_is_cpu_port(ds, i))
-- continue;
-- if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
-- continue;
-- /* Add this port to the portvlan mask of the other ports
-- * in the bridge
-- */
-- ret = regmap_set_bits(priv->regmap,
-- QCA8K_PORT_LOOKUP_CTRL(i),
-- BIT(port));
-- if (ret)
-- return ret;
-- port_mask |= BIT(i);
-- }
--
-- /* Add all other ports to this ports portvlan mask */
-- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
-- QCA8K_PORT_LOOKUP_MEMBER, port_mask);
--
-- return ret;
-+ return qca8k_update_port_member(priv, port, bridge.dev, true);
- }
-
- void qca8k_port_bridge_leave(struct dsa_switch *ds, int port,
- struct dsa_bridge bridge)
- {
- struct qca8k_priv *priv = ds->priv;
-- int cpu_port, i;
--
-- cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
--
-- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-- if (i == port)
-- continue;
-- if (dsa_is_cpu_port(ds, i))
-- continue;
-- if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
-- continue;
-- /* Remove this port to the portvlan mask of the other ports
-- * in the bridge
-- */
-- regmap_clear_bits(priv->regmap,
-- QCA8K_PORT_LOOKUP_CTRL(i),
-- BIT(port));
-- }
-+ int err;
-
-- /* Set the cpu port to be the only one in the portvlan mask of
-- * this port
-- */
-- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
-- QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
-+ err = qca8k_update_port_member(priv, port, bridge.dev, false);
-+ if (err)
-+ dev_err(priv->dev,
-+ "Failed to update switch config for bridge leave: %d\n",
-+ err);
- }
-
- void qca8k_port_fast_age(struct dsa_switch *ds, int port)
+++ /dev/null
-From 422b64025ec10981c48f9367311846bf4bd38042 Mon Sep 17 00:00:00 2001
-Message-ID: <422b64025ec10981c48f9367311846bf4bd38042.1728674648.git.mschiffer@universe-factory.net>
-In-Reply-To: <e85d3e6fea05c8ae21a40809a3c6b7adc97411c7.1728674648.git.mschiffer@universe-factory.net>
-References: <e85d3e6fea05c8ae21a40809a3c6b7adc97411c7.1728674648.git.mschiffer@universe-factory.net>
-From: Matthias Schiffer <mschiffer@universe-factory.net>
-Date: Thu, 20 Jun 2024 19:25:50 +0200
-Subject: [PATCH] net: dsa: qca8k: add support for bridge port isolation
-
-Remove a pair of ports from the port matrix when both ports have the
-isolated flag set.
-
-Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
-Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-common.c | 22 ++++++++++++++++++++--
- drivers/net/dsa/qca/qca8k.h | 1 +
- 2 files changed, 21 insertions(+), 2 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-common.c
-+++ b/drivers/net/dsa/qca/qca8k-common.c
-@@ -619,6 +619,7 @@ static int qca8k_update_port_member(stru
- const struct net_device *bridge_dev,
- bool join)
- {
-+ bool isolated = !!(priv->port_isolated_map & BIT(port)), other_isolated;
- struct dsa_port *dp = dsa_to_port(priv->ds, port), *other_dp;
- u32 port_mask = BIT(dp->cpu_dp->index);
- int i, ret;
-@@ -633,10 +634,12 @@ static int qca8k_update_port_member(stru
- if (!dsa_port_offloads_bridge_dev(other_dp, bridge_dev))
- continue;
-
-+ other_isolated = !!(priv->port_isolated_map & BIT(i));
-+
- /* Add/remove this port to/from the portvlan mask of the other
- * ports in the bridge
- */
-- if (join) {
-+ if (join && !(isolated && other_isolated)) {
- port_mask |= BIT(i);
- ret = regmap_set_bits(priv->regmap,
- QCA8K_PORT_LOOKUP_CTRL(i),
-@@ -662,7 +665,7 @@ int qca8k_port_pre_bridge_flags(struct d
- struct switchdev_brport_flags flags,
- struct netlink_ext_ack *extack)
- {
-- if (flags.mask & ~BR_LEARNING)
-+ if (flags.mask & ~(BR_LEARNING | BR_ISOLATED))
- return -EINVAL;
-
- return 0;
-@@ -672,6 +675,7 @@ int qca8k_port_bridge_flags(struct dsa_s
- struct switchdev_brport_flags flags,
- struct netlink_ext_ack *extack)
- {
-+ struct qca8k_priv *priv = ds->priv;
- int ret;
-
- if (flags.mask & BR_LEARNING) {
-@@ -680,6 +684,20 @@ int qca8k_port_bridge_flags(struct dsa_s
- if (ret)
- return ret;
- }
-+
-+ if (flags.mask & BR_ISOLATED) {
-+ struct dsa_port *dp = dsa_to_port(ds, port);
-+ struct net_device *bridge_dev = dsa_port_bridge_dev_get(dp);
-+
-+ if (flags.val & BR_ISOLATED)
-+ priv->port_isolated_map |= BIT(port);
-+ else
-+ priv->port_isolated_map &= ~BIT(port);
-+
-+ ret = qca8k_update_port_member(priv, port, bridge_dev, true);
-+ if (ret)
-+ return ret;
-+ }
-
- return 0;
- }
---- a/drivers/net/dsa/qca/qca8k.h
-+++ b/drivers/net/dsa/qca/qca8k.h
-@@ -451,6 +451,7 @@ struct qca8k_priv {
- * Bit 1: port enabled. Bit 0: port disabled.
- */
- u8 port_enabled_map;
-+ u8 port_isolated_map;
- struct qca8k_ports_config ports_config;
- struct regmap *regmap;
- struct mii_bus *bus;
+++ /dev/null
-From 788d30daa8f97f06166b6a63f0e51f2a4c2f036a Mon Sep 17 00:00:00 2001
-From: Hayes Wang <hayeswang@realtek.com>
-Date: Tue, 26 Sep 2023 19:17:14 +0800
-Subject: [PATCH] r8152: use napi_gro_frags
-
-Use napi_gro_frags() for the skb of fragments when the work_done is less
-than budget.
-
-Signed-off-by: Hayes Wang <hayeswang@realtek.com>
-Link: https://lore.kernel.org/r/20230926111714.9448-434-nic_swsd@realtek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/usb/r8152.c | 67 ++++++++++++++++++++++++++++++-----------
- 1 file changed, 50 insertions(+), 17 deletions(-)
-
---- a/drivers/net/usb/r8152.c
-+++ b/drivers/net/usb/r8152.c
-@@ -2584,8 +2584,9 @@ static int rx_bottom(struct r8152 *tp, i
- while (urb->actual_length > len_used) {
- struct net_device *netdev = tp->netdev;
- struct net_device_stats *stats = &netdev->stats;
-- unsigned int pkt_len, rx_frag_head_sz;
-+ unsigned int pkt_len, rx_frag_head_sz, len;
- struct sk_buff *skb;
-+ bool use_frags;
-
- WARN_ON_ONCE(skb_queue_len(&tp->rx_queue) >= 1000);
-
-@@ -2598,45 +2599,77 @@ static int rx_bottom(struct r8152 *tp, i
- break;
-
- pkt_len -= ETH_FCS_LEN;
-+ len = pkt_len;
- rx_data += sizeof(struct rx_desc);
-
-- if (!agg_free || tp->rx_copybreak > pkt_len)
-- rx_frag_head_sz = pkt_len;
-+ if (!agg_free || tp->rx_copybreak > len)
-+ use_frags = false;
- else
-- rx_frag_head_sz = tp->rx_copybreak;
-+ use_frags = true;
-+
-+ if (use_frags) {
-+ /* If the budget is exhausted, the packet
-+ * would be queued in the driver. That is,
-+ * napi_gro_frags() wouldn't be called, so
-+ * we couldn't use napi_get_frags().
-+ */
-+ if (work_done >= budget) {
-+ rx_frag_head_sz = tp->rx_copybreak;
-+ skb = napi_alloc_skb(napi,
-+ rx_frag_head_sz);
-+ } else {
-+ rx_frag_head_sz = 0;
-+ skb = napi_get_frags(napi);
-+ }
-+ } else {
-+ rx_frag_head_sz = 0;
-+ skb = napi_alloc_skb(napi, len);
-+ }
-
-- skb = napi_alloc_skb(napi, rx_frag_head_sz);
- if (!skb) {
- stats->rx_dropped++;
- goto find_next_rx;
- }
-
- skb->ip_summed = r8152_rx_csum(tp, rx_desc);
-- memcpy(skb->data, rx_data, rx_frag_head_sz);
-- skb_put(skb, rx_frag_head_sz);
-- pkt_len -= rx_frag_head_sz;
-- rx_data += rx_frag_head_sz;
-- if (pkt_len) {
-+ rtl_rx_vlan_tag(rx_desc, skb);
-+
-+ if (use_frags) {
-+ if (rx_frag_head_sz) {
-+ memcpy(skb->data, rx_data,
-+ rx_frag_head_sz);
-+ skb_put(skb, rx_frag_head_sz);
-+ len -= rx_frag_head_sz;
-+ rx_data += rx_frag_head_sz;
-+ skb->protocol = eth_type_trans(skb,
-+ netdev);
-+ }
-+
- skb_add_rx_frag(skb, 0, agg->page,
- agg_offset(agg, rx_data),
-- pkt_len,
-- SKB_DATA_ALIGN(pkt_len));
-+ len, SKB_DATA_ALIGN(len));
- get_page(agg->page);
-+ } else {
-+ memcpy(skb->data, rx_data, len);
-+ skb_put(skb, len);
-+ skb->protocol = eth_type_trans(skb, netdev);
- }
-
-- skb->protocol = eth_type_trans(skb, netdev);
-- rtl_rx_vlan_tag(rx_desc, skb);
- if (work_done < budget) {
-+ if (use_frags)
-+ napi_gro_frags(napi);
-+ else
-+ napi_gro_receive(napi, skb);
-+
- work_done++;
- stats->rx_packets++;
-- stats->rx_bytes += skb->len;
-- napi_gro_receive(napi, skb);
-+ stats->rx_bytes += pkt_len;
- } else {
- __skb_queue_tail(&tp->rx_queue, skb);
- }
-
- find_next_rx:
-- rx_data = rx_agg_align(rx_data + pkt_len + ETH_FCS_LEN);
-+ rx_data = rx_agg_align(rx_data + len + ETH_FCS_LEN);
- rx_desc = (struct rx_desc *)rx_data;
- len_used = agg_offset(agg, rx_data);
- len_used += sizeof(struct rx_desc);
+++ /dev/null
-From 71e79430117d56c409c5ea485a263bc0d8083390 Mon Sep 17 00:00:00 2001
-From: Eric Woudstra <ericwouds@gmail.com>
-Date: Tue, 26 Mar 2024 17:23:05 +0100
-Subject: [PATCH] net: phy: air_en8811h: Add the Airoha EN8811H PHY driver
-
-Add the driver for the Airoha EN8811H 2.5 Gigabit PHY. The phy supports
-100/1000/2500 Mbps with auto negotiation only.
-
-The driver uses two firmware files, for which updated versions are added to
-linux-firmware already.
-
-Note: At phy-address + 8 there is another device on the mdio bus, that
-belongs to the EN881H. While the original driver writes to it, Airoha
-has confirmed this is not needed. Therefore, communication with this
-device is not included in this driver.
-
-Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240326162305.303598-3-ericwouds@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/Kconfig | 5 +
- drivers/net/phy/Makefile | 1 +
- drivers/net/phy/air_en8811h.c | 1086 +++++++++++++++++++++++++++++++++
- 3 files changed, 1092 insertions(+)
- create mode 100644 drivers/net/phy/air_en8811h.c
-
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -68,6 +68,11 @@ config SFP
-
- comment "MII PHY device drivers"
-
-+config AIR_EN8811H_PHY
-+ tristate "Airoha EN8811H 2.5 Gigabit PHY"
-+ help
-+ Currently supports the Airoha EN8811H PHY.
-+
- config AMD_PHY
- tristate "AMD PHYs"
- help
---- a/drivers/net/phy/Makefile
-+++ b/drivers/net/phy/Makefile
-@@ -34,6 +34,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m)
-
- obj-$(CONFIG_ADIN_PHY) += adin.o
- obj-$(CONFIG_ADIN1100_PHY) += adin1100.o
-+obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o
- obj-$(CONFIG_AMD_PHY) += amd.o
- obj-$(CONFIG_AQUANTIA_PHY) += aquantia/
- obj-$(CONFIG_AX88796B_PHY) += ax88796b.o
---- /dev/null
-+++ b/drivers/net/phy/air_en8811h.c
-@@ -0,0 +1,1086 @@
-+// SPDX-License-Identifier: GPL-2.0+
-+/*
-+ * Driver for the Airoha EN8811H 2.5 Gigabit PHY.
-+ *
-+ * Limitations of the EN8811H:
-+ * - Only full duplex supported
-+ * - Forced speed (AN off) is not supported by hardware (100Mbps)
-+ *
-+ * Source originated from airoha's en8811h.c and en8811h.h v1.2.1
-+ *
-+ * Copyright (C) 2023 Airoha Technology Corp.
-+ */
-+
-+#include <linux/phy.h>
-+#include <linux/firmware.h>
-+#include <linux/property.h>
-+#include <linux/wordpart.h>
-+#include <asm/unaligned.h>
-+
-+#define EN8811H_PHY_ID 0x03a2a411
-+
-+#define EN8811H_MD32_DM "airoha/EthMD32.dm.bin"
-+#define EN8811H_MD32_DSP "airoha/EthMD32.DSP.bin"
-+
-+#define AIR_FW_ADDR_DM 0x00000000
-+#define AIR_FW_ADDR_DSP 0x00100000
-+
-+/* MII Registers */
-+#define AIR_AUX_CTRL_STATUS 0x1d
-+#define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2)
-+#define AIR_AUX_CTRL_STATUS_SPEED_100 0x4
-+#define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8
-+#define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc
-+
-+#define AIR_EXT_PAGE_ACCESS 0x1f
-+#define AIR_PHY_PAGE_STANDARD 0x0000
-+#define AIR_PHY_PAGE_EXTENDED_4 0x0004
-+
-+/* MII Registers Page 4*/
-+#define AIR_BPBUS_MODE 0x10
-+#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000
-+#define AIR_BPBUS_MODE_ADDR_INCR BIT(15)
-+#define AIR_BPBUS_WR_ADDR_HIGH 0x11
-+#define AIR_BPBUS_WR_ADDR_LOW 0x12
-+#define AIR_BPBUS_WR_DATA_HIGH 0x13
-+#define AIR_BPBUS_WR_DATA_LOW 0x14
-+#define AIR_BPBUS_RD_ADDR_HIGH 0x15
-+#define AIR_BPBUS_RD_ADDR_LOW 0x16
-+#define AIR_BPBUS_RD_DATA_HIGH 0x17
-+#define AIR_BPBUS_RD_DATA_LOW 0x18
-+
-+/* Registers on MDIO_MMD_VEND1 */
-+#define EN8811H_PHY_FW_STATUS 0x8009
-+#define EN8811H_PHY_READY 0x02
-+
-+#define AIR_PHY_MCU_CMD_1 0x800c
-+#define AIR_PHY_MCU_CMD_1_MODE1 0x0
-+#define AIR_PHY_MCU_CMD_2 0x800d
-+#define AIR_PHY_MCU_CMD_2_MODE1 0x0
-+#define AIR_PHY_MCU_CMD_3 0x800e
-+#define AIR_PHY_MCU_CMD_3_MODE1 0x1101
-+#define AIR_PHY_MCU_CMD_3_DOCMD 0x1100
-+#define AIR_PHY_MCU_CMD_4 0x800f
-+#define AIR_PHY_MCU_CMD_4_MODE1 0x0002
-+#define AIR_PHY_MCU_CMD_4_INTCLR 0x00e4
-+
-+/* Registers on MDIO_MMD_VEND2 */
-+#define AIR_PHY_LED_BCR 0x021
-+#define AIR_PHY_LED_BCR_MODE_MASK GENMASK(1, 0)
-+#define AIR_PHY_LED_BCR_TIME_TEST BIT(2)
-+#define AIR_PHY_LED_BCR_CLK_EN BIT(3)
-+#define AIR_PHY_LED_BCR_EXT_CTRL BIT(15)
-+
-+#define AIR_PHY_LED_DUR_ON 0x022
-+
-+#define AIR_PHY_LED_DUR_BLINK 0x023
-+
-+#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2))
-+#define AIR_PHY_LED_ON_MASK (GENMASK(6, 0) | BIT(8))
-+#define AIR_PHY_LED_ON_LINK1000 BIT(0)
-+#define AIR_PHY_LED_ON_LINK100 BIT(1)
-+#define AIR_PHY_LED_ON_LINK10 BIT(2)
-+#define AIR_PHY_LED_ON_LINKDOWN BIT(3)
-+#define AIR_PHY_LED_ON_FDX BIT(4) /* Full duplex */
-+#define AIR_PHY_LED_ON_HDX BIT(5) /* Half duplex */
-+#define AIR_PHY_LED_ON_FORCE_ON BIT(6)
-+#define AIR_PHY_LED_ON_LINK2500 BIT(8)
-+#define AIR_PHY_LED_ON_POLARITY BIT(14)
-+#define AIR_PHY_LED_ON_ENABLE BIT(15)
-+
-+#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2))
-+#define AIR_PHY_LED_BLINK_1000TX BIT(0)
-+#define AIR_PHY_LED_BLINK_1000RX BIT(1)
-+#define AIR_PHY_LED_BLINK_100TX BIT(2)
-+#define AIR_PHY_LED_BLINK_100RX BIT(3)
-+#define AIR_PHY_LED_BLINK_10TX BIT(4)
-+#define AIR_PHY_LED_BLINK_10RX BIT(5)
-+#define AIR_PHY_LED_BLINK_COLLISION BIT(6)
-+#define AIR_PHY_LED_BLINK_RX_CRC_ERR BIT(7)
-+#define AIR_PHY_LED_BLINK_RX_IDLE_ERR BIT(8)
-+#define AIR_PHY_LED_BLINK_FORCE_BLINK BIT(9)
-+#define AIR_PHY_LED_BLINK_2500TX BIT(10)
-+#define AIR_PHY_LED_BLINK_2500RX BIT(11)
-+
-+/* Registers on BUCKPBUS */
-+#define EN8811H_2P5G_LPA 0x3b30
-+#define EN8811H_2P5G_LPA_2P5G BIT(0)
-+
-+#define EN8811H_FW_VERSION 0x3b3c
-+
-+#define EN8811H_POLARITY 0xca0f8
-+#define EN8811H_POLARITY_TX_NORMAL BIT(0)
-+#define EN8811H_POLARITY_RX_REVERSE BIT(1)
-+
-+#define EN8811H_GPIO_OUTPUT 0xcf8b8
-+#define EN8811H_GPIO_OUTPUT_345 (BIT(3) | BIT(4) | BIT(5))
-+
-+#define EN8811H_FW_CTRL_1 0x0f0018
-+#define EN8811H_FW_CTRL_1_START 0x0
-+#define EN8811H_FW_CTRL_1_FINISH 0x1
-+#define EN8811H_FW_CTRL_2 0x800000
-+#define EN8811H_FW_CTRL_2_LOADING BIT(11)
-+
-+/* Led definitions */
-+#define EN8811H_LED_COUNT 3
-+
-+/* Default LED setup:
-+ * GPIO5 <-> LED0 On: Link detected, blink Rx/Tx
-+ * GPIO4 <-> LED1 On: Link detected at 2500 or 1000 Mbps
-+ * GPIO3 <-> LED2 On: Link detected at 2500 or 100 Mbps
-+ */
-+#define AIR_DEFAULT_TRIGGER_LED0 (BIT(TRIGGER_NETDEV_LINK) | \
-+ BIT(TRIGGER_NETDEV_RX) | \
-+ BIT(TRIGGER_NETDEV_TX))
-+#define AIR_DEFAULT_TRIGGER_LED1 (BIT(TRIGGER_NETDEV_LINK_2500) | \
-+ BIT(TRIGGER_NETDEV_LINK_1000))
-+#define AIR_DEFAULT_TRIGGER_LED2 (BIT(TRIGGER_NETDEV_LINK_2500) | \
-+ BIT(TRIGGER_NETDEV_LINK_100))
-+
-+struct led {
-+ unsigned long rules;
-+ unsigned long state;
-+};
-+
-+struct en8811h_priv {
-+ u32 firmware_version;
-+ bool mcu_needs_restart;
-+ struct led led[EN8811H_LED_COUNT];
-+};
-+
-+enum {
-+ AIR_PHY_LED_STATE_FORCE_ON,
-+ AIR_PHY_LED_STATE_FORCE_BLINK,
-+};
-+
-+enum {
-+ AIR_PHY_LED_DUR_BLINK_32MS,
-+ AIR_PHY_LED_DUR_BLINK_64MS,
-+ AIR_PHY_LED_DUR_BLINK_128MS,
-+ AIR_PHY_LED_DUR_BLINK_256MS,
-+ AIR_PHY_LED_DUR_BLINK_512MS,
-+ AIR_PHY_LED_DUR_BLINK_1024MS,
-+};
-+
-+enum {
-+ AIR_LED_DISABLE,
-+ AIR_LED_ENABLE,
-+};
-+
-+enum {
-+ AIR_ACTIVE_LOW,
-+ AIR_ACTIVE_HIGH,
-+};
-+
-+enum {
-+ AIR_LED_MODE_DISABLE,
-+ AIR_LED_MODE_USER_DEFINE,
-+};
-+
-+#define AIR_PHY_LED_DUR_UNIT 1024
-+#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS)
-+
-+static const unsigned long en8811h_led_trig = BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
-+ BIT(TRIGGER_NETDEV_LINK) |
-+ BIT(TRIGGER_NETDEV_LINK_10) |
-+ BIT(TRIGGER_NETDEV_LINK_100) |
-+ BIT(TRIGGER_NETDEV_LINK_1000) |
-+ BIT(TRIGGER_NETDEV_LINK_2500) |
-+ BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX);
-+
-+static int air_phy_read_page(struct phy_device *phydev)
-+{
-+ return __phy_read(phydev, AIR_EXT_PAGE_ACCESS);
-+}
-+
-+static int air_phy_write_page(struct phy_device *phydev, int page)
-+{
-+ return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page);
-+}
-+
-+static int __air_buckpbus_reg_write(struct phy_device *phydev,
-+ u32 pbus_address, u32 pbus_data)
-+{
-+ int ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
-+ upper_16_bits(pbus_address));
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
-+ lower_16_bits(pbus_address));
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
-+ upper_16_bits(pbus_data));
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
-+ lower_16_bits(pbus_data));
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int air_buckpbus_reg_write(struct phy_device *phydev,
-+ u32 pbus_address, u32 pbus_data)
-+{
-+ int saved_page;
-+ int ret = 0;
-+
-+ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
-+
-+ if (saved_page >= 0) {
-+ ret = __air_buckpbus_reg_write(phydev, pbus_address,
-+ pbus_data);
-+ if (ret < 0)
-+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
-+ pbus_address, ret);
-+ }
-+
-+ return phy_restore_page(phydev, saved_page, ret);
-+}
-+
-+static int __air_buckpbus_reg_read(struct phy_device *phydev,
-+ u32 pbus_address, u32 *pbus_data)
-+{
-+ int pbus_data_low, pbus_data_high;
-+ int ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
-+ upper_16_bits(pbus_address));
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
-+ lower_16_bits(pbus_address));
-+ if (ret < 0)
-+ return ret;
-+
-+ pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
-+ if (pbus_data_high < 0)
-+ return ret;
-+
-+ pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
-+ if (pbus_data_low < 0)
-+ return ret;
-+
-+ *pbus_data = pbus_data_low | (pbus_data_high << 16);
-+ return 0;
-+}
-+
-+static int air_buckpbus_reg_read(struct phy_device *phydev,
-+ u32 pbus_address, u32 *pbus_data)
-+{
-+ int saved_page;
-+ int ret = 0;
-+
-+ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
-+
-+ if (saved_page >= 0) {
-+ ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data);
-+ if (ret < 0)
-+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
-+ pbus_address, ret);
-+ }
-+
-+ return phy_restore_page(phydev, saved_page, ret);
-+}
-+
-+static int __air_buckpbus_reg_modify(struct phy_device *phydev,
-+ u32 pbus_address, u32 mask, u32 set)
-+{
-+ int pbus_data_low, pbus_data_high;
-+ u32 pbus_data_old, pbus_data_new;
-+ int ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
-+ upper_16_bits(pbus_address));
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
-+ lower_16_bits(pbus_address));
-+ if (ret < 0)
-+ return ret;
-+
-+ pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
-+ if (pbus_data_high < 0)
-+ return ret;
-+
-+ pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
-+ if (pbus_data_low < 0)
-+ return ret;
-+
-+ pbus_data_old = pbus_data_low | (pbus_data_high << 16);
-+ pbus_data_new = (pbus_data_old & ~mask) | set;
-+ if (pbus_data_new == pbus_data_old)
-+ return 0;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
-+ upper_16_bits(pbus_address));
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
-+ lower_16_bits(pbus_address));
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
-+ upper_16_bits(pbus_data_new));
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
-+ lower_16_bits(pbus_data_new));
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int air_buckpbus_reg_modify(struct phy_device *phydev,
-+ u32 pbus_address, u32 mask, u32 set)
-+{
-+ int saved_page;
-+ int ret = 0;
-+
-+ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
-+
-+ if (saved_page >= 0) {
-+ ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask,
-+ set);
-+ if (ret < 0)
-+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
-+ pbus_address, ret);
-+ }
-+
-+ return phy_restore_page(phydev, saved_page, ret);
-+}
-+
-+static int __air_write_buf(struct phy_device *phydev, u32 address,
-+ const struct firmware *fw)
-+{
-+ unsigned int offset;
-+ int ret;
-+ u16 val;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_INCR);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
-+ upper_16_bits(address));
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
-+ lower_16_bits(address));
-+ if (ret < 0)
-+ return ret;
-+
-+ for (offset = 0; offset < fw->size; offset += 4) {
-+ val = get_unaligned_le16(&fw->data[offset + 2]);
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, val);
-+ if (ret < 0)
-+ return ret;
-+
-+ val = get_unaligned_le16(&fw->data[offset]);
-+ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, val);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int air_write_buf(struct phy_device *phydev, u32 address,
-+ const struct firmware *fw)
-+{
-+ int saved_page;
-+ int ret = 0;
-+
-+ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
-+
-+ if (saved_page >= 0) {
-+ ret = __air_write_buf(phydev, address, fw);
-+ if (ret < 0)
-+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
-+ address, ret);
-+ }
-+
-+ return phy_restore_page(phydev, saved_page, ret);
-+}
-+
-+static int en8811h_wait_mcu_ready(struct phy_device *phydev)
-+{
-+ int ret, reg_value;
-+
-+ /* Because of mdio-lock, may have to wait for multiple loads */
-+ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
-+ EN8811H_PHY_FW_STATUS, reg_value,
-+ reg_value == EN8811H_PHY_READY,
-+ 20000, 7500000, true);
-+ if (ret) {
-+ phydev_err(phydev, "MCU not ready: 0x%x\n", reg_value);
-+ return -ENODEV;
-+ }
-+
-+ return 0;
-+}
-+
-+static int en8811h_load_firmware(struct phy_device *phydev)
-+{
-+ struct en8811h_priv *priv = phydev->priv;
-+ struct device *dev = &phydev->mdio.dev;
-+ const struct firmware *fw1, *fw2;
-+ int ret;
-+
-+ ret = request_firmware_direct(&fw1, EN8811H_MD32_DM, dev);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = request_firmware_direct(&fw2, EN8811H_MD32_DSP, dev);
-+ if (ret < 0)
-+ goto en8811h_load_firmware_rel1;
-+
-+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-+ EN8811H_FW_CTRL_1_START);
-+ if (ret < 0)
-+ goto en8811h_load_firmware_out;
-+
-+ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
-+ EN8811H_FW_CTRL_2_LOADING,
-+ EN8811H_FW_CTRL_2_LOADING);
-+ if (ret < 0)
-+ goto en8811h_load_firmware_out;
-+
-+ ret = air_write_buf(phydev, AIR_FW_ADDR_DM, fw1);
-+ if (ret < 0)
-+ goto en8811h_load_firmware_out;
-+
-+ ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, fw2);
-+ if (ret < 0)
-+ goto en8811h_load_firmware_out;
-+
-+ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
-+ EN8811H_FW_CTRL_2_LOADING, 0);
-+ if (ret < 0)
-+ goto en8811h_load_firmware_out;
-+
-+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-+ EN8811H_FW_CTRL_1_FINISH);
-+ if (ret < 0)
-+ goto en8811h_load_firmware_out;
-+
-+ ret = en8811h_wait_mcu_ready(phydev);
-+
-+ air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
-+ &priv->firmware_version);
-+ phydev_info(phydev, "MD32 firmware version: %08x\n",
-+ priv->firmware_version);
-+
-+en8811h_load_firmware_out:
-+ release_firmware(fw2);
-+
-+en8811h_load_firmware_rel1:
-+ release_firmware(fw1);
-+
-+ if (ret < 0)
-+ phydev_err(phydev, "Load firmware failed: %d\n", ret);
-+
-+ return ret;
-+}
-+
-+static int en8811h_restart_mcu(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-+ EN8811H_FW_CTRL_1_START);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-+ EN8811H_FW_CTRL_1_FINISH);
-+ if (ret < 0)
-+ return ret;
-+
-+ return en8811h_wait_mcu_ready(phydev);
-+}
-+
-+static int air_hw_led_on_set(struct phy_device *phydev, u8 index, bool on)
-+{
-+ struct en8811h_priv *priv = phydev->priv;
-+ bool changed;
-+
-+ if (index >= EN8811H_LED_COUNT)
-+ return -EINVAL;
-+
-+ if (on)
-+ changed = !test_and_set_bit(AIR_PHY_LED_STATE_FORCE_ON,
-+ &priv->led[index].state);
-+ else
-+ changed = !!test_and_clear_bit(AIR_PHY_LED_STATE_FORCE_ON,
-+ &priv->led[index].state);
-+
-+ changed |= (priv->led[index].rules != 0);
-+
-+ if (changed)
-+ return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
-+ AIR_PHY_LED_ON(index),
-+ AIR_PHY_LED_ON_MASK,
-+ on ? AIR_PHY_LED_ON_FORCE_ON : 0);
-+
-+ return 0;
-+}
-+
-+static int air_hw_led_blink_set(struct phy_device *phydev, u8 index,
-+ bool blinking)
-+{
-+ struct en8811h_priv *priv = phydev->priv;
-+ bool changed;
-+
-+ if (index >= EN8811H_LED_COUNT)
-+ return -EINVAL;
-+
-+ if (blinking)
-+ changed = !test_and_set_bit(AIR_PHY_LED_STATE_FORCE_BLINK,
-+ &priv->led[index].state);
-+ else
-+ changed = !!test_and_clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK,
-+ &priv->led[index].state);
-+
-+ changed |= (priv->led[index].rules != 0);
-+
-+ if (changed)
-+ return phy_write_mmd(phydev, MDIO_MMD_VEND2,
-+ AIR_PHY_LED_BLINK(index),
-+ blinking ?
-+ AIR_PHY_LED_BLINK_FORCE_BLINK : 0);
-+ else
-+ return 0;
-+}
-+
-+static int air_led_blink_set(struct phy_device *phydev, u8 index,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ struct en8811h_priv *priv = phydev->priv;
-+ bool blinking = false;
-+ int err;
-+
-+ if (index >= EN8811H_LED_COUNT)
-+ return -EINVAL;
-+
-+ if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) {
-+ blinking = true;
-+ *delay_on = 50;
-+ *delay_off = 50;
-+ }
-+
-+ err = air_hw_led_blink_set(phydev, index, blinking);
-+ if (err)
-+ return err;
-+
-+ /* led-blink set, so switch led-on off */
-+ err = air_hw_led_on_set(phydev, index, false);
-+ if (err)
-+ return err;
-+
-+ /* hw-control is off*/
-+ if (!!test_bit(AIR_PHY_LED_STATE_FORCE_BLINK, &priv->led[index].state))
-+ priv->led[index].rules = 0;
-+
-+ return 0;
-+}
-+
-+static int air_led_brightness_set(struct phy_device *phydev, u8 index,
-+ enum led_brightness value)
-+{
-+ struct en8811h_priv *priv = phydev->priv;
-+ int err;
-+
-+ if (index >= EN8811H_LED_COUNT)
-+ return -EINVAL;
-+
-+ /* led-on set, so switch led-blink off */
-+ err = air_hw_led_blink_set(phydev, index, false);
-+ if (err)
-+ return err;
-+
-+ err = air_hw_led_on_set(phydev, index, (value != LED_OFF));
-+ if (err)
-+ return err;
-+
-+ /* hw-control is off */
-+ if (!!test_bit(AIR_PHY_LED_STATE_FORCE_ON, &priv->led[index].state))
-+ priv->led[index].rules = 0;
-+
-+ return 0;
-+}
-+
-+static int air_led_hw_control_get(struct phy_device *phydev, u8 index,
-+ unsigned long *rules)
-+{
-+ struct en8811h_priv *priv = phydev->priv;
-+
-+ if (index >= EN8811H_LED_COUNT)
-+ return -EINVAL;
-+
-+ *rules = priv->led[index].rules;
-+
-+ return 0;
-+};
-+
-+static int air_led_hw_control_set(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ struct en8811h_priv *priv = phydev->priv;
-+ u16 on = 0, blink = 0;
-+ int ret;
-+
-+ if (index >= EN8811H_LED_COUNT)
-+ return -EINVAL;
-+
-+ priv->led[index].rules = rules;
-+
-+ if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
-+ on |= AIR_PHY_LED_ON_FDX;
-+
-+ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK)))
-+ on |= AIR_PHY_LED_ON_LINK10;
-+
-+ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
-+ on |= AIR_PHY_LED_ON_LINK100;
-+
-+ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
-+ on |= AIR_PHY_LED_ON_LINK1000;
-+
-+ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK)))
-+ on |= AIR_PHY_LED_ON_LINK2500;
-+
-+ if (rules & BIT(TRIGGER_NETDEV_RX)) {
-+ blink |= AIR_PHY_LED_BLINK_10RX |
-+ AIR_PHY_LED_BLINK_100RX |
-+ AIR_PHY_LED_BLINK_1000RX |
-+ AIR_PHY_LED_BLINK_2500RX;
-+ }
-+
-+ if (rules & BIT(TRIGGER_NETDEV_TX)) {
-+ blink |= AIR_PHY_LED_BLINK_10TX |
-+ AIR_PHY_LED_BLINK_100TX |
-+ AIR_PHY_LED_BLINK_1000TX |
-+ AIR_PHY_LED_BLINK_2500TX;
-+ }
-+
-+ if (blink || on) {
-+ /* switch hw-control on, so led-on and led-blink are off */
-+ clear_bit(AIR_PHY_LED_STATE_FORCE_ON,
-+ &priv->led[index].state);
-+ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK,
-+ &priv->led[index].state);
-+ } else {
-+ priv->led[index].rules = 0;
-+ }
-+
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index),
-+ AIR_PHY_LED_ON_MASK, on);
-+
-+ if (ret < 0)
-+ return ret;
-+
-+ return phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BLINK(index),
-+ blink);
-+};
-+
-+static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol)
-+{
-+ int val = 0;
-+ int err;
-+
-+ if (index >= EN8811H_LED_COUNT)
-+ return -EINVAL;
-+
-+ if (state == AIR_LED_ENABLE)
-+ val |= AIR_PHY_LED_ON_ENABLE;
-+ else
-+ val &= ~AIR_PHY_LED_ON_ENABLE;
-+
-+ if (pol == AIR_ACTIVE_HIGH)
-+ val |= AIR_PHY_LED_ON_POLARITY;
-+ else
-+ val &= ~AIR_PHY_LED_ON_POLARITY;
-+
-+ err = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index),
-+ AIR_PHY_LED_ON_ENABLE |
-+ AIR_PHY_LED_ON_POLARITY, val);
-+
-+ if (err < 0)
-+ return err;
-+
-+ return 0;
-+}
-+
-+static int air_leds_init(struct phy_device *phydev, int num, int dur, int mode)
-+{
-+ struct en8811h_priv *priv = phydev->priv;
-+ int ret, i;
-+
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK,
-+ dur);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON,
-+ dur >> 1);
-+ if (ret < 0)
-+ return ret;
-+
-+ switch (mode) {
-+ case AIR_LED_MODE_DISABLE:
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
-+ AIR_PHY_LED_BCR_EXT_CTRL |
-+ AIR_PHY_LED_BCR_MODE_MASK, 0);
-+ if (ret < 0)
-+ return ret;
-+ break;
-+ case AIR_LED_MODE_USER_DEFINE:
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
-+ AIR_PHY_LED_BCR_EXT_CTRL |
-+ AIR_PHY_LED_BCR_CLK_EN,
-+ AIR_PHY_LED_BCR_EXT_CTRL |
-+ AIR_PHY_LED_BCR_CLK_EN);
-+ if (ret < 0)
-+ return ret;
-+ break;
-+ default:
-+ phydev_err(phydev, "LED mode %d is not supported\n", mode);
-+ return -EINVAL;
-+ }
-+
-+ for (i = 0; i < num; ++i) {
-+ ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH);
-+ if (ret < 0) {
-+ phydev_err(phydev, "LED%d init failed: %d\n", i, ret);
-+ return ret;
-+ }
-+ air_led_hw_control_set(phydev, i, priv->led[i].rules);
-+ }
-+
-+ return 0;
-+}
-+
-+static int en8811h_led_hw_is_supported(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ if (index >= EN8811H_LED_COUNT)
-+ return -EINVAL;
-+
-+ /* All combinations of the supported triggers are allowed */
-+ if (rules & ~en8811h_led_trig)
-+ return -EOPNOTSUPP;
-+
-+ return 0;
-+};
-+
-+static int en8811h_probe(struct phy_device *phydev)
-+{
-+ struct en8811h_priv *priv;
-+ int ret;
-+
-+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct en8811h_priv),
-+ GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+ phydev->priv = priv;
-+
-+ ret = en8811h_load_firmware(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* mcu has just restarted after firmware load */
-+ priv->mcu_needs_restart = false;
-+
-+ priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0;
-+ priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1;
-+ priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2;
-+
-+ /* MDIO_DEVS1/2 empty, so set mmds_present bits here */
-+ phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
-+
-+ ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR,
-+ AIR_LED_MODE_DISABLE);
-+ if (ret < 0) {
-+ phydev_err(phydev, "Failed to disable leds: %d\n", ret);
-+ return ret;
-+ }
-+
-+ /* Configure led gpio pins as output */
-+ ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT,
-+ EN8811H_GPIO_OUTPUT_345,
-+ EN8811H_GPIO_OUTPUT_345);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int en8811h_config_init(struct phy_device *phydev)
-+{
-+ struct en8811h_priv *priv = phydev->priv;
-+ struct device *dev = &phydev->mdio.dev;
-+ u32 pbus_value;
-+ int ret;
-+
-+ /* If restart happened in .probe(), no need to restart now */
-+ if (priv->mcu_needs_restart) {
-+ ret = en8811h_restart_mcu(phydev);
-+ if (ret < 0)
-+ return ret;
-+ } else {
-+ /* Next calls to .config_init() mcu needs to restart */
-+ priv->mcu_needs_restart = true;
-+ }
-+
-+ /* Select mode 1, the only mode supported.
-+ * Configures the SerDes for 2500Base-X with rate adaptation
-+ */
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_1,
-+ AIR_PHY_MCU_CMD_1_MODE1);
-+ if (ret < 0)
-+ return ret;
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_2,
-+ AIR_PHY_MCU_CMD_2_MODE1);
-+ if (ret < 0)
-+ return ret;
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3,
-+ AIR_PHY_MCU_CMD_3_MODE1);
-+ if (ret < 0)
-+ return ret;
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4,
-+ AIR_PHY_MCU_CMD_4_MODE1);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Serdes polarity */
-+ pbus_value = 0;
-+ if (device_property_read_bool(dev, "airoha,pnswap-rx"))
-+ pbus_value |= EN8811H_POLARITY_RX_REVERSE;
-+ else
-+ pbus_value &= ~EN8811H_POLARITY_RX_REVERSE;
-+ if (device_property_read_bool(dev, "airoha,pnswap-tx"))
-+ pbus_value &= ~EN8811H_POLARITY_TX_NORMAL;
-+ else
-+ pbus_value |= EN8811H_POLARITY_TX_NORMAL;
-+ ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
-+ EN8811H_POLARITY_RX_REVERSE |
-+ EN8811H_POLARITY_TX_NORMAL, pbus_value);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR,
-+ AIR_LED_MODE_USER_DEFINE);
-+ if (ret < 0) {
-+ phydev_err(phydev, "Failed to initialize leds: %d\n", ret);
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int en8811h_get_features(struct phy_device *phydev)
-+{
-+ linkmode_set_bit_array(phy_basic_ports_array,
-+ ARRAY_SIZE(phy_basic_ports_array),
-+ phydev->supported);
-+
-+ return genphy_c45_pma_read_abilities(phydev);
-+}
-+
-+static int en8811h_get_rate_matching(struct phy_device *phydev,
-+ phy_interface_t iface)
-+{
-+ return RATE_MATCH_PAUSE;
-+}
-+
-+static int en8811h_config_aneg(struct phy_device *phydev)
-+{
-+ bool changed = false;
-+ int ret;
-+ u32 adv;
-+
-+ if (phydev->autoneg == AUTONEG_DISABLE) {
-+ phydev_warn(phydev, "Disabling autoneg is not supported\n");
-+ return -EINVAL;
-+ }
-+
-+ adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising);
-+
-+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
-+ MDIO_AN_10GBT_CTRL_ADV2_5G, adv);
-+ if (ret < 0)
-+ return ret;
-+ if (ret > 0)
-+ changed = true;
-+
-+ return __genphy_config_aneg(phydev, changed);
-+}
-+
-+static int en8811h_read_status(struct phy_device *phydev)
-+{
-+ struct en8811h_priv *priv = phydev->priv;
-+ u32 pbus_value;
-+ int ret, val;
-+
-+ ret = genphy_update_link(phydev);
-+ if (ret)
-+ return ret;
-+
-+ phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED;
-+ phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
-+ phydev->speed = SPEED_UNKNOWN;
-+ phydev->duplex = DUPLEX_UNKNOWN;
-+ phydev->pause = 0;
-+ phydev->asym_pause = 0;
-+ phydev->rate_matching = RATE_MATCH_PAUSE;
-+
-+ ret = genphy_read_master_slave(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = genphy_read_lpa(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Get link partner 2.5GBASE-T ability from vendor register */
-+ ret = air_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA, &pbus_value);
-+ if (ret < 0)
-+ return ret;
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
-+ phydev->lp_advertising,
-+ pbus_value & EN8811H_2P5G_LPA_2P5G);
-+
-+ if (phydev->autoneg_complete)
-+ phy_resolve_aneg_pause(phydev);
-+
-+ if (!phydev->link)
-+ return 0;
-+
-+ /* Get real speed from vendor register */
-+ val = phy_read(phydev, AIR_AUX_CTRL_STATUS);
-+ if (val < 0)
-+ return val;
-+ switch (val & AIR_AUX_CTRL_STATUS_SPEED_MASK) {
-+ case AIR_AUX_CTRL_STATUS_SPEED_2500:
-+ phydev->speed = SPEED_2500;
-+ break;
-+ case AIR_AUX_CTRL_STATUS_SPEED_1000:
-+ phydev->speed = SPEED_1000;
-+ break;
-+ case AIR_AUX_CTRL_STATUS_SPEED_100:
-+ phydev->speed = SPEED_100;
-+ break;
-+ }
-+
-+ /* Firmware before version 24011202 has no vendor register 2P5G_LPA.
-+ * Assume link partner advertised it if connected at 2500Mbps.
-+ */
-+ if (priv->firmware_version < 0x24011202) {
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
-+ phydev->lp_advertising,
-+ phydev->speed == SPEED_2500);
-+ }
-+
-+ /* Only supports full duplex */
-+ phydev->duplex = DUPLEX_FULL;
-+
-+ return 0;
-+}
-+
-+static int en8811h_clear_intr(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3,
-+ AIR_PHY_MCU_CMD_3_DOCMD);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4,
-+ AIR_PHY_MCU_CMD_4_INTCLR);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static irqreturn_t en8811h_handle_interrupt(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = en8811h_clear_intr(phydev);
-+ if (ret < 0) {
-+ phy_error(phydev);
-+ return IRQ_NONE;
-+ }
-+
-+ phy_trigger_machine(phydev);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static struct phy_driver en8811h_driver[] = {
-+{
-+ PHY_ID_MATCH_MODEL(EN8811H_PHY_ID),
-+ .name = "Airoha EN8811H",
-+ .probe = en8811h_probe,
-+ .get_features = en8811h_get_features,
-+ .config_init = en8811h_config_init,
-+ .get_rate_matching = en8811h_get_rate_matching,
-+ .config_aneg = en8811h_config_aneg,
-+ .read_status = en8811h_read_status,
-+ .config_intr = en8811h_clear_intr,
-+ .handle_interrupt = en8811h_handle_interrupt,
-+ .led_hw_is_supported = en8811h_led_hw_is_supported,
-+ .read_page = air_phy_read_page,
-+ .write_page = air_phy_write_page,
-+ .led_blink_set = air_led_blink_set,
-+ .led_brightness_set = air_led_brightness_set,
-+ .led_hw_control_set = air_led_hw_control_set,
-+ .led_hw_control_get = air_led_hw_control_get,
-+} };
-+
-+module_phy_driver(en8811h_driver);
-+
-+static struct mdio_device_id __maybe_unused en8811h_tbl[] = {
-+ { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID) },
-+ { }
-+};
-+
-+MODULE_DEVICE_TABLE(mdio, en8811h_tbl);
-+MODULE_FIRMWARE(EN8811H_MD32_DM);
-+MODULE_FIRMWARE(EN8811H_MD32_DSP);
-+
-+MODULE_DESCRIPTION("Airoha EN8811H PHY drivers");
-+MODULE_AUTHOR("Airoha");
-+MODULE_AUTHOR("Eric Woudstra <ericwouds@gmail.com>");
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From 87c33315af380ca12a2e59ac94edad4fe0481b4c Mon Sep 17 00:00:00 2001
-From: Dan Carpenter <dan.carpenter@linaro.org>
-Date: Fri, 5 Apr 2024 13:08:59 +0300
-Subject: [PATCH] net: phy: air_en8811h: fix some error codes
-
-These error paths accidentally return "ret" which is zero/success
-instead of the correct error code.
-
-Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver")
-Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://lore.kernel.org/r/7ef2e230-dfb7-4a77-8973-9e5be1a99fc2@moroto.mountain
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/air_en8811h.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
---- a/drivers/net/phy/air_en8811h.c
-+++ b/drivers/net/phy/air_en8811h.c
-@@ -272,11 +272,11 @@ static int __air_buckpbus_reg_read(struc
-
- pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
- if (pbus_data_high < 0)
-- return ret;
-+ return pbus_data_high;
-
- pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
- if (pbus_data_low < 0)
-- return ret;
-+ return pbus_data_low;
-
- *pbus_data = pbus_data_low | (pbus_data_high << 16);
- return 0;
-@@ -323,11 +323,11 @@ static int __air_buckpbus_reg_modify(str
-
- pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
- if (pbus_data_high < 0)
-- return ret;
-+ return pbus_data_high;
-
- pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
- if (pbus_data_low < 0)
-- return ret;
-+ return pbus_data_low;
-
- pbus_data_old = pbus_data_low | (pbus_data_high << 16);
- pbus_data_new = (pbus_data_old & ~mask) | set;
+++ /dev/null
-From 9be9a00adfac8118b6d685e71696f83187308c66 Mon Sep 17 00:00:00 2001
-Message-ID: <9be9a00adfac8118b6d685e71696f83187308c66.1715125851.git.daniel@makrotopia.org>
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 7 May 2024 22:43:30 +0100
-Subject: [PATCH net] net: phy: air_en8811h: reset netdev rules when LED is set
- manually
-To: Andrew Lunn <andrew@lunn.ch>,
- Heiner Kallweit <hkallweit1@gmail.com>,
- Russell King <linux@armlinux.org.uk>,
- David S. Miller <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- SkyLake Huang <skylake.huang@mediatek.com>,
- Eric Woudstra <ericwouds@gmail.com>,
- netdev@vger.kernel.org,
- linux-kernel@vger.kernel.org
-
-Setting LED_OFF via the brightness_set should deactivate hw control,
-so make sure netdev trigger rules also get cleared in that case.
-
-Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver")
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
----
-This is basically a stop-gap measure until unified LED handling has
-been implemented accross all MediaTek and Airoha PHYs.
-See also
-https://patchwork.kernel.org/project/netdevbpf/patch/20240425023325.15586-3-SkyLake.Huang@mediatek.com/
-
- drivers/net/phy/air_en8811h.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
---- a/drivers/net/phy/air_en8811h.c
-+++ b/drivers/net/phy/air_en8811h.c
-@@ -544,6 +544,10 @@ static int air_hw_led_on_set(struct phy_
-
- changed |= (priv->led[index].rules != 0);
-
-+ /* clear netdev trigger rules in case LED_OFF has been set */
-+ if (!on)
-+ priv->led[index].rules = 0;
-+
- if (changed)
- return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
- AIR_PHY_LED_ON(index),
+++ /dev/null
-From ec18a2a83b8b9f7e39c80105ea148c769c46227b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Wed, 17 Jan 2024 16:17:36 +0100
-Subject: [PATCH] dt-bindings: leds: Add FUNCTION defines for per-band WLANs
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Most wireless routers and access points can operate in multiple bands
-simultaneously. Vendors often equip their devices with per-band LEDs.
-
-Add defines for those very common functions to allow cleaner & clearer
-bindings.
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Acked-by: Rob Herring <robh@kernel.org>
-Link: https://lore.kernel.org/r/20240117151736.27440-1-zajec5@gmail.com
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- include/dt-bindings/leds/common.h | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/include/dt-bindings/leds/common.h
-+++ b/include/dt-bindings/leds/common.h
-@@ -101,6 +101,9 @@
- #define LED_FUNCTION_USB "usb"
- #define LED_FUNCTION_WAN "wan"
- #define LED_FUNCTION_WLAN "wlan"
-+#define LED_FUNCTION_WLAN_2GHZ "wlan-2ghz"
-+#define LED_FUNCTION_WLAN_5GHZ "wlan-5ghz"
-+#define LED_FUNCTION_WLAN_6GHZ "wlan-6ghz"
- #define LED_FUNCTION_WPS "wps"
-
- #endif /* __DT_BINDINGS_LEDS_H */
+++ /dev/null
-From 64e558500d2d04878b8a6d6578850c475171d6ba Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Fri, 23 Feb 2024 12:22:23 +0100
-Subject: [PATCH] dt-bindings: leds: Add LED_FUNCTION_WAN_ONLINE for Internet
- access
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-It's common for routers to have LED indicating link on the WAN port.
-
-Some devices however have an extra LED that's meant to be used if WAN
-connection is actually "online" (there is Internet access available).
-
-It was suggested to add #define for such use case.
-
-Link: https://lore.kernel.org/linux-devicetree/80e92209-5578-44e7-bd4b-603a29053ddf@collabora.com/T/#u
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
-Link: https://lore.kernel.org/r/20240223112223.1368-1-zajec5@gmail.com
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- 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
-@@ -100,6 +100,7 @@
- #define LED_FUNCTION_TX "tx"
- #define LED_FUNCTION_USB "usb"
- #define LED_FUNCTION_WAN "wan"
-+#define LED_FUNCTION_WAN_ONLINE "wan-online"
- #define LED_FUNCTION_WLAN "wlan"
- #define LED_FUNCTION_WLAN_2GHZ "wlan-2ghz"
- #define LED_FUNCTION_WLAN_5GHZ "wlan-5ghz"
+++ /dev/null
-From 32596b101f6cd87ab1f6e6a1c2a44c70546dde48 Mon Sep 17 00:00:00 2001
-From: Lech Perczak <lech.perczak@gmail.com>
-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
-output sw1a of PF3000 PMIC, which was defined in downstream derivative
-of linux-imx (see link) in the sources for "Android Things" devkit.
-It is required to support CPU frequency scaling.
-
-Map the respective "cpu-supply" nodes of each core to sw1a of the PMIC.
-
-Enabling them causes cpufreq-dt, and imx-thermal drivers to probe
-successfully, and CPU frequency scaling to function.
-
-Link: https://android.googlesource.com/platform/hardware/bsp/kernel/nxp/imx-v4.1/+/o-iot-preview-5/arch/arm/boot/dts/imx7d-pico.dtsi#849
-
-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/nxp/imx/imx7d-pico.dtsi | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
---- a/arch/arm/boot/dts/nxp/imx/imx7d-pico.dtsi
-+++ b/arch/arm/boot/dts/nxp/imx/imx7d-pico.dtsi
-@@ -108,6 +108,14 @@
- assigned-clock-rates = <0>, <32768>;
- };
-
-+&cpu0 {
-+ cpu-supply = <&sw1a_reg>;
-+};
-+
-+&cpu1 {
-+ cpu-supply = <&sw1a_reg>;
-+};
-+
- &ecspi3 {
- cs-gpios = <&gpio4 11 GPIO_ACTIVE_LOW>;
- pinctrl-names = "default";
+++ /dev/null
-From 7d36c3573391dcf0da089298a4b5a25c39f7289d Mon Sep 17 00:00:00 2001
-From: INAGAKI Hiroshi <musashino.open@gmail.com>
-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
-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>
-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(+)
-
---- 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 77b9f2d6fd9bf34ec810de6bdad42d7d0a47d31b Mon Sep 17 00:00:00 2001
-From: INAGAKI Hiroshi <musashino.open@gmail.com>
-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
-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)
-
-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/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 6ce402327a6fb714a9f40a0bb59bcbfe383839a5 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 4 Jun 2024 18:43:43 +0200
-Subject: [PATCH] hwmon: g672: add support for g761
-
-Add support for g761 PWM Fan Controller.
-
-The g761 is a copy of the g763 with the only difference of supporting
-and internal clock. The internal clock is used if no clocks property is
-defined in device node and in such case the required bit is enabled and
-clock handling is skipped.
-
-The internal clock oscillator runs at 31KHz.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Link: https://lore.kernel.org/r/20240604164348.542-3-ansuelsmth@gmail.com
-Signed-off-by: Guenter Roeck <linux@roeck-us.net>
----
- drivers/hwmon/g762.c | 33 ++++++++++++++++++++++++++++++---
- 1 file changed, 30 insertions(+), 3 deletions(-)
-
---- a/drivers/hwmon/g762.c
-+++ b/drivers/hwmon/g762.c
-@@ -69,6 +69,7 @@ enum g762_regs {
- #define G762_REG_FAN_CMD1_PWM_POLARITY 0x02 /* PWM polarity */
- #define G762_REG_FAN_CMD1_PULSE_PER_REV 0x01 /* pulse per fan revolution */
-
-+#define G761_REG_FAN_CMD2_FAN_CLOCK 0x20 /* choose internal clock*/
- #define G762_REG_FAN_CMD2_GEAR_MODE_1 0x08 /* fan gear mode */
- #define G762_REG_FAN_CMD2_GEAR_MODE_0 0x04
- #define G762_REG_FAN_CMD2_FAN_STARTV_1 0x02 /* fan startup voltage */
-@@ -115,6 +116,7 @@ enum g762_regs {
-
- struct g762_data {
- struct i2c_client *client;
-+ bool internal_clock;
- struct clk *clk;
-
- /* update mutex */
-@@ -566,6 +568,7 @@ static int do_set_fan_startv(struct devi
-
- #ifdef CONFIG_OF
- static const struct of_device_id g762_dt_match[] = {
-+ { .compatible = "gmt,g761" },
- { .compatible = "gmt,g762" },
- { .compatible = "gmt,g763" },
- { },
-@@ -597,6 +600,21 @@ static int g762_of_clock_enable(struct i
- if (!client->dev.of_node)
- return 0;
-
-+ data = i2c_get_clientdata(client);
-+
-+ /*
-+ * Skip CLK detection and handling if we use internal clock.
-+ * This is only valid for g761.
-+ */
-+ data->internal_clock = of_device_is_compatible(client->dev.of_node,
-+ "gmt,g761") &&
-+ !of_property_present(client->dev.of_node,
-+ "clocks");
-+ if (data->internal_clock) {
-+ do_set_clk_freq(&client->dev, 32768);
-+ return 0;
-+ }
-+
- clk = of_clk_get(client->dev.of_node, 0);
- if (IS_ERR(clk)) {
- dev_err(&client->dev, "failed to get clock\n");
-@@ -616,7 +634,6 @@ static int g762_of_clock_enable(struct i
- goto clk_unprep;
- }
-
-- data = i2c_get_clientdata(client);
- data->clk = clk;
-
- ret = devm_add_action(&client->dev, g762_of_clock_disable, data);
-@@ -1025,16 +1042,26 @@ ATTRIBUTE_GROUPS(g762);
- static inline int g762_fan_init(struct device *dev)
- {
- struct g762_data *data = g762_update_client(dev);
-+ int ret;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
-+ /* internal_clock can only be set with compatible g761 */
-+ if (data->internal_clock)
-+ data->fan_cmd2 |= G761_REG_FAN_CMD2_FAN_CLOCK;
-+
- data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_FAIL;
- data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_OOC;
- data->valid = false;
-
-- return i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
-- data->fan_cmd1);
-+ ret = i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
-+ data->fan_cmd1);
-+ if (ret)
-+ return ret;
-+
-+ return i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD2,
-+ data->fan_cmd2);
- }
-
- static int g762_probe(struct i2c_client *client)
+++ /dev/null
-From a64c3c1357275b1fd61bc9734f638cdb5d8a8bbb Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 18 Sep 2023 18:11:02 +0200
-Subject: [PATCH 4/6] leds: turris-omnia: Make set_brightness() more efficient
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Implement caching of the LED color and state values that are sent to MCU
-in order to make the set_brightness() operation more efficient by
-avoiding I2C transactions which are not needed.
-
-On Turris Omnia's MCU, which acts as the RGB LED controller, each LED
-has a RGB color, and a ON/OFF state, which are configurable via I2C
-commands CMD_LED_COLOR and CMD_LED_STATE.
-
-The CMD_LED_COLOR command sends 5 bytes and the CMD_LED_STATE command 2
-bytes over the I2C bus, which operates at 100 kHz. With I2C overhead
-this allows ~1670 color changing commands and ~3200 state changing
-commands per second (or around 1000 color + state changes per second).
-This may seem more than enough, but the issue is that the I2C bus is
-shared with another peripheral, the MCU. The MCU exposes an interrupt
-interface, and it can trigger hundreds of interrupts per second. Each
-time, we need to read the interrupt state register over this I2C bus.
-Whenever we are sending a LED color/state changing command, the
-interrupt reading is waiting.
-
-Currently, every time LED brightness or LED multi intensity is changed,
-we send a CMD_LED_STATE command, and if the computed color (brightness
-adjusted multi_intensity) is non-zero, we also send a CMD_LED_COLOR
-command.
-
-Consider for example the situation when we have a netdev trigger enabled
-for a LED. The netdev trigger does not change the LED color, only the
-brightness (either to 0 or to currently configured brightness), and so
-there is no need to send the CMD_LED_COLOR command. But each change of
-brightness to 0 sends one CMD_LED_STATE command, and each change of
-brightness to max_brightness sends one CMD_LED_STATE command and one
-CMD_LED_COLOR command:
- set_brightness(0) -> CMD_LED_STATE
- set_brightness(255) -> CMD_LED_STATE + CMD_LED_COLOR
- (unnecessary)
-
-We can avoid the unnecessary I2C transactions if we cache the values of
-state and color that are sent to the controller. If the color does not
-change from the one previously sent, there is no need to do the
-CMD_LED_COLOR I2C transaction, and if the state does not change, there
-is no need to do the CMD_LED_STATE transaction.
-
-Because we need to make sure that our cached values are consistent with
-the controller state, add explicit setting of the LED color to white at
-probe time (this is the default setting when MCU resets, but does not
-necessarily need to be the case, for example if U-Boot played with the
-LED colors).
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Link: https://lore.kernel.org/r/20230918161104.20860-3-kabel@kernel.org
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/leds-turris-omnia.c | 96 ++++++++++++++++++++++++++------
- 1 file changed, 78 insertions(+), 18 deletions(-)
-
---- a/drivers/leds/leds-turris-omnia.c
-+++ b/drivers/leds/leds-turris-omnia.c
-@@ -30,6 +30,8 @@
- struct omnia_led {
- struct led_classdev_mc mc_cdev;
- struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS];
-+ u8 cached_channels[OMNIA_LED_NUM_CHANNELS];
-+ bool on;
- int reg;
- };
-
-@@ -72,36 +74,82 @@ static int omnia_cmd_read_u8(const struc
- return -EIO;
- }
-
-+static int omnia_led_send_color_cmd(const struct i2c_client *client,
-+ struct omnia_led *led)
-+{
-+ char cmd[5];
-+ int ret;
-+
-+ cmd[0] = CMD_LED_COLOR;
-+ cmd[1] = led->reg;
-+ cmd[2] = led->subled_info[0].brightness;
-+ cmd[3] = led->subled_info[1].brightness;
-+ cmd[4] = led->subled_info[2].brightness;
-+
-+ /* Send the color change command */
-+ ret = i2c_master_send(client, cmd, 5);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Cache the RGB channel brightnesses */
-+ for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i)
-+ led->cached_channels[i] = led->subled_info[i].brightness;
-+
-+ return 0;
-+}
-+
-+/* Determine if the computed RGB channels are different from the cached ones */
-+static bool omnia_led_channels_changed(struct omnia_led *led)
-+{
-+ for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i)
-+ if (led->subled_info[i].brightness != led->cached_channels[i])
-+ return true;
-+
-+ return false;
-+}
-+
- static int omnia_led_brightness_set_blocking(struct led_classdev *cdev,
- enum led_brightness brightness)
- {
- struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
- struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
- struct omnia_led *led = to_omnia_led(mc_cdev);
-- u8 buf[5], state;
-- int ret;
-+ int err = 0;
-
- mutex_lock(&leds->lock);
-
-- led_mc_calc_color_components(&led->mc_cdev, brightness);
-+ /*
-+ * Only recalculate RGB brightnesses from intensities if brightness is
-+ * non-zero. Otherwise we won't be using them and we can save ourselves
-+ * some software divisions (Omnia's CPU does not implement the division
-+ * instruction).
-+ */
-+ if (brightness) {
-+ led_mc_calc_color_components(mc_cdev, brightness);
-+
-+ /*
-+ * Send color command only if brightness is non-zero and the RGB
-+ * channel brightnesses changed.
-+ */
-+ if (omnia_led_channels_changed(led))
-+ err = omnia_led_send_color_cmd(leds->client, led);
-+ }
-
-- buf[0] = CMD_LED_COLOR;
-- buf[1] = led->reg;
-- buf[2] = mc_cdev->subled_info[0].brightness;
-- buf[3] = mc_cdev->subled_info[1].brightness;
-- buf[4] = mc_cdev->subled_info[2].brightness;
--
-- state = CMD_LED_STATE_LED(led->reg);
-- if (buf[2] || buf[3] || buf[4])
-- state |= CMD_LED_STATE_ON;
--
-- ret = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state);
-- if (ret >= 0 && (state & CMD_LED_STATE_ON))
-- ret = i2c_master_send(leds->client, buf, 5);
-+ /* Send on/off state change only if (bool)brightness changed */
-+ if (!err && !brightness != !led->on) {
-+ u8 state = CMD_LED_STATE_LED(led->reg);
-+
-+ if (brightness)
-+ state |= CMD_LED_STATE_ON;
-+
-+ err = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state);
-+ if (!err)
-+ led->on = !!brightness;
-+ }
-
- mutex_unlock(&leds->lock);
-
-- return ret;
-+ return err;
- }
-
- static int omnia_led_register(struct i2c_client *client, struct omnia_led *led,
-@@ -129,11 +177,15 @@ static int omnia_led_register(struct i2c
- }
-
- led->subled_info[0].color_index = LED_COLOR_ID_RED;
-- led->subled_info[0].channel = 0;
- led->subled_info[1].color_index = LED_COLOR_ID_GREEN;
-- led->subled_info[1].channel = 1;
- led->subled_info[2].color_index = LED_COLOR_ID_BLUE;
-- led->subled_info[2].channel = 2;
-+
-+ /* Initial color is white */
-+ for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) {
-+ led->subled_info[i].intensity = 255;
-+ led->subled_info[i].brightness = 255;
-+ led->subled_info[i].channel = i;
-+ }
-
- led->mc_cdev.subled_info = led->subled_info;
- led->mc_cdev.num_colors = OMNIA_LED_NUM_CHANNELS;
-@@ -162,6 +214,14 @@ static int omnia_led_register(struct i2c
- return ret;
- }
-
-+ /* Set initial color and cache it */
-+ ret = omnia_led_send_color_cmd(client, led);
-+ if (ret < 0) {
-+ dev_err(dev, "Cannot set LED %pOF initial color: %i\n", np,
-+ ret);
-+ return ret;
-+ }
-+
- ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc_cdev,
- &init_data);
- if (ret < 0) {
+++ /dev/null
-From e965e0f6de60874fc0a0caed9a9e0122999e0c7b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 18 Sep 2023 18:11:03 +0200
-Subject: [PATCH 5/6] leds: turris-omnia: Support HW controlled mode via
- private trigger
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add support for enabling MCU controlled mode of the Turris Omnia LEDs
-via a LED private trigger called "omnia-mcu". Recall that private LED
-triggers will only be listed in the sysfs trigger file for LEDs that
-support them (currently there is no user of this mechanism).
-
-When in MCU controlled mode, the user can still set LED color, but the
-blinking is done by MCU, which does different things for different LEDs:
-- WAN LED is blinked according to the LED[0] pin of the WAN PHY
-- LAN LEDs are blinked according to the LED[0] output of the
- corresponding port of the LAN switch
-- PCIe LEDs are blinked according to the logical OR of the MiniPCIe port
- LED pins
-
-In the future I want to make the netdev trigger to transparently offload
-the blinking to the HW if user sets compatible settings for the netdev
-trigger (for LEDs associated with network devices).
-There was some work on this already, and hopefully we will be able to
-complete it sometime, but for now there are still multiple blockers for
-this, and even if there weren't, we still would not be able to configure
-HW controlled mode for the LEDs associated with MiniPCIe ports.
-
-In the meantime let's support HW controlled mode via the private LED
-trigger mechanism. If, in the future, we manage to complete the netdev
-trigger offloading, we can still keep this private trigger for backwards
-compatibility, if needed.
-
-We also set "omnia-mcu" to cdev->default_trigger, so that the MCU keeps
-control until the user first wants to take over it. If a different
-default trigger is specified in device-tree via the
-'linux,default-trigger' property, LED class will overwrite
-cdev->default_trigger, and so the DT property will be respected.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Link: https://lore.kernel.org/r/20230918161104.20860-4-kabel@kernel.org
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/Kconfig | 1 +
- drivers/leds/leds-turris-omnia.c | 98 +++++++++++++++++++++++++++++---
- 2 files changed, 91 insertions(+), 8 deletions(-)
-
---- a/drivers/leds/Kconfig
-+++ b/drivers/leds/Kconfig
-@@ -188,6 +188,7 @@ config LEDS_TURRIS_OMNIA
- depends on I2C
- depends on MACH_ARMADA_38X || COMPILE_TEST
- depends on OF
-+ select LEDS_TRIGGERS
- help
- This option enables basic support for the LEDs found on the front
- side of CZ.NIC's Turris Omnia router. There are 12 RGB LEDs on the
---- a/drivers/leds/leds-turris-omnia.c
-+++ b/drivers/leds/leds-turris-omnia.c
-@@ -31,7 +31,7 @@ struct omnia_led {
- struct led_classdev_mc mc_cdev;
- struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS];
- u8 cached_channels[OMNIA_LED_NUM_CHANNELS];
-- bool on;
-+ bool on, hwtrig;
- int reg;
- };
-
-@@ -120,12 +120,14 @@ static int omnia_led_brightness_set_bloc
-
- /*
- * Only recalculate RGB brightnesses from intensities if brightness is
-- * non-zero. Otherwise we won't be using them and we can save ourselves
-- * some software divisions (Omnia's CPU does not implement the division
-- * instruction).
-+ * non-zero (if it is zero and the LED is in HW blinking mode, we use
-+ * max_brightness as brightness). Otherwise we won't be using them and
-+ * we can save ourselves some software divisions (Omnia's CPU does not
-+ * implement the division instruction).
- */
-- if (brightness) {
-- led_mc_calc_color_components(mc_cdev, brightness);
-+ if (brightness || led->hwtrig) {
-+ led_mc_calc_color_components(mc_cdev, brightness ?:
-+ cdev->max_brightness);
-
- /*
- * Send color command only if brightness is non-zero and the RGB
-@@ -135,8 +137,11 @@ static int omnia_led_brightness_set_bloc
- err = omnia_led_send_color_cmd(leds->client, led);
- }
-
-- /* Send on/off state change only if (bool)brightness changed */
-- if (!err && !brightness != !led->on) {
-+ /*
-+ * Send on/off state change only if (bool)brightness changed and the LED
-+ * is not being blinked by HW.
-+ */
-+ if (!err && !led->hwtrig && !brightness != !led->on) {
- u8 state = CMD_LED_STATE_LED(led->reg);
-
- if (brightness)
-@@ -152,6 +157,71 @@ static int omnia_led_brightness_set_bloc
- return err;
- }
-
-+static struct led_hw_trigger_type omnia_hw_trigger_type;
-+
-+static int omnia_hwtrig_activate(struct led_classdev *cdev)
-+{
-+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
-+ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
-+ struct omnia_led *led = to_omnia_led(mc_cdev);
-+ int err = 0;
-+
-+ mutex_lock(&leds->lock);
-+
-+ if (!led->on) {
-+ /*
-+ * If the LED is off (brightness was set to 0), the last
-+ * configured color was not necessarily sent to the MCU.
-+ * Recompute with max_brightness and send if needed.
-+ */
-+ led_mc_calc_color_components(mc_cdev, cdev->max_brightness);
-+
-+ if (omnia_led_channels_changed(led))
-+ err = omnia_led_send_color_cmd(leds->client, led);
-+ }
-+
-+ if (!err) {
-+ /* Put the LED into MCU controlled mode */
-+ err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE,
-+ CMD_LED_MODE_LED(led->reg));
-+ if (!err)
-+ led->hwtrig = true;
-+ }
-+
-+ mutex_unlock(&leds->lock);
-+
-+ return err;
-+}
-+
-+static void omnia_hwtrig_deactivate(struct led_classdev *cdev)
-+{
-+ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
-+ struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev));
-+ int err;
-+
-+ mutex_lock(&leds->lock);
-+
-+ led->hwtrig = false;
-+
-+ /* Put the LED into software mode */
-+ err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE,
-+ CMD_LED_MODE_LED(led->reg) |
-+ CMD_LED_MODE_USER);
-+
-+ mutex_unlock(&leds->lock);
-+
-+ if (err < 0)
-+ dev_err(cdev->dev, "Cannot put LED to software mode: %i\n",
-+ err);
-+}
-+
-+static struct led_trigger omnia_hw_trigger = {
-+ .name = "omnia-mcu",
-+ .activate = omnia_hwtrig_activate,
-+ .deactivate = omnia_hwtrig_deactivate,
-+ .trigger_type = &omnia_hw_trigger_type,
-+};
-+
- static int omnia_led_register(struct i2c_client *client, struct omnia_led *led,
- struct device_node *np)
- {
-@@ -195,6 +265,12 @@ static int omnia_led_register(struct i2c
- cdev = &led->mc_cdev.led_cdev;
- cdev->max_brightness = 255;
- cdev->brightness_set_blocking = omnia_led_brightness_set_blocking;
-+ cdev->trigger_type = &omnia_hw_trigger_type;
-+ /*
-+ * Use the omnia-mcu trigger as the default trigger. It may be rewritten
-+ * by LED class from the linux,default-trigger property.
-+ */
-+ cdev->default_trigger = omnia_hw_trigger.name;
-
- /* put the LED into software mode */
- ret = omnia_cmd_write_u8(client, CMD_LED_MODE,
-@@ -308,6 +384,12 @@ static int omnia_leds_probe(struct i2c_c
-
- mutex_init(&leds->lock);
-
-+ ret = devm_led_trigger_register(dev, &omnia_hw_trigger);
-+ if (ret < 0) {
-+ dev_err(dev, "Cannot register private LED trigger: %d\n", ret);
-+ return ret;
-+ }
-+
- led = &leds->leds[0];
- for_each_available_child_of_node(np, child) {
- ret = omnia_led_register(client, led, child);
+++ /dev/null
-From 0efb3f9609d3de5a7d8c31e3835d7eb3e6adce79 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 18 Sep 2023 18:11:04 +0200
-Subject: [PATCH 6/6] leds: turris-omnia: Add support for enabling/disabling HW
- gamma correction
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If the MCU on Turris Omnia is running newer firmware versions, the LED
-controller supports RGB gamma correction (and enables it by default for
-newer boards).
-
-Determine whether the gamma correction setting feature is supported and
-add the ability to set it via sysfs attribute file.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Link: https://lore.kernel.org/r/20230918161104.20860-5-kabel@kernel.org
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- .../sysfs-class-led-driver-turris-omnia | 14 ++
- drivers/leds/leds-turris-omnia.c | 137 +++++++++++++++---
- 2 files changed, 134 insertions(+), 17 deletions(-)
-
---- a/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia
-+++ b/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia
-@@ -12,3 +12,17 @@ Description: (RW) On the front panel of
- able to change this setting from software.
-
- Format: %i
-+
-+What: /sys/class/leds/<led>/device/gamma_correction
-+Date: August 2023
-+KernelVersion: 6.6
-+Contact: Marek Behún <kabel@kernel.org>
-+Description: (RW) Newer versions of the microcontroller firmware of the
-+ Turris Omnia router support gamma correction for the RGB LEDs.
-+ This feature can be enabled/disabled by writing to this file.
-+
-+ If the feature is not supported because the MCU firmware is too
-+ old, the file always reads as 0, and writing to the file results
-+ in the EOPNOTSUPP error.
-+
-+ Format: %i
---- a/drivers/leds/leds-turris-omnia.c
-+++ b/drivers/leds/leds-turris-omnia.c
-@@ -15,17 +15,30 @@
- #define OMNIA_BOARD_LEDS 12
- #define OMNIA_LED_NUM_CHANNELS 3
-
--#define CMD_LED_MODE 3
--#define CMD_LED_MODE_LED(l) ((l) & 0x0f)
--#define CMD_LED_MODE_USER 0x10
--
--#define CMD_LED_STATE 4
--#define CMD_LED_STATE_LED(l) ((l) & 0x0f)
--#define CMD_LED_STATE_ON 0x10
--
--#define CMD_LED_COLOR 5
--#define CMD_LED_SET_BRIGHTNESS 7
--#define CMD_LED_GET_BRIGHTNESS 8
-+/* MCU controller commands at I2C address 0x2a */
-+#define OMNIA_MCU_I2C_ADDR 0x2a
-+
-+#define CMD_GET_STATUS_WORD 0x01
-+#define STS_FEATURES_SUPPORTED BIT(2)
-+
-+#define CMD_GET_FEATURES 0x10
-+#define FEAT_LED_GAMMA_CORRECTION BIT(5)
-+
-+/* LED controller commands at I2C address 0x2b */
-+#define CMD_LED_MODE 0x03
-+#define CMD_LED_MODE_LED(l) ((l) & 0x0f)
-+#define CMD_LED_MODE_USER 0x10
-+
-+#define CMD_LED_STATE 0x04
-+#define CMD_LED_STATE_LED(l) ((l) & 0x0f)
-+#define CMD_LED_STATE_ON 0x10
-+
-+#define CMD_LED_COLOR 0x05
-+#define CMD_LED_SET_BRIGHTNESS 0x07
-+#define CMD_LED_GET_BRIGHTNESS 0x08
-+
-+#define CMD_SET_GAMMA_CORRECTION 0x30
-+#define CMD_GET_GAMMA_CORRECTION 0x31
-
- struct omnia_led {
- struct led_classdev_mc mc_cdev;
-@@ -40,6 +53,7 @@ struct omnia_led {
- struct omnia_leds {
- struct i2c_client *client;
- struct mutex lock;
-+ bool has_gamma_correction;
- struct omnia_led leds[];
- };
-
-@@ -50,30 +64,42 @@ static int omnia_cmd_write_u8(const stru
- return i2c_master_send(client, buf, sizeof(buf));
- }
-
--static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)
-+static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd,
-+ void *reply, size_t len)
- {
- struct i2c_msg msgs[2];
-- u8 reply;
- int ret;
-
-- msgs[0].addr = client->addr;
-+ msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = 1;
- msgs[0].buf = &cmd;
-- msgs[1].addr = client->addr;
-+ msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
-- msgs[1].len = 1;
-- msgs[1].buf = &reply;
-+ msgs[1].len = len;
-+ msgs[1].buf = reply;
-
-- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
-+ ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
- if (likely(ret == ARRAY_SIZE(msgs)))
-- return reply;
-+ return len;
- else if (ret < 0)
- return ret;
- else
- return -EIO;
- }
-
-+static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)
-+{
-+ u8 reply;
-+ int ret;
-+
-+ ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1);
-+ if (ret < 0)
-+ return ret;
-+
-+ return reply;
-+}
-+
- static int omnia_led_send_color_cmd(const struct i2c_client *client,
- struct omnia_led *led)
- {
-@@ -352,12 +378,74 @@ static ssize_t brightness_store(struct d
- }
- static DEVICE_ATTR_RW(brightness);
-
-+static ssize_t gamma_correction_show(struct device *dev,
-+ struct device_attribute *a, char *buf)
-+{
-+ struct i2c_client *client = to_i2c_client(dev);
-+ struct omnia_leds *leds = i2c_get_clientdata(client);
-+ int ret;
-+
-+ if (leds->has_gamma_correction) {
-+ ret = omnia_cmd_read_u8(client, CMD_GET_GAMMA_CORRECTION);
-+ if (ret < 0)
-+ return ret;
-+ } else {
-+ ret = 0;
-+ }
-+
-+ return sysfs_emit(buf, "%d\n", !!ret);
-+}
-+
-+static ssize_t gamma_correction_store(struct device *dev,
-+ struct device_attribute *a,
-+ const char *buf, size_t count)
-+{
-+ struct i2c_client *client = to_i2c_client(dev);
-+ struct omnia_leds *leds = i2c_get_clientdata(client);
-+ bool val;
-+ int ret;
-+
-+ if (!leds->has_gamma_correction)
-+ return -EOPNOTSUPP;
-+
-+ if (kstrtobool(buf, &val) < 0)
-+ return -EINVAL;
-+
-+ ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val);
-+
-+ return ret < 0 ? ret : count;
-+}
-+static DEVICE_ATTR_RW(gamma_correction);
-+
- static struct attribute *omnia_led_controller_attrs[] = {
- &dev_attr_brightness.attr,
-+ &dev_attr_gamma_correction.attr,
- NULL,
- };
- ATTRIBUTE_GROUPS(omnia_led_controller);
-
-+static int omnia_mcu_get_features(const struct i2c_client *client)
-+{
-+ u16 reply;
-+ int err;
-+
-+ err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
-+ CMD_GET_STATUS_WORD, &reply, sizeof(reply));
-+ if (err < 0)
-+ return err;
-+
-+ /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */
-+ if (!(le16_to_cpu(reply) & STS_FEATURES_SUPPORTED))
-+ return 0;
-+
-+ err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
-+ CMD_GET_FEATURES, &reply, sizeof(reply));
-+ if (err < 0)
-+ return err;
-+
-+ return le16_to_cpu(reply);
-+}
-+
- static int omnia_leds_probe(struct i2c_client *client)
- {
- struct device *dev = &client->dev;
-@@ -382,6 +470,21 @@ static int omnia_leds_probe(struct i2c_c
- leds->client = client;
- i2c_set_clientdata(client, leds);
-
-+ ret = omnia_mcu_get_features(client);
-+ if (ret < 0) {
-+ dev_err(dev, "Cannot determine MCU supported features: %d\n",
-+ ret);
-+ return ret;
-+ }
-+
-+ leds->has_gamma_correction = ret & FEAT_LED_GAMMA_CORRECTION;
-+ if (!leds->has_gamma_correction) {
-+ dev_info(dev,
-+ "Your board's MCU firmware does not support the LED gamma correction feature.\n");
-+ dev_info(dev,
-+ "Consider upgrading MCU firmware with the omnia-mcutool utility.\n");
-+ }
-+
- mutex_init(&leds->lock);
-
- ret = devm_led_trigger_register(dev, &omnia_hw_trigger);
+++ /dev/null
-From ffec49d391c5f0195360912b216aa24dbc9b53c8 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 16 Oct 2023 16:15:38 +0200
-Subject: [PATCH] leds: turris-omnia: Fix brightness setting and trigger
- activating
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-I have improperly refactored commits
- 4d5ed2621c24 ("leds: turris-omnia: Make set_brightness() more efficient")
-and
- aaf38273cf76 ("leds: turris-omnia: Support HW controlled mode via private trigger")
-after Lee requested a change in API semantics of the new functions I
-introduced in commit
- 28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls").
-
-Before the change, the function omnia_cmd_write_u8() returned 0 on
-success, and afterwards it returned a positive value (number of bytes
-written). The latter version was applied, but the following commits did
-not properly account for this change.
-
-This results in non-functional LED's .brightness_set_blocking() and
-trigger's .activate() methods.
-
-The main reasoning behind the semantics change was that read/write
-methods should return the number of read/written bytes on success.
-It was pointed to me [1] that this is not always true (for example the
-regmap API does not do so), and since the driver never uses this number
-of read/written bytes information, I decided to fix this issue by
-changing the functions to the original semantics (return 0 on success).
-
-[1] https://lore.kernel.org/linux-gpio/ZQnn+Gi0xVlsGCYA@smile.fi.intel.com/
-
-Fixes: 28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls")
-Signed-off-by: Marek Behún <kabel@kernel.org>
----
- drivers/leds/leds-turris-omnia.c | 37 +++++++++++++++++---------------
- 1 file changed, 20 insertions(+), 17 deletions(-)
-
---- a/drivers/leds/leds-turris-omnia.c
-+++ b/drivers/leds/leds-turris-omnia.c
-@@ -60,8 +60,11 @@ struct omnia_leds {
- static int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, u8 val)
- {
- u8 buf[2] = { cmd, val };
-+ int ret;
-+
-+ ret = i2c_master_send(client, buf, sizeof(buf));
-
-- return i2c_master_send(client, buf, sizeof(buf));
-+ return ret < 0 ? ret : 0;
- }
-
- static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd,
-@@ -81,7 +84,7 @@ static int omnia_cmd_read_raw(struct i2c
-
- ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
- if (likely(ret == ARRAY_SIZE(msgs)))
-- return len;
-+ return 0;
- else if (ret < 0)
- return ret;
- else
-@@ -91,11 +94,11 @@ static int omnia_cmd_read_raw(struct i2c
- static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)
- {
- u8 reply;
-- int ret;
-+ int err;
-
-- ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1);
-- if (ret < 0)
-- return ret;
-+ err = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1);
-+ if (err)
-+ return err;
-
- return reply;
- }
-@@ -236,7 +239,7 @@ static void omnia_hwtrig_deactivate(stru
-
- mutex_unlock(&leds->lock);
-
-- if (err < 0)
-+ if (err)
- dev_err(cdev->dev, "Cannot put LED to software mode: %i\n",
- err);
- }
-@@ -302,7 +305,7 @@ static int omnia_led_register(struct i2c
- ret = omnia_cmd_write_u8(client, CMD_LED_MODE,
- CMD_LED_MODE_LED(led->reg) |
- CMD_LED_MODE_USER);
-- if (ret < 0) {
-+ if (ret) {
- dev_err(dev, "Cannot set LED %pOF to software mode: %i\n", np,
- ret);
- return ret;
-@@ -311,7 +314,7 @@ static int omnia_led_register(struct i2c
- /* disable the LED */
- ret = omnia_cmd_write_u8(client, CMD_LED_STATE,
- CMD_LED_STATE_LED(led->reg));
-- if (ret < 0) {
-+ if (ret) {
- dev_err(dev, "Cannot set LED %pOF brightness: %i\n", np, ret);
- return ret;
- }
-@@ -364,7 +367,7 @@ static ssize_t brightness_store(struct d
- {
- struct i2c_client *client = to_i2c_client(dev);
- unsigned long brightness;
-- int ret;
-+ int err;
-
- if (kstrtoul(buf, 10, &brightness))
- return -EINVAL;
-@@ -372,9 +375,9 @@ static ssize_t brightness_store(struct d
- if (brightness > 100)
- return -EINVAL;
-
-- ret = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness);
-+ err = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness);
-
-- return ret < 0 ? ret : count;
-+ return err ?: count;
- }
- static DEVICE_ATTR_RW(brightness);
-
-@@ -403,7 +406,7 @@ static ssize_t gamma_correction_store(st
- struct i2c_client *client = to_i2c_client(dev);
- struct omnia_leds *leds = i2c_get_clientdata(client);
- bool val;
-- int ret;
-+ int err;
-
- if (!leds->has_gamma_correction)
- return -EOPNOTSUPP;
-@@ -411,9 +414,9 @@ static ssize_t gamma_correction_store(st
- if (kstrtobool(buf, &val) < 0)
- return -EINVAL;
-
-- ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val);
-+ err = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val);
-
-- return ret < 0 ? ret : count;
-+ return err ?: count;
- }
- static DEVICE_ATTR_RW(gamma_correction);
-
-@@ -431,7 +434,7 @@ static int omnia_mcu_get_features(const
-
- err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
- CMD_GET_STATUS_WORD, &reply, sizeof(reply));
-- if (err < 0)
-+ if (err)
- return err;
-
- /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */
-@@ -440,7 +443,7 @@ static int omnia_mcu_get_features(const
-
- err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
- CMD_GET_FEATURES, &reply, sizeof(reply));
-- if (err < 0)
-+ if (err)
- return err;
-
- return le16_to_cpu(reply);
+++ /dev/null
-From 16724d6ea40a2c9315f5a0d81005dfa4d7a6da24 Mon Sep 17 00:00:00 2001
-From: Luca Weiss <luca.weiss@fairphone.com>
-Date: Fri, 20 Oct 2023 11:55:40 +0100
-Subject: [PATCH] nvmem: qfprom: Mark core clk as optional
-
-On some platforms like sc7280 on non-ChromeOS devices the core clock
-cannot be touched by Linux so we cannot provide it. Mark it as optional
-as accessing qfprom for reading works without it but we still prohibit
-writing if we cannot provide the clock.
-
-Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
-Reviewed-by: Douglas Anderson <dianders@chromium.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20231020105545.216052-2-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/qfprom.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
---- a/drivers/nvmem/qfprom.c
-+++ b/drivers/nvmem/qfprom.c
-@@ -424,12 +424,12 @@ static int qfprom_probe(struct platform_
- if (IS_ERR(priv->vcc))
- return PTR_ERR(priv->vcc);
-
-- priv->secclk = devm_clk_get(dev, "core");
-+ priv->secclk = devm_clk_get_optional(dev, "core");
- if (IS_ERR(priv->secclk))
- return dev_err_probe(dev, PTR_ERR(priv->secclk), "Error getting clock\n");
-
-- /* Only enable writing if we have SoC data. */
-- if (priv->soc_data)
-+ /* Only enable writing if we have SoC data and a valid clock */
-+ if (priv->soc_data && priv->secclk)
- econfig.reg_write = qfprom_reg_write;
- }
-
+++ /dev/null
-From 0720219f4d34a88a9badb4de70cfad7585687d48 Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Fri, 20 Oct 2023 11:55:45 +0100
-Subject: [PATCH] nvmem: Use device_get_match_data()
-
-Use preferred device_get_match_data() instead of of_match_device() to
-get the driver match data. With this, adjust the includes to explicitly
-include the correct headers.
-
-Signed-off-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20231020105545.216052-7-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/mxs-ocotp.c | 10 ++++------
- drivers/nvmem/stm32-romem.c | 7 ++++---
- 2 files changed, 8 insertions(+), 9 deletions(-)
-
---- a/drivers/nvmem/mxs-ocotp.c
-+++ b/drivers/nvmem/mxs-ocotp.c
-@@ -13,8 +13,9 @@
- #include <linux/io.h>
- #include <linux/module.h>
- #include <linux/nvmem-provider.h>
--#include <linux/of_device.h>
-+#include <linux/of.h>
- #include <linux/platform_device.h>
-+#include <linux/property.h>
- #include <linux/slab.h>
- #include <linux/stmp_device.h>
-
-@@ -140,11 +141,10 @@ static int mxs_ocotp_probe(struct platfo
- struct device *dev = &pdev->dev;
- const struct mxs_data *data;
- struct mxs_ocotp *otp;
-- const struct of_device_id *match;
- int ret;
-
-- match = of_match_device(dev->driver->of_match_table, dev);
-- if (!match || !match->data)
-+ data = device_get_match_data(dev);
-+ if (!data)
- return -EINVAL;
-
- otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
-@@ -169,8 +169,6 @@ static int mxs_ocotp_probe(struct platfo
- if (ret)
- return ret;
-
-- data = match->data;
--
- ocotp_config.size = data->size;
- ocotp_config.priv = otp;
- ocotp_config.dev = dev;
---- a/drivers/nvmem/stm32-romem.c
-+++ b/drivers/nvmem/stm32-romem.c
-@@ -10,7 +10,9 @@
- #include <linux/io.h>
- #include <linux/module.h>
- #include <linux/nvmem-provider.h>
--#include <linux/of_device.h>
-+#include <linux/of.h>
-+#include <linux/platform_device.h>
-+#include <linux/property.h>
- #include <linux/tee_drv.h>
-
- #include "stm32-bsec-optee-ta.h"
-@@ -211,8 +213,7 @@ static int stm32_romem_probe(struct plat
-
- priv->lower = 0;
-
-- cfg = (const struct stm32_romem_cfg *)
-- of_match_device(dev->driver->of_match_table, dev)->data;
-+ cfg = device_get_match_data(dev);
- if (!cfg) {
- priv->cfg.read_only = true;
- priv->cfg.size = resource_size(res);
+++ /dev/null
-From f4cf4e5db331a5ce69e3f0b21d322cac0f4e4b5d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Mon, 23 Oct 2023 12:27:59 +0200
-Subject: [PATCH] Revert "nvmem: add new config option"
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This reverts commit 517f14d9cf3533d5ab4fded195ab6f80a92e378f.
-
-Config option "no_of_node" is no longer needed since adding a more
-explicit and targeted option "add_legacy_fixed_of_cells".
-
-That "no_of_node" config option was needed *earlier* to help mtd's case.
-
-DT nodes of MTD partitions (that are also NVMEM devices) may contain
-subnodes. Those SHOULD NOT be treated as NVMEM fixed cells.
-
-To prevent NVMEM core code from parsing subnodes a "no_of_node" option
-was added (and set to true in mtd) to make for_each_child_of_node() in
-NVMEM a no-op. That was a bit hacky because it was messing with
-"of_node" pointer to achieve some side-effect.
-
-With the introduction of "add_legacy_fixed_of_cells" config option
-things got more explicit. MTD subsystem simply tells NVMEM when to look
-for fixed cells and there is no need to hack "of_node" pointer anymore.
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20231023102759.31529-1-zajec5@gmail.com
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/mtd/mtdcore.c | 1 -
- drivers/nvmem/core.c | 2 +-
- include/linux/nvmem-provider.h | 2 --
- 3 files changed, 1 insertion(+), 4 deletions(-)
-
---- a/drivers/mtd/mtdcore.c
-+++ b/drivers/mtd/mtdcore.c
-@@ -560,7 +560,6 @@ static int mtd_nvmem_add(struct mtd_info
- config.read_only = true;
- config.root_only = true;
- config.ignore_wp = true;
-- config.no_of_node = !of_device_is_compatible(node, "nvmem-cells");
- config.priv = mtd;
-
- mtd->nvmem = nvmem_register(&config);
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -918,7 +918,7 @@ struct nvmem_device *nvmem_register(cons
- nvmem->nkeepout = config->nkeepout;
- if (config->of_node)
- nvmem->dev.of_node = config->of_node;
-- else if (!config->no_of_node)
-+ else
- nvmem->dev.of_node = config->dev->of_node;
-
- switch (config->id) {
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -91,7 +91,6 @@ struct nvmem_cell_info {
- * @read_only: Device is read-only.
- * @root_only: Device is accessibly to root only.
- * @of_node: If given, this will be used instead of the parent's of_node.
-- * @no_of_node: Device should not use the parent's of_node even if it's !NULL.
- * @reg_read: Callback to read data.
- * @reg_write: Callback to write data.
- * @size: Device size.
-@@ -126,7 +125,6 @@ struct nvmem_config {
- bool ignore_wp;
- struct nvmem_layout *layout;
- struct device_node *of_node;
-- bool no_of_node;
- nvmem_reg_read_t reg_read;
- nvmem_reg_write_t reg_write;
- int size;
+++ /dev/null
-From e20cd62b1f1708a4dec7ff4beb9e748a0bdb5716 Mon Sep 17 00:00:00 2001
-From: Alexander Stein <alexander.stein@ew.tq-group.com>
-Date: Wed, 17 Jan 2024 09:32:06 +0100
-Subject: [PATCH] of: property: Make 'no port node found' output a debug
- message
-
-There are cases where an unavailable port is not an error, making this
-error message a false-positive. Since commit d56de8c9a17d8 ("usb: typec:
-tcpm: try to get role switch from tcpc fwnode") the role switch is tried
-on the port dev first and tcpc fwnode afterwards. If using the latter
-bindings getting from port dev fails every time. The kernel log is flooded
-with the messages like:
- OF: graph: no port node found in /soc@0/bus@42000000/i2c@42530000/usb-typec@50
-Silence this message by making it a debug message.
-
-Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
-Link: https://lore.kernel.org/r/20240117083206.2901534-1-alexander.stein@ew.tq-group.com
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/of/property.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/of/property.c
-+++ b/drivers/of/property.c
-@@ -664,7 +664,7 @@ struct device_node *of_graph_get_next_en
- of_node_put(node);
-
- if (!port) {
-- pr_err("graph: no port node found in %pOF\n", parent);
-+ pr_debug("graph: no port node found in %pOF\n", parent);
- return NULL;
- }
- } else {
+++ /dev/null
-From 7f38b70042fcaa49219045bd1a9a2836e27a58ac Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Fri, 15 Dec 2023 11:15:27 +0000
-Subject: [PATCH] of: device: Export of_device_make_bus_id()
-
-This helper is really handy to create unique device names based on their
-device tree path, we may need it outside of the OF core (in the NVMEM
-subsystem) so let's export it. As this helper has nothing patform
-specific, let's move it to of/device.c instead of of/platform.c so we
-can add its prototype to of_device.h.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Acked-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20231215111536.316972-2-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/of/device.c | 41 +++++++++++++++++++++++++++++++++++++++
- drivers/of/platform.c | 40 --------------------------------------
- include/linux/of_device.h | 6 ++++++
- 3 files changed, 47 insertions(+), 40 deletions(-)
-
---- a/drivers/of/device.c
-+++ b/drivers/of/device.c
-@@ -304,3 +304,44 @@ int of_device_uevent_modalias(const stru
- return 0;
- }
- EXPORT_SYMBOL_GPL(of_device_uevent_modalias);
-+
-+/**
-+ * of_device_make_bus_id - Use the device node data to assign a unique name
-+ * @dev: pointer to device structure that is linked to a device tree node
-+ *
-+ * This routine will first try using the translated bus address to
-+ * derive a unique name. If it cannot, then it will prepend names from
-+ * parent nodes until a unique name can be derived.
-+ */
-+void of_device_make_bus_id(struct device *dev)
-+{
-+ struct device_node *node = dev->of_node;
-+ const __be32 *reg;
-+ u64 addr;
-+ u32 mask;
-+
-+ /* Construct the name, using parent nodes if necessary to ensure uniqueness */
-+ while (node->parent) {
-+ /*
-+ * If the address can be translated, then that is as much
-+ * uniqueness as we need. Make it the first component and return
-+ */
-+ reg = of_get_property(node, "reg", NULL);
-+ if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
-+ if (!of_property_read_u32(node, "mask", &mask))
-+ dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn",
-+ addr, ffs(mask) - 1, node, dev_name(dev));
-+
-+ else
-+ dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
-+ addr, node, dev_name(dev));
-+ return;
-+ }
-+
-+ /* format arguments only used if dev_name() resolves to NULL */
-+ dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
-+ kbasename(node->full_name), dev_name(dev));
-+ node = node->parent;
-+ }
-+}
-+EXPORT_SYMBOL_GPL(of_device_make_bus_id);
---- a/drivers/of/platform.c
-+++ b/drivers/of/platform.c
-@@ -98,46 +98,6 @@ static const struct of_device_id of_skip
- */
-
- /**
-- * of_device_make_bus_id - Use the device node data to assign a unique name
-- * @dev: pointer to device structure that is linked to a device tree node
-- *
-- * This routine will first try using the translated bus address to
-- * derive a unique name. If it cannot, then it will prepend names from
-- * parent nodes until a unique name can be derived.
-- */
--static void of_device_make_bus_id(struct device *dev)
--{
-- struct device_node *node = dev->of_node;
-- const __be32 *reg;
-- u64 addr;
-- u32 mask;
--
-- /* Construct the name, using parent nodes if necessary to ensure uniqueness */
-- while (node->parent) {
-- /*
-- * If the address can be translated, then that is as much
-- * uniqueness as we need. Make it the first component and return
-- */
-- reg = of_get_property(node, "reg", NULL);
-- if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
-- if (!of_property_read_u32(node, "mask", &mask))
-- dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn",
-- addr, ffs(mask) - 1, node, dev_name(dev));
--
-- else
-- dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
-- addr, node, dev_name(dev));
-- return;
-- }
--
-- /* format arguments only used if dev_name() resolves to NULL */
-- dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
-- kbasename(node->full_name), dev_name(dev));
-- node = node->parent;
-- }
--}
--
--/**
- * of_device_alloc - Allocate and initialize an of_device
- * @np: device node to assign to device
- * @bus_id: Name to assign to the device. May be null to use default name.
---- a/include/linux/of_device.h
-+++ b/include/linux/of_device.h
-@@ -40,6 +40,9 @@ static inline int of_dma_configure(struc
- {
- return of_dma_configure_id(dev, np, force_dma, NULL);
- }
-+
-+void of_device_make_bus_id(struct device *dev);
-+
- #else /* CONFIG_OF */
-
- static inline int of_driver_match_device(struct device *dev,
-@@ -82,6 +85,9 @@ static inline int of_dma_configure(struc
- {
- return 0;
- }
-+
-+static inline void of_device_make_bus_id(struct device *dev) {}
-+
- #endif /* CONFIG_OF */
-
- #endif /* _LINUX_OF_DEVICE_H */
+++ /dev/null
-From 4a1a40233b4a9fc159a5c7a27dc34c5c7bc5be55 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Fri, 15 Dec 2023 11:15:28 +0000
-Subject: [PATCH] nvmem: Move of_nvmem_layout_get_container() in another header
-
-nvmem-consumer.h is included by consumer devices, extracting data from
-NVMEM devices whereas nvmem-provider.h is included by devices providing
-NVMEM content.
-
-The only users of of_nvmem_layout_get_container() outside of the core
-are layout drivers, so better move its prototype to nvmem-provider.h.
-
-While we do so, we also move the kdoc associated with the function to
-the header rather than the .c file.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20231215111536.316972-3-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 8 --------
- include/linux/nvmem-consumer.h | 7 -------
- include/linux/nvmem-provider.h | 21 +++++++++++++++++++++
- 3 files changed, 21 insertions(+), 15 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -823,14 +823,6 @@ static int nvmem_add_cells_from_layout(s
- }
-
- #if IS_ENABLED(CONFIG_OF)
--/**
-- * of_nvmem_layout_get_container() - Get OF node to layout container.
-- *
-- * @nvmem: nvmem device.
-- *
-- * Return: a node pointer with refcount incremented or NULL if no
-- * container exists. Use of_node_put() on it when done.
-- */
- struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
- {
- return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
---- a/include/linux/nvmem-consumer.h
-+++ b/include/linux/nvmem-consumer.h
-@@ -242,7 +242,6 @@ struct nvmem_cell *of_nvmem_cell_get(str
- const char *id);
- struct nvmem_device *of_nvmem_device_get(struct device_node *np,
- const char *name);
--struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
- #else
- static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
- const char *id)
-@@ -255,12 +254,6 @@ static inline struct nvmem_device *of_nv
- {
- return ERR_PTR(-EOPNOTSUPP);
- }
--
--static inline struct device_node *
--of_nvmem_layout_get_container(struct nvmem_device *nvmem)
--{
-- return NULL;
--}
- #endif /* CONFIG_NVMEM && CONFIG_OF */
-
- #endif /* ifndef _LINUX_NVMEM_CONSUMER_H */
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -241,6 +241,27 @@ nvmem_layout_get_match_data(struct nvmem
-
- #endif /* CONFIG_NVMEM */
-
-+#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
-+
-+/**
-+ * of_nvmem_layout_get_container() - Get OF node of layout container
-+ *
-+ * @nvmem: nvmem device
-+ *
-+ * Return: a node pointer with refcount incremented or NULL if no
-+ * container exists. Use of_node_put() on it when done.
-+ */
-+struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
-+
-+#else /* CONFIG_NVMEM && CONFIG_OF */
-+
-+static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
-+{
-+ return NULL;
-+}
-+
-+#endif /* CONFIG_NVMEM && CONFIG_OF */
-+
- #define module_nvmem_layout_driver(__layout_driver) \
- module_driver(__layout_driver, nvmem_layout_register, \
- nvmem_layout_unregister)
+++ /dev/null
-From fc29fd821d9ac2ae3d32a722fac39ce874efb883 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Fri, 15 Dec 2023 11:15:32 +0000
-Subject: [PATCH] nvmem: core: Rework layouts to become regular devices
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Current layout support was initially written without modules support in
-mind. When the requirement for module support rose, the existing base
-was improved to adopt modularization support, but kind of a design flaw
-was introduced. With the existing implementation, when a storage device
-registers into NVMEM, the core tries to hook a layout (if any) and
-populates its cells immediately. This means, if the hardware description
-expects a layout to be hooked up, but no driver was provided for that,
-the storage medium will fail to probe and try later from
-scratch. Even if we consider that the hardware description shall be
-correct, we could still probe the storage device (especially if it
-contains the rootfs).
-
-One way to overcome this situation is to consider the layouts as
-devices, and leverage the native notifier mechanism. When a new NVMEM
-device is registered, we can populate its nvmem-layout child, if any,
-and wait for the matching to be done in order to get the cells (the
-waiting can be easily done with the NVMEM notifiers). If the layout
-driver is compiled as a module, it should automatically be loaded. This
-way, there is no strong order to enforce, any NVMEM device creation
-or NVMEM layout driver insertion will be observed as a new event which
-may lead to the creation of additional cells, without disturbing the
-probes with costly (and sometimes endless) deferrals.
-
-In order to achieve that goal we create a new bus for the nvmem-layouts
-with minimal logic to match nvmem-layout devices with nvmem-layout
-drivers. All this infrastructure code is created in the layouts.c file.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Tested-by: Rafał Miłecki <rafal@milecki.pl>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20231215111536.316972-7-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/Kconfig | 1 +
- drivers/nvmem/Makefile | 2 +
- drivers/nvmem/core.c | 170 ++++++++++----------------
- drivers/nvmem/internals.h | 21 ++++
- drivers/nvmem/layouts.c | 201 +++++++++++++++++++++++++++++++
- drivers/nvmem/layouts/Kconfig | 8 ++
- drivers/nvmem/layouts/onie-tlv.c | 24 +++-
- drivers/nvmem/layouts/sl28vpd.c | 24 +++-
- include/linux/nvmem-provider.h | 38 +++---
- 9 files changed, 354 insertions(+), 135 deletions(-)
- create mode 100644 drivers/nvmem/layouts.c
-
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -1,6 +1,7 @@
- # SPDX-License-Identifier: GPL-2.0-only
- menuconfig NVMEM
- bool "NVMEM Support"
-+ imply NVMEM_LAYOUTS
- help
- Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES...
-
---- a/drivers/nvmem/Makefile
-+++ b/drivers/nvmem/Makefile
-@@ -5,6 +5,8 @@
-
- obj-$(CONFIG_NVMEM) += nvmem_core.o
- nvmem_core-y := core.o
-+obj-$(CONFIG_NVMEM_LAYOUTS) += nvmem_layouts.o
-+nvmem_layouts-y := layouts.o
- obj-y += layouts/
-
- # Devices
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -55,9 +55,6 @@ static LIST_HEAD(nvmem_lookup_list);
-
- static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
-
--static DEFINE_SPINLOCK(nvmem_layout_lock);
--static LIST_HEAD(nvmem_layouts);
--
- static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
- void *val, size_t bytes)
- {
-@@ -739,97 +736,22 @@ static int nvmem_add_cells_from_fixed_la
- return err;
- }
-
--int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
-+int nvmem_layout_register(struct nvmem_layout *layout)
- {
-- layout->owner = owner;
--
-- spin_lock(&nvmem_layout_lock);
-- list_add(&layout->node, &nvmem_layouts);
-- spin_unlock(&nvmem_layout_lock);
--
-- blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout);
-+ if (!layout->add_cells)
-+ return -EINVAL;
-
-- return 0;
-+ /* Populate the cells */
-+ return layout->add_cells(&layout->nvmem->dev, layout->nvmem);
- }
--EXPORT_SYMBOL_GPL(__nvmem_layout_register);
-+EXPORT_SYMBOL_GPL(nvmem_layout_register);
-
- void nvmem_layout_unregister(struct nvmem_layout *layout)
- {
-- blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_REMOVE, layout);
--
-- spin_lock(&nvmem_layout_lock);
-- list_del(&layout->node);
-- spin_unlock(&nvmem_layout_lock);
-+ /* Keep the API even with an empty stub in case we need it later */
- }
- EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
-
--static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
--{
-- struct device_node *layout_np;
-- struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
--
-- layout_np = of_nvmem_layout_get_container(nvmem);
-- if (!layout_np)
-- return NULL;
--
-- /* Fixed layouts don't have a matching driver */
-- if (of_device_is_compatible(layout_np, "fixed-layout")) {
-- of_node_put(layout_np);
-- return NULL;
-- }
--
-- /*
-- * In case the nvmem device was built-in while the layout was built as a
-- * module, we shall manually request the layout driver loading otherwise
-- * we'll never have any match.
-- */
-- of_request_module(layout_np);
--
-- spin_lock(&nvmem_layout_lock);
--
-- list_for_each_entry(l, &nvmem_layouts, node) {
-- if (of_match_node(l->of_match_table, layout_np)) {
-- if (try_module_get(l->owner))
-- layout = l;
--
-- break;
-- }
-- }
--
-- spin_unlock(&nvmem_layout_lock);
-- of_node_put(layout_np);
--
-- return layout;
--}
--
--static void nvmem_layout_put(struct nvmem_layout *layout)
--{
-- if (layout)
-- module_put(layout->owner);
--}
--
--static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
--{
-- struct nvmem_layout *layout = nvmem->layout;
-- int ret;
--
-- if (layout && layout->add_cells) {
-- ret = layout->add_cells(&nvmem->dev, nvmem);
-- if (ret)
-- return ret;
-- }
--
-- return 0;
--}
--
--#if IS_ENABLED(CONFIG_OF)
--struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
--{
-- return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
--}
--EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
--#endif
--
- const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
- struct nvmem_layout *layout)
- {
-@@ -837,7 +759,7 @@ const void *nvmem_layout_get_match_data(
- const struct of_device_id *match;
-
- layout_np = of_nvmem_layout_get_container(nvmem);
-- match = of_match_node(layout->of_match_table, layout_np);
-+ match = of_match_node(layout->dev.driver->of_match_table, layout_np);
-
- return match ? match->data : NULL;
- }
-@@ -949,19 +871,6 @@ struct nvmem_device *nvmem_register(cons
- goto err_put_device;
- }
-
-- /*
-- * If the driver supplied a layout by config->layout, the module
-- * pointer will be NULL and nvmem_layout_put() will be a noop.
-- */
-- nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
-- if (IS_ERR(nvmem->layout)) {
-- rval = PTR_ERR(nvmem->layout);
-- nvmem->layout = NULL;
--
-- if (rval == -EPROBE_DEFER)
-- goto err_teardown_compat;
-- }
--
- if (config->cells) {
- rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
- if (rval)
-@@ -982,24 +891,24 @@ struct nvmem_device *nvmem_register(cons
- if (rval)
- goto err_remove_cells;
-
-- rval = nvmem_add_cells_from_layout(nvmem);
-- if (rval)
-- goto err_remove_cells;
--
- dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
-
- rval = device_add(&nvmem->dev);
- if (rval)
- goto err_remove_cells;
-
-+ rval = nvmem_populate_layout(nvmem);
-+ if (rval)
-+ goto err_remove_dev;
-+
- blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
-
- return nvmem;
-
-+err_remove_dev:
-+ device_del(&nvmem->dev);
- err_remove_cells:
- nvmem_device_remove_all_cells(nvmem);
-- nvmem_layout_put(nvmem->layout);
--err_teardown_compat:
- if (config->compat)
- nvmem_sysfs_remove_compat(nvmem, config);
- err_put_device:
-@@ -1021,7 +930,7 @@ static void nvmem_device_release(struct
- device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
-
- nvmem_device_remove_all_cells(nvmem);
-- nvmem_layout_put(nvmem->layout);
-+ nvmem_destroy_layout(nvmem);
- device_unregister(&nvmem->dev);
- }
-
-@@ -1323,6 +1232,12 @@ nvmem_cell_get_from_lookup(struct device
- return cell;
- }
-
-+static void nvmem_layout_module_put(struct nvmem_device *nvmem)
-+{
-+ if (nvmem->layout && nvmem->layout->dev.driver)
-+ module_put(nvmem->layout->dev.driver->owner);
-+}
-+
- #if IS_ENABLED(CONFIG_OF)
- static struct nvmem_cell_entry *
- nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np)
-@@ -1341,6 +1256,18 @@ nvmem_find_cell_entry_by_node(struct nvm
- return cell;
- }
-
-+static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem)
-+{
-+ if (!nvmem->layout)
-+ return 0;
-+
-+ if (!nvmem->layout->dev.driver ||
-+ !try_module_get(nvmem->layout->dev.driver->owner))
-+ return -EPROBE_DEFER;
-+
-+ return 0;
-+}
-+
- /**
- * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id
- *
-@@ -1403,16 +1330,29 @@ struct nvmem_cell *of_nvmem_cell_get(str
- return ERR_CAST(nvmem);
- }
-
-+ ret = nvmem_layout_module_get_optional(nvmem);
-+ if (ret) {
-+ of_node_put(cell_np);
-+ __nvmem_device_put(nvmem);
-+ return ERR_PTR(ret);
-+ }
-+
- cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np);
- of_node_put(cell_np);
- if (!cell_entry) {
- __nvmem_device_put(nvmem);
-- return ERR_PTR(-ENOENT);
-+ nvmem_layout_module_put(nvmem);
-+ if (nvmem->layout)
-+ return ERR_PTR(-EPROBE_DEFER);
-+ else
-+ return ERR_PTR(-ENOENT);
- }
-
- cell = nvmem_create_cell(cell_entry, id, cell_index);
-- if (IS_ERR(cell))
-+ if (IS_ERR(cell)) {
- __nvmem_device_put(nvmem);
-+ nvmem_layout_module_put(nvmem);
-+ }
-
- return cell;
- }
-@@ -1526,6 +1466,7 @@ void nvmem_cell_put(struct nvmem_cell *c
-
- kfree(cell);
- __nvmem_device_put(nvmem);
-+ nvmem_layout_module_put(nvmem);
- }
- EXPORT_SYMBOL_GPL(nvmem_cell_put);
-
-@@ -2118,11 +2059,22 @@ EXPORT_SYMBOL_GPL(nvmem_dev_size);
-
- static int __init nvmem_init(void)
- {
-- return bus_register(&nvmem_bus_type);
-+ int ret;
-+
-+ ret = bus_register(&nvmem_bus_type);
-+ if (ret)
-+ return ret;
-+
-+ ret = nvmem_layout_bus_register();
-+ if (ret)
-+ bus_unregister(&nvmem_bus_type);
-+
-+ return ret;
- }
-
- static void __exit nvmem_exit(void)
- {
-+ nvmem_layout_bus_unregister();
- bus_unregister(&nvmem_bus_type);
- }
-
---- a/drivers/nvmem/internals.h
-+++ b/drivers/nvmem/internals.h
-@@ -34,4 +34,25 @@ struct nvmem_device {
- void *priv;
- };
-
-+#if IS_ENABLED(CONFIG_OF)
-+int nvmem_layout_bus_register(void);
-+void nvmem_layout_bus_unregister(void);
-+int nvmem_populate_layout(struct nvmem_device *nvmem);
-+void nvmem_destroy_layout(struct nvmem_device *nvmem);
-+#else /* CONFIG_OF */
-+static inline int nvmem_layout_bus_register(void)
-+{
-+ return 0;
-+}
-+
-+static inline void nvmem_layout_bus_unregister(void) {}
-+
-+static inline int nvmem_populate_layout(struct nvmem_device *nvmem)
-+{
-+ return 0;
-+}
-+
-+static inline void nvmem_destroy_layout(struct nvmem_device *nvmem) { }
-+#endif /* CONFIG_OF */
-+
- #endif /* ifndef _LINUX_NVMEM_INTERNALS_H */
---- /dev/null
-+++ b/drivers/nvmem/layouts.c
-@@ -0,0 +1,201 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * NVMEM layout bus handling
-+ *
-+ * Copyright (C) 2023 Bootlin
-+ * Author: Miquel Raynal <miquel.raynal@bootlin.com
-+ */
-+
-+#include <linux/device.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/nvmem-consumer.h>
-+#include <linux/nvmem-provider.h>
-+#include <linux/of.h>
-+#include <linux/of_device.h>
-+#include <linux/of_irq.h>
-+
-+#include "internals.h"
-+
-+#define to_nvmem_layout_driver(drv) \
-+ (container_of((drv), struct nvmem_layout_driver, driver))
-+#define to_nvmem_layout_device(_dev) \
-+ container_of((_dev), struct nvmem_layout, dev)
-+
-+static int nvmem_layout_bus_match(struct device *dev, struct device_driver *drv)
-+{
-+ return of_driver_match_device(dev, drv);
-+}
-+
-+static int nvmem_layout_bus_probe(struct device *dev)
-+{
-+ struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver);
-+ struct nvmem_layout *layout = to_nvmem_layout_device(dev);
-+
-+ if (!drv->probe || !drv->remove)
-+ return -EINVAL;
-+
-+ return drv->probe(layout);
-+}
-+
-+static void nvmem_layout_bus_remove(struct device *dev)
-+{
-+ struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver);
-+ struct nvmem_layout *layout = to_nvmem_layout_device(dev);
-+
-+ return drv->remove(layout);
-+}
-+
-+static struct bus_type nvmem_layout_bus_type = {
-+ .name = "nvmem-layout",
-+ .match = nvmem_layout_bus_match,
-+ .probe = nvmem_layout_bus_probe,
-+ .remove = nvmem_layout_bus_remove,
-+};
-+
-+int nvmem_layout_driver_register(struct nvmem_layout_driver *drv)
-+{
-+ drv->driver.bus = &nvmem_layout_bus_type;
-+
-+ return driver_register(&drv->driver);
-+}
-+EXPORT_SYMBOL_GPL(nvmem_layout_driver_register);
-+
-+void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv)
-+{
-+ driver_unregister(&drv->driver);
-+}
-+EXPORT_SYMBOL_GPL(nvmem_layout_driver_unregister);
-+
-+static void nvmem_layout_release_device(struct device *dev)
-+{
-+ struct nvmem_layout *layout = to_nvmem_layout_device(dev);
-+
-+ of_node_put(layout->dev.of_node);
-+ kfree(layout);
-+}
-+
-+static int nvmem_layout_create_device(struct nvmem_device *nvmem,
-+ struct device_node *np)
-+{
-+ struct nvmem_layout *layout;
-+ struct device *dev;
-+ int ret;
-+
-+ layout = kzalloc(sizeof(*layout), GFP_KERNEL);
-+ if (!layout)
-+ return -ENOMEM;
-+
-+ /* Create a bidirectional link */
-+ layout->nvmem = nvmem;
-+ nvmem->layout = layout;
-+
-+ /* Device model registration */
-+ dev = &layout->dev;
-+ device_initialize(dev);
-+ dev->parent = &nvmem->dev;
-+ dev->bus = &nvmem_layout_bus_type;
-+ dev->release = nvmem_layout_release_device;
-+ dev->coherent_dma_mask = DMA_BIT_MASK(32);
-+ dev->dma_mask = &dev->coherent_dma_mask;
-+ device_set_node(dev, of_fwnode_handle(of_node_get(np)));
-+ of_device_make_bus_id(dev);
-+ of_msi_configure(dev, dev->of_node);
-+
-+ ret = device_add(dev);
-+ if (ret) {
-+ put_device(dev);
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static const struct of_device_id of_nvmem_layout_skip_table[] = {
-+ { .compatible = "fixed-layout", },
-+ {}
-+};
-+
-+static int nvmem_layout_bus_populate(struct nvmem_device *nvmem,
-+ struct device_node *layout_dn)
-+{
-+ int ret;
-+
-+ /* Make sure it has a compatible property */
-+ if (!of_get_property(layout_dn, "compatible", NULL)) {
-+ pr_debug("%s() - skipping %pOF, no compatible prop\n",
-+ __func__, layout_dn);
-+ return 0;
-+ }
-+
-+ /* Fixed layouts are parsed manually somewhere else for now */
-+ if (of_match_node(of_nvmem_layout_skip_table, layout_dn)) {
-+ pr_debug("%s() - skipping %pOF node\n", __func__, layout_dn);
-+ return 0;
-+ }
-+
-+ if (of_node_check_flag(layout_dn, OF_POPULATED_BUS)) {
-+ pr_debug("%s() - skipping %pOF, already populated\n",
-+ __func__, layout_dn);
-+
-+ return 0;
-+ }
-+
-+ /* NVMEM layout buses expect only a single device representing the layout */
-+ ret = nvmem_layout_create_device(nvmem, layout_dn);
-+ if (ret)
-+ return ret;
-+
-+ of_node_set_flag(layout_dn, OF_POPULATED_BUS);
-+
-+ return 0;
-+}
-+
-+struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
-+{
-+ return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
-+}
-+EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
-+
-+/*
-+ * Returns the number of devices populated, 0 if the operation was not relevant
-+ * for this nvmem device, an error code otherwise.
-+ */
-+int nvmem_populate_layout(struct nvmem_device *nvmem)
-+{
-+ struct device_node *layout_dn;
-+ int ret;
-+
-+ layout_dn = of_nvmem_layout_get_container(nvmem);
-+ if (!layout_dn)
-+ return 0;
-+
-+ /* Populate the layout device */
-+ device_links_supplier_sync_state_pause();
-+ ret = nvmem_layout_bus_populate(nvmem, layout_dn);
-+ device_links_supplier_sync_state_resume();
-+
-+ of_node_put(layout_dn);
-+ return ret;
-+}
-+
-+void nvmem_destroy_layout(struct nvmem_device *nvmem)
-+{
-+ struct device *dev;
-+
-+ if (!nvmem->layout)
-+ return;
-+
-+ dev = &nvmem->layout->dev;
-+ of_node_clear_flag(dev->of_node, OF_POPULATED_BUS);
-+ device_unregister(dev);
-+}
-+
-+int nvmem_layout_bus_register(void)
-+{
-+ return bus_register(&nvmem_layout_bus_type);
-+}
-+
-+void nvmem_layout_bus_unregister(void)
-+{
-+ bus_unregister(&nvmem_layout_bus_type);
-+}
---- a/drivers/nvmem/layouts/Kconfig
-+++ b/drivers/nvmem/layouts/Kconfig
-@@ -1,5 +1,11 @@
- # SPDX-License-Identifier: GPL-2.0
-
-+config NVMEM_LAYOUTS
-+ bool
-+ depends on OF
-+
-+if NVMEM_LAYOUTS
-+
- menu "Layout Types"
-
- config NVMEM_LAYOUT_SL28_VPD
-@@ -21,3 +27,5 @@ config NVMEM_LAYOUT_ONIE_TLV
- If unsure, say N.
-
- endmenu
-+
-+endif
---- a/drivers/nvmem/layouts/onie-tlv.c
-+++ b/drivers/nvmem/layouts/onie-tlv.c
-@@ -225,16 +225,32 @@ static int onie_tlv_parse_table(struct d
- return 0;
- }
-
-+static int onie_tlv_probe(struct nvmem_layout *layout)
-+{
-+ layout->add_cells = onie_tlv_parse_table;
-+
-+ return nvmem_layout_register(layout);
-+}
-+
-+static void onie_tlv_remove(struct nvmem_layout *layout)
-+{
-+ nvmem_layout_unregister(layout);
-+}
-+
- static const struct of_device_id onie_tlv_of_match_table[] = {
- { .compatible = "onie,tlv-layout", },
- {},
- };
- MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table);
-
--static struct nvmem_layout onie_tlv_layout = {
-- .name = "ONIE tlv layout",
-- .of_match_table = onie_tlv_of_match_table,
-- .add_cells = onie_tlv_parse_table,
-+static struct nvmem_layout_driver onie_tlv_layout = {
-+ .driver = {
-+ .owner = THIS_MODULE,
-+ .name = "onie-tlv-layout",
-+ .of_match_table = onie_tlv_of_match_table,
-+ },
-+ .probe = onie_tlv_probe,
-+ .remove = onie_tlv_remove,
- };
- module_nvmem_layout_driver(onie_tlv_layout);
-
---- a/drivers/nvmem/layouts/sl28vpd.c
-+++ b/drivers/nvmem/layouts/sl28vpd.c
-@@ -134,16 +134,32 @@ static int sl28vpd_add_cells(struct devi
- return 0;
- }
-
-+static int sl28vpd_probe(struct nvmem_layout *layout)
-+{
-+ layout->add_cells = sl28vpd_add_cells;
-+
-+ return nvmem_layout_register(layout);
-+}
-+
-+static void sl28vpd_remove(struct nvmem_layout *layout)
-+{
-+ nvmem_layout_unregister(layout);
-+}
-+
- static const struct of_device_id sl28vpd_of_match_table[] = {
- { .compatible = "kontron,sl28-vpd" },
- {},
- };
- MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
-
--static struct nvmem_layout sl28vpd_layout = {
-- .name = "sl28-vpd",
-- .of_match_table = sl28vpd_of_match_table,
-- .add_cells = sl28vpd_add_cells,
-+static struct nvmem_layout_driver sl28vpd_layout = {
-+ .driver = {
-+ .owner = THIS_MODULE,
-+ .name = "kontron-sl28vpd-layout",
-+ .of_match_table = sl28vpd_of_match_table,
-+ },
-+ .probe = sl28vpd_probe,
-+ .remove = sl28vpd_remove,
- };
- module_nvmem_layout_driver(sl28vpd_layout);
-
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -9,6 +9,7 @@
- #ifndef _LINUX_NVMEM_PROVIDER_H
- #define _LINUX_NVMEM_PROVIDER_H
-
-+#include <linux/device.h>
- #include <linux/device/driver.h>
- #include <linux/err.h>
- #include <linux/errno.h>
-@@ -158,12 +159,11 @@ struct nvmem_cell_table {
- /**
- * struct nvmem_layout - NVMEM layout definitions
- *
-- * @name: Layout name.
-- * @of_match_table: Open firmware match table.
-- * @add_cells: Called to populate the layout using
-- * nvmem_add_one_cell().
-- * @owner: Pointer to struct module.
-- * @node: List node.
-+ * @dev: Device-model layout device.
-+ * @nvmem: The underlying NVMEM device
-+ * @add_cells: Will be called if a nvmem device is found which
-+ * has this layout. The function will add layout
-+ * specific cells with nvmem_add_one_cell().
- *
- * A nvmem device can hold a well defined structure which can just be
- * evaluated during runtime. For example a TLV list, or a list of "name=val"
-@@ -171,13 +171,15 @@ struct nvmem_cell_table {
- * cells.
- */
- struct nvmem_layout {
-- const char *name;
-- const struct of_device_id *of_match_table;
-+ struct device dev;
-+ struct nvmem_device *nvmem;
- int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
-+};
-
-- /* private */
-- struct module *owner;
-- struct list_head node;
-+struct nvmem_layout_driver {
-+ struct device_driver driver;
-+ int (*probe)(struct nvmem_layout *layout);
-+ void (*remove)(struct nvmem_layout *layout);
- };
-
- #if IS_ENABLED(CONFIG_NVMEM)
-@@ -194,11 +196,15 @@ void nvmem_del_cell_table(struct nvmem_c
- int nvmem_add_one_cell(struct nvmem_device *nvmem,
- const struct nvmem_cell_info *info);
-
--int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner);
--#define nvmem_layout_register(layout) \
-- __nvmem_layout_register(layout, THIS_MODULE)
-+int nvmem_layout_register(struct nvmem_layout *layout);
- void nvmem_layout_unregister(struct nvmem_layout *layout);
-
-+int nvmem_layout_driver_register(struct nvmem_layout_driver *drv);
-+void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv);
-+#define module_nvmem_layout_driver(__nvmem_layout_driver) \
-+ module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \
-+ nvmem_layout_driver_unregister)
-+
- const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
- struct nvmem_layout *layout);
-
-@@ -262,8 +268,4 @@ static inline struct device_node *of_nvm
-
- #endif /* CONFIG_NVMEM && CONFIG_OF */
-
--#define module_nvmem_layout_driver(__layout_driver) \
-- module_driver(__layout_driver, nvmem_layout_register, \
-- nvmem_layout_unregister)
--
- #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */
+++ /dev/null
-From 0331c611949fffdf486652450901a4dc52bc5cca Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Fri, 15 Dec 2023 11:15:34 +0000
-Subject: [PATCH] nvmem: core: Expose cells through sysfs
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The binary content of nvmem devices is available to the user so in the
-easiest cases, finding the content of a cell is rather easy as it is
-just a matter of looking at a known and fixed offset. However, nvmem
-layouts have been recently introduced to cope with more advanced
-situations, where the offset and size of the cells is not known in
-advance or is dynamic. When using layouts, more advanced parsers are
-used by the kernel in order to give direct access to the content of each
-cell, regardless of its position/size in the underlying
-device. Unfortunately, these information are not accessible by users,
-unless by fully re-implementing the parser logic in userland.
-
-Let's expose the cells and their content through sysfs to avoid these
-situations. Of course the relevant NVMEM sysfs Kconfig option must be
-enabled for this support to be available.
-
-Not all nvmem devices expose cells. Indeed, the .bin_attrs attribute
-group member will be filled at runtime only when relevant and will
-remain empty otherwise. In this case, as the cells attribute group will
-be empty, it will not lead to any additional folder/file creation.
-
-Exposed cells are read-only. There is, in practice, everything in the
-core to support a write path, but as I don't see any need for that, I
-prefer to keep the interface simple (and probably safer). The interface
-is documented as being in the "testing" state which means we can later
-add a write attribute if though relevant.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Tested-by: Rafał Miłecki <rafal@milecki.pl>
-Tested-by: Chen-Yu Tsai <wenst@chromium.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20231215111536.316972-9-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 135 +++++++++++++++++++++++++++++++++++++-
- drivers/nvmem/internals.h | 1 +
- 2 files changed, 135 insertions(+), 1 deletion(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -299,6 +299,43 @@ static umode_t nvmem_bin_attr_is_visible
- return nvmem_bin_attr_get_umode(nvmem);
- }
-
-+static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry,
-+ const char *id, int index);
-+
-+static ssize_t nvmem_cell_attr_read(struct file *filp, struct kobject *kobj,
-+ struct bin_attribute *attr, char *buf,
-+ loff_t pos, size_t count)
-+{
-+ struct nvmem_cell_entry *entry;
-+ struct nvmem_cell *cell = NULL;
-+ size_t cell_sz, read_len;
-+ void *content;
-+
-+ entry = attr->private;
-+ cell = nvmem_create_cell(entry, entry->name, 0);
-+ if (IS_ERR(cell))
-+ return PTR_ERR(cell);
-+
-+ if (!cell)
-+ return -EINVAL;
-+
-+ content = nvmem_cell_read(cell, &cell_sz);
-+ if (IS_ERR(content)) {
-+ read_len = PTR_ERR(content);
-+ goto destroy_cell;
-+ }
-+
-+ read_len = min_t(unsigned int, cell_sz - pos, count);
-+ memcpy(buf, content + pos, read_len);
-+ kfree(content);
-+
-+destroy_cell:
-+ kfree_const(cell->id);
-+ kfree(cell);
-+
-+ return read_len;
-+}
-+
- /* default read/write permissions */
- static struct bin_attribute bin_attr_rw_nvmem = {
- .attr = {
-@@ -320,11 +357,21 @@ static const struct attribute_group nvme
- .is_bin_visible = nvmem_bin_attr_is_visible,
- };
-
-+/* Cell attributes will be dynamically allocated */
-+static struct attribute_group nvmem_cells_group = {
-+ .name = "cells",
-+};
-+
- static const struct attribute_group *nvmem_dev_groups[] = {
- &nvmem_bin_group,
- NULL,
- };
-
-+static const struct attribute_group *nvmem_cells_groups[] = {
-+ &nvmem_cells_group,
-+ NULL,
-+};
-+
- static struct bin_attribute bin_attr_nvmem_eeprom_compat = {
- .attr = {
- .name = "eeprom",
-@@ -379,6 +426,68 @@ static void nvmem_sysfs_remove_compat(st
- device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
- }
-
-+static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
-+{
-+ struct bin_attribute **cells_attrs, *attrs;
-+ struct nvmem_cell_entry *entry;
-+ unsigned int ncells = 0, i = 0;
-+ int ret = 0;
-+
-+ mutex_lock(&nvmem_mutex);
-+
-+ if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) {
-+ nvmem_cells_group.bin_attrs = NULL;
-+ goto unlock_mutex;
-+ }
-+
-+ /* Allocate an array of attributes with a sentinel */
-+ ncells = list_count_nodes(&nvmem->cells);
-+ cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1,
-+ sizeof(struct bin_attribute *), GFP_KERNEL);
-+ if (!cells_attrs) {
-+ ret = -ENOMEM;
-+ goto unlock_mutex;
-+ }
-+
-+ attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL);
-+ if (!attrs) {
-+ ret = -ENOMEM;
-+ goto unlock_mutex;
-+ }
-+
-+ /* Initialize each attribute to take the name and size of the cell */
-+ list_for_each_entry(entry, &nvmem->cells, node) {
-+ sysfs_bin_attr_init(&attrs[i]);
-+ attrs[i].attr.name = devm_kasprintf(&nvmem->dev, GFP_KERNEL,
-+ "%s@%x", entry->name,
-+ entry->offset);
-+ attrs[i].attr.mode = 0444;
-+ attrs[i].size = entry->bytes;
-+ attrs[i].read = &nvmem_cell_attr_read;
-+ attrs[i].private = entry;
-+ if (!attrs[i].attr.name) {
-+ ret = -ENOMEM;
-+ goto unlock_mutex;
-+ }
-+
-+ cells_attrs[i] = &attrs[i];
-+ i++;
-+ }
-+
-+ nvmem_cells_group.bin_attrs = cells_attrs;
-+
-+ ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups);
-+ if (ret)
-+ goto unlock_mutex;
-+
-+ nvmem->sysfs_cells_populated = true;
-+
-+unlock_mutex:
-+ mutex_unlock(&nvmem_mutex);
-+
-+ return ret;
-+}
-+
- #else /* CONFIG_NVMEM_SYSFS */
-
- static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
-@@ -738,11 +847,25 @@ static int nvmem_add_cells_from_fixed_la
-
- int nvmem_layout_register(struct nvmem_layout *layout)
- {
-+ int ret;
-+
- if (!layout->add_cells)
- return -EINVAL;
-
- /* Populate the cells */
-- return layout->add_cells(&layout->nvmem->dev, layout->nvmem);
-+ ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem);
-+ if (ret)
-+ return ret;
-+
-+#ifdef CONFIG_NVMEM_SYSFS
-+ ret = nvmem_populate_sysfs_cells(layout->nvmem);
-+ if (ret) {
-+ nvmem_device_remove_all_cells(layout->nvmem);
-+ return ret;
-+ }
-+#endif
-+
-+ return 0;
- }
- EXPORT_SYMBOL_GPL(nvmem_layout_register);
-
-@@ -901,10 +1024,20 @@ struct nvmem_device *nvmem_register(cons
- if (rval)
- goto err_remove_dev;
-
-+#ifdef CONFIG_NVMEM_SYSFS
-+ rval = nvmem_populate_sysfs_cells(nvmem);
-+ if (rval)
-+ goto err_destroy_layout;
-+#endif
-+
- blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
-
- return nvmem;
-
-+#ifdef CONFIG_NVMEM_SYSFS
-+err_destroy_layout:
-+ nvmem_destroy_layout(nvmem);
-+#endif
- err_remove_dev:
- device_del(&nvmem->dev);
- err_remove_cells:
---- a/drivers/nvmem/internals.h
-+++ b/drivers/nvmem/internals.h
-@@ -32,6 +32,7 @@ struct nvmem_device {
- struct gpio_desc *wp_gpio;
- struct nvmem_layout *layout;
- void *priv;
-+ bool sysfs_cells_populated;
- };
-
- #if IS_ENABLED(CONFIG_OF)
+++ /dev/null
-From f0ac5b23039610619ca4a4805528553ecb6bc815 Mon Sep 17 00:00:00 2001
-From: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Date: Fri, 15 Dec 2023 11:15:36 +0000
-Subject: [PATCH] nvmem: stm32: add support for STM32MP25 BSEC to control OTP
- data
-
-On STM32MP25, OTP area may be read/written by using BSEC (boot, security
-and OTP control). The BSEC internal peripheral is only managed by the
-secure world.
-
-The 12 Kbits of OTP (effective) are organized into the following regions:
-- lower OTP (OTP0 to OTP127) = 4096 lower OTP bits,
- bitwise (1-bit) programmable
-- mid OTP (OTP128 to OTP255) = 4096 middle OTP bits,
- bulk (32-bit) programmable
-- upper OTP (OTP256 to OTP383) = 4096 upper OTP bits,
- bulk (32-bit) programmable,
- only accessible when BSEC is in closed state.
-
-As HWKEY and ECIES key are only accessible by ROM code;
-only 368 OTP words are managed in this driver (OTP0 to OTP267).
-
-This patch adds the STM32MP25 configuration for reading and writing
-the OTP data using the OP-TEE BSEC TA services.
-
-Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20231215111536.316972-11-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/stm32-romem.c | 16 ++++++++++++++++
- 1 file changed, 16 insertions(+)
-
---- a/drivers/nvmem/stm32-romem.c
-+++ b/drivers/nvmem/stm32-romem.c
-@@ -269,6 +269,19 @@ static const struct stm32_romem_cfg stm3
- .ta = true,
- };
-
-+/*
-+ * STM32MP25 BSEC OTP: 3 regions of 32-bits data words
-+ * lower OTP (OTP0 to OTP127), bitwise (1-bit) programmable
-+ * mid OTP (OTP128 to OTP255), bulk (32-bit) programmable
-+ * upper OTP (OTP256 to OTP383), bulk (32-bit) programmable
-+ * but no access to HWKEY and ECIES key: limited at OTP367
-+ */
-+static const struct stm32_romem_cfg stm32mp25_bsec_cfg = {
-+ .size = 368 * 4,
-+ .lower = 127,
-+ .ta = true,
-+};
-+
- static const struct of_device_id stm32_romem_of_match[] __maybe_unused = {
- { .compatible = "st,stm32f4-otp", }, {
- .compatible = "st,stm32mp15-bsec",
-@@ -276,6 +289,9 @@ static const struct of_device_id stm32_r
- }, {
- .compatible = "st,stm32mp13-bsec",
- .data = (void *)&stm32mp13_bsec_cfg,
-+ }, {
-+ .compatible = "st,stm32mp25-bsec",
-+ .data = (void *)&stm32mp25_bsec_cfg,
- },
- { /* sentinel */ },
- };
+++ /dev/null
-From 401df0d4f4098ecc9c5278da2f50756d62e5b37d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Tue, 19 Dec 2023 13:01:03 +0100
-Subject: [PATCH] nvmem: layouts: refactor .add_cells() callback arguments
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Simply pass whole "struct nvmem_layout" instead of single variables.
-There is nothing in "struct nvmem_layout" that we have to hide from
-layout drivers. They also access it during .probe() and .remove().
-
-Thanks to this change:
-
-1. API gets more consistent
- All layouts drivers callbacks get the same argument
-
-2. Layouts get correct device
- Before this change NVMEM core code was passing NVMEM device instead
- of layout device. That resulted in:
- * Confusing prints
- * Calling devm_*() helpers on wrong device
- * Helpers like of_device_get_match_data() dereferencing NULLs
-
-3. It gets possible to get match data
- First of all nvmem_layout_get_match_data() requires passing "struct
- nvmem_layout" which .add_cells() callback didn't have before this. It
- doesn't matter much as it's rather useless now anyway (and will be
- dropped).
- What's more important however is that of_device_get_match_data() can
- be used now thanks to owning a proper device pointer.
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Reviewed-by: Michael Walle <michael@walle.cc>
-Link: https://lore.kernel.org/r/20231219120104.3422-1-zajec5@gmail.com
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 2 +-
- drivers/nvmem/layouts/onie-tlv.c | 4 +++-
- drivers/nvmem/layouts/sl28vpd.c | 4 +++-
- include/linux/nvmem-provider.h | 2 +-
- 4 files changed, 8 insertions(+), 4 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -853,7 +853,7 @@ int nvmem_layout_register(struct nvmem_l
- return -EINVAL;
-
- /* Populate the cells */
-- ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem);
-+ ret = layout->add_cells(layout);
- if (ret)
- return ret;
-
---- a/drivers/nvmem/layouts/onie-tlv.c
-+++ b/drivers/nvmem/layouts/onie-tlv.c
-@@ -182,8 +182,10 @@ static bool onie_tlv_crc_is_valid(struct
- return true;
- }
-
--static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem)
-+static int onie_tlv_parse_table(struct nvmem_layout *layout)
- {
-+ struct nvmem_device *nvmem = layout->nvmem;
-+ struct device *dev = &layout->dev;
- struct onie_tlv_hdr hdr;
- size_t table_len, data_len, hdr_len;
- u8 *table, *data;
---- a/drivers/nvmem/layouts/sl28vpd.c
-+++ b/drivers/nvmem/layouts/sl28vpd.c
-@@ -80,8 +80,10 @@ static int sl28vpd_v1_check_crc(struct d
- return 0;
- }
-
--static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem)
-+static int sl28vpd_add_cells(struct nvmem_layout *layout)
- {
-+ struct nvmem_device *nvmem = layout->nvmem;
-+ struct device *dev = &layout->dev;
- const struct nvmem_cell_info *pinfo;
- struct nvmem_cell_info info = {0};
- struct device_node *layout_np;
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -173,7 +173,7 @@ struct nvmem_cell_table {
- struct nvmem_layout {
- struct device dev;
- struct nvmem_device *nvmem;
-- int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
-+ int (*add_cells)(struct nvmem_layout *layout);
- };
-
- struct nvmem_layout_driver {
+++ /dev/null
-From 43f60e3fb62edc7bd8891de8779fb422f4ae23ae Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Tue, 19 Dec 2023 13:01:04 +0100
-Subject: [PATCH] nvmem: drop nvmem_layout_get_match_data()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Thanks for layouts refactoring we now have "struct device" associated
-with layout. Also its OF pointer points directly to the "nvmem-layout"
-DT node.
-
-All it takes to get match data is a generic of_device_get_match_data().
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Reviewed-by: Michael Walle <michael@walle.cc>
-Link: https://lore.kernel.org/r/20231219120104.3422-2-zajec5@gmail.com
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 13 -------------
- include/linux/nvmem-provider.h | 10 ----------
- 2 files changed, 23 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -875,19 +875,6 @@ void nvmem_layout_unregister(struct nvme
- }
- EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
-
--const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
-- struct nvmem_layout *layout)
--{
-- struct device_node __maybe_unused *layout_np;
-- const struct of_device_id *match;
--
-- layout_np = of_nvmem_layout_get_container(nvmem);
-- match = of_match_node(layout->dev.driver->of_match_table, layout_np);
--
-- return match ? match->data : NULL;
--}
--EXPORT_SYMBOL_GPL(nvmem_layout_get_match_data);
--
- /**
- * nvmem_register() - Register a nvmem device for given nvmem_config.
- * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -205,9 +205,6 @@ void nvmem_layout_driver_unregister(stru
- module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \
- nvmem_layout_driver_unregister)
-
--const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
-- struct nvmem_layout *layout);
--
- #else
-
- static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c)
-@@ -238,13 +235,6 @@ static inline int nvmem_layout_register(
-
- static inline void nvmem_layout_unregister(struct nvmem_layout *layout) {}
-
--static inline const void *
--nvmem_layout_get_match_data(struct nvmem_device *nvmem,
-- struct nvmem_layout *layout)
--{
-- return NULL;
--}
--
- #endif /* CONFIG_NVMEM */
-
- #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
+++ /dev/null
-From 998f0633773b3432829fe45d2cd2ffb842f3c78e Mon Sep 17 00:00:00 2001
-From: William-tw Lin <william-tw.lin@mediatek.com>
-Date: Sat, 24 Feb 2024 11:45:07 +0000
-Subject: [PATCH] nvmem: mtk-efuse: Register MediaTek socinfo driver from efuse
-
-The socinfo driver reads chip information from eFuses and does not need
-any devicetree node. Register it from mtk-efuse.
-
-While at it, also add the name for this driver's nvmem_config.
-
-Signed-off-by: William-tw Lin <william-tw.lin@mediatek.com>
-Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240224114516.86365-3-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/mtk-efuse.c | 21 ++++++++++++++++++++-
- 1 file changed, 20 insertions(+), 1 deletion(-)
-
---- a/drivers/nvmem/mtk-efuse.c
-+++ b/drivers/nvmem/mtk-efuse.c
-@@ -68,6 +68,7 @@ static int mtk_efuse_probe(struct platfo
- struct nvmem_config econfig = {};
- struct mtk_efuse_priv *priv;
- const struct mtk_efuse_pdata *pdata;
-+ struct platform_device *socinfo;
-
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
-@@ -85,11 +86,20 @@ static int mtk_efuse_probe(struct platfo
- econfig.size = resource_size(res);
- econfig.priv = priv;
- econfig.dev = dev;
-+ econfig.name = "mtk-efuse";
- if (pdata->uses_post_processing)
- econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
- nvmem = devm_nvmem_register(dev, &econfig);
-+ if (IS_ERR(nvmem))
-+ return PTR_ERR(nvmem);
-
-- return PTR_ERR_OR_ZERO(nvmem);
-+ socinfo = platform_device_register_data(&pdev->dev, "mtk-socinfo",
-+ PLATFORM_DEVID_AUTO, NULL, 0);
-+ if (IS_ERR(socinfo))
-+ dev_info(dev, "MediaTek SoC Information will be unavailable\n");
-+
-+ platform_set_drvdata(pdev, socinfo);
-+ return 0;
- }
-
- static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = {
-@@ -108,8 +118,17 @@ static const struct of_device_id mtk_efu
- };
- MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
-
-+static void mtk_efuse_remove(struct platform_device *pdev)
-+{
-+ struct platform_device *socinfo = platform_get_drvdata(pdev);
-+
-+ if (!IS_ERR_OR_NULL(socinfo))
-+ platform_device_unregister(socinfo);
-+}
-+
- static struct platform_driver mtk_efuse_driver = {
- .probe = mtk_efuse_probe,
-+ .remove_new = mtk_efuse_remove,
- .driver = {
- .name = "mediatek,efuse",
- .of_match_table = mtk_efuse_of_match,
+++ /dev/null
-From 29be47fcd6a06ea2e79eeeca6e69ad1e23254a69 Mon Sep 17 00:00:00 2001
-From: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
-Date: Sat, 24 Feb 2024 11:45:11 +0000
-Subject: [PATCH] nvmem: zynqmp_nvmem: zynqmp_nvmem_probe cleanup
-
-- Remove static nvmem_config declaration
-- Remove zynqmp_nvmem_data
-
-Signed-off-by: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
-Acked-by: Kalyani Akula <Kalyani.akula@amd.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240224114516.86365-7-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/zynqmp_nvmem.c | 37 ++++++++++++------------------------
- 1 file changed, 12 insertions(+), 25 deletions(-)
-
---- a/drivers/nvmem/zynqmp_nvmem.c
-+++ b/drivers/nvmem/zynqmp_nvmem.c
-@@ -1,6 +1,7 @@
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2019 Xilinx, Inc.
-+ * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
- */
-
- #include <linux/module.h>
-@@ -11,36 +12,25 @@
-
- #define SILICON_REVISION_MASK 0xF
-
--struct zynqmp_nvmem_data {
-- struct device *dev;
-- struct nvmem_device *nvmem;
--};
-
- static int zynqmp_nvmem_read(void *context, unsigned int offset,
- void *val, size_t bytes)
- {
-+ struct device *dev = context;
- int ret;
-- int idcode, version;
-- struct zynqmp_nvmem_data *priv = context;
-+ int idcode;
-+ int version;
-
- ret = zynqmp_pm_get_chipid(&idcode, &version);
- if (ret < 0)
- return ret;
-
-- dev_dbg(priv->dev, "Read chipid val %x %x\n", idcode, version);
-+ dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
- *(int *)val = version & SILICON_REVISION_MASK;
-
- return 0;
- }
-
--static struct nvmem_config econfig = {
-- .name = "zynqmp-nvmem",
-- .owner = THIS_MODULE,
-- .word_size = 1,
-- .size = 1,
-- .read_only = true,
--};
--
- static const struct of_device_id zynqmp_nvmem_match[] = {
- { .compatible = "xlnx,zynqmp-nvmem-fw", },
- { /* sentinel */ },
-@@ -50,21 +40,18 @@ MODULE_DEVICE_TABLE(of, zynqmp_nvmem_mat
- static int zynqmp_nvmem_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
-- struct zynqmp_nvmem_data *priv;
-+ struct nvmem_config econfig = {};
-
-- priv = devm_kzalloc(dev, sizeof(struct zynqmp_nvmem_data), GFP_KERNEL);
-- if (!priv)
-- return -ENOMEM;
--
-- priv->dev = dev;
-+ econfig.name = "zynqmp-nvmem";
-+ econfig.owner = THIS_MODULE;
-+ econfig.word_size = 1;
-+ econfig.size = 1;
- econfig.dev = dev;
- econfig.add_legacy_fixed_of_cells = true;
-+ econfig.read_only = true;
- econfig.reg_read = zynqmp_nvmem_read;
-- econfig.priv = priv;
--
-- priv->nvmem = devm_nvmem_register(dev, &econfig);
-
-- return PTR_ERR_OR_ZERO(priv->nvmem);
-+ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &econfig));
- }
-
- static struct platform_driver zynqmp_nvmem_driver = {
+++ /dev/null
-From 737c0c8d07b5f671c0a33cec95965fcb2d2ea893 Mon Sep 17 00:00:00 2001
-From: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
-Date: Sat, 24 Feb 2024 11:45:12 +0000
-Subject: [PATCH] nvmem: zynqmp_nvmem: Add support to access efuse
-
-Add support to read/write efuse memory map of ZynqMP.
-Below are the offsets of ZynqMP efuse memory map
- 0 - SOC version(read only)
- 0xC - 0xFC -ZynqMP specific purpose efuses
- 0x100 - 0x17F - Physical Unclonable Function(PUF)
- efuses repurposed as user efuses
-
-Signed-off-by: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
-Acked-by: Kalyani Akula <Kalyani.akula@amd.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240224114516.86365-8-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/zynqmp_nvmem.c | 186 +++++++++++++++++++++++++++++++++--
- 1 file changed, 176 insertions(+), 10 deletions(-)
-
---- a/drivers/nvmem/zynqmp_nvmem.c
-+++ b/drivers/nvmem/zynqmp_nvmem.c
-@@ -4,6 +4,7 @@
- * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
- */
-
-+#include <linux/dma-mapping.h>
- #include <linux/module.h>
- #include <linux/nvmem-provider.h>
- #include <linux/of.h>
-@@ -11,24 +12,189 @@
- #include <linux/firmware/xlnx-zynqmp.h>
-
- #define SILICON_REVISION_MASK 0xF
-+#define P_USER_0_64_UPPER_MASK GENMASK(31, 16)
-+#define P_USER_127_LOWER_4_BIT_MASK GENMASK(3, 0)
-+#define WORD_INBYTES 4
-+#define SOC_VER_SIZE 0x4
-+#define EFUSE_MEMORY_SIZE 0x177
-+#define UNUSED_SPACE 0x8
-+#define ZYNQMP_NVMEM_SIZE (SOC_VER_SIZE + UNUSED_SPACE + \
-+ EFUSE_MEMORY_SIZE)
-+#define SOC_VERSION_OFFSET 0x0
-+#define EFUSE_START_OFFSET 0xC
-+#define EFUSE_END_OFFSET 0xFC
-+#define EFUSE_PUF_START_OFFSET 0x100
-+#define EFUSE_PUF_MID_OFFSET 0x140
-+#define EFUSE_PUF_END_OFFSET 0x17F
-+#define EFUSE_NOT_ENABLED 29
-
-+/*
-+ * efuse access type
-+ */
-+enum efuse_access {
-+ EFUSE_READ = 0,
-+ EFUSE_WRITE
-+};
-+
-+/**
-+ * struct xilinx_efuse - the basic structure
-+ * @src: address of the buffer to store the data to be write/read
-+ * @size: read/write word count
-+ * @offset: read/write offset
-+ * @flag: 0 - represents efuse read and 1- represents efuse write
-+ * @pufuserfuse:0 - represents non-puf efuses, offset is used for read/write
-+ * 1 - represents puf user fuse row number.
-+ *
-+ * this structure stores all the required details to
-+ * read/write efuse memory.
-+ */
-+struct xilinx_efuse {
-+ u64 src;
-+ u32 size;
-+ u32 offset;
-+ enum efuse_access flag;
-+ u32 pufuserfuse;
-+};
-+
-+static int zynqmp_efuse_access(void *context, unsigned int offset,
-+ void *val, size_t bytes, enum efuse_access flag,
-+ unsigned int pufflag)
-+{
-+ struct device *dev = context;
-+ struct xilinx_efuse *efuse;
-+ dma_addr_t dma_addr;
-+ dma_addr_t dma_buf;
-+ size_t words = bytes / WORD_INBYTES;
-+ int ret;
-+ int value;
-+ char *data;
-
--static int zynqmp_nvmem_read(void *context, unsigned int offset,
-- void *val, size_t bytes)
-+ if (bytes % WORD_INBYTES != 0) {
-+ dev_err(dev, "Bytes requested should be word aligned\n");
-+ return -EOPNOTSUPP;
-+ }
-+
-+ if (pufflag == 0 && offset % WORD_INBYTES) {
-+ dev_err(dev, "Offset requested should be word aligned\n");
-+ return -EOPNOTSUPP;
-+ }
-+
-+ if (pufflag == 1 && flag == EFUSE_WRITE) {
-+ memcpy(&value, val, bytes);
-+ if ((offset == EFUSE_PUF_START_OFFSET ||
-+ offset == EFUSE_PUF_MID_OFFSET) &&
-+ value & P_USER_0_64_UPPER_MASK) {
-+ dev_err(dev, "Only lower 4 bytes are allowed to be programmed in P_USER_0 & P_USER_64\n");
-+ return -EOPNOTSUPP;
-+ }
-+
-+ if (offset == EFUSE_PUF_END_OFFSET &&
-+ (value & P_USER_127_LOWER_4_BIT_MASK)) {
-+ dev_err(dev, "Only MSB 28 bits are allowed to be programmed for P_USER_127\n");
-+ return -EOPNOTSUPP;
-+ }
-+ }
-+
-+ efuse = dma_alloc_coherent(dev, sizeof(struct xilinx_efuse),
-+ &dma_addr, GFP_KERNEL);
-+ if (!efuse)
-+ return -ENOMEM;
-+
-+ data = dma_alloc_coherent(dev, sizeof(bytes),
-+ &dma_buf, GFP_KERNEL);
-+ if (!data) {
-+ ret = -ENOMEM;
-+ goto efuse_data_fail;
-+ }
-+
-+ if (flag == EFUSE_WRITE) {
-+ memcpy(data, val, bytes);
-+ efuse->flag = EFUSE_WRITE;
-+ } else {
-+ efuse->flag = EFUSE_READ;
-+ }
-+
-+ efuse->src = dma_buf;
-+ efuse->size = words;
-+ efuse->offset = offset;
-+ efuse->pufuserfuse = pufflag;
-+
-+ zynqmp_pm_efuse_access(dma_addr, (u32 *)&ret);
-+ if (ret != 0) {
-+ if (ret == EFUSE_NOT_ENABLED) {
-+ dev_err(dev, "efuse access is not enabled\n");
-+ ret = -EOPNOTSUPP;
-+ } else {
-+ dev_err(dev, "Error in efuse read %x\n", ret);
-+ ret = -EPERM;
-+ }
-+ goto efuse_access_err;
-+ }
-+
-+ if (flag == EFUSE_READ)
-+ memcpy(val, data, bytes);
-+efuse_access_err:
-+ dma_free_coherent(dev, sizeof(bytes),
-+ data, dma_buf);
-+efuse_data_fail:
-+ dma_free_coherent(dev, sizeof(struct xilinx_efuse),
-+ efuse, dma_addr);
-+
-+ return ret;
-+}
-+
-+static int zynqmp_nvmem_read(void *context, unsigned int offset, void *val, size_t bytes)
- {
- struct device *dev = context;
- int ret;
-+ int pufflag = 0;
- int idcode;
- int version;
-
-- ret = zynqmp_pm_get_chipid(&idcode, &version);
-- if (ret < 0)
-- return ret;
-+ if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET)
-+ pufflag = 1;
-+
-+ switch (offset) {
-+ /* Soc version offset is zero */
-+ case SOC_VERSION_OFFSET:
-+ if (bytes != SOC_VER_SIZE)
-+ return -EOPNOTSUPP;
-+
-+ ret = zynqmp_pm_get_chipid((u32 *)&idcode, (u32 *)&version);
-+ if (ret < 0)
-+ return ret;
-+
-+ dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
-+ *(int *)val = version & SILICON_REVISION_MASK;
-+ break;
-+ /* Efuse offset starts from 0xc */
-+ case EFUSE_START_OFFSET ... EFUSE_END_OFFSET:
-+ case EFUSE_PUF_START_OFFSET ... EFUSE_PUF_END_OFFSET:
-+ ret = zynqmp_efuse_access(context, offset, val,
-+ bytes, EFUSE_READ, pufflag);
-+ break;
-+ default:
-+ *(u32 *)val = 0xDEADBEEF;
-+ ret = 0;
-+ break;
-+ }
-+
-+ return ret;
-+}
-+
-+static int zynqmp_nvmem_write(void *context,
-+ unsigned int offset, void *val, size_t bytes)
-+{
-+ int pufflag = 0;
-+
-+ if (offset < EFUSE_START_OFFSET || offset > EFUSE_PUF_END_OFFSET)
-+ return -EOPNOTSUPP;
-
-- dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
-- *(int *)val = version & SILICON_REVISION_MASK;
-+ if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET)
-+ pufflag = 1;
-
-- return 0;
-+ return zynqmp_efuse_access(context, offset,
-+ val, bytes, EFUSE_WRITE, pufflag);
- }
-
- static const struct of_device_id zynqmp_nvmem_match[] = {
-@@ -45,11 +211,11 @@ static int zynqmp_nvmem_probe(struct pla
- econfig.name = "zynqmp-nvmem";
- econfig.owner = THIS_MODULE;
- econfig.word_size = 1;
-- econfig.size = 1;
-+ econfig.size = ZYNQMP_NVMEM_SIZE;
- econfig.dev = dev;
- econfig.add_legacy_fixed_of_cells = true;
-- econfig.read_only = true;
- econfig.reg_read = zynqmp_nvmem_read;
-+ econfig.reg_write = zynqmp_nvmem_write;
-
- return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &econfig));
- }
+++ /dev/null
-From 76c345edef754b16cab81ad9452cc49c09e67066 Mon Sep 17 00:00:00 2001
-From: Chen-Yu Tsai <wenst@chromium.org>
-Date: Sat, 24 Feb 2024 11:45:14 +0000
-Subject: [PATCH] nvmem: mtk-efuse: Drop NVMEM device name
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The MT8183 has not one but two efuse devices. The static name and ID
-causes the second efuse device to fail to probe, due to duplicate sysfs
-entries.
-
-With the rework of the mtk-socinfo driver, lookup by name is no longer
-necessary. The custom name can simply be dropped.
-
-Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
-Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
-Tested-by: "Nícolas F. R. A. Prado" <nfraprado@collabora.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240224114516.86365-10-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/mtk-efuse.c | 1 -
- 1 file changed, 1 deletion(-)
-
---- a/drivers/nvmem/mtk-efuse.c
-+++ b/drivers/nvmem/mtk-efuse.c
-@@ -86,7 +86,6 @@ static int mtk_efuse_probe(struct platfo
- econfig.size = resource_size(res);
- econfig.priv = priv;
- econfig.dev = dev;
-- econfig.name = "mtk-efuse";
- if (pdata->uses_post_processing)
- econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
- nvmem = devm_nvmem_register(dev, &econfig);
+++ /dev/null
-From 8ec0faf2572216b4e25d6829cd41cf3ee2dab979 Mon Sep 17 00:00:00 2001
-From: "Ricardo B. Marliere" <ricardo@marliere.net>
-Date: Sat, 24 Feb 2024 11:45:15 +0000
-Subject: [PATCH] nvmem: core: make nvmem_layout_bus_type const
-
-Since commit d492cc2573a0 ("driver core: device.h: make struct bus_type
-a const *"), the driver core can properly handle constant struct
-bus_type, move the nvmem_layout_bus_type variable to be a constant
-structure as well, placing it into read-only memory which can not be
-modified at runtime.
-
-Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Signed-off-by: "Ricardo B. Marliere" <ricardo@marliere.net>
-Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240224114516.86365-11-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/layouts.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/layouts.c
-+++ b/drivers/nvmem/layouts.c
-@@ -45,7 +45,7 @@ static void nvmem_layout_bus_remove(stru
- return drv->remove(layout);
- }
-
--static struct bus_type nvmem_layout_bus_type = {
-+static const struct bus_type nvmem_layout_bus_type = {
- .name = "nvmem-layout",
- .match = nvmem_layout_bus_match,
- .probe = nvmem_layout_bus_probe,
+++ /dev/null
-From def3173d4f17b37cecbd74d7c269a080b0b01598 Mon Sep 17 00:00:00 2001
-From: Markus Schneider-Pargmann <msp@baylibre.com>
-Date: Sat, 24 Feb 2024 11:45:16 +0000
-Subject: [PATCH] nvmem: core: Print error on wrong bits DT property
-
-The algorithms in nvmem core are built with the constraint that
-bit_offset < 8. If bit_offset is greater the results are wrong. Print an
-error if the devicetree 'bits' property is outside of the valid range
-and abort parsing.
-
-Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240224114516.86365-12-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -805,6 +805,11 @@ static int nvmem_add_cells_from_dt(struc
- if (addr && len == (2 * sizeof(u32))) {
- info.bit_offset = be32_to_cpup(addr++);
- info.nbits = be32_to_cpup(addr);
-+ if (info.bit_offset >= BITS_PER_BYTE || info.nbits < 1) {
-+ dev_err(dev, "nvmem: invalid bits on %pOF\n", child);
-+ of_node_put(child);
-+ return -EINVAL;
-+ }
- }
-
- info.np = of_node_get(child);
+++ /dev/null
-From 6d0ca4a2a7e25f9ad07c1f335f20b4d9e048cdd5 Mon Sep 17 00:00:00 2001
-From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Date: Tue, 30 Apr 2024 09:49:11 +0100
-Subject: [PATCH] nvmem: layouts: store owner from modules with
- nvmem_layout_driver_register()
-
-Modules registering driver with nvmem_layout_driver_register() might
-forget to set .owner field. The field is used by some of other kernel
-parts for reference counting (try_module_get()), so it is expected that
-drivers will set it.
-
-Solve the problem by moving this task away from the drivers to the core
-code, just like we did for platform_driver in
-commit 9447057eaff8 ("platform_device: use a macro instead of
-platform_driver_register").
-
-Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Reviewed-by: Michael Walle <mwalle@kernel.org>
-Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240430084921.33387-2-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/layouts.c | 6 ++++--
- include/linux/nvmem-provider.h | 5 ++++-
- 2 files changed, 8 insertions(+), 3 deletions(-)
-
---- a/drivers/nvmem/layouts.c
-+++ b/drivers/nvmem/layouts.c
-@@ -52,13 +52,15 @@ static const struct bus_type nvmem_layou
- .remove = nvmem_layout_bus_remove,
- };
-
--int nvmem_layout_driver_register(struct nvmem_layout_driver *drv)
-+int __nvmem_layout_driver_register(struct nvmem_layout_driver *drv,
-+ struct module *owner)
- {
- drv->driver.bus = &nvmem_layout_bus_type;
-+ drv->driver.owner = owner;
-
- return driver_register(&drv->driver);
- }
--EXPORT_SYMBOL_GPL(nvmem_layout_driver_register);
-+EXPORT_SYMBOL_GPL(__nvmem_layout_driver_register);
-
- void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv)
- {
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -199,7 +199,10 @@ int nvmem_add_one_cell(struct nvmem_devi
- int nvmem_layout_register(struct nvmem_layout *layout);
- void nvmem_layout_unregister(struct nvmem_layout *layout);
-
--int nvmem_layout_driver_register(struct nvmem_layout_driver *drv);
-+#define nvmem_layout_driver_register(drv) \
-+ __nvmem_layout_driver_register(drv, THIS_MODULE)
-+int __nvmem_layout_driver_register(struct nvmem_layout_driver *drv,
-+ struct module *owner);
- void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv);
- #define module_nvmem_layout_driver(__nvmem_layout_driver) \
- module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \
+++ /dev/null
-From 21833338eccb91194fec6ba7548d9c454824eca0 Mon Sep 17 00:00:00 2001
-From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Date: Tue, 30 Apr 2024 09:49:12 +0100
-Subject: [PATCH] nvmem: layouts: onie-tlv: drop driver owner initialization
-
-Core in nvmem_layout_driver_register() already sets the .owner, so
-driver does not need to.
-
-Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Reviewed-by: Michael Walle <mwalle@kernel.org>
-Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240430084921.33387-3-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/layouts/onie-tlv.c | 1 -
- 1 file changed, 1 deletion(-)
-
---- a/drivers/nvmem/layouts/onie-tlv.c
-+++ b/drivers/nvmem/layouts/onie-tlv.c
-@@ -247,7 +247,6 @@ MODULE_DEVICE_TABLE(of, onie_tlv_of_matc
-
- static struct nvmem_layout_driver onie_tlv_layout = {
- .driver = {
-- .owner = THIS_MODULE,
- .name = "onie-tlv-layout",
- .of_match_table = onie_tlv_of_match_table,
- },
+++ /dev/null
-From 23fd602f21953c03c0714257d36685cd6b486f04 Mon Sep 17 00:00:00 2001
-From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Date: Tue, 30 Apr 2024 09:49:13 +0100
-Subject: [PATCH] nvmem: layouts: sl28vpd: drop driver owner initialization
-
-Core in nvmem_layout_driver_register() already sets the .owner, so
-driver does not need to.
-
-Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Reviewed-by: Michael Walle <mwalle@kernel.org>
-Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240430084921.33387-4-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/layouts/sl28vpd.c | 1 -
- 1 file changed, 1 deletion(-)
-
---- a/drivers/nvmem/layouts/sl28vpd.c
-+++ b/drivers/nvmem/layouts/sl28vpd.c
-@@ -156,7 +156,6 @@ MODULE_DEVICE_TABLE(of, sl28vpd_of_match
-
- static struct nvmem_layout_driver sl28vpd_layout = {
- .driver = {
-- .owner = THIS_MODULE,
- .name = "kontron-sl28vpd-layout",
- .of_match_table = sl28vpd_of_match_table,
- },
+++ /dev/null
-From dc3d88ade857ba3dca34f008e0b0aed3ef79cb15 Mon Sep 17 00:00:00 2001
-From: Krzysztof Kozlowski <krzk@kernel.org>
-Date: Tue, 30 Apr 2024 09:49:14 +0100
-Subject: [PATCH] nvmem: sc27xx: fix module autoloading
-
-Add MODULE_DEVICE_TABLE(), so the module could be properly autoloaded
-based on the alias from of_device_id table.
-
-Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240430084921.33387-5-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/sc27xx-efuse.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/nvmem/sc27xx-efuse.c
-+++ b/drivers/nvmem/sc27xx-efuse.c
-@@ -262,6 +262,7 @@ static const struct of_device_id sc27xx_
- { .compatible = "sprd,sc2730-efuse", .data = &sc2730_edata},
- { }
- };
-+MODULE_DEVICE_TABLE(of, sc27xx_efuse_of_match);
-
- static struct platform_driver sc27xx_efuse_driver = {
- .probe = sc27xx_efuse_probe,
+++ /dev/null
-From 154c1ec943e34f3188c9305b0c91d5e7dc1373b8 Mon Sep 17 00:00:00 2001
-From: Krzysztof Kozlowski <krzk@kernel.org>
-Date: Tue, 30 Apr 2024 09:49:15 +0100
-Subject: [PATCH] nvmem: sprd: fix module autoloading
-
-Add MODULE_DEVICE_TABLE(), so the module could be properly autoloaded
-based on the alias from of_device_id table.
-
-Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240430084921.33387-6-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/sprd-efuse.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/nvmem/sprd-efuse.c
-+++ b/drivers/nvmem/sprd-efuse.c
-@@ -426,6 +426,7 @@ static const struct of_device_id sprd_ef
- { .compatible = "sprd,ums312-efuse", .data = &ums312_data },
- { }
- };
-+MODULE_DEVICE_TABLE(of, sprd_efuse_of_match);
-
- static struct platform_driver sprd_efuse_driver = {
- .probe = sprd_efuse_probe,
+++ /dev/null
-From 8d8fc146dd7a0d6a6b37695747a524310dfb9d57 Mon Sep 17 00:00:00 2001
-From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Date: Tue, 30 Apr 2024 09:49:16 +0100
-Subject: [PATCH] nvmem: core: switch to use device_add_groups()
-
-devm_device_add_groups() is being removed from the kernel, so move the
-nvmem driver to use device_add_groups() instead. The logic is
-identical, when the device is removed the driver core will properly
-clean up and remove the groups, and the memory used by the attribute
-groups will be freed because it was created with dev_* calls, so this is
-functionally identical overall.
-
-Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Link: https://lore.kernel.org/r/20240430084921.33387-7-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -476,7 +476,7 @@ static int nvmem_populate_sysfs_cells(st
-
- nvmem_cells_group.bin_attrs = cells_attrs;
-
-- ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups);
-+ ret = device_add_groups(&nvmem->dev, nvmem_cells_groups);
- if (ret)
- goto unlock_mutex;
-
+++ /dev/null
-From 693d2f629962628ddefc88f4b6b453edda5ac32e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de>
-Date: Tue, 30 Apr 2024 09:49:17 +0100
-Subject: [PATCH] nvmem: lpc18xx_eeprom: Convert to platform remove callback
- returning void
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The .remove() callback for a platform driver returns an int which makes
-many driver authors wrongly assume it's possible to do error handling by
-returning an error code. However the value returned is ignored (apart
-from emitting a warning) and this typically results in resource leaks.
-
-To improve here there is a quest to make the remove callback return
-void. In the first step of this quest all drivers are converted to
-.remove_new(), which already returns void. Eventually after all drivers
-are converted, .remove_new() will be renamed to .remove().
-
-Trivially convert this driver from always returning zero in the remove
-callback to the void returning variant.
-
-Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
-Acked-by: Vladimir Zapolskiy <vz@mleia.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240430084921.33387-8-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/lpc18xx_eeprom.c | 6 ++----
- 1 file changed, 2 insertions(+), 4 deletions(-)
-
---- a/drivers/nvmem/lpc18xx_eeprom.c
-+++ b/drivers/nvmem/lpc18xx_eeprom.c
-@@ -249,13 +249,11 @@ err_clk:
- return ret;
- }
-
--static int lpc18xx_eeprom_remove(struct platform_device *pdev)
-+static void lpc18xx_eeprom_remove(struct platform_device *pdev)
- {
- struct lpc18xx_eeprom_dev *eeprom = platform_get_drvdata(pdev);
-
- clk_disable_unprepare(eeprom->clk);
--
-- return 0;
- }
-
- static const struct of_device_id lpc18xx_eeprom_of_match[] = {
-@@ -266,7 +264,7 @@ MODULE_DEVICE_TABLE(of, lpc18xx_eeprom_o
-
- static struct platform_driver lpc18xx_eeprom_driver = {
- .probe = lpc18xx_eeprom_probe,
-- .remove = lpc18xx_eeprom_remove,
-+ .remove_new = lpc18xx_eeprom_remove,
- .driver = {
- .name = "lpc18xx-eeprom",
- .of_match_table = lpc18xx_eeprom_of_match,
+++ /dev/null
-From 2a1ad6b75292d38aa2f6ded7335979e0632521da Mon Sep 17 00:00:00 2001
-From: Mukesh Ojha <quic_mojha@quicinc.com>
-Date: Tue, 30 Apr 2024 09:49:21 +0100
-Subject: [PATCH] nvmem: meson-mx-efuse: Remove nvmem_device from efuse struct
-
-nvmem_device is used at one place while registering nvmem
-device and it is not required to be present in efuse struct
-for just this purpose.
-
-Drop nvmem_device and manage with nvmem device stack variable.
-
-Signed-off-by: Mukesh Ojha <quic_mojha@quicinc.com>
-Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240430084921.33387-12-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/meson-mx-efuse.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
---- a/drivers/nvmem/meson-mx-efuse.c
-+++ b/drivers/nvmem/meson-mx-efuse.c
-@@ -43,7 +43,6 @@ struct meson_mx_efuse_platform_data {
- struct meson_mx_efuse {
- void __iomem *base;
- struct clk *core_clk;
-- struct nvmem_device *nvmem;
- struct nvmem_config config;
- };
-
-@@ -193,6 +192,7 @@ static int meson_mx_efuse_probe(struct p
- {
- const struct meson_mx_efuse_platform_data *drvdata;
- struct meson_mx_efuse *efuse;
-+ struct nvmem_device *nvmem;
-
- drvdata = of_device_get_match_data(&pdev->dev);
- if (!drvdata)
-@@ -223,9 +223,9 @@ static int meson_mx_efuse_probe(struct p
- return PTR_ERR(efuse->core_clk);
- }
-
-- efuse->nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);
-+ nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);
-
-- return PTR_ERR_OR_ZERO(efuse->nvmem);
-+ return PTR_ERR_OR_ZERO(nvmem);
- }
-
- static struct platform_driver meson_mx_efuse_driver = {
+++ /dev/null
-From c553bad4c5fc5ae44bd2fcaa73e1d6bedfb1c35c Mon Sep 17 00:00:00 2001
-From: Jeff Johnson <quic_jjohnson@quicinc.com>
-Date: Fri, 5 Jul 2024 08:48:38 +0100
-Subject: [PATCH] nvmem: add missing MODULE_DESCRIPTION() macros
-
-make allmodconfig && make W=1 C=1 reports:
-WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/nvmem/nvmem-apple-efuses.o
-WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/nvmem/nvmem_brcm_nvram.o
-WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/nvmem/nvmem_u-boot-env.o
-
-Add the missing invocations of the MODULE_DESCRIPTION() macro.
-
-Signed-off-by: Jeff Johnson <quic_jjohnson@quicinc.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240705074852.423202-2-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/apple-efuses.c | 1 +
- drivers/nvmem/brcm_nvram.c | 1 +
- drivers/nvmem/u-boot-env.c | 1 +
- 3 files changed, 3 insertions(+)
-
---- a/drivers/nvmem/apple-efuses.c
-+++ b/drivers/nvmem/apple-efuses.c
-@@ -78,4 +78,5 @@ static struct platform_driver apple_efus
- module_platform_driver(apple_efuses_driver);
-
- MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
-+MODULE_DESCRIPTION("Apple SoC eFuse driver");
- MODULE_LICENSE("GPL");
---- a/drivers/nvmem/brcm_nvram.c
-+++ b/drivers/nvmem/brcm_nvram.c
-@@ -253,5 +253,6 @@ static int __init brcm_nvram_init(void)
- subsys_initcall_sync(brcm_nvram_init);
-
- MODULE_AUTHOR("Rafał Miłecki");
-+MODULE_DESCRIPTION("Broadcom I/O-mapped NVRAM support driver");
- MODULE_LICENSE("GPL");
- MODULE_DEVICE_TABLE(of, brcm_nvram_of_match_table);
---- a/drivers/nvmem/u-boot-env.c
-+++ b/drivers/nvmem/u-boot-env.c
-@@ -256,5 +256,6 @@ static struct platform_driver u_boot_env
- module_platform_driver(u_boot_env_driver);
-
- MODULE_AUTHOR("Rafał Miłecki");
-+MODULE_DESCRIPTION("U-Boot environment variables support module");
- MODULE_LICENSE("GPL");
- MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table);
+++ /dev/null
-From 5fecb932607d83d37a703c731268e9d9051457f5 Mon Sep 17 00:00:00 2001
-From: MarileneGarcia <marilene.agarcia@gmail.com>
-Date: Fri, 5 Jul 2024 08:48:40 +0100
-Subject: [PATCH] nvmem: meson-efuse: Replacing the use of of_node_put to
- __free
-
-Use __free for device_node values, and thus drop calls to
-of_node_put.
-
-The goal is to reduce memory management issues by using this
-scope-based of_node_put() cleanup to simplify function exit
-handling. When using __free a resource is allocated within a
-block, it is automatically freed at the end of the block.
-
-Suggested-by: Julia Lawall <julia.lawall@inria.fr>
-Signed-off-by: MarileneGarcia <marilene.agarcia@gmail.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240705074852.423202-4-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/meson-efuse.c | 5 ++---
- 1 file changed, 2 insertions(+), 3 deletions(-)
-
---- a/drivers/nvmem/meson-efuse.c
-+++ b/drivers/nvmem/meson-efuse.c
-@@ -48,20 +48,19 @@ static int meson_efuse_probe(struct plat
- {
- struct device *dev = &pdev->dev;
- struct meson_sm_firmware *fw;
-- struct device_node *sm_np;
- struct nvmem_device *nvmem;
- struct nvmem_config *econfig;
- struct clk *clk;
- unsigned int size;
-+ struct device_node *sm_np __free(device_node) =
-+ of_parse_phandle(pdev->dev.of_node, "secure-monitor", 0);
-
-- sm_np = of_parse_phandle(pdev->dev.of_node, "secure-monitor", 0);
- if (!sm_np) {
- dev_err(&pdev->dev, "no secure-monitor node\n");
- return -ENODEV;
- }
-
- fw = meson_sm_get(sm_np);
-- of_node_put(sm_np);
- if (!fw)
- return -EPROBE_DEFER;
-
+++ /dev/null
-From 39f95600d8c53355b212a117e91a6ba15e0cac47 Mon Sep 17 00:00:00 2001
-From: Heiko Stuebner <heiko.stuebner@cherry.de>
-Date: Fri, 5 Jul 2024 08:48:42 +0100
-Subject: [PATCH] nvmem: rockchip-otp: Set type to OTP
-
-The Rockchip OTP is obviously an OTP memory, so document this fact.
-
-Signed-off-by: Heiko Stuebner <heiko.stuebner@cherry.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240705074852.423202-6-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/rockchip-otp.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/nvmem/rockchip-otp.c
-+++ b/drivers/nvmem/rockchip-otp.c
-@@ -256,6 +256,7 @@ static struct nvmem_config otp_config =
- .name = "rockchip-otp",
- .owner = THIS_MODULE,
- .add_legacy_fixed_of_cells = true,
-+ .type = NVMEM_TYPE_OTP,
- .read_only = true,
- .stride = 1,
- .word_size = 1,
+++ /dev/null
-From ba64a04474d2989f397982c48e405cfd785e2dd5 Mon Sep 17 00:00:00 2001
-From: Heiko Stuebner <heiko.stuebner@cherry.de>
-Date: Fri, 5 Jul 2024 08:48:43 +0100
-Subject: [PATCH] nvmem: rockchip-efuse: set type to OTP
-
-This device currently reports an "Unknown" type in sysfs.
-Since it is an eFuse hardware device, set its type to OTP.
-
-Signed-off-by: Heiko Stuebner <heiko.stuebner@cherry.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240705074852.423202-7-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/rockchip-efuse.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/nvmem/rockchip-efuse.c
-+++ b/drivers/nvmem/rockchip-efuse.c
-@@ -206,6 +206,7 @@ static int rockchip_rk3399_efuse_read(vo
- static struct nvmem_config econfig = {
- .name = "rockchip-efuse",
- .add_legacy_fixed_of_cells = true,
-+ .type = NVMEM_TYPE_OTP,
- .stride = 1,
- .word_size = 1,
- .read_only = true,
+++ /dev/null
-From 6188f233161c6a5b2d1c396a221dfafc77dc9eec Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <linux@weissschuh.net>
-Date: Fri, 5 Jul 2024 08:48:46 +0100
-Subject: [PATCH] nvmem: core: add single sysfs group
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The sysfs core provides a function to easily register a single group.
-Use it and remove the now unnecessary nvmem_cells_groups array.
-
-Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240705074852.423202-10-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 7 +------
- 1 file changed, 1 insertion(+), 6 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -367,11 +367,6 @@ static const struct attribute_group *nvm
- NULL,
- };
-
--static const struct attribute_group *nvmem_cells_groups[] = {
-- &nvmem_cells_group,
-- NULL,
--};
--
- static struct bin_attribute bin_attr_nvmem_eeprom_compat = {
- .attr = {
- .name = "eeprom",
-@@ -476,7 +471,7 @@ static int nvmem_populate_sysfs_cells(st
-
- nvmem_cells_group.bin_attrs = cells_attrs;
-
-- ret = device_add_groups(&nvmem->dev, nvmem_cells_groups);
-+ ret = device_add_group(&nvmem->dev, &nvmem_cells_group);
- if (ret)
- goto unlock_mutex;
-
+++ /dev/null
-From 6839fed062b7898665983368c88269a6fb1fc10f Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <linux@weissschuh.net>
-Date: Fri, 5 Jul 2024 08:48:47 +0100
-Subject: [PATCH] nvmem: core: remove global nvmem_cells_group
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-nvmem_cells_groups is a global variable that is also mutated.
-This is complicated and error-prone.
-
-Instead use a normal stack variable.
-
-Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240705074852.423202-11-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 26 ++++++++++----------------
- 1 file changed, 10 insertions(+), 16 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -357,11 +357,6 @@ static const struct attribute_group nvme
- .is_bin_visible = nvmem_bin_attr_is_visible,
- };
-
--/* Cell attributes will be dynamically allocated */
--static struct attribute_group nvmem_cells_group = {
-- .name = "cells",
--};
--
- static const struct attribute_group *nvmem_dev_groups[] = {
- &nvmem_bin_group,
- NULL,
-@@ -423,23 +418,24 @@ static void nvmem_sysfs_remove_compat(st
-
- static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
- {
-- struct bin_attribute **cells_attrs, *attrs;
-+ struct attribute_group group = {
-+ .name = "cells",
-+ };
- struct nvmem_cell_entry *entry;
-+ struct bin_attribute *attrs;
- unsigned int ncells = 0, i = 0;
- int ret = 0;
-
- mutex_lock(&nvmem_mutex);
-
-- if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) {
-- nvmem_cells_group.bin_attrs = NULL;
-+ if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated)
- goto unlock_mutex;
-- }
-
- /* Allocate an array of attributes with a sentinel */
- ncells = list_count_nodes(&nvmem->cells);
-- cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1,
-- sizeof(struct bin_attribute *), GFP_KERNEL);
-- if (!cells_attrs) {
-+ group.bin_attrs = devm_kcalloc(&nvmem->dev, ncells + 1,
-+ sizeof(struct bin_attribute *), GFP_KERNEL);
-+ if (!group.bin_attrs) {
- ret = -ENOMEM;
- goto unlock_mutex;
- }
-@@ -465,13 +461,11 @@ static int nvmem_populate_sysfs_cells(st
- goto unlock_mutex;
- }
-
-- cells_attrs[i] = &attrs[i];
-+ group.bin_attrs[i] = &attrs[i];
- i++;
- }
-
-- nvmem_cells_group.bin_attrs = cells_attrs;
--
-- ret = device_add_group(&nvmem->dev, &nvmem_cells_group);
-+ ret = device_add_group(&nvmem->dev, &group);
- if (ret)
- goto unlock_mutex;
-
+++ /dev/null
-From 588773802c386d38f9c4e91acd47369e89d95a30 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <linux@weissschuh.net>
-Date: Fri, 5 Jul 2024 08:48:48 +0100
-Subject: [PATCH] nvmem: core: drop unnecessary range checks in sysfs callbacks
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The same checks have already been done in sysfs_kf_bin_write() and
-sysfs_kf_bin_read() just before the callbacks are invoked.
-
-Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240705074852.423202-12-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 14 --------------
- 1 file changed, 14 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -203,19 +203,12 @@ static ssize_t bin_attr_nvmem_read(struc
- dev = kobj_to_dev(kobj);
- nvmem = to_nvmem_device(dev);
-
-- /* Stop the user from reading */
-- if (pos >= nvmem->size)
-- return 0;
--
- if (!IS_ALIGNED(pos, nvmem->stride))
- return -EINVAL;
-
- if (count < nvmem->word_size)
- return -EINVAL;
-
-- if (pos + count > nvmem->size)
-- count = nvmem->size - pos;
--
- count = round_down(count, nvmem->word_size);
-
- if (!nvmem->reg_read)
-@@ -243,19 +236,12 @@ static ssize_t bin_attr_nvmem_write(stru
- dev = kobj_to_dev(kobj);
- nvmem = to_nvmem_device(dev);
-
-- /* Stop the user from writing */
-- if (pos >= nvmem->size)
-- return -EFBIG;
--
- if (!IS_ALIGNED(pos, nvmem->stride))
- return -EINVAL;
-
- if (count < nvmem->word_size)
- return -EINVAL;
-
-- if (pos + count > nvmem->size)
-- count = nvmem->size - pos;
--
- count = round_down(count, nvmem->word_size);
-
- if (!nvmem->reg_write)
+++ /dev/null
-From 08c367e45b6d322956878774f0b88bf5e52c6d54 Mon Sep 17 00:00:00 2001
-From: Marek Vasut <marex@denx.de>
-Date: Fri, 5 Jul 2024 08:48:51 +0100
-Subject: [PATCH] nvmem: Use sysfs_emit() for type attribute
-
-Use sysfs_emit() instead of sprintf() to follow best practice per
-Documentation/filesystems/sysfs.rst
-"
-show() should only use sysfs_emit()...
-"
-
-Signed-off-by: Marek Vasut <marex@denx.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240705074852.423202-15-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -179,7 +179,7 @@ static ssize_t type_show(struct device *
- {
- struct nvmem_device *nvmem = to_nvmem_device(dev);
-
-- return sprintf(buf, "%s\n", nvmem_type_str[nvmem->type]);
-+ return sysfs_emit(buf, "%s\n", nvmem_type_str[nvmem->type]);
- }
-
- static DEVICE_ATTR_RO(type);
+++ /dev/null
-From 9d7eb234ac7a56b88aea8a52ed81553a730fe25c Mon Sep 17 00:00:00 2001
-From: Marek Vasut <marex@denx.de>
-Date: Fri, 5 Jul 2024 08:48:52 +0100
-Subject: [PATCH] nvmem: core: Implement force_ro sysfs attribute
-
-Implement "force_ro" sysfs attribute to allow users to set read-write
-devices as read-only and back to read-write from userspace. The choice
-of the name is based on MMC core 'force_ro' attribute.
-
-This solves a situation where an AT24 I2C EEPROM with GPIO based nWP
-signal may have to be occasionally updated. Such I2C EEPROM device is
-usually set as read-only during most of the regular system operation,
-but in case it has to be updated in a controlled manner, it could be
-unlocked using this new "force_ro" sysfs attribute and then re-locked
-again.
-
-The "read-only" DT property and config->read_only configuration is
-respected and is used to set default state of the device, read-only
-or read-write, for devices which do implement .reg_write function.
-For devices which do not implement .reg_write function, the device
-is unconditionally read-only and the "force_ro" attribute is not
-visible.
-
-Signed-off-by: Marek Vasut <marex@denx.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240705074852.423202-16-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- Documentation/ABI/stable/sysfs-bus-nvmem | 17 ++++++++++
- drivers/nvmem/core.c | 43 ++++++++++++++++++++++++
- 2 files changed, 60 insertions(+)
-
---- a/Documentation/ABI/stable/sysfs-bus-nvmem
-+++ b/Documentation/ABI/stable/sysfs-bus-nvmem
-@@ -1,3 +1,20 @@
-+What: /sys/bus/nvmem/devices/.../force_ro
-+Date: June 2024
-+KernelVersion: 6.11
-+Contact: Marek Vasut <marex@denx.de>
-+Description:
-+ This read/write attribute allows users to set read-write
-+ devices as read-only and back to read-write from userspace.
-+ This can be used to unlock and relock write-protection of
-+ devices which are generally locked, except during sporadic
-+ programming operation.
-+ Read returns '0' or '1' for read-write or read-only modes
-+ respectively.
-+ Write parses one of 'YyTt1NnFf0', or [oO][NnFf] for "on"
-+ and "off", i.e. what kstrbool() supports.
-+ Note: This file is only present if CONFIG_NVMEM_SYSFS
-+ is enabled.
-+
- What: /sys/bus/nvmem/devices/.../nvmem
- Date: July 2015
- KernelVersion: 4.2
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -184,7 +184,30 @@ static ssize_t type_show(struct device *
-
- static DEVICE_ATTR_RO(type);
-
-+static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr,
-+ char *buf)
-+{
-+ struct nvmem_device *nvmem = to_nvmem_device(dev);
-+
-+ return sysfs_emit(buf, "%d\n", nvmem->read_only);
-+}
-+
-+static ssize_t force_ro_store(struct device *dev, struct device_attribute *attr,
-+ const char *buf, size_t count)
-+{
-+ struct nvmem_device *nvmem = to_nvmem_device(dev);
-+ int ret = kstrtobool(buf, &nvmem->read_only);
-+
-+ if (ret < 0)
-+ return ret;
-+
-+ return count;
-+}
-+
-+static DEVICE_ATTR_RW(force_ro);
-+
- static struct attribute *nvmem_attrs[] = {
-+ &dev_attr_force_ro.attr,
- &dev_attr_type.attr,
- NULL,
- };
-@@ -285,6 +308,25 @@ static umode_t nvmem_bin_attr_is_visible
- return nvmem_bin_attr_get_umode(nvmem);
- }
-
-+static umode_t nvmem_attr_is_visible(struct kobject *kobj,
-+ struct attribute *attr, int i)
-+{
-+ struct device *dev = kobj_to_dev(kobj);
-+ struct nvmem_device *nvmem = to_nvmem_device(dev);
-+
-+ /*
-+ * If the device has no .reg_write operation, do not allow
-+ * configuration as read-write.
-+ * If the device is set as read-only by configuration, it
-+ * can be forced into read-write mode using the 'force_ro'
-+ * attribute.
-+ */
-+ if (attr == &dev_attr_force_ro.attr && !nvmem->reg_write)
-+ return 0; /* Attribute not visible */
-+
-+ return attr->mode;
-+}
-+
- static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry,
- const char *id, int index);
-
-@@ -341,6 +383,7 @@ static const struct attribute_group nvme
- .bin_attrs = nvmem_bin_attributes,
- .attrs = nvmem_attrs,
- .is_bin_visible = nvmem_bin_attr_is_visible,
-+ .is_visible = nvmem_attr_is_visible,
- };
-
- static const struct attribute_group *nvmem_dev_groups[] = {
+++ /dev/null
-From c3f9b7b4e5f9de319d00784577cda42036ff243a Mon Sep 17 00:00:00 2001
-From: Peng Fan <peng.fan@nxp.com>
-Date: Mon, 2 Sep 2024 15:29:45 +0100
-Subject: [PATCH] nvmem: imx-ocotp-ele: support i.MX95
-
-i.MX95 OCOTP has same accessing method, so add an entry for i.MX95, but
-some fuse has ECC feature, so only read out the lower 16bits for ECC fuses.
-
-Signed-off-by: Peng Fan <peng.fan@nxp.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240902142952.71639-3-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/imx-ocotp-ele.c | 32 +++++++++++++++++++++++++++++---
- 1 file changed, 29 insertions(+), 3 deletions(-)
-
---- a/drivers/nvmem/imx-ocotp-ele.c
-+++ b/drivers/nvmem/imx-ocotp-ele.c
-@@ -14,8 +14,9 @@
- #include <linux/slab.h>
-
- enum fuse_type {
-- FUSE_FSB = 1,
-- FUSE_ELE = 2,
-+ FUSE_FSB = BIT(0),
-+ FUSE_ELE = BIT(1),
-+ FUSE_ECC = BIT(2),
- FUSE_INVALID = -1
- };
-
-@@ -95,7 +96,10 @@ static int imx_ocotp_reg_read(void *cont
- continue;
- }
-
-- *buf++ = readl_relaxed(reg + (i << 2));
-+ if (type & FUSE_ECC)
-+ *buf++ = readl_relaxed(reg + (i << 2)) & GENMASK(15, 0);
-+ else
-+ *buf++ = readl_relaxed(reg + (i << 2));
- }
-
- memcpy(val, ((u8 *)p) + skipbytes, bytes);
-@@ -179,8 +183,30 @@ static const struct ocotp_devtype_data i
- },
- };
-
-+static const struct ocotp_devtype_data imx95_ocotp_data = {
-+ .reg_off = 0x8000,
-+ .reg_read = imx_ocotp_reg_read,
-+ .size = 2048,
-+ .num_entry = 12,
-+ .entry = {
-+ { 0, 1, FUSE_FSB | FUSE_ECC },
-+ { 7, 1, FUSE_FSB | FUSE_ECC },
-+ { 9, 3, FUSE_FSB | FUSE_ECC },
-+ { 12, 24, FUSE_FSB },
-+ { 36, 2, FUSE_FSB | FUSE_ECC },
-+ { 38, 14, FUSE_FSB },
-+ { 63, 1, FUSE_ELE },
-+ { 128, 16, FUSE_ELE },
-+ { 188, 1, FUSE_ELE },
-+ { 317, 2, FUSE_FSB | FUSE_ECC },
-+ { 320, 7, FUSE_FSB },
-+ { 328, 184, FUSE_FSB }
-+ },
-+};
-+
- static const struct of_device_id imx_ele_ocotp_dt_ids[] = {
- { .compatible = "fsl,imx93-ocotp", .data = &imx93_ocotp_data, },
-+ { .compatible = "fsl,imx95-ocotp", .data = &imx95_ocotp_data, },
- {},
- };
- MODULE_DEVICE_TABLE(of, imx_ele_ocotp_dt_ids);
+++ /dev/null
-From 98ee46391baf35987227236d0c3bb30ab6e758c8 Mon Sep 17 00:00:00 2001
-From: Zhang Zekun <zhangzekun11@huawei.com>
-Date: Mon, 2 Sep 2024 15:29:50 +0100
-Subject: [PATCH] nvmem: sunplus-ocotp: Use
- devm_platform_ioremap_resource_byname() helper function
-
-platform_get_resource_byname() and devm_ioremap_resource() can be
-replaced by devm_platform_ioremap_resource_byname(), which can
-simplify the code logic a bit, No functional change here.
-
-Signed-off-by: Zhang Zekun <zhangzekun11@huawei.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240902142952.71639-8-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/sunplus-ocotp.c | 7 ++-----
- 1 file changed, 2 insertions(+), 5 deletions(-)
-
---- a/drivers/nvmem/sunplus-ocotp.c
-+++ b/drivers/nvmem/sunplus-ocotp.c
-@@ -159,7 +159,6 @@ static int sp_ocotp_probe(struct platfor
- struct device *dev = &pdev->dev;
- struct nvmem_device *nvmem;
- struct sp_ocotp_priv *otp;
-- struct resource *res;
- int ret;
-
- otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
-@@ -168,13 +167,11 @@ static int sp_ocotp_probe(struct platfor
-
- otp->dev = dev;
-
-- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hb_gpio");
-- otp->base[HB_GPIO] = devm_ioremap_resource(dev, res);
-+ otp->base[HB_GPIO] = devm_platform_ioremap_resource_byname(pdev, "hb_gpio");
- if (IS_ERR(otp->base[HB_GPIO]))
- return PTR_ERR(otp->base[HB_GPIO]);
-
-- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otprx");
-- otp->base[OTPRX] = devm_ioremap_resource(dev, res);
-+ otp->base[OTPRX] = devm_platform_ioremap_resource_byname(pdev, "otprx");
- if (IS_ERR(otp->base[OTPRX]))
- return PTR_ERR(otp->base[OTPRX]);
-
+++ /dev/null
-From 5f15811286aff4664bf275a7ede64e1b8858151b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Mon, 2 Sep 2024 15:29:47 +0100
-Subject: [PATCH] nvmem: layouts: add U-Boot env layout
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-U-Boot environment variables are stored in a specific format. Actual
-data can be placed in various storage sources (MTD, UBI volume, EEPROM,
-NVRAM, etc.).
-
-Move all generic (NVMEM device independent) code from NVMEM device
-driver to an NVMEM layout driver. Then add a simple NVMEM layout code on
-top of it.
-
-This allows using NVMEM layout for parsing U-Boot env data stored in any
-kind of NVMEM device.
-
-The old NVMEM glue driver stays in place for handling bindings in the
-MTD context. To avoid code duplication it uses exported layout parsing
-function. Please note that handling MTD & NVMEM layout bindings may be
-refactored in the future.
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20240902142952.71639-5-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- MAINTAINERS | 1 +
- drivers/nvmem/Kconfig | 3 +-
- drivers/nvmem/layouts/Kconfig | 11 ++
- drivers/nvmem/layouts/Makefile | 1 +
- drivers/nvmem/layouts/u-boot-env.c | 211 +++++++++++++++++++++++++++++
- drivers/nvmem/layouts/u-boot-env.h | 15 ++
- drivers/nvmem/u-boot-env.c | 165 +---------------------
- 7 files changed, 242 insertions(+), 165 deletions(-)
- create mode 100644 drivers/nvmem/layouts/u-boot-env.c
- create mode 100644 drivers/nvmem/layouts/u-boot-env.h
-
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -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
-+F: drivers/nvmem/layouts/u-boot-env.c
- F: drivers/nvmem/u-boot-env.c
-
- UACCE ACCELERATOR FRAMEWORK
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -363,8 +363,7 @@ config NVMEM_SUNXI_SID
- config NVMEM_U_BOOT_ENV
- tristate "U-Boot environment variables support"
- depends on OF && MTD
-- select CRC32
-- select GENERIC_NET_UTILS
-+ select NVMEM_LAYOUT_U_BOOT_ENV
- help
- U-Boot stores its setup as environment variables. This driver adds
- support for verifying & exporting such data. It also exposes variables
---- a/drivers/nvmem/layouts/Kconfig
-+++ b/drivers/nvmem/layouts/Kconfig
-@@ -26,6 +26,17 @@ config NVMEM_LAYOUT_ONIE_TLV
-
- If unsure, say N.
-
-+config NVMEM_LAYOUT_U_BOOT_ENV
-+ tristate "U-Boot environment variables layout"
-+ select CRC32
-+ select GENERIC_NET_UTILS
-+ help
-+ U-Boot stores its setup as environment variables. This driver adds
-+ support for verifying & exporting such data. It also exposes variables
-+ as NVMEM cells so they can be referenced by other drivers.
-+
-+ If unsure, say N.
-+
- endmenu
-
- endif
---- a/drivers/nvmem/layouts/Makefile
-+++ b/drivers/nvmem/layouts/Makefile
-@@ -5,3 +5,4 @@
-
- obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
- obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
-+obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o
---- /dev/null
-+++ b/drivers/nvmem/layouts/u-boot-env.c
-@@ -0,0 +1,211 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/*
-+ * Copyright (C) 2022 - 2023 Rafał Miłecki <rafal@milecki.pl>
-+ */
-+
-+#include <linux/crc32.h>
-+#include <linux/etherdevice.h>
-+#include <linux/export.h>
-+#include <linux/if_ether.h>
-+#include <linux/nvmem-consumer.h>
-+#include <linux/nvmem-provider.h>
-+#include <linux/of.h>
-+#include <linux/slab.h>
-+
-+#include "u-boot-env.h"
-+
-+struct u_boot_env_image_single {
-+ __le32 crc32;
-+ uint8_t data[];
-+} __packed;
-+
-+struct u_boot_env_image_redundant {
-+ __le32 crc32;
-+ u8 mark;
-+ uint8_t data[];
-+} __packed;
-+
-+struct u_boot_env_image_broadcom {
-+ __le32 magic;
-+ __le32 len;
-+ __le32 crc32;
-+ DECLARE_FLEX_ARRAY(uint8_t, data);
-+} __packed;
-+
-+static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
-+ unsigned int offset, void *buf, size_t bytes)
-+{
-+ u8 mac[ETH_ALEN];
-+
-+ if (bytes != 3 * ETH_ALEN - 1)
-+ return -EINVAL;
-+
-+ if (!mac_pton(buf, mac))
-+ return -EINVAL;
-+
-+ if (index)
-+ eth_addr_add(mac, index);
-+
-+ ether_addr_copy(buf, mac);
-+
-+ return 0;
-+}
-+
-+static int u_boot_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf,
-+ size_t data_offset, size_t data_len)
-+{
-+ char *data = buf + data_offset;
-+ char *var, *value, *eq;
-+
-+ for (var = data;
-+ var < data + data_len && *var;
-+ var = value + strlen(value) + 1) {
-+ struct nvmem_cell_info info = {};
-+
-+ eq = strchr(var, '=');
-+ if (!eq)
-+ break;
-+ *eq = '\0';
-+ value = eq + 1;
-+
-+ info.name = devm_kstrdup(dev, var, GFP_KERNEL);
-+ if (!info.name)
-+ return -ENOMEM;
-+ info.offset = data_offset + value - data;
-+ info.bytes = strlen(value);
-+ info.np = of_get_child_by_name(dev->of_node, info.name);
-+ if (!strcmp(var, "ethaddr")) {
-+ info.raw_len = strlen(value);
-+ info.bytes = ETH_ALEN;
-+ info.read_post_process = u_boot_env_read_post_process_ethaddr;
-+ }
-+
-+ nvmem_add_one_cell(nvmem, &info);
-+ }
-+
-+ return 0;
-+}
-+
-+int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem,
-+ enum u_boot_env_format format)
-+{
-+ size_t crc32_data_offset;
-+ size_t crc32_data_len;
-+ size_t crc32_offset;
-+ __le32 *crc32_addr;
-+ size_t data_offset;
-+ size_t data_len;
-+ size_t dev_size;
-+ uint32_t crc32;
-+ uint32_t calc;
-+ uint8_t *buf;
-+ int bytes;
-+ int err;
-+
-+ dev_size = nvmem_dev_size(nvmem);
-+
-+ buf = kzalloc(dev_size, GFP_KERNEL);
-+ if (!buf) {
-+ err = -ENOMEM;
-+ goto err_out;
-+ }
-+
-+ bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
-+ if (bytes < 0) {
-+ err = bytes;
-+ goto err_kfree;
-+ } else if (bytes != dev_size) {
-+ err = -EIO;
-+ goto err_kfree;
-+ }
-+
-+ switch (format) {
-+ case U_BOOT_FORMAT_SINGLE:
-+ crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
-+ crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
-+ data_offset = offsetof(struct u_boot_env_image_single, data);
-+ break;
-+ case U_BOOT_FORMAT_REDUNDANT:
-+ crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
-+ crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
-+ data_offset = offsetof(struct u_boot_env_image_redundant, data);
-+ break;
-+ case U_BOOT_FORMAT_BROADCOM:
-+ crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
-+ crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
-+ data_offset = offsetof(struct u_boot_env_image_broadcom, data);
-+ break;
-+ }
-+
-+ if (dev_size < data_offset) {
-+ dev_err(dev, "Device too small for u-boot-env\n");
-+ err = -EIO;
-+ goto err_kfree;
-+ }
-+
-+ crc32_addr = (__le32 *)(buf + crc32_offset);
-+ crc32 = le32_to_cpu(*crc32_addr);
-+ crc32_data_len = dev_size - crc32_data_offset;
-+ data_len = dev_size - data_offset;
-+
-+ calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
-+ if (calc != crc32) {
-+ dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
-+ err = -EINVAL;
-+ goto err_kfree;
-+ }
-+
-+ buf[dev_size - 1] = '\0';
-+ err = u_boot_env_parse_cells(dev, nvmem, buf, data_offset, data_len);
-+
-+err_kfree:
-+ kfree(buf);
-+err_out:
-+ return err;
-+}
-+EXPORT_SYMBOL_GPL(u_boot_env_parse);
-+
-+static int u_boot_env_add_cells(struct nvmem_layout *layout)
-+{
-+ struct device *dev = &layout->dev;
-+ enum u_boot_env_format format;
-+
-+ format = (uintptr_t)device_get_match_data(dev);
-+
-+ return u_boot_env_parse(dev, layout->nvmem, format);
-+}
-+
-+static int u_boot_env_probe(struct nvmem_layout *layout)
-+{
-+ layout->add_cells = u_boot_env_add_cells;
-+
-+ return nvmem_layout_register(layout);
-+}
-+
-+static void u_boot_env_remove(struct nvmem_layout *layout)
-+{
-+ nvmem_layout_unregister(layout);
-+}
-+
-+static const struct of_device_id u_boot_env_of_match_table[] = {
-+ { .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, },
-+ { .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
-+ { .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
-+ { .compatible = "brcm,env", .data = (void *)U_BOOT_FORMAT_BROADCOM, },
-+ {},
-+};
-+
-+static struct nvmem_layout_driver u_boot_env_layout = {
-+ .driver = {
-+ .name = "u-boot-env-layout",
-+ .of_match_table = u_boot_env_of_match_table,
-+ },
-+ .probe = u_boot_env_probe,
-+ .remove = u_boot_env_remove,
-+};
-+module_nvmem_layout_driver(u_boot_env_layout);
-+
-+MODULE_AUTHOR("Rafał Miłecki");
-+MODULE_LICENSE("GPL");
-+MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table);
-+MODULE_DESCRIPTION("NVMEM layout driver for U-Boot environment variables");
---- /dev/null
-+++ b/drivers/nvmem/layouts/u-boot-env.h
-@@ -0,0 +1,15 @@
-+/* SPDX-License-Identifier: GPL-2.0-only */
-+
-+#ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H
-+#define _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H
-+
-+enum u_boot_env_format {
-+ U_BOOT_FORMAT_SINGLE,
-+ U_BOOT_FORMAT_REDUNDANT,
-+ U_BOOT_FORMAT_BROADCOM,
-+};
-+
-+int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem,
-+ enum u_boot_env_format format);
-+
-+#endif /* ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H */
---- a/drivers/nvmem/u-boot-env.c
-+++ b/drivers/nvmem/u-boot-env.c
-@@ -3,23 +3,15 @@
- * Copyright (C) 2022 Rafał Miłecki <rafal@milecki.pl>
- */
-
--#include <linux/crc32.h>
--#include <linux/etherdevice.h>
--#include <linux/if_ether.h>
- #include <linux/mod_devicetable.h>
- #include <linux/module.h>
- #include <linux/mtd/mtd.h>
--#include <linux/nvmem-consumer.h>
- #include <linux/nvmem-provider.h>
- #include <linux/of.h>
- #include <linux/platform_device.h>
- #include <linux/slab.h>
-
--enum u_boot_env_format {
-- U_BOOT_FORMAT_SINGLE,
-- U_BOOT_FORMAT_REDUNDANT,
-- U_BOOT_FORMAT_BROADCOM,
--};
-+#include "layouts/u-boot-env.h"
-
- struct u_boot_env {
- struct device *dev;
-@@ -29,24 +21,6 @@ struct u_boot_env {
- struct mtd_info *mtd;
- };
-
--struct u_boot_env_image_single {
-- __le32 crc32;
-- uint8_t data[];
--} __packed;
--
--struct u_boot_env_image_redundant {
-- __le32 crc32;
-- u8 mark;
-- uint8_t data[];
--} __packed;
--
--struct u_boot_env_image_broadcom {
-- __le32 magic;
-- __le32 len;
-- __le32 crc32;
-- DECLARE_FLEX_ARRAY(uint8_t, data);
--} __packed;
--
- static int u_boot_env_read(void *context, unsigned int offset, void *val,
- size_t bytes)
- {
-@@ -69,141 +43,6 @@ static int u_boot_env_read(void *context
- return 0;
- }
-
--static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
-- unsigned int offset, void *buf, size_t bytes)
--{
-- u8 mac[ETH_ALEN];
--
-- if (bytes != 3 * ETH_ALEN - 1)
-- return -EINVAL;
--
-- if (!mac_pton(buf, mac))
-- return -EINVAL;
--
-- if (index)
-- eth_addr_add(mac, index);
--
-- ether_addr_copy(buf, mac);
--
-- return 0;
--}
--
--static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf,
-- size_t data_offset, size_t data_len)
--{
-- struct nvmem_device *nvmem = priv->nvmem;
-- struct device *dev = priv->dev;
-- char *data = buf + data_offset;
-- char *var, *value, *eq;
--
-- for (var = data;
-- var < data + data_len && *var;
-- var = value + strlen(value) + 1) {
-- struct nvmem_cell_info info = {};
--
-- eq = strchr(var, '=');
-- if (!eq)
-- break;
-- *eq = '\0';
-- value = eq + 1;
--
-- info.name = devm_kstrdup(dev, var, GFP_KERNEL);
-- if (!info.name)
-- return -ENOMEM;
-- info.offset = data_offset + value - data;
-- info.bytes = strlen(value);
-- info.np = of_get_child_by_name(dev->of_node, info.name);
-- if (!strcmp(var, "ethaddr")) {
-- info.raw_len = strlen(value);
-- info.bytes = ETH_ALEN;
-- info.read_post_process = u_boot_env_read_post_process_ethaddr;
-- }
--
-- nvmem_add_one_cell(nvmem, &info);
-- }
--
-- return 0;
--}
--
--static int u_boot_env_parse(struct u_boot_env *priv)
--{
-- struct nvmem_device *nvmem = priv->nvmem;
-- struct device *dev = priv->dev;
-- size_t crc32_data_offset;
-- size_t crc32_data_len;
-- size_t crc32_offset;
-- __le32 *crc32_addr;
-- size_t data_offset;
-- size_t data_len;
-- size_t dev_size;
-- uint32_t crc32;
-- uint32_t calc;
-- uint8_t *buf;
-- int bytes;
-- int err;
--
-- dev_size = nvmem_dev_size(nvmem);
--
-- buf = kzalloc(dev_size, GFP_KERNEL);
-- if (!buf) {
-- err = -ENOMEM;
-- goto err_out;
-- }
--
-- bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
-- if (bytes < 0) {
-- err = bytes;
-- goto err_kfree;
-- } else if (bytes != dev_size) {
-- err = -EIO;
-- goto err_kfree;
-- }
--
-- switch (priv->format) {
-- case U_BOOT_FORMAT_SINGLE:
-- crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
-- crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
-- data_offset = offsetof(struct u_boot_env_image_single, data);
-- break;
-- case U_BOOT_FORMAT_REDUNDANT:
-- crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
-- crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
-- data_offset = offsetof(struct u_boot_env_image_redundant, data);
-- break;
-- case U_BOOT_FORMAT_BROADCOM:
-- crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
-- crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
-- data_offset = offsetof(struct u_boot_env_image_broadcom, data);
-- break;
-- }
--
-- if (dev_size < data_offset) {
-- dev_err(dev, "Device too small for u-boot-env\n");
-- err = -EIO;
-- goto err_kfree;
-- }
--
-- crc32_addr = (__le32 *)(buf + crc32_offset);
-- crc32 = le32_to_cpu(*crc32_addr);
-- crc32_data_len = dev_size - crc32_data_offset;
-- data_len = dev_size - data_offset;
--
-- calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
-- if (calc != crc32) {
-- dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
-- err = -EINVAL;
-- goto err_kfree;
-- }
--
-- buf[dev_size - 1] = '\0';
-- err = u_boot_env_add_cells(priv, buf, data_offset, data_len);
--
--err_kfree:
-- kfree(buf);
--err_out:
-- return err;
--}
--
- static int u_boot_env_probe(struct platform_device *pdev)
- {
- struct nvmem_config config = {
-@@ -235,7 +74,7 @@ static int u_boot_env_probe(struct platf
- if (IS_ERR(priv->nvmem))
- return PTR_ERR(priv->nvmem);
-
-- return u_boot_env_parse(priv);
-+ return u_boot_env_parse(dev, priv->nvmem, priv->format);
- }
-
- static const struct of_device_id u_boot_env_of_match_table[] = {
+++ /dev/null
-From edd25a77e69b7c546c28077e5dffe72c54c0afe8 Mon Sep 17 00:00:00 2001
-From: Linus Walleij <linus.walleij@linaro.org>
-Date: Thu, 21 Sep 2023 22:18:12 +0200
-Subject: [PATCH 2/4] rtc: rtc7301: Support byte-addressed IO
-
-The old RTC7301 driver in OpenWrt used byte access, but the
-current mainline Linux driver uses 32bit word access.
-
-Make this configurable using device properties using the
-standard property "reg-io-width" in e.g. device tree.
-
-This is needed for the USRobotics USR8200 which has the
-chip connected using byte accesses.
-
-Debugging and testing by Howard Harte.
-
-Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
----
- drivers/rtc/rtc-r7301.c | 35 +++++++++++++++++++++++++++++++++--
- 1 file changed, 33 insertions(+), 2 deletions(-)
-
---- a/drivers/rtc/rtc-r7301.c
-+++ b/drivers/rtc/rtc-r7301.c
-@@ -14,6 +14,7 @@
- #include <linux/module.h>
- #include <linux/mod_devicetable.h>
- #include <linux/delay.h>
-+#include <linux/property.h>
- #include <linux/regmap.h>
- #include <linux/platform_device.h>
- #include <linux/rtc.h>
-@@ -55,12 +56,23 @@ struct rtc7301_priv {
- u8 bank;
- };
-
--static const struct regmap_config rtc7301_regmap_config = {
-+/*
-+ * When the device is memory-mapped, some platforms pack the registers into
-+ * 32-bit access using the lower 8 bits at each 4-byte stride, while others
-+ * expose them as simply consecutive bytes.
-+ */
-+static const struct regmap_config rtc7301_regmap_32_config = {
- .reg_bits = 32,
- .val_bits = 8,
- .reg_stride = 4,
- };
-
-+static const struct regmap_config rtc7301_regmap_8_config = {
-+ .reg_bits = 8,
-+ .val_bits = 8,
-+ .reg_stride = 1,
-+};
-+
- static u8 rtc7301_read(struct rtc7301_priv *priv, unsigned int reg)
- {
- int reg_stride = regmap_get_reg_stride(priv->regmap);
-@@ -356,7 +368,9 @@ static int __init rtc7301_rtc_probe(stru
- void __iomem *regs;
- struct rtc7301_priv *priv;
- struct rtc_device *rtc;
-+ static const struct regmap_config *mapconf;
- int ret;
-+ u32 val;
-
- priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
-@@ -366,8 +380,25 @@ static int __init rtc7301_rtc_probe(stru
- if (IS_ERR(regs))
- return PTR_ERR(regs);
-
-+ ret = device_property_read_u32(&dev->dev, "reg-io-width", &val);
-+ if (ret)
-+ /* Default to 32bit accesses */
-+ val = 4;
-+
-+ switch (val) {
-+ case 1:
-+ mapconf = &rtc7301_regmap_8_config;
-+ break;
-+ case 4:
-+ mapconf = &rtc7301_regmap_32_config;
-+ break;
-+ default:
-+ dev_err(&dev->dev, "invalid reg-io-width %d\n", val);
-+ return -EINVAL;
-+ }
-+
- priv->regmap = devm_regmap_init_mmio(&dev->dev, regs,
-- &rtc7301_regmap_config);
-+ mapconf);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
-
+++ /dev/null
-From 49e5663b505070424e18099841943f34342aa405 Mon Sep 17 00:00:00 2001
-From: Linus Walleij <linus.walleij@linaro.org>
-Date: Sun, 24 Sep 2023 01:09:01 +0200
-Subject: [PATCH] net: phy: amd: Support the Altima AMI101L
-
-The Altima AC101L is obviously compatible with the AMD PHY,
-as seen by reading the datasheet.
-
-Datasheet: https://docs.broadcom.com/doc/AC101L-DS05-405-RDS.pdf
-
-Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
----
- drivers/net/phy/Kconfig | 4 ++--
- drivers/net/phy/amd.c | 33 +++++++++++++++++++++++----------
- 2 files changed, 25 insertions(+), 12 deletions(-)
-
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -74,9 +74,9 @@ config AIR_EN8811H_PHY
- Currently supports the Airoha EN8811H PHY.
-
- config AMD_PHY
-- tristate "AMD PHYs"
-+ tristate "AMD and Altima PHYs"
- help
-- Currently supports the am79c874
-+ Currently supports the AMD am79c874 and Altima AC101L.
-
- config MESON_GXL_PHY
- tristate "Amlogic Meson GXL Internal PHY"
---- a/drivers/net/phy/amd.c
-+++ b/drivers/net/phy/amd.c
-@@ -13,6 +13,7 @@
- #include <linux/mii.h>
- #include <linux/phy.h>
-
-+#define PHY_ID_AC101L 0x00225520
- #define PHY_ID_AM79C874 0x0022561b
-
- #define MII_AM79C_IR 17 /* Interrupt Status/Control Register */
-@@ -87,19 +88,31 @@ static irqreturn_t am79c_handle_interrup
- return IRQ_HANDLED;
- }
-
--static struct phy_driver am79c_driver[] = { {
-- .phy_id = PHY_ID_AM79C874,
-- .name = "AM79C874",
-- .phy_id_mask = 0xfffffff0,
-- /* PHY_BASIC_FEATURES */
-- .config_init = am79c_config_init,
-- .config_intr = am79c_config_intr,
-- .handle_interrupt = am79c_handle_interrupt,
--} };
-+static struct phy_driver am79c_drivers[] = {
-+ {
-+ .phy_id = PHY_ID_AM79C874,
-+ .name = "AM79C874",
-+ .phy_id_mask = 0xfffffff0,
-+ /* PHY_BASIC_FEATURES */
-+ .config_init = am79c_config_init,
-+ .config_intr = am79c_config_intr,
-+ .handle_interrupt = am79c_handle_interrupt,
-+ },
-+ {
-+ .phy_id = PHY_ID_AC101L,
-+ .name = "AC101L",
-+ .phy_id_mask = 0xfffffff0,
-+ /* PHY_BASIC_FEATURES */
-+ .config_init = am79c_config_init,
-+ .config_intr = am79c_config_intr,
-+ .handle_interrupt = am79c_handle_interrupt,
-+ },
-+};
-
--module_phy_driver(am79c_driver);
-+module_phy_driver(am79c_drivers);
-
- static struct mdio_device_id __maybe_unused amd_tbl[] = {
-+ { PHY_ID_AC101L, 0xfffffff0 },
- { PHY_ID_AM79C874, 0xfffffff0 },
- { }
- };
+++ /dev/null
-From a067943129b4ec6b835e02cfd5fbef01093c1471 Mon Sep 17 00:00:00 2001
-From: Ondrej Jirman <megi@xff.cz>
-Date: Sun, 8 Oct 2023 16:40:13 +0200
-Subject: [PATCH] leds: core: Add more colors from DT bindings to led_colors
-
-The colors are already part of DT bindings. Make sure the kernel is
-able to convert them to strings.
-
-Signed-off-by: Ondrej Jirman <megi@xff.cz>
-Link: https://lore.kernel.org/r/20231008144014.1180334-1-megi@xff.cz
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/led-core.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/drivers/leds/led-core.c
-+++ b/drivers/leds/led-core.c
-@@ -36,6 +36,11 @@ const char * const led_colors[LED_COLOR_
- [LED_COLOR_ID_IR] = "ir",
- [LED_COLOR_ID_MULTI] = "multicolor",
- [LED_COLOR_ID_RGB] = "rgb",
-+ [LED_COLOR_ID_PURPLE] = "purple",
-+ [LED_COLOR_ID_ORANGE] = "orange",
-+ [LED_COLOR_ID_PINK] = "pink",
-+ [LED_COLOR_ID_CYAN] = "cyan",
-+ [LED_COLOR_ID_LIME] = "lime",
- };
- EXPORT_SYMBOL_GPL(led_colors);
-
+++ /dev/null
-From bc8e1da69a68d9871773b657d18400a7941cbdef Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 28 Nov 2023 04:00:10 +0000
-Subject: [PATCH] leds: trigger: netdev: Extend speeds up to 10G
-
-Add 2.5G, 5G and 10G as available speeds to the netdev LED trigger.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/99e7d3304c6bba7f4863a4a80764a869855f2085.1701143925.git.daniel@makrotopia.org
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/trigger/ledtrig-netdev.c | 32 ++++++++++++++++++++++++++-
- include/linux/leds.h | 3 +++
- 2 files changed, 34 insertions(+), 1 deletion(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -99,6 +99,18 @@ static void set_baseline_state(struct le
- trigger_data->link_speed == SPEED_1000)
- blink_on = true;
-
-+ if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) &&
-+ trigger_data->link_speed == SPEED_2500)
-+ blink_on = true;
-+
-+ if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) &&
-+ trigger_data->link_speed == SPEED_5000)
-+ blink_on = true;
-+
-+ if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) &&
-+ trigger_data->link_speed == SPEED_10000)
-+ blink_on = true;
-+
- if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) &&
- trigger_data->duplex == DUPLEX_HALF)
- blink_on = true;
-@@ -289,6 +301,9 @@ static ssize_t netdev_led_attr_show(stru
- case TRIGGER_NETDEV_LINK_10:
- case TRIGGER_NETDEV_LINK_100:
- case TRIGGER_NETDEV_LINK_1000:
-+ case TRIGGER_NETDEV_LINK_2500:
-+ case TRIGGER_NETDEV_LINK_5000:
-+ case TRIGGER_NETDEV_LINK_10000:
- case TRIGGER_NETDEV_HALF_DUPLEX:
- case TRIGGER_NETDEV_FULL_DUPLEX:
- case TRIGGER_NETDEV_TX:
-@@ -319,6 +334,9 @@ static ssize_t netdev_led_attr_store(str
- case TRIGGER_NETDEV_LINK_10:
- case TRIGGER_NETDEV_LINK_100:
- case TRIGGER_NETDEV_LINK_1000:
-+ case TRIGGER_NETDEV_LINK_2500:
-+ case TRIGGER_NETDEV_LINK_5000:
-+ case TRIGGER_NETDEV_LINK_10000:
- case TRIGGER_NETDEV_HALF_DUPLEX:
- case TRIGGER_NETDEV_FULL_DUPLEX:
- case TRIGGER_NETDEV_TX:
-@@ -337,7 +355,10 @@ static ssize_t netdev_led_attr_store(str
- if (test_bit(TRIGGER_NETDEV_LINK, &mode) &&
- (test_bit(TRIGGER_NETDEV_LINK_10, &mode) ||
- test_bit(TRIGGER_NETDEV_LINK_100, &mode) ||
-- test_bit(TRIGGER_NETDEV_LINK_1000, &mode)))
-+ test_bit(TRIGGER_NETDEV_LINK_1000, &mode) ||
-+ test_bit(TRIGGER_NETDEV_LINK_2500, &mode) ||
-+ test_bit(TRIGGER_NETDEV_LINK_5000, &mode) ||
-+ test_bit(TRIGGER_NETDEV_LINK_10000, &mode)))
- return -EINVAL;
-
- cancel_delayed_work_sync(&trigger_data->work);
-@@ -367,6 +388,9 @@ DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETD
- DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10);
- DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100);
- DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000);
-+DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500);
-+DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000);
-+DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000);
- DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX);
- DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX);
- DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
-@@ -425,6 +449,9 @@ static struct attribute *netdev_trig_att
- &dev_attr_link_10.attr,
- &dev_attr_link_100.attr,
- &dev_attr_link_1000.attr,
-+ &dev_attr_link_2500.attr,
-+ &dev_attr_link_5000.attr,
-+ &dev_attr_link_10000.attr,
- &dev_attr_full_duplex.attr,
- &dev_attr_half_duplex.attr,
- &dev_attr_rx.attr,
-@@ -522,6 +549,9 @@ static void netdev_trig_work(struct work
- test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) ||
- test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) ||
- test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) ||
-+ test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) ||
-+ test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) ||
-+ test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) ||
- test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) ||
- test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode);
- interval = jiffies_to_msecs(
---- a/include/linux/leds.h
-+++ b/include/linux/leds.h
-@@ -586,6 +586,9 @@ enum led_trigger_netdev_modes {
- TRIGGER_NETDEV_LINK_10,
- TRIGGER_NETDEV_LINK_100,
- TRIGGER_NETDEV_LINK_1000,
-+ TRIGGER_NETDEV_LINK_2500,
-+ TRIGGER_NETDEV_LINK_5000,
-+ TRIGGER_NETDEV_LINK_10000,
- TRIGGER_NETDEV_HALF_DUPLEX,
- TRIGGER_NETDEV_FULL_DUPLEX,
- TRIGGER_NETDEV_TX,
+++ /dev/null
-From 7ae215ee7bb855f13c80565470fc7f67db4ba82f Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 25 Jan 2024 21:36:59 +0100
-Subject: [PATCH 3/5] net: phy: add support for PHY LEDs polarity modes
-
-Add support for PHY LEDs polarity modes. Some PHY require LED to be set
-to active low to be turned ON. Adds support for this by declaring
-active-low property in DT.
-
-PHY driver needs to declare .led_polarity_set() to configure LED
-polarity modes. Function will pass the index with the LED index and a
-bitmap with all the required modes to set.
-
-Current supported modes are:
-- active-low with the flag PHY_LED_ACTIVE_LOW. LED is set to active-low
- to turn it ON.
-- inactive-high-impedance with the flag PHY_LED_INACTIVE_HIGH_IMPEDANCE.
- LED is set to high impedance to turn it OFF.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20240125203702.4552-4-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phy_device.c | 16 ++++++++++++++++
- include/linux/phy.h | 22 ++++++++++++++++++++++
- 2 files changed, 38 insertions(+)
-
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -3202,6 +3202,7 @@ static int of_phy_led(struct phy_device
- struct device *dev = &phydev->mdio.dev;
- struct led_init_data init_data = {};
- struct led_classdev *cdev;
-+ unsigned long modes = 0;
- struct phy_led *phyled;
- u32 index;
- int err;
-@@ -3219,6 +3220,21 @@ static int of_phy_led(struct phy_device
- if (index > U8_MAX)
- return -EINVAL;
-
-+ if (of_property_read_bool(led, "active-low"))
-+ set_bit(PHY_LED_ACTIVE_LOW, &modes);
-+ if (of_property_read_bool(led, "inactive-high-impedance"))
-+ set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes);
-+
-+ if (modes) {
-+ /* Return error if asked to set polarity modes but not supported */
-+ if (!phydev->drv->led_polarity_set)
-+ return -EINVAL;
-+
-+ err = phydev->drv->led_polarity_set(phydev, index, modes);
-+ if (err)
-+ return err;
-+ }
-+
- phyled->index = index;
- if (phydev->drv->led_brightness_set)
- cdev->brightness_set_blocking = phy_led_set_brightness;
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -870,6 +870,15 @@ struct phy_led {
-
- #define to_phy_led(d) container_of(d, struct phy_led, led_cdev)
-
-+/* Modes for PHY LED configuration */
-+enum phy_led_modes {
-+ PHY_LED_ACTIVE_LOW = 0,
-+ PHY_LED_INACTIVE_HIGH_IMPEDANCE = 1,
-+
-+ /* keep it last */
-+ __PHY_LED_MODES_NUM,
-+};
-+
- /**
- * struct phy_driver - Driver structure for a particular PHY type
- *
-@@ -1146,6 +1155,19 @@ struct phy_driver {
- int (*led_hw_control_get)(struct phy_device *dev, u8 index,
- unsigned long *rules);
-
-+ /**
-+ * @led_polarity_set: Set the LED polarity modes
-+ * @dev: PHY device which has the LED
-+ * @index: Which LED of the PHY device
-+ * @modes: bitmap of LED polarity modes
-+ *
-+ * Configure LED with all the required polarity modes in @modes
-+ * to make it correctly turn ON or OFF.
-+ *
-+ * Returns 0, or an error code.
-+ */
-+ int (*led_polarity_set)(struct phy_device *dev, int index,
-+ unsigned long modes);
- };
- #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
- struct phy_driver, mdiodrv)
+++ /dev/null
-From cffac22c9215f1883d3848c788f9b03656dced27 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Sun, 11 Feb 2024 18:39:19 +0100
-Subject: [PATCH] net: phy: aquantia: clear PMD Global Transmit Disable bit
- during init
-
-PMD Global Transmit Disable bit should be cleared for normal operation.
-This should be HW default, however I found that on Asus RT-AX89X that uses
-AQR113C PHY and firmware 5.4 this bit is set by default.
-
-With this bit set the AQR cannot achieve a link with its link-partner and
-it took me multiple hours of digging through the vendor GPL source to find
-this out, so lets always clear this bit during .config_init() to avoid a
-situation like this in the future.
-
-aqr107_wait_processor_intensive_op() is moved up because datasheet notes
-that any changes to this bit are processor intensive.
-
-Signed-off-by: Robert Marko <robimarko@gmail.com>
----
- drivers/net/phy/aquantia/aquantia_main.c | 57 ++++++++++++++----------
- 1 file changed, 33 insertions(+), 24 deletions(-)
-
---- a/drivers/net/phy/aquantia/aquantia_main.c
-+++ b/drivers/net/phy/aquantia/aquantia_main.c
-@@ -473,6 +473,30 @@ static void aqr107_chip_info(struct phy_
- fw_major, fw_minor, build_id, prov_id);
- }
-
-+static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
-+{
-+ int val, err;
-+
-+ /* The datasheet notes to wait at least 1ms after issuing a
-+ * processor intensive operation before checking.
-+ * We cannot use the 'sleep_before_read' parameter of read_poll_timeout
-+ * because that just determines the maximum time slept, not the minimum.
-+ */
-+ usleep_range(1000, 5000);
-+
-+ err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
-+ VEND1_GLOBAL_GEN_STAT2, val,
-+ !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG),
-+ AQR107_OP_IN_PROG_SLEEP,
-+ AQR107_OP_IN_PROG_TIMEOUT, false);
-+ if (err) {
-+ phydev_err(phydev, "timeout: processor-intensive MDIO operation\n");
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
- static int aqr107_config_init(struct phy_device *phydev)
- {
- struct aqr107_priv *priv = phydev->priv;
-@@ -498,6 +522,15 @@ static int aqr107_config_init(struct phy
- if (!ret)
- aqr107_chip_info(phydev);
-
-+ ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS,
-+ MDIO_PMD_TXDIS_GLOBAL);
-+ if (ret)
-+ return ret;
-+
-+ ret = aqr107_wait_processor_intensive_op(phydev);
-+ if (ret)
-+ return ret;
-+
- ret = aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
- if (ret)
- return ret;
-@@ -580,30 +613,6 @@ static void aqr107_link_change_notify(st
- phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n");
- }
-
--static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
--{
-- int val, err;
--
-- /* The datasheet notes to wait at least 1ms after issuing a
-- * processor intensive operation before checking.
-- * We cannot use the 'sleep_before_read' parameter of read_poll_timeout
-- * because that just determines the maximum time slept, not the minimum.
-- */
-- usleep_range(1000, 5000);
--
-- err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
-- VEND1_GLOBAL_GEN_STAT2, val,
-- !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG),
-- AQR107_OP_IN_PROG_SLEEP,
-- AQR107_OP_IN_PROG_TIMEOUT, false);
-- if (err) {
-- phydev_err(phydev, "timeout: processor-intensive MDIO operation\n");
-- return err;
-- }
--
-- return 0;
--}
--
- static int aqr107_get_rate_matching(struct phy_device *phydev,
- phy_interface_t iface)
- {
+++ /dev/null
-From patchwork Tue Sep 17 13:49:40 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: 13806176
-X-Patchwork-Delegate: kuba@kernel.org
-Date: Tue, 17 Sep 2024 14:49:40 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: Andrew Lunn <andrew@lunn.ch>, Heiner Kallweit <hkallweit1@gmail.com>,
- Russell King <linux@armlinux.org.uk>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
- Daniel Golle <daniel@makrotopia.org>,
- Christian Marangi <ansuelsmth@gmail.com>,
- Bartosz Golaszewski <bartosz.golaszewski@linaro.org>,
- Robert Marko <robimarko@gmail.com>,
- Russell King <rmk+kernel@armlinux.org.uk>, netdev@vger.kernel.org,
- linux-kernel@vger.kernel.org
-Subject: [PATCH net 1/2] net: phy: aquantia: fix setting active_low bit
-Message-ID:
- <ab963584b0a7e3b4dac39472a4b82ca264d79630.1726580902.git.daniel@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
-
-phy_modify_mmd was used wrongly in aqr_phy_led_active_low_set() resulting
-in a no-op instead of setting the VEND1_GLOBAL_LED_DRIVE_VDD bit.
-Correctly set VEND1_GLOBAL_LED_DRIVE_VDD bit.
-
-Fixes: 61578f679378 ("net: phy: aquantia: add support for PHY LEDs")
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
----
- drivers/net/phy/aquantia/aquantia_leds.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/net/phy/aquantia/aquantia_leds.c
-+++ b/drivers/net/phy/aquantia/aquantia_leds.c
-@@ -120,7 +120,8 @@ int aqr_phy_led_hw_control_set(struct ph
- int aqr_phy_led_active_low_set(struct phy_device *phydev, int index, bool enable)
- {
- return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_DRIVE(index),
-- VEND1_GLOBAL_LED_DRIVE_VDD, enable);
-+ VEND1_GLOBAL_LED_DRIVE_VDD,
-+ enable ? VEND1_GLOBAL_LED_DRIVE_VDD : 0);
- }
-
- int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long modes)
+++ /dev/null
-From patchwork Tue Sep 17 13:49:55 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: 13806177
-X-Patchwork-Delegate: kuba@kernel.org
-Date: Tue, 17 Sep 2024 14:49:55 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: Andrew Lunn <andrew@lunn.ch>, Heiner Kallweit <hkallweit1@gmail.com>,
- Russell King <linux@armlinux.org.uk>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
- Daniel Golle <daniel@makrotopia.org>,
- Christian Marangi <ansuelsmth@gmail.com>,
- Bartosz Golaszewski <bartosz.golaszewski@linaro.org>,
- Robert Marko <robimarko@gmail.com>,
- Russell King <rmk+kernel@armlinux.org.uk>, netdev@vger.kernel.org,
- linux-kernel@vger.kernel.org
-Subject: [PATCH net 2/2] net: phy: aquantia: fix applying active_low bit
- after reset
-Message-ID:
- <9b1f0cd91f4cda54c8be56b4fe780480baf4aa0f.1726580902.git.daniel@makrotopia.org>
-References:
- <ab963584b0a7e3b4dac39472a4b82ca264d79630.1726580902.git.daniel@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
-In-Reply-To:
- <ab963584b0a7e3b4dac39472a4b82ca264d79630.1726580902.git.daniel@makrotopia.org>
-X-Patchwork-Delegate: kuba@kernel.org
-
-for_each_set_bit was used wrongly in aqr107_config_init() when iterating
-over LEDs. Drop misleading 'index' variable and call
-aqr_phy_led_active_low_set() for each set bit representing an LED which
-is driven by VDD instead of GND pin.
-
-Fixes: 61578f679378 ("net: phy: aquantia: add support for PHY LEDs")
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
----
- drivers/net/phy/aquantia/aquantia_main.c | 5 ++---
- 1 file changed, 2 insertions(+), 3 deletions(-)
-
---- a/drivers/net/phy/aquantia/aquantia_main.c
-+++ b/drivers/net/phy/aquantia/aquantia_main.c
-@@ -501,7 +501,7 @@ static int aqr107_config_init(struct phy
- {
- struct aqr107_priv *priv = phydev->priv;
- u32 led_active_low;
-- int ret, index = 0;
-+ int ret;
-
- /* Check that the PHY interface type is compatible */
- if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
-@@ -537,10 +537,9 @@ static int aqr107_config_init(struct phy
-
- /* Restore LED polarity state after reset */
- for_each_set_bit(led_active_low, &priv->leds_active_low, AQR_MAX_LEDS) {
-- ret = aqr_phy_led_active_low_set(phydev, index, led_active_low);
-+ ret = aqr_phy_led_active_low_set(phydev, led_active_low, true);
- if (ret)
- return ret;
-- index++;
- }
-
- return 0;
+++ /dev/null
-From 6ab3d50b106c9aea123a80551a6c9deace83b914 Mon Sep 17 00:00:00 2001
-From: Qiang Yu <quic_qianyu@quicinc.com>
-Date: Tue, 7 Nov 2023 16:14:49 +0800
-Subject: [PATCH] bus: mhi: host: Add a separate timeout parameter for waiting
- ready
-
-Some devices(eg. SDX75) take longer than expected (default, 8 seconds) to
-set ready after reboot. Hence add optional ready timeout parameter and pass
-the appropriate timeout value to mhi_poll_reg_field() to wait enough for
-device ready as part of power up sequence.
-
-Signed-off-by: Qiang Yu <quic_qianyu@quicinc.com>
-Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-Link: https://lore.kernel.org/r/1699344890-87076-2-git-send-email-quic_qianyu@quicinc.com
-Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
----
- drivers/bus/mhi/host/init.c | 1 +
- drivers/bus/mhi/host/internal.h | 2 +-
- drivers/bus/mhi/host/main.c | 5 +++--
- drivers/bus/mhi/host/pm.c | 24 +++++++++++++++++-------
- include/linux/mhi.h | 4 ++++
- 5 files changed, 26 insertions(+), 10 deletions(-)
-
---- a/drivers/bus/mhi/host/init.c
-+++ b/drivers/bus/mhi/host/init.c
-@@ -882,6 +882,7 @@ static int parse_config(struct mhi_contr
- if (!mhi_cntrl->timeout_ms)
- mhi_cntrl->timeout_ms = MHI_TIMEOUT_MS;
-
-+ mhi_cntrl->ready_timeout_ms = config->ready_timeout_ms;
- mhi_cntrl->bounce_buf = config->use_bounce_buf;
- mhi_cntrl->buffer_len = config->buf_len;
- if (!mhi_cntrl->buffer_len)
---- a/drivers/bus/mhi/host/internal.h
-+++ b/drivers/bus/mhi/host/internal.h
-@@ -324,7 +324,7 @@ int __must_check mhi_read_reg_field(stru
- u32 *out);
- int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
- void __iomem *base, u32 offset, u32 mask,
-- u32 val, u32 delayus);
-+ u32 val, u32 delayus, u32 timeout_ms);
- void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
- u32 offset, u32 val);
- int __must_check mhi_write_reg_field(struct mhi_controller *mhi_cntrl,
---- a/drivers/bus/mhi/host/main.c
-+++ b/drivers/bus/mhi/host/main.c
-@@ -40,10 +40,11 @@ int __must_check mhi_read_reg_field(stru
-
- int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
- void __iomem *base, u32 offset,
-- u32 mask, u32 val, u32 delayus)
-+ u32 mask, u32 val, u32 delayus,
-+ u32 timeout_ms)
- {
- int ret;
-- u32 out, retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
-+ u32 out, retry = (timeout_ms * 1000) / delayus;
-
- while (retry--) {
- ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, &out);
---- a/drivers/bus/mhi/host/pm.c
-+++ b/drivers/bus/mhi/host/pm.c
-@@ -171,6 +171,7 @@ int mhi_ready_state_transition(struct mh
- enum mhi_pm_state cur_state;
- struct device *dev = &mhi_cntrl->mhi_dev->dev;
- u32 interval_us = 25000; /* poll register field every 25 milliseconds */
-+ u32 timeout_ms;
- int ret, i;
-
- /* Check if device entered error state */
-@@ -181,14 +182,18 @@ int mhi_ready_state_transition(struct mh
-
- /* Wait for RESET to be cleared and READY bit to be set by the device */
- ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
-- MHICTRL_RESET_MASK, 0, interval_us);
-+ MHICTRL_RESET_MASK, 0, interval_us,
-+ mhi_cntrl->timeout_ms);
- if (ret) {
- dev_err(dev, "Device failed to clear MHI Reset\n");
- return ret;
- }
-
-+ timeout_ms = mhi_cntrl->ready_timeout_ms ?
-+ mhi_cntrl->ready_timeout_ms : mhi_cntrl->timeout_ms;
- ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS,
-- MHISTATUS_READY_MASK, 1, interval_us);
-+ MHISTATUS_READY_MASK, 1, interval_us,
-+ timeout_ms);
- if (ret) {
- dev_err(dev, "Device failed to enter MHI Ready\n");
- return ret;
-@@ -487,7 +492,7 @@ static void mhi_pm_disable_transition(st
-
- /* Wait for the reset bit to be cleared by the device */
- ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
-- MHICTRL_RESET_MASK, 0, 25000);
-+ MHICTRL_RESET_MASK, 0, 25000, mhi_cntrl->timeout_ms);
- if (ret)
- dev_err(dev, "Device failed to clear MHI Reset\n");
-
-@@ -500,8 +505,8 @@ static void mhi_pm_disable_transition(st
- if (!MHI_IN_PBL(mhi_get_exec_env(mhi_cntrl))) {
- /* wait for ready to be set */
- ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs,
-- MHISTATUS,
-- MHISTATUS_READY_MASK, 1, 25000);
-+ MHISTATUS, MHISTATUS_READY_MASK,
-+ 1, 25000, mhi_cntrl->timeout_ms);
- if (ret)
- dev_err(dev, "Device failed to enter READY state\n");
- }
-@@ -1125,7 +1130,8 @@ int mhi_async_power_up(struct mhi_contro
- if (state == MHI_STATE_SYS_ERR) {
- mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
- ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
-- MHICTRL_RESET_MASK, 0, interval_us);
-+ MHICTRL_RESET_MASK, 0, interval_us,
-+ mhi_cntrl->timeout_ms);
- if (ret) {
- dev_info(dev, "Failed to reset MHI due to syserr state\n");
- goto error_exit;
-@@ -1216,14 +1222,18 @@ EXPORT_SYMBOL_GPL(mhi_power_down);
- int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
- {
- int ret = mhi_async_power_up(mhi_cntrl);
-+ u32 timeout_ms;
-
- if (ret)
- return ret;
-
-+ /* Some devices need more time to set ready during power up */
-+ timeout_ms = mhi_cntrl->ready_timeout_ms ?
-+ mhi_cntrl->ready_timeout_ms : mhi_cntrl->timeout_ms;
- wait_event_timeout(mhi_cntrl->state_event,
- MHI_IN_MISSION_MODE(mhi_cntrl->ee) ||
- MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
-- msecs_to_jiffies(mhi_cntrl->timeout_ms));
-+ msecs_to_jiffies(timeout_ms));
-
- ret = (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) ? 0 : -ETIMEDOUT;
- if (ret)
---- a/include/linux/mhi.h
-+++ b/include/linux/mhi.h
-@@ -266,6 +266,7 @@ struct mhi_event_config {
- * struct mhi_controller_config - Root MHI controller configuration
- * @max_channels: Maximum number of channels supported
- * @timeout_ms: Timeout value for operations. 0 means use default
-+ * @ready_timeout_ms: Timeout value for waiting device to be ready (optional)
- * @buf_len: Size of automatically allocated buffers. 0 means use default
- * @num_channels: Number of channels defined in @ch_cfg
- * @ch_cfg: Array of defined channels
-@@ -277,6 +278,7 @@ struct mhi_event_config {
- struct mhi_controller_config {
- u32 max_channels;
- u32 timeout_ms;
-+ u32 ready_timeout_ms;
- u32 buf_len;
- u32 num_channels;
- const struct mhi_channel_config *ch_cfg;
-@@ -330,6 +332,7 @@ struct mhi_controller_config {
- * @pm_mutex: Mutex for suspend/resume operation
- * @pm_lock: Lock for protecting MHI power management state
- * @timeout_ms: Timeout in ms for state transitions
-+ * @ready_timeout_ms: Timeout in ms for waiting device to be ready (optional)
- * @pm_state: MHI power management state
- * @db_access: DB access states
- * @ee: MHI device execution environment
-@@ -419,6 +422,7 @@ struct mhi_controller {
- struct mutex pm_mutex;
- rwlock_t pm_lock;
- u32 timeout_ms;
-+ u32 ready_timeout_ms;
- u32 pm_state;
- u32 db_access;
- enum mhi_ee_type ee;
+++ /dev/null
-From 4dc9c850a974ba7db2091ce73bcffe631aafe144 Mon Sep 17 00:00:00 2001
-From: Qiang Yu <quic_qianyu@quicinc.com>
-Date: Tue, 7 Nov 2023 16:14:50 +0800
-Subject: [PATCH 1/2] bus: mhi: host: pci_generic: Add SDX75 based modem
- support
-
-Add generic info for SDX75 based modems. SDX75 takes longer to set ready
-during power up. Hence use separate configuration.
-
-Signed-off-by: Qiang Yu <quic_qianyu@quicinc.com>
-Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-Link: https://lore.kernel.org/r/1699344890-87076-3-git-send-email-quic_qianyu@quicinc.com
-Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
----
- drivers/bus/mhi/host/pci_generic.c | 22 ++++++++++++++++++++++
- 1 file changed, 22 insertions(+)
-
---- a/drivers/bus/mhi/host/pci_generic.c
-+++ b/drivers/bus/mhi/host/pci_generic.c
-@@ -269,6 +269,16 @@ static struct mhi_event_config modem_qco
- MHI_EVENT_CONFIG_HW_DATA(5, 2048, 101)
- };
-
-+static const struct mhi_controller_config modem_qcom_v2_mhiv_config = {
-+ .max_channels = 128,
-+ .timeout_ms = 8000,
-+ .ready_timeout_ms = 50000,
-+ .num_channels = ARRAY_SIZE(modem_qcom_v1_mhi_channels),
-+ .ch_cfg = modem_qcom_v1_mhi_channels,
-+ .num_events = ARRAY_SIZE(modem_qcom_v1_mhi_events),
-+ .event_cfg = modem_qcom_v1_mhi_events,
-+};
-+
- static const struct mhi_controller_config modem_qcom_v1_mhiv_config = {
- .max_channels = 128,
- .timeout_ms = 8000,
-@@ -278,6 +288,16 @@ static const struct mhi_controller_confi
- .event_cfg = modem_qcom_v1_mhi_events,
- };
-
-+static const struct mhi_pci_dev_info mhi_qcom_sdx75_info = {
-+ .name = "qcom-sdx75m",
-+ .fw = "qcom/sdx75m/xbl.elf",
-+ .edl = "qcom/sdx75m/edl.mbn",
-+ .config = &modem_qcom_v2_mhiv_config,
-+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
-+ .dma_data_width = 32,
-+ .sideband_wake = false,
-+};
-+
- static const struct mhi_pci_dev_info mhi_qcom_sdx65_info = {
- .name = "qcom-sdx65m",
- .fw = "qcom/sdx65m/xbl.elf",
-@@ -609,6 +629,8 @@ static const struct pci_device_id mhi_pc
- .driver_data = (kernel_ulong_t) &mhi_telit_fe990a_info },
- { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0308),
- .driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info },
-+ { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0309),
-+ .driver_data = (kernel_ulong_t) &mhi_qcom_sdx75_info },
- { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1001), /* EM120R-GL (sdx24) */
- .driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
- { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1002), /* EM160R-GL (sdx24) */
+++ /dev/null
-From 2f5e59d70566902d7b4e13c6af3f042f5d28b78b Mon Sep 17 00:00:00 2001
-From: Jeff Johnson <quic_jjohnson@quicinc.com>
-Date: Thu, 22 Feb 2024 18:00:23 -0800
-Subject: [PATCH 2/2] bus: mhi: host: pci_generic: constify
- modem_telit_fn980_hw_v1_config
-
-MHI expects the controller configs to be const, and all of the other ones
-in this file already are, so constify modem_telit_fn980_hw_v1_config.
-
-Signed-off-by: Jeff Johnson <quic_jjohnson@quicinc.com>
-Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-Link: https://lore.kernel.org/r/20240222-mhi-const-bus-mhi-host-pci_generic-v1-1-d4c9b0b0a7a5@quicinc.com
-Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
----
- drivers/bus/mhi/host/pci_generic.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/bus/mhi/host/pci_generic.c
-+++ b/drivers/bus/mhi/host/pci_generic.c
-@@ -538,7 +538,7 @@ static struct mhi_event_config mhi_telit
- MHI_EVENT_CONFIG_HW_DATA(2, 2048, 101)
- };
-
--static struct mhi_controller_config modem_telit_fn980_hw_v1_config = {
-+static const struct mhi_controller_config modem_telit_fn980_hw_v1_config = {
- .max_channels = 128,
- .timeout_ms = 20000,
- .num_channels = ARRAY_SIZE(mhi_telit_fn980_hw_v1_channels),
+++ /dev/null
-From 3471bba1b33a8b54cb0be9d30b7aac4fb2766365 Mon Sep 17 00:00:00 2001
-From: Baochen Qiang <quic_bqiang@quicinc.com>
-Date: Tue, 5 Mar 2024 10:13:18 +0800
-Subject: [PATCH] bus: mhi: host: Add mhi_power_down_keep_dev() API to support
- system suspend/hibernation
-
-Currently, ath11k fails to resume from system suspend/hibernation on some
-the x86 host machines with below error message:
-
-```
-ath11k_pci 0000:06:00.0: timeout while waiting for restart complete
-```
-
-This happens because, ath11k powers down the MHI stack during suspend and
-that leads to destruction of the struct device associated with the MHI
-channels. And during resume, ath11k calls calling mhi_sync_power_up() to
-power up the MHI subsystem and that eventually calls the driver framework's
-device_add() API from mhi_create_devices(). But the PM framework blocks the
-struct device creation during device_add() and this leads to probe deferral
-as below:
-
-```
-mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral
-```
-
-The reason for deferring device creation during resume is explained in
-dpm_prepare():
-
- /*
- * It is unsafe if probing of devices will happen during suspend or
- * hibernation and system behavior will be unpredictable in this
- * case. So, let's prohibit device's probing here and defer their
- * probes instead. The normal behavior will be restored in
- * dpm_complete().
- */
-
-Due to the device probe deferral, qcom_mhi_qrtr_probe() API is not getting
-called during resume and thus MHI channels are not prepared. So this blocks
-the QMI messages from being transferred between ath11k and firmware,
-resulting in a firmware initialization failure.
-
-After consulting with Rafael, it was decided to not destroy the struct
-device for the MHI channels during system suspend/hibernation because the
-device is bound to appear again during resume.
-
-So to achieve this, a new API called mhi_power_down_keep_dev() is
-introduced for MHI controllers to keep the struct device when required.
-This API is similar to the existing mhi_power_down() API, except that it
-keeps the struct device associated with MHI channels instead of destroying
-them.
-
-Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30
-
-Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
-Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-Reviewed-by: Jeff Johnson <quic_jjohnson@quicinc.com>
-Link: https://lore.kernel.org/r/20240305021320.3367-2-quic_bqiang@quicinc.com
-[mani: reworded the commit message and subject]
-Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
----
- drivers/bus/mhi/host/init.c | 1 +
- drivers/bus/mhi/host/internal.h | 1 +
- drivers/bus/mhi/host/pm.c | 42 ++++++++++++++++++++++++++++-----
- include/linux/mhi.h | 18 +++++++++++++-
- 4 files changed, 55 insertions(+), 7 deletions(-)
-
---- a/drivers/bus/mhi/host/init.c
-+++ b/drivers/bus/mhi/host/init.c
-@@ -43,6 +43,7 @@ const char * const dev_state_tran_str[DE
- [DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
- [DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
- [DEV_ST_TRANSITION_DISABLE] = "DISABLE",
-+ [DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE] = "DISABLE (DESTROY DEVICE)",
- };
-
- const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
---- a/drivers/bus/mhi/host/internal.h
-+++ b/drivers/bus/mhi/host/internal.h
-@@ -69,6 +69,7 @@ enum dev_st_transition {
- DEV_ST_TRANSITION_FP,
- DEV_ST_TRANSITION_SYS_ERR,
- DEV_ST_TRANSITION_DISABLE,
-+ DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE,
- DEV_ST_TRANSITION_MAX,
- };
-
---- a/drivers/bus/mhi/host/pm.c
-+++ b/drivers/bus/mhi/host/pm.c
-@@ -466,7 +466,8 @@ error_mission_mode:
- }
-
- /* Handle shutdown transitions */
--static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
-+static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
-+ bool destroy_device)
- {
- enum mhi_pm_state cur_state;
- struct mhi_event *mhi_event;
-@@ -528,8 +529,16 @@ skip_mhi_reset:
- dev_dbg(dev, "Waiting for all pending threads to complete\n");
- wake_up_all(&mhi_cntrl->state_event);
-
-- dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
-- device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
-+ /*
-+ * Only destroy the 'struct device' for channels if indicated by the
-+ * 'destroy_device' flag. Because, during system suspend or hibernation
-+ * state, there is no need to destroy the 'struct device' as the endpoint
-+ * device would still be physically attached to the machine.
-+ */
-+ if (destroy_device) {
-+ dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
-+ device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
-+ }
-
- mutex_lock(&mhi_cntrl->pm_mutex);
-
-@@ -820,7 +829,10 @@ void mhi_pm_st_worker(struct work_struct
- mhi_pm_sys_error_transition(mhi_cntrl);
- break;
- case DEV_ST_TRANSITION_DISABLE:
-- mhi_pm_disable_transition(mhi_cntrl);
-+ mhi_pm_disable_transition(mhi_cntrl, false);
-+ break;
-+ case DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE:
-+ mhi_pm_disable_transition(mhi_cntrl, true);
- break;
- default:
- break;
-@@ -1174,7 +1186,8 @@ error_exit:
- }
- EXPORT_SYMBOL_GPL(mhi_async_power_up);
-
--void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
-+static void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
-+ bool destroy_device)
- {
- enum mhi_pm_state cur_state, transition_state;
- struct device *dev = &mhi_cntrl->mhi_dev->dev;
-@@ -1210,15 +1223,32 @@ void mhi_power_down(struct mhi_controlle
- write_unlock_irq(&mhi_cntrl->pm_lock);
- mutex_unlock(&mhi_cntrl->pm_mutex);
-
-- mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_DISABLE);
-+ if (destroy_device)
-+ mhi_queue_state_transition(mhi_cntrl,
-+ DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE);
-+ else
-+ mhi_queue_state_transition(mhi_cntrl,
-+ DEV_ST_TRANSITION_DISABLE);
-
- /* Wait for shutdown to complete */
- flush_work(&mhi_cntrl->st_worker);
-
- disable_irq(mhi_cntrl->irq[0]);
- }
-+
-+void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
-+{
-+ __mhi_power_down(mhi_cntrl, graceful, true);
-+}
- EXPORT_SYMBOL_GPL(mhi_power_down);
-
-+void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl,
-+ bool graceful)
-+{
-+ __mhi_power_down(mhi_cntrl, graceful, false);
-+}
-+EXPORT_SYMBOL_GPL(mhi_power_down_keep_dev);
-+
- int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
- {
- int ret = mhi_async_power_up(mhi_cntrl);
---- a/include/linux/mhi.h
-+++ b/include/linux/mhi.h
-@@ -649,13 +649,29 @@ int mhi_async_power_up(struct mhi_contro
- int mhi_sync_power_up(struct mhi_controller *mhi_cntrl);
-
- /**
-- * mhi_power_down - Start MHI power down sequence
-+ * mhi_power_down - Power down the MHI device and also destroy the
-+ * 'struct device' for the channels associated with it.
-+ * See also mhi_power_down_keep_dev() which is a variant
-+ * of this API that keeps the 'struct device' for channels
-+ * (useful during suspend/hibernation).
- * @mhi_cntrl: MHI controller
- * @graceful: Link is still accessible, so do a graceful shutdown process
- */
- void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
-
- /**
-+ * mhi_power_down_keep_dev - Power down the MHI device but keep the 'struct
-+ * device' for the channels associated with it.
-+ * This is a variant of 'mhi_power_down()' and
-+ * useful in scenarios such as suspend/hibernation
-+ * where destroying of the 'struct device' is not
-+ * needed.
-+ * @mhi_cntrl: MHI controller
-+ * @graceful: Link is still accessible, so do a graceful shutdown process
-+ */
-+void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl, bool graceful);
-+
-+/**
- * mhi_unprepare_after_power_down - Free any allocated memory after power down
- * @mhi_cntrl: MHI controller
- */
+++ /dev/null
-From 0ebdb7210943eb345992bea9892adbd15a206193 Mon Sep 17 00:00:00 2001
-From: André Apitzsch <git@apitzsch.eu>
-Date: Mon, 2 Oct 2023 18:48:28 +0200
-Subject: leds: Add ktd202x driver
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This commit adds support for Kinetic KTD2026/7 RGB/White LED driver.
-
-Signed-off-by: André Apitzsch <git@apitzsch.eu>
-Link: https://lore.kernel.org/r/20231002-ktd202x-v6-2-26be8eefeb88@apitzsch.eu
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/rgb/Kconfig | 13 +
- drivers/leds/rgb/Makefile | 1 +
- drivers/leds/rgb/leds-ktd202x.c | 625 ++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 639 insertions(+)
- create mode 100644 drivers/leds/rgb/leds-ktd202x.c
-
-(limited to 'drivers/leds/rgb')
-
---- a/drivers/leds/rgb/Kconfig
-+++ b/drivers/leds/rgb/Kconfig
-@@ -14,6 +14,19 @@ config LEDS_GROUP_MULTICOLOR
- To compile this driver as a module, choose M here: the module
- will be called leds-group-multicolor.
-
-+config LEDS_KTD202X
-+ tristate "LED support for KTD202x Chips"
-+ depends on I2C
-+ depends on OF
-+ select REGMAP_I2C
-+ help
-+ This option enables support for the Kinetic KTD2026/KTD2027
-+ RGB/White LED driver found in different BQ mobile phones.
-+ It is a 3 or 4 channel LED driver programmed via an I2C interface.
-+
-+ To compile this driver as a module, choose M here: the module
-+ will be called leds-ktd202x.
-+
- config LEDS_PWM_MULTICOLOR
- tristate "PWM driven multi-color LED Support"
- depends on PWM
---- a/drivers/leds/rgb/Makefile
-+++ b/drivers/leds/rgb/Makefile
-@@ -1,6 +1,7 @@
- # SPDX-License-Identifier: GPL-2.0
-
- obj-$(CONFIG_LEDS_GROUP_MULTICOLOR) += leds-group-multicolor.o
-+obj-$(CONFIG_LEDS_KTD202X) += leds-ktd202x.o
- obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o
- obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o
- obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370-rgb.o
---- /dev/null
-+++ b/drivers/leds/rgb/leds-ktd202x.c
-@@ -0,0 +1,625 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+/*
-+ * Kinetic KTD2026/7 RGB/White LED driver with I2C interface
-+ *
-+ * Copyright 2023 André Apitzsch <git@apitzsch.eu>
-+ *
-+ * Datasheet: https://www.kinet-ic.com/uploads/KTD2026-7-04h.pdf
-+ */
-+
-+#include <linux/i2c.h>
-+#include <linux/led-class-multicolor.h>
-+#include <linux/module.h>
-+#include <linux/mutex.h>
-+#include <linux/of.h>
-+#include <linux/of_device.h>
-+#include <linux/regmap.h>
-+#include <linux/regulator/consumer.h>
-+
-+#define KTD2026_NUM_LEDS 3
-+#define KTD2027_NUM_LEDS 4
-+#define KTD202X_MAX_LEDS 4
-+
-+/* Register bank */
-+#define KTD202X_REG_RESET_CONTROL 0x00
-+#define KTD202X_REG_FLASH_PERIOD 0x01
-+#define KTD202X_REG_PWM1_TIMER 0x02
-+#define KTD202X_REG_PWM2_TIMER 0x03
-+#define KTD202X_REG_CHANNEL_CTRL 0x04
-+#define KTD202X_REG_TRISE_FALL 0x05
-+#define KTD202X_REG_LED_IOUT(x) (0x06 + (x))
-+
-+/* Register 0 */
-+#define KTD202X_TIMER_SLOT_CONTROL_TSLOT1 0x00
-+#define KTD202X_TIMER_SLOT_CONTROL_TSLOT2 0x01
-+#define KTD202X_TIMER_SLOT_CONTROL_TSLOT3 0x02
-+#define KTD202X_TIMER_SLOT_CONTROL_TSLOT4 0x03
-+#define KTD202X_RSTR_RESET 0x07
-+
-+#define KTD202X_ENABLE_CTRL_WAKE 0x00 /* SCL High & SDA High */
-+#define KTD202X_ENABLE_CTRL_SLEEP 0x08 /* SCL High & SDA Toggling */
-+
-+#define KTD202X_TRISE_FALL_SCALE_NORMAL 0x00
-+#define KTD202X_TRISE_FALL_SCALE_SLOW_X2 0x20
-+#define KTD202X_TRISE_FALL_SCALE_SLOW_X4 0x40
-+#define KTD202X_TRISE_FALL_SCALE_FAST_X8 0x60
-+
-+/* Register 1 */
-+#define KTD202X_FLASH_PERIOD_256_MS_LOG_RAMP 0x00
-+
-+/* Register 2-3 */
-+#define KTD202X_FLASH_ON_TIME_0_4_PERCENT 0x01
-+
-+/* Register 4 */
-+#define KTD202X_CHANNEL_CTRL_MASK(x) (BIT(2 * (x)) | BIT(2 * (x) + 1))
-+#define KTD202X_CHANNEL_CTRL_OFF 0x00
-+#define KTD202X_CHANNEL_CTRL_ON(x) BIT(2 * (x))
-+#define KTD202X_CHANNEL_CTRL_PWM1(x) BIT(2 * (x) + 1)
-+#define KTD202X_CHANNEL_CTRL_PWM2(x) (BIT(2 * (x)) | BIT(2 * (x) + 1))
-+
-+/* Register 5 */
-+#define KTD202X_RAMP_TIMES_2_MS 0x00
-+
-+/* Register 6-9 */
-+#define KTD202X_LED_CURRENT_10_mA 0x4f
-+
-+#define KTD202X_FLASH_PERIOD_MIN_MS 256
-+#define KTD202X_FLASH_PERIOD_STEP_MS 128
-+#define KTD202X_FLASH_PERIOD_MAX_STEPS 126
-+#define KTD202X_FLASH_ON_MAX 256
-+
-+#define KTD202X_MAX_BRIGHTNESS 192
-+
-+static const struct reg_default ktd202x_reg_defaults[] = {
-+ { KTD202X_REG_RESET_CONTROL, KTD202X_TIMER_SLOT_CONTROL_TSLOT1 |
-+ KTD202X_ENABLE_CTRL_WAKE | KTD202X_TRISE_FALL_SCALE_NORMAL },
-+ { KTD202X_REG_FLASH_PERIOD, KTD202X_FLASH_PERIOD_256_MS_LOG_RAMP },
-+ { KTD202X_REG_PWM1_TIMER, KTD202X_FLASH_ON_TIME_0_4_PERCENT },
-+ { KTD202X_REG_PWM2_TIMER, KTD202X_FLASH_ON_TIME_0_4_PERCENT },
-+ { KTD202X_REG_CHANNEL_CTRL, KTD202X_CHANNEL_CTRL_OFF },
-+ { KTD202X_REG_TRISE_FALL, KTD202X_RAMP_TIMES_2_MS },
-+ { KTD202X_REG_LED_IOUT(0), KTD202X_LED_CURRENT_10_mA },
-+ { KTD202X_REG_LED_IOUT(1), KTD202X_LED_CURRENT_10_mA },
-+ { KTD202X_REG_LED_IOUT(2), KTD202X_LED_CURRENT_10_mA },
-+ { KTD202X_REG_LED_IOUT(3), KTD202X_LED_CURRENT_10_mA },
-+};
-+
-+struct ktd202x_led {
-+ struct ktd202x *chip;
-+ union {
-+ struct led_classdev cdev;
-+ struct led_classdev_mc mcdev;
-+ };
-+ u32 index;
-+};
-+
-+struct ktd202x {
-+ struct mutex mutex;
-+ struct regulator_bulk_data regulators[2];
-+ struct device *dev;
-+ struct regmap *regmap;
-+ bool enabled;
-+ int num_leds;
-+ struct ktd202x_led leds[] __counted_by(num_leds);
-+};
-+
-+static int ktd202x_chip_disable(struct ktd202x *chip)
-+{
-+ int ret;
-+
-+ if (!chip->enabled)
-+ return 0;
-+
-+ regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_ENABLE_CTRL_SLEEP);
-+
-+ ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators);
-+ if (ret) {
-+ dev_err(chip->dev, "Failed to disable regulators: %d\n", ret);
-+ return ret;
-+ }
-+
-+ chip->enabled = false;
-+ return 0;
-+}
-+
-+static int ktd202x_chip_enable(struct ktd202x *chip)
-+{
-+ int ret;
-+
-+ if (chip->enabled)
-+ return 0;
-+
-+ ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators), chip->regulators);
-+ if (ret) {
-+ dev_err(chip->dev, "Failed to enable regulators: %d\n", ret);
-+ return ret;
-+ }
-+ chip->enabled = true;
-+
-+ ret = regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_ENABLE_CTRL_WAKE);
-+
-+ if (ret) {
-+ dev_err(chip->dev, "Failed to enable the chip: %d\n", ret);
-+ ktd202x_chip_disable(chip);
-+ }
-+
-+ return ret;
-+}
-+
-+static bool ktd202x_chip_in_use(struct ktd202x *chip)
-+{
-+ int i;
-+
-+ for (i = 0; i < chip->num_leds; i++) {
-+ if (chip->leds[i].cdev.brightness)
-+ return true;
-+ }
-+
-+ return false;
-+}
-+
-+static int ktd202x_brightness_set(struct ktd202x_led *led,
-+ struct mc_subled *subleds,
-+ unsigned int num_channels)
-+{
-+ bool mode_blink = false;
-+ int channel;
-+ int state;
-+ int ret;
-+ int i;
-+
-+ if (ktd202x_chip_in_use(led->chip)) {
-+ ret = ktd202x_chip_enable(led->chip);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ ret = regmap_read(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL, &state);
-+ if (ret)
-+ return ret;
-+
-+ /*
-+ * In multicolor case, assume blink mode if PWM is set for at least one
-+ * channel because another channel cannot be in state ON at the same time
-+ */
-+ for (i = 0; i < num_channels; i++) {
-+ int channel_state;
-+
-+ channel = subleds[i].channel;
-+ channel_state = (state >> 2 * channel) & KTD202X_CHANNEL_CTRL_MASK(0);
-+ if (channel_state == KTD202X_CHANNEL_CTRL_OFF)
-+ continue;
-+ mode_blink = channel_state == KTD202X_CHANNEL_CTRL_PWM1(0);
-+ break;
-+ }
-+
-+ for (i = 0; i < num_channels; i++) {
-+ enum led_brightness brightness;
-+ int mode;
-+
-+ brightness = subleds[i].brightness;
-+ channel = subleds[i].channel;
-+
-+ if (brightness) {
-+ /* Register expects brightness between 0 and MAX_BRIGHTNESS - 1 */
-+ ret = regmap_write(led->chip->regmap, KTD202X_REG_LED_IOUT(channel),
-+ brightness - 1);
-+ if (ret)
-+ return ret;
-+
-+ if (mode_blink)
-+ mode = KTD202X_CHANNEL_CTRL_PWM1(channel);
-+ else
-+ mode = KTD202X_CHANNEL_CTRL_ON(channel);
-+ } else {
-+ mode = KTD202X_CHANNEL_CTRL_OFF;
-+ }
-+ ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL,
-+ KTD202X_CHANNEL_CTRL_MASK(channel), mode);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ if (!ktd202x_chip_in_use(led->chip))
-+ return ktd202x_chip_disable(led->chip);
-+
-+ return 0;
-+}
-+
-+static int ktd202x_brightness_single_set(struct led_classdev *cdev,
-+ enum led_brightness value)
-+{
-+ struct ktd202x_led *led = container_of(cdev, struct ktd202x_led, cdev);
-+ struct mc_subled info;
-+ int ret;
-+
-+ cdev->brightness = value;
-+
-+ mutex_lock(&led->chip->mutex);
-+
-+ info.brightness = value;
-+ info.channel = led->index;
-+ ret = ktd202x_brightness_set(led, &info, 1);
-+
-+ mutex_unlock(&led->chip->mutex);
-+
-+ return ret;
-+}
-+
-+static int ktd202x_brightness_mc_set(struct led_classdev *cdev,
-+ enum led_brightness value)
-+{
-+ struct led_classdev_mc *mc = lcdev_to_mccdev(cdev);
-+ struct ktd202x_led *led = container_of(mc, struct ktd202x_led, mcdev);
-+ int ret;
-+
-+ cdev->brightness = value;
-+
-+ mutex_lock(&led->chip->mutex);
-+
-+ led_mc_calc_color_components(mc, value);
-+ ret = ktd202x_brightness_set(led, mc->subled_info, mc->num_colors);
-+
-+ mutex_unlock(&led->chip->mutex);
-+
-+ return ret;
-+}
-+
-+static int ktd202x_blink_set(struct ktd202x_led *led, unsigned long *delay_on,
-+ unsigned long *delay_off, struct mc_subled *subleds,
-+ unsigned int num_channels)
-+{
-+ unsigned long delay_total_ms;
-+ int ret, num_steps, on;
-+ u8 ctrl_mask = 0;
-+ u8 ctrl_pwm1 = 0;
-+ u8 ctrl_on = 0;
-+ int i;
-+
-+ mutex_lock(&led->chip->mutex);
-+
-+ for (i = 0; i < num_channels; i++) {
-+ int channel = subleds[i].channel;
-+
-+ ctrl_mask |= KTD202X_CHANNEL_CTRL_MASK(channel);
-+ ctrl_on |= KTD202X_CHANNEL_CTRL_ON(channel);
-+ ctrl_pwm1 |= KTD202X_CHANNEL_CTRL_PWM1(channel);
-+ }
-+
-+ /* Never off - brightness is already set, disable blinking */
-+ if (!*delay_off) {
-+ ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL,
-+ ctrl_mask, ctrl_on);
-+ goto out;
-+ }
-+
-+ /* Convert into values the HW will understand. */
-+
-+ /* Integer representation of time of flash period */
-+ num_steps = (*delay_on + *delay_off - KTD202X_FLASH_PERIOD_MIN_MS) /
-+ KTD202X_FLASH_PERIOD_STEP_MS;
-+ num_steps = clamp(num_steps, 0, KTD202X_FLASH_PERIOD_MAX_STEPS);
-+
-+ /* Integer representation of percentage of LED ON time */
-+ on = (*delay_on * KTD202X_FLASH_ON_MAX) / (*delay_on + *delay_off);
-+
-+ /* Actually used delay_{on,off} values */
-+ delay_total_ms = num_steps * KTD202X_FLASH_PERIOD_STEP_MS + KTD202X_FLASH_PERIOD_MIN_MS;
-+ *delay_on = (delay_total_ms * on) / KTD202X_FLASH_ON_MAX;
-+ *delay_off = delay_total_ms - *delay_on;
-+
-+ /* Set timings */
-+ ret = regmap_write(led->chip->regmap, KTD202X_REG_FLASH_PERIOD, num_steps);
-+ if (ret)
-+ goto out;
-+
-+ ret = regmap_write(led->chip->regmap, KTD202X_REG_PWM1_TIMER, on);
-+ if (ret)
-+ goto out;
-+
-+ ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL,
-+ ctrl_mask, ctrl_pwm1);
-+out:
-+ mutex_unlock(&led->chip->mutex);
-+ return ret;
-+}
-+
-+static int ktd202x_blink_single_set(struct led_classdev *cdev,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ struct ktd202x_led *led = container_of(cdev, struct ktd202x_led, cdev);
-+ struct mc_subled info;
-+ int ret;
-+
-+ if (!cdev->brightness) {
-+ ret = ktd202x_brightness_single_set(cdev, KTD202X_MAX_BRIGHTNESS);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ /* If no blink specified, default to 1 Hz. */
-+ if (!*delay_off && !*delay_on) {
-+ *delay_off = 500;
-+ *delay_on = 500;
-+ }
-+
-+ /* Never on - just set to off */
-+ if (!*delay_on)
-+ return ktd202x_brightness_single_set(cdev, LED_OFF);
-+
-+ info.channel = led->index;
-+
-+ return ktd202x_blink_set(led, delay_on, delay_off, &info, 1);
-+}
-+
-+static int ktd202x_blink_mc_set(struct led_classdev *cdev,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ struct led_classdev_mc *mc = lcdev_to_mccdev(cdev);
-+ struct ktd202x_led *led = container_of(mc, struct ktd202x_led, mcdev);
-+ int ret;
-+
-+ if (!cdev->brightness) {
-+ ret = ktd202x_brightness_mc_set(cdev, KTD202X_MAX_BRIGHTNESS);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ /* If no blink specified, default to 1 Hz. */
-+ if (!*delay_off && !*delay_on) {
-+ *delay_off = 500;
-+ *delay_on = 500;
-+ }
-+
-+ /* Never on - just set to off */
-+ if (!*delay_on)
-+ return ktd202x_brightness_mc_set(cdev, LED_OFF);
-+
-+ return ktd202x_blink_set(led, delay_on, delay_off, mc->subled_info,
-+ mc->num_colors);
-+}
-+
-+static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct device_node *np,
-+ struct ktd202x_led *led, struct led_init_data *init_data)
-+{
-+ struct led_classdev *cdev;
-+ struct device_node *child;
-+ struct mc_subled *info;
-+ int num_channels;
-+ int i = 0;
-+
-+ num_channels = of_get_available_child_count(np);
-+ if (!num_channels || num_channels > chip->num_leds)
-+ return -EINVAL;
-+
-+ info = devm_kcalloc(chip->dev, num_channels, sizeof(*info), GFP_KERNEL);
-+ if (!info)
-+ return -ENOMEM;
-+
-+ for_each_available_child_of_node(np, child) {
-+ u32 mono_color;
-+ u32 reg;
-+ int ret;
-+
-+ ret = of_property_read_u32(child, "reg", ®);
-+ if (ret != 0 || reg >= chip->num_leds) {
-+ dev_err(chip->dev, "invalid 'reg' of %pOFn\n", child);
-+ of_node_put(child);
-+ return -EINVAL;
-+ }
-+
-+ ret = of_property_read_u32(child, "color", &mono_color);
-+ if (ret < 0 && ret != -EINVAL) {
-+ dev_err(chip->dev, "failed to parse 'color' of %pOF\n", child);
-+ of_node_put(child);
-+ return ret;
-+ }
-+
-+ info[i].color_index = mono_color;
-+ info[i].channel = reg;
-+ info[i].intensity = KTD202X_MAX_BRIGHTNESS;
-+ i++;
-+ }
-+
-+ led->mcdev.subled_info = info;
-+ led->mcdev.num_colors = num_channels;
-+
-+ cdev = &led->mcdev.led_cdev;
-+ cdev->brightness_set_blocking = ktd202x_brightness_mc_set;
-+ cdev->blink_set = ktd202x_blink_mc_set;
-+
-+ return devm_led_classdev_multicolor_register_ext(chip->dev, &led->mcdev, init_data);
-+}
-+
-+static int ktd202x_setup_led_single(struct ktd202x *chip, struct device_node *np,
-+ struct ktd202x_led *led, struct led_init_data *init_data)
-+{
-+ struct led_classdev *cdev;
-+ u32 reg;
-+ int ret;
-+
-+ ret = of_property_read_u32(np, "reg", ®);
-+ if (ret != 0 || reg >= chip->num_leds) {
-+ dev_err(chip->dev, "invalid 'reg' of %pOFn\n", np);
-+ return -EINVAL;
-+ }
-+ led->index = reg;
-+
-+ cdev = &led->cdev;
-+ cdev->brightness_set_blocking = ktd202x_brightness_single_set;
-+ cdev->blink_set = ktd202x_blink_single_set;
-+
-+ return devm_led_classdev_register_ext(chip->dev, &led->cdev, init_data);
-+}
-+
-+static int ktd202x_add_led(struct ktd202x *chip, struct device_node *np, unsigned int index)
-+{
-+ struct ktd202x_led *led = &chip->leds[index];
-+ struct led_init_data init_data = {};
-+ struct led_classdev *cdev;
-+ u32 color;
-+ int ret;
-+
-+ /* Color property is optional in single color case */
-+ ret = of_property_read_u32(np, "color", &color);
-+ if (ret < 0 && ret != -EINVAL) {
-+ dev_err(chip->dev, "failed to parse 'color' of %pOF\n", np);
-+ return ret;
-+ }
-+
-+ led->chip = chip;
-+ init_data.fwnode = of_fwnode_handle(np);
-+
-+ if (color == LED_COLOR_ID_RGB) {
-+ cdev = &led->mcdev.led_cdev;
-+ ret = ktd202x_setup_led_rgb(chip, np, led, &init_data);
-+ } else {
-+ cdev = &led->cdev;
-+ ret = ktd202x_setup_led_single(chip, np, led, &init_data);
-+ }
-+
-+ if (ret) {
-+ dev_err(chip->dev, "unable to register %s\n", cdev->name);
-+ return ret;
-+ }
-+
-+ cdev->max_brightness = KTD202X_MAX_BRIGHTNESS;
-+
-+ return 0;
-+}
-+
-+static int ktd202x_probe_dt(struct ktd202x *chip)
-+{
-+ struct device_node *np = dev_of_node(chip->dev), *child;
-+ int count;
-+ int i = 0;
-+
-+ chip->num_leds = (int)(unsigned long)of_device_get_match_data(chip->dev);
-+
-+ count = of_get_available_child_count(np);
-+ if (!count || count > chip->num_leds)
-+ return -EINVAL;
-+
-+ regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_RSTR_RESET);
-+
-+ /* Allow the device to execute the complete reset */
-+ usleep_range(200, 300);
-+
-+ for_each_available_child_of_node(np, child) {
-+ int ret = ktd202x_add_led(chip, child, i);
-+
-+ if (ret) {
-+ of_node_put(child);
-+ return ret;
-+ }
-+ i++;
-+ }
-+
-+ return 0;
-+}
-+
-+static const struct regmap_config ktd202x_regmap_config = {
-+ .reg_bits = 8,
-+ .val_bits = 8,
-+ .max_register = 0x09,
-+ .cache_type = REGCACHE_FLAT,
-+ .reg_defaults = ktd202x_reg_defaults,
-+ .num_reg_defaults = ARRAY_SIZE(ktd202x_reg_defaults),
-+};
-+
-+static int ktd202x_probe(struct i2c_client *client)
-+{
-+ struct device *dev = &client->dev;
-+ struct ktd202x *chip;
-+ int count;
-+ int ret;
-+
-+ count = device_get_child_node_count(dev);
-+ if (!count || count > KTD202X_MAX_LEDS)
-+ return dev_err_probe(dev, -EINVAL, "Incorrect number of leds (%d)", count);
-+
-+ chip = devm_kzalloc(dev, struct_size(chip, leds, count), GFP_KERNEL);
-+ if (!chip)
-+ return -ENOMEM;
-+
-+ chip->dev = dev;
-+ i2c_set_clientdata(client, chip);
-+
-+ chip->regmap = devm_regmap_init_i2c(client, &ktd202x_regmap_config);
-+ if (IS_ERR(chip->regmap)) {
-+ ret = dev_err_probe(dev, PTR_ERR(chip->regmap),
-+ "Failed to allocate register map.\n");
-+ return ret;
-+ }
-+
-+ chip->regulators[0].supply = "vin";
-+ chip->regulators[1].supply = "vio";
-+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(chip->regulators), chip->regulators);
-+ if (ret < 0) {
-+ dev_err_probe(dev, ret, "Failed to request regulators.\n");
-+ return ret;
-+ }
-+
-+ ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators), chip->regulators);
-+ if (ret) {
-+ dev_err_probe(dev, ret, "Failed to enable regulators.\n");
-+ return ret;
-+ }
-+
-+ ret = ktd202x_probe_dt(chip);
-+ if (ret < 0) {
-+ regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators);
-+ return ret;
-+ }
-+
-+ ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators);
-+ if (ret) {
-+ dev_err_probe(dev, ret, "Failed to disable regulators.\n");
-+ return ret;
-+ }
-+
-+ mutex_init(&chip->mutex);
-+
-+ return 0;
-+}
-+
-+static void ktd202x_remove(struct i2c_client *client)
-+{
-+ struct ktd202x *chip = i2c_get_clientdata(client);
-+
-+ ktd202x_chip_disable(chip);
-+
-+ mutex_destroy(&chip->mutex);
-+}
-+
-+static void ktd202x_shutdown(struct i2c_client *client)
-+{
-+ struct ktd202x *chip = i2c_get_clientdata(client);
-+
-+ /* Reset registers to make sure all LEDs are off before shutdown */
-+ regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_RSTR_RESET);
-+}
-+
-+static const struct of_device_id ktd202x_match_table[] = {
-+ { .compatible = "kinetic,ktd2026", .data = (void *)KTD2026_NUM_LEDS },
-+ { .compatible = "kinetic,ktd2027", .data = (void *)KTD2027_NUM_LEDS },
-+ {},
-+};
-+MODULE_DEVICE_TABLE(of, ktd202x_match_table);
-+
-+static struct i2c_driver ktd202x_driver = {
-+ .driver = {
-+ .name = "leds-ktd202x",
-+ .of_match_table = ktd202x_match_table,
-+ },
-+ .probe = ktd202x_probe,
-+ .remove = ktd202x_remove,
-+ .shutdown = ktd202x_shutdown,
-+};
-+module_i2c_driver(ktd202x_driver);
-+
-+MODULE_AUTHOR("André Apitzsch <git@apitzsch.eu>");
-+MODULE_DESCRIPTION("Kinetic KTD2026/7 LED driver");
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From f14aa5ea415b8add245e976bfab96a12986c6843 Mon Sep 17 00:00:00 2001
-From: Kate Hsuan <hpa@redhat.com>
-Date: Fri, 31 May 2024 13:41:19 +0200
-Subject: leds: rgb: leds-ktd202x: Get device properties through fwnode to
- support ACPI
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This LED controller is installed on a Xiaomi pad2 and it is an x86
-platform. The original driver is based on the device tree and can't be
-used for this ACPI based system. This patch migrated the driver to use
-fwnode to access the properties. Moreover, the fwnode API supports the
-device tree so this work won't affect the original implementations.
-
-Signed-off-by: Kate Hsuan <hpa@redhat.com>
-Tested-by: André Apitzsch <git@apitzsch.eu> # on BQ Aquaris M5
-Reviewed-by: Hans de Goede <hdegoede@redhat.com>
-Reviewed-by: Andy Shevchenko <andy@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://lore.kernel.org/r/20240531114124.45346-2-hdegoede@redhat.com
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/rgb/Kconfig | 1 -
- drivers/leds/rgb/leds-ktd202x.c | 64 ++++++++++++++++++++++-------------------
- 2 files changed, 34 insertions(+), 31 deletions(-)
-
-(limited to 'drivers/leds/rgb')
-
---- a/drivers/leds/rgb/Kconfig
-+++ b/drivers/leds/rgb/Kconfig
-@@ -17,7 +17,6 @@ config LEDS_GROUP_MULTICOLOR
- config LEDS_KTD202X
- tristate "LED support for KTD202x Chips"
- depends on I2C
-- depends on OF
- select REGMAP_I2C
- help
- This option enables support for the Kinetic KTD2026/KTD2027
---- a/drivers/leds/rgb/leds-ktd202x.c
-+++ b/drivers/leds/rgb/leds-ktd202x.c
-@@ -99,7 +99,7 @@ struct ktd202x {
- struct device *dev;
- struct regmap *regmap;
- bool enabled;
-- int num_leds;
-+ unsigned long num_leds;
- struct ktd202x_led leds[] __counted_by(num_leds);
- };
-
-@@ -381,16 +381,19 @@ static int ktd202x_blink_mc_set(struct l
- mc->num_colors);
- }
-
--static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct device_node *np,
-+static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct fwnode_handle *fwnode,
- struct ktd202x_led *led, struct led_init_data *init_data)
- {
-+ struct fwnode_handle *child;
- struct led_classdev *cdev;
-- struct device_node *child;
- struct mc_subled *info;
- int num_channels;
- int i = 0;
-
-- num_channels = of_get_available_child_count(np);
-+ num_channels = 0;
-+ fwnode_for_each_available_child_node(fwnode, child)
-+ num_channels++;
-+
- if (!num_channels || num_channels > chip->num_leds)
- return -EINVAL;
-
-@@ -398,22 +401,22 @@ static int ktd202x_setup_led_rgb(struct
- if (!info)
- return -ENOMEM;
-
-- for_each_available_child_of_node(np, child) {
-+ fwnode_for_each_available_child_node(fwnode, child) {
- u32 mono_color;
- u32 reg;
- int ret;
-
-- ret = of_property_read_u32(child, "reg", ®);
-+ ret = fwnode_property_read_u32(child, "reg", ®);
- if (ret != 0 || reg >= chip->num_leds) {
-- dev_err(chip->dev, "invalid 'reg' of %pOFn\n", child);
-- of_node_put(child);
-- return -EINVAL;
-+ dev_err(chip->dev, "invalid 'reg' of %pfw\n", child);
-+ fwnode_handle_put(child);
-+ return ret;
- }
-
-- ret = of_property_read_u32(child, "color", &mono_color);
-+ ret = fwnode_property_read_u32(child, "color", &mono_color);
- if (ret < 0 && ret != -EINVAL) {
-- dev_err(chip->dev, "failed to parse 'color' of %pOF\n", child);
-- of_node_put(child);
-+ dev_err(chip->dev, "failed to parse 'color' of %pfw\n", child);
-+ fwnode_handle_put(child);
- return ret;
- }
-
-@@ -433,16 +436,16 @@ static int ktd202x_setup_led_rgb(struct
- return devm_led_classdev_multicolor_register_ext(chip->dev, &led->mcdev, init_data);
- }
-
--static int ktd202x_setup_led_single(struct ktd202x *chip, struct device_node *np,
-+static int ktd202x_setup_led_single(struct ktd202x *chip, struct fwnode_handle *fwnode,
- struct ktd202x_led *led, struct led_init_data *init_data)
- {
- struct led_classdev *cdev;
- u32 reg;
- int ret;
-
-- ret = of_property_read_u32(np, "reg", ®);
-+ ret = fwnode_property_read_u32(fwnode, "reg", ®);
- if (ret != 0 || reg >= chip->num_leds) {
-- dev_err(chip->dev, "invalid 'reg' of %pOFn\n", np);
-+ dev_err(chip->dev, "invalid 'reg' of %pfw\n", fwnode);
- return -EINVAL;
- }
- led->index = reg;
-@@ -454,7 +457,7 @@ static int ktd202x_setup_led_single(stru
- return devm_led_classdev_register_ext(chip->dev, &led->cdev, init_data);
- }
-
--static int ktd202x_add_led(struct ktd202x *chip, struct device_node *np, unsigned int index)
-+static int ktd202x_add_led(struct ktd202x *chip, struct fwnode_handle *fwnode, unsigned int index)
- {
- struct ktd202x_led *led = &chip->leds[index];
- struct led_init_data init_data = {};
-@@ -463,21 +466,21 @@ static int ktd202x_add_led(struct ktd202
- int ret;
-
- /* Color property is optional in single color case */
-- ret = of_property_read_u32(np, "color", &color);
-+ ret = fwnode_property_read_u32(fwnode, "color", &color);
- if (ret < 0 && ret != -EINVAL) {
-- dev_err(chip->dev, "failed to parse 'color' of %pOF\n", np);
-+ dev_err(chip->dev, "failed to parse 'color' of %pfw\n", fwnode);
- return ret;
- }
-
- led->chip = chip;
-- init_data.fwnode = of_fwnode_handle(np);
-+ init_data.fwnode = fwnode;
-
- if (color == LED_COLOR_ID_RGB) {
- cdev = &led->mcdev.led_cdev;
-- ret = ktd202x_setup_led_rgb(chip, np, led, &init_data);
-+ ret = ktd202x_setup_led_rgb(chip, fwnode, led, &init_data);
- } else {
- cdev = &led->cdev;
-- ret = ktd202x_setup_led_single(chip, np, led, &init_data);
-+ ret = ktd202x_setup_led_single(chip, fwnode, led, &init_data);
- }
-
- if (ret) {
-@@ -490,15 +493,14 @@ static int ktd202x_add_led(struct ktd202
- return 0;
- }
-
--static int ktd202x_probe_dt(struct ktd202x *chip)
-+static int ktd202x_probe_fw(struct ktd202x *chip)
- {
-- struct device_node *np = dev_of_node(chip->dev), *child;
-+ struct fwnode_handle *child;
-+ struct device *dev = chip->dev;
- int count;
- int i = 0;
-
-- chip->num_leds = (int)(unsigned long)of_device_get_match_data(chip->dev);
--
-- count = of_get_available_child_count(np);
-+ count = device_get_child_node_count(dev);
- if (!count || count > chip->num_leds)
- return -EINVAL;
-
-@@ -507,11 +509,11 @@ static int ktd202x_probe_dt(struct ktd20
- /* Allow the device to execute the complete reset */
- usleep_range(200, 300);
-
-- for_each_available_child_of_node(np, child) {
-+ device_for_each_child_node(dev, child) {
- int ret = ktd202x_add_led(chip, child, i);
-
- if (ret) {
-- of_node_put(child);
-+ fwnode_handle_put(child);
- return ret;
- }
- i++;
-@@ -554,6 +556,8 @@ static int ktd202x_probe(struct i2c_clie
- return ret;
- }
-
-+ chip->num_leds = (unsigned long)i2c_get_match_data(client);
-+
- chip->regulators[0].supply = "vin";
- chip->regulators[1].supply = "vio";
- ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(chip->regulators), chip->regulators);
-@@ -568,7 +572,7 @@ static int ktd202x_probe(struct i2c_clie
- return ret;
- }
-
-- ret = ktd202x_probe_dt(chip);
-+ ret = ktd202x_probe_fw(chip);
- if (ret < 0) {
- regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators);
- return ret;
-@@ -605,7 +609,7 @@ static void ktd202x_shutdown(struct i2c_
- static const struct of_device_id ktd202x_match_table[] = {
- { .compatible = "kinetic,ktd2026", .data = (void *)KTD2026_NUM_LEDS },
- { .compatible = "kinetic,ktd2027", .data = (void *)KTD2027_NUM_LEDS },
-- {},
-+ {}
- };
- MODULE_DEVICE_TABLE(of, ktd202x_match_table);
-
+++ /dev/null
-From 75bd07aef47e1a984229e6ec702e8b9aee0226e4 Mon Sep 17 00:00:00 2001
-From: Kate Hsuan <hpa@redhat.com>
-Date: Fri, 31 May 2024 13:41:20 +0200
-Subject: leds: rgb: leds-ktd202x: I2C ID tables for KTD2026 and 2027
-
-Add an i2c_device_id id_table to match manually instantiated
-(non device-tree / ACPI instantiated) KTD202x controllers as
-found on some x86 boards.
-
-This table shows the maximum support LED channel for KTD2026
-(three LEDs) and KTD-2027 (4 LEDs).
-
-Link: https://www.kinet-ic.com/uploads/KTD2026-7-04h.pdf
-Signed-off-by: Kate Hsuan <hpa@redhat.com>
-Reviewed-by: Hans de Goede <hdegoede@redhat.com>
-Reviewed-by: Andy Shevchenko <andy@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://lore.kernel.org/r/20240531114124.45346-3-hdegoede@redhat.com
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/rgb/leds-ktd202x.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
-(limited to 'drivers/leds/rgb')
-
---- a/drivers/leds/rgb/leds-ktd202x.c
-+++ b/drivers/leds/rgb/leds-ktd202x.c
-@@ -606,6 +606,13 @@ static void ktd202x_shutdown(struct i2c_
- regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_RSTR_RESET);
- }
-
-+static const struct i2c_device_id ktd202x_id[] = {
-+ {"ktd2026", KTD2026_NUM_LEDS},
-+ {"ktd2027", KTD2027_NUM_LEDS},
-+ {}
-+};
-+MODULE_DEVICE_TABLE(i2c, ktd202x_id);
-+
- static const struct of_device_id ktd202x_match_table[] = {
- { .compatible = "kinetic,ktd2026", .data = (void *)KTD2026_NUM_LEDS },
- { .compatible = "kinetic,ktd2027", .data = (void *)KTD2027_NUM_LEDS },
-@@ -621,6 +628,7 @@ static struct i2c_driver ktd202x_driver
- .probe = ktd202x_probe,
- .remove = ktd202x_remove,
- .shutdown = ktd202x_shutdown,
-+ .id_table = ktd202x_id,
- };
- module_i2c_driver(ktd202x_driver);
-
+++ /dev/null
-From e1b08c6f5b92d408a9fcc1030a340caeb9852250 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Fri, 31 May 2024 13:41:21 +0200
-Subject: leds: rgb: leds-ktd202x: Initialize mutex earlier
-
-The mutex must be initialized before the LED class device is registered
-otherwise there is a race where it may get used before it is initialized:
-
- DEBUG_LOCKS_WARN_ON(lock->magic != lock)
- WARNING: CPU: 2 PID: 2045 at kernel/locking/mutex.c:587 __mutex_lock
- ...
- RIP: 0010:__mutex_lock+0x7db/0xc10
- ...
- set_brightness_delayed_set_brightness.part.0+0x17/0x60
- set_brightness_delayed+0xf1/0x100
- process_one_work+0x222/0x5a0
-
-Move the mutex_init() call earlier to avoid this race condition and
-switch to devm_mutex_init() to avoid the need to add error-exit
-cleanup to probe() if probe() fails later on.
-
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Reviewed-by: Andy Shevchenko <andy@kernel.org>
-Link: https://lore.kernel.org/r/20240531114124.45346-4-hdegoede@redhat.com
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/rgb/leds-ktd202x.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-(limited to 'drivers/leds/rgb')
-
---- a/drivers/leds/rgb/leds-ktd202x.c
-+++ b/drivers/leds/rgb/leds-ktd202x.c
-@@ -556,6 +556,10 @@ static int ktd202x_probe(struct i2c_clie
- return ret;
- }
-
-+ ret = devm_mutex_init(dev, &chip->mutex);
-+ if (ret)
-+ return ret;
-+
- chip->num_leds = (unsigned long)i2c_get_match_data(client);
-
- chip->regulators[0].supply = "vin";
-@@ -584,8 +588,6 @@ static int ktd202x_probe(struct i2c_clie
- return ret;
- }
-
-- mutex_init(&chip->mutex);
--
- return 0;
- }
-
-@@ -594,8 +596,6 @@ static void ktd202x_remove(struct i2c_cl
- struct ktd202x *chip = i2c_get_clientdata(client);
-
- ktd202x_chip_disable(chip);
--
-- mutex_destroy(&chip->mutex);
- }
-
- static void ktd202x_shutdown(struct i2c_client *client)
+++ /dev/null
-From a78794562fcb2659c976388b1285eddda97e9954 Mon Sep 17 00:00:00 2001
-From: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-Date: Tue, 10 Oct 2023 21:29:13 +0530
-Subject: [PATCH] PCI: dwc: Add host_post_init() callback
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This callback can be used by the platform drivers to do configuration
-once all the devices are scanned. Like changing LNKCTL of all downstream
-devices to enable ASPM etc...
-
-Link: https://lore.kernel.org/linux-pci/20231010155914.9516-2-manivannan.sadhasivam@linaro.org
-Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
----
- drivers/pci/controller/dwc/pcie-designware-host.c | 3 +++
- drivers/pci/controller/dwc/pcie-designware.h | 1 +
- 2 files changed, 4 insertions(+)
-
---- a/drivers/pci/controller/dwc/pcie-designware-host.c
-+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
-@@ -502,6 +502,9 @@ int dw_pcie_host_init(struct dw_pcie_rp
- if (ret)
- goto err_stop_link;
-
-+ if (pp->ops->host_post_init)
-+ pp->ops->host_post_init(pp);
-+
- return 0;
-
- err_stop_link:
---- a/drivers/pci/controller/dwc/pcie-designware.h
-+++ b/drivers/pci/controller/dwc/pcie-designware.h
-@@ -301,6 +301,7 @@ enum dw_pcie_ltssm {
- struct dw_pcie_host_ops {
- int (*host_init)(struct dw_pcie_rp *pp);
- void (*host_deinit)(struct dw_pcie_rp *pp);
-+ void (*host_post_init)(struct dw_pcie_rp *pp);
- int (*msi_host_init)(struct dw_pcie_rp *pp);
- void (*pme_turn_off)(struct dw_pcie_rp *pp);
- };
+++ /dev/null
-From 9f4f3dfad8cf08208fbb78b1b9cbf957c12618b9 Mon Sep 17 00:00:00 2001
-From: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-Date: Tue, 10 Oct 2023 21:29:14 +0530
-Subject: [PATCH] PCI: qcom: Enable ASPM for platforms supporting 1.9.0 ops
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-ASPM is supported by Qcom host controllers/bridges on most of the recent
-platforms and so the devices tested so far. But for enabling ASPM by
-default (without using Kconfig, kernel command-line or sysfs), BIOS has
-to enable ASPM on both host bridge and downstream devices during boot.
-
-Unfortunately, none of the BIOS available on Qcom platforms enables
-ASPM. Due to this, the platforms making use of Qcom SoCs draw high power
-during runtime.
-
-To fix this power draw issue, users have to enable ASPM using Kconfig,
-kernel command-line, sysfs or the BIOS has to start enabling ASPM.
-
-The latter may happen in the future, but that won't address the issue on
-current platforms. Also, asking users to enable a feature to get the power
-management right would provide an unpleasant out-of-the-box experience.
-
-So the apt solution is to enable ASPM in the controller driver itself. And
-this is being accomplished by calling pci_enable_link_state() in the newly
-introduced host_post_init() callback for all the devices connected to the
-bus. This function enables all supported link low power states for both
-host bridge and the downstream devices.
-
-Due to limited testing, ASPM is only enabled for platforms making use of
-ops_1_9_0 callbacks.
-
-[kwilczynski: commit log]
-Link: https://lore.kernel.org/linux-pci/20231010155914.9516-3-manivannan.sadhasivam@linaro.org
-Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
----
- drivers/pci/controller/dwc/pcie-qcom.c | 28 ++++++++++++++++++++++++++
- 1 file changed, 28 insertions(+)
-
---- a/drivers/pci/controller/dwc/pcie-qcom.c
-+++ b/drivers/pci/controller/dwc/pcie-qcom.c
-@@ -222,6 +222,7 @@ struct qcom_pcie_ops {
- int (*get_resources)(struct qcom_pcie *pcie);
- int (*init)(struct qcom_pcie *pcie);
- int (*post_init)(struct qcom_pcie *pcie);
-+ void (*host_post_init)(struct qcom_pcie *pcie);
- void (*deinit)(struct qcom_pcie *pcie);
- void (*ltssm_enable)(struct qcom_pcie *pcie);
- int (*config_sid)(struct qcom_pcie *pcie);
-@@ -966,6 +967,22 @@ static int qcom_pcie_post_init_2_7_0(str
- return 0;
- }
-
-+static int qcom_pcie_enable_aspm(struct pci_dev *pdev, void *userdata)
-+{
-+ /* Downstream devices need to be in D0 state before enabling PCI PM substates */
-+ pci_set_power_state(pdev, PCI_D0);
-+ pci_enable_link_state(pdev, PCIE_LINK_STATE_ALL);
-+
-+ return 0;
-+}
-+
-+static void qcom_pcie_host_post_init_2_7_0(struct qcom_pcie *pcie)
-+{
-+ struct dw_pcie_rp *pp = &pcie->pci->pp;
-+
-+ pci_walk_bus(pp->bridge->bus, qcom_pcie_enable_aspm, NULL);
-+}
-+
- static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie)
- {
- struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
-@@ -1224,9 +1241,19 @@ static void qcom_pcie_host_deinit(struct
- pcie->cfg->ops->deinit(pcie);
- }
-
-+static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp)
-+{
-+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
-+ struct qcom_pcie *pcie = to_qcom_pcie(pci);
-+
-+ if (pcie->cfg->ops->host_post_init)
-+ pcie->cfg->ops->host_post_init(pcie);
-+}
-+
- static const struct dw_pcie_host_ops qcom_pcie_dw_ops = {
- .host_init = qcom_pcie_host_init,
- .host_deinit = qcom_pcie_host_deinit,
-+ .host_post_init = qcom_pcie_host_post_init,
- };
-
- /* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */
-@@ -1288,6 +1315,7 @@ static const struct qcom_pcie_ops ops_1_
- .get_resources = qcom_pcie_get_resources_2_7_0,
- .init = qcom_pcie_init_2_7_0,
- .post_init = qcom_pcie_post_init_2_7_0,
-+ .host_post_init = qcom_pcie_host_post_init_2_7_0,
- .deinit = qcom_pcie_deinit_2_7_0,
- .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable,
- .config_sid = qcom_pcie_config_sid_1_9_0,
+++ /dev/null
-From d33d1214a1ddf9e7e4d14c62637518252927f0be Mon Sep 17 00:00:00 2001
-From: Lee Jones <lee@kernel.org>
-Date: Wed, 12 Jun 2024 16:36:40 +0100
-Subject: [PATCH] leds: core: Omit set_brightness error message for a LED
- supporting hw trigger only
-
-If both set_brightness functions return -ENOTSUPP, then the LED doesn't
-support setting a fixed brightness value, and the error message isn't
-helpful. This can be the case e.g. for LEDs supporting a specific hw
-trigger only.
-
-Pinched the subject line and commit message from Heiner:
-Link: https://lore.kernel.org/all/44177e37-9512-4044-8991-bb23b184bf37@gmail.com/
-
-Reworked the function to provide Heiner's required semantics whilst
-simultaneously increasing readability and flow.
-
-Cc: Pavel Machek <pavel@ucw.cz>
-Cc: linux-leds@vger.kernel.org
-Suggested-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Heiner Kallweit <hkallweit1@gmail.com>
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/led-core.c | 19 +++++++++++++------
- 1 file changed, 13 insertions(+), 6 deletions(-)
-
---- a/drivers/leds/led-core.c
-+++ b/drivers/leds/led-core.c
-@@ -122,15 +122,22 @@ static void led_timer_function(struct ti
- static void set_brightness_delayed_set_brightness(struct led_classdev *led_cdev,
- unsigned int value)
- {
-- int ret = 0;
-+ int ret;
-
- ret = __led_set_brightness(led_cdev, value);
-- if (ret == -ENOTSUPP)
-+ if (ret == -ENOTSUPP) {
- ret = __led_set_brightness_blocking(led_cdev, value);
-- if (ret < 0 &&
-- /* LED HW might have been unplugged, therefore don't warn */
-- !(ret == -ENODEV && (led_cdev->flags & LED_UNREGISTERING) &&
-- (led_cdev->flags & LED_HW_PLUGGABLE)))
-+ if (ret == -ENOTSUPP)
-+ /* No back-end support to set a fixed brightness value */
-+ return;
-+ }
-+
-+ /* LED HW might have been unplugged, therefore don't warn */
-+ if (ret == -ENODEV && led_cdev->flags & LED_UNREGISTERING &&
-+ led_cdev->flags & LED_HW_PLUGGABLE)
-+ return;
-+
-+ if (ret < 0)
- dev_err(led_cdev->dev,
- "Setting an LED's brightness failed (%d)\n", ret);
- }
+++ /dev/null
-From d48a5472b8f2b29800bb25913f9403765005f1bc Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de>
-Date: Mon, 18 Sep 2023 21:19:14 +0200
-Subject: [PATCH] net: dsa: realtek: Convert to platform remove callback
- returning void
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The .remove() callback for a platform driver returns an int which makes
-many driver authors wrongly assume it's possible to do error handling by
-returning an error code. However the value returned is ignored (apart
-from emitting a warning) and this typically results in resource leaks.
-To improve here there is a quest to make the remove callback return
-void. In the first step of this quest all drivers are converted to
-.remove_new() which already returns void. Eventually after all drivers
-are converted, .remove_new() is renamed to .remove().
-
-Trivially convert this driver from always returning zero in the remove
-callback to the void returning variant.
-
-Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
----
- drivers/net/dsa/realtek/realtek-smi.c | 8 +++-----
- 1 file changed, 3 insertions(+), 5 deletions(-)
-
---- a/drivers/net/dsa/realtek/realtek-smi.c
-+++ b/drivers/net/dsa/realtek/realtek-smi.c
-@@ -506,12 +506,12 @@ static int realtek_smi_probe(struct plat
- return 0;
- }
-
--static int realtek_smi_remove(struct platform_device *pdev)
-+static void realtek_smi_remove(struct platform_device *pdev)
- {
- struct realtek_priv *priv = platform_get_drvdata(pdev);
-
- if (!priv)
-- return 0;
-+ return;
-
- dsa_unregister_switch(priv->ds);
- if (priv->slave_mii_bus)
-@@ -520,8 +520,6 @@ static int realtek_smi_remove(struct pla
- /* leave the device reset asserted */
- if (priv->reset)
- gpiod_set_value(priv->reset, 1);
--
-- return 0;
- }
-
- static void realtek_smi_shutdown(struct platform_device *pdev)
-@@ -559,7 +557,7 @@ static struct platform_driver realtek_sm
- .of_match_table = realtek_smi_of_match,
- },
- .probe = realtek_smi_probe,
-- .remove = realtek_smi_remove,
-+ .remove_new = realtek_smi_remove,
- .shutdown = realtek_smi_shutdown,
- };
- module_platform_driver(realtek_smi_driver);
+++ /dev/null
-From mboxrd@z Thu Jan 1 00:00:00 1970
-Authentication-Results: smtp.subspace.kernel.org;
- dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="sMUeie/T"
-Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49])
- by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 84BB8D6D
- for <bpf@vger.kernel.org>; Wed, 6 Dec 2023 15:16:16 -0800 (PST)
-Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-db5416d0fccso403298276.1
- for <bpf@vger.kernel.org>; Wed, 06 Dec 2023 15:16:16 -0800 (PST)
-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
- d=google.com; s=20230601; t=1701904575; x=1702509375; darn=vger.kernel.org;
- h=cc:to:from:subject:message-id:references:mime-version:in-reply-to
- :date:from:to:cc:subject:date:message-id:reply-to;
- bh=/7eYcPC4ZNNyPcPPs0B5tDplF0arxw3r0vINNNou0rY=;
- b=sMUeie/TxdytzC0EyT11QWi1TqTtiv7KCTs1F2vLmUUvPKNA3+1MHFo8ECW+0gQuDE
- FGrgdZKGK5mXQgkF0N3JiSLvKO8tpQOIB57JLCG5IVy5dr2vVv0ExU3Dag2Cc4oBIBIO
- w/cH95O1oPlvluIpATmAsxenVr7mFomU63BqYiRGLaEhWeb2hJ636GO8lubtsDfdFFoi
- GPOL2tQwV93VnqmywBBpFaNAULN0UoCFhfkKv5prvpkXq19sWI7zyorVZ+rdTYem5m4T
- dXsDaLXPtC3Dh2JOad1duSQIah/wCHYYUcV3IoFhwj2y0Uk/TTCrnZPORweSADcEy6Ho
- vDrA==
-X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
- d=1e100.net; s=20230601; t=1701904575; x=1702509375;
- h=cc:to:from:subject:message-id:references:mime-version:in-reply-to
- :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;
- bh=/7eYcPC4ZNNyPcPPs0B5tDplF0arxw3r0vINNNou0rY=;
- b=Dmc6aSntxPlxAk72zVO1G9WoZnFtLolxENlLscYYAHG3VE+PQ8gGN2rPzcGoKb2Btb
- 4b0PvjOzSlPQyghahdhdlz04RtAeeGG/MkfNiYjFql5OifIoovb51kroiPYrVsa7Ps7Y
- +Pxug0+NPdTm5s9TNz940ZKl3GRME8UTmVxpWJRX03XMOqb6Wgsh2SK9ahXKc4yRsi62
- 3a3J72WmmSgvimxwM/99fXwvoUQpiv2J1xCoqc1Ng4q4qSuZvzmHN7ZTGaUhLxOqLeLK
- 3W4RKHW6rZ7UjppuB6I3NXW+D344By2rdKp1sRXpjdQ0GS3YUcvlRETcJBXJudHfQP5Y
- CLOw==
-X-Gm-Message-State: AOJu0YzdCTLdwny+N99zeMgyKqFsEZhfIhL2cbgKA6zC1U/OLkxxRLoM
- XrYVBC9DmxCGmP4o+M/Z/kHUew/9faHlCiLGxw==
-X-Google-Smtp-Source: AGHT+IFRXxBV6JuX5Cl/k2o1+WKkCwkR8j20MJSkmoGCedPAtqFttH8OVh1/6vdfnq8MPN++A2h89peZQhyG8OsJ8A==
-X-Received: from jstitt-linux1.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:23b5])
- (user=justinstitt job=sendgmr) by 2002:a25:dac7:0:b0:da0:3117:f35 with SMTP
- id n190-20020a25dac7000000b00da031170f35mr28652ybf.3.1701904575576; Wed, 06
- Dec 2023 15:16:15 -0800 (PST)
-Date: Wed, 06 Dec 2023 23:16:10 +0000
-In-Reply-To: <20231206-ethtool_puts_impl-v5-0-5a2528e17bf8@google.com>
-Precedence: bulk
-X-Mailing-List: bpf@vger.kernel.org
-List-Id: <bpf.vger.kernel.org>
-List-Subscribe: <mailto:bpf+subscribe@vger.kernel.org>
-List-Unsubscribe: <mailto:bpf+unsubscribe@vger.kernel.org>
-Mime-Version: 1.0
-References: <20231206-ethtool_puts_impl-v5-0-5a2528e17bf8@google.com>
-X-Developer-Key: i=justinstitt@google.com; a=ed25519; pk=tC3hNkJQTpNX/gLKxTNQKDmiQl6QjBNCGKJINqAdJsE=
-X-Developer-Signature: v=1; a=ed25519-sha256; t=1701904573; l=1840;
- i=justinstitt@google.com; s=20230717; h=from:subject:message-id;
- bh=UMdetIL2ZsPIkSodqhw2fM21NHJVjCu0lRImFuNhVoM=; b=a8rMnXfVVQ5gsxHWG4WRMwOLxZgflqXZtNuKx26vv4DwYvvCtCiYjl3f1frOjV/Ul2kaxq5g/
- b/UOv678JKCDASVokxG5GJifAnU7/kqRxdhcwfRkrD8RUfcsmiZOfyF
-X-Mailer: b4 0.12.3
-Message-ID: <20231206-ethtool_puts_impl-v5-1-5a2528e17bf8@google.com>
-Subject: [PATCH net-next v5 1/3] ethtool: Implement ethtool_puts()
-From: justinstitt@google.com
-To: "David S. Miller" <davem@davemloft.net>, Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>, Shay Agroskin <shayagr@amazon.com>,
- Arthur Kiyanovski <akiyano@amazon.com>, David Arinzon <darinzon@amazon.com>, Noam Dagan <ndagan@amazon.com>,
- Saeed Bishara <saeedb@amazon.com>, Rasesh Mody <rmody@marvell.com>,
- Sudarsana Kalluru <skalluru@marvell.com>, GR-Linux-NIC-Dev@marvell.com,
- Dimitris Michailidis <dmichail@fungible.com>, Yisen Zhuang <yisen.zhuang@huawei.com>,
- Salil Mehta <salil.mehta@huawei.com>, Jesse Brandeburg <jesse.brandeburg@intel.com>,
- Tony Nguyen <anthony.l.nguyen@intel.com>, Louis Peens <louis.peens@corigine.com>,
- Shannon Nelson <shannon.nelson@amd.com>, Brett Creeley <brett.creeley@amd.com>, drivers@pensando.io,
- "K. Y. Srinivasan" <kys@microsoft.com>, Haiyang Zhang <haiyangz@microsoft.com>, Wei Liu <wei.liu@kernel.org>,
- Dexuan Cui <decui@microsoft.com>, Ronak Doshi <doshir@vmware.com>,
- VMware PV-Drivers Reviewers <pv-drivers@vmware.com>, Andy Whitcroft <apw@canonical.com>, Joe Perches <joe@perches.com>,
- Dwaipayan Ray <dwaipayanray1@gmail.com>, Lukas Bulwahn <lukas.bulwahn@gmail.com>,
- Hauke Mehrtens <hauke@hauke-m.de>, Andrew Lunn <andrew@lunn.ch>,
- Florian Fainelli <f.fainelli@gmail.com>, Vladimir Oltean <olteanv@gmail.com>,
- "=?utf-8?q?Ar=C4=B1n=C3=A7_=C3=9CNAL?=" <arinc.unal@arinc9.com>, Daniel Golle <daniel@makrotopia.org>,
- Landen Chao <Landen.Chao@mediatek.com>, DENG Qingfang <dqfext@gmail.com>,
- Sean Wang <sean.wang@mediatek.com>, Matthias Brugger <matthias.bgg@gmail.com>,
- AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>,
- Linus Walleij <linus.walleij@linaro.org>,
- "=?utf-8?q?Alvin_=C5=A0ipraga?=" <alsi@bang-olufsen.dk>, Wei Fang <wei.fang@nxp.com>,
- Shenwei Wang <shenwei.wang@nxp.com>, Clark Wang <xiaoning.wang@nxp.com>,
- NXP Linux Team <linux-imx@nxp.com>, Lars Povlsen <lars.povlsen@microchip.com>,
- Steen Hegelund <Steen.Hegelund@microchip.com>, Daniel Machon <daniel.machon@microchip.com>,
- UNGLinuxDriver@microchip.com, Jiawen Wu <jiawenwu@trustnetic.com>,
- Mengyuan Lou <mengyuanlou@net-swift.com>, Heiner Kallweit <hkallweit1@gmail.com>,
- Russell King <linux@armlinux.org.uk>, Alexei Starovoitov <ast@kernel.org>,
- Daniel Borkmann <daniel@iogearbox.net>, Jesper Dangaard Brouer <hawk@kernel.org>,
- John Fastabend <john.fastabend@gmail.com>
-Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
- Nick Desaulniers <ndesaulniers@google.com>, Nathan Chancellor <nathan@kernel.org>,
- Kees Cook <keescook@chromium.org>, intel-wired-lan@lists.osuosl.org,
- oss-drivers@corigine.com, linux-hyperv@vger.kernel.org,
- linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org,
- bpf@vger.kernel.org, Justin Stitt <justinstitt@google.com>
-Content-Type: text/plain; charset="utf-8"
-
-Use strscpy() to implement ethtool_puts().
-
-Functionally the same as ethtool_sprintf() when it's used with two
-arguments or with just "%s" format specifier.
-
-Signed-off-by: Justin Stitt <justinstitt@google.com>
----
- include/linux/ethtool.h | 13 +++++++++++++
- net/ethtool/ioctl.c | 7 +++++++
- 2 files changed, 20 insertions(+)
-
---- a/include/linux/ethtool.h
-+++ b/include/linux/ethtool.h
-@@ -1052,4 +1052,17 @@ static inline int ethtool_mm_frag_size_m
- * next string.
- */
- extern __printf(2, 3) void ethtool_sprintf(u8 **data, const char *fmt, ...);
-+
-+/**
-+ * ethtool_puts - Write string to ethtool string data
-+ * @data: Pointer to a pointer to the start of string to update
-+ * @str: String to write
-+ *
-+ * Write string to *data without a trailing newline. Update *data
-+ * to point at start of next string.
-+ *
-+ * Prefer this function to ethtool_sprintf() when given only
-+ * two arguments or if @fmt is just "%s".
-+ */
-+extern void ethtool_puts(u8 **data, const char *str);
- #endif /* _LINUX_ETHTOOL_H */
---- a/net/ethtool/ioctl.c
-+++ b/net/ethtool/ioctl.c
-@@ -1994,6 +1994,13 @@ __printf(2, 3) void ethtool_sprintf(u8 *
- }
- EXPORT_SYMBOL(ethtool_sprintf);
-
-+void ethtool_puts(u8 **data, const char *str)
-+{
-+ strscpy(*data, str, ETH_GSTRING_LEN);
-+ *data += ETH_GSTRING_LEN;
-+}
-+EXPORT_SYMBOL(ethtool_puts);
-+
- static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
- {
- struct ethtool_value id;
+++ /dev/null
-From 1a7aa058bc92f0edae7a0d1ef1a7b05aec0c643a Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 24 Nov 2023 12:27:52 +0000
-Subject: [PATCH 1/7] net: phy: add possible interfaces
-
-Add a possible_interfaces member to struct phy_device to indicate which
-interfaces a clause 45 PHY may switch between depending on the media.
-This must be populated by the PHY driver by the time the .config_init()
-method completes according to the PHYs host-side configuration.
-
-For example, the Marvell 88x3310 PHY can switch between 10GBASE-R,
-5GBASE-R, 2500BASE-X, and SGMII on the host side depending on the media
-side speed, so all these interface modes are set in the
-possible_interfaces member.
-
-This allows phylib users (such as phylink) to know in advance which
-interface modes to expect, which allows them to appropriately restrict
-the advertised link modes according to the capabilities of other parts
-of the link.
-
-Tested-by: Luo Jie <quic_luoj@quicinc.com>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/E1r6VHk-00DDLN-I7@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phy_device.c | 2 ++
- include/linux/phy.h | 3 +++
- 2 files changed, 5 insertions(+)
-
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -1247,6 +1247,8 @@ int phy_init_hw(struct phy_device *phyde
- if (ret < 0)
- return ret;
-
-+ phy_interface_zero(phydev->possible_interfaces);
-+
- if (phydev->drv->config_init) {
- ret = phydev->drv->config_init(phydev);
- if (ret < 0)
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -609,6 +609,8 @@ struct macsec_ops;
- * @irq_rerun: Flag indicating interrupts occurred while PHY was suspended,
- * requiring a rerun of the interrupt handler after resume
- * @interface: enum phy_interface_t value
-+ * @possible_interfaces: bitmap if interface modes that the attached PHY
-+ * will switch between depending on media speed.
- * @skb: Netlink message for cable diagnostics
- * @nest: Netlink nest used for cable diagnostics
- * @ehdr: nNtlink header for cable diagnostics
-@@ -678,6 +680,7 @@ struct phy_device {
- u32 dev_flags;
-
- phy_interface_t interface;
-+ DECLARE_PHY_INTERFACE_MASK(possible_interfaces);
-
- /*
- * forced speed & duplex (no autoneg)
+++ /dev/null
-From 85631f5b33f2acce7d42dec1d0a062ab40de95b8 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Sun, 19 Nov 2023 21:07:43 +0000
-Subject: [PATCH 2/7] net: phylink: use for_each_set_bit()
-
-Use for_each_set_bit() rather than open coding the for() test_bit()
-loop.
-
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
-Link: https://lore.kernel.org/r/E1r4p15-00Cpxe-C7@rmk-PC.armlinux.org.uk
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/phy/phylink.c | 18 ++++++++----------
- 1 file changed, 8 insertions(+), 10 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -712,18 +712,16 @@ static int phylink_validate_mask(struct
- __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, };
- __ETHTOOL_DECLARE_LINK_MODE_MASK(s);
- struct phylink_link_state t;
-- int intf;
-+ int interface;
-
-- for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
-- if (test_bit(intf, interfaces)) {
-- linkmode_copy(s, supported);
-+ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) {
-+ linkmode_copy(s, supported);
-
-- t = *state;
-- t.interface = intf;
-- if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
-- linkmode_or(all_s, all_s, s);
-- linkmode_or(all_adv, all_adv, t.advertising);
-- }
-+ t = *state;
-+ t.interface = interface;
-+ if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
-+ linkmode_or(all_s, all_s, s);
-+ linkmode_or(all_adv, all_adv, t.advertising);
- }
- }
-
+++ /dev/null
-From d4788b4383ce5caeb4e68818357c81a02117a3f9 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 24 Nov 2023 12:28:19 +0000
-Subject: [PATCH 3/7] net: phylink: split out per-interface validation
-
-Split out the internals of phylink_validate_mask() to make the code
-easier to read.
-
-Tested-by: Luo Jie <quic_luoj@quicinc.com>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/E1r6VIB-00DDLr-7g@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 42 ++++++++++++++++++++++++++++-----------
- 1 file changed, 30 insertions(+), 12 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -704,26 +704,44 @@ static int phylink_validate_mac_and_pcs(
- return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
- }
-
-+static void phylink_validate_one(struct phylink *pl,
-+ const unsigned long *supported,
-+ const struct phylink_link_state *state,
-+ phy_interface_t interface,
-+ unsigned long *accum_supported,
-+ unsigned long *accum_advertising)
-+{
-+ __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_supported);
-+ struct phylink_link_state tmp_state;
-+
-+ linkmode_copy(tmp_supported, supported);
-+
-+ tmp_state = *state;
-+ tmp_state.interface = interface;
-+
-+ if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) {
-+ phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n",
-+ interface, phy_modes(interface),
-+ phy_rate_matching_to_str(tmp_state.rate_matching),
-+ __ETHTOOL_LINK_MODE_MASK_NBITS, tmp_supported);
-+
-+ linkmode_or(accum_supported, accum_supported, tmp_supported);
-+ linkmode_or(accum_advertising, accum_advertising,
-+ tmp_state.advertising);
-+ }
-+}
-+
- static int phylink_validate_mask(struct phylink *pl, unsigned long *supported,
- struct phylink_link_state *state,
- const unsigned long *interfaces)
- {
- __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, };
- __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, };
-- __ETHTOOL_DECLARE_LINK_MODE_MASK(s);
-- struct phylink_link_state t;
- int interface;
-
-- for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) {
-- linkmode_copy(s, supported);
--
-- t = *state;
-- t.interface = interface;
-- if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
-- linkmode_or(all_s, all_s, s);
-- linkmode_or(all_adv, all_adv, t.advertising);
-- }
-- }
-+ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
-+ phylink_validate_one(pl, supported, state, interface,
-+ all_s, all_adv);
-
- linkmode_copy(supported, all_s);
- linkmode_copy(state->advertising, all_adv);
+++ /dev/null
-From ce7273c31fadb3143fc80c96a72a42adc19c2757 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 24 Nov 2023 12:28:24 +0000
-Subject: [PATCH 4/7] net: phylink: pass PHY into phylink_validate_one()
-
-Pass the phy (if any) into phylink_validate_one() so that we can
-validate each interface with its rate matching setting.
-
-Tested-by: Luo Jie <quic_luoj@quicinc.com>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/E1r6VIG-00DDLx-Cb@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 7 +++++--
- 1 file changed, 5 insertions(+), 2 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -704,7 +704,7 @@ static int phylink_validate_mac_and_pcs(
- return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
- }
-
--static void phylink_validate_one(struct phylink *pl,
-+static void phylink_validate_one(struct phylink *pl, struct phy_device *phy,
- const unsigned long *supported,
- const struct phylink_link_state *state,
- phy_interface_t interface,
-@@ -719,6 +719,9 @@ static void phylink_validate_one(struct
- tmp_state = *state;
- tmp_state.interface = interface;
-
-+ if (phy)
-+ tmp_state.rate_matching = phy_get_rate_matching(phy, interface);
-+
- if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) {
- phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n",
- interface, phy_modes(interface),
-@@ -740,7 +743,7 @@ static int phylink_validate_mask(struct
- int interface;
-
- for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
-- phylink_validate_one(pl, supported, state, interface,
-+ phylink_validate_one(pl, NULL, supported, state, interface,
- all_s, all_adv);
-
- linkmode_copy(supported, all_s);
+++ /dev/null
-From c6fec66d3cd76d797f70b30f1511bed10ba45a96 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 24 Nov 2023 12:28:29 +0000
-Subject: [PATCH 5/7] net: phylink: pass PHY into phylink_validate_mask()
-
-Pass the phy (if any) into phylink_validate_mask() so that we can
-validate each interface with its rate matching setting.
-
-Tested-by: Luo Jie <quic_luoj@quicinc.com>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/E1r6VIL-00DDM3-HJ@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 11 +++++++----
- 1 file changed, 7 insertions(+), 4 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -734,7 +734,8 @@ static void phylink_validate_one(struct
- }
- }
-
--static int phylink_validate_mask(struct phylink *pl, unsigned long *supported,
-+static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy,
-+ unsigned long *supported,
- struct phylink_link_state *state,
- const unsigned long *interfaces)
- {
-@@ -743,7 +744,7 @@ static int phylink_validate_mask(struct
- int interface;
-
- for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
-- phylink_validate_one(pl, NULL, supported, state, interface,
-+ phylink_validate_one(pl, phy, supported, state, interface,
- all_s, all_adv);
-
- linkmode_copy(supported, all_s);
-@@ -758,7 +759,8 @@ static int phylink_validate(struct phyli
- const unsigned long *interfaces = pl->config->supported_interfaces;
-
- if (state->interface == PHY_INTERFACE_MODE_NA)
-- return phylink_validate_mask(pl, supported, state, interfaces);
-+ return phylink_validate_mask(pl, NULL, supported, state,
-+ interfaces);
-
- if (!test_bit(state->interface, interfaces))
- return -EINVAL;
-@@ -3194,7 +3196,8 @@ static int phylink_sfp_config_optical(st
- /* For all the interfaces that are supported, reduce the sfp_support
- * mask to only those link modes that can be supported.
- */
-- ret = phylink_validate_mask(pl, pl->sfp_support, &config, interfaces);
-+ ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config,
-+ interfaces);
- if (ret) {
- phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n",
- __ETHTOOL_LINK_MODE_MASK_NBITS, support);
+++ /dev/null
-From ee0e0ddb910e7e989b65a19d72b6435baa641fc7 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 24 Nov 2023 12:28:34 +0000
-Subject: [PATCH 6/7] net: phylink: split out PHY validation from
- phylink_bringup_phy()
-
-When bringing up a PHY, we need to work out which ethtool link modes it
-should support and advertise. Clause 22 PHYs operate in a single
-interface mode, which can be easily dealt with. However, clause 45 PHYs
-tend to switch interface mode depending on the media. We need more
-flexible validation at this point, so this patch splits out that code
-in preparation to changing it.
-
-Tested-by: Luo Jie <quic_luoj@quicinc.com>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/E1r6VIQ-00DDM9-LK@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 56 ++++++++++++++++++++++-----------------
- 1 file changed, 31 insertions(+), 25 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -1775,6 +1775,35 @@ static void phylink_phy_change(struct ph
- phylink_pause_to_str(pl->phy_state.pause));
- }
-
-+static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy,
-+ unsigned long *supported,
-+ struct phylink_link_state *state)
-+{
-+ /* Check whether we would use rate matching for the proposed interface
-+ * mode.
-+ */
-+ state->rate_matching = phy_get_rate_matching(phy, state->interface);
-+
-+ /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R,
-+ * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching.
-+ * For some interface modes (e.g. RXAUI, XAUI and USXGMII) switching
-+ * their Serdes is either unnecessary or not reasonable.
-+ *
-+ * For these which switch interface modes, we really need to know which
-+ * interface modes the PHY supports to properly work out which ethtool
-+ * linkmodes can be supported. For now, as a work-around, we validate
-+ * against all interface modes, which may lead to more ethtool link
-+ * modes being advertised than are actually supported.
-+ */
-+ if (phy->is_c45 && state->rate_matching == RATE_MATCH_NONE &&
-+ state->interface != PHY_INTERFACE_MODE_RXAUI &&
-+ state->interface != PHY_INTERFACE_MODE_XAUI &&
-+ state->interface != PHY_INTERFACE_MODE_USXGMII)
-+ state->interface = PHY_INTERFACE_MODE_NA;
-+
-+ return phylink_validate(pl, supported, state);
-+}
-+
- static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
- phy_interface_t interface)
- {
-@@ -1795,32 +1824,9 @@ static int phylink_bringup_phy(struct ph
- memset(&config, 0, sizeof(config));
- linkmode_copy(supported, phy->supported);
- linkmode_copy(config.advertising, phy->advertising);
-+ config.interface = interface;
-
-- /* Check whether we would use rate matching for the proposed interface
-- * mode.
-- */
-- config.rate_matching = phy_get_rate_matching(phy, interface);
--
-- /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R,
-- * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching.
-- * For some interface modes (e.g. RXAUI, XAUI and USXGMII) switching
-- * their Serdes is either unnecessary or not reasonable.
-- *
-- * For these which switch interface modes, we really need to know which
-- * interface modes the PHY supports to properly work out which ethtool
-- * linkmodes can be supported. For now, as a work-around, we validate
-- * against all interface modes, which may lead to more ethtool link
-- * modes being advertised than are actually supported.
-- */
-- if (phy->is_c45 && config.rate_matching == RATE_MATCH_NONE &&
-- interface != PHY_INTERFACE_MODE_RXAUI &&
-- interface != PHY_INTERFACE_MODE_XAUI &&
-- interface != PHY_INTERFACE_MODE_USXGMII)
-- config.interface = PHY_INTERFACE_MODE_NA;
-- else
-- config.interface = interface;
--
-- ret = phylink_validate(pl, supported, &config);
-+ ret = phylink_validate_phy(pl, phy, supported, &config);
- if (ret) {
- phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %pe\n",
- phy_modes(config.interface),
+++ /dev/null
-From 8f7a9799c5949f94ecc3acfd71b36437a7ade73b Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 24 Nov 2023 12:28:39 +0000
-Subject: [PATCH 7/7] net: phylink: use the PHY's possible_interfaces if
- populated
-
-Some PHYs such as Aquantia, Broadcom 84881, and Marvell 88X33x0 can
-switch between a set of interface types depending on the negotiated
-media speed, or can use rate adaption for some or all of these
-interface types.
-
-We currently assume that these are Clause 45 PHYs that are configured
-not to use a specific set of interface modes, which has worked so far,
-but is just a work-around. In this workaround, we validate using all
-interfaces that the MAC supports, which can lead to extra modes being
-advertised that can not be supported.
-
-To properly address this, switch to using the newly introduced PHY
-possible_interfaces bitmap which indicates which interface modes will
-be used by the PHY as configured. We calculate the union of the PHY's
-possible interfaces and MACs supported interfaces, checking that is
-non-empty. If the PHY is on a SFP, we further reduce the set by those
-which can be used on a SFP module, again checking that is non-empty.
-Finally, we validate the subset of interfaces, taking account of
-whether rate matching will be used for each individual interface mode.
-
-This becomes independent of whether the PHY is clause 22 or clause 45.
-
-It is encouraged that all PHYs that switch interface modes or use
-rate matching should populate phydev->possible_interfaces.
-
-Tested-by: Luo Jie <quic_luoj@quicinc.com>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/E1r6VIV-00DDMF-Pi@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 67 +++++++++++++++++++++++++++++++--------
- 1 file changed, 54 insertions(+), 13 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -121,6 +121,19 @@ do { \
- })
- #endif
-
-+static const phy_interface_t phylink_sfp_interface_preference[] = {
-+ PHY_INTERFACE_MODE_25GBASER,
-+ PHY_INTERFACE_MODE_USXGMII,
-+ PHY_INTERFACE_MODE_10GBASER,
-+ PHY_INTERFACE_MODE_5GBASER,
-+ PHY_INTERFACE_MODE_2500BASEX,
-+ PHY_INTERFACE_MODE_SGMII,
-+ PHY_INTERFACE_MODE_1000BASEX,
-+ PHY_INTERFACE_MODE_100BASEX,
-+};
-+
-+static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
-+
- /**
- * phylink_set_port_modes() - set the port type modes in the ethtool mask
- * @mask: ethtool link mode mask
-@@ -1779,6 +1792,47 @@ static int phylink_validate_phy(struct p
- unsigned long *supported,
- struct phylink_link_state *state)
- {
-+ DECLARE_PHY_INTERFACE_MASK(interfaces);
-+
-+ /* If the PHY provides a bitmap of the interfaces it will be using
-+ * depending on the negotiated media speeds, use this to validate
-+ * which ethtool link modes can be used.
-+ */
-+ if (!phy_interface_empty(phy->possible_interfaces)) {
-+ /* We only care about the union of the PHY's interfaces and
-+ * those which the host supports.
-+ */
-+ phy_interface_and(interfaces, phy->possible_interfaces,
-+ pl->config->supported_interfaces);
-+
-+ if (phy_interface_empty(interfaces)) {
-+ phylink_err(pl, "PHY has no common interfaces\n");
-+ return -EINVAL;
-+ }
-+
-+ if (phy_on_sfp(phy)) {
-+ /* If the PHY is on a SFP, limit the interfaces to
-+ * those that can be used with a SFP module.
-+ */
-+ phy_interface_and(interfaces, interfaces,
-+ phylink_sfp_interfaces);
-+
-+ if (phy_interface_empty(interfaces)) {
-+ phylink_err(pl, "SFP PHY's possible interfaces becomes empty\n");
-+ return -EINVAL;
-+ }
-+ }
-+
-+ phylink_dbg(pl, "PHY %s uses interfaces %*pbl, validating %*pbl\n",
-+ phydev_name(phy),
-+ (int)PHY_INTERFACE_MODE_MAX,
-+ phy->possible_interfaces,
-+ (int)PHY_INTERFACE_MODE_MAX, interfaces);
-+
-+ return phylink_validate_mask(pl, phy, supported, state,
-+ interfaces);
-+ }
-+
- /* Check whether we would use rate matching for the proposed interface
- * mode.
- */
-@@ -3047,19 +3101,6 @@ static void phylink_sfp_detach(void *ups
- pl->netdev->sfp_bus = NULL;
- }
-
--static const phy_interface_t phylink_sfp_interface_preference[] = {
-- PHY_INTERFACE_MODE_25GBASER,
-- PHY_INTERFACE_MODE_USXGMII,
-- PHY_INTERFACE_MODE_10GBASER,
-- PHY_INTERFACE_MODE_5GBASER,
-- PHY_INTERFACE_MODE_2500BASEX,
-- PHY_INTERFACE_MODE_SGMII,
-- PHY_INTERFACE_MODE_1000BASEX,
-- PHY_INTERFACE_MODE_100BASEX,
--};
--
--static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
--
- static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl,
- const unsigned long *intf)
- {
+++ /dev/null
-From 5c5b0c444be3e851046f1c1074459b8d15d2a0f9 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Tue, 27 Feb 2024 18:54:21 +0100
-Subject: [PATCH 1/2] net: dsa: mv88e6xxx: rename
- mv88e6xxx_g2_scratch_gpio_set_smi
-
-The name mv88e6xxx_g2_scratch_gpio_set_smi is a bit ambiguous as it appears
-to only be applicable to the 6390 family, so lets rename it to
-mv88e6390_g2_scratch_gpio_set_smi to make it more obvious.
-
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mv88e6xxx/chip.c | 2 +-
- drivers/net/dsa/mv88e6xxx/global2.h | 2 +-
- drivers/net/dsa/mv88e6xxx/global2_scratch.c | 4 ++--
- 3 files changed, 4 insertions(+), 4 deletions(-)
-
---- a/drivers/net/dsa/mv88e6xxx/chip.c
-+++ b/drivers/net/dsa/mv88e6xxx/chip.c
-@@ -3699,7 +3699,7 @@ static int mv88e6xxx_mdio_register(struc
-
- if (external) {
- mv88e6xxx_reg_lock(chip);
-- err = mv88e6xxx_g2_scratch_gpio_set_smi(chip, true);
-+ err = mv88e6390_g2_scratch_gpio_set_smi(chip, true);
- mv88e6xxx_reg_unlock(chip);
-
- if (err)
---- a/drivers/net/dsa/mv88e6xxx/global2.h
-+++ b/drivers/net/dsa/mv88e6xxx/global2.h
-@@ -378,7 +378,7 @@ extern const struct mv88e6xxx_avb_ops mv
-
- extern const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops;
-
--int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
-+int mv88e6390_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
- bool external);
- int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port);
- int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin);
---- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c
-+++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c
-@@ -240,7 +240,7 @@ const struct mv88e6xxx_gpio_ops mv88e635
- };
-
- /**
-- * mv88e6xxx_g2_scratch_gpio_set_smi - set gpio muxing for external smi
-+ * mv88e6390_g2_scratch_gpio_set_smi - set gpio muxing for external smi
- * @chip: chip private data
- * @external: set mux for external smi, or free for gpio usage
- *
-@@ -248,7 +248,7 @@ const struct mv88e6xxx_gpio_ops mv88e635
- * an external SMI interface, or they may be made free for other
- * GPIO uses.
- */
--int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
-+int mv88e6390_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
- bool external)
- {
- int misc_cfg = MV88E6352_G2_SCRATCH_MISC_CFG;
+++ /dev/null
-From e3ab3267a0bbedc37725bb845a332ec33b247263 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Tue, 27 Feb 2024 18:54:22 +0100
-Subject: [PATCH 2/2] net: dsa: mv88e6xxx: add Amethyst specific SMI GPIO
- function
-
-The existing mv88e6390_g2_scratch_gpio_set_smi() cannot be used on the
-88E6393X as it requires certain P0_MODE, it also checks the CPU mode
-as it impacts the bit setting value.
-
-This is all irrelevant for Amethyst (MV88E6191X/6193X/6393X) as only
-the default value of the SMI_PHY Config bit is set to CPU_MGD bootstrap
-pin value but it can be changed without restrictions so that GPIO pins
-9 and 10 are used as SMI pins.
-
-So, introduce Amethyst specific function and call that if the Amethyst
-family wants to setup the external PHY.
-
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/mv88e6xxx/chip.c | 5 +++-
- drivers/net/dsa/mv88e6xxx/global2.h | 2 ++
- drivers/net/dsa/mv88e6xxx/global2_scratch.c | 31 +++++++++++++++++++++
- 3 files changed, 37 insertions(+), 1 deletion(-)
-
---- a/drivers/net/dsa/mv88e6xxx/chip.c
-+++ b/drivers/net/dsa/mv88e6xxx/chip.c
-@@ -3699,7 +3699,10 @@ static int mv88e6xxx_mdio_register(struc
-
- if (external) {
- mv88e6xxx_reg_lock(chip);
-- err = mv88e6390_g2_scratch_gpio_set_smi(chip, true);
-+ if (chip->info->family == MV88E6XXX_FAMILY_6393)
-+ err = mv88e6393x_g2_scratch_gpio_set_smi(chip, true);
-+ else
-+ err = mv88e6390_g2_scratch_gpio_set_smi(chip, true);
- mv88e6xxx_reg_unlock(chip);
-
- if (err)
---- a/drivers/net/dsa/mv88e6xxx/global2.h
-+++ b/drivers/net/dsa/mv88e6xxx/global2.h
-@@ -380,6 +380,8 @@ extern const struct mv88e6xxx_gpio_ops m
-
- int mv88e6390_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
- bool external);
-+int mv88e6393x_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
-+ bool external);
- int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port);
- int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin);
- int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip, u16 *stats);
---- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c
-+++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c
-@@ -291,6 +291,37 @@ int mv88e6390_g2_scratch_gpio_set_smi(st
- }
-
- /**
-+ * mv88e6393x_g2_scratch_gpio_set_smi - set gpio muxing for external smi
-+ * @chip: chip private data
-+ * @external: set mux for external smi, or free for gpio usage
-+ *
-+ * MV88E6191X/6193X/6393X GPIO pins 9 and 10 can be configured as an
-+ * external SMI interface or as regular GPIO-s.
-+ *
-+ * They however have a different register layout then the existing
-+ * function.
-+ */
-+
-+int mv88e6393x_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
-+ bool external)
-+{
-+ int misc_cfg = MV88E6352_G2_SCRATCH_MISC_CFG;
-+ int err;
-+ u8 val;
-+
-+ err = mv88e6xxx_g2_scratch_read(chip, misc_cfg, &val);
-+ if (err)
-+ return err;
-+
-+ if (external)
-+ val &= ~MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI;
-+ else
-+ val |= MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI;
-+
-+ return mv88e6xxx_g2_scratch_write(chip, misc_cfg, val);
-+}
-+
-+/**
- * mv88e6352_g2_scratch_port_has_serdes - indicate if a port can have a serdes
- * @chip: chip private data
- * @port: port number to check for serdes
+++ /dev/null
-From 33f4336cbd32c21717b60d013693a0bd51a27db6 Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Fri, 9 Feb 2024 02:03:37 -0300
-Subject: net: dsa: realtek: drop cleanup from realtek_ops
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-It was never used and never referenced.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/realtek.h | 1 -
- 1 file changed, 1 deletion(-)
-
---- a/drivers/net/dsa/realtek/realtek.h
-+++ b/drivers/net/dsa/realtek/realtek.h
-@@ -91,7 +91,6 @@ struct realtek_ops {
- int (*detect)(struct realtek_priv *priv);
- int (*reset_chip)(struct realtek_priv *priv);
- int (*setup)(struct realtek_priv *priv);
-- void (*cleanup)(struct realtek_priv *priv);
- int (*get_mib_counter)(struct realtek_priv *priv,
- int port,
- struct rtl8366_mib_counter *mib,
+++ /dev/null
-From ded3813b44fe11a3bbd2c9d7df8870e8c19a7ccd Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Fri, 9 Feb 2024 02:03:38 -0300
-Subject: net: dsa: realtek: introduce REALTEK_DSA namespace
-
-Create a namespace to group the exported symbols.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/realtek-mdio.c | 1 +
- drivers/net/dsa/realtek/realtek-smi.c | 1 +
- drivers/net/dsa/realtek/rtl8365mb.c | 1 +
- drivers/net/dsa/realtek/rtl8366-core.c | 22 +++++++++++-----------
- drivers/net/dsa/realtek/rtl8366rb.c | 1 +
- 5 files changed, 15 insertions(+), 11 deletions(-)
-
---- a/drivers/net/dsa/realtek/realtek-mdio.c
-+++ b/drivers/net/dsa/realtek/realtek-mdio.c
-@@ -288,3 +288,4 @@ mdio_module_driver(realtek_mdio_driver);
- MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
- MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface");
- MODULE_LICENSE("GPL");
-+MODULE_IMPORT_NS(REALTEK_DSA);
---- a/drivers/net/dsa/realtek/realtek-smi.c
-+++ b/drivers/net/dsa/realtek/realtek-smi.c
-@@ -565,3 +565,4 @@ module_platform_driver(realtek_smi_drive
- MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
- MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface");
- MODULE_LICENSE("GPL");
-+MODULE_IMPORT_NS(REALTEK_DSA);
---- a/drivers/net/dsa/realtek/rtl8365mb.c
-+++ b/drivers/net/dsa/realtek/rtl8365mb.c
-@@ -2178,3 +2178,4 @@ EXPORT_SYMBOL_GPL(rtl8365mb_variant);
- MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>");
- MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch");
- MODULE_LICENSE("GPL");
-+MODULE_IMPORT_NS(REALTEK_DSA);
---- a/drivers/net/dsa/realtek/rtl8366-core.c
-+++ b/drivers/net/dsa/realtek/rtl8366-core.c
-@@ -34,7 +34,7 @@ int rtl8366_mc_is_used(struct realtek_pr
-
- return 0;
- }
--EXPORT_SYMBOL_GPL(rtl8366_mc_is_used);
-+EXPORT_SYMBOL_NS_GPL(rtl8366_mc_is_used, REALTEK_DSA);
-
- /**
- * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration
-@@ -187,7 +187,7 @@ int rtl8366_set_vlan(struct realtek_priv
-
- return ret;
- }
--EXPORT_SYMBOL_GPL(rtl8366_set_vlan);
-+EXPORT_SYMBOL_NS_GPL(rtl8366_set_vlan, REALTEK_DSA);
-
- int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port,
- unsigned int vid)
-@@ -217,7 +217,7 @@ int rtl8366_set_pvid(struct realtek_priv
-
- return 0;
- }
--EXPORT_SYMBOL_GPL(rtl8366_set_pvid);
-+EXPORT_SYMBOL_NS_GPL(rtl8366_set_pvid, REALTEK_DSA);
-
- int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable)
- {
-@@ -243,7 +243,7 @@ int rtl8366_enable_vlan4k(struct realtek
- priv->vlan4k_enabled = enable;
- return 0;
- }
--EXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k);
-+EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan4k, REALTEK_DSA);
-
- int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable)
- {
-@@ -265,7 +265,7 @@ int rtl8366_enable_vlan(struct realtek_p
-
- return ret;
- }
--EXPORT_SYMBOL_GPL(rtl8366_enable_vlan);
-+EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan, REALTEK_DSA);
-
- int rtl8366_reset_vlan(struct realtek_priv *priv)
- {
-@@ -290,7 +290,7 @@ int rtl8366_reset_vlan(struct realtek_pr
-
- return 0;
- }
--EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
-+EXPORT_SYMBOL_NS_GPL(rtl8366_reset_vlan, REALTEK_DSA);
-
- int rtl8366_vlan_add(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan,
-@@ -345,7 +345,7 @@ int rtl8366_vlan_add(struct dsa_switch *
-
- return 0;
- }
--EXPORT_SYMBOL_GPL(rtl8366_vlan_add);
-+EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_add, REALTEK_DSA);
-
- int rtl8366_vlan_del(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan)
-@@ -389,7 +389,7 @@ int rtl8366_vlan_del(struct dsa_switch *
-
- return 0;
- }
--EXPORT_SYMBOL_GPL(rtl8366_vlan_del);
-+EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_del, REALTEK_DSA);
-
- void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset,
- uint8_t *data)
-@@ -407,7 +407,7 @@ void rtl8366_get_strings(struct dsa_swit
- mib->name, ETH_GSTRING_LEN);
- }
- }
--EXPORT_SYMBOL_GPL(rtl8366_get_strings);
-+EXPORT_SYMBOL_NS_GPL(rtl8366_get_strings, REALTEK_DSA);
-
- int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset)
- {
-@@ -421,7 +421,7 @@ int rtl8366_get_sset_count(struct dsa_sw
-
- return priv->num_mib_counters;
- }
--EXPORT_SYMBOL_GPL(rtl8366_get_sset_count);
-+EXPORT_SYMBOL_NS_GPL(rtl8366_get_sset_count, REALTEK_DSA);
-
- void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
- {
-@@ -445,4 +445,4 @@ void rtl8366_get_ethtool_stats(struct ds
- data[i] = mibvalue;
- }
- }
--EXPORT_SYMBOL_GPL(rtl8366_get_ethtool_stats);
-+EXPORT_SYMBOL_NS_GPL(rtl8366_get_ethtool_stats, REALTEK_DSA);
---- a/drivers/net/dsa/realtek/rtl8366rb.c
-+++ b/drivers/net/dsa/realtek/rtl8366rb.c
-@@ -1852,3 +1852,4 @@ EXPORT_SYMBOL_GPL(rtl8366rb_variant);
- MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
- MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch");
- MODULE_LICENSE("GPL");
-+MODULE_IMPORT_NS(REALTEK_DSA);
+++ /dev/null
-From bce254b839abe67577bebdef0838796af409c229 Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Fri, 9 Feb 2024 02:03:39 -0300
-Subject: net: dsa: realtek: convert variants into real drivers
-
-Previously, the interface modules realtek-smi and realtek-mdio served as
-a platform and an MDIO driver, respectively. Each interface module
-redundantly specified the same compatible strings for both variants and
-referenced symbols from the variants.
-
-Now, each variant module has been transformed into a unified driver
-serving both as a platform and an MDIO driver. This modification
-reverses the relationship between the interface and variant modules,
-with the variant module now utilizing symbols from the interface
-modules.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/Kconfig | 20 +++----
- drivers/net/dsa/realtek/realtek-mdio.c | 68 +++++++++++++++---------
- drivers/net/dsa/realtek/realtek-mdio.h | 48 +++++++++++++++++
- drivers/net/dsa/realtek/realtek-smi.c | 73 +++++++++++++++-----------
- drivers/net/dsa/realtek/realtek-smi.h | 48 +++++++++++++++++
- drivers/net/dsa/realtek/rtl8365mb.c | 54 ++++++++++++++++++-
- drivers/net/dsa/realtek/rtl8366rb.c | 54 ++++++++++++++++++-
- 7 files changed, 292 insertions(+), 73 deletions(-)
- create mode 100644 drivers/net/dsa/realtek/realtek-mdio.h
- create mode 100644 drivers/net/dsa/realtek/realtek-smi.h
-
---- a/drivers/net/dsa/realtek/Kconfig
-+++ b/drivers/net/dsa/realtek/Kconfig
-@@ -16,37 +16,29 @@ menuconfig NET_DSA_REALTEK
- if NET_DSA_REALTEK
-
- config NET_DSA_REALTEK_MDIO
-- tristate "Realtek MDIO interface driver"
-+ tristate "Realtek MDIO interface support"
- depends on OF
-- depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB
-- depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB
-- depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB
- help
- Select to enable support for registering switches configured
- through MDIO.
-
- config NET_DSA_REALTEK_SMI
-- tristate "Realtek SMI interface driver"
-+ tristate "Realtek SMI interface support"
- depends on OF
-- depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB
-- depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB
-- depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB
- help
- Select to enable support for registering switches connected
- through SMI.
-
- config NET_DSA_REALTEK_RTL8365MB
-- tristate "Realtek RTL8365MB switch subdriver"
-- imply NET_DSA_REALTEK_SMI
-- imply NET_DSA_REALTEK_MDIO
-+ tristate "Realtek RTL8365MB switch driver"
-+ depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO
- select NET_DSA_TAG_RTL8_4
- help
- Select to enable support for Realtek RTL8365MB-VC and RTL8367S.
-
- config NET_DSA_REALTEK_RTL8366RB
-- tristate "Realtek RTL8366RB switch subdriver"
-- imply NET_DSA_REALTEK_SMI
-- imply NET_DSA_REALTEK_MDIO
-+ tristate "Realtek RTL8366RB switch driver"
-+ depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO
- select NET_DSA_TAG_RTL4_A
- help
- Select to enable support for Realtek RTL8366RB.
---- a/drivers/net/dsa/realtek/realtek-mdio.c
-+++ b/drivers/net/dsa/realtek/realtek-mdio.c
-@@ -25,6 +25,7 @@
- #include <linux/regmap.h>
-
- #include "realtek.h"
-+#include "realtek-mdio.h"
-
- /* Read/write via mdiobus */
- #define REALTEK_MDIO_CTRL0_REG 31
-@@ -140,7 +141,19 @@ static const struct regmap_config realte
- .disable_locking = true,
- };
-
--static int realtek_mdio_probe(struct mdio_device *mdiodev)
-+/**
-+ * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch
-+ * @mdiodev: mdio_device to probe on.
-+ *
-+ * This function should be used as the .probe in an mdio_driver. It
-+ * initializes realtek_priv and read data from the device-tree node. The switch
-+ * is hard reset if a method is provided. It checks the switch chip ID and,
-+ * finally, a DSA switch is registered.
-+ *
-+ * Context: Can sleep. Takes and releases priv->map_lock.
-+ * Return: Returns 0 on success, a negative error on failure.
-+ */
-+int realtek_mdio_probe(struct mdio_device *mdiodev)
- {
- struct realtek_priv *priv;
- struct device *dev = &mdiodev->dev;
-@@ -235,8 +248,20 @@ static int realtek_mdio_probe(struct mdi
-
- return 0;
- }
-+EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, REALTEK_DSA);
-
--static void realtek_mdio_remove(struct mdio_device *mdiodev)
-+/**
-+ * realtek_mdio_remove() - Remove the driver of an MDIO-connected switch
-+ * @mdiodev: mdio_device to be removed.
-+ *
-+ * This function should be used as the .remove_new in an mdio_driver. First
-+ * it unregisters the DSA switch and cleans internal data. If a method is
-+ * provided, the hard reset is asserted to avoid traffic leakage.
-+ *
-+ * Context: Can sleep.
-+ * Return: Nothing.
-+ */
-+void realtek_mdio_remove(struct mdio_device *mdiodev)
- {
- struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev);
-
-@@ -249,8 +274,21 @@ static void realtek_mdio_remove(struct m
- if (priv->reset)
- gpiod_set_value(priv->reset, 1);
- }
-+EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA);
-
--static void realtek_mdio_shutdown(struct mdio_device *mdiodev)
-+/**
-+ * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch
-+ * @mdiodev: mdio_device shutting down.
-+ *
-+ * This function should be used as the .shutdown in an mdio_driver. It shuts
-+ * down the DSA switch and cleans the platform driver data, to prevent
-+ * realtek_mdio_remove() from running afterwards, which is possible if the
-+ * parent bus implements its own .shutdown() as .remove().
-+ *
-+ * Context: Can sleep.
-+ * Return: Nothing.
-+ */
-+void realtek_mdio_shutdown(struct mdio_device *mdiodev)
- {
- struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev);
-
-@@ -261,29 +299,7 @@ static void realtek_mdio_shutdown(struct
-
- dev_set_drvdata(&mdiodev->dev, NULL);
- }
--
--static const struct of_device_id realtek_mdio_of_match[] = {
--#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB)
-- { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, },
--#endif
--#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB)
-- { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, },
--#endif
-- { /* sentinel */ },
--};
--MODULE_DEVICE_TABLE(of, realtek_mdio_of_match);
--
--static struct mdio_driver realtek_mdio_driver = {
-- .mdiodrv.driver = {
-- .name = "realtek-mdio",
-- .of_match_table = realtek_mdio_of_match,
-- },
-- .probe = realtek_mdio_probe,
-- .remove = realtek_mdio_remove,
-- .shutdown = realtek_mdio_shutdown,
--};
--
--mdio_module_driver(realtek_mdio_driver);
-+EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);
-
- MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
- MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface");
---- /dev/null
-+++ b/drivers/net/dsa/realtek/realtek-mdio.h
-@@ -0,0 +1,48 @@
-+/* SPDX-License-Identifier: GPL-2.0+ */
-+
-+#ifndef _REALTEK_MDIO_H
-+#define _REALTEK_MDIO_H
-+
-+#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO)
-+
-+static inline int realtek_mdio_driver_register(struct mdio_driver *drv)
-+{
-+ return mdio_driver_register(drv);
-+}
-+
-+static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv)
-+{
-+ mdio_driver_unregister(drv);
-+}
-+
-+int realtek_mdio_probe(struct mdio_device *mdiodev);
-+void realtek_mdio_remove(struct mdio_device *mdiodev);
-+void realtek_mdio_shutdown(struct mdio_device *mdiodev);
-+
-+#else /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) */
-+
-+static inline int realtek_mdio_driver_register(struct mdio_driver *drv)
-+{
-+ return 0;
-+}
-+
-+static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv)
-+{
-+}
-+
-+static inline int realtek_mdio_probe(struct mdio_device *mdiodev)
-+{
-+ return -ENOENT;
-+}
-+
-+static inline void realtek_mdio_remove(struct mdio_device *mdiodev)
-+{
-+}
-+
-+static inline void realtek_mdio_shutdown(struct mdio_device *mdiodev)
-+{
-+}
-+
-+#endif /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) */
-+
-+#endif /* _REALTEK_MDIO_H */
---- a/drivers/net/dsa/realtek/realtek-smi.c
-+++ b/drivers/net/dsa/realtek/realtek-smi.c
-@@ -40,6 +40,7 @@
- #include <linux/if_bridge.h>
-
- #include "realtek.h"
-+#include "realtek-smi.h"
-
- #define REALTEK_SMI_ACK_RETRY_COUNT 5
-
-@@ -408,7 +409,19 @@ err_put_node:
- return ret;
- }
-
--static int realtek_smi_probe(struct platform_device *pdev)
-+/**
-+ * realtek_smi_probe() - Probe a platform device for an SMI-connected switch
-+ * @pdev: platform_device to probe on.
-+ *
-+ * This function should be used as the .probe in a platform_driver. It
-+ * initializes realtek_priv and read data from the device-tree node. The switch
-+ * is hard reset if a method is provided. It checks the switch chip ID and,
-+ * finally, a DSA switch is registered.
-+ *
-+ * Context: Can sleep. Takes and releases priv->map_lock.
-+ * Return: Returns 0 on success, a negative error on failure.
-+ */
-+int realtek_smi_probe(struct platform_device *pdev)
- {
- const struct realtek_variant *var;
- struct device *dev = &pdev->dev;
-@@ -505,8 +518,20 @@ static int realtek_smi_probe(struct plat
- }
- return 0;
- }
-+EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA);
-
--static void realtek_smi_remove(struct platform_device *pdev)
-+/**
-+ * realtek_smi_remove() - Remove the driver of a SMI-connected switch
-+ * @pdev: platform_device to be removed.
-+ *
-+ * This function should be used as the .remove_new in a platform_driver. First
-+ * it unregisters the DSA switch and cleans internal data. If a method is
-+ * provided, the hard reset is asserted to avoid traffic leakage.
-+ *
-+ * Context: Can sleep.
-+ * Return: Nothing.
-+ */
-+void realtek_smi_remove(struct platform_device *pdev)
- {
- struct realtek_priv *priv = platform_get_drvdata(pdev);
-
-@@ -521,8 +546,21 @@ static void realtek_smi_remove(struct pl
- if (priv->reset)
- gpiod_set_value(priv->reset, 1);
- }
-+EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA);
-
--static void realtek_smi_shutdown(struct platform_device *pdev)
-+/**
-+ * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch
-+ * @pdev: platform_device shutting down.
-+ *
-+ * This function should be used as the .shutdown in a platform_driver. It shuts
-+ * down the DSA switch and cleans the platform driver data, to prevent
-+ * realtek_smi_remove() from running afterwards, which is possible if the
-+ * parent bus implements its own .shutdown() as .remove().
-+ *
-+ * Context: Can sleep.
-+ * Return: Nothing.
-+ */
-+void realtek_smi_shutdown(struct platform_device *pdev)
- {
- struct realtek_priv *priv = platform_get_drvdata(pdev);
-
-@@ -533,34 +571,7 @@ static void realtek_smi_shutdown(struct
-
- platform_set_drvdata(pdev, NULL);
- }
--
--static const struct of_device_id realtek_smi_of_match[] = {
--#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB)
-- {
-- .compatible = "realtek,rtl8366rb",
-- .data = &rtl8366rb_variant,
-- },
--#endif
--#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB)
-- {
-- .compatible = "realtek,rtl8365mb",
-- .data = &rtl8365mb_variant,
-- },
--#endif
-- { /* sentinel */ },
--};
--MODULE_DEVICE_TABLE(of, realtek_smi_of_match);
--
--static struct platform_driver realtek_smi_driver = {
-- .driver = {
-- .name = "realtek-smi",
-- .of_match_table = realtek_smi_of_match,
-- },
-- .probe = realtek_smi_probe,
-- .remove_new = realtek_smi_remove,
-- .shutdown = realtek_smi_shutdown,
--};
--module_platform_driver(realtek_smi_driver);
-+EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA);
-
- MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
- MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface");
---- /dev/null
-+++ b/drivers/net/dsa/realtek/realtek-smi.h
-@@ -0,0 +1,48 @@
-+/* SPDX-License-Identifier: GPL-2.0+ */
-+
-+#ifndef _REALTEK_SMI_H
-+#define _REALTEK_SMI_H
-+
-+#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI)
-+
-+static inline int realtek_smi_driver_register(struct platform_driver *drv)
-+{
-+ return platform_driver_register(drv);
-+}
-+
-+static inline void realtek_smi_driver_unregister(struct platform_driver *drv)
-+{
-+ platform_driver_unregister(drv);
-+}
-+
-+int realtek_smi_probe(struct platform_device *pdev);
-+void realtek_smi_remove(struct platform_device *pdev);
-+void realtek_smi_shutdown(struct platform_device *pdev);
-+
-+#else /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) */
-+
-+static inline int realtek_smi_driver_register(struct platform_driver *drv)
-+{
-+ return 0;
-+}
-+
-+static inline void realtek_smi_driver_unregister(struct platform_driver *drv)
-+{
-+}
-+
-+static inline int realtek_smi_probe(struct platform_device *pdev)
-+{
-+ return -ENOENT;
-+}
-+
-+static inline void realtek_smi_remove(struct platform_device *pdev)
-+{
-+}
-+
-+static inline void realtek_smi_shutdown(struct platform_device *pdev)
-+{
-+}
-+
-+#endif /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) */
-+
-+#endif /* _REALTEK_SMI_H */
---- a/drivers/net/dsa/realtek/rtl8365mb.c
-+++ b/drivers/net/dsa/realtek/rtl8365mb.c
-@@ -101,6 +101,8 @@
- #include <linux/if_vlan.h>
-
- #include "realtek.h"
-+#include "realtek-smi.h"
-+#include "realtek-mdio.h"
-
- /* Family-specific data and limits */
- #define RTL8365MB_PHYADDRMAX 7
-@@ -2173,7 +2175,57 @@ const struct realtek_variant rtl8365mb_v
- .cmd_write = 0xb8,
- .chip_data_sz = sizeof(struct rtl8365mb),
- };
--EXPORT_SYMBOL_GPL(rtl8365mb_variant);
-+
-+static const struct of_device_id rtl8365mb_of_match[] = {
-+ { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, },
-+ { /* sentinel */ },
-+};
-+MODULE_DEVICE_TABLE(of, rtl8365mb_of_match);
-+
-+static struct platform_driver rtl8365mb_smi_driver = {
-+ .driver = {
-+ .name = "rtl8365mb-smi",
-+ .of_match_table = rtl8365mb_of_match,
-+ },
-+ .probe = realtek_smi_probe,
-+ .remove_new = realtek_smi_remove,
-+ .shutdown = realtek_smi_shutdown,
-+};
-+
-+static struct mdio_driver rtl8365mb_mdio_driver = {
-+ .mdiodrv.driver = {
-+ .name = "rtl8365mb-mdio",
-+ .of_match_table = rtl8365mb_of_match,
-+ },
-+ .probe = realtek_mdio_probe,
-+ .remove = realtek_mdio_remove,
-+ .shutdown = realtek_mdio_shutdown,
-+};
-+
-+static int rtl8365mb_init(void)
-+{
-+ int ret;
-+
-+ ret = realtek_mdio_driver_register(&rtl8365mb_mdio_driver);
-+ if (ret)
-+ return ret;
-+
-+ ret = realtek_smi_driver_register(&rtl8365mb_smi_driver);
-+ if (ret) {
-+ realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver);
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+module_init(rtl8365mb_init);
-+
-+static void __exit rtl8365mb_exit(void)
-+{
-+ realtek_smi_driver_unregister(&rtl8365mb_smi_driver);
-+ realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver);
-+}
-+module_exit(rtl8365mb_exit);
-
- MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>");
- MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch");
---- a/drivers/net/dsa/realtek/rtl8366rb.c
-+++ b/drivers/net/dsa/realtek/rtl8366rb.c
-@@ -22,6 +22,8 @@
- #include <linux/regmap.h>
-
- #include "realtek.h"
-+#include "realtek-smi.h"
-+#include "realtek-mdio.h"
-
- #define RTL8366RB_PORT_NUM_CPU 5
- #define RTL8366RB_NUM_PORTS 6
-@@ -1847,7 +1849,57 @@ const struct realtek_variant rtl8366rb_v
- .cmd_write = 0xa8,
- .chip_data_sz = sizeof(struct rtl8366rb),
- };
--EXPORT_SYMBOL_GPL(rtl8366rb_variant);
-+
-+static const struct of_device_id rtl8366rb_of_match[] = {
-+ { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, },
-+ { /* sentinel */ },
-+};
-+MODULE_DEVICE_TABLE(of, rtl8366rb_of_match);
-+
-+static struct platform_driver rtl8366rb_smi_driver = {
-+ .driver = {
-+ .name = "rtl8366rb-smi",
-+ .of_match_table = rtl8366rb_of_match,
-+ },
-+ .probe = realtek_smi_probe,
-+ .remove_new = realtek_smi_remove,
-+ .shutdown = realtek_smi_shutdown,
-+};
-+
-+static struct mdio_driver rtl8366rb_mdio_driver = {
-+ .mdiodrv.driver = {
-+ .name = "rtl8366rb-mdio",
-+ .of_match_table = rtl8366rb_of_match,
-+ },
-+ .probe = realtek_mdio_probe,
-+ .remove = realtek_mdio_remove,
-+ .shutdown = realtek_mdio_shutdown,
-+};
-+
-+static int rtl8366rb_init(void)
-+{
-+ int ret;
-+
-+ ret = realtek_mdio_driver_register(&rtl8366rb_mdio_driver);
-+ if (ret)
-+ return ret;
-+
-+ ret = realtek_smi_driver_register(&rtl8366rb_smi_driver);
-+ if (ret) {
-+ realtek_mdio_driver_unregister(&rtl8366rb_mdio_driver);
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+module_init(rtl8366rb_init);
-+
-+static void __exit rtl8366rb_exit(void)
-+{
-+ realtek_smi_driver_unregister(&rtl8366rb_smi_driver);
-+ realtek_mdio_driver_unregister(&rtl8366rb_mdio_driver);
-+}
-+module_exit(rtl8366rb_exit);
-
- MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
- MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch");
+++ /dev/null
-From 4667a1db2f550d23e01ba655fce331196ead6e92 Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Fri, 9 Feb 2024 02:03:40 -0300
-Subject: net: dsa: realtek: keep variant reference in
- realtek_priv
-
-Instead of copying values from the variant, we can keep a reference in
-realtek_priv.
-
-This is a preliminary change for sharing code betwen interfaces. It will
-allow to move most of the probe into a common module while still allow
-code specific to each interface to read variant fields.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/realtek-mdio.c | 4 +---
- drivers/net/dsa/realtek/realtek-smi.c | 10 ++++------
- drivers/net/dsa/realtek/realtek.h | 5 ++---
- 3 files changed, 7 insertions(+), 12 deletions(-)
-
---- a/drivers/net/dsa/realtek/realtek-mdio.c
-+++ b/drivers/net/dsa/realtek/realtek-mdio.c
-@@ -196,9 +196,7 @@ int realtek_mdio_probe(struct mdio_devic
- priv->dev = &mdiodev->dev;
- priv->chip_data = (void *)priv + sizeof(*priv);
-
-- priv->clk_delay = var->clk_delay;
-- priv->cmd_read = var->cmd_read;
-- priv->cmd_write = var->cmd_write;
-+ priv->variant = var;
- priv->ops = var->ops;
-
- priv->write_reg_noack = realtek_mdio_write;
---- a/drivers/net/dsa/realtek/realtek-smi.c
-+++ b/drivers/net/dsa/realtek/realtek-smi.c
-@@ -46,7 +46,7 @@
-
- static inline void realtek_smi_clk_delay(struct realtek_priv *priv)
- {
-- ndelay(priv->clk_delay);
-+ ndelay(priv->variant->clk_delay);
- }
-
- static void realtek_smi_start(struct realtek_priv *priv)
-@@ -209,7 +209,7 @@ static int realtek_smi_read_reg(struct r
- realtek_smi_start(priv);
-
- /* Send READ command */
-- ret = realtek_smi_write_byte(priv, priv->cmd_read);
-+ ret = realtek_smi_write_byte(priv, priv->variant->cmd_read);
- if (ret)
- goto out;
-
-@@ -250,7 +250,7 @@ static int realtek_smi_write_reg(struct
- realtek_smi_start(priv);
-
- /* Send WRITE command */
-- ret = realtek_smi_write_byte(priv, priv->cmd_write);
-+ ret = realtek_smi_write_byte(priv, priv->variant->cmd_write);
- if (ret)
- goto out;
-
-@@ -459,9 +459,7 @@ int realtek_smi_probe(struct platform_de
-
- /* Link forward and backward */
- priv->dev = dev;
-- priv->clk_delay = var->clk_delay;
-- priv->cmd_read = var->cmd_read;
-- priv->cmd_write = var->cmd_write;
-+ priv->variant = var;
- priv->ops = var->ops;
-
- priv->setup_interface = realtek_smi_setup_mdio;
---- a/drivers/net/dsa/realtek/realtek.h
-+++ b/drivers/net/dsa/realtek/realtek.h
-@@ -58,9 +58,8 @@ struct realtek_priv {
- struct mii_bus *bus;
- int mdio_addr;
-
-- unsigned int clk_delay;
-- u8 cmd_read;
-- u8 cmd_write;
-+ const struct realtek_variant *variant;
-+
- spinlock_t lock; /* Locks around command writes */
- struct dsa_switch *ds;
- struct irq_domain *irqdomain;
+++ /dev/null
-From 8be040ecd94c1a9a137927d18534edfae0a9b68a Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Fri, 9 Feb 2024 02:03:41 -0300
-Subject: net: dsa: realtek: common rtl83xx module
-
-Some code can be shared between both interface modules (MDIO and SMI)
-and among variants. These interface functions migrated to a common
-module:
-
-- rtl83xx_lock
-- rtl83xx_unlock
-- rtl83xx_probe
-- rtl83xx_register_switch
-- rtl83xx_unregister_switch
-- rtl83xx_shutdown
-- rtl83xx_remove
-
-The reset during probe was moved to the end of the common probe. This way,
-we avoid a reset if anything else fails.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/Makefile | 2 +
- drivers/net/dsa/realtek/realtek-mdio.c | 152 +++-------------
- drivers/net/dsa/realtek/realtek-smi.c | 166 ++++-------------
- drivers/net/dsa/realtek/realtek.h | 1 +
- drivers/net/dsa/realtek/rtl8365mb.c | 9 +-
- drivers/net/dsa/realtek/rtl8366rb.c | 9 +-
- drivers/net/dsa/realtek/rtl83xx.c | 235 +++++++++++++++++++++++++
- drivers/net/dsa/realtek/rtl83xx.h | 21 +++
- 8 files changed, 322 insertions(+), 273 deletions(-)
- create mode 100644 drivers/net/dsa/realtek/rtl83xx.c
- create mode 100644 drivers/net/dsa/realtek/rtl83xx.h
-
---- a/drivers/net/dsa/realtek/Makefile
-+++ b/drivers/net/dsa/realtek/Makefile
-@@ -1,4 +1,6 @@
- # SPDX-License-Identifier: GPL-2.0
-+obj-$(CONFIG_NET_DSA_REALTEK) += realtek_dsa.o
-+realtek_dsa-objs := rtl83xx.o
- obj-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o
- obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o
- obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o
---- a/drivers/net/dsa/realtek/realtek-mdio.c
-+++ b/drivers/net/dsa/realtek/realtek-mdio.c
-@@ -26,6 +26,7 @@
-
- #include "realtek.h"
- #include "realtek-mdio.h"
-+#include "rtl83xx.h"
-
- /* Read/write via mdiobus */
- #define REALTEK_MDIO_CTRL0_REG 31
-@@ -100,147 +101,41 @@ out_unlock:
- return ret;
- }
-
--static void realtek_mdio_lock(void *ctx)
--{
-- struct realtek_priv *priv = ctx;
--
-- mutex_lock(&priv->map_lock);
--}
--
--static void realtek_mdio_unlock(void *ctx)
--{
-- struct realtek_priv *priv = ctx;
--
-- mutex_unlock(&priv->map_lock);
--}
--
--static const struct regmap_config realtek_mdio_regmap_config = {
-- .reg_bits = 10, /* A4..A0 R4..R0 */
-- .val_bits = 16,
-- .reg_stride = 1,
-- /* PHY regs are at 0x8000 */
-- .max_register = 0xffff,
-- .reg_format_endian = REGMAP_ENDIAN_BIG,
-+static const struct realtek_interface_info realtek_mdio_info = {
- .reg_read = realtek_mdio_read,
- .reg_write = realtek_mdio_write,
-- .cache_type = REGCACHE_NONE,
-- .lock = realtek_mdio_lock,
-- .unlock = realtek_mdio_unlock,
--};
--
--static const struct regmap_config realtek_mdio_nolock_regmap_config = {
-- .reg_bits = 10, /* A4..A0 R4..R0 */
-- .val_bits = 16,
-- .reg_stride = 1,
-- /* PHY regs are at 0x8000 */
-- .max_register = 0xffff,
-- .reg_format_endian = REGMAP_ENDIAN_BIG,
-- .reg_read = realtek_mdio_read,
-- .reg_write = realtek_mdio_write,
-- .cache_type = REGCACHE_NONE,
-- .disable_locking = true,
- };
-
- /**
- * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch
- * @mdiodev: mdio_device to probe on.
- *
-- * This function should be used as the .probe in an mdio_driver. It
-- * initializes realtek_priv and read data from the device-tree node. The switch
-- * is hard reset if a method is provided. It checks the switch chip ID and,
-- * finally, a DSA switch is registered.
-+ * This function should be used as the .probe in an mdio_driver. After
-+ * calling the common probe function for both interfaces, it initializes the
-+ * values specific for MDIO-connected devices. Finally, it calls a common
-+ * function to register the DSA switch.
- *
- * Context: Can sleep. Takes and releases priv->map_lock.
- * Return: Returns 0 on success, a negative error on failure.
- */
- int realtek_mdio_probe(struct mdio_device *mdiodev)
- {
-- struct realtek_priv *priv;
- struct device *dev = &mdiodev->dev;
-- const struct realtek_variant *var;
-- struct regmap_config rc;
-- struct device_node *np;
-+ struct realtek_priv *priv;
- int ret;
-
-- var = of_device_get_match_data(dev);
-- if (!var)
-- return -EINVAL;
--
-- priv = devm_kzalloc(&mdiodev->dev,
-- size_add(sizeof(*priv), var->chip_data_sz),
-- GFP_KERNEL);
-- if (!priv)
-- return -ENOMEM;
--
-- mutex_init(&priv->map_lock);
--
-- rc = realtek_mdio_regmap_config;
-- rc.lock_arg = priv;
-- priv->map = devm_regmap_init(dev, NULL, priv, &rc);
-- if (IS_ERR(priv->map)) {
-- ret = PTR_ERR(priv->map);
-- dev_err(dev, "regmap init failed: %d\n", ret);
-- return ret;
-- }
-+ priv = rtl83xx_probe(dev, &realtek_mdio_info);
-+ if (IS_ERR(priv))
-+ return PTR_ERR(priv);
-
-- rc = realtek_mdio_nolock_regmap_config;
-- priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc);
-- if (IS_ERR(priv->map_nolock)) {
-- ret = PTR_ERR(priv->map_nolock);
-- dev_err(dev, "regmap init failed: %d\n", ret);
-- return ret;
-- }
--
-- priv->mdio_addr = mdiodev->addr;
- priv->bus = mdiodev->bus;
-- priv->dev = &mdiodev->dev;
-- priv->chip_data = (void *)priv + sizeof(*priv);
--
-- priv->variant = var;
-- priv->ops = var->ops;
--
-+ priv->mdio_addr = mdiodev->addr;
- priv->write_reg_noack = realtek_mdio_write;
-+ priv->ds_ops = priv->variant->ds_ops_mdio;
-
-- np = dev->of_node;
--
-- dev_set_drvdata(dev, priv);
--
-- /* TODO: if power is software controlled, set up any regulators here */
-- priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds");
--
-- priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
-- if (IS_ERR(priv->reset)) {
-- dev_err(dev, "failed to get RESET GPIO\n");
-- return PTR_ERR(priv->reset);
-- }
--
-- if (priv->reset) {
-- gpiod_set_value(priv->reset, 1);
-- dev_dbg(dev, "asserted RESET\n");
-- msleep(REALTEK_HW_STOP_DELAY);
-- gpiod_set_value(priv->reset, 0);
-- msleep(REALTEK_HW_START_DELAY);
-- dev_dbg(dev, "deasserted RESET\n");
-- }
--
-- ret = priv->ops->detect(priv);
-- if (ret) {
-- dev_err(dev, "unable to detect switch\n");
-- return ret;
-- }
--
-- priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL);
-- if (!priv->ds)
-- return -ENOMEM;
--
-- priv->ds->dev = dev;
-- priv->ds->num_ports = priv->num_ports;
-- priv->ds->priv = priv;
-- priv->ds->ops = var->ds_ops_mdio;
--
-- ret = dsa_register_switch(priv->ds);
-+ ret = rtl83xx_register_switch(priv);
- if (ret) {
-- dev_err(priv->dev, "unable to register switch ret = %d\n", ret);
-+ rtl83xx_remove(priv);
- return ret;
- }
-
-@@ -253,8 +148,7 @@ EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe,
- * @mdiodev: mdio_device to be removed.
- *
- * This function should be used as the .remove_new in an mdio_driver. First
-- * it unregisters the DSA switch and cleans internal data. If a method is
-- * provided, the hard reset is asserted to avoid traffic leakage.
-+ * it unregisters the DSA switch and then it calls the common remove function.
- *
- * Context: Can sleep.
- * Return: Nothing.
-@@ -266,11 +160,9 @@ void realtek_mdio_remove(struct mdio_dev
- if (!priv)
- return;
-
-- dsa_unregister_switch(priv->ds);
-+ rtl83xx_unregister_switch(priv);
-
-- /* leave the device reset asserted */
-- if (priv->reset)
-- gpiod_set_value(priv->reset, 1);
-+ rtl83xx_remove(priv);
- }
- EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA);
-
-@@ -278,10 +170,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove
- * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch
- * @mdiodev: mdio_device shutting down.
- *
-- * This function should be used as the .shutdown in an mdio_driver. It shuts
-- * down the DSA switch and cleans the platform driver data, to prevent
-- * realtek_mdio_remove() from running afterwards, which is possible if the
-- * parent bus implements its own .shutdown() as .remove().
-+ * This function should be used as the .shutdown in a platform_driver. It calls
-+ * the common shutdown function.
- *
- * Context: Can sleep.
- * Return: Nothing.
-@@ -293,9 +183,7 @@ void realtek_mdio_shutdown(struct mdio_d
- if (!priv)
- return;
-
-- dsa_switch_shutdown(priv->ds);
--
-- dev_set_drvdata(&mdiodev->dev, NULL);
-+ rtl83xx_shutdown(priv);
- }
- EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);
-
---- a/drivers/net/dsa/realtek/realtek-smi.c
-+++ b/drivers/net/dsa/realtek/realtek-smi.c
-@@ -41,6 +41,7 @@
-
- #include "realtek.h"
- #include "realtek-smi.h"
-+#include "rtl83xx.h"
-
- #define REALTEK_SMI_ACK_RETRY_COUNT 5
-
-@@ -311,47 +312,6 @@ static int realtek_smi_read(void *ctx, u
- return realtek_smi_read_reg(priv, reg, val);
- }
-
--static void realtek_smi_lock(void *ctx)
--{
-- struct realtek_priv *priv = ctx;
--
-- mutex_lock(&priv->map_lock);
--}
--
--static void realtek_smi_unlock(void *ctx)
--{
-- struct realtek_priv *priv = ctx;
--
-- mutex_unlock(&priv->map_lock);
--}
--
--static const struct regmap_config realtek_smi_regmap_config = {
-- .reg_bits = 10, /* A4..A0 R4..R0 */
-- .val_bits = 16,
-- .reg_stride = 1,
-- /* PHY regs are at 0x8000 */
-- .max_register = 0xffff,
-- .reg_format_endian = REGMAP_ENDIAN_BIG,
-- .reg_read = realtek_smi_read,
-- .reg_write = realtek_smi_write,
-- .cache_type = REGCACHE_NONE,
-- .lock = realtek_smi_lock,
-- .unlock = realtek_smi_unlock,
--};
--
--static const struct regmap_config realtek_smi_nolock_regmap_config = {
-- .reg_bits = 10, /* A4..A0 R4..R0 */
-- .val_bits = 16,
-- .reg_stride = 1,
-- /* PHY regs are at 0x8000 */
-- .max_register = 0xffff,
-- .reg_format_endian = REGMAP_ENDIAN_BIG,
-- .reg_read = realtek_smi_read,
-- .reg_write = realtek_smi_write,
-- .cache_type = REGCACHE_NONE,
-- .disable_locking = true,
--};
--
- static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum)
- {
- struct realtek_priv *priv = bus->priv;
-@@ -409,111 +369,56 @@ err_put_node:
- return ret;
- }
-
-+static const struct realtek_interface_info realtek_smi_info = {
-+ .reg_read = realtek_smi_read,
-+ .reg_write = realtek_smi_write,
-+};
-+
- /**
- * realtek_smi_probe() - Probe a platform device for an SMI-connected switch
- * @pdev: platform_device to probe on.
- *
-- * This function should be used as the .probe in a platform_driver. It
-- * initializes realtek_priv and read data from the device-tree node. The switch
-- * is hard reset if a method is provided. It checks the switch chip ID and,
-- * finally, a DSA switch is registered.
-+ * This function should be used as the .probe in a platform_driver. After
-+ * calling the common probe function for both interfaces, it initializes the
-+ * values specific for SMI-connected devices. Finally, it calls a common
-+ * function to register the DSA switch.
- *
- * Context: Can sleep. Takes and releases priv->map_lock.
- * Return: Returns 0 on success, a negative error on failure.
- */
- int realtek_smi_probe(struct platform_device *pdev)
- {
-- const struct realtek_variant *var;
- struct device *dev = &pdev->dev;
- struct realtek_priv *priv;
-- struct regmap_config rc;
-- struct device_node *np;
- int ret;
-
-- var = of_device_get_match_data(dev);
-- np = dev->of_node;
--
-- priv = devm_kzalloc(dev, sizeof(*priv) + var->chip_data_sz, GFP_KERNEL);
-- if (!priv)
-- return -ENOMEM;
-- priv->chip_data = (void *)priv + sizeof(*priv);
--
-- mutex_init(&priv->map_lock);
--
-- rc = realtek_smi_regmap_config;
-- rc.lock_arg = priv;
-- priv->map = devm_regmap_init(dev, NULL, priv, &rc);
-- if (IS_ERR(priv->map)) {
-- ret = PTR_ERR(priv->map);
-- dev_err(dev, "regmap init failed: %d\n", ret);
-- return ret;
-- }
--
-- rc = realtek_smi_nolock_regmap_config;
-- priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc);
-- if (IS_ERR(priv->map_nolock)) {
-- ret = PTR_ERR(priv->map_nolock);
-- dev_err(dev, "regmap init failed: %d\n", ret);
-- return ret;
-- }
--
-- /* Link forward and backward */
-- priv->dev = dev;
-- priv->variant = var;
-- priv->ops = var->ops;
--
-- priv->setup_interface = realtek_smi_setup_mdio;
-- priv->write_reg_noack = realtek_smi_write_reg_noack;
--
-- dev_set_drvdata(dev, priv);
-- spin_lock_init(&priv->lock);
--
-- /* TODO: if power is software controlled, set up any regulators here */
--
-- priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
-- if (IS_ERR(priv->reset)) {
-- dev_err(dev, "failed to get RESET GPIO\n");
-- return PTR_ERR(priv->reset);
-- }
-- if (priv->reset) {
-- gpiod_set_value(priv->reset, 1);
-- dev_dbg(dev, "asserted RESET\n");
-- msleep(REALTEK_HW_STOP_DELAY);
-- gpiod_set_value(priv->reset, 0);
-- msleep(REALTEK_HW_START_DELAY);
-- dev_dbg(dev, "deasserted RESET\n");
-- }
-+ priv = rtl83xx_probe(dev, &realtek_smi_info);
-+ if (IS_ERR(priv))
-+ return PTR_ERR(priv);
-
- /* Fetch MDIO pins */
- priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW);
-- if (IS_ERR(priv->mdc))
-+ if (IS_ERR(priv->mdc)) {
-+ rtl83xx_remove(priv);
- return PTR_ERR(priv->mdc);
-+ }
-+
- priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW);
-- if (IS_ERR(priv->mdio))
-+ if (IS_ERR(priv->mdio)) {
-+ rtl83xx_remove(priv);
- return PTR_ERR(priv->mdio);
--
-- priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds");
--
-- ret = priv->ops->detect(priv);
-- if (ret) {
-- dev_err(dev, "unable to detect switch\n");
-- return ret;
- }
-
-- priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL);
-- if (!priv->ds)
-- return -ENOMEM;
--
-- priv->ds->dev = dev;
-- priv->ds->num_ports = priv->num_ports;
-- priv->ds->priv = priv;
-+ priv->write_reg_noack = realtek_smi_write_reg_noack;
-+ priv->setup_interface = realtek_smi_setup_mdio;
-+ priv->ds_ops = priv->variant->ds_ops_smi;
-
-- priv->ds->ops = var->ds_ops_smi;
-- ret = dsa_register_switch(priv->ds);
-+ ret = rtl83xx_register_switch(priv);
- if (ret) {
-- dev_err_probe(dev, ret, "unable to register switch\n");
-+ rtl83xx_remove(priv);
- return ret;
- }
-+
- return 0;
- }
- EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA);
-@@ -523,8 +428,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_probe,
- * @pdev: platform_device to be removed.
- *
- * This function should be used as the .remove_new in a platform_driver. First
-- * it unregisters the DSA switch and cleans internal data. If a method is
-- * provided, the hard reset is asserted to avoid traffic leakage.
-+ * it unregisters the DSA switch and cleans internal data. Finally, it calls
-+ * the common remove function.
- *
- * Context: Can sleep.
- * Return: Nothing.
-@@ -536,13 +441,12 @@ void realtek_smi_remove(struct platform_
- if (!priv)
- return;
-
-- dsa_unregister_switch(priv->ds);
-+ rtl83xx_unregister_switch(priv);
-+
- if (priv->slave_mii_bus)
- of_node_put(priv->slave_mii_bus->dev.of_node);
-
-- /* leave the device reset asserted */
-- if (priv->reset)
-- gpiod_set_value(priv->reset, 1);
-+ rtl83xx_remove(priv);
- }
- EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA);
-
-@@ -550,10 +454,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_remove,
- * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch
- * @pdev: platform_device shutting down.
- *
-- * This function should be used as the .shutdown in a platform_driver. It shuts
-- * down the DSA switch and cleans the platform driver data, to prevent
-- * realtek_smi_remove() from running afterwards, which is possible if the
-- * parent bus implements its own .shutdown() as .remove().
-+ * This function should be used as the .shutdown in a platform_driver. It calls
-+ * the common shutdown function.
- *
- * Context: Can sleep.
- * Return: Nothing.
-@@ -565,9 +467,7 @@ void realtek_smi_shutdown(struct platfor
- if (!priv)
- return;
-
-- dsa_switch_shutdown(priv->ds);
--
-- platform_set_drvdata(pdev, NULL);
-+ rtl83xx_shutdown(priv);
- }
- EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA);
-
---- a/drivers/net/dsa/realtek/realtek.h
-+++ b/drivers/net/dsa/realtek/realtek.h
-@@ -62,6 +62,7 @@ struct realtek_priv {
-
- spinlock_t lock; /* Locks around command writes */
- struct dsa_switch *ds;
-+ const struct dsa_switch_ops *ds_ops;
- struct irq_domain *irqdomain;
- bool leds_disabled;
-
---- a/drivers/net/dsa/realtek/rtl8365mb.c
-+++ b/drivers/net/dsa/realtek/rtl8365mb.c
-@@ -103,6 +103,7 @@
- #include "realtek.h"
- #include "realtek-smi.h"
- #include "realtek-mdio.h"
-+#include "rtl83xx.h"
-
- /* Family-specific data and limits */
- #define RTL8365MB_PHYADDRMAX 7
-@@ -691,7 +692,7 @@ static int rtl8365mb_phy_ocp_read(struct
- u32 val;
- int ret;
-
-- mutex_lock(&priv->map_lock);
-+ rtl83xx_lock(priv);
-
- ret = rtl8365mb_phy_poll_busy(priv);
- if (ret)
-@@ -724,7 +725,7 @@ static int rtl8365mb_phy_ocp_read(struct
- *data = val & 0xFFFF;
-
- out:
-- mutex_unlock(&priv->map_lock);
-+ rtl83xx_unlock(priv);
-
- return ret;
- }
-@@ -735,7 +736,7 @@ static int rtl8365mb_phy_ocp_write(struc
- u32 val;
- int ret;
-
-- mutex_lock(&priv->map_lock);
-+ rtl83xx_lock(priv);
-
- ret = rtl8365mb_phy_poll_busy(priv);
- if (ret)
-@@ -766,7 +767,7 @@ static int rtl8365mb_phy_ocp_write(struc
- goto out;
-
- out:
-- mutex_unlock(&priv->map_lock);
-+ rtl83xx_unlock(priv);
-
- return 0;
- }
---- a/drivers/net/dsa/realtek/rtl8366rb.c
-+++ b/drivers/net/dsa/realtek/rtl8366rb.c
-@@ -24,6 +24,7 @@
- #include "realtek.h"
- #include "realtek-smi.h"
- #include "realtek-mdio.h"
-+#include "rtl83xx.h"
-
- #define RTL8366RB_PORT_NUM_CPU 5
- #define RTL8366RB_NUM_PORTS 6
-@@ -1634,7 +1635,7 @@ static int rtl8366rb_phy_read(struct rea
- if (phy > RTL8366RB_PHY_NO_MAX)
- return -EINVAL;
-
-- mutex_lock(&priv->map_lock);
-+ rtl83xx_lock(priv);
-
- ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG,
- RTL8366RB_PHY_CTRL_READ);
-@@ -1662,7 +1663,7 @@ static int rtl8366rb_phy_read(struct rea
- phy, regnum, reg, val);
-
- out:
-- mutex_unlock(&priv->map_lock);
-+ rtl83xx_unlock(priv);
-
- return ret;
- }
-@@ -1676,7 +1677,7 @@ static int rtl8366rb_phy_write(struct re
- if (phy > RTL8366RB_PHY_NO_MAX)
- return -EINVAL;
-
-- mutex_lock(&priv->map_lock);
-+ rtl83xx_lock(priv);
-
- ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG,
- RTL8366RB_PHY_CTRL_WRITE);
-@@ -1693,7 +1694,7 @@ static int rtl8366rb_phy_write(struct re
- goto out;
-
- out:
-- mutex_unlock(&priv->map_lock);
-+ rtl83xx_unlock(priv);
-
- return ret;
- }
---- /dev/null
-+++ b/drivers/net/dsa/realtek/rtl83xx.c
-@@ -0,0 +1,236 @@
-+// SPDX-License-Identifier: GPL-2.0+
-+
-+#include <linux/module.h>
-+#include <linux/regmap.h>
-+#include <linux/of_device.h> /* krnl 6.1 only */
-+
-+#include "realtek.h"
-+#include "rtl83xx.h"
-+
-+/**
-+ * rtl83xx_lock() - Locks the mutex used by regmaps
-+ * @ctx: realtek_priv pointer
-+ *
-+ * This function is passed to regmap to be used as the lock function.
-+ * It is also used externally to block regmap before executing multiple
-+ * operations that must happen in sequence (which will use
-+ * realtek_priv.map_nolock instead).
-+ *
-+ * Context: Can sleep. Holds priv->map_lock lock.
-+ * Return: nothing
-+ */
-+void rtl83xx_lock(void *ctx)
-+{
-+ struct realtek_priv *priv = ctx;
-+
-+ mutex_lock(&priv->map_lock);
-+}
-+EXPORT_SYMBOL_NS_GPL(rtl83xx_lock, REALTEK_DSA);
-+
-+/**
-+ * rtl83xx_unlock() - Unlocks the mutex used by regmaps
-+ * @ctx: realtek_priv pointer
-+ *
-+ * This function unlocks the lock acquired by rtl83xx_lock.
-+ *
-+ * Context: Releases priv->map_lock lock.
-+ * Return: nothing
-+ */
-+void rtl83xx_unlock(void *ctx)
-+{
-+ struct realtek_priv *priv = ctx;
-+
-+ mutex_unlock(&priv->map_lock);
-+}
-+EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, REALTEK_DSA);
-+
-+/**
-+ * rtl83xx_probe() - probe a Realtek switch
-+ * @dev: the device being probed
-+ * @interface_info: specific management interface info.
-+ *
-+ * This function initializes realtek_priv and reads data from the device tree
-+ * node. The switch is hard resetted if a method is provided.
-+ *
-+ * Context: Can sleep.
-+ * Return: Pointer to the realtek_priv or ERR_PTR() in case of failure.
-+ *
-+ * The realtek_priv pointer does not need to be freed as it is controlled by
-+ * devres.
-+ */
-+struct realtek_priv *
-+rtl83xx_probe(struct device *dev,
-+ const struct realtek_interface_info *interface_info)
-+{
-+ const struct realtek_variant *var;
-+ struct realtek_priv *priv;
-+ struct regmap_config rc = {
-+ .reg_bits = 10, /* A4..A0 R4..R0 */
-+ .val_bits = 16,
-+ .reg_stride = 1,
-+ .max_register = 0xffff,
-+ .reg_format_endian = REGMAP_ENDIAN_BIG,
-+ .reg_read = interface_info->reg_read,
-+ .reg_write = interface_info->reg_write,
-+ .cache_type = REGCACHE_NONE,
-+ .lock = rtl83xx_lock,
-+ .unlock = rtl83xx_unlock,
-+ };
-+ int ret;
-+
-+ var = of_device_get_match_data(dev);
-+ if (!var)
-+ return ERR_PTR(-EINVAL);
-+
-+ priv = devm_kzalloc(dev, size_add(sizeof(*priv), var->chip_data_sz),
-+ GFP_KERNEL);
-+ if (!priv)
-+ return ERR_PTR(-ENOMEM);
-+
-+ mutex_init(&priv->map_lock);
-+
-+ rc.lock_arg = priv;
-+ priv->map = devm_regmap_init(dev, NULL, priv, &rc);
-+ if (IS_ERR(priv->map)) {
-+ ret = PTR_ERR(priv->map);
-+ dev_err(dev, "regmap init failed: %d\n", ret);
-+ return ERR_PTR(ret);
-+ }
-+
-+ rc.disable_locking = true;
-+ priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc);
-+ if (IS_ERR(priv->map_nolock)) {
-+ ret = PTR_ERR(priv->map_nolock);
-+ dev_err(dev, "regmap init failed: %d\n", ret);
-+ return ERR_PTR(ret);
-+ }
-+
-+ /* Link forward and backward */
-+ priv->dev = dev;
-+ priv->variant = var;
-+ priv->ops = var->ops;
-+ priv->chip_data = (void *)priv + sizeof(*priv);
-+
-+ spin_lock_init(&priv->lock);
-+
-+ priv->leds_disabled = of_property_read_bool(dev->of_node,
-+ "realtek,disable-leds");
-+
-+ /* TODO: if power is software controlled, set up any regulators here */
-+ priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
-+ if (IS_ERR(priv->reset)) {
-+ dev_err(dev, "failed to get RESET GPIO\n");
-+ return ERR_CAST(priv->reset);
-+ }
-+
-+ dev_set_drvdata(dev, priv);
-+
-+ if (priv->reset) {
-+ gpiod_set_value(priv->reset, 1);
-+ dev_dbg(dev, "asserted RESET\n");
-+ msleep(REALTEK_HW_STOP_DELAY);
-+ gpiod_set_value(priv->reset, 0);
-+ msleep(REALTEK_HW_START_DELAY);
-+ dev_dbg(dev, "deasserted RESET\n");
-+ }
-+
-+ return priv;
-+}
-+EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, REALTEK_DSA);
-+
-+/**
-+ * rtl83xx_register_switch() - detects and register a switch
-+ * @priv: realtek_priv pointer
-+ *
-+ * This function first checks the switch chip ID and register a DSA
-+ * switch.
-+ *
-+ * Context: Can sleep. Takes and releases priv->map_lock.
-+ * Return: 0 on success, negative value for failure.
-+ */
-+int rtl83xx_register_switch(struct realtek_priv *priv)
-+{
-+ struct dsa_switch *ds;
-+ int ret;
-+
-+ ret = priv->ops->detect(priv);
-+ if (ret) {
-+ dev_err_probe(priv->dev, ret, "unable to detect switch\n");
-+ return ret;
-+ }
-+
-+ ds = devm_kzalloc(priv->dev, sizeof(*ds), GFP_KERNEL);
-+ if (!ds)
-+ return -ENOMEM;
-+
-+ ds->priv = priv;
-+ ds->dev = priv->dev;
-+ ds->ops = priv->ds_ops;
-+ ds->num_ports = priv->num_ports;
-+ priv->ds = ds;
-+
-+ ret = dsa_register_switch(ds);
-+ if (ret) {
-+ dev_err_probe(priv->dev, ret, "unable to register switch\n");
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_NS_GPL(rtl83xx_register_switch, REALTEK_DSA);
-+
-+/**
-+ * rtl83xx_unregister_switch() - unregister a switch
-+ * @priv: realtek_priv pointer
-+ *
-+ * This function unregister a DSA switch.
-+ *
-+ * Context: Can sleep.
-+ * Return: Nothing.
-+ */
-+void rtl83xx_unregister_switch(struct realtek_priv *priv)
-+{
-+ dsa_unregister_switch(priv->ds);
-+}
-+EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, REALTEK_DSA);
-+
-+/**
-+ * rtl83xx_shutdown() - shutdown a switch
-+ * @priv: realtek_priv pointer
-+ *
-+ * This function shuts down the DSA switch and cleans the platform driver data,
-+ * to prevent realtek_{smi,mdio}_remove() from running afterwards, which is
-+ * possible if the parent bus implements its own .shutdown() as .remove().
-+ *
-+ * Context: Can sleep.
-+ * Return: Nothing.
-+ */
-+void rtl83xx_shutdown(struct realtek_priv *priv)
-+{
-+ dsa_switch_shutdown(priv->ds);
-+
-+ dev_set_drvdata(priv->dev, NULL);
-+}
-+EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, REALTEK_DSA);
-+
-+/**
-+ * rtl83xx_remove() - Cleanup a realtek switch driver
-+ * @priv: realtek_priv pointer
-+ *
-+ * If a method is provided, this function asserts the hard reset of the switch
-+ * in order to avoid leaking traffic when the driver is gone.
-+ *
-+ * Context: Might sleep if priv->gdev->chip->can_sleep.
-+ * Return: nothing
-+ */
-+void rtl83xx_remove(struct realtek_priv *priv)
-+{
-+ /* leave the device reset asserted */
-+ if (priv->reset)
-+ gpiod_set_value(priv->reset, 1);
-+}
-+EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA);
-+
-+MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
-+MODULE_DESCRIPTION("Realtek DSA switches common module");
-+MODULE_LICENSE("GPL");
---- /dev/null
-+++ b/drivers/net/dsa/realtek/rtl83xx.h
-@@ -0,0 +1,21 @@
-+/* SPDX-License-Identifier: GPL-2.0+ */
-+
-+#ifndef _RTL83XX_H
-+#define _RTL83XX_H
-+
-+struct realtek_interface_info {
-+ int (*reg_read)(void *ctx, u32 reg, u32 *val);
-+ int (*reg_write)(void *ctx, u32 reg, u32 val);
-+};
-+
-+void rtl83xx_lock(void *ctx);
-+void rtl83xx_unlock(void *ctx);
-+struct realtek_priv *
-+rtl83xx_probe(struct device *dev,
-+ const struct realtek_interface_info *interface_info);
-+int rtl83xx_register_switch(struct realtek_priv *priv);
-+void rtl83xx_unregister_switch(struct realtek_priv *priv);
-+void rtl83xx_shutdown(struct realtek_priv *priv);
-+void rtl83xx_remove(struct realtek_priv *priv);
-+
-+#endif /* _RTL83XX_H */
+++ /dev/null
-From 98b75c1c149c653ad11a440636213eb070325158 Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Fri, 9 Feb 2024 02:03:42 -0300
-Subject: net: dsa: realtek: merge rtl83xx and interface
- modules into realtek_dsa
-
-Since rtl83xx and realtek-{smi,mdio} are always loaded together,
-we can optimize resource usage by consolidating them into a single
-module.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/Kconfig | 4 ++--
- drivers/net/dsa/realtek/Makefile | 11 +++++++++--
- drivers/net/dsa/realtek/realtek-mdio.c | 5 -----
- drivers/net/dsa/realtek/realtek-smi.c | 5 -----
- drivers/net/dsa/realtek/rtl83xx.c | 1 +
- 5 files changed, 12 insertions(+), 14 deletions(-)
-
---- a/drivers/net/dsa/realtek/Kconfig
-+++ b/drivers/net/dsa/realtek/Kconfig
-@@ -16,14 +16,14 @@ menuconfig NET_DSA_REALTEK
- if NET_DSA_REALTEK
-
- config NET_DSA_REALTEK_MDIO
-- tristate "Realtek MDIO interface support"
-+ bool "Realtek MDIO interface support"
- depends on OF
- help
- Select to enable support for registering switches configured
- through MDIO.
-
- config NET_DSA_REALTEK_SMI
-- tristate "Realtek SMI interface support"
-+ bool "Realtek SMI interface support"
- depends on OF
- help
- Select to enable support for registering switches connected
---- a/drivers/net/dsa/realtek/Makefile
-+++ b/drivers/net/dsa/realtek/Makefile
-@@ -1,8 +1,15 @@
- # SPDX-License-Identifier: GPL-2.0
- obj-$(CONFIG_NET_DSA_REALTEK) += realtek_dsa.o
- realtek_dsa-objs := rtl83xx.o
--obj-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o
--obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o
-+
-+ifdef CONFIG_NET_DSA_REALTEK_MDIO
-+realtek_dsa-objs += realtek-mdio.o
-+endif
-+
-+ifdef CONFIG_NET_DSA_REALTEK_SMI
-+realtek_dsa-objs += realtek-smi.o
-+endif
-+
- obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o
- rtl8366-objs := rtl8366-core.o rtl8366rb.o
- obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o
---- a/drivers/net/dsa/realtek/realtek-mdio.c
-+++ b/drivers/net/dsa/realtek/realtek-mdio.c
-@@ -186,8 +186,3 @@ void realtek_mdio_shutdown(struct mdio_d
- rtl83xx_shutdown(priv);
- }
- EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);
--
--MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
--MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface");
--MODULE_LICENSE("GPL");
--MODULE_IMPORT_NS(REALTEK_DSA);
---- a/drivers/net/dsa/realtek/realtek-smi.c
-+++ b/drivers/net/dsa/realtek/realtek-smi.c
-@@ -470,8 +470,3 @@ void realtek_smi_shutdown(struct platfor
- rtl83xx_shutdown(priv);
- }
- EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA);
--
--MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
--MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface");
--MODULE_LICENSE("GPL");
--MODULE_IMPORT_NS(REALTEK_DSA);
---- a/drivers/net/dsa/realtek/rtl83xx.c
-+++ b/drivers/net/dsa/realtek/rtl83xx.c
-@@ -232,5 +232,6 @@ void rtl83xx_remove(struct realtek_priv
- EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA);
-
- MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
-+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
- MODULE_DESCRIPTION("Realtek DSA switches common module");
- MODULE_LICENSE("GPL");
+++ /dev/null
-From 8685c98d45c54346caf005de69988e13c731c533 Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Fri, 9 Feb 2024 02:03:43 -0300
-Subject: net: dsa: realtek: get internal MDIO node by name
-
-The binding docs requires for SMI-connected devices that the switch
-must have a child node named "mdio" and with a compatible string of
-"realtek,smi-mdio". Meanwile, for MDIO-connected switches, the binding
-docs only requires a child node named "mdio".
-
-This patch changes the driver to use the common denominator for both
-interfaces, looking for the MDIO node by name, ignoring the compatible
-string.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/realtek-smi.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/dsa/realtek/realtek-smi.c
-+++ b/drivers/net/dsa/realtek/realtek-smi.c
-@@ -333,7 +333,7 @@ static int realtek_smi_setup_mdio(struct
- struct device_node *mdio_np;
- int ret;
-
-- mdio_np = of_get_compatible_child(priv->dev->of_node, "realtek,smi-mdio");
-+ mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio");
- if (!mdio_np) {
- dev_err(priv->dev, "no MDIO bus node\n");
- return -ENODEV;
+++ /dev/null
-From 68c66d8d8a19088967a0ab6bb98cb5ecc80ca0be Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Fri, 9 Feb 2024 02:03:44 -0300
-Subject: net: dsa: realtek: clean slave_mii_bus setup
-
-Remove the line assigning dev.of_node in mdio_bus as subsequent
-of_mdiobus_register will always overwrite it.
-
-As discussed in [1], allow the DSA core to be simplified, by not
-assigning ds->slave_mii_bus when the MDIO bus is described in OF, as it
-is unnecessary.
-
-Since commit 3b73a7b8ec38 ("net: mdio_bus: add refcounting for fwnodes
-to mdiobus"), we can put the "mdio" node just after the MDIO bus
-registration.
-
-[1] https://lkml.kernel.org/netdev/20231213120656.x46fyad6ls7sqyzv@skbuf/T/#u
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/realtek-smi.c | 13 +++----------
- 1 file changed, 3 insertions(+), 10 deletions(-)
-
---- a/drivers/net/dsa/realtek/realtek-smi.c
-+++ b/drivers/net/dsa/realtek/realtek-smi.c
-@@ -331,7 +331,7 @@ static int realtek_smi_setup_mdio(struct
- {
- struct realtek_priv *priv = ds->priv;
- struct device_node *mdio_np;
-- int ret;
-+ int ret = 0;
-
- mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio");
- if (!mdio_np) {
-@@ -344,15 +344,14 @@ static int realtek_smi_setup_mdio(struct
- ret = -ENOMEM;
- goto err_put_node;
- }
-+
- priv->slave_mii_bus->priv = priv;
- priv->slave_mii_bus->name = "SMI slave MII";
- priv->slave_mii_bus->read = realtek_smi_mdio_read;
- priv->slave_mii_bus->write = realtek_smi_mdio_write;
- snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d",
- ds->index);
-- priv->slave_mii_bus->dev.of_node = mdio_np;
- priv->slave_mii_bus->parent = priv->dev;
-- ds->slave_mii_bus = priv->slave_mii_bus;
-
- ret = devm_of_mdiobus_register(priv->dev, priv->slave_mii_bus, mdio_np);
- if (ret) {
-@@ -361,8 +360,6 @@ static int realtek_smi_setup_mdio(struct
- goto err_put_node;
- }
-
-- return 0;
--
- err_put_node:
- of_node_put(mdio_np);
-
-@@ -428,8 +425,7 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_probe,
- * @pdev: platform_device to be removed.
- *
- * This function should be used as the .remove_new in a platform_driver. First
-- * it unregisters the DSA switch and cleans internal data. Finally, it calls
-- * the common remove function.
-+ * it unregisters the DSA switch and then it calls the common remove function.
- *
- * Context: Can sleep.
- * Return: Nothing.
-@@ -443,9 +439,6 @@ void realtek_smi_remove(struct platform_
-
- rtl83xx_unregister_switch(priv);
-
-- if (priv->slave_mii_bus)
-- of_node_put(priv->slave_mii_bus->dev.of_node);
--
- rtl83xx_remove(priv);
- }
- EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA);
+++ /dev/null
-From b4bd77971f3c290c4694ed710cc6967593b10bc2 Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Fri, 9 Feb 2024 02:03:45 -0300
-Subject: net: dsa: realtek: migrate slave_mii_bus setup to
- realtek_dsa
-
-In the user MDIO driver, despite numerous references to SMI, including
-its compatible string, there's nothing inherently specific about the SMI
-interface in the user MDIO bus. Consequently, the code has been migrated
-to the rtl83xx module. All references to SMI have been eliminated.
-
-The MDIO bus id was changed from Realtek-<switch id> to the switch
-devname suffixed with :slave_mii, giving more information about the bus
-it is referencing.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/realtek-smi.c | 57 +---------------------
- drivers/net/dsa/realtek/rtl83xx.c | 68 +++++++++++++++++++++++++++
- drivers/net/dsa/realtek/rtl83xx.h | 1 +
- 3 files changed, 70 insertions(+), 56 deletions(-)
-
---- a/drivers/net/dsa/realtek/realtek-smi.c
-+++ b/drivers/net/dsa/realtek/realtek-smi.c
-@@ -31,7 +31,6 @@
- #include <linux/spinlock.h>
- #include <linux/skbuff.h>
- #include <linux/of.h>
--#include <linux/of_mdio.h>
- #include <linux/delay.h>
- #include <linux/gpio/consumer.h>
- #include <linux/platform_device.h>
-@@ -312,60 +311,6 @@ static int realtek_smi_read(void *ctx, u
- return realtek_smi_read_reg(priv, reg, val);
- }
-
--static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum)
--{
-- struct realtek_priv *priv = bus->priv;
--
-- return priv->ops->phy_read(priv, addr, regnum);
--}
--
--static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum,
-- u16 val)
--{
-- struct realtek_priv *priv = bus->priv;
--
-- return priv->ops->phy_write(priv, addr, regnum, val);
--}
--
--static int realtek_smi_setup_mdio(struct dsa_switch *ds)
--{
-- struct realtek_priv *priv = ds->priv;
-- struct device_node *mdio_np;
-- int ret = 0;
--
-- mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio");
-- if (!mdio_np) {
-- dev_err(priv->dev, "no MDIO bus node\n");
-- return -ENODEV;
-- }
--
-- priv->slave_mii_bus = devm_mdiobus_alloc(priv->dev);
-- if (!priv->slave_mii_bus) {
-- ret = -ENOMEM;
-- goto err_put_node;
-- }
--
-- priv->slave_mii_bus->priv = priv;
-- priv->slave_mii_bus->name = "SMI slave MII";
-- priv->slave_mii_bus->read = realtek_smi_mdio_read;
-- priv->slave_mii_bus->write = realtek_smi_mdio_write;
-- snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d",
-- ds->index);
-- priv->slave_mii_bus->parent = priv->dev;
--
-- ret = devm_of_mdiobus_register(priv->dev, priv->slave_mii_bus, mdio_np);
-- if (ret) {
-- dev_err(priv->dev, "unable to register MDIO bus %s\n",
-- priv->slave_mii_bus->id);
-- goto err_put_node;
-- }
--
--err_put_node:
-- of_node_put(mdio_np);
--
-- return ret;
--}
--
- static const struct realtek_interface_info realtek_smi_info = {
- .reg_read = realtek_smi_read,
- .reg_write = realtek_smi_write,
-@@ -407,7 +352,7 @@ int realtek_smi_probe(struct platform_de
- }
-
- priv->write_reg_noack = realtek_smi_write_reg_noack;
-- priv->setup_interface = realtek_smi_setup_mdio;
-+ priv->setup_interface = rtl83xx_setup_user_mdio;
- priv->ds_ops = priv->variant->ds_ops_smi;
-
- ret = rtl83xx_register_switch(priv);
---- a/drivers/net/dsa/realtek/rtl83xx.c
-+++ b/drivers/net/dsa/realtek/rtl83xx.c
-@@ -3,6 +3,7 @@
- #include <linux/module.h>
- #include <linux/regmap.h>
- #include <linux/of_device.h> /* krnl 6.1 only */
-+#include <linux/of_mdio.h>
-
- #include "realtek.h"
- #include "rtl83xx.h"
-@@ -44,6 +45,73 @@ void rtl83xx_unlock(void *ctx)
- }
- EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, REALTEK_DSA);
-
-+static int rtl83xx_user_mdio_read(struct mii_bus *bus, int addr, int regnum)
-+{
-+ struct realtek_priv *priv = bus->priv;
-+
-+ return priv->ops->phy_read(priv, addr, regnum);
-+}
-+
-+static int rtl83xx_user_mdio_write(struct mii_bus *bus, int addr, int regnum,
-+ u16 val)
-+{
-+ struct realtek_priv *priv = bus->priv;
-+
-+ return priv->ops->phy_write(priv, addr, regnum, val);
-+}
-+
-+/**
-+ * rtl83xx_setup_user_mdio() - register the user mii bus driver
-+ * @ds: DSA switch associated with this slave_mii_bus
-+ *
-+ * Registers the MDIO bus for built-in Ethernet PHYs, and associates it with
-+ * the mandatory 'mdio' child OF node of the switch.
-+ *
-+ * Context: Can sleep.
-+ * Return: 0 on success, negative value for failure.
-+ */
-+int rtl83xx_setup_user_mdio(struct dsa_switch *ds)
-+{
-+ struct realtek_priv *priv = ds->priv;
-+ struct device_node *mdio_np;
-+ struct mii_bus *bus;
-+ int ret = 0;
-+
-+ mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio");
-+ if (!mdio_np) {
-+ dev_err(priv->dev, "no MDIO bus node\n");
-+ return -ENODEV;
-+ }
-+
-+ bus = devm_mdiobus_alloc(priv->dev);
-+ if (!bus) {
-+ ret = -ENOMEM;
-+ goto err_put_node;
-+ }
-+
-+ bus->priv = priv;
-+ bus->name = "Realtek user MII";
-+ bus->read = rtl83xx_user_mdio_read;
-+ bus->write = rtl83xx_user_mdio_write;
-+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s:slave_mii", dev_name(priv->dev));
-+ bus->parent = priv->dev;
-+
-+ ret = devm_of_mdiobus_register(priv->dev, bus, mdio_np);
-+ if (ret) {
-+ dev_err(priv->dev, "unable to register MDIO bus %s\n",
-+ bus->id);
-+ goto err_put_node;
-+ }
-+
-+ priv->slave_mii_bus = bus;
-+
-+err_put_node:
-+ of_node_put(mdio_np);
-+
-+ return ret;
-+}
-+EXPORT_SYMBOL_NS_GPL(rtl83xx_setup_user_mdio, REALTEK_DSA);
-+
- /**
- * rtl83xx_probe() - probe a Realtek switch
- * @dev: the device being probed
---- a/drivers/net/dsa/realtek/rtl83xx.h
-+++ b/drivers/net/dsa/realtek/rtl83xx.h
-@@ -10,6 +10,7 @@ struct realtek_interface_info {
-
- void rtl83xx_lock(void *ctx);
- void rtl83xx_unlock(void *ctx);
-+int rtl83xx_setup_user_mdio(struct dsa_switch *ds);
- struct realtek_priv *
- rtl83xx_probe(struct device *dev,
- const struct realtek_interface_info *interface_info);
+++ /dev/null
-From bba140a566ed075304c49c52ab32c0016cab624a Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Fri, 9 Feb 2024 02:03:46 -0300
-Subject: net: dsa: realtek: use the same mii bus driver for
- both interfaces
-
-The realtek-mdio will now use this driver instead of the generic DSA
-driver ("dsa user smi"), which should not be used with OF[1].
-
-With a single ds_ops for both interfaces, the ds_ops in realtek_priv is
-no longer necessary. Now, the realtek_variant.ds_ops can be used
-directly.
-
-The realtek_priv.setup_interface() has been removed as we can directly
-call the new common function.
-
-[1] https://lkml.kernel.org/netdev/20220630200423.tieprdu5fpabflj7@bang-olufsen.dk/T/
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/realtek-mdio.c | 1 -
- drivers/net/dsa/realtek/realtek-smi.c | 2 -
- drivers/net/dsa/realtek/realtek.h | 5 +--
- drivers/net/dsa/realtek/rtl8365mb.c | 49 +++---------------------
- drivers/net/dsa/realtek/rtl8366rb.c | 52 +++-----------------------
- drivers/net/dsa/realtek/rtl83xx.c | 2 +-
- 6 files changed, 14 insertions(+), 97 deletions(-)
-
---- a/drivers/net/dsa/realtek/realtek-mdio.c
-+++ b/drivers/net/dsa/realtek/realtek-mdio.c
-@@ -131,7 +131,6 @@ int realtek_mdio_probe(struct mdio_devic
- priv->bus = mdiodev->bus;
- priv->mdio_addr = mdiodev->addr;
- priv->write_reg_noack = realtek_mdio_write;
-- priv->ds_ops = priv->variant->ds_ops_mdio;
-
- ret = rtl83xx_register_switch(priv);
- if (ret) {
---- a/drivers/net/dsa/realtek/realtek-smi.c
-+++ b/drivers/net/dsa/realtek/realtek-smi.c
-@@ -352,8 +352,6 @@ int realtek_smi_probe(struct platform_de
- }
-
- priv->write_reg_noack = realtek_smi_write_reg_noack;
-- priv->setup_interface = rtl83xx_setup_user_mdio;
-- priv->ds_ops = priv->variant->ds_ops_smi;
-
- ret = rtl83xx_register_switch(priv);
- if (ret) {
---- a/drivers/net/dsa/realtek/realtek.h
-+++ b/drivers/net/dsa/realtek/realtek.h
-@@ -62,7 +62,6 @@ struct realtek_priv {
-
- spinlock_t lock; /* Locks around command writes */
- struct dsa_switch *ds;
-- const struct dsa_switch_ops *ds_ops;
- struct irq_domain *irqdomain;
- bool leds_disabled;
-
-@@ -73,7 +72,6 @@ struct realtek_priv {
- struct rtl8366_mib_counter *mib_counters;
-
- const struct realtek_ops *ops;
-- int (*setup_interface)(struct dsa_switch *ds);
- int (*write_reg_noack)(void *ctx, u32 addr, u32 data);
-
- int vlan_enabled;
-@@ -115,8 +113,7 @@ struct realtek_ops {
- };
-
- struct realtek_variant {
-- const struct dsa_switch_ops *ds_ops_smi;
-- const struct dsa_switch_ops *ds_ops_mdio;
-+ const struct dsa_switch_ops *ds_ops;
- const struct realtek_ops *ops;
- unsigned int clk_delay;
- u8 cmd_read;
---- a/drivers/net/dsa/realtek/rtl8365mb.c
-+++ b/drivers/net/dsa/realtek/rtl8365mb.c
-@@ -828,17 +828,6 @@ static int rtl8365mb_phy_write(struct re
- return 0;
- }
-
--static int rtl8365mb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum)
--{
-- return rtl8365mb_phy_read(ds->priv, phy, regnum);
--}
--
--static int rtl8365mb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum,
-- u16 val)
--{
-- return rtl8365mb_phy_write(ds->priv, phy, regnum, val);
--}
--
- static const struct rtl8365mb_extint *
- rtl8365mb_get_port_extint(struct realtek_priv *priv, int port)
- {
-@@ -2018,12 +2007,10 @@ static int rtl8365mb_setup(struct dsa_sw
- if (ret)
- goto out_teardown_irq;
-
-- if (priv->setup_interface) {
-- ret = priv->setup_interface(ds);
-- if (ret) {
-- dev_err(priv->dev, "could not set up MDIO bus\n");
-- goto out_teardown_irq;
-- }
-+ ret = rtl83xx_setup_user_mdio(ds);
-+ if (ret) {
-+ dev_err(priv->dev, "could not set up MDIO bus\n");
-+ goto out_teardown_irq;
- }
-
- /* Start statistics counter polling */
-@@ -2117,28 +2104,7 @@ static int rtl8365mb_detect(struct realt
- return 0;
- }
-
--static const struct dsa_switch_ops rtl8365mb_switch_ops_smi = {
-- .get_tag_protocol = rtl8365mb_get_tag_protocol,
-- .change_tag_protocol = rtl8365mb_change_tag_protocol,
-- .setup = rtl8365mb_setup,
-- .teardown = rtl8365mb_teardown,
-- .phylink_get_caps = rtl8365mb_phylink_get_caps,
-- .phylink_mac_config = rtl8365mb_phylink_mac_config,
-- .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down,
-- .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up,
-- .port_stp_state_set = rtl8365mb_port_stp_state_set,
-- .get_strings = rtl8365mb_get_strings,
-- .get_ethtool_stats = rtl8365mb_get_ethtool_stats,
-- .get_sset_count = rtl8365mb_get_sset_count,
-- .get_eth_phy_stats = rtl8365mb_get_phy_stats,
-- .get_eth_mac_stats = rtl8365mb_get_mac_stats,
-- .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats,
-- .get_stats64 = rtl8365mb_get_stats64,
-- .port_change_mtu = rtl8365mb_port_change_mtu,
-- .port_max_mtu = rtl8365mb_port_max_mtu,
--};
--
--static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = {
-+static const struct dsa_switch_ops rtl8365mb_switch_ops = {
- .get_tag_protocol = rtl8365mb_get_tag_protocol,
- .change_tag_protocol = rtl8365mb_change_tag_protocol,
- .setup = rtl8365mb_setup,
-@@ -2147,8 +2113,6 @@ static const struct dsa_switch_ops rtl83
- .phylink_mac_config = rtl8365mb_phylink_mac_config,
- .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down,
- .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up,
-- .phy_read = rtl8365mb_dsa_phy_read,
-- .phy_write = rtl8365mb_dsa_phy_write,
- .port_stp_state_set = rtl8365mb_port_stp_state_set,
- .get_strings = rtl8365mb_get_strings,
- .get_ethtool_stats = rtl8365mb_get_ethtool_stats,
-@@ -2168,8 +2132,7 @@ static const struct realtek_ops rtl8365m
- };
-
- const struct realtek_variant rtl8365mb_variant = {
-- .ds_ops_smi = &rtl8365mb_switch_ops_smi,
-- .ds_ops_mdio = &rtl8365mb_switch_ops_mdio,
-+ .ds_ops = &rtl8365mb_switch_ops,
- .ops = &rtl8365mb_ops,
- .clk_delay = 10,
- .cmd_read = 0xb9,
---- a/drivers/net/dsa/realtek/rtl8366rb.c
-+++ b/drivers/net/dsa/realtek/rtl8366rb.c
-@@ -1035,12 +1035,10 @@ static int rtl8366rb_setup(struct dsa_sw
- if (ret)
- dev_info(priv->dev, "no interrupt support\n");
-
-- if (priv->setup_interface) {
-- ret = priv->setup_interface(ds);
-- if (ret) {
-- dev_err(priv->dev, "could not set up MDIO bus\n");
-- return -ENODEV;
-- }
-+ ret = rtl83xx_setup_user_mdio(ds);
-+ if (ret) {
-+ dev_err(priv->dev, "could not set up MDIO bus\n");
-+ return -ENODEV;
- }
-
- return 0;
-@@ -1699,17 +1697,6 @@ out:
- return ret;
- }
-
--static int rtl8366rb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum)
--{
-- return rtl8366rb_phy_read(ds->priv, phy, regnum);
--}
--
--static int rtl8366rb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum,
-- u16 val)
--{
-- return rtl8366rb_phy_write(ds->priv, phy, regnum, val);
--}
--
- static int rtl8366rb_reset_chip(struct realtek_priv *priv)
- {
- int timeout = 10;
-@@ -1775,35 +1762,9 @@ static int rtl8366rb_detect(struct realt
- return 0;
- }
-
--static const struct dsa_switch_ops rtl8366rb_switch_ops_smi = {
-- .get_tag_protocol = rtl8366_get_tag_protocol,
-- .setup = rtl8366rb_setup,
-- .phylink_get_caps = rtl8366rb_phylink_get_caps,
-- .phylink_mac_link_up = rtl8366rb_mac_link_up,
-- .phylink_mac_link_down = rtl8366rb_mac_link_down,
-- .get_strings = rtl8366_get_strings,
-- .get_ethtool_stats = rtl8366_get_ethtool_stats,
-- .get_sset_count = rtl8366_get_sset_count,
-- .port_bridge_join = rtl8366rb_port_bridge_join,
-- .port_bridge_leave = rtl8366rb_port_bridge_leave,
-- .port_vlan_filtering = rtl8366rb_vlan_filtering,
-- .port_vlan_add = rtl8366_vlan_add,
-- .port_vlan_del = rtl8366_vlan_del,
-- .port_enable = rtl8366rb_port_enable,
-- .port_disable = rtl8366rb_port_disable,
-- .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags,
-- .port_bridge_flags = rtl8366rb_port_bridge_flags,
-- .port_stp_state_set = rtl8366rb_port_stp_state_set,
-- .port_fast_age = rtl8366rb_port_fast_age,
-- .port_change_mtu = rtl8366rb_change_mtu,
-- .port_max_mtu = rtl8366rb_max_mtu,
--};
--
--static const struct dsa_switch_ops rtl8366rb_switch_ops_mdio = {
-+static const struct dsa_switch_ops rtl8366rb_switch_ops = {
- .get_tag_protocol = rtl8366_get_tag_protocol,
- .setup = rtl8366rb_setup,
-- .phy_read = rtl8366rb_dsa_phy_read,
-- .phy_write = rtl8366rb_dsa_phy_write,
- .phylink_get_caps = rtl8366rb_phylink_get_caps,
- .phylink_mac_link_up = rtl8366rb_mac_link_up,
- .phylink_mac_link_down = rtl8366rb_mac_link_down,
-@@ -1842,8 +1803,7 @@ static const struct realtek_ops rtl8366r
- };
-
- const struct realtek_variant rtl8366rb_variant = {
-- .ds_ops_smi = &rtl8366rb_switch_ops_smi,
-- .ds_ops_mdio = &rtl8366rb_switch_ops_mdio,
-+ .ds_ops = &rtl8366rb_switch_ops,
- .ops = &rtl8366rb_ops,
- .clk_delay = 10,
- .cmd_read = 0xa9,
---- a/drivers/net/dsa/realtek/rtl83xx.c
-+++ b/drivers/net/dsa/realtek/rtl83xx.c
-@@ -233,7 +233,7 @@ int rtl83xx_register_switch(struct realt
-
- ds->priv = priv;
- ds->dev = priv->dev;
-- ds->ops = priv->ds_ops;
-+ ds->ops = priv->variant->ds_ops;
- ds->num_ports = priv->num_ports;
- priv->ds = ds;
-
+++ /dev/null
-From 9fc469b2943d9b1ff2a7800f823e7cd7a5cac0ca Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Fri, 9 Feb 2024 02:03:47 -0300
-Subject: net: dsa: realtek: embed dsa_switch into realtek_priv
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Embed dsa_switch within realtek_priv to eliminate the need for a second
-memory allocation.
-
-Suggested-by: Alvin Šipraga <alsi@bang-olufsen.dk>
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/realtek.h | 2 +-
- drivers/net/dsa/realtek/rtl8365mb.c | 15 +++++++++------
- drivers/net/dsa/realtek/rtl8366rb.c | 3 ++-
- drivers/net/dsa/realtek/rtl83xx.c | 15 +++++++--------
- 4 files changed, 19 insertions(+), 16 deletions(-)
-
---- a/drivers/net/dsa/realtek/realtek.h
-+++ b/drivers/net/dsa/realtek/realtek.h
-@@ -61,7 +61,7 @@ struct realtek_priv {
- const struct realtek_variant *variant;
-
- spinlock_t lock; /* Locks around command writes */
-- struct dsa_switch *ds;
-+ struct dsa_switch ds;
- struct irq_domain *irqdomain;
- bool leds_disabled;
-
---- a/drivers/net/dsa/realtek/rtl8365mb.c
-+++ b/drivers/net/dsa/realtek/rtl8365mb.c
-@@ -870,6 +870,7 @@ static int rtl8365mb_ext_config_rgmii(st
- {
- const struct rtl8365mb_extint *extint =
- rtl8365mb_get_port_extint(priv, port);
-+ struct dsa_switch *ds = &priv->ds;
- struct device_node *dn;
- struct dsa_port *dp;
- int tx_delay = 0;
-@@ -880,7 +881,7 @@ static int rtl8365mb_ext_config_rgmii(st
- if (!extint)
- return -ENODEV;
-
-- dp = dsa_to_port(priv->ds, port);
-+ dp = dsa_to_port(ds, port);
- dn = dp->dn;
-
- /* Set the RGMII TX/RX delay
-@@ -1534,6 +1535,7 @@ static void rtl8365mb_get_stats64(struct
- static void rtl8365mb_stats_setup(struct realtek_priv *priv)
- {
- struct rtl8365mb *mb = priv->chip_data;
-+ struct dsa_switch *ds = &priv->ds;
- int i;
-
- /* Per-chip global mutex to protect MIB counter access, since doing
-@@ -1544,7 +1546,7 @@ static void rtl8365mb_stats_setup(struct
- for (i = 0; i < priv->num_ports; i++) {
- struct rtl8365mb_port *p = &mb->ports[i];
-
-- if (dsa_is_unused_port(priv->ds, i))
-+ if (dsa_is_unused_port(ds, i))
- continue;
-
- /* Per-port spinlock to protect the stats64 data */
-@@ -1560,12 +1562,13 @@ static void rtl8365mb_stats_setup(struct
- static void rtl8365mb_stats_teardown(struct realtek_priv *priv)
- {
- struct rtl8365mb *mb = priv->chip_data;
-+ struct dsa_switch *ds = &priv->ds;
- int i;
-
- for (i = 0; i < priv->num_ports; i++) {
- struct rtl8365mb_port *p = &mb->ports[i];
-
-- if (dsa_is_unused_port(priv->ds, i))
-+ if (dsa_is_unused_port(ds, i))
- continue;
-
- cancel_delayed_work_sync(&p->mib_work);
-@@ -1964,7 +1967,7 @@ static int rtl8365mb_setup(struct dsa_sw
- dev_info(priv->dev, "no interrupt support\n");
-
- /* Configure CPU tagging */
-- dsa_switch_for_each_cpu_port(cpu_dp, priv->ds) {
-+ dsa_switch_for_each_cpu_port(cpu_dp, ds) {
- cpu->mask |= BIT(cpu_dp->index);
-
- if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS)
-@@ -1979,7 +1982,7 @@ static int rtl8365mb_setup(struct dsa_sw
- for (i = 0; i < priv->num_ports; i++) {
- struct rtl8365mb_port *p = &mb->ports[i];
-
-- if (dsa_is_unused_port(priv->ds, i))
-+ if (dsa_is_unused_port(ds, i))
- continue;
-
- /* Forward only to the CPU */
-@@ -1996,7 +1999,7 @@ static int rtl8365mb_setup(struct dsa_sw
- * ports will still forward frames to the CPU despite being
- * administratively down by default.
- */
-- rtl8365mb_port_stp_state_set(priv->ds, i, BR_STATE_DISABLED);
-+ rtl8365mb_port_stp_state_set(ds, i, BR_STATE_DISABLED);
-
- /* Set up per-port private data */
- p->priv = priv;
---- a/drivers/net/dsa/realtek/rtl8366rb.c
-+++ b/drivers/net/dsa/realtek/rtl8366rb.c
-@@ -1565,6 +1565,7 @@ static int rtl8366rb_get_mc_index(struct
-
- static int rtl8366rb_set_mc_index(struct realtek_priv *priv, int port, int index)
- {
-+ struct dsa_switch *ds = &priv->ds;
- struct rtl8366rb *rb;
- bool pvid_enabled;
- int ret;
-@@ -1589,7 +1590,7 @@ static int rtl8366rb_set_mc_index(struct
- * not drop any untagged or C-tagged frames. Make sure to update the
- * filtering setting.
- */
-- if (dsa_port_is_vlan_filtering(dsa_to_port(priv->ds, port)))
-+ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port)))
- ret = rtl8366rb_drop_untagged(priv, port, !pvid_enabled);
-
- return ret;
---- a/drivers/net/dsa/realtek/rtl83xx.c
-+++ b/drivers/net/dsa/realtek/rtl83xx.c
-@@ -218,7 +218,7 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, REAL
- */
- int rtl83xx_register_switch(struct realtek_priv *priv)
- {
-- struct dsa_switch *ds;
-+ struct dsa_switch *ds = &priv->ds;
- int ret;
-
- ret = priv->ops->detect(priv);
-@@ -227,15 +227,10 @@ int rtl83xx_register_switch(struct realt
- return ret;
- }
-
-- ds = devm_kzalloc(priv->dev, sizeof(*ds), GFP_KERNEL);
-- if (!ds)
-- return -ENOMEM;
--
- ds->priv = priv;
- ds->dev = priv->dev;
- ds->ops = priv->variant->ds_ops;
- ds->num_ports = priv->num_ports;
-- priv->ds = ds;
-
- ret = dsa_register_switch(ds);
- if (ret) {
-@@ -258,7 +253,9 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_register_sw
- */
- void rtl83xx_unregister_switch(struct realtek_priv *priv)
- {
-- dsa_unregister_switch(priv->ds);
-+ struct dsa_switch *ds = &priv->ds;
-+
-+ dsa_unregister_switch(ds);
- }
- EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, REALTEK_DSA);
-
-@@ -275,7 +272,9 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_
- */
- void rtl83xx_shutdown(struct realtek_priv *priv)
- {
-- dsa_switch_shutdown(priv->ds);
-+ struct dsa_switch *ds = &priv->ds;
-+
-+ dsa_switch_shutdown(ds);
-
- dev_set_drvdata(priv->dev, NULL);
- }
+++ /dev/null
-From f058b2dd70b1a5503dff899010aeb53b436091e5 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Wed, 28 Feb 2024 18:24:09 +0100
-Subject: [PATCH 1/2] net: phy: qcom: qca808x: add helper for checking for 1G
- only model
-
-There are 2 versions of QCA808x, one 2.5G capable and one 1G capable.
-Currently, this matter only in the .get_features call however, it will
-be required for filling supported interface modes so lets add a helper
-that can be reused.
-
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/qcom/qca808x.c | 17 ++++++++++++-----
- 1 file changed, 12 insertions(+), 5 deletions(-)
-
---- a/drivers/net/phy/qcom/qca808x.c
-+++ b/drivers/net/phy/qcom/qca808x.c
-@@ -156,6 +156,17 @@ static bool qca808x_has_fast_retrain_or_
- return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
- }
-
-+static bool qca808x_is_1g_only(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
-+ if (ret < 0)
-+ return true;
-+
-+ return !!(QCA808X_PHY_CHIP_TYPE_1G & ret);
-+}
-+
- static int qca808x_probe(struct phy_device *phydev)
- {
- struct device *dev = &phydev->mdio.dev;
-@@ -350,11 +361,7 @@ static int qca808x_get_features(struct p
- * existed in the bit0 of MMD1.21, we need to remove it manually if
- * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
- */
-- ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
-- if (ret < 0)
-- return ret;
--
-- if (QCA808X_PHY_CHIP_TYPE_1G & ret)
-+ if (qca808x_is_1g_only(phydev))
- linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
-
- return 0;
+++ /dev/null
-From cb28f702960695e26597c332b0e46776e825cc34 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Wed, 28 Feb 2024 18:24:10 +0100
-Subject: [PATCH 2/2] net: phy: qcom: qca808x: fill in possible_interfaces
-
-Currently QCA808x driver does not fill the possible_interfaces.
-2.5G QCA808x support SGMII and 2500Base-X while 1G model only supports
-SGMII, so fill the possible_interfaces accordingly.
-
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/qcom/qca808x.c | 12 ++++++++++++
- 1 file changed, 12 insertions(+)
-
---- a/drivers/net/phy/qcom/qca808x.c
-+++ b/drivers/net/phy/qcom/qca808x.c
-@@ -167,6 +167,16 @@ static bool qca808x_is_1g_only(struct ph
- return !!(QCA808X_PHY_CHIP_TYPE_1G & ret);
- }
-
-+static void qca808x_fill_possible_interfaces(struct phy_device *phydev)
-+{
-+ unsigned long *possible = phydev->possible_interfaces;
-+
-+ __set_bit(PHY_INTERFACE_MODE_SGMII, possible);
-+
-+ if (!qca808x_is_1g_only(phydev))
-+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, possible);
-+}
-+
- static int qca808x_probe(struct phy_device *phydev)
- {
- struct device *dev = &phydev->mdio.dev;
-@@ -231,6 +241,8 @@ static int qca808x_config_init(struct ph
- }
- }
-
-+ qca808x_fill_possible_interfaces(phydev);
-+
- /* Configure adc threshold as 100mv for the link 10M */
- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
- QCA808X_ADC_THRESHOLD_MASK,
+++ /dev/null
-From 32e4a5447ed9fa904a2dfcf4609c64bce053b4e8 Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Mon, 12 Feb 2024 18:34:33 -0300
-Subject: [PATCH] net: dsa: realtek: fix digital interface select macro for
- EXT0
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-While no supported devices currently utilize EXT0, the register reserves
-the bits for an EXT0. EXT0 is utilized by devices from the generation
-prior to rtl8365mb, such as those supported by the driver library
-rtl8367b.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Link: https://lore.kernel.org/r/20240212-realtek-fix_ext0-v1-1-f3d2536d191a@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/realtek/rtl8365mb.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/net/dsa/realtek/rtl8365mb.c
-+++ b/drivers/net/dsa/realtek/rtl8365mb.c
-@@ -209,10 +209,10 @@
- #define RTL8365MB_EXT_PORT_MODE_100FX 13
-
- /* External interface mode configuration registers 0~1 */
--#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT1 */
-+#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT0,EXT1 */
- #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 /* EXT2 */
- #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \
-- ((_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \
-+ ((_extint) <= 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \
- (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \
- 0x0)
- #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \
+++ /dev/null
-From 6a804fb72de56d6a99b799f565ae45f2cec7cd55 Mon Sep 17 00:00:00 2001
-From: Sridharan S N <quic_sridsn@quicinc.com>
-Date: Thu, 12 Oct 2023 12:11:34 +0530
-Subject: mtd: spinand: winbond: add support for serial NAND flash
-
-Add support for W25N01JW, W25N02JWZEIF, W25N512GW,
-W25N02KWZEIR and W25N01GWZEIG.
-
-W25N02KWZEIR has 8b/512b on-die ECC capability and other
-four has 4b/512b on-die ECC capability.
-
-Signed-off-by: Sridharan S N <quic_sridsn@quicinc.com>
-Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/20231012064134.4068621-1-quic_sridsn@quicinc.com
----
- drivers/mtd/nand/spi/winbond.c | 45 ++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 45 insertions(+)
-
-(limited to 'drivers/mtd/nand/spi/winbond.c')
-
---- a/drivers/mtd/nand/spi/winbond.c
-+++ b/drivers/mtd/nand/spi/winbond.c
-@@ -195,6 +195,51 @@ static const struct spinand_info winbond
- &update_cache_variants),
- 0,
- SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
-+ SPINAND_INFO("W25N01JW",
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbc, 0x21),
-+ NAND_MEMORG(1, 2048, 64, 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(&w25m02gv_ooblayout, w25n02kv_ecc_get_status)),
-+ SPINAND_INFO("W25N02JWZEIF",
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbf, 0x22),
-+ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 2, 1),
-+ NAND_ECCREQ(4, 512),
-+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+ &write_cache_variants,
-+ &update_cache_variants),
-+ 0,
-+ SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
-+ SPINAND_INFO("W25N512GW",
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xba, 0x20),
-+ NAND_MEMORG(1, 2048, 64, 64, 512, 10, 1, 1, 1),
-+ NAND_ECCREQ(4, 512),
-+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+ &write_cache_variants,
-+ &update_cache_variants),
-+ 0,
-+ SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
-+ SPINAND_INFO("W25N02KWZEIR",
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xba, 0x22),
-+ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
-+ NAND_ECCREQ(8, 512),
-+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+ &write_cache_variants,
-+ &update_cache_variants),
-+ 0,
-+ SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
-+ SPINAND_INFO("W25N01GWZEIG",
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xba, 0x21),
-+ NAND_MEMORG(1, 2048, 64, 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(&w25m02gv_ooblayout, w25n02kv_ecc_get_status)),
- };
-
- static int winbond_spinand_init(struct spinand_device *spinand)
+++ /dev/null
-From 28001bb1955fcfa63e535848c4289fcd7bb88daf Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Sun, 25 Feb 2024 13:29:53 -0300
-Subject: [PATCH 1/3] dt-bindings: net: dsa: realtek: reset-gpios is not
- required
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The 'reset-gpios' should not be mandatory. although they might be
-required for some devices if the switch reset was left asserted by a
-previous driver, such as the bootloader.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Cc: devicetree@vger.kernel.org
-Acked-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Acked-by: Rob Herring <robh@kernel.org>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- Documentation/devicetree/bindings/net/dsa/realtek.yaml | 1 -
- 1 file changed, 1 deletion(-)
-
---- a/Documentation/devicetree/bindings/net/dsa/realtek.yaml
-+++ b/Documentation/devicetree/bindings/net/dsa/realtek.yaml
-@@ -125,7 +125,6 @@ else:
- - mdc-gpios
- - mdio-gpios
- - mdio
-- - reset-gpios
-
- required:
- - compatible
+++ /dev/null
-From 5fc2d68fc81801162188995e4d3dc0b26747dd76 Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Sun, 25 Feb 2024 13:29:54 -0300
-Subject: [PATCH 2/3] dt-bindings: net: dsa: realtek: add reset controller
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Realtek switches can use a reset controller instead of reset-gpios.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Cc: devicetree@vger.kernel.org
-Acked-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
-Acked-by: Rob Herring <robh@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- Documentation/devicetree/bindings/net/dsa/realtek.yaml | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/Documentation/devicetree/bindings/net/dsa/realtek.yaml
-+++ b/Documentation/devicetree/bindings/net/dsa/realtek.yaml
-@@ -59,6 +59,9 @@ properties:
- description: GPIO to be used to reset the whole device
- maxItems: 1
-
-+ resets:
-+ maxItems: 1
-+
- realtek,disable-leds:
- type: boolean
- description: |
+++ /dev/null
-From 56998aa6b7f0f31ce8df23c00701af2d8e8a1f1a Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Sun, 25 Feb 2024 13:29:55 -0300
-Subject: [PATCH 3/3] net: dsa: realtek: support reset controller
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add support for resetting the device using a reset controller,
-complementing the existing GPIO reset functionality (reset-gpios).
-
-Although the reset is optional and the driver performs a soft reset
-during setup, if the initial reset pin state was asserted, the driver
-will not detect the device until the reset is deasserted.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/realtek.h | 2 ++
- drivers/net/dsa/realtek/rtl83xx.c | 42 +++++++++++++++++++++++++++----
- drivers/net/dsa/realtek/rtl83xx.h | 2 ++
- 3 files changed, 41 insertions(+), 5 deletions(-)
-
---- a/drivers/net/dsa/realtek/realtek.h
-+++ b/drivers/net/dsa/realtek/realtek.h
-@@ -12,6 +12,7 @@
- #include <linux/platform_device.h>
- #include <linux/gpio/consumer.h>
- #include <net/dsa.h>
-+#include <linux/reset.h>
-
- #define REALTEK_HW_STOP_DELAY 25 /* msecs */
- #define REALTEK_HW_START_DELAY 100 /* msecs */
-@@ -48,6 +49,7 @@ struct rtl8366_vlan_4k {
-
- struct realtek_priv {
- struct device *dev;
-+ struct reset_control *reset_ctl;
- struct gpio_desc *reset;
- struct gpio_desc *mdc;
- struct gpio_desc *mdio;
---- a/drivers/net/dsa/realtek/rtl83xx.c
-+++ b/drivers/net/dsa/realtek/rtl83xx.c
-@@ -185,6 +185,13 @@ rtl83xx_probe(struct device *dev,
- "realtek,disable-leds");
-
- /* TODO: if power is software controlled, set up any regulators here */
-+ priv->reset_ctl = devm_reset_control_get_optional(dev, NULL);
-+ if (IS_ERR(priv->reset_ctl)) {
-+ ret = PTR_ERR(priv->reset_ctl);
-+ dev_err_probe(dev, ret, "failed to get reset control\n");
-+ return ERR_CAST(priv->reset_ctl);
-+ }
-+
- priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
- if (IS_ERR(priv->reset)) {
- dev_err(dev, "failed to get RESET GPIO\n");
-@@ -193,11 +200,11 @@ rtl83xx_probe(struct device *dev,
-
- dev_set_drvdata(dev, priv);
-
-- if (priv->reset) {
-- gpiod_set_value(priv->reset, 1);
-+ if (priv->reset_ctl || priv->reset) {
-+ rtl83xx_reset_assert(priv);
- dev_dbg(dev, "asserted RESET\n");
- msleep(REALTEK_HW_STOP_DELAY);
-- gpiod_set_value(priv->reset, 0);
-+ rtl83xx_reset_deassert(priv);
- msleep(REALTEK_HW_START_DELAY);
- dev_dbg(dev, "deasserted RESET\n");
- }
-@@ -293,11 +300,36 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, R
- void rtl83xx_remove(struct realtek_priv *priv)
- {
- /* leave the device reset asserted */
-- if (priv->reset)
-- gpiod_set_value(priv->reset, 1);
-+ rtl83xx_reset_assert(priv);
- }
- EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA);
-
-+void rtl83xx_reset_assert(struct realtek_priv *priv)
-+{
-+ int ret;
-+
-+ ret = reset_control_assert(priv->reset_ctl);
-+ if (ret)
-+ dev_warn(priv->dev,
-+ "Failed to assert the switch reset control: %pe\n",
-+ ERR_PTR(ret));
-+
-+ gpiod_set_value(priv->reset, true);
-+}
-+
-+void rtl83xx_reset_deassert(struct realtek_priv *priv)
-+{
-+ int ret;
-+
-+ ret = reset_control_deassert(priv->reset_ctl);
-+ if (ret)
-+ dev_warn(priv->dev,
-+ "Failed to deassert the switch reset control: %pe\n",
-+ ERR_PTR(ret));
-+
-+ gpiod_set_value(priv->reset, false);
-+}
-+
- MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
- MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
- MODULE_DESCRIPTION("Realtek DSA switches common module");
---- a/drivers/net/dsa/realtek/rtl83xx.h
-+++ b/drivers/net/dsa/realtek/rtl83xx.h
-@@ -18,5 +18,7 @@ int rtl83xx_register_switch(struct realt
- void rtl83xx_unregister_switch(struct realtek_priv *priv);
- void rtl83xx_shutdown(struct realtek_priv *priv);
- void rtl83xx_remove(struct realtek_priv *priv);
-+void rtl83xx_reset_assert(struct realtek_priv *priv);
-+void rtl83xx_reset_deassert(struct realtek_priv *priv);
-
- #endif /* _RTL83XX_H */
+++ /dev/null
-From 4f580e9aced1816398c1c64f178302a22b8ea6e2 Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Sat, 27 Apr 2024 02:11:29 -0300
-Subject: [PATCH 2/3] net: dsa: realtek: do not assert reset on remove
-
-The necessity of asserting the reset on removal was previously
-questioned, as DSA's own cleanup methods should suffice to prevent
-traffic leakage[1].
-
-When a driver has subdrivers controlled by devres, they will be
-unregistered after the main driver's .remove is executed. If it asserts
-a reset, the subdrivers will be unable to communicate with the hardware
-during their cleanup. For LEDs, this means that they will fail to turn
-off, resulting in a timeout error.
-
-[1] https://lore.kernel.org/r/20240123215606.26716-9-luizluca@gmail.com/
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/rtl83xx.c | 7 ++-----
- 1 file changed, 2 insertions(+), 5 deletions(-)
-
---- a/drivers/net/dsa/realtek/rtl83xx.c
-+++ b/drivers/net/dsa/realtek/rtl83xx.c
-@@ -291,16 +291,13 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, R
- * rtl83xx_remove() - Cleanup a realtek switch driver
- * @priv: realtek_priv pointer
- *
-- * If a method is provided, this function asserts the hard reset of the switch
-- * in order to avoid leaking traffic when the driver is gone.
-+ * Placehold for common cleanup procedures.
- *
-- * Context: Might sleep if priv->gdev->chip->can_sleep.
-+ * Context: Any
- * Return: nothing
- */
- void rtl83xx_remove(struct realtek_priv *priv)
- {
-- /* leave the device reset asserted */
-- rtl83xx_reset_assert(priv);
- }
- EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA);
-
+++ /dev/null
-From 32d617005475a71ebcc4ec8b2791e8d1481e9a10 Mon Sep 17 00:00:00 2001
-From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Date: Sat, 27 Apr 2024 02:11:30 -0300
-Subject: [PATCH 3/3] net: dsa: realtek: add LED drivers for rtl8366rb
-
-This commit introduces LED drivers for rtl8366rb, enabling LEDs to be
-described in the device tree using the same format as qca8k. Each port
-can configure up to 4 LEDs.
-
-If all LEDs in a group use the default state "keep", they will use the
-default behavior after a reset. Changing the brightness of one LED,
-either manually or by a trigger, will disable the default hardware
-trigger and switch the entire LED group to manually controlled LEDs.
-Once in this mode, there is no way to revert to hardware-controlled LEDs
-(except by resetting the switch).
-
-Software triggers function as expected with manually controlled LEDs.
-
-Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/realtek/rtl8366rb.c | 304 ++++++++++++++++++++++++----
- 1 file changed, 265 insertions(+), 39 deletions(-)
-
---- a/drivers/net/dsa/realtek/rtl8366rb.c
-+++ b/drivers/net/dsa/realtek/rtl8366rb.c
-@@ -180,6 +180,7 @@
- #define RTL8366RB_VLAN_INGRESS_CTRL2_REG 0x037f
-
- /* LED control registers */
-+/* The LED blink rate is global; it is used by all triggers in all groups. */
- #define RTL8366RB_LED_BLINKRATE_REG 0x0430
- #define RTL8366RB_LED_BLINKRATE_MASK 0x0007
- #define RTL8366RB_LED_BLINKRATE_28MS 0x0000
-@@ -195,31 +196,21 @@
- (4 * (led_group))
- #define RTL8366RB_LED_CTRL_MASK(led_group) \
- (0xf << RTL8366RB_LED_CTRL_OFFSET(led_group))
--#define RTL8366RB_LED_OFF 0x0
--#define RTL8366RB_LED_DUP_COL 0x1
--#define RTL8366RB_LED_LINK_ACT 0x2
--#define RTL8366RB_LED_SPD1000 0x3
--#define RTL8366RB_LED_SPD100 0x4
--#define RTL8366RB_LED_SPD10 0x5
--#define RTL8366RB_LED_SPD1000_ACT 0x6
--#define RTL8366RB_LED_SPD100_ACT 0x7
--#define RTL8366RB_LED_SPD10_ACT 0x8
--#define RTL8366RB_LED_SPD100_10_ACT 0x9
--#define RTL8366RB_LED_FIBER 0xa
--#define RTL8366RB_LED_AN_FAULT 0xb
--#define RTL8366RB_LED_LINK_RX 0xc
--#define RTL8366RB_LED_LINK_TX 0xd
--#define RTL8366RB_LED_MASTER 0xe
--#define RTL8366RB_LED_FORCE 0xf
-
- /* The RTL8366RB_LED_X_X registers are used to manually set the LED state only
- * when the corresponding LED group in RTL8366RB_LED_CTRL_REG is
-- * RTL8366RB_LED_FORCE. Otherwise, it is ignored.
-+ * RTL8366RB_LEDGROUP_FORCE. Otherwise, it is ignored.
- */
- #define RTL8366RB_LED_0_1_CTRL_REG 0x0432
--#define RTL8366RB_LED_1_OFFSET 6
- #define RTL8366RB_LED_2_3_CTRL_REG 0x0433
--#define RTL8366RB_LED_3_OFFSET 6
-+#define RTL8366RB_LED_X_X_CTRL_REG(led_group) \
-+ ((led_group) <= 1 ? \
-+ RTL8366RB_LED_0_1_CTRL_REG : \
-+ RTL8366RB_LED_2_3_CTRL_REG)
-+#define RTL8366RB_LED_0_X_CTRL_MASK GENMASK(5, 0)
-+#define RTL8366RB_LED_X_1_CTRL_MASK GENMASK(11, 6)
-+#define RTL8366RB_LED_2_X_CTRL_MASK GENMASK(5, 0)
-+#define RTL8366RB_LED_X_3_CTRL_MASK GENMASK(11, 6)
-
- #define RTL8366RB_MIB_COUNT 33
- #define RTL8366RB_GLOBAL_MIB_COUNT 1
-@@ -363,14 +354,44 @@
- #define RTL8366RB_GREEN_FEATURE_TX BIT(0)
- #define RTL8366RB_GREEN_FEATURE_RX BIT(2)
-
-+enum rtl8366_ledgroup_mode {
-+ RTL8366RB_LEDGROUP_OFF = 0x0,
-+ RTL8366RB_LEDGROUP_DUP_COL = 0x1,
-+ RTL8366RB_LEDGROUP_LINK_ACT = 0x2,
-+ RTL8366RB_LEDGROUP_SPD1000 = 0x3,
-+ RTL8366RB_LEDGROUP_SPD100 = 0x4,
-+ RTL8366RB_LEDGROUP_SPD10 = 0x5,
-+ RTL8366RB_LEDGROUP_SPD1000_ACT = 0x6,
-+ RTL8366RB_LEDGROUP_SPD100_ACT = 0x7,
-+ RTL8366RB_LEDGROUP_SPD10_ACT = 0x8,
-+ RTL8366RB_LEDGROUP_SPD100_10_ACT = 0x9,
-+ RTL8366RB_LEDGROUP_FIBER = 0xa,
-+ RTL8366RB_LEDGROUP_AN_FAULT = 0xb,
-+ RTL8366RB_LEDGROUP_LINK_RX = 0xc,
-+ RTL8366RB_LEDGROUP_LINK_TX = 0xd,
-+ RTL8366RB_LEDGROUP_MASTER = 0xe,
-+ RTL8366RB_LEDGROUP_FORCE = 0xf,
-+
-+ __RTL8366RB_LEDGROUP_MODE_MAX
-+};
-+
-+struct rtl8366rb_led {
-+ u8 port_num;
-+ u8 led_group;
-+ struct realtek_priv *priv;
-+ struct led_classdev cdev;
-+};
-+
- /**
- * struct rtl8366rb - RTL8366RB-specific data
- * @max_mtu: per-port max MTU setting
- * @pvid_enabled: if PVID is set for respective port
-+ * @leds: per-port and per-ledgroup led info
- */
- struct rtl8366rb {
- unsigned int max_mtu[RTL8366RB_NUM_PORTS];
- bool pvid_enabled[RTL8366RB_NUM_PORTS];
-+ struct rtl8366rb_led leds[RTL8366RB_NUM_PORTS][RTL8366RB_NUM_LEDGROUPS];
- };
-
- static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = {
-@@ -813,6 +834,217 @@ static int rtl8366rb_jam_table(const str
- return 0;
- }
-
-+static int rb8366rb_set_ledgroup_mode(struct realtek_priv *priv,
-+ u8 led_group,
-+ enum rtl8366_ledgroup_mode mode)
-+{
-+ int ret;
-+ u32 val;
-+
-+ val = mode << RTL8366RB_LED_CTRL_OFFSET(led_group);
-+
-+ ret = regmap_update_bits(priv->map,
-+ RTL8366RB_LED_CTRL_REG,
-+ RTL8366RB_LED_CTRL_MASK(led_group),
-+ val);
-+ if (ret)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static inline u32 rtl8366rb_led_group_port_mask(u8 led_group, u8 port)
-+{
-+ switch (led_group) {
-+ case 0:
-+ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port));
-+ case 1:
-+ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port));
-+ case 2:
-+ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port));
-+ case 3:
-+ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port));
-+ default:
-+ return 0;
-+ }
-+}
-+
-+static int rb8366rb_get_port_led(struct rtl8366rb_led *led)
-+{
-+ struct realtek_priv *priv = led->priv;
-+ u8 led_group = led->led_group;
-+ u8 port_num = led->port_num;
-+ int ret;
-+ u32 val;
-+
-+ ret = regmap_read(priv->map, RTL8366RB_LED_X_X_CTRL_REG(led_group),
-+ &val);
-+ if (ret) {
-+ dev_err(priv->dev, "error reading LED on port %d group %d\n",
-+ led_group, port_num);
-+ return ret;
-+ }
-+
-+ return !!(val & rtl8366rb_led_group_port_mask(led_group, port_num));
-+}
-+
-+static int rb8366rb_set_port_led(struct rtl8366rb_led *led, bool enable)
-+{
-+ struct realtek_priv *priv = led->priv;
-+ u8 led_group = led->led_group;
-+ u8 port_num = led->port_num;
-+ int ret;
-+
-+ ret = regmap_update_bits(priv->map,
-+ RTL8366RB_LED_X_X_CTRL_REG(led_group),
-+ rtl8366rb_led_group_port_mask(led_group,
-+ port_num),
-+ enable ? 0xffff : 0);
-+ if (ret) {
-+ dev_err(priv->dev, "error updating LED on port %d group %d\n",
-+ led_group, port_num);
-+ return ret;
-+ }
-+
-+ /* Change the LED group to manual controlled LEDs if required */
-+ ret = rb8366rb_set_ledgroup_mode(priv, led_group,
-+ RTL8366RB_LEDGROUP_FORCE);
-+
-+ if (ret) {
-+ dev_err(priv->dev, "error updating LED GROUP group %d\n",
-+ led_group);
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+rtl8366rb_cled_brightness_set_blocking(struct led_classdev *ldev,
-+ enum led_brightness brightness)
-+{
-+ struct rtl8366rb_led *led = container_of(ldev, struct rtl8366rb_led,
-+ cdev);
-+
-+ return rb8366rb_set_port_led(led, brightness == LED_ON);
-+}
-+
-+static int rtl8366rb_setup_led(struct realtek_priv *priv, struct dsa_port *dp,
-+ struct fwnode_handle *led_fwnode)
-+{
-+ struct rtl8366rb *rb = priv->chip_data;
-+ struct led_init_data init_data = { };
-+ enum led_default_state state;
-+ struct rtl8366rb_led *led;
-+ u32 led_group;
-+ int ret;
-+
-+ ret = fwnode_property_read_u32(led_fwnode, "reg", &led_group);
-+ if (ret)
-+ return ret;
-+
-+ if (led_group >= RTL8366RB_NUM_LEDGROUPS) {
-+ dev_warn(priv->dev, "Invalid LED reg %d defined for port %d",
-+ led_group, dp->index);
-+ return -EINVAL;
-+ }
-+
-+ led = &rb->leds[dp->index][led_group];
-+ led->port_num = dp->index;
-+ led->led_group = led_group;
-+ led->priv = priv;
-+
-+ state = led_init_default_state_get(led_fwnode);
-+ switch (state) {
-+ case LEDS_DEFSTATE_ON:
-+ led->cdev.brightness = 1;
-+ rb8366rb_set_port_led(led, 1);
-+ break;
-+ case LEDS_DEFSTATE_KEEP:
-+ led->cdev.brightness =
-+ rb8366rb_get_port_led(led);
-+ break;
-+ case LEDS_DEFSTATE_OFF:
-+ default:
-+ led->cdev.brightness = 0;
-+ rb8366rb_set_port_led(led, 0);
-+ }
-+
-+ led->cdev.max_brightness = 1;
-+ led->cdev.brightness_set_blocking =
-+ rtl8366rb_cled_brightness_set_blocking;
-+ init_data.fwnode = led_fwnode;
-+ init_data.devname_mandatory = true;
-+
-+ init_data.devicename = kasprintf(GFP_KERNEL, "Realtek-%d:0%d:%d",
-+ dp->ds->index, dp->index, led_group);
-+ if (!init_data.devicename)
-+ return -ENOMEM;
-+
-+ ret = devm_led_classdev_register_ext(priv->dev, &led->cdev, &init_data);
-+ if (ret) {
-+ dev_warn(priv->dev, "Failed to init LED %d for port %d",
-+ led_group, dp->index);
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int rtl8366rb_setup_all_leds_off(struct realtek_priv *priv)
-+{
-+ int ret = 0;
-+ int i;
-+
-+ regmap_update_bits(priv->map,
-+ RTL8366RB_INTERRUPT_CONTROL_REG,
-+ RTL8366RB_P4_RGMII_LED,
-+ 0);
-+
-+ for (i = 0; i < RTL8366RB_NUM_LEDGROUPS; i++) {
-+ ret = rb8366rb_set_ledgroup_mode(priv, i,
-+ RTL8366RB_LEDGROUP_OFF);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ return ret;
-+}
-+
-+static int rtl8366rb_setup_leds(struct realtek_priv *priv)
-+{
-+ struct device_node *leds_np, *led_np;
-+ struct dsa_switch *ds = &priv->ds;
-+ struct dsa_port *dp;
-+ int ret = 0;
-+
-+ dsa_switch_for_each_port(dp, ds) {
-+ if (!dp->dn)
-+ continue;
-+
-+ leds_np = of_get_child_by_name(dp->dn, "leds");
-+ if (!leds_np) {
-+ dev_dbg(priv->dev, "No leds defined for port %d",
-+ dp->index);
-+ continue;
-+ }
-+
-+ for_each_child_of_node(leds_np, led_np) {
-+ ret = rtl8366rb_setup_led(priv, dp,
-+ of_fwnode_handle(led_np));
-+ if (ret) {
-+ of_node_put(led_np);
-+ break;
-+ }
-+ }
-+
-+ of_node_put(leds_np);
-+ if (ret)
-+ return ret;
-+ }
-+ return 0;
-+}
-+
- static int rtl8366rb_setup(struct dsa_switch *ds)
- {
- struct realtek_priv *priv = ds->priv;
-@@ -821,7 +1053,6 @@ static int rtl8366rb_setup(struct dsa_sw
- u32 chip_ver = 0;
- u32 chip_id = 0;
- int jam_size;
-- u32 val;
- int ret;
- int i;
-
-@@ -997,7 +1228,9 @@ static int rtl8366rb_setup(struct dsa_sw
- if (ret)
- return ret;
-
-- /* Set blinking, TODO: make this configurable */
-+ /* Set blinking, used by all LED groups using HW triggers.
-+ * TODO: make this configurable
-+ */
- ret = regmap_update_bits(priv->map, RTL8366RB_LED_BLINKRATE_REG,
- RTL8366RB_LED_BLINKRATE_MASK,
- RTL8366RB_LED_BLINKRATE_56MS);
-@@ -1005,26 +1238,19 @@ static int rtl8366rb_setup(struct dsa_sw
- return ret;
-
- /* Set up LED activity:
-- * Each port has 4 LEDs, we configure all ports to the same
-- * behaviour (no individual config) but we can set up each
-- * LED separately.
-+ * Each port has 4 LEDs on fixed groups. Each group shares the same
-+ * hardware trigger across all ports. LEDs can only be indiviually
-+ * controlled setting the LED group to fixed mode and using the driver
-+ * to toggle them LEDs on/off.
- */
- if (priv->leds_disabled) {
-- /* Turn everything off */
-- regmap_update_bits(priv->map,
-- RTL8366RB_INTERRUPT_CONTROL_REG,
-- RTL8366RB_P4_RGMII_LED,
-- 0);
--
-- for (i = 0; i < RTL8366RB_NUM_LEDGROUPS; i++) {
-- val = RTL8366RB_LED_OFF << RTL8366RB_LED_CTRL_OFFSET(i);
-- ret = regmap_update_bits(priv->map,
-- RTL8366RB_LED_CTRL_REG,
-- RTL8366RB_LED_CTRL_MASK(i),
-- val);
-- if (ret)
-- return ret;
-- }
-+ ret = rtl8366rb_setup_all_leds_off(priv);
-+ if (ret)
-+ return ret;
-+ } else {
-+ ret = rtl8366rb_setup_leds(priv);
-+ if (ret)
-+ return ret;
- }
-
- ret = rtl8366_reset_vlan(priv);
+++ /dev/null
-From e0ccf861b80698a5cc6f97c89bf8d5761f465fce Mon Sep 17 00:00:00 2001
-From: Zhi-Jun You <hujy652@gmail.com>
-Date: Sun, 7 Jan 2024 14:41:20 +0000
-Subject: mtd: spinand: winbond: add support for W25N04KV
-
-Add support for W25N04KV.
-
-W25N04KV has 8-bit on-die ECC.
-
-Signed-off-by: Zhi-Jun You <hujy652@gmail.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/20240107144120.532-1-hujy652@gmail.com
----
- drivers/mtd/nand/spi/winbond.c | 12 ++++++++++++
- 1 file changed, 12 insertions(+)
-
-(limited to 'drivers/mtd/nand/spi/winbond.c')
-
---- a/drivers/mtd/nand/spi/winbond.c
-+++ b/drivers/mtd/nand/spi/winbond.c
-@@ -15,6 +15,8 @@
-
- #define WINBOND_CFG_BUF_READ BIT(3)
-
-+#define W25N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4)
-+
- 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),
-@@ -135,6 +137,7 @@ static int w25n02kv_ecc_get_status(struc
- return -EBADMSG;
-
- case STATUS_ECC_HAS_BITFLIPS:
-+ case W25N04KV_STATUS_ECC_5_8_BITFLIPS:
- /*
- * Let's try to retrieve the real maximum number of bitflips
- * in order to avoid forcing the wear-leveling layer to move
-@@ -240,6 +243,15 @@ static const struct spinand_info winbond
- &update_cache_variants),
- 0,
- SPINAND_ECCINFO(&w25m02gv_ooblayout, w25n02kv_ecc_get_status)),
-+ SPINAND_INFO("W25N04KV",
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23),
-+ NAND_MEMORG(1, 2048, 128, 64, 4096, 40, 2, 1, 1),
-+ NAND_ECCREQ(8, 512),
-+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+ &write_cache_variants,
-+ &update_cache_variants),
-+ 0,
-+ SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
- };
-
- static int winbond_spinand_init(struct spinand_device *spinand)
+++ /dev/null
-From f8d05679fb3faae478d604177b0c188b340371cd Mon Sep 17 00:00:00 2001
-From: Breno Leitao <leitao@debian.org>
-Date: Mon, 22 Apr 2024 05:38:55 -0700
-Subject: [PATCH] net: free_netdev: exit earlier if dummy
-
-For dummy devices, exit earlier at free_netdev() instead of executing
-the whole function. This is necessary, because dummy devices are
-special, and shouldn't have the second part of the function executed.
-
-Otherwise reg_state, which is NETREG_DUMMY, will be overwritten and
-there will be no way to identify that this is a dummy device. Also, this
-device do not need the final put_device(), since dummy devices are not
-registered (through register_netdevice()), where the device reference is
-increased (at netdev_register_kobject()/device_add()).
-
-Suggested-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Breno Leitao <leitao@debian.org>
-Reviewed-by: Ido Schimmel <idosch@nvidia.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- net/core/dev.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -11020,7 +11020,8 @@ void free_netdev(struct net_device *dev)
- dev->xdp_bulkq = NULL;
-
- /* Compatibility with error handling in drivers */
-- if (dev->reg_state == NETREG_UNINITIALIZED) {
-+ if (dev->reg_state == NETREG_UNINITIALIZED ||
-+ dev->reg_state == NETREG_DUMMY) {
- netdev_freemem(dev);
- return;
- }
+++ /dev/null
-From 6a9eb32230f33ae273f6ffbabb0aaf560869816e Mon Sep 17 00:00:00 2001
-From: Scott Ehlert <ehlert@battelle.org>
-Date: Mon, 29 Apr 2024 11:23:09 -0700
-Subject: [PATCH] Bluetooth: btsdio: Do not bind to non-removable CYW4373
-
-CYW4373 devices soldered onto the PCB (non-removable),
-use a UART connection for Bluetooth and the advertised btsdio
-support as an SDIO function should be ignored.
-
-Signed-off-by: Scott Ehlert <ehlert@battelle.org>
-Signed-off-by: Tim Harvey <tharvey@gateworks.com>
----
- drivers/bluetooth/btsdio.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/bluetooth/btsdio.c
-+++ b/drivers/bluetooth/btsdio.c
-@@ -295,6 +295,7 @@ static int btsdio_probe(struct sdio_func
- case SDIO_DEVICE_ID_BROADCOM_4345:
- case SDIO_DEVICE_ID_BROADCOM_43455:
- case SDIO_DEVICE_ID_BROADCOM_4356:
-+ case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373:
- return -ENODEV;
- }
- }
+++ /dev/null
-From 04597c8dd6c4b55e946fec50dc3b14a5d9d54501 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Stephan=20M=C3=BCller?= <smueller@chronox.de>
-Date: Thu, 21 Sep 2023 13:48:11 +0200
-Subject: [PATCH] crypto: jitter - add RCT/APT support for different OSRs
-
-The oversampling rate (OSR) value specifies the heuristically implied
-entropy in the recorded data - H_submitter = 1/osr. A different entropy
-estimate implies a different APT/RCT cutoff value. This change adds
-support for OSRs 1 through 15. This OSR can be selected by the caller
-of the Jitter RNG.
-
-For this patch, the caller still uses one hard-coded OSR. A subsequent
-patch allows this value to be configured.
-
-In addition, the power-up self test is adjusted as follows:
-
-* It allows the caller to provide an oversampling rate that should be
-tested with - commonly it should be the same as used for the actual
-runtime operation. This makes the power-up testing therefore consistent
-with the runtime operation.
-
-* It calls now jent_measure_jitter (i.e. collects the full entropy
-that can possibly be harvested by the Jitter RNG) instead of only
-jent_condition_data (which only returns the entropy harvested from
-the conditioning component). This should now alleviate reports where
-the Jitter RNG initialization thinks there is too little entropy.
-
-* The power-up test now solely relies on the (enhanced) APT and RCT
-test that is used as a health test at runtime.
-
-The code allowing the different OSRs as well as the power-up test
-changes are present in the user space version of the Jitter RNG 3.4.1
-and thus was already in production use for some time.
-
-Reported-by "Ospan, Abylay" <aospan@amazon.com>
-Signed-off-by: Stephan Mueller <smueller@chronox.de>
-Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
----
- crypto/jitterentropy-kcapi.c | 4 +-
- crypto/jitterentropy.c | 233 ++++++++++++++++++-----------------
- crypto/jitterentropy.h | 3 +-
- 3 files changed, 123 insertions(+), 117 deletions(-)
-
---- a/crypto/jitterentropy-kcapi.c
-+++ b/crypto/jitterentropy-kcapi.c
-@@ -245,7 +245,7 @@ static int jent_kcapi_init(struct crypto
- crypto_shash_init(sdesc);
- rng->sdesc = sdesc;
-
-- rng->entropy_collector = jent_entropy_collector_alloc(1, 0, sdesc);
-+ rng->entropy_collector = jent_entropy_collector_alloc(0, 0, sdesc);
- if (!rng->entropy_collector) {
- ret = -ENOMEM;
- goto err;
-@@ -334,7 +334,7 @@ static int __init jent_mod_init(void)
-
- desc->tfm = tfm;
- crypto_shash_init(desc);
-- ret = jent_entropy_init(desc);
-+ ret = jent_entropy_init(0, 0, desc);
- shash_desc_zero(desc);
- crypto_free_shash(tfm);
- if (ret) {
---- a/crypto/jitterentropy.c
-+++ b/crypto/jitterentropy.c
-@@ -72,6 +72,8 @@ struct rand_data {
- __u64 prev_time; /* SENSITIVE Previous time stamp */
- __u64 last_delta; /* SENSITIVE stuck test */
- __s64 last_delta2; /* SENSITIVE stuck test */
-+
-+ unsigned int flags; /* Flags used to initialize */
- unsigned int osr; /* Oversample rate */
- #define JENT_MEMORY_BLOCKS 64
- #define JENT_MEMORY_BLOCKSIZE 32
-@@ -88,16 +90,9 @@ struct rand_data {
- /* Repetition Count Test */
- unsigned int rct_count; /* Number of stuck values */
-
-- /* Intermittent health test failure threshold of 2^-30 */
-- /* From an SP800-90B perspective, this RCT cutoff value is equal to 31. */
-- /* However, our RCT implementation starts at 1, so we subtract 1 here. */
--#define JENT_RCT_CUTOFF (31 - 1) /* Taken from SP800-90B sec 4.4.1 */
--#define JENT_APT_CUTOFF 325 /* Taken from SP800-90B sec 4.4.2 */
-- /* Permanent health test failure threshold of 2^-60 */
-- /* From an SP800-90B perspective, this RCT cutoff value is equal to 61. */
-- /* However, our RCT implementation starts at 1, so we subtract 1 here. */
--#define JENT_RCT_CUTOFF_PERMANENT (61 - 1)
--#define JENT_APT_CUTOFF_PERMANENT 355
-+ /* Adaptive Proportion Test cutoff values */
-+ unsigned int apt_cutoff; /* Intermittent health test failure */
-+ unsigned int apt_cutoff_permanent; /* Permanent health test failure */
- #define JENT_APT_WINDOW_SIZE 512 /* Data window size */
- /* LSB of time stamp to process */
- #define JENT_APT_LSB 16
-@@ -122,6 +117,9 @@ struct rand_data {
- * zero). */
- #define JENT_ESTUCK 8 /* Too many stuck results during init. */
- #define JENT_EHEALTH 9 /* Health test failed during initialization */
-+#define JENT_ERCT 10 /* RCT failed during initialization */
-+#define JENT_EHASH 11 /* Hash self test failed */
-+#define JENT_EMEM 12 /* Can't allocate memory for initialization */
-
- /*
- * The output n bits can receive more than n bits of min entropy, of course,
-@@ -148,6 +146,48 @@ struct rand_data {
- ***************************************************************************/
-
- /*
-+ * See the SP 800-90B comment #10b for the corrected cutoff for the SP 800-90B
-+ * APT.
-+ * http://www.untruth.org/~josh/sp80090b/UL%20SP800-90B-final%20comments%20v1.9%2020191212.pdf
-+ * In in the syntax of R, this is C = 2 + qbinom(1 − 2^(−30), 511, 2^(-1/osr)).
-+ * (The original formula wasn't correct because the first symbol must
-+ * necessarily have been observed, so there is no chance of observing 0 of these
-+ * symbols.)
-+ *
-+ * For the alpha < 2^-53, R cannot be used as it uses a float data type without
-+ * arbitrary precision. A SageMath script is used to calculate those cutoff
-+ * values.
-+ *
-+ * For any value above 14, this yields the maximal allowable value of 512
-+ * (by FIPS 140-2 IG 7.19 Resolution # 16, we cannot choose a cutoff value that
-+ * renders the test unable to fail).
-+ */
-+static const unsigned int jent_apt_cutoff_lookup[15] = {
-+ 325, 422, 459, 477, 488, 494, 499, 502,
-+ 505, 507, 508, 509, 510, 511, 512 };
-+static const unsigned int jent_apt_cutoff_permanent_lookup[15] = {
-+ 355, 447, 479, 494, 502, 507, 510, 512,
-+ 512, 512, 512, 512, 512, 512, 512 };
-+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-+
-+static void jent_apt_init(struct rand_data *ec, unsigned int osr)
-+{
-+ /*
-+ * Establish the apt_cutoff based on the presumed entropy rate of
-+ * 1/osr.
-+ */
-+ if (osr >= ARRAY_SIZE(jent_apt_cutoff_lookup)) {
-+ ec->apt_cutoff = jent_apt_cutoff_lookup[
-+ ARRAY_SIZE(jent_apt_cutoff_lookup) - 1];
-+ ec->apt_cutoff_permanent = jent_apt_cutoff_permanent_lookup[
-+ ARRAY_SIZE(jent_apt_cutoff_permanent_lookup) - 1];
-+ } else {
-+ ec->apt_cutoff = jent_apt_cutoff_lookup[osr - 1];
-+ ec->apt_cutoff_permanent =
-+ jent_apt_cutoff_permanent_lookup[osr - 1];
-+ }
-+}
-+/*
- * Reset the APT counter
- *
- * @ec [in] Reference to entropy collector
-@@ -187,12 +227,12 @@ static void jent_apt_insert(struct rand_
- /* APT health test failure detection */
- static int jent_apt_permanent_failure(struct rand_data *ec)
- {
-- return (ec->apt_count >= JENT_APT_CUTOFF_PERMANENT) ? 1 : 0;
-+ return (ec->apt_count >= ec->apt_cutoff_permanent) ? 1 : 0;
- }
-
- static int jent_apt_failure(struct rand_data *ec)
- {
-- return (ec->apt_count >= JENT_APT_CUTOFF) ? 1 : 0;
-+ return (ec->apt_count >= ec->apt_cutoff) ? 1 : 0;
- }
-
- /***************************************************************************
-@@ -275,15 +315,28 @@ static int jent_stuck(struct rand_data *
- return 0;
- }
-
--/* RCT health test failure detection */
-+/*
-+ * The cutoff value is based on the following consideration:
-+ * alpha = 2^-30 or 2^-60 as recommended in SP800-90B.
-+ * In addition, we require an entropy value H of 1/osr as this is the minimum
-+ * entropy required to provide full entropy.
-+ * Note, we collect (DATA_SIZE_BITS + ENTROPY_SAFETY_FACTOR)*osr deltas for
-+ * inserting them into the entropy pool which should then have (close to)
-+ * DATA_SIZE_BITS bits of entropy in the conditioned output.
-+ *
-+ * Note, ec->rct_count (which equals to value B in the pseudo code of SP800-90B
-+ * section 4.4.1) starts with zero. Hence we need to subtract one from the
-+ * cutoff value as calculated following SP800-90B. Thus
-+ * C = ceil(-log_2(alpha)/H) = 30*osr or 60*osr.
-+ */
- static int jent_rct_permanent_failure(struct rand_data *ec)
- {
-- return (ec->rct_count >= JENT_RCT_CUTOFF_PERMANENT) ? 1 : 0;
-+ return (ec->rct_count >= (60 * ec->osr)) ? 1 : 0;
- }
-
- static int jent_rct_failure(struct rand_data *ec)
- {
-- return (ec->rct_count >= JENT_RCT_CUTOFF) ? 1 : 0;
-+ return (ec->rct_count >= (30 * ec->osr)) ? 1 : 0;
- }
-
- /* Report of health test failures */
-@@ -448,7 +501,7 @@ static void jent_memaccess(struct rand_d
- *
- * @return result of stuck test
- */
--static int jent_measure_jitter(struct rand_data *ec)
-+static int jent_measure_jitter(struct rand_data *ec, __u64 *ret_current_delta)
- {
- __u64 time = 0;
- __u64 current_delta = 0;
-@@ -472,6 +525,10 @@ static int jent_measure_jitter(struct ra
- if (jent_condition_data(ec, current_delta, stuck))
- stuck = 1;
-
-+ /* return the raw entropy value */
-+ if (ret_current_delta)
-+ *ret_current_delta = current_delta;
-+
- return stuck;
- }
-
-@@ -489,11 +546,11 @@ static void jent_gen_entropy(struct rand
- safety_factor = JENT_ENTROPY_SAFETY_FACTOR;
-
- /* priming of the ->prev_time value */
-- jent_measure_jitter(ec);
-+ jent_measure_jitter(ec, NULL);
-
- while (!jent_health_failure(ec)) {
- /* If a stuck measurement is received, repeat measurement */
-- if (jent_measure_jitter(ec))
-+ if (jent_measure_jitter(ec, NULL))
- continue;
-
- /*
-@@ -554,7 +611,8 @@ int jent_read_entropy(struct rand_data *
- * Perform startup health tests and return permanent
- * error if it fails.
- */
-- if (jent_entropy_init(ec->hash_state))
-+ if (jent_entropy_init(ec->osr, ec->flags,
-+ ec->hash_state))
- return -3;
-
- return -2;
-@@ -604,11 +662,15 @@ struct rand_data *jent_entropy_collector
-
- /* verify and set the oversampling rate */
- if (osr == 0)
-- osr = 1; /* minimum sampling rate is 1 */
-+ osr = 1; /* H_submitter = 1 / osr */
- entropy_collector->osr = osr;
-+ entropy_collector->flags = flags;
-
- entropy_collector->hash_state = hash_state;
-
-+ /* Initialize the APT */
-+ jent_apt_init(entropy_collector, osr);
-+
- /* fill the data pad with non-zero values */
- jent_gen_entropy(entropy_collector);
-
-@@ -622,20 +684,14 @@ void jent_entropy_collector_free(struct
- jent_zfree(entropy_collector);
- }
-
--int jent_entropy_init(void *hash_state)
-+int jent_entropy_init(unsigned int osr, unsigned int flags, void *hash_state)
- {
-- int i;
-- __u64 delta_sum = 0;
-- __u64 old_delta = 0;
-- unsigned int nonstuck = 0;
-- int time_backwards = 0;
-- int count_mod = 0;
-- int count_stuck = 0;
-- struct rand_data ec = { 0 };
--
-- /* Required for RCT */
-- ec.osr = 1;
-- ec.hash_state = hash_state;
-+ struct rand_data *ec;
-+ int i, time_backwards = 0, ret = 0;
-+
-+ ec = jent_entropy_collector_alloc(osr, flags, hash_state);
-+ if (!ec)
-+ return JENT_EMEM;
-
- /* We could perform statistical tests here, but the problem is
- * that we only have a few loop counts to do testing. These
-@@ -664,31 +720,28 @@ int jent_entropy_init(void *hash_state)
- #define TESTLOOPCOUNT 1024
- #define CLEARCACHE 100
- for (i = 0; (TESTLOOPCOUNT + CLEARCACHE) > i; i++) {
-- __u64 time = 0;
-- __u64 time2 = 0;
-- __u64 delta = 0;
-- unsigned int lowdelta = 0;
-- int stuck;
-+ __u64 start_time = 0, end_time = 0, delta = 0;
-
- /* Invoke core entropy collection logic */
-- jent_get_nstime(&time);
-- ec.prev_time = time;
-- jent_condition_data(&ec, time, 0);
-- jent_get_nstime(&time2);
-+ jent_measure_jitter(ec, &delta);
-+ end_time = ec->prev_time;
-+ start_time = ec->prev_time - delta;
-
- /* test whether timer works */
-- if (!time || !time2)
-- return JENT_ENOTIME;
-- delta = jent_delta(time, time2);
-+ if (!start_time || !end_time) {
-+ ret = JENT_ENOTIME;
-+ goto out;
-+ }
-+
- /*
- * test whether timer is fine grained enough to provide
- * delta even when called shortly after each other -- this
- * implies that we also have a high resolution timer
- */
-- if (!delta)
-- return JENT_ECOARSETIME;
--
-- stuck = jent_stuck(&ec, delta);
-+ if (!delta || (end_time == start_time)) {
-+ ret = JENT_ECOARSETIME;
-+ goto out;
-+ }
-
- /*
- * up to here we did not modify any variable that will be
-@@ -700,49 +753,9 @@ int jent_entropy_init(void *hash_state)
- if (i < CLEARCACHE)
- continue;
-
-- if (stuck)
-- count_stuck++;
-- else {
-- nonstuck++;
--
-- /*
-- * Ensure that the APT succeeded.
-- *
-- * With the check below that count_stuck must be less
-- * than 10% of the overall generated raw entropy values
-- * it is guaranteed that the APT is invoked at
-- * floor((TESTLOOPCOUNT * 0.9) / 64) == 14 times.
-- */
-- if ((nonstuck % JENT_APT_WINDOW_SIZE) == 0) {
-- jent_apt_reset(&ec,
-- delta & JENT_APT_WORD_MASK);
-- }
-- }
--
-- /* Validate health test result */
-- if (jent_health_failure(&ec))
-- return JENT_EHEALTH;
--
- /* test whether we have an increasing timer */
-- if (!(time2 > time))
-+ if (!(end_time > start_time))
- time_backwards++;
--
-- /* use 32 bit value to ensure compilation on 32 bit arches */
-- lowdelta = time2 - time;
-- if (!(lowdelta % 100))
-- count_mod++;
--
-- /*
-- * ensure that we have a varying delta timer which is necessary
-- * for the calculation of entropy -- perform this check
-- * only after the first loop is executed as we need to prime
-- * the old_data value
-- */
-- if (delta > old_delta)
-- delta_sum += (delta - old_delta);
-- else
-- delta_sum += (old_delta - delta);
-- old_delta = delta;
- }
-
- /*
-@@ -752,31 +765,23 @@ int jent_entropy_init(void *hash_state)
- * should not fail. The value of 3 should cover the NTP case being
- * performed during our test run.
- */
-- if (time_backwards > 3)
-- return JENT_ENOMONOTONIC;
--
-- /*
-- * Variations of deltas of time must on average be larger
-- * than 1 to ensure the entropy estimation
-- * implied with 1 is preserved
-- */
-- if ((delta_sum) <= 1)
-- return JENT_EVARVAR;
-+ if (time_backwards > 3) {
-+ ret = JENT_ENOMONOTONIC;
-+ goto out;
-+ }
-
-- /*
-- * Ensure that we have variations in the time stamp below 10 for at
-- * least 10% of all checks -- on some platforms, the counter increments
-- * in multiples of 100, but not always
-- */
-- if ((TESTLOOPCOUNT/10 * 9) < count_mod)
-- return JENT_ECOARSETIME;
-+ /* Did we encounter a health test failure? */
-+ if (jent_rct_failure(ec)) {
-+ ret = JENT_ERCT;
-+ goto out;
-+ }
-+ if (jent_apt_failure(ec)) {
-+ ret = JENT_EHEALTH;
-+ goto out;
-+ }
-
-- /*
-- * If we have more than 90% stuck results, then this Jitter RNG is
-- * likely to not work well.
-- */
-- if ((TESTLOOPCOUNT/10 * 9) < count_stuck)
-- return JENT_ESTUCK;
-+out:
-+ jent_entropy_collector_free(ec);
-
-- return 0;
-+ return ret;
- }
---- a/crypto/jitterentropy.h
-+++ b/crypto/jitterentropy.h
-@@ -9,7 +9,8 @@ extern int jent_hash_time(void *hash_sta
- int jent_read_random_block(void *hash_state, char *dst, unsigned int dst_len);
-
- struct rand_data;
--extern int jent_entropy_init(void *hash_state);
-+extern int jent_entropy_init(unsigned int osr, unsigned int flags,
-+ void *hash_state);
- extern int jent_read_entropy(struct rand_data *ec, unsigned char *data,
- unsigned int len);
-
+++ /dev/null
-From 59bcfd788552504606e3eb774ae68052379396b6 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Stephan=20M=C3=BCller?= <smueller@chronox.de>
-Date: Thu, 21 Sep 2023 13:48:33 +0200
-Subject: [PATCH] crypto: jitter - Allow configuration of memory size
-
-The memory size consumed by the Jitter RNG is one contributing factor in
-the amount of entropy that is gathered. As the amount of entropy
-directly correlates with the distance of the memory from the CPU, the
-caches that are possibly present on a given system have an impact on the
-collected entropy.
-
-Thus, the kernel compile time should offer a means to configure the
-amount of memory used by the Jitter RNG. Although this option could be
-turned into a runtime option (e.g. a kernel command line option), it
-should remain a compile time option as otherwise adminsitrators who may
-not have performed an entropy assessment may select a value that is
-inappropriate.
-
-The default value selected by the configuration is identical to the
-current Jitter RNG value. Thus, the patch should not lead to any change
-in the Jitter RNG behavior.
-
-To accommodate larger memory buffers, kvzalloc / kvfree is used.
-
-Signed-off-by: Stephan Mueller <smueller@chronox.de>
-Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
----
- crypto/Kconfig | 43 ++++++++++++++++++++++++++++++++++++
- crypto/jitterentropy-kcapi.c | 11 +++++++++
- crypto/jitterentropy.c | 16 ++++++++------
- crypto/jitterentropy.h | 2 ++
- 4 files changed, 65 insertions(+), 7 deletions(-)
-
---- a/crypto/Kconfig
-+++ b/crypto/Kconfig
-@@ -1297,6 +1297,49 @@ config CRYPTO_JITTERENTROPY
-
- See https://www.chronox.de/jent/
-
-+choice
-+ prompt "CPU Jitter RNG Memory Size"
-+ default CRYPTO_JITTERENTROPY_MEMSIZE_2
-+ depends on CRYPTO_JITTERENTROPY
-+ help
-+ The Jitter RNG measures the execution time of memory accesses.
-+ Multiple consecutive memory accesses are performed. If the memory
-+ size fits into a cache (e.g. L1), only the memory access timing
-+ to that cache is measured. The closer the cache is to the CPU
-+ the less variations are measured and thus the less entropy is
-+ obtained. Thus, if the memory size fits into the L1 cache, the
-+ obtained entropy is less than if the memory size fits within
-+ L1 + L2, which in turn is less if the memory fits into
-+ L1 + L2 + L3. Thus, by selecting a different memory size,
-+ the entropy rate produced by the Jitter RNG can be modified.
-+
-+ config CRYPTO_JITTERENTROPY_MEMSIZE_2
-+ bool "2048 Bytes (default)"
-+
-+ config CRYPTO_JITTERENTROPY_MEMSIZE_128
-+ bool "128 kBytes"
-+
-+ config CRYPTO_JITTERENTROPY_MEMSIZE_1024
-+ bool "1024 kBytes"
-+
-+ config CRYPTO_JITTERENTROPY_MEMSIZE_8192
-+ bool "8192 kBytes"
-+endchoice
-+
-+config CRYPTO_JITTERENTROPY_MEMORY_BLOCKS
-+ int
-+ default 64 if CRYPTO_JITTERENTROPY_MEMSIZE_2
-+ default 512 if CRYPTO_JITTERENTROPY_MEMSIZE_128
-+ default 1024 if CRYPTO_JITTERENTROPY_MEMSIZE_1024
-+ default 4096 if CRYPTO_JITTERENTROPY_MEMSIZE_8192
-+
-+config CRYPTO_JITTERENTROPY_MEMORY_BLOCKSIZE
-+ int
-+ default 32 if CRYPTO_JITTERENTROPY_MEMSIZE_2
-+ default 256 if CRYPTO_JITTERENTROPY_MEMSIZE_128
-+ default 1024 if CRYPTO_JITTERENTROPY_MEMSIZE_1024
-+ default 2048 if CRYPTO_JITTERENTROPY_MEMSIZE_8192
-+
- config CRYPTO_JITTERENTROPY_TESTINTERFACE
- bool "CPU Jitter RNG Test Interface"
- depends on CRYPTO_JITTERENTROPY
---- a/crypto/jitterentropy-kcapi.c
-+++ b/crypto/jitterentropy-kcapi.c
-@@ -54,6 +54,17 @@
- * Helper function
- ***************************************************************************/
-
-+void *jent_kvzalloc(unsigned int len)
-+{
-+ return kvzalloc(len, GFP_KERNEL);
-+}
-+
-+void jent_kvzfree(void *ptr, unsigned int len)
-+{
-+ memzero_explicit(ptr, len);
-+ kvfree(ptr);
-+}
-+
- void *jent_zalloc(unsigned int len)
- {
- return kzalloc(len, GFP_KERNEL);
---- a/crypto/jitterentropy.c
-+++ b/crypto/jitterentropy.c
-@@ -75,10 +75,10 @@ struct rand_data {
-
- unsigned int flags; /* Flags used to initialize */
- unsigned int osr; /* Oversample rate */
--#define JENT_MEMORY_BLOCKS 64
--#define JENT_MEMORY_BLOCKSIZE 32
- #define JENT_MEMORY_ACCESSLOOPS 128
--#define JENT_MEMORY_SIZE (JENT_MEMORY_BLOCKS*JENT_MEMORY_BLOCKSIZE)
-+#define JENT_MEMORY_SIZE \
-+ (CONFIG_CRYPTO_JITTERENTROPY_MEMORY_BLOCKS * \
-+ CONFIG_CRYPTO_JITTERENTROPY_MEMORY_BLOCKSIZE)
- unsigned char *mem; /* Memory access location with size of
- * memblocks * memblocksize */
- unsigned int memlocation; /* Pointer to byte in *mem */
-@@ -650,13 +650,15 @@ struct rand_data *jent_entropy_collector
- /* Allocate memory for adding variations based on memory
- * access
- */
-- entropy_collector->mem = jent_zalloc(JENT_MEMORY_SIZE);
-+ entropy_collector->mem = jent_kvzalloc(JENT_MEMORY_SIZE);
- if (!entropy_collector->mem) {
- jent_zfree(entropy_collector);
- return NULL;
- }
-- entropy_collector->memblocksize = JENT_MEMORY_BLOCKSIZE;
-- entropy_collector->memblocks = JENT_MEMORY_BLOCKS;
-+ entropy_collector->memblocksize =
-+ CONFIG_CRYPTO_JITTERENTROPY_MEMORY_BLOCKSIZE;
-+ entropy_collector->memblocks =
-+ CONFIG_CRYPTO_JITTERENTROPY_MEMORY_BLOCKS;
- entropy_collector->memaccessloops = JENT_MEMORY_ACCESSLOOPS;
- }
-
-@@ -679,7 +681,7 @@ struct rand_data *jent_entropy_collector
-
- void jent_entropy_collector_free(struct rand_data *entropy_collector)
- {
-- jent_zfree(entropy_collector->mem);
-+ jent_kvzfree(entropy_collector->mem, JENT_MEMORY_SIZE);
- entropy_collector->mem = NULL;
- jent_zfree(entropy_collector);
- }
---- a/crypto/jitterentropy.h
-+++ b/crypto/jitterentropy.h
-@@ -1,5 +1,7 @@
- // SPDX-License-Identifier: GPL-2.0-or-later
-
-+extern void *jent_kvzalloc(unsigned int len);
-+extern void jent_kvzfree(void *ptr, unsigned int len);
- extern void *jent_zalloc(unsigned int len);
- extern void jent_zfree(void *ptr);
- extern void jent_get_nstime(__u64 *out);
+++ /dev/null
-From 0baa8fab334a4d7017235b72fa8a547433572109 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Stephan=20M=C3=BCller?= <smueller@chronox.de>
-Date: Thu, 21 Sep 2023 13:48:59 +0200
-Subject: [PATCH] crypto: jitter - Allow configuration of oversampling rate
-
-The oversampling rate used by the Jitter RNG allows the configuration of
-the heuristically implied entropy in one timing measurement. This
-entropy rate is (1 / OSR) bits of entropy per time stamp.
-
-Considering that the Jitter RNG now support APT/RCT health tests for
-different OSRs, allow this value to be configured at compile time to
-support systems with limited amount of entropy in their timer.
-
-The allowed range of OSR values complies with the APT/RCT cutoff health
-test values which range from 1 through 15.
-
-The default value of the OSR selection support is left at 1 which is the
-current default. Thus, the addition of the configuration support does
-not alter the default Jitter RNG behavior.
-
-Signed-off-by: Stephan Mueller <smueller@chronox.de>
-Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
----
- crypto/Kconfig | 17 +++++++++++++++++
- crypto/jitterentropy-kcapi.c | 6 ++++--
- 2 files changed, 21 insertions(+), 2 deletions(-)
-
---- a/crypto/Kconfig
-+++ b/crypto/Kconfig
-@@ -1340,6 +1340,23 @@ config CRYPTO_JITTERENTROPY_MEMORY_BLOCK
- default 1024 if CRYPTO_JITTERENTROPY_MEMSIZE_1024
- default 2048 if CRYPTO_JITTERENTROPY_MEMSIZE_8192
-
-+config CRYPTO_JITTERENTROPY_OSR
-+ int "CPU Jitter RNG Oversampling Rate"
-+ range 1 15
-+ default 1
-+ depends on CRYPTO_JITTERENTROPY
-+ help
-+ The Jitter RNG allows the specification of an oversampling rate (OSR).
-+ The Jitter RNG operation requires a fixed amount of timing
-+ measurements to produce one output block of random numbers. The
-+ OSR value is multiplied with the amount of timing measurements to
-+ generate one output block. Thus, the timing measurement is oversampled
-+ by the OSR factor. The oversampling allows the Jitter RNG to operate
-+ on hardware whose timers deliver limited amount of entropy (e.g.
-+ the timer is coarse) by setting the OSR to a higher value. The
-+ trade-off, however, is that the Jitter RNG now requires more time
-+ to generate random numbers.
-+
- config CRYPTO_JITTERENTROPY_TESTINTERFACE
- bool "CPU Jitter RNG Test Interface"
- depends on CRYPTO_JITTERENTROPY
---- a/crypto/jitterentropy-kcapi.c
-+++ b/crypto/jitterentropy-kcapi.c
-@@ -256,7 +256,9 @@ static int jent_kcapi_init(struct crypto
- crypto_shash_init(sdesc);
- rng->sdesc = sdesc;
-
-- rng->entropy_collector = jent_entropy_collector_alloc(0, 0, sdesc);
-+ rng->entropy_collector =
-+ jent_entropy_collector_alloc(CONFIG_CRYPTO_JITTERENTROPY_OSR, 0,
-+ sdesc);
- if (!rng->entropy_collector) {
- ret = -ENOMEM;
- goto err;
-@@ -345,7 +347,7 @@ static int __init jent_mod_init(void)
-
- desc->tfm = tfm;
- crypto_shash_init(desc);
-- ret = jent_entropy_init(0, 0, desc);
-+ ret = jent_entropy_init(CONFIG_CRYPTO_JITTERENTROPY_OSR, 0, desc);
- shash_desc_zero(desc);
- crypto_free_shash(tfm);
- if (ret) {
+++ /dev/null
-From 8405ec8e3c02df8b3720874c3e2169fef4553868 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Stephan=20M=C3=BCller?= <smueller@chronox.de>
-Date: Sat, 7 Oct 2023 09:10:43 +0200
-Subject: [PATCH] crypto: jitter - reuse allocated entropy collector
-
-In case a health test error occurs during runtime, the power-up health
-tests are rerun to verify that the noise source is still good and
-that the reported health test error was an outlier. For performing this
-power-up health test, the already existing entropy collector instance
-is used instead of allocating a new one. This change has the following
-implications:
-
-* The noise that is collected as part of the newly run health tests is
- inserted into the entropy collector and thus stirs the existing
- data present in there further. Thus, the entropy collected during
- the health test is not wasted. This is also allowed by SP800-90B.
-
-* The power-on health test is not affected by the state of the entropy
- collector, because it resets the APT / RCT state. The remainder of
- the state is unrelated to the health test as it is only applied to
- newly obtained time stamps.
-
-This change also fixes a bug report about an allocation while in an
-atomic lock (the lock is taken in jent_kcapi_random, jent_read_entropy
-is called and this can call jent_entropy_init).
-
-Fixes: 04597c8dd6c4 ("jitter - add RCT/APT support for different OSRs")
-Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
-Signed-off-by: Stephan Mueller <smueller@chronox.de>
-Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
----
- crypto/jitterentropy-kcapi.c | 2 +-
- crypto/jitterentropy.c | 36 ++++++++++++++++++++++++++----------
- crypto/jitterentropy.h | 2 +-
- 3 files changed, 28 insertions(+), 12 deletions(-)
-
---- a/crypto/jitterentropy-kcapi.c
-+++ b/crypto/jitterentropy-kcapi.c
-@@ -347,7 +347,7 @@ static int __init jent_mod_init(void)
-
- desc->tfm = tfm;
- crypto_shash_init(desc);
-- ret = jent_entropy_init(CONFIG_CRYPTO_JITTERENTROPY_OSR, 0, desc);
-+ ret = jent_entropy_init(CONFIG_CRYPTO_JITTERENTROPY_OSR, 0, desc, NULL);
- shash_desc_zero(desc);
- crypto_free_shash(tfm);
- if (ret) {
---- a/crypto/jitterentropy.c
-+++ b/crypto/jitterentropy.c
-@@ -611,8 +611,7 @@ int jent_read_entropy(struct rand_data *
- * Perform startup health tests and return permanent
- * error if it fails.
- */
-- if (jent_entropy_init(ec->osr, ec->flags,
-- ec->hash_state))
-+ if (jent_entropy_init(0, 0, NULL, ec))
- return -3;
-
- return -2;
-@@ -686,14 +685,30 @@ void jent_entropy_collector_free(struct
- jent_zfree(entropy_collector);
- }
-
--int jent_entropy_init(unsigned int osr, unsigned int flags, void *hash_state)
-+int jent_entropy_init(unsigned int osr, unsigned int flags, void *hash_state,
-+ struct rand_data *p_ec)
- {
-- struct rand_data *ec;
-- int i, time_backwards = 0, ret = 0;
-+ /*
-+ * If caller provides an allocated ec, reuse it which implies that the
-+ * health test entropy data is used to further still the available
-+ * entropy pool.
-+ */
-+ struct rand_data *ec = p_ec;
-+ int i, time_backwards = 0, ret = 0, ec_free = 0;
-
-- ec = jent_entropy_collector_alloc(osr, flags, hash_state);
-- if (!ec)
-- return JENT_EMEM;
-+ if (!ec) {
-+ ec = jent_entropy_collector_alloc(osr, flags, hash_state);
-+ if (!ec)
-+ return JENT_EMEM;
-+ ec_free = 1;
-+ } else {
-+ /* Reset the APT */
-+ jent_apt_reset(ec, 0);
-+ /* Ensure that a new APT base is obtained */
-+ ec->apt_base_set = 0;
-+ /* Reset the RCT */
-+ ec->rct_count = 0;
-+ }
-
- /* We could perform statistical tests here, but the problem is
- * that we only have a few loop counts to do testing. These
-@@ -783,7 +798,8 @@ int jent_entropy_init(unsigned int osr,
- }
-
- out:
-- jent_entropy_collector_free(ec);
-+ if (ec_free)
-+ jent_entropy_collector_free(ec);
-
- return ret;
- }
---- a/crypto/jitterentropy.h
-+++ b/crypto/jitterentropy.h
-@@ -12,7 +12,7 @@ int jent_read_random_block(void *hash_st
-
- struct rand_data;
- extern int jent_entropy_init(unsigned int osr, unsigned int flags,
-- void *hash_state);
-+ void *hash_state, struct rand_data *p_ec);
- extern int jent_read_entropy(struct rand_data *ec, unsigned char *data,
- unsigned int len);
-
+++ /dev/null
-From cf27d9475f37fb69b5bc293e6e6d6c1d03cf7cc6 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Stephan=20M=C3=BCller?= <smueller@chronox.de>
-Date: Thu, 19 Oct 2023 09:40:42 +0200
-Subject: [PATCH] crypto: jitter - use permanent health test storage
-
-The health test result in the current code is only given for the currently
-processed raw time stamp. This implies to react on the health test error,
-the result must be checked after each raw time stamp being processed. To
-avoid this constant checking requirement, any health test error is recorded
-and stored to be analyzed at a later time, if needed.
-
-This change ensures that the power-up test catches any health test error.
-Without that patch, the power-up health test result is not enforced.
-
-The introduced changes are already in use with the user space version of
-the Jitter RNG.
-
-Fixes: 04597c8dd6c4 ("jitter - add RCT/APT support for different OSRs")
-Reported-by: Joachim Vandersmissen <git@jvdsn.com>
-Signed-off-by: Stephan Mueller <smueller@chronox.de>
-Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
----
- crypto/jitterentropy.c | 125 ++++++++++++++++++++++++-----------------
- 1 file changed, 74 insertions(+), 51 deletions(-)
-
---- a/crypto/jitterentropy.c
-+++ b/crypto/jitterentropy.c
-@@ -100,6 +100,8 @@ struct rand_data {
- unsigned int apt_observations; /* Number of collected observations */
- unsigned int apt_count; /* APT counter */
- unsigned int apt_base; /* APT base reference */
-+ unsigned int health_failure; /* Record health failure */
-+
- unsigned int apt_base_set:1; /* APT base reference set? */
- };
-
-@@ -121,6 +123,13 @@ struct rand_data {
- #define JENT_EHASH 11 /* Hash self test failed */
- #define JENT_EMEM 12 /* Can't allocate memory for initialization */
-
-+#define JENT_RCT_FAILURE 1 /* Failure in RCT health test. */
-+#define JENT_APT_FAILURE 2 /* Failure in APT health test. */
-+#define JENT_PERMANENT_FAILURE_SHIFT 16
-+#define JENT_PERMANENT_FAILURE(x) (x << JENT_PERMANENT_FAILURE_SHIFT)
-+#define JENT_RCT_FAILURE_PERMANENT JENT_PERMANENT_FAILURE(JENT_RCT_FAILURE)
-+#define JENT_APT_FAILURE_PERMANENT JENT_PERMANENT_FAILURE(JENT_APT_FAILURE)
-+
- /*
- * The output n bits can receive more than n bits of min entropy, of course,
- * but the fixed output of the conditioning function can only asymptotically
-@@ -215,26 +224,22 @@ static void jent_apt_insert(struct rand_
- return;
- }
-
-- if (delta_masked == ec->apt_base)
-+ if (delta_masked == ec->apt_base) {
- ec->apt_count++;
-
-+ /* Note, ec->apt_count starts with one. */
-+ if (ec->apt_count >= ec->apt_cutoff_permanent)
-+ ec->health_failure |= JENT_APT_FAILURE_PERMANENT;
-+ else if (ec->apt_count >= ec->apt_cutoff)
-+ ec->health_failure |= JENT_APT_FAILURE;
-+ }
-+
- ec->apt_observations++;
-
- if (ec->apt_observations >= JENT_APT_WINDOW_SIZE)
- jent_apt_reset(ec, delta_masked);
- }
-
--/* APT health test failure detection */
--static int jent_apt_permanent_failure(struct rand_data *ec)
--{
-- return (ec->apt_count >= ec->apt_cutoff_permanent) ? 1 : 0;
--}
--
--static int jent_apt_failure(struct rand_data *ec)
--{
-- return (ec->apt_count >= ec->apt_cutoff) ? 1 : 0;
--}
--
- /***************************************************************************
- * Stuck Test and its use as Repetition Count Test
- *
-@@ -261,6 +266,30 @@ static void jent_rct_insert(struct rand_
- {
- if (stuck) {
- ec->rct_count++;
-+
-+ /*
-+ * The cutoff value is based on the following consideration:
-+ * alpha = 2^-30 or 2^-60 as recommended in SP800-90B.
-+ * In addition, we require an entropy value H of 1/osr as this
-+ * is the minimum entropy required to provide full entropy.
-+ * Note, we collect (DATA_SIZE_BITS + ENTROPY_SAFETY_FACTOR)*osr
-+ * deltas for inserting them into the entropy pool which should
-+ * then have (close to) DATA_SIZE_BITS bits of entropy in the
-+ * conditioned output.
-+ *
-+ * Note, ec->rct_count (which equals to value B in the pseudo
-+ * code of SP800-90B section 4.4.1) starts with zero. Hence
-+ * we need to subtract one from the cutoff value as calculated
-+ * following SP800-90B. Thus C = ceil(-log_2(alpha)/H) = 30*osr
-+ * or 60*osr.
-+ */
-+ if ((unsigned int)ec->rct_count >= (60 * ec->osr)) {
-+ ec->rct_count = -1;
-+ ec->health_failure |= JENT_RCT_FAILURE_PERMANENT;
-+ } else if ((unsigned int)ec->rct_count >= (30 * ec->osr)) {
-+ ec->rct_count = -1;
-+ ec->health_failure |= JENT_RCT_FAILURE;
-+ }
- } else {
- /* Reset RCT */
- ec->rct_count = 0;
-@@ -316,38 +345,24 @@ static int jent_stuck(struct rand_data *
- }
-
- /*
-- * The cutoff value is based on the following consideration:
-- * alpha = 2^-30 or 2^-60 as recommended in SP800-90B.
-- * In addition, we require an entropy value H of 1/osr as this is the minimum
-- * entropy required to provide full entropy.
-- * Note, we collect (DATA_SIZE_BITS + ENTROPY_SAFETY_FACTOR)*osr deltas for
-- * inserting them into the entropy pool which should then have (close to)
-- * DATA_SIZE_BITS bits of entropy in the conditioned output.
-+ * Report any health test failures
- *
-- * Note, ec->rct_count (which equals to value B in the pseudo code of SP800-90B
-- * section 4.4.1) starts with zero. Hence we need to subtract one from the
-- * cutoff value as calculated following SP800-90B. Thus
-- * C = ceil(-log_2(alpha)/H) = 30*osr or 60*osr.
-- */
--static int jent_rct_permanent_failure(struct rand_data *ec)
--{
-- return (ec->rct_count >= (60 * ec->osr)) ? 1 : 0;
--}
--
--static int jent_rct_failure(struct rand_data *ec)
--{
-- return (ec->rct_count >= (30 * ec->osr)) ? 1 : 0;
--}
--
--/* Report of health test failures */
--static int jent_health_failure(struct rand_data *ec)
--{
-- return jent_rct_failure(ec) | jent_apt_failure(ec);
--}
-+ * @ec [in] Reference to entropy collector
-+ *
-+ * @return a bitmask indicating which tests failed
-+ * 0 No health test failure
-+ * 1 RCT failure
-+ * 2 APT failure
-+ * 1<<JENT_PERMANENT_FAILURE_SHIFT RCT permanent failure
-+ * 2<<JENT_PERMANENT_FAILURE_SHIFT APT permanent failure
-+ */
-+static unsigned int jent_health_failure(struct rand_data *ec)
-+{
-+ /* Test is only enabled in FIPS mode */
-+ if (!fips_enabled)
-+ return 0;
-
--static int jent_permanent_health_failure(struct rand_data *ec)
--{
-- return jent_rct_permanent_failure(ec) | jent_apt_permanent_failure(ec);
-+ return ec->health_failure;
- }
-
- /***************************************************************************
-@@ -594,11 +609,12 @@ int jent_read_entropy(struct rand_data *
- return -1;
-
- while (len > 0) {
-- unsigned int tocopy;
-+ unsigned int tocopy, health_test_result;
-
- jent_gen_entropy(ec);
-
-- if (jent_permanent_health_failure(ec)) {
-+ health_test_result = jent_health_failure(ec);
-+ if (health_test_result > JENT_PERMANENT_FAILURE_SHIFT) {
- /*
- * At this point, the Jitter RNG instance is considered
- * as a failed instance. There is no rerun of the
-@@ -606,13 +622,18 @@ int jent_read_entropy(struct rand_data *
- * is assumed to not further use this instance.
- */
- return -3;
-- } else if (jent_health_failure(ec)) {
-+ } else if (health_test_result) {
- /*
- * Perform startup health tests and return permanent
- * error if it fails.
- */
-- if (jent_entropy_init(0, 0, NULL, ec))
-+ if (jent_entropy_init(0, 0, NULL, ec)) {
-+ /* Mark the permanent error */
-+ ec->health_failure &=
-+ JENT_RCT_FAILURE_PERMANENT |
-+ JENT_APT_FAILURE_PERMANENT;
- return -3;
-+ }
-
- return -2;
- }
-@@ -695,6 +716,7 @@ int jent_entropy_init(unsigned int osr,
- */
- struct rand_data *ec = p_ec;
- int i, time_backwards = 0, ret = 0, ec_free = 0;
-+ unsigned int health_test_result;
-
- if (!ec) {
- ec = jent_entropy_collector_alloc(osr, flags, hash_state);
-@@ -708,6 +730,9 @@ int jent_entropy_init(unsigned int osr,
- ec->apt_base_set = 0;
- /* Reset the RCT */
- ec->rct_count = 0;
-+ /* Reset intermittent, leave permanent health test result */
-+ ec->health_failure &= (~JENT_RCT_FAILURE);
-+ ec->health_failure &= (~JENT_APT_FAILURE);
- }
-
- /* We could perform statistical tests here, but the problem is
-@@ -788,12 +813,10 @@ int jent_entropy_init(unsigned int osr,
- }
-
- /* Did we encounter a health test failure? */
-- if (jent_rct_failure(ec)) {
-- ret = JENT_ERCT;
-- goto out;
-- }
-- if (jent_apt_failure(ec)) {
-- ret = JENT_EHEALTH;
-+ health_test_result = jent_health_failure(ec);
-+ if (health_test_result) {
-+ ret = (health_test_result & JENT_RCT_FAILURE) ? JENT_ERCT :
-+ JENT_EHEALTH;
- goto out;
- }
-
+++ /dev/null
-From e7ed6473c2c8c4e45dd861bfa06e96189b11d8db Mon Sep 17 00:00:00 2001
-From: Herbert Xu <herbert@gondor.apana.org.au>
-Date: Mon, 6 Nov 2023 18:00:08 +0800
-Subject: [PATCH] crypto: jitterentropy - Hide esoteric Kconfig options under
- FIPS and EXPERT
-
-As JITTERENTROPY is selected by default if you enable the CRYPTO
-API, any Kconfig options added there will show up for every single
-user. Hide the esoteric options under EXPERT as well as FIPS so
-that only distro makers will see them.
-
-Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
-Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
-Reviewed-by: Stephan Mueller <smueller@chronox.de>
-Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
----
- crypto/Kconfig | 28 +++++++++++++++++++++++++---
- 1 file changed, 25 insertions(+), 3 deletions(-)
-
---- a/crypto/Kconfig
-+++ b/crypto/Kconfig
-@@ -1297,10 +1297,12 @@ config CRYPTO_JITTERENTROPY
-
- See https://www.chronox.de/jent/
-
-+if CRYPTO_JITTERENTROPY
-+if CRYPTO_FIPS && EXPERT
-+
- choice
- prompt "CPU Jitter RNG Memory Size"
- default CRYPTO_JITTERENTROPY_MEMSIZE_2
-- depends on CRYPTO_JITTERENTROPY
- help
- The Jitter RNG measures the execution time of memory accesses.
- Multiple consecutive memory accesses are performed. If the memory
-@@ -1344,7 +1346,6 @@ config CRYPTO_JITTERENTROPY_OSR
- int "CPU Jitter RNG Oversampling Rate"
- range 1 15
- default 1
-- depends on CRYPTO_JITTERENTROPY
- help
- The Jitter RNG allows the specification of an oversampling rate (OSR).
- The Jitter RNG operation requires a fixed amount of timing
-@@ -1359,7 +1360,6 @@ config CRYPTO_JITTERENTROPY_OSR
-
- config CRYPTO_JITTERENTROPY_TESTINTERFACE
- bool "CPU Jitter RNG Test Interface"
-- depends on CRYPTO_JITTERENTROPY
- help
- The test interface allows a privileged process to capture
- the raw unconditioned high resolution time stamp noise that
-@@ -1377,6 +1377,28 @@ config CRYPTO_JITTERENTROPY_TESTINTERFAC
-
- If unsure, select N.
-
-+endif # if CRYPTO_FIPS && EXPERT
-+
-+if !(CRYPTO_FIPS && EXPERT)
-+
-+config CRYPTO_JITTERENTROPY_MEMORY_BLOCKS
-+ int
-+ default 64
-+
-+config CRYPTO_JITTERENTROPY_MEMORY_BLOCKSIZE
-+ int
-+ default 32
-+
-+config CRYPTO_JITTERENTROPY_OSR
-+ int
-+ default 1
-+
-+config CRYPTO_JITTERENTROPY_TESTINTERFACE
-+ bool
-+
-+endif # if !(CRYPTO_FIPS && EXPERT)
-+endif # if CRYPTO_JITTERENTROPY
-+
- config CRYPTO_KDF800108_CTR
- tristate
- select CRYPTO_HMAC
+++ /dev/null
-From 6e61ee1ca551292d8714c35c92a019c41db79e4e Mon Sep 17 00:00:00 2001
-From: Thorsten Blum <thorsten.blum@toblux.com>
-Date: Wed, 27 Mar 2024 23:25:09 +0100
-Subject: [PATCH] crypto: jitter - Use kvfree_sensitive() to fix Coccinelle
- warning
-
-Replace memzero_explicit() and kvfree() with kvfree_sensitive() to fix
-the following Coccinelle/coccicheck warning reported by
-kfree_sensitive.cocci:
-
- WARNING opportunity for kfree_sensitive/kvfree_sensitive
-
-Signed-off-by: Thorsten Blum <thorsten.blum@toblux.com>
-Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
----
- crypto/jitterentropy-kcapi.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/crypto/jitterentropy-kcapi.c
-+++ b/crypto/jitterentropy-kcapi.c
-@@ -61,8 +61,7 @@ void *jent_kvzalloc(unsigned int len)
-
- void jent_kvzfree(void *ptr, unsigned int len)
- {
-- memzero_explicit(ptr, len);
-- kvfree(ptr);
-+ kvfree_sensitive(ptr, len);
- }
-
- void *jent_zalloc(unsigned int len)
+++ /dev/null
-From 95a798d20060d2b648dd604321e347c85edfd783 Mon Sep 17 00:00:00 2001
-From: Stephan Mueller <smueller@chronox.de>
-Date: Mon, 12 Aug 2024 08:25:42 +0200
-Subject: [PATCH] crypto: jitter - set default OSR to 3
-
-The user space Jitter RNG library uses the oversampling rate of 3 which
-implies that each time stamp is credited with 1/3 bit of entropy. To
-obtain 256 bits of entropy, 768 time stamps need to be sampled. The
-increase in OSR is applied based on a report where the Jitter RNG is
-used on a system exhibiting a challenging environment to collect
-entropy.
-
-This OSR default value is now applied to the Linux kernel version of
-the Jitter RNG as well.
-
-The increase in the OSR from 1 to 3 also implies that the Jitter RNG is
-now slower by default.
-
-Reported-by: Jeff Barnes <jeffbarnes@microsoft.com>
-Signed-off-by: Stephan Mueller <smueller@chronox.com>
-Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
----
- crypto/Kconfig | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/crypto/Kconfig
-+++ b/crypto/Kconfig
-@@ -1345,7 +1345,7 @@ config CRYPTO_JITTERENTROPY_MEMORY_BLOCK
- config CRYPTO_JITTERENTROPY_OSR
- int "CPU Jitter RNG Oversampling Rate"
- range 1 15
-- default 1
-+ default 3
- help
- The Jitter RNG allows the specification of an oversampling rate (OSR).
- The Jitter RNG operation requires a fixed amount of timing
+++ /dev/null
-From 9da39ef332c417ce52732564c1c682a6e1209302 Mon Sep 17 00:00:00 2001
-From: Florian Eckert <fe@dev.tdt.de>
-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:
-
-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>
-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(-)
-
---- 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,
+++ /dev/null
-From f64f610ec6ab59dd0391b03842cea3a4cd8ee34f Mon Sep 17 00:00:00 2001
-From: Lucas Stach <l.stach@pengutronix.de>
-Date: Wed, 18 Dec 2024 19:44:33 +0100
-Subject: [PATCH] pmdomain: core: add dummy release function to genpd device
-
-The genpd device, which is really only used as a handle to lookup
-OPP, but not even registered to the device core otherwise and thus
-lifetime linked to the genpd struct it is contained in, is missing
-a release function. After b8f7bbd1f4ec ("pmdomain: core: Add
-missing put_device()") the device will be cleaned up going through
-the driver core device_release() function, which will warn when no
-release callback is present for the device. Add a dummy release
-function to shut up the warning.
-
-Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
-Tested-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
-Fixes: b8f7bbd1f4ec ("pmdomain: core: Add missing put_device()")
-Cc: stable@vger.kernel.org
-Message-ID: <20241218184433.1930532-1-l.stach@pengutronix.de>
-Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
----
- drivers/base/power/domain.c | 6 ++++++
- 1 file changed, 6 insertions(+)
-
---- a/drivers/base/power/domain.c
-+++ b/drivers/base/power/domain.c
-@@ -2040,6 +2040,11 @@ static void genpd_lock_init(struct gener
- }
- }
-
-+static void genpd_provider_release(struct device *dev)
-+{
-+ /* nothing to be done here */
-+}
-+
- /**
- * pm_genpd_init - Initialize a generic I/O PM domain object.
- * @genpd: PM domain object to initialize.
-@@ -2106,6 +2111,7 @@ int pm_genpd_init(struct generic_pm_doma
- return ret;
-
- device_initialize(&genpd->dev);
-+ genpd->dev.release = genpd_provider_release;
- dev_set_name(&genpd->dev, "%s", genpd->name);
-
- mutex_lock(&gpd_list_lock);