--- /dev/null
+From 13b1ecc3401653a355798eb1dee10cc1608202f4 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 18 Jan 2016 12:27:49 +0100
+Subject: [PATCH 33/34] Kbuild: don't hardcode path to awk in
+ scripts/ld-version.sh
+
+On some systems /usr/bin/awk does not exist, or is broken. Find it via
+$PATH instead.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ scripts/ld-version.sh | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/scripts/ld-version.sh
++++ b/scripts/ld-version.sh
+@@ -1,6 +1,7 @@
+-#!/usr/bin/awk -f
++#!/bin/sh
+ # SPDX-License-Identifier: GPL-2.0
+ # extract linker version number from stdin and turn into single number
++exec awk '
+ {
+ gsub(".*\\)", "");
+ gsub(".*version ", "");
+@@ -9,3 +10,4 @@
+ print a[1]*100000000 + a[2]*1000000 + a[3]*10000;
+ exit
+ }
++'
--- /dev/null
+From 173019b66dcc9d68ad9333aa744dad1e369b5aa8 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 9 Jul 2017 00:26:53 +0200
+Subject: [PATCH 34/34] kernel: add compile fix for linux 4.9 on x86
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ Makefile | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -507,7 +507,7 @@ KBUILD_LDFLAGS_MODULE :=
+ KBUILD_LDFLAGS :=
+ CLANG_FLAGS :=
+
+-export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC
++export ARCH SRCARCH SUBARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC
+ export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL
+ export PERL PYTHON PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX
+ export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ ZSTD
--- /dev/null
+From 1027a42c25cbf8cfc4ade6503c5110aae04866af Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20Cabanelas?= <dgcbueu@gmail.com>
+Date: Fri, 16 Oct 2020 20:22:37 +0200
+Subject: [PATCH] power: reset: linkstation-poweroff: add missing put_device()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The of_mdio_find_bus() takes a reference to the underlying device
+structure, we should release that reference using a put_device() call.
+
+Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
+Signed-off-by: Sebastian Reichel <sre@kernel.org>
+---
+ drivers/power/reset/linkstation-poweroff.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/power/reset/linkstation-poweroff.c
++++ b/drivers/power/reset/linkstation-poweroff.c
+@@ -113,6 +113,7 @@ static int __init linkstation_poweroff_i
+ return -EPROBE_DEFER;
+
+ phydev = phy_find_first(bus);
++ put_device(&bus->dev);
+ if (!phydev)
+ return -EPROBE_DEFER;
+
--- /dev/null
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:02 +0200
+Subject: [PATCH] MIPS: uasm: Enable muhu opcode for MIPS R6
+
+Enable the 'muhu' instruction, complementing the existing 'mulu', needed
+to implement a MIPS32 BPF JIT.
+
+Also fix a typo in the existing definition of 'dmulu'.
+
+Signed-off-by: Tony Ambardar <Tony.Ambardar@gmail.com>
+
+This patch is a dependency for my 32-bit MIPS eBPF JIT.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+---
+
+--- a/arch/mips/include/asm/uasm.h
++++ b/arch/mips/include/asm/uasm.h
+@@ -145,6 +145,7 @@ Ip_u1(_mtlo);
+ Ip_u3u1u2(_mul);
+ Ip_u1u2(_multu);
+ Ip_u3u1u2(_mulu);
++Ip_u3u1u2(_muhu);
+ Ip_u3u1u2(_nor);
+ Ip_u3u1u2(_or);
+ Ip_u2u1u3(_ori);
+--- a/arch/mips/mm/uasm-mips.c
++++ b/arch/mips/mm/uasm-mips.c
+@@ -90,7 +90,7 @@ static const struct insn insn_table[insn
+ RS | RT | RD},
+ [insn_dmtc0] = {M(cop0_op, dmtc_op, 0, 0, 0, 0), RT | RD | SET},
+ [insn_dmultu] = {M(spec_op, 0, 0, 0, 0, dmultu_op), RS | RT},
+- [insn_dmulu] = {M(spec_op, 0, 0, 0, dmult_dmul_op, dmultu_op),
++ [insn_dmulu] = {M(spec_op, 0, 0, 0, dmultu_dmulu_op, dmultu_op),
+ RS | RT | RD},
+ [insn_drotr] = {M(spec_op, 1, 0, 0, 0, dsrl_op), RT | RD | RE},
+ [insn_drotr32] = {M(spec_op, 1, 0, 0, 0, dsrl32_op), RT | RD | RE},
+@@ -150,6 +150,8 @@ static const struct insn insn_table[insn
+ [insn_mtlo] = {M(spec_op, 0, 0, 0, 0, mtlo_op), RS},
+ [insn_mulu] = {M(spec_op, 0, 0, 0, multu_mulu_op, multu_op),
+ RS | RT | RD},
++ [insn_muhu] = {M(spec_op, 0, 0, 0, multu_muhu_op, multu_op),
++ RS | RT | RD},
+ #ifndef CONFIG_CPU_MIPSR6
+ [insn_mul] = {M(spec2_op, 0, 0, 0, 0, mul_op), RS | RT | RD},
+ #else
+--- a/arch/mips/mm/uasm.c
++++ b/arch/mips/mm/uasm.c
+@@ -59,7 +59,7 @@ enum opcode {
+ insn_lddir, insn_ldpte, insn_ldx, insn_lh, insn_lhu, insn_ll, insn_lld,
+ insn_lui, insn_lw, insn_lwu, insn_lwx, insn_mfc0, insn_mfhc0, insn_mfhi,
+ insn_mflo, insn_modu, insn_movn, insn_movz, insn_mtc0, insn_mthc0,
+- insn_mthi, insn_mtlo, insn_mul, insn_multu, insn_mulu, insn_nor,
++ insn_mthi, insn_mtlo, insn_mul, insn_multu, insn_mulu, insn_muhu, insn_nor,
+ insn_or, insn_ori, insn_pref, insn_rfe, insn_rotr, insn_sb, insn_sc,
+ insn_scd, insn_seleqz, insn_selnez, insn_sd, insn_sh, insn_sll,
+ insn_sllv, insn_slt, insn_slti, insn_sltiu, insn_sltu, insn_sra,
+@@ -344,6 +344,7 @@ I_u1(_mtlo)
+ I_u3u1u2(_mul)
+ I_u1u2(_multu)
+ I_u3u1u2(_mulu)
++I_u3u1u2(_muhu)
+ I_u3u1u2(_nor)
+ I_u3u1u2(_or)
+ I_u2u1u3(_ori)
--- /dev/null
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:03 +0200
+Subject: [PATCH] mips: uasm: Add workaround for Loongson-2F nop CPU errata
+
+This patch implements a workaround for the Loongson-2F nop in generated,
+code, if the existing option CONFIG_CPU_NOP_WORKAROUND is set. Before,
+the binutils option -mfix-loongson2f-nop was enabled, but no workaround
+was done when emitting MIPS code. Now, the nop pseudo instruction is
+emitted as "or ax,ax,zero" instead of the default "sll zero,zero,0". This
+is consistent with the workaround implemented by binutils.
+
+Link: https://sourceware.org/legacy-ml/binutils/2009-11/msg00387.html
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
+---
+
+--- a/arch/mips/include/asm/uasm.h
++++ b/arch/mips/include/asm/uasm.h
+@@ -249,7 +249,11 @@ static inline void uasm_l##lb(struct uas
+ #define uasm_i_bnezl(buf, rs, off) uasm_i_bnel(buf, rs, 0, off)
+ #define uasm_i_ehb(buf) uasm_i_sll(buf, 0, 0, 3)
+ #define uasm_i_move(buf, a, b) UASM_i_ADDU(buf, a, 0, b)
++#ifdef CONFIG_CPU_NOP_WORKAROUNDS
++#define uasm_i_nop(buf) uasm_i_or(buf, 1, 1, 0)
++#else
+ #define uasm_i_nop(buf) uasm_i_sll(buf, 0, 0, 0)
++#endif
+ #define uasm_i_ssnop(buf) uasm_i_sll(buf, 0, 0, 1)
+
+ static inline void uasm_i_drotr_safe(u32 **p, unsigned int a1,
--- /dev/null
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:04 +0200
+Subject: [PATCH] mips: bpf: Add eBPF JIT for 32-bit MIPS
+
+This is an implementation of an eBPF JIT for 32-bit MIPS I-V and MIPS32.
+The implementation supports all 32-bit and 64-bit ALU and JMP operations,
+including the recently-added atomics. 64-bit div/mod and 64-bit atomics
+are implemented using function calls to math64 and atomic64 functions,
+respectively. All 32-bit operations are implemented natively by the JIT,
+except if the CPU lacks ll/sc instructions.
+
+Register mapping
+================
+All 64-bit eBPF registers are mapped to native 32-bit MIPS register pairs,
+and does not use any stack scratch space for register swapping. This means
+that all eBPF register data is kept in CPU registers all the time, and
+this simplifies the register management a lot. It also reduces the JIT's
+pressure on temporary registers since we do not have to move data around.
+
+Native register pairs are ordered according to CPU endiannes, following
+the O32 calling convention for passing 64-bit arguments and return values.
+The eBPF return value, arguments and callee-saved registers are mapped to
+their native MIPS equivalents.
+
+Since the 32 highest bits in the eBPF FP (frame pointer) register are
+always zero, only one general-purpose register is actually needed for the
+mapping. The MIPS fp register is used for this purpose. The high bits are
+mapped to MIPS register r0. This saves us one CPU register, which is much
+needed for temporaries, while still allowing us to treat the R10 (FP)
+register just like any other eBPF register in the JIT.
+
+The MIPS gp (global pointer) and at (assembler temporary) registers are
+used as internal temporary registers for constant blinding. CPU registers
+t6-t9 are used internally by the JIT when constructing more complex 64-bit
+operations. This is precisely what is needed - two registers to store an
+operand value, and two more as scratch registers when performing the
+operation.
+
+The register mapping is shown below.
+
+ R0 - $v1, $v0 return value
+ R1 - $a1, $a0 argument 1, passed in registers
+ R2 - $a3, $a2 argument 2, passed in registers
+ R3 - $t1, $t0 argument 3, passed on stack
+ R4 - $t3, $t2 argument 4, passed on stack
+ R5 - $t4, $t3 argument 5, passed on stack
+ R6 - $s1, $s0 callee-saved
+ R7 - $s3, $s2 callee-saved
+ R8 - $s5, $s4 callee-saved
+ R9 - $s7, $s6 callee-saved
+ FP - $r0, $fp 32-bit frame pointer
+ AX - $gp, $at constant-blinding
+ $t6 - $t9 unallocated, JIT temporaries
+
+Jump offsets
+============
+The JIT tries to map all conditional JMP operations to MIPS conditional
+PC-relative branches. The MIPS branch offset field is 18 bits, in bytes,
+which is equivalent to the eBPF 16-bit instruction offset. However, since
+the JIT may emit more than one CPU instruction per eBPF instruction, the
+field width may overflow. If that happens, the JIT converts the long
+conditional jump to a short PC-relative branch with the condition
+inverted, jumping over a long unconditional absolute jmp (j).
+
+This conversion will change the instruction offset mapping used for jumps,
+and may in turn result in more branch offset overflows. The JIT therefore
+dry-runs the translation until no more branches are converted and the
+offsets do not change anymore. There is an upper bound on this of course,
+and if the JIT hits that limit, the last two iterations are run with all
+branches being converted.
+
+Tail call count
+===============
+The current tail call count is stored in the 16-byte area of the caller's
+stack frame that is reserved for the callee in the o32 ABI. The value is
+initialized in the prologue, and propagated to the tail-callee by skipping
+the initialization instructions when emitting the tail call.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+---
+ create mode 100644 arch/mips/net/bpf_jit_comp.c
+ create mode 100644 arch/mips/net/bpf_jit_comp.h
+ create mode 100644 arch/mips/net/bpf_jit_comp32.c
+
+--- a/arch/mips/net/Makefile
++++ b/arch/mips/net/Makefile
+@@ -2,4 +2,9 @@
+ # MIPS networking code
+
+ obj-$(CONFIG_MIPS_CBPF_JIT) += bpf_jit.o bpf_jit_asm.o
+-obj-$(CONFIG_MIPS_EBPF_JIT) += ebpf_jit.o
++
++ifeq ($(CONFIG_32BIT),y)
++ obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp.o bpf_jit_comp32.o
++else
++ obj-$(CONFIG_MIPS_EBPF_JIT) += ebpf_jit.o
++endif
+--- /dev/null
++++ b/arch/mips/net/bpf_jit_comp.c
+@@ -0,0 +1,1020 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Just-In-Time compiler for eBPF bytecode on MIPS.
++ * Implementation of JIT functions common to 32-bit and 64-bit CPUs.
++ *
++ * Copyright (c) 2021 Anyfi Networks AB.
++ * Author: Johan Almbladh <johan.almbladh@gmail.com>
++ *
++ * Based on code and ideas from
++ * Copyright (c) 2017 Cavium, Inc.
++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com>
++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
++ */
++
++/*
++ * Code overview
++ * =============
++ *
++ * - bpf_jit_comp.h
++ * Common definitions and utilities.
++ *
++ * - bpf_jit_comp.c
++ * Implementation of JIT top-level logic and exported JIT API functions.
++ * Implementation of internal operations shared by 32-bit and 64-bit code.
++ * JMP and ALU JIT control code, register control code, shared ALU and
++ * JMP/JMP32 JIT operations.
++ *
++ * - bpf_jit_comp32.c
++ * Implementation of functions to JIT prologue, epilogue and a single eBPF
++ * instruction for 32-bit MIPS CPUs. The functions use shared operations
++ * where possible, and implement the rest for 32-bit MIPS such as ALU64
++ * operations.
++ *
++ * - bpf_jit_comp64.c
++ * Ditto, for 64-bit MIPS CPUs.
++ *
++ * Zero and sign extension
++ * ========================
++ * 32-bit MIPS instructions on 64-bit MIPS registers use sign extension,
++ * but the eBPF instruction set mandates zero extension. We let the verifier
++ * insert explicit zero-extensions after 32-bit ALU operations, both for
++ * 32-bit and 64-bit MIPS JITs. Conditional JMP32 operations on 64-bit MIPs
++ * are JITed with sign extensions inserted when so expected.
++ *
++ * ALU operations
++ * ==============
++ * ALU operations on 32/64-bit MIPS and ALU64 operations on 64-bit MIPS are
++ * JITed in the following steps. ALU64 operations on 32-bit MIPS are more
++ * complicated and therefore only processed by special implementations in
++ * step (3).
++ *
++ * 1) valid_alu_i:
++ * Determine if an immediate operation can be emitted as such, or if
++ * we must fall back to the register version.
++ *
++ * 2) rewrite_alu_i:
++ * Convert BPF operation and immediate value to a canonical form for
++ * JITing. In some degenerate cases this form may be a no-op.
++ *
++ * 3) emit_alu_{i,i64,r,64}:
++ * Emit instructions for an ALU or ALU64 immediate or register operation.
++ *
++ * JMP operations
++ * ==============
++ * JMP and JMP32 operations require an JIT instruction offset table for
++ * translating the jump offset. This table is computed by dry-running the
++ * JIT without actually emitting anything. However, the computed PC-relative
++ * offset may overflow the 18-bit offset field width of the native MIPS
++ * branch instruction. In such cases, the long jump is converted into the
++ * following sequence.
++ *
++ * <branch> !<cond> +2 Inverted PC-relative branch
++ * nop Delay slot
++ * j <offset> Unconditional absolute long jump
++ * nop Delay slot
++ *
++ * Since this converted sequence alters the offset table, all offsets must
++ * be re-calculated. This may in turn trigger new branch conversions, so
++ * the process is repeated until no further changes are made. Normally it
++ * completes in 1-2 iterations. If JIT_MAX_ITERATIONS should reached, we
++ * fall back to converting every remaining jump operation. The branch
++ * conversion is independent of how the JMP or JMP32 condition is JITed.
++ *
++ * JMP32 and JMP operations are JITed as follows.
++ *
++ * 1) setup_jmp_{i,r}:
++ * Convert jump conditional and offset into a form that can be JITed.
++ * This form may be a no-op, a canonical form, or an inverted PC-relative
++ * jump if branch conversion is necessary.
++ *
++ * 2) valid_jmp_i:
++ * Determine if an immediate operations can be emitted as such, or if
++ * we must fall back to the register version. Applies to JMP32 for 32-bit
++ * MIPS, and both JMP and JMP32 for 64-bit MIPS.
++ *
++ * 3) emit_jmp_{i,i64,r,r64}:
++ * Emit instructions for an JMP or JMP32 immediate or register operation.
++ *
++ * 4) finish_jmp_{i,r}:
++ * Emit any instructions needed to finish the jump. This includes a nop
++ * for the delay slot if a branch was emitted, and a long absolute jump
++ * if the branch was converted.
++ */
++
++#include <linux/limits.h>
++#include <linux/bitops.h>
++#include <linux/errno.h>
++#include <linux/filter.h>
++#include <linux/bpf.h>
++#include <linux/slab.h>
++#include <asm/bitops.h>
++#include <asm/cacheflush.h>
++#include <asm/cpu-features.h>
++#include <asm/isa-rev.h>
++#include <asm/uasm.h>
++
++#include "bpf_jit_comp.h"
++
++/* Convenience macros for descriptor access */
++#define CONVERTED(desc) ((desc) & JIT_DESC_CONVERT)
++#define INDEX(desc) ((desc) & ~JIT_DESC_CONVERT)
++
++/*
++ * Push registers on the stack, starting at a given depth from the stack
++ * pointer and increasing. The next depth to be written is returned.
++ */
++int push_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth)
++{
++ int reg;
++
++ for (reg = 0; reg < BITS_PER_BYTE * sizeof(mask); reg++)
++ if (mask & BIT(reg)) {
++ if ((excl & BIT(reg)) == 0) {
++ if (sizeof(long) == 4)
++ emit(ctx, sw, reg, depth, MIPS_R_SP);
++ else /* sizeof(long) == 8 */
++ emit(ctx, sd, reg, depth, MIPS_R_SP);
++ }
++ depth += sizeof(long);
++ }
++
++ ctx->stack_used = max((int)ctx->stack_used, depth);
++ return depth;
++}
++
++/*
++ * Pop registers from the stack, starting at a given depth from the stack
++ * pointer and increasing. The next depth to be read is returned.
++ */
++int pop_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth)
++{
++ int reg;
++
++ for (reg = 0; reg < BITS_PER_BYTE * sizeof(mask); reg++)
++ if (mask & BIT(reg)) {
++ if ((excl & BIT(reg)) == 0) {
++ if (sizeof(long) == 4)
++ emit(ctx, lw, reg, depth, MIPS_R_SP);
++ else /* sizeof(long) == 8 */
++ emit(ctx, ld, reg, depth, MIPS_R_SP);
++ }
++ depth += sizeof(long);
++ }
++
++ return depth;
++}
++
++/* Compute the 28-bit jump target address from a BPF program location */
++int get_target(struct jit_context *ctx, u32 loc)
++{
++ u32 index = INDEX(ctx->descriptors[loc]);
++ unsigned long pc = (unsigned long)&ctx->target[ctx->jit_index];
++ unsigned long addr = (unsigned long)&ctx->target[index];
++
++ if (!ctx->target)
++ return 0;
++
++ if ((addr ^ pc) & ~MIPS_JMP_MASK)
++ return -1;
++
++ return addr & MIPS_JMP_MASK;
++}
++
++/* Compute the PC-relative offset to relative BPF program offset */
++int get_offset(const struct jit_context *ctx, int off)
++{
++ return (INDEX(ctx->descriptors[ctx->bpf_index + off]) -
++ ctx->jit_index - 1) * sizeof(u32);
++}
++
++/* dst = imm (register width) */
++void emit_mov_i(struct jit_context *ctx, u8 dst, s32 imm)
++{
++ if (imm >= -0x8000 && imm <= 0x7fff) {
++ emit(ctx, addiu, dst, MIPS_R_ZERO, imm);
++ } else {
++ emit(ctx, lui, dst, (s16)((u32)imm >> 16));
++ emit(ctx, ori, dst, dst, (u16)(imm & 0xffff));
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* dst = src (register width) */
++void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src)
++{
++ emit(ctx, ori, dst, src, 0);
++ clobber_reg(ctx, dst);
++}
++
++/* Validate ALU immediate range */
++bool valid_alu_i(u8 op, s32 imm)
++{
++ switch (BPF_OP(op)) {
++ case BPF_NEG:
++ case BPF_LSH:
++ case BPF_RSH:
++ case BPF_ARSH:
++ /* All legal eBPF values are valid */
++ return true;
++ case BPF_ADD:
++ /* imm must be 16 bits */
++ return imm >= -0x8000 && imm <= 0x7fff;
++ case BPF_SUB:
++ /* -imm must be 16 bits */
++ return imm >= -0x7fff && imm <= 0x8000;
++ case BPF_AND:
++ case BPF_OR:
++ case BPF_XOR:
++ /* imm must be 16 bits unsigned */
++ return imm >= 0 && imm <= 0xffff;
++ case BPF_MUL:
++ /* imm must be zero or a positive power of two */
++ return imm == 0 || (imm > 0 && is_power_of_2(imm));
++ case BPF_DIV:
++ case BPF_MOD:
++ /* imm must be an 17-bit power of two */
++ return (u32)imm <= 0x10000 && is_power_of_2((u32)imm);
++ }
++ return false;
++}
++
++/* Rewrite ALU immediate operation */
++bool rewrite_alu_i(u8 op, s32 imm, u8 *alu, s32 *val)
++{
++ bool act = true;
++
++ switch (BPF_OP(op)) {
++ case BPF_LSH:
++ case BPF_RSH:
++ case BPF_ARSH:
++ case BPF_ADD:
++ case BPF_SUB:
++ case BPF_OR:
++ case BPF_XOR:
++ /* imm == 0 is a no-op */
++ act = imm != 0;
++ break;
++ case BPF_MUL:
++ if (imm == 1) {
++ /* dst * 1 is a no-op */
++ act = false;
++ } else if (imm == 0) {
++ /* dst * 0 is dst & 0 */
++ op = BPF_AND;
++ } else {
++ /* dst * (1 << n) is dst << n */
++ op = BPF_LSH;
++ imm = ilog2(abs(imm));
++ }
++ break;
++ case BPF_DIV:
++ if (imm == 1) {
++ /* dst / 1 is a no-op */
++ act = false;
++ } else {
++ /* dst / (1 << n) is dst >> n */
++ op = BPF_RSH;
++ imm = ilog2(imm);
++ }
++ break;
++ case BPF_MOD:
++ /* dst % (1 << n) is dst & ((1 << n) - 1) */
++ op = BPF_AND;
++ imm--;
++ break;
++ }
++
++ *alu = op;
++ *val = imm;
++ return act;
++}
++
++/* ALU immediate operation (32-bit) */
++void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = -dst */
++ case BPF_NEG:
++ emit(ctx, subu, dst, MIPS_R_ZERO, dst);
++ break;
++ /* dst = dst & imm */
++ case BPF_AND:
++ emit(ctx, andi, dst, dst, (u16)imm);
++ break;
++ /* dst = dst | imm */
++ case BPF_OR:
++ emit(ctx, ori, dst, dst, (u16)imm);
++ break;
++ /* dst = dst ^ imm */
++ case BPF_XOR:
++ emit(ctx, xori, dst, dst, (u16)imm);
++ break;
++ /* dst = dst << imm */
++ case BPF_LSH:
++ emit(ctx, sll, dst, dst, imm);
++ break;
++ /* dst = dst >> imm */
++ case BPF_RSH:
++ emit(ctx, srl, dst, dst, imm);
++ break;
++ /* dst = dst >> imm (arithmetic) */
++ case BPF_ARSH:
++ emit(ctx, sra, dst, dst, imm);
++ break;
++ /* dst = dst + imm */
++ case BPF_ADD:
++ emit(ctx, addiu, dst, dst, imm);
++ break;
++ /* dst = dst - imm */
++ case BPF_SUB:
++ emit(ctx, addiu, dst, dst, -imm);
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* ALU register operation (32-bit) */
++void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = dst & src */
++ case BPF_AND:
++ emit(ctx, and, dst, dst, src);
++ break;
++ /* dst = dst | src */
++ case BPF_OR:
++ emit(ctx, or, dst, dst, src);
++ break;
++ /* dst = dst ^ src */
++ case BPF_XOR:
++ emit(ctx, xor, dst, dst, src);
++ break;
++ /* dst = dst << src */
++ case BPF_LSH:
++ emit(ctx, sllv, dst, dst, src);
++ break;
++ /* dst = dst >> src */
++ case BPF_RSH:
++ emit(ctx, srlv, dst, dst, src);
++ break;
++ /* dst = dst >> src (arithmetic) */
++ case BPF_ARSH:
++ emit(ctx, srav, dst, dst, src);
++ break;
++ /* dst = dst + src */
++ case BPF_ADD:
++ emit(ctx, addu, dst, dst, src);
++ break;
++ /* dst = dst - src */
++ case BPF_SUB:
++ emit(ctx, subu, dst, dst, src);
++ break;
++ /* dst = dst * src */
++ case BPF_MUL:
++ if (cpu_has_mips32r1 || cpu_has_mips32r6) {
++ emit(ctx, mul, dst, dst, src);
++ } else {
++ emit(ctx, multu, dst, src);
++ emit(ctx, mflo, dst);
++ }
++ break;
++ /* dst = dst / src */
++ case BPF_DIV:
++ if (cpu_has_mips32r6) {
++ emit(ctx, divu_r6, dst, dst, src);
++ } else {
++ emit(ctx, divu, dst, src);
++ emit(ctx, mflo, dst);
++ }
++ break;
++ /* dst = dst % src */
++ case BPF_MOD:
++ if (cpu_has_mips32r6) {
++ emit(ctx, modu, dst, dst, src);
++ } else {
++ emit(ctx, divu, dst, src);
++ emit(ctx, mfhi, dst);
++ }
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Atomic read-modify-write (32-bit) */
++void emit_atomic_r(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 code)
++{
++ emit(ctx, ll, MIPS_R_T9, off, dst);
++ switch (code) {
++ case BPF_ADD:
++ emit(ctx, addu, MIPS_R_T8, MIPS_R_T9, src);
++ break;
++ case BPF_AND:
++ emit(ctx, and, MIPS_R_T8, MIPS_R_T9, src);
++ break;
++ case BPF_OR:
++ emit(ctx, or, MIPS_R_T8, MIPS_R_T9, src);
++ break;
++ case BPF_XOR:
++ emit(ctx, xor, MIPS_R_T8, MIPS_R_T9, src);
++ break;
++ }
++ emit(ctx, sc, MIPS_R_T8, off, dst);
++ emit(ctx, beqz, MIPS_R_T8, -16);
++ emit(ctx, nop); /* Delay slot */
++}
++
++/* Atomic compare-and-exchange (32-bit) */
++void emit_cmpxchg_r(struct jit_context *ctx, u8 dst, u8 src, u8 res, s16 off)
++{
++ emit(ctx, ll, MIPS_R_T9, off, dst);
++ emit(ctx, bne, MIPS_R_T9, res, 12);
++ emit(ctx, move, MIPS_R_T8, src); /* Delay slot */
++ emit(ctx, sc, MIPS_R_T8, off, dst);
++ emit(ctx, beqz, MIPS_R_T8, -20);
++ emit(ctx, move, res, MIPS_R_T9); /* Delay slot */
++ clobber_reg(ctx, res);
++}
++
++/* Swap bytes and truncate a register word or half word */
++void emit_bswap_r(struct jit_context *ctx, u8 dst, u32 width)
++{
++ u8 tmp = MIPS_R_T8;
++ u8 msk = MIPS_R_T9;
++
++ switch (width) {
++ /* Swap bytes in a word */
++ case 32:
++ if (cpu_has_mips32r2 || cpu_has_mips32r6) {
++ emit(ctx, wsbh, dst, dst);
++ emit(ctx, rotr, dst, dst, 16);
++ } else {
++ emit(ctx, sll, tmp, dst, 16); /* tmp = dst << 16 */
++ emit(ctx, srl, dst, dst, 16); /* dst = dst >> 16 */
++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */
++
++ emit(ctx, lui, msk, 0xff); /* msk = 0x00ff0000 */
++ emit(ctx, ori, msk, msk, 0xff); /* msk = msk | 0xff */
++
++ emit(ctx, and, tmp, dst, msk); /* tmp = dst & msk */
++ emit(ctx, sll, tmp, tmp, 8); /* tmp = tmp << 8 */
++ emit(ctx, srl, dst, dst, 8); /* dst = dst >> 8 */
++ emit(ctx, and, dst, dst, msk); /* dst = dst & msk */
++ emit(ctx, or, dst, dst, tmp); /* reg = dst | tmp */
++ }
++ break;
++ /* Swap bytes in a half word */
++ case 16:
++ if (cpu_has_mips32r2 || cpu_has_mips32r6) {
++ emit(ctx, wsbh, dst, dst);
++ emit(ctx, andi, dst, dst, 0xffff);
++ } else {
++ emit(ctx, andi, tmp, dst, 0xff00); /* t = d & 0xff00 */
++ emit(ctx, srl, tmp, tmp, 8); /* t = t >> 8 */
++ emit(ctx, andi, dst, dst, 0x00ff); /* d = d & 0x00ff */
++ emit(ctx, sll, dst, dst, 8); /* d = d << 8 */
++ emit(ctx, or, dst, dst, tmp); /* d = d | t */
++ }
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Validate jump immediate range */
++bool valid_jmp_i(u8 op, s32 imm)
++{
++ switch (op) {
++ case JIT_JNOP:
++ /* Immediate value not used */
++ return true;
++ case BPF_JEQ:
++ case BPF_JNE:
++ /* No immediate operation */
++ return false;
++ case BPF_JSET:
++ case JIT_JNSET:
++ /* imm must be 16 bits unsigned */
++ return imm >= 0 && imm <= 0xffff;
++ case BPF_JGE:
++ case BPF_JLT:
++ case BPF_JSGE:
++ case BPF_JSLT:
++ /* imm must be 16 bits */
++ return imm >= -0x8000 && imm <= 0x7fff;
++ case BPF_JGT:
++ case BPF_JLE:
++ case BPF_JSGT:
++ case BPF_JSLE:
++ /* imm + 1 must be 16 bits */
++ return imm >= -0x8001 && imm <= 0x7ffe;
++ }
++ return false;
++}
++
++/* Invert a conditional jump operation */
++static u8 invert_jmp(u8 op)
++{
++ switch (op) {
++ case BPF_JA: return JIT_JNOP;
++ case BPF_JEQ: return BPF_JNE;
++ case BPF_JNE: return BPF_JEQ;
++ case BPF_JSET: return JIT_JNSET;
++ case BPF_JGT: return BPF_JLE;
++ case BPF_JGE: return BPF_JLT;
++ case BPF_JLT: return BPF_JGE;
++ case BPF_JLE: return BPF_JGT;
++ case BPF_JSGT: return BPF_JSLE;
++ case BPF_JSGE: return BPF_JSLT;
++ case BPF_JSLT: return BPF_JSGE;
++ case BPF_JSLE: return BPF_JSGT;
++ }
++ return 0;
++}
++
++/* Prepare a PC-relative jump operation */
++static void setup_jmp(struct jit_context *ctx, u8 bpf_op,
++ s16 bpf_off, u8 *jit_op, s32 *jit_off)
++{
++ u32 *descp = &ctx->descriptors[ctx->bpf_index];
++ int op = bpf_op;
++ int offset = 0;
++
++ /* Do not compute offsets on the first pass */
++ if (INDEX(*descp) == 0)
++ goto done;
++
++ /* Skip jumps never taken */
++ if (bpf_op == JIT_JNOP)
++ goto done;
++
++ /* Convert jumps always taken */
++ if (bpf_op == BPF_JA)
++ *descp |= JIT_DESC_CONVERT;
++
++ /*
++ * Current ctx->jit_index points to the start of the branch preamble.
++ * Since the preamble differs among different branch conditionals,
++ * the current index cannot be used to compute the branch offset.
++ * Instead, we use the offset table value for the next instruction,
++ * which gives the index immediately after the branch delay slot.
++ */
++ if (!CONVERTED(*descp)) {
++ int target = ctx->bpf_index + bpf_off + 1;
++ int origin = ctx->bpf_index + 1;
++
++ offset = (INDEX(ctx->descriptors[target]) -
++ INDEX(ctx->descriptors[origin]) + 1) * sizeof(u32);
++ }
++
++ /*
++ * The PC-relative branch offset field on MIPS is 18 bits signed,
++ * so if the computed offset is larger than this we generate a an
++ * absolute jump that we skip with an inverted conditional branch.
++ */
++ if (CONVERTED(*descp) || offset < -0x20000 || offset > 0x1ffff) {
++ offset = 3 * sizeof(u32);
++ op = invert_jmp(bpf_op);
++ ctx->changes += !CONVERTED(*descp);
++ *descp |= JIT_DESC_CONVERT;
++ }
++
++done:
++ *jit_off = offset;
++ *jit_op = op;
++}
++
++/* Prepare a PC-relative jump operation with immediate conditional */
++void setup_jmp_i(struct jit_context *ctx, s32 imm, u8 width,
++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off)
++{
++ bool always = false;
++ bool never = false;
++
++ switch (bpf_op) {
++ case BPF_JEQ:
++ case BPF_JNE:
++ break;
++ case BPF_JSET:
++ case BPF_JLT:
++ never = imm == 0;
++ break;
++ case BPF_JGE:
++ always = imm == 0;
++ break;
++ case BPF_JGT:
++ never = (u32)imm == U32_MAX;
++ break;
++ case BPF_JLE:
++ always = (u32)imm == U32_MAX;
++ break;
++ case BPF_JSGT:
++ never = imm == S32_MAX && width == 32;
++ break;
++ case BPF_JSGE:
++ always = imm == S32_MIN && width == 32;
++ break;
++ case BPF_JSLT:
++ never = imm == S32_MIN && width == 32;
++ break;
++ case BPF_JSLE:
++ always = imm == S32_MAX && width == 32;
++ break;
++ }
++
++ if (never)
++ bpf_op = JIT_JNOP;
++ if (always)
++ bpf_op = BPF_JA;
++ setup_jmp(ctx, bpf_op, bpf_off, jit_op, jit_off);
++}
++
++/* Prepare a PC-relative jump operation with register conditional */
++void setup_jmp_r(struct jit_context *ctx, bool same_reg,
++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off)
++{
++ switch (bpf_op) {
++ case BPF_JSET:
++ break;
++ case BPF_JEQ:
++ case BPF_JGE:
++ case BPF_JLE:
++ case BPF_JSGE:
++ case BPF_JSLE:
++ if (same_reg)
++ bpf_op = BPF_JA;
++ break;
++ case BPF_JNE:
++ case BPF_JLT:
++ case BPF_JGT:
++ case BPF_JSGT:
++ case BPF_JSLT:
++ if (same_reg)
++ bpf_op = JIT_JNOP;
++ break;
++ }
++ setup_jmp(ctx, bpf_op, bpf_off, jit_op, jit_off);
++}
++
++/* Finish a PC-relative jump operation */
++int finish_jmp(struct jit_context *ctx, u8 jit_op, s16 bpf_off)
++{
++ /* Emit conditional branch delay slot */
++ if (jit_op != JIT_JNOP)
++ emit(ctx, nop);
++ /*
++ * Emit an absolute long jump with delay slot,
++ * if the PC-relative branch was converted.
++ */
++ if (CONVERTED(ctx->descriptors[ctx->bpf_index])) {
++ int target = get_target(ctx, ctx->bpf_index + bpf_off + 1);
++
++ if (target < 0)
++ return -1;
++ emit(ctx, j, target);
++ emit(ctx, nop);
++ }
++ return 0;
++}
++
++/* Jump immediate (32-bit) */
++void emit_jmp_i(struct jit_context *ctx, u8 dst, s32 imm, s32 off, u8 op)
++{
++ switch (op) {
++ /* No-op, used internally for branch optimization */
++ case JIT_JNOP:
++ break;
++ /* PC += off if dst & imm */
++ case BPF_JSET:
++ emit(ctx, andi, MIPS_R_T9, dst, (u16)imm);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */
++ case JIT_JNSET:
++ emit(ctx, andi, MIPS_R_T9, dst, (u16)imm);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst > imm */
++ case BPF_JGT:
++ emit(ctx, sltiu, MIPS_R_T9, dst, imm + 1);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst >= imm */
++ case BPF_JGE:
++ emit(ctx, sltiu, MIPS_R_T9, dst, imm);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst < imm */
++ case BPF_JLT:
++ emit(ctx, sltiu, MIPS_R_T9, dst, imm);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst <= imm */
++ case BPF_JLE:
++ emit(ctx, sltiu, MIPS_R_T9, dst, imm + 1);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst > imm (signed) */
++ case BPF_JSGT:
++ emit(ctx, slti, MIPS_R_T9, dst, imm + 1);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst >= imm (signed) */
++ case BPF_JSGE:
++ emit(ctx, slti, MIPS_R_T9, dst, imm);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst < imm (signed) */
++ case BPF_JSLT:
++ emit(ctx, slti, MIPS_R_T9, dst, imm);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JSLE:
++ emit(ctx, slti, MIPS_R_T9, dst, imm + 1);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ }
++}
++
++/* Jump register (32-bit) */
++void emit_jmp_r(struct jit_context *ctx, u8 dst, u8 src, s32 off, u8 op)
++{
++ switch (op) {
++ /* No-op, used internally for branch optimization */
++ case JIT_JNOP:
++ break;
++ /* PC += off if dst == src */
++ case BPF_JEQ:
++ emit(ctx, beq, dst, src, off);
++ break;
++ /* PC += off if dst != src */
++ case BPF_JNE:
++ emit(ctx, bne, dst, src, off);
++ break;
++ /* PC += off if dst & src */
++ case BPF_JSET:
++ emit(ctx, and, MIPS_R_T9, dst, src);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */
++ case JIT_JNSET:
++ emit(ctx, and, MIPS_R_T9, dst, src);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst > src */
++ case BPF_JGT:
++ emit(ctx, sltu, MIPS_R_T9, src, dst);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst >= src */
++ case BPF_JGE:
++ emit(ctx, sltu, MIPS_R_T9, dst, src);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst < src */
++ case BPF_JLT:
++ emit(ctx, sltu, MIPS_R_T9, dst, src);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst <= src */
++ case BPF_JLE:
++ emit(ctx, sltu, MIPS_R_T9, src, dst);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst > src (signed) */
++ case BPF_JSGT:
++ emit(ctx, slt, MIPS_R_T9, src, dst);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst >= src (signed) */
++ case BPF_JSGE:
++ emit(ctx, slt, MIPS_R_T9, dst, src);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst < src (signed) */
++ case BPF_JSLT:
++ emit(ctx, slt, MIPS_R_T9, dst, src);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst <= src (signed) */
++ case BPF_JSLE:
++ emit(ctx, slt, MIPS_R_T9, src, dst);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ }
++}
++
++/* Jump always */
++int emit_ja(struct jit_context *ctx, s16 off)
++{
++ int target = get_target(ctx, ctx->bpf_index + off + 1);
++
++ if (target < 0)
++ return -1;
++ emit(ctx, j, target);
++ emit(ctx, nop);
++ return 0;
++}
++
++/* Jump to epilogue */
++int emit_exit(struct jit_context *ctx)
++{
++ int target = get_target(ctx, ctx->program->len);
++
++ if (target < 0)
++ return -1;
++ emit(ctx, j, target);
++ emit(ctx, nop);
++ return 0;
++}
++
++/* Build the program body from eBPF bytecode */
++static int build_body(struct jit_context *ctx)
++{
++ const struct bpf_prog *prog = ctx->program;
++ unsigned int i;
++
++ ctx->stack_used = 0;
++ for (i = 0; i < prog->len; i++) {
++ const struct bpf_insn *insn = &prog->insnsi[i];
++ u32 *descp = &ctx->descriptors[i];
++ int ret;
++
++ access_reg(ctx, insn->src_reg);
++ access_reg(ctx, insn->dst_reg);
++
++ ctx->bpf_index = i;
++ if (ctx->target == NULL) {
++ ctx->changes += INDEX(*descp) != ctx->jit_index;
++ *descp &= JIT_DESC_CONVERT;
++ *descp |= ctx->jit_index;
++ }
++
++ ret = build_insn(insn, ctx);
++ if (ret < 0)
++ return ret;
++
++ if (ret > 0) {
++ i++;
++ if (ctx->target == NULL)
++ descp[1] = ctx->jit_index;
++ }
++ }
++
++ /* Store the end offset, where the epilogue begins */
++ ctx->descriptors[prog->len] = ctx->jit_index;
++ return 0;
++}
++
++/* Set the branch conversion flag on all instructions */
++static void set_convert_flag(struct jit_context *ctx, bool enable)
++{
++ const struct bpf_prog *prog = ctx->program;
++ u32 flag = enable ? JIT_DESC_CONVERT : 0;
++ unsigned int i;
++
++ for (i = 0; i <= prog->len; i++)
++ ctx->descriptors[i] = INDEX(ctx->descriptors[i]) | flag;
++}
++
++static void jit_fill_hole(void *area, unsigned int size)
++{
++ u32 *p;
++
++ /* We are guaranteed to have aligned memory. */
++ for (p = area; size >= sizeof(u32); size -= sizeof(u32))
++ uasm_i_break(&p, BRK_BUG); /* Increments p */
++}
++
++bool bpf_jit_needs_zext(void)
++{
++ return true;
++}
++
++struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
++{
++ struct bpf_prog *tmp, *orig_prog = prog;
++ struct bpf_binary_header *header = NULL;
++ struct jit_context ctx;
++ bool tmp_blinded = false;
++ unsigned int tmp_idx;
++ unsigned int image_size;
++ u8 *image_ptr;
++ int tries;
++
++ /*
++ * If BPF JIT was not enabled then we must fall back to
++ * the interpreter.
++ */
++ if (!prog->jit_requested)
++ return orig_prog;
++ /*
++ * If constant blinding was enabled and we failed during blinding
++ * then we must fall back to the interpreter. Otherwise, we save
++ * the new JITed code.
++ */
++ tmp = bpf_jit_blind_constants(prog);
++ if (IS_ERR(tmp))
++ return orig_prog;
++ if (tmp != prog) {
++ tmp_blinded = true;
++ prog = tmp;
++ }
++
++ memset(&ctx, 0, sizeof(ctx));
++ ctx.program = prog;
++
++ /*
++ * Not able to allocate memory for descriptors[], then
++ * we must fall back to the interpreter
++ */
++ ctx.descriptors = kcalloc(prog->len + 1, sizeof(*ctx.descriptors),
++ GFP_KERNEL);
++ if (ctx.descriptors == NULL)
++ goto out_err;
++
++ /* First pass discovers used resources */
++ if (build_body(&ctx) < 0)
++ goto out_err;
++ /*
++ * Second pass computes instruction offsets.
++ * If any PC-relative branches are out of range, a sequence of
++ * a PC-relative branch + a jump is generated, and we have to
++ * try again from the beginning to generate the new offsets.
++ * This is done until no additional conversions are necessary.
++ * The last two iterations are done with all branches being
++ * converted, to guarantee offset table convergence within a
++ * fixed number of iterations.
++ */
++ ctx.jit_index = 0;
++ build_prologue(&ctx);
++ tmp_idx = ctx.jit_index;
++
++ tries = JIT_MAX_ITERATIONS;
++ do {
++ ctx.jit_index = tmp_idx;
++ ctx.changes = 0;
++ if (tries == 2)
++ set_convert_flag(&ctx, true);
++ if (build_body(&ctx) < 0)
++ goto out_err;
++ } while (ctx.changes > 0 && --tries > 0);
++
++ if (WARN_ONCE(ctx.changes > 0, "JIT offsets failed to converge"))
++ goto out_err;
++
++ build_epilogue(&ctx, MIPS_R_RA);
++
++ /* Now we know the size of the structure to make */
++ image_size = sizeof(u32) * ctx.jit_index;
++ header = bpf_jit_binary_alloc(image_size, &image_ptr,
++ sizeof(u32), jit_fill_hole);
++ /*
++ * Not able to allocate memory for the structure then
++ * we must fall back to the interpretation
++ */
++ if (header == NULL)
++ goto out_err;
++
++ /* Actual pass to generate final JIT code */
++ ctx.target = (u32 *)image_ptr;
++ ctx.jit_index = 0;
++
++ /*
++ * If building the JITed code fails somehow,
++ * we fall back to the interpretation.
++ */
++ build_prologue(&ctx);
++ if (build_body(&ctx) < 0)
++ goto out_err;
++ build_epilogue(&ctx, MIPS_R_RA);
++
++ /* Populate line info meta data */
++ set_convert_flag(&ctx, false);
++ bpf_prog_fill_jited_linfo(prog, &ctx.descriptors[1]);
++
++ /* Set as read-only exec and flush instruction cache */
++ bpf_jit_binary_lock_ro(header);
++ flush_icache_range((unsigned long)header,
++ (unsigned long)&ctx.target[ctx.jit_index]);
++
++ if (bpf_jit_enable > 1)
++ bpf_jit_dump(prog->len, image_size, 2, ctx.target);
++
++ prog->bpf_func = (void *)ctx.target;
++ prog->jited = 1;
++ prog->jited_len = image_size;
++
++out:
++ if (tmp_blinded)
++ bpf_jit_prog_release_other(prog, prog == orig_prog ?
++ tmp : orig_prog);
++ kfree(ctx.descriptors);
++ return prog;
++
++out_err:
++ prog = orig_prog;
++ if (header)
++ bpf_jit_binary_free(header);
++ goto out;
++}
+--- /dev/null
++++ b/arch/mips/net/bpf_jit_comp.h
+@@ -0,0 +1,211 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Just-In-Time compiler for eBPF bytecode on 32-bit and 64-bit MIPS.
++ *
++ * Copyright (c) 2021 Anyfi Networks AB.
++ * Author: Johan Almbladh <johan.almbladh@gmail.com>
++ *
++ * Based on code and ideas from
++ * Copyright (c) 2017 Cavium, Inc.
++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com>
++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
++ */
++
++#ifndef _BPF_JIT_COMP_H
++#define _BPF_JIT_COMP_H
++
++/* MIPS registers */
++#define MIPS_R_ZERO 0 /* Const zero */
++#define MIPS_R_AT 1 /* Asm temp */
++#define MIPS_R_V0 2 /* Result */
++#define MIPS_R_V1 3 /* Result */
++#define MIPS_R_A0 4 /* Argument */
++#define MIPS_R_A1 5 /* Argument */
++#define MIPS_R_A2 6 /* Argument */
++#define MIPS_R_A3 7 /* Argument */
++#define MIPS_R_A4 8 /* Arg (n64) */
++#define MIPS_R_A5 9 /* Arg (n64) */
++#define MIPS_R_A6 10 /* Arg (n64) */
++#define MIPS_R_A7 11 /* Arg (n64) */
++#define MIPS_R_T0 8 /* Temp (o32) */
++#define MIPS_R_T1 9 /* Temp (o32) */
++#define MIPS_R_T2 10 /* Temp (o32) */
++#define MIPS_R_T3 11 /* Temp (o32) */
++#define MIPS_R_T4 12 /* Temporary */
++#define MIPS_R_T5 13 /* Temporary */
++#define MIPS_R_T6 14 /* Temporary */
++#define MIPS_R_T7 15 /* Temporary */
++#define MIPS_R_S0 16 /* Saved */
++#define MIPS_R_S1 17 /* Saved */
++#define MIPS_R_S2 18 /* Saved */
++#define MIPS_R_S3 19 /* Saved */
++#define MIPS_R_S4 20 /* Saved */
++#define MIPS_R_S5 21 /* Saved */
++#define MIPS_R_S6 22 /* Saved */
++#define MIPS_R_S7 23 /* Saved */
++#define MIPS_R_T8 24 /* Temporary */
++#define MIPS_R_T9 25 /* Temporary */
++/* MIPS_R_K0 26 Reserved */
++/* MIPS_R_K1 27 Reserved */
++#define MIPS_R_GP 28 /* Global ptr */
++#define MIPS_R_SP 29 /* Stack ptr */
++#define MIPS_R_FP 30 /* Frame ptr */
++#define MIPS_R_RA 31 /* Return */
++
++/*
++ * Jump address mask for immediate jumps. The four most significant bits
++ * must be equal to PC.
++ */
++#define MIPS_JMP_MASK 0x0fffffffUL
++
++/* Maximum number of iterations in offset table computation */
++#define JIT_MAX_ITERATIONS 8
++
++/*
++ * Jump pseudo-instructions used internally
++ * for branch conversion and branch optimization.
++ */
++#define JIT_JNSET 0xe0
++#define JIT_JNOP 0xf0
++
++/* Descriptor flag for PC-relative branch conversion */
++#define JIT_DESC_CONVERT BIT(31)
++
++/* JIT context for an eBPF program */
++struct jit_context {
++ struct bpf_prog *program; /* The eBPF program being JITed */
++ u32 *descriptors; /* eBPF to JITed CPU insn descriptors */
++ u32 *target; /* JITed code buffer */
++ u32 bpf_index; /* Index of current BPF program insn */
++ u32 jit_index; /* Index of current JIT target insn */
++ u32 changes; /* Number of PC-relative branch conv */
++ u32 accessed; /* Bit mask of read eBPF registers */
++ u32 clobbered; /* Bit mask of modified CPU registers */
++ u32 stack_size; /* Total allocated stack size in bytes */
++ u32 saved_size; /* Size of callee-saved registers */
++ u32 stack_used; /* Stack size used for function calls */
++};
++
++/* Emit the instruction if the JIT memory space has been allocated */
++#define emit(ctx, func, ...) \
++do { \
++ if ((ctx)->target != NULL) { \
++ u32 *p = &(ctx)->target[ctx->jit_index]; \
++ uasm_i_##func(&p, ##__VA_ARGS__); \
++ } \
++ (ctx)->jit_index++; \
++} while (0)
++
++/*
++ * Mark a BPF register as accessed, it needs to be
++ * initialized by the program if expected, e.g. FP.
++ */
++static inline void access_reg(struct jit_context *ctx, u8 reg)
++{
++ ctx->accessed |= BIT(reg);
++}
++
++/*
++ * Mark a CPU register as clobbered, it needs to be
++ * saved/restored by the program if callee-saved.
++ */
++static inline void clobber_reg(struct jit_context *ctx, u8 reg)
++{
++ ctx->clobbered |= BIT(reg);
++}
++
++/*
++ * Push registers on the stack, starting at a given depth from the stack
++ * pointer and increasing. The next depth to be written is returned.
++ */
++int push_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth);
++
++/*
++ * Pop registers from the stack, starting at a given depth from the stack
++ * pointer and increasing. The next depth to be read is returned.
++ */
++int pop_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth);
++
++/* Compute the 28-bit jump target address from a BPF program location */
++int get_target(struct jit_context *ctx, u32 loc);
++
++/* Compute the PC-relative offset to relative BPF program offset */
++int get_offset(const struct jit_context *ctx, int off);
++
++/* dst = imm (32-bit) */
++void emit_mov_i(struct jit_context *ctx, u8 dst, s32 imm);
++
++/* dst = src (32-bit) */
++void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src);
++
++/* Validate ALU/ALU64 immediate range */
++bool valid_alu_i(u8 op, s32 imm);
++
++/* Rewrite ALU/ALU64 immediate operation */
++bool rewrite_alu_i(u8 op, s32 imm, u8 *alu, s32 *val);
++
++/* ALU immediate operation (32-bit) */
++void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op);
++
++/* ALU register operation (32-bit) */
++void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op);
++
++/* Atomic read-modify-write (32-bit) */
++void emit_atomic_r(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 code);
++
++/* Atomic compare-and-exchange (32-bit) */
++void emit_cmpxchg_r(struct jit_context *ctx, u8 dst, u8 src, u8 res, s16 off);
++
++/* Swap bytes and truncate a register word or half word */
++void emit_bswap_r(struct jit_context *ctx, u8 dst, u32 width);
++
++/* Validate JMP/JMP32 immediate range */
++bool valid_jmp_i(u8 op, s32 imm);
++
++/* Prepare a PC-relative jump operation with immediate conditional */
++void setup_jmp_i(struct jit_context *ctx, s32 imm, u8 width,
++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off);
++
++/* Prepare a PC-relative jump operation with register conditional */
++void setup_jmp_r(struct jit_context *ctx, bool same_reg,
++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off);
++
++/* Finish a PC-relative jump operation */
++int finish_jmp(struct jit_context *ctx, u8 jit_op, s16 bpf_off);
++
++/* Conditional JMP/JMP32 immediate */
++void emit_jmp_i(struct jit_context *ctx, u8 dst, s32 imm, s32 off, u8 op);
++
++/* Conditional JMP/JMP32 register */
++void emit_jmp_r(struct jit_context *ctx, u8 dst, u8 src, s32 off, u8 op);
++
++/* Jump always */
++int emit_ja(struct jit_context *ctx, s16 off);
++
++/* Jump to epilogue */
++int emit_exit(struct jit_context *ctx);
++
++/*
++ * Build program prologue to set up the stack and registers.
++ * This function is implemented separately for 32-bit and 64-bit JITs.
++ */
++void build_prologue(struct jit_context *ctx);
++
++/*
++ * Build the program epilogue to restore the stack and registers.
++ * This function is implemented separately for 32-bit and 64-bit JITs.
++ */
++void build_epilogue(struct jit_context *ctx, int dest_reg);
++
++/*
++ * Convert an eBPF instruction to native instruction, i.e
++ * JITs an eBPF instruction.
++ * Returns :
++ * 0 - Successfully JITed an 8-byte eBPF instruction
++ * >0 - Successfully JITed a 16-byte eBPF instruction
++ * <0 - Failed to JIT.
++ * This function is implemented separately for 32-bit and 64-bit JITs.
++ */
++int build_insn(const struct bpf_insn *insn, struct jit_context *ctx);
++
++#endif /* _BPF_JIT_COMP_H */
+--- /dev/null
++++ b/arch/mips/net/bpf_jit_comp32.c
+@@ -0,0 +1,1741 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Just-In-Time compiler for eBPF bytecode on MIPS.
++ * Implementation of JIT functions for 32-bit CPUs.
++ *
++ * Copyright (c) 2021 Anyfi Networks AB.
++ * Author: Johan Almbladh <johan.almbladh@gmail.com>
++ *
++ * Based on code and ideas from
++ * Copyright (c) 2017 Cavium, Inc.
++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com>
++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
++ */
++
++#include <linux/math64.h>
++#include <linux/errno.h>
++#include <linux/filter.h>
++#include <linux/bpf.h>
++#include <asm/cpu-features.h>
++#include <asm/isa-rev.h>
++#include <asm/uasm.h>
++
++#include "bpf_jit_comp.h"
++
++/* MIPS a4-a7 are not available in the o32 ABI */
++#undef MIPS_R_A4
++#undef MIPS_R_A5
++#undef MIPS_R_A6
++#undef MIPS_R_A7
++
++/* Stack is 8-byte aligned in o32 ABI */
++#define MIPS_STACK_ALIGNMENT 8
++
++/*
++ * The top 16 bytes of a stack frame is reserved for the callee in O32 ABI.
++ * This corresponds to stack space for register arguments a0-a3.
++ */
++#define JIT_RESERVED_STACK 16
++
++/* Temporary 64-bit register used by JIT */
++#define JIT_REG_TMP MAX_BPF_JIT_REG
++
++/*
++ * Number of prologue bytes to skip when doing a tail call.
++ * Tail call count (TCC) initialization (8 bytes) always, plus
++ * R0-to-v0 assignment (4 bytes) if big endian.
++ */
++#ifdef __BIG_ENDIAN
++#define JIT_TCALL_SKIP 12
++#else
++#define JIT_TCALL_SKIP 8
++#endif
++
++/* CPU registers holding the callee return value */
++#define JIT_RETURN_REGS \
++ (BIT(MIPS_R_V0) | \
++ BIT(MIPS_R_V1))
++
++/* CPU registers arguments passed to callee directly */
++#define JIT_ARG_REGS \
++ (BIT(MIPS_R_A0) | \
++ BIT(MIPS_R_A1) | \
++ BIT(MIPS_R_A2) | \
++ BIT(MIPS_R_A3))
++
++/* CPU register arguments passed to callee on stack */
++#define JIT_STACK_REGS \
++ (BIT(MIPS_R_T0) | \
++ BIT(MIPS_R_T1) | \
++ BIT(MIPS_R_T2) | \
++ BIT(MIPS_R_T3) | \
++ BIT(MIPS_R_T4) | \
++ BIT(MIPS_R_T5))
++
++/* Caller-saved CPU registers */
++#define JIT_CALLER_REGS \
++ (JIT_RETURN_REGS | \
++ JIT_ARG_REGS | \
++ JIT_STACK_REGS)
++
++/* Callee-saved CPU registers */
++#define JIT_CALLEE_REGS \
++ (BIT(MIPS_R_S0) | \
++ BIT(MIPS_R_S1) | \
++ BIT(MIPS_R_S2) | \
++ BIT(MIPS_R_S3) | \
++ BIT(MIPS_R_S4) | \
++ BIT(MIPS_R_S5) | \
++ BIT(MIPS_R_S6) | \
++ BIT(MIPS_R_S7) | \
++ BIT(MIPS_R_GP) | \
++ BIT(MIPS_R_FP) | \
++ BIT(MIPS_R_RA))
++
++/*
++ * Mapping of 64-bit eBPF registers to 32-bit native MIPS registers.
++ *
++ * 1) Native register pairs are ordered according to CPU endiannes, following
++ * the MIPS convention for passing 64-bit arguments and return values.
++ * 2) The eBPF return value, arguments and callee-saved registers are mapped
++ * to their native MIPS equivalents.
++ * 3) Since the 32 highest bits in the eBPF FP register are always zero,
++ * only one general-purpose register is actually needed for the mapping.
++ * We use the fp register for this purpose, and map the highest bits to
++ * the MIPS register r0 (zero).
++ * 4) We use the MIPS gp and at registers as internal temporary registers
++ * for constant blinding. The gp register is callee-saved.
++ * 5) One 64-bit temporary register is mapped for use when sign-extending
++ * immediate operands. MIPS registers t6-t9 are available to the JIT
++ * for as temporaries when implementing complex 64-bit operations.
++ *
++ * With this scheme all eBPF registers are being mapped to native MIPS
++ * registers without having to use any stack scratch space. The direct
++ * register mapping (2) simplifies the handling of function calls.
++ */
++static const u8 bpf2mips32[][2] = {
++ /* Return value from in-kernel function, and exit value from eBPF */
++ [BPF_REG_0] = {MIPS_R_V1, MIPS_R_V0},
++ /* Arguments from eBPF program to in-kernel function */
++ [BPF_REG_1] = {MIPS_R_A1, MIPS_R_A0},
++ [BPF_REG_2] = {MIPS_R_A3, MIPS_R_A2},
++ /* Remaining arguments, to be passed on the stack per O32 ABI */
++ [BPF_REG_3] = {MIPS_R_T1, MIPS_R_T0},
++ [BPF_REG_4] = {MIPS_R_T3, MIPS_R_T2},
++ [BPF_REG_5] = {MIPS_R_T5, MIPS_R_T4},
++ /* Callee-saved registers that in-kernel function will preserve */
++ [BPF_REG_6] = {MIPS_R_S1, MIPS_R_S0},
++ [BPF_REG_7] = {MIPS_R_S3, MIPS_R_S2},
++ [BPF_REG_8] = {MIPS_R_S5, MIPS_R_S4},
++ [BPF_REG_9] = {MIPS_R_S7, MIPS_R_S6},
++ /* Read-only frame pointer to access the eBPF stack */
++#ifdef __BIG_ENDIAN
++ [BPF_REG_FP] = {MIPS_R_FP, MIPS_R_ZERO},
++#else
++ [BPF_REG_FP] = {MIPS_R_ZERO, MIPS_R_FP},
++#endif
++ /* Temporary register for blinding constants */
++ [BPF_REG_AX] = {MIPS_R_GP, MIPS_R_AT},
++ /* Temporary register for internal JIT use */
++ [JIT_REG_TMP] = {MIPS_R_T7, MIPS_R_T6},
++};
++
++/* Get low CPU register for a 64-bit eBPF register mapping */
++static inline u8 lo(const u8 reg[])
++{
++#ifdef __BIG_ENDIAN
++ return reg[0];
++#else
++ return reg[1];
++#endif
++}
++
++/* Get high CPU register for a 64-bit eBPF register mapping */
++static inline u8 hi(const u8 reg[])
++{
++#ifdef __BIG_ENDIAN
++ return reg[1];
++#else
++ return reg[0];
++#endif
++}
++
++/*
++ * Mark a 64-bit CPU register pair as clobbered, it needs to be
++ * saved/restored by the program if callee-saved.
++ */
++static void clobber_reg64(struct jit_context *ctx, const u8 reg[])
++{
++ clobber_reg(ctx, reg[0]);
++ clobber_reg(ctx, reg[1]);
++}
++
++/* dst = imm (sign-extended) */
++static void emit_mov_se_i64(struct jit_context *ctx, const u8 dst[], s32 imm)
++{
++ emit_mov_i(ctx, lo(dst), imm);
++ if (imm < 0)
++ emit(ctx, addiu, hi(dst), MIPS_R_ZERO, -1);
++ else
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ clobber_reg64(ctx, dst);
++}
++
++/* Zero extension, if verifier does not do it for us */
++static void emit_zext_ver(struct jit_context *ctx, const u8 dst[])
++{
++ if (!ctx->program->aux->verifier_zext) {
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ clobber_reg(ctx, hi(dst));
++ }
++}
++
++/* Load delay slot, if ISA mandates it */
++static void emit_load_delay(struct jit_context *ctx)
++{
++ if (!cpu_has_mips_2_3_4_5_r)
++ emit(ctx, nop);
++}
++
++/* ALU immediate operation (64-bit) */
++static void emit_alu_i64(struct jit_context *ctx,
++ const u8 dst[], s32 imm, u8 op)
++{
++ u8 src = MIPS_R_T6;
++
++ /*
++ * ADD/SUB with all but the max negative imm can be handled by
++ * inverting the operation and the imm value, saving one insn.
++ */
++ if (imm > S32_MIN && imm < 0)
++ switch (op) {
++ case BPF_ADD:
++ op = BPF_SUB;
++ imm = -imm;
++ break;
++ case BPF_SUB:
++ op = BPF_ADD;
++ imm = -imm;
++ break;
++ }
++
++ /* Move immediate to temporary register */
++ emit_mov_i(ctx, src, imm);
++
++ switch (op) {
++ /* dst = dst + imm */
++ case BPF_ADD:
++ emit(ctx, addu, lo(dst), lo(dst), src);
++ emit(ctx, sltu, MIPS_R_T9, lo(dst), src);
++ emit(ctx, addu, hi(dst), hi(dst), MIPS_R_T9);
++ if (imm < 0)
++ emit(ctx, addiu, hi(dst), hi(dst), -1);
++ break;
++ /* dst = dst - imm */
++ case BPF_SUB:
++ emit(ctx, sltu, MIPS_R_T9, lo(dst), src);
++ emit(ctx, subu, lo(dst), lo(dst), src);
++ emit(ctx, subu, hi(dst), hi(dst), MIPS_R_T9);
++ if (imm < 0)
++ emit(ctx, addiu, hi(dst), hi(dst), 1);
++ break;
++ /* dst = dst | imm */
++ case BPF_OR:
++ emit(ctx, or, lo(dst), lo(dst), src);
++ if (imm < 0)
++ emit(ctx, addiu, hi(dst), MIPS_R_ZERO, -1);
++ break;
++ /* dst = dst & imm */
++ case BPF_AND:
++ emit(ctx, and, lo(dst), lo(dst), src);
++ if (imm >= 0)
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ break;
++ /* dst = dst ^ imm */
++ case BPF_XOR:
++ emit(ctx, xor, lo(dst), lo(dst), src);
++ if (imm < 0) {
++ emit(ctx, subu, hi(dst), MIPS_R_ZERO, hi(dst));
++ emit(ctx, addiu, hi(dst), hi(dst), -1);
++ }
++ break;
++ }
++ clobber_reg64(ctx, dst);
++}
++
++/* ALU register operation (64-bit) */
++static void emit_alu_r64(struct jit_context *ctx,
++ const u8 dst[], const u8 src[], u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = dst + src */
++ case BPF_ADD:
++ if (src == dst) {
++ emit(ctx, srl, MIPS_R_T9, lo(dst), 31);
++ emit(ctx, addu, lo(dst), lo(dst), lo(dst));
++ } else {
++ emit(ctx, addu, lo(dst), lo(dst), lo(src));
++ emit(ctx, sltu, MIPS_R_T9, lo(dst), lo(src));
++ }
++ emit(ctx, addu, hi(dst), hi(dst), hi(src));
++ emit(ctx, addu, hi(dst), hi(dst), MIPS_R_T9);
++ break;
++ /* dst = dst - src */
++ case BPF_SUB:
++ emit(ctx, sltu, MIPS_R_T9, lo(dst), lo(src));
++ emit(ctx, subu, lo(dst), lo(dst), lo(src));
++ emit(ctx, subu, hi(dst), hi(dst), hi(src));
++ emit(ctx, subu, hi(dst), hi(dst), MIPS_R_T9);
++ break;
++ /* dst = dst | src */
++ case BPF_OR:
++ emit(ctx, or, lo(dst), lo(dst), lo(src));
++ emit(ctx, or, hi(dst), hi(dst), hi(src));
++ break;
++ /* dst = dst & src */
++ case BPF_AND:
++ emit(ctx, and, lo(dst), lo(dst), lo(src));
++ emit(ctx, and, hi(dst), hi(dst), hi(src));
++ break;
++ /* dst = dst ^ src */
++ case BPF_XOR:
++ emit(ctx, xor, lo(dst), lo(dst), lo(src));
++ emit(ctx, xor, hi(dst), hi(dst), hi(src));
++ break;
++ }
++ clobber_reg64(ctx, dst);
++}
++
++/* ALU invert (64-bit) */
++static void emit_neg_i64(struct jit_context *ctx, const u8 dst[])
++{
++ emit(ctx, sltu, MIPS_R_T9, MIPS_R_ZERO, lo(dst));
++ emit(ctx, subu, lo(dst), MIPS_R_ZERO, lo(dst));
++ emit(ctx, subu, hi(dst), MIPS_R_ZERO, hi(dst));
++ emit(ctx, subu, hi(dst), hi(dst), MIPS_R_T9);
++
++ clobber_reg64(ctx, dst);
++}
++
++/* ALU shift immediate (64-bit) */
++static void emit_shift_i64(struct jit_context *ctx,
++ const u8 dst[], u32 imm, u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = dst << imm */
++ case BPF_LSH:
++ if (imm < 32) {
++ emit(ctx, srl, MIPS_R_T9, lo(dst), 32 - imm);
++ emit(ctx, sll, lo(dst), lo(dst), imm);
++ emit(ctx, sll, hi(dst), hi(dst), imm);
++ emit(ctx, or, hi(dst), hi(dst), MIPS_R_T9);
++ } else {
++ emit(ctx, sll, hi(dst), lo(dst), imm - 32);
++ emit(ctx, move, lo(dst), MIPS_R_ZERO);
++ }
++ break;
++ /* dst = dst >> imm */
++ case BPF_RSH:
++ if (imm < 32) {
++ emit(ctx, sll, MIPS_R_T9, hi(dst), 32 - imm);
++ emit(ctx, srl, lo(dst), lo(dst), imm);
++ emit(ctx, srl, hi(dst), hi(dst), imm);
++ emit(ctx, or, lo(dst), lo(dst), MIPS_R_T9);
++ } else {
++ emit(ctx, srl, lo(dst), hi(dst), imm - 32);
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ }
++ break;
++ /* dst = dst >> imm (arithmetic) */
++ case BPF_ARSH:
++ if (imm < 32) {
++ emit(ctx, sll, MIPS_R_T9, hi(dst), 32 - imm);
++ emit(ctx, srl, lo(dst), lo(dst), imm);
++ emit(ctx, sra, hi(dst), hi(dst), imm);
++ emit(ctx, or, lo(dst), lo(dst), MIPS_R_T9);
++ } else {
++ emit(ctx, sra, lo(dst), hi(dst), imm - 32);
++ emit(ctx, sra, hi(dst), hi(dst), 31);
++ }
++ break;
++ }
++ clobber_reg64(ctx, dst);
++}
++
++/* ALU shift register (64-bit) */
++static void emit_shift_r64(struct jit_context *ctx,
++ const u8 dst[], u8 src, u8 op)
++{
++ u8 t1 = MIPS_R_T8;
++ u8 t2 = MIPS_R_T9;
++
++ emit(ctx, andi, t1, src, 32); /* t1 = src & 32 */
++ emit(ctx, beqz, t1, 16); /* PC += 16 if t1 == 0 */
++ emit(ctx, nor, t2, src, MIPS_R_ZERO); /* t2 = ~src (delay slot) */
++
++ switch (BPF_OP(op)) {
++ /* dst = dst << src */
++ case BPF_LSH:
++ /* Next: shift >= 32 */
++ emit(ctx, sllv, hi(dst), lo(dst), src); /* dh = dl << src */
++ emit(ctx, move, lo(dst), MIPS_R_ZERO); /* dl = 0 */
++ emit(ctx, b, 20); /* PC += 20 */
++ /* +16: shift < 32 */
++ emit(ctx, srl, t1, lo(dst), 1); /* t1 = dl >> 1 */
++ emit(ctx, srlv, t1, t1, t2); /* t1 = t1 >> t2 */
++ emit(ctx, sllv, lo(dst), lo(dst), src); /* dl = dl << src */
++ emit(ctx, sllv, hi(dst), hi(dst), src); /* dh = dh << src */
++ emit(ctx, or, hi(dst), hi(dst), t1); /* dh = dh | t1 */
++ break;
++ /* dst = dst >> src */
++ case BPF_RSH:
++ /* Next: shift >= 32 */
++ emit(ctx, srlv, lo(dst), hi(dst), src); /* dl = dh >> src */
++ emit(ctx, move, hi(dst), MIPS_R_ZERO); /* dh = 0 */
++ emit(ctx, b, 20); /* PC += 20 */
++ /* +16: shift < 32 */
++ emit(ctx, sll, t1, hi(dst), 1); /* t1 = dl << 1 */
++ emit(ctx, sllv, t1, t1, t2); /* t1 = t1 << t2 */
++ emit(ctx, srlv, lo(dst), lo(dst), src); /* dl = dl >> src */
++ emit(ctx, srlv, hi(dst), hi(dst), src); /* dh = dh >> src */
++ emit(ctx, or, lo(dst), lo(dst), t1); /* dl = dl | t1 */
++ break;
++ /* dst = dst >> src (arithmetic) */
++ case BPF_ARSH:
++ /* Next: shift >= 32 */
++ emit(ctx, srav, lo(dst), hi(dst), src); /* dl = dh >>a src */
++ emit(ctx, sra, hi(dst), hi(dst), 31); /* dh = dh >>a 31 */
++ emit(ctx, b, 20); /* PC += 20 */
++ /* +16: shift < 32 */
++ emit(ctx, sll, t1, hi(dst), 1); /* t1 = dl << 1 */
++ emit(ctx, sllv, t1, t1, t2); /* t1 = t1 << t2 */
++ emit(ctx, srlv, lo(dst), lo(dst), src); /* dl = dl >>a src */
++ emit(ctx, srav, hi(dst), hi(dst), src); /* dh = dh >> src */
++ emit(ctx, or, lo(dst), lo(dst), t1); /* dl = dl | t1 */
++ break;
++ }
++
++ /* +20: Done */
++ clobber_reg64(ctx, dst);
++}
++
++/* ALU mul immediate (64x32-bit) */
++static void emit_mul_i64(struct jit_context *ctx, const u8 dst[], s32 imm)
++{
++ u8 src = MIPS_R_T6;
++ u8 tmp = MIPS_R_T9;
++
++ switch (imm) {
++ /* dst = dst * 1 is a no-op */
++ case 1:
++ break;
++ /* dst = dst * -1 */
++ case -1:
++ emit_neg_i64(ctx, dst);
++ break;
++ case 0:
++ emit_mov_r(ctx, lo(dst), MIPS_R_ZERO);
++ emit_mov_r(ctx, hi(dst), MIPS_R_ZERO);
++ break;
++ /* Full 64x32 multiply */
++ default:
++ /* hi(dst) = hi(dst) * src(imm) */
++ emit_mov_i(ctx, src, imm);
++ if (cpu_has_mips32r1 || cpu_has_mips32r6) {
++ emit(ctx, mul, hi(dst), hi(dst), src);
++ } else {
++ emit(ctx, multu, hi(dst), src);
++ emit(ctx, mflo, hi(dst));
++ }
++
++ /* hi(dst) = hi(dst) - lo(dst) */
++ if (imm < 0)
++ emit(ctx, subu, hi(dst), hi(dst), lo(dst));
++
++ /* tmp = lo(dst) * src(imm) >> 32 */
++ /* lo(dst) = lo(dst) * src(imm) */
++ if (cpu_has_mips32r6) {
++ emit(ctx, muhu, tmp, lo(dst), src);
++ emit(ctx, mulu, lo(dst), lo(dst), src);
++ } else {
++ emit(ctx, multu, lo(dst), src);
++ emit(ctx, mflo, lo(dst));
++ emit(ctx, mfhi, tmp);
++ }
++
++ /* hi(dst) += tmp */
++ emit(ctx, addu, hi(dst), hi(dst), tmp);
++ clobber_reg64(ctx, dst);
++ break;
++ }
++}
++
++/* ALU mul register (64x64-bit) */
++static void emit_mul_r64(struct jit_context *ctx,
++ const u8 dst[], const u8 src[])
++{
++ u8 acc = MIPS_R_T8;
++ u8 tmp = MIPS_R_T9;
++
++ /* acc = hi(dst) * lo(src) */
++ if (cpu_has_mips32r1 || cpu_has_mips32r6) {
++ emit(ctx, mul, acc, hi(dst), lo(src));
++ } else {
++ emit(ctx, multu, hi(dst), lo(src));
++ emit(ctx, mflo, acc);
++ }
++
++ /* tmp = lo(dst) * hi(src) */
++ if (cpu_has_mips32r1 || cpu_has_mips32r6) {
++ emit(ctx, mul, tmp, lo(dst), hi(src));
++ } else {
++ emit(ctx, multu, lo(dst), hi(src));
++ emit(ctx, mflo, tmp);
++ }
++
++ /* acc += tmp */
++ emit(ctx, addu, acc, acc, tmp);
++
++ /* tmp = lo(dst) * lo(src) >> 32 */
++ /* lo(dst) = lo(dst) * lo(src) */
++ if (cpu_has_mips32r6) {
++ emit(ctx, muhu, tmp, lo(dst), lo(src));
++ emit(ctx, mulu, lo(dst), lo(dst), lo(src));
++ } else {
++ emit(ctx, multu, lo(dst), lo(src));
++ emit(ctx, mflo, lo(dst));
++ emit(ctx, mfhi, tmp);
++ }
++
++ /* hi(dst) = acc + tmp */
++ emit(ctx, addu, hi(dst), acc, tmp);
++ clobber_reg64(ctx, dst);
++}
++
++/* Helper function for 64-bit modulo */
++static u64 jit_mod64(u64 a, u64 b)
++{
++ u64 rem;
++
++ div64_u64_rem(a, b, &rem);
++ return rem;
++}
++
++/* ALU div/mod register (64-bit) */
++static void emit_divmod_r64(struct jit_context *ctx,
++ const u8 dst[], const u8 src[], u8 op)
++{
++ const u8 *r0 = bpf2mips32[BPF_REG_0]; /* Mapped to v0-v1 */
++ const u8 *r1 = bpf2mips32[BPF_REG_1]; /* Mapped to a0-a1 */
++ const u8 *r2 = bpf2mips32[BPF_REG_2]; /* Mapped to a2-a3 */
++ int exclude, k;
++ u32 addr = 0;
++
++ /* Push caller-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ 0, JIT_RESERVED_STACK);
++
++ /* Put 64-bit arguments 1 and 2 in registers a0-a3 */
++ for (k = 0; k < 2; k++) {
++ emit(ctx, move, MIPS_R_T9, src[k]);
++ emit(ctx, move, r1[k], dst[k]);
++ emit(ctx, move, r2[k], MIPS_R_T9);
++ }
++
++ /* Emit function call */
++ switch (BPF_OP(op)) {
++ /* dst = dst / src */
++ case BPF_DIV:
++ addr = (u32)&div64_u64;
++ break;
++ /* dst = dst % src */
++ case BPF_MOD:
++ addr = (u32)&jit_mod64;
++ break;
++ }
++ emit_mov_i(ctx, MIPS_R_T9, addr);
++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9);
++ emit(ctx, nop); /* Delay slot */
++
++ /* Store the 64-bit result in dst */
++ emit(ctx, move, dst[0], r0[0]);
++ emit(ctx, move, dst[1], r0[1]);
++
++ /* Restore caller-saved registers, excluding the computed result */
++ exclude = BIT(lo(dst)) | BIT(hi(dst));
++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ exclude, JIT_RESERVED_STACK);
++ emit_load_delay(ctx);
++
++ clobber_reg64(ctx, dst);
++ clobber_reg(ctx, MIPS_R_V0);
++ clobber_reg(ctx, MIPS_R_V1);
++ clobber_reg(ctx, MIPS_R_RA);
++}
++
++/* Swap bytes in a register word */
++static void emit_swap8_r(struct jit_context *ctx, u8 dst, u8 src, u8 mask)
++{
++ u8 tmp = MIPS_R_T9;
++
++ emit(ctx, and, tmp, src, mask); /* tmp = src & 0x00ff00ff */
++ emit(ctx, sll, tmp, tmp, 8); /* tmp = tmp << 8 */
++ emit(ctx, srl, dst, src, 8); /* dst = src >> 8 */
++ emit(ctx, and, dst, dst, mask); /* dst = dst & 0x00ff00ff */
++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */
++}
++
++/* Swap half words in a register word */
++static void emit_swap16_r(struct jit_context *ctx, u8 dst, u8 src)
++{
++ u8 tmp = MIPS_R_T9;
++
++ emit(ctx, sll, tmp, src, 16); /* tmp = src << 16 */
++ emit(ctx, srl, dst, src, 16); /* dst = src >> 16 */
++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */
++}
++
++/* Swap bytes and truncate a register double word, word or half word */
++static void emit_bswap_r64(struct jit_context *ctx, const u8 dst[], u32 width)
++{
++ u8 tmp = MIPS_R_T8;
++
++ switch (width) {
++ /* Swap bytes in a double word */
++ case 64:
++ if (cpu_has_mips32r2 || cpu_has_mips32r6) {
++ emit(ctx, rotr, tmp, hi(dst), 16);
++ emit(ctx, rotr, hi(dst), lo(dst), 16);
++ emit(ctx, wsbh, lo(dst), tmp);
++ emit(ctx, wsbh, hi(dst), hi(dst));
++ } else {
++ emit_swap16_r(ctx, tmp, lo(dst));
++ emit_swap16_r(ctx, lo(dst), hi(dst));
++ emit(ctx, move, hi(dst), tmp);
++
++ emit(ctx, lui, tmp, 0xff); /* tmp = 0x00ff0000 */
++ emit(ctx, ori, tmp, tmp, 0xff); /* tmp = 0x00ff00ff */
++ emit_swap8_r(ctx, lo(dst), lo(dst), tmp);
++ emit_swap8_r(ctx, hi(dst), hi(dst), tmp);
++ }
++ break;
++ /* Swap bytes in a word */
++ /* Swap bytes in a half word */
++ case 32:
++ case 16:
++ emit_bswap_r(ctx, lo(dst), width);
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ break;
++ }
++ clobber_reg64(ctx, dst);
++}
++
++/* Truncate a register double word, word or half word */
++static void emit_trunc_r64(struct jit_context *ctx, const u8 dst[], u32 width)
++{
++ switch (width) {
++ case 64:
++ break;
++ /* Zero-extend a word */
++ case 32:
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ clobber_reg(ctx, hi(dst));
++ break;
++ /* Zero-extend a half word */
++ case 16:
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ emit(ctx, andi, lo(dst), lo(dst), 0xffff);
++ clobber_reg64(ctx, dst);
++ break;
++ }
++}
++
++/* Load operation: dst = *(size*)(src + off) */
++static void emit_ldx(struct jit_context *ctx,
++ const u8 dst[], u8 src, s16 off, u8 size)
++{
++ switch (size) {
++ /* Load a byte */
++ case BPF_B:
++ emit(ctx, lbu, lo(dst), off, src);
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ break;
++ /* Load a half word */
++ case BPF_H:
++ emit(ctx, lhu, lo(dst), off, src);
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ break;
++ /* Load a word */
++ case BPF_W:
++ emit(ctx, lw, lo(dst), off, src);
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ break;
++ /* Load a double word */
++ case BPF_DW:
++ if (dst[1] == src) {
++ emit(ctx, lw, dst[0], off + 4, src);
++ emit(ctx, lw, dst[1], off, src);
++ } else {
++ emit(ctx, lw, dst[1], off, src);
++ emit(ctx, lw, dst[0], off + 4, src);
++ }
++ emit_load_delay(ctx);
++ break;
++ }
++ clobber_reg64(ctx, dst);
++}
++
++/* Store operation: *(size *)(dst + off) = src */
++static void emit_stx(struct jit_context *ctx,
++ const u8 dst, const u8 src[], s16 off, u8 size)
++{
++ switch (size) {
++ /* Store a byte */
++ case BPF_B:
++ emit(ctx, sb, lo(src), off, dst);
++ break;
++ /* Store a half word */
++ case BPF_H:
++ emit(ctx, sh, lo(src), off, dst);
++ break;
++ /* Store a word */
++ case BPF_W:
++ emit(ctx, sw, lo(src), off, dst);
++ break;
++ /* Store a double word */
++ case BPF_DW:
++ emit(ctx, sw, src[1], off, dst);
++ emit(ctx, sw, src[0], off + 4, dst);
++ break;
++ }
++}
++
++/* Atomic read-modify-write (32-bit, non-ll/sc fallback) */
++static void emit_atomic_r32(struct jit_context *ctx,
++ u8 dst, u8 src, s16 off, u8 code)
++{
++ u32 exclude = 0;
++ u32 addr = 0;
++
++ /* Push caller-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ 0, JIT_RESERVED_STACK);
++ /*
++ * Argument 1: dst+off if xchg, otherwise src, passed in register a0
++ * Argument 2: src if xchg, othersize dst+off, passed in register a1
++ */
++ emit(ctx, move, MIPS_R_T9, dst);
++ emit(ctx, move, MIPS_R_A0, src);
++ emit(ctx, addiu, MIPS_R_A1, MIPS_R_T9, off);
++
++ /* Emit function call */
++ switch (code) {
++ case BPF_ADD:
++ addr = (u32)&atomic_add;
++ break;
++ case BPF_SUB:
++ addr = (u32)&atomic_sub;
++ break;
++ case BPF_OR:
++ addr = (u32)&atomic_or;
++ break;
++ case BPF_AND:
++ addr = (u32)&atomic_and;
++ break;
++ case BPF_XOR:
++ addr = (u32)&atomic_xor;
++ break;
++ }
++ emit_mov_i(ctx, MIPS_R_T9, addr);
++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9);
++ emit(ctx, nop); /* Delay slot */
++
++ /* Restore caller-saved registers, except any fetched value */
++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ exclude, JIT_RESERVED_STACK);
++ emit_load_delay(ctx);
++ clobber_reg(ctx, MIPS_R_RA);
++}
++
++/* Atomic read-modify-write (64-bit) */
++static void emit_atomic_r64(struct jit_context *ctx,
++ u8 dst, const u8 src[], s16 off, u8 code)
++{
++ const u8 *r1 = bpf2mips32[BPF_REG_1]; /* Mapped to a0-a1 */
++ u32 exclude = 0;
++ u32 addr = 0;
++
++ /* Push caller-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ 0, JIT_RESERVED_STACK);
++ /*
++ * Argument 1: 64-bit src, passed in registers a0-a1
++ * Argument 2: 32-bit dst+off, passed in register a2
++ */
++ emit(ctx, move, MIPS_R_T9, dst);
++ emit(ctx, move, r1[0], src[0]);
++ emit(ctx, move, r1[1], src[1]);
++ emit(ctx, addiu, MIPS_R_A2, MIPS_R_T9, off);
++
++ /* Emit function call */
++ switch (code) {
++ case BPF_ADD:
++ addr = (u32)&atomic64_add;
++ break;
++ case BPF_SUB:
++ addr = (u32)&atomic64_sub;
++ break;
++ case BPF_OR:
++ addr = (u32)&atomic64_or;
++ break;
++ case BPF_AND:
++ addr = (u32)&atomic64_and;
++ break;
++ case BPF_XOR:
++ addr = (u32)&atomic64_xor;
++ break;
++ }
++ emit_mov_i(ctx, MIPS_R_T9, addr);
++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9);
++ emit(ctx, nop); /* Delay slot */
++
++ /* Restore caller-saved registers, except any fetched value */
++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ exclude, JIT_RESERVED_STACK);
++ emit_load_delay(ctx);
++ clobber_reg(ctx, MIPS_R_RA);
++}
++
++/*
++ * Conditional movz or an emulated equivalent.
++ * Note that the rs register may be modified.
++ */
++static void emit_movz_r(struct jit_context *ctx, u8 rd, u8 rs, u8 rt)
++{
++ if (cpu_has_mips_2) {
++ emit(ctx, movz, rd, rs, rt); /* rd = rt ? rd : rs */
++ } else if (cpu_has_mips32r6) {
++ if (rs != MIPS_R_ZERO)
++ emit(ctx, seleqz, rs, rs, rt); /* rs = 0 if rt == 0 */
++ emit(ctx, selnez, rd, rd, rt); /* rd = 0 if rt != 0 */
++ if (rs != MIPS_R_ZERO)
++ emit(ctx, or, rd, rd, rs); /* rd = rd | rs */
++ } else {
++ emit(ctx, bnez, rt, 8); /* PC += 8 if rd != 0 */
++ emit(ctx, nop); /* +0: delay slot */
++ emit(ctx, or, rd, rs, MIPS_R_ZERO); /* +4: rd = rs */
++ }
++ clobber_reg(ctx, rd);
++ clobber_reg(ctx, rs);
++}
++
++/*
++ * Conditional movn or an emulated equivalent.
++ * Note that the rs register may be modified.
++ */
++static void emit_movn_r(struct jit_context *ctx, u8 rd, u8 rs, u8 rt)
++{
++ if (cpu_has_mips_2) {
++ emit(ctx, movn, rd, rs, rt); /* rd = rt ? rs : rd */
++ } else if (cpu_has_mips32r6) {
++ if (rs != MIPS_R_ZERO)
++ emit(ctx, selnez, rs, rs, rt); /* rs = 0 if rt == 0 */
++ emit(ctx, seleqz, rd, rd, rt); /* rd = 0 if rt != 0 */
++ if (rs != MIPS_R_ZERO)
++ emit(ctx, or, rd, rd, rs); /* rd = rd | rs */
++ } else {
++ emit(ctx, beqz, rt, 8); /* PC += 8 if rd == 0 */
++ emit(ctx, nop); /* +0: delay slot */
++ emit(ctx, or, rd, rs, MIPS_R_ZERO); /* +4: rd = rs */
++ }
++ clobber_reg(ctx, rd);
++ clobber_reg(ctx, rs);
++}
++
++/* Emulation of 64-bit sltiu rd, rs, imm, where imm may be S32_MAX + 1 */
++static void emit_sltiu_r64(struct jit_context *ctx, u8 rd,
++ const u8 rs[], s64 imm)
++{
++ u8 tmp = MIPS_R_T9;
++
++ if (imm < 0) {
++ emit_mov_i(ctx, rd, imm); /* rd = imm */
++ emit(ctx, sltu, rd, lo(rs), rd); /* rd = rsl < rd */
++ emit(ctx, sltiu, tmp, hi(rs), -1); /* tmp = rsh < ~0U */
++ emit(ctx, or, rd, rd, tmp); /* rd = rd | tmp */
++ } else { /* imm >= 0 */
++ if (imm > 0x7fff) {
++ emit_mov_i(ctx, rd, (s32)imm); /* rd = imm */
++ emit(ctx, sltu, rd, lo(rs), rd); /* rd = rsl < rd */
++ } else {
++ emit(ctx, sltiu, rd, lo(rs), imm); /* rd = rsl < imm */
++ }
++ emit_movn_r(ctx, rd, MIPS_R_ZERO, hi(rs)); /* rd = 0 if rsh */
++ }
++}
++
++/* Emulation of 64-bit sltu rd, rs, rt */
++static void emit_sltu_r64(struct jit_context *ctx, u8 rd,
++ const u8 rs[], const u8 rt[])
++{
++ u8 tmp = MIPS_R_T9;
++
++ emit(ctx, sltu, rd, lo(rs), lo(rt)); /* rd = rsl < rtl */
++ emit(ctx, subu, tmp, hi(rs), hi(rt)); /* tmp = rsh - rth */
++ emit_movn_r(ctx, rd, MIPS_R_ZERO, tmp); /* rd = 0 if tmp != 0 */
++ emit(ctx, sltu, tmp, hi(rs), hi(rt)); /* tmp = rsh < rth */
++ emit(ctx, or, rd, rd, tmp); /* rd = rd | tmp */
++}
++
++/* Emulation of 64-bit slti rd, rs, imm, where imm may be S32_MAX + 1 */
++static void emit_slti_r64(struct jit_context *ctx, u8 rd,
++ const u8 rs[], s64 imm)
++{
++ u8 t1 = MIPS_R_T8;
++ u8 t2 = MIPS_R_T9;
++ u8 cmp;
++
++ /*
++ * if ((rs < 0) ^ (imm < 0)) t1 = imm >u rsl
++ * else t1 = rsl <u imm
++ */
++ emit_mov_i(ctx, rd, (s32)imm);
++ emit(ctx, sltu, t1, lo(rs), rd); /* t1 = rsl <u imm */
++ emit(ctx, sltu, t2, rd, lo(rs)); /* t2 = imm <u rsl */
++ emit(ctx, srl, rd, hi(rs), 31); /* rd = rsh >> 31 */
++ if (imm < 0)
++ emit_movz_r(ctx, t1, t2, rd); /* t1 = rd ? t1 : t2 */
++ else
++ emit_movn_r(ctx, t1, t2, rd); /* t1 = rd ? t2 : t1 */
++ /*
++ * if ((imm < 0 && rsh != 0xffffffff) ||
++ * (imm >= 0 && rsh != 0))
++ * t1 = 0
++ */
++ if (imm < 0) {
++ emit(ctx, addiu, rd, hi(rs), 1); /* rd = rsh + 1 */
++ cmp = rd;
++ } else { /* imm >= 0 */
++ cmp = hi(rs);
++ }
++ emit_movn_r(ctx, t1, MIPS_R_ZERO, cmp); /* t1 = 0 if cmp != 0 */
++
++ /*
++ * if (imm < 0) rd = rsh < -1
++ * else rd = rsh != 0
++ * rd = rd | t1
++ */
++ emit(ctx, slti, rd, hi(rs), imm < 0 ? -1 : 0); /* rd = rsh < hi(imm) */
++ emit(ctx, or, rd, rd, t1); /* rd = rd | t1 */
++}
++
++/* Emulation of 64-bit(slt rd, rs, rt) */
++static void emit_slt_r64(struct jit_context *ctx, u8 rd,
++ const u8 rs[], const u8 rt[])
++{
++ u8 t1 = MIPS_R_T7;
++ u8 t2 = MIPS_R_T8;
++ u8 t3 = MIPS_R_T9;
++
++ /*
++ * if ((rs < 0) ^ (rt < 0)) t1 = rtl <u rsl
++ * else t1 = rsl <u rtl
++ * if (rsh == rth) t1 = 0
++ */
++ emit(ctx, sltu, t1, lo(rs), lo(rt)); /* t1 = rsl <u rtl */
++ emit(ctx, sltu, t2, lo(rt), lo(rs)); /* t2 = rtl <u rsl */
++ emit(ctx, xor, t3, hi(rs), hi(rt)); /* t3 = rlh ^ rth */
++ emit(ctx, srl, rd, t3, 31); /* rd = t3 >> 31 */
++ emit_movn_r(ctx, t1, t2, rd); /* t1 = rd ? t2 : t1 */
++ emit_movn_r(ctx, t1, MIPS_R_ZERO, t3); /* t1 = 0 if t3 != 0 */
++
++ /* rd = (rsh < rth) | t1 */
++ emit(ctx, slt, rd, hi(rs), hi(rt)); /* rd = rsh <s rth */
++ emit(ctx, or, rd, rd, t1); /* rd = rd | t1 */
++}
++
++/* Jump immediate (64-bit) */
++static void emit_jmp_i64(struct jit_context *ctx,
++ const u8 dst[], s32 imm, s32 off, u8 op)
++{
++ u8 tmp = MIPS_R_T6;
++
++ switch (op) {
++ /* No-op, used internally for branch optimization */
++ case JIT_JNOP:
++ break;
++ /* PC += off if dst == imm */
++ /* PC += off if dst != imm */
++ case BPF_JEQ:
++ case BPF_JNE:
++ if (imm >= -0x7fff && imm <= 0x8000) {
++ emit(ctx, addiu, tmp, lo(dst), -imm);
++ } else if ((u32)imm <= 0xffff) {
++ emit(ctx, xori, tmp, lo(dst), imm);
++ } else { /* Register fallback */
++ emit_mov_i(ctx, tmp, imm);
++ emit(ctx, xor, tmp, lo(dst), tmp);
++ }
++ if (imm < 0) { /* Compare sign extension */
++ emit(ctx, addu, MIPS_R_T9, hi(dst), 1);
++ emit(ctx, or, tmp, tmp, MIPS_R_T9);
++ } else { /* Compare zero extension */
++ emit(ctx, or, tmp, tmp, hi(dst));
++ }
++ if (op == BPF_JEQ)
++ emit(ctx, beqz, tmp, off);
++ else /* BPF_JNE */
++ emit(ctx, bnez, tmp, off);
++ break;
++ /* PC += off if dst & imm */
++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */
++ case BPF_JSET:
++ case JIT_JNSET:
++ if ((u32)imm <= 0xffff) {
++ emit(ctx, andi, tmp, lo(dst), imm);
++ } else { /* Register fallback */
++ emit_mov_i(ctx, tmp, imm);
++ emit(ctx, and, tmp, lo(dst), tmp);
++ }
++ if (imm < 0) /* Sign-extension pulls in high word */
++ emit(ctx, or, tmp, tmp, hi(dst));
++ if (op == BPF_JSET)
++ emit(ctx, bnez, tmp, off);
++ else /* JIT_JNSET */
++ emit(ctx, beqz, tmp, off);
++ break;
++ /* PC += off if dst > imm */
++ case BPF_JGT:
++ emit_sltiu_r64(ctx, tmp, dst, (s64)imm + 1);
++ emit(ctx, beqz, tmp, off);
++ break;
++ /* PC += off if dst >= imm */
++ case BPF_JGE:
++ emit_sltiu_r64(ctx, tmp, dst, imm);
++ emit(ctx, beqz, tmp, off);
++ break;
++ /* PC += off if dst < imm */
++ case BPF_JLT:
++ emit_sltiu_r64(ctx, tmp, dst, imm);
++ emit(ctx, bnez, tmp, off);
++ break;
++ /* PC += off if dst <= imm */
++ case BPF_JLE:
++ emit_sltiu_r64(ctx, tmp, dst, (s64)imm + 1);
++ emit(ctx, bnez, tmp, off);
++ break;
++ /* PC += off if dst > imm (signed) */
++ case BPF_JSGT:
++ emit_slti_r64(ctx, tmp, dst, (s64)imm + 1);
++ emit(ctx, beqz, tmp, off);
++ break;
++ /* PC += off if dst >= imm (signed) */
++ case BPF_JSGE:
++ emit_slti_r64(ctx, tmp, dst, imm);
++ emit(ctx, beqz, tmp, off);
++ break;
++ /* PC += off if dst < imm (signed) */
++ case BPF_JSLT:
++ emit_slti_r64(ctx, tmp, dst, imm);
++ emit(ctx, bnez, tmp, off);
++ break;
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JSLE:
++ emit_slti_r64(ctx, tmp, dst, (s64)imm + 1);
++ emit(ctx, bnez, tmp, off);
++ break;
++ }
++}
++
++/* Jump register (64-bit) */
++static void emit_jmp_r64(struct jit_context *ctx,
++ const u8 dst[], const u8 src[], s32 off, u8 op)
++{
++ u8 t1 = MIPS_R_T6;
++ u8 t2 = MIPS_R_T7;
++
++ switch (op) {
++ /* No-op, used internally for branch optimization */
++ case JIT_JNOP:
++ break;
++ /* PC += off if dst == src */
++ /* PC += off if dst != src */
++ case BPF_JEQ:
++ case BPF_JNE:
++ emit(ctx, subu, t1, lo(dst), lo(src));
++ emit(ctx, subu, t2, hi(dst), hi(src));
++ emit(ctx, or, t1, t1, t2);
++ if (op == BPF_JEQ)
++ emit(ctx, beqz, t1, off);
++ else /* BPF_JNE */
++ emit(ctx, bnez, t1, off);
++ break;
++ /* PC += off if dst & src */
++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */
++ case BPF_JSET:
++ case JIT_JNSET:
++ emit(ctx, and, t1, lo(dst), lo(src));
++ emit(ctx, and, t2, hi(dst), hi(src));
++ emit(ctx, or, t1, t1, t2);
++ if (op == BPF_JSET)
++ emit(ctx, bnez, t1, off);
++ else /* JIT_JNSET */
++ emit(ctx, beqz, t1, off);
++ break;
++ /* PC += off if dst > src */
++ case BPF_JGT:
++ emit_sltu_r64(ctx, t1, src, dst);
++ emit(ctx, bnez, t1, off);
++ break;
++ /* PC += off if dst >= src */
++ case BPF_JGE:
++ emit_sltu_r64(ctx, t1, dst, src);
++ emit(ctx, beqz, t1, off);
++ break;
++ /* PC += off if dst < src */
++ case BPF_JLT:
++ emit_sltu_r64(ctx, t1, dst, src);
++ emit(ctx, bnez, t1, off);
++ break;
++ /* PC += off if dst <= src */
++ case BPF_JLE:
++ emit_sltu_r64(ctx, t1, src, dst);
++ emit(ctx, beqz, t1, off);
++ break;
++ /* PC += off if dst > src (signed) */
++ case BPF_JSGT:
++ emit_slt_r64(ctx, t1, src, dst);
++ emit(ctx, bnez, t1, off);
++ break;
++ /* PC += off if dst >= src (signed) */
++ case BPF_JSGE:
++ emit_slt_r64(ctx, t1, dst, src);
++ emit(ctx, beqz, t1, off);
++ break;
++ /* PC += off if dst < src (signed) */
++ case BPF_JSLT:
++ emit_slt_r64(ctx, t1, dst, src);
++ emit(ctx, bnez, t1, off);
++ break;
++ /* PC += off if dst <= src (signed) */
++ case BPF_JSLE:
++ emit_slt_r64(ctx, t1, src, dst);
++ emit(ctx, beqz, t1, off);
++ break;
++ }
++}
++
++/* Function call */
++static int emit_call(struct jit_context *ctx, const struct bpf_insn *insn)
++{
++ bool fixed;
++ u64 addr;
++
++ /* Decode the call address */
++ if (bpf_jit_get_func_addr(ctx->program, insn, false,
++ &addr, &fixed) < 0)
++ return -1;
++ if (!fixed)
++ return -1;
++
++ /* Push stack arguments */
++ push_regs(ctx, JIT_STACK_REGS, 0, JIT_RESERVED_STACK);
++
++ /* Emit function call */
++ emit_mov_i(ctx, MIPS_R_T9, addr);
++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9);
++ emit(ctx, nop); /* Delay slot */
++
++ clobber_reg(ctx, MIPS_R_RA);
++ clobber_reg(ctx, MIPS_R_V0);
++ clobber_reg(ctx, MIPS_R_V1);
++ return 0;
++}
++
++/* Function tail call */
++static int emit_tail_call(struct jit_context *ctx)
++{
++ u8 ary = lo(bpf2mips32[BPF_REG_2]);
++ u8 ind = lo(bpf2mips32[BPF_REG_3]);
++ u8 t1 = MIPS_R_T8;
++ u8 t2 = MIPS_R_T9;
++ int off;
++
++ /*
++ * Tail call:
++ * eBPF R1 - function argument (context ptr), passed in a0-a1
++ * eBPF R2 - ptr to object with array of function entry points
++ * eBPF R3 - array index of function to be called
++ * stack[sz] - remaining tail call count, initialized in prologue
++ */
++
++ /* if (ind >= ary->map.max_entries) goto out */
++ off = offsetof(struct bpf_array, map.max_entries);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, lw, t1, off, ary); /* t1 = ary->map.max_entries*/
++ emit_load_delay(ctx); /* Load delay slot */
++ emit(ctx, sltu, t1, ind, t1); /* t1 = ind < t1 */
++ emit(ctx, beqz, t1, get_offset(ctx, 1)); /* PC += off(1) if t1 == 0 */
++ /* (next insn delay slot) */
++ /* if (TCC-- <= 0) goto out */
++ emit(ctx, lw, t2, ctx->stack_size, MIPS_R_SP); /* t2 = *(SP + size) */
++ emit_load_delay(ctx); /* Load delay slot */
++ emit(ctx, blez, t2, get_offset(ctx, 1)); /* PC += off(1) if t2 < 0 */
++ emit(ctx, addiu, t2, t2, -1); /* t2-- (delay slot) */
++ emit(ctx, sw, t2, ctx->stack_size, MIPS_R_SP); /* *(SP + size) = t2 */
++
++ /* prog = ary->ptrs[ind] */
++ off = offsetof(struct bpf_array, ptrs);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, sll, t1, ind, 2); /* t1 = ind << 2 */
++ emit(ctx, addu, t1, t1, ary); /* t1 += ary */
++ emit(ctx, lw, t2, off, t1); /* t2 = *(t1 + off) */
++ emit_load_delay(ctx); /* Load delay slot */
++
++ /* if (prog == 0) goto out */
++ emit(ctx, beqz, t2, get_offset(ctx, 1)); /* PC += off(1) if t2 == 0 */
++ emit(ctx, nop); /* Delay slot */
++
++ /* func = prog->bpf_func + 8 (prologue skip offset) */
++ off = offsetof(struct bpf_prog, bpf_func);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, lw, t1, off, t2); /* t1 = *(t2 + off) */
++ emit_load_delay(ctx); /* Load delay slot */
++ emit(ctx, addiu, t1, t1, JIT_TCALL_SKIP); /* t1 += skip (8 or 12) */
++
++ /* goto func */
++ build_epilogue(ctx, t1);
++ return 0;
++}
++
++/*
++ * Stack frame layout for a JITed program (stack grows down).
++ *
++ * Higher address : Caller's stack frame :
++ * :----------------------------:
++ * : 64-bit eBPF args r3-r5 :
++ * :----------------------------:
++ * : Reserved / tail call count :
++ * +============================+ <--- MIPS sp before call
++ * | Callee-saved registers, |
++ * | including RA and FP |
++ * +----------------------------+ <--- eBPF FP (MIPS zero,fp)
++ * | Local eBPF variables |
++ * | allocated by program |
++ * +----------------------------+
++ * | Reserved for caller-saved |
++ * | registers |
++ * +----------------------------+
++ * | Reserved for 64-bit eBPF |
++ * | args r3-r5 & args passed |
++ * | on stack in kernel calls |
++ * Lower address +============================+ <--- MIPS sp
++ */
++
++/* Build program prologue to set up the stack and registers */
++void build_prologue(struct jit_context *ctx)
++{
++ const u8 *r1 = bpf2mips32[BPF_REG_1];
++ const u8 *fp = bpf2mips32[BPF_REG_FP];
++ int stack, saved, locals, reserved;
++
++ /*
++ * The first two instructions initialize TCC in the reserved (for us)
++ * 16-byte area in the parent's stack frame. On a tail call, the
++ * calling function jumps into the prologue after these instructions.
++ */
++ emit(ctx, ori, MIPS_R_T9, MIPS_R_ZERO,
++ min(MAX_TAIL_CALL_CNT + 1, 0xffff));
++ emit(ctx, sw, MIPS_R_T9, 0, MIPS_R_SP);
++
++ /*
++ * Register eBPF R1 contains the 32-bit context pointer argument.
++ * A 32-bit argument is always passed in MIPS register a0, regardless
++ * of CPU endianness. Initialize R1 accordingly and zero-extend.
++ */
++#ifdef __BIG_ENDIAN
++ emit(ctx, move, lo(r1), MIPS_R_A0);
++#endif
++
++ /* === Entry-point for tail calls === */
++
++ /* Zero-extend the 32-bit argument */
++ emit(ctx, move, hi(r1), MIPS_R_ZERO);
++
++ /* If the eBPF frame pointer was accessed it must be saved */
++ if (ctx->accessed & BIT(BPF_REG_FP))
++ clobber_reg64(ctx, fp);
++
++ /* Compute the stack space needed for callee-saved registers */
++ saved = hweight32(ctx->clobbered & JIT_CALLEE_REGS) * sizeof(u32);
++ saved = ALIGN(saved, MIPS_STACK_ALIGNMENT);
++
++ /* Stack space used by eBPF program local data */
++ locals = ALIGN(ctx->program->aux->stack_depth, MIPS_STACK_ALIGNMENT);
++
++ /*
++ * If we are emitting function calls, reserve extra stack space for
++ * caller-saved registers and function arguments passed on the stack.
++ * The required space is computed automatically during resource
++ * usage discovery (pass 1).
++ */
++ reserved = ctx->stack_used;
++
++ /* Allocate the stack frame */
++ stack = ALIGN(saved + locals + reserved, MIPS_STACK_ALIGNMENT);
++ emit(ctx, addiu, MIPS_R_SP, MIPS_R_SP, -stack);
++
++ /* Store callee-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0, stack - saved);
++
++ /* Initialize the eBPF frame pointer if accessed */
++ if (ctx->accessed & BIT(BPF_REG_FP))
++ emit(ctx, addiu, lo(fp), MIPS_R_SP, stack - saved);
++
++ ctx->saved_size = saved;
++ ctx->stack_size = stack;
++}
++
++/* Build the program epilogue to restore the stack and registers */
++void build_epilogue(struct jit_context *ctx, int dest_reg)
++{
++ /* Restore callee-saved registers from stack */
++ pop_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0,
++ ctx->stack_size - ctx->saved_size);
++ /*
++ * A 32-bit return value is always passed in MIPS register v0,
++ * but on big-endian targets the low part of R0 is mapped to v1.
++ */
++#ifdef __BIG_ENDIAN
++ emit(ctx, move, MIPS_R_V0, MIPS_R_V1);
++#endif
++
++ /* Jump to the return address and adjust the stack pointer */
++ emit(ctx, jr, dest_reg);
++ emit(ctx, addiu, MIPS_R_SP, MIPS_R_SP, ctx->stack_size);
++}
++
++/* Build one eBPF instruction */
++int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
++{
++ const u8 *dst = bpf2mips32[insn->dst_reg];
++ const u8 *src = bpf2mips32[insn->src_reg];
++ const u8 *tmp = bpf2mips32[JIT_REG_TMP];
++ u8 code = insn->code;
++ s16 off = insn->off;
++ s32 imm = insn->imm;
++ s32 val, rel;
++ u8 alu, jmp;
++
++ switch (code) {
++ /* ALU operations */
++ /* dst = imm */
++ case BPF_ALU | BPF_MOV | BPF_K:
++ emit_mov_i(ctx, lo(dst), imm);
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = src */
++ case BPF_ALU | BPF_MOV | BPF_X:
++ if (imm == 1) {
++ /* Special mov32 for zext */
++ emit_mov_i(ctx, hi(dst), 0);
++ } else {
++ emit_mov_r(ctx, lo(dst), lo(src));
++ emit_zext_ver(ctx, dst);
++ }
++ break;
++ /* dst = -dst */
++ case BPF_ALU | BPF_NEG:
++ emit_alu_i(ctx, lo(dst), 0, BPF_NEG);
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst & imm */
++ /* dst = dst | imm */
++ /* dst = dst ^ imm */
++ /* dst = dst << imm */
++ /* dst = dst >> imm */
++ /* dst = dst >> imm (arithmetic) */
++ /* dst = dst + imm */
++ /* dst = dst - imm */
++ /* dst = dst * imm */
++ /* dst = dst / imm */
++ /* dst = dst % imm */
++ case BPF_ALU | BPF_OR | BPF_K:
++ case BPF_ALU | BPF_AND | BPF_K:
++ case BPF_ALU | BPF_XOR | BPF_K:
++ case BPF_ALU | BPF_LSH | BPF_K:
++ case BPF_ALU | BPF_RSH | BPF_K:
++ case BPF_ALU | BPF_ARSH | BPF_K:
++ case BPF_ALU | BPF_ADD | BPF_K:
++ case BPF_ALU | BPF_SUB | BPF_K:
++ case BPF_ALU | BPF_MUL | BPF_K:
++ case BPF_ALU | BPF_DIV | BPF_K:
++ case BPF_ALU | BPF_MOD | BPF_K:
++ if (!valid_alu_i(BPF_OP(code), imm)) {
++ emit_mov_i(ctx, MIPS_R_T6, imm);
++ emit_alu_r(ctx, lo(dst), MIPS_R_T6, BPF_OP(code));
++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
++ emit_alu_i(ctx, lo(dst), val, alu);
++ }
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst & src */
++ /* dst = dst | src */
++ /* dst = dst ^ src */
++ /* dst = dst << src */
++ /* dst = dst >> src */
++ /* dst = dst >> src (arithmetic) */
++ /* dst = dst + src */
++ /* dst = dst - src */
++ /* dst = dst * src */
++ /* dst = dst / src */
++ /* dst = dst % src */
++ case BPF_ALU | BPF_AND | BPF_X:
++ case BPF_ALU | BPF_OR | BPF_X:
++ case BPF_ALU | BPF_XOR | BPF_X:
++ case BPF_ALU | BPF_LSH | BPF_X:
++ case BPF_ALU | BPF_RSH | BPF_X:
++ case BPF_ALU | BPF_ARSH | BPF_X:
++ case BPF_ALU | BPF_ADD | BPF_X:
++ case BPF_ALU | BPF_SUB | BPF_X:
++ case BPF_ALU | BPF_MUL | BPF_X:
++ case BPF_ALU | BPF_DIV | BPF_X:
++ case BPF_ALU | BPF_MOD | BPF_X:
++ emit_alu_r(ctx, lo(dst), lo(src), BPF_OP(code));
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = imm (64-bit) */
++ case BPF_ALU64 | BPF_MOV | BPF_K:
++ emit_mov_se_i64(ctx, dst, imm);
++ break;
++ /* dst = src (64-bit) */
++ case BPF_ALU64 | BPF_MOV | BPF_X:
++ emit_mov_r(ctx, lo(dst), lo(src));
++ emit_mov_r(ctx, hi(dst), hi(src));
++ break;
++ /* dst = -dst (64-bit) */
++ case BPF_ALU64 | BPF_NEG:
++ emit_neg_i64(ctx, dst);
++ break;
++ /* dst = dst & imm (64-bit) */
++ case BPF_ALU64 | BPF_AND | BPF_K:
++ emit_alu_i64(ctx, dst, imm, BPF_OP(code));
++ break;
++ /* dst = dst | imm (64-bit) */
++ /* dst = dst ^ imm (64-bit) */
++ /* dst = dst + imm (64-bit) */
++ /* dst = dst - imm (64-bit) */
++ case BPF_ALU64 | BPF_OR | BPF_K:
++ case BPF_ALU64 | BPF_XOR | BPF_K:
++ case BPF_ALU64 | BPF_ADD | BPF_K:
++ case BPF_ALU64 | BPF_SUB | BPF_K:
++ if (imm)
++ emit_alu_i64(ctx, dst, imm, BPF_OP(code));
++ break;
++ /* dst = dst << imm (64-bit) */
++ /* dst = dst >> imm (64-bit) */
++ /* dst = dst >> imm (64-bit, arithmetic) */
++ case BPF_ALU64 | BPF_LSH | BPF_K:
++ case BPF_ALU64 | BPF_RSH | BPF_K:
++ case BPF_ALU64 | BPF_ARSH | BPF_K:
++ if (imm)
++ emit_shift_i64(ctx, dst, imm, BPF_OP(code));
++ break;
++ /* dst = dst * imm (64-bit) */
++ case BPF_ALU64 | BPF_MUL | BPF_K:
++ emit_mul_i64(ctx, dst, imm);
++ break;
++ /* dst = dst / imm (64-bit) */
++ /* dst = dst % imm (64-bit) */
++ case BPF_ALU64 | BPF_DIV | BPF_K:
++ case BPF_ALU64 | BPF_MOD | BPF_K:
++ /*
++ * Sign-extend the immediate value into a temporary register,
++ * and then do the operation on this register.
++ */
++ emit_mov_se_i64(ctx, tmp, imm);
++ emit_divmod_r64(ctx, dst, tmp, BPF_OP(code));
++ break;
++ /* dst = dst & src (64-bit) */
++ /* dst = dst | src (64-bit) */
++ /* dst = dst ^ src (64-bit) */
++ /* dst = dst + src (64-bit) */
++ /* dst = dst - src (64-bit) */
++ case BPF_ALU64 | BPF_AND | BPF_X:
++ case BPF_ALU64 | BPF_OR | BPF_X:
++ case BPF_ALU64 | BPF_XOR | BPF_X:
++ case BPF_ALU64 | BPF_ADD | BPF_X:
++ case BPF_ALU64 | BPF_SUB | BPF_X:
++ emit_alu_r64(ctx, dst, src, BPF_OP(code));
++ break;
++ /* dst = dst << src (64-bit) */
++ /* dst = dst >> src (64-bit) */
++ /* dst = dst >> src (64-bit, arithmetic) */
++ case BPF_ALU64 | BPF_LSH | BPF_X:
++ case BPF_ALU64 | BPF_RSH | BPF_X:
++ case BPF_ALU64 | BPF_ARSH | BPF_X:
++ emit_shift_r64(ctx, dst, lo(src), BPF_OP(code));
++ break;
++ /* dst = dst * src (64-bit) */
++ case BPF_ALU64 | BPF_MUL | BPF_X:
++ emit_mul_r64(ctx, dst, src);
++ break;
++ /* dst = dst / src (64-bit) */
++ /* dst = dst % src (64-bit) */
++ case BPF_ALU64 | BPF_DIV | BPF_X:
++ case BPF_ALU64 | BPF_MOD | BPF_X:
++ emit_divmod_r64(ctx, dst, src, BPF_OP(code));
++ break;
++ /* dst = htole(dst) */
++ /* dst = htobe(dst) */
++ case BPF_ALU | BPF_END | BPF_FROM_LE:
++ case BPF_ALU | BPF_END | BPF_FROM_BE:
++ if (BPF_SRC(code) ==
++#ifdef __BIG_ENDIAN
++ BPF_FROM_LE
++#else
++ BPF_FROM_BE
++#endif
++ )
++ emit_bswap_r64(ctx, dst, imm);
++ else
++ emit_trunc_r64(ctx, dst, imm);
++ break;
++ /* dst = imm64 */
++ case BPF_LD | BPF_IMM | BPF_DW:
++ emit_mov_i(ctx, lo(dst), imm);
++ emit_mov_i(ctx, hi(dst), insn[1].imm);
++ return 1;
++ /* LDX: dst = *(size *)(src + off) */
++ case BPF_LDX | BPF_MEM | BPF_W:
++ case BPF_LDX | BPF_MEM | BPF_H:
++ case BPF_LDX | BPF_MEM | BPF_B:
++ case BPF_LDX | BPF_MEM | BPF_DW:
++ emit_ldx(ctx, dst, lo(src), off, BPF_SIZE(code));
++ break;
++ /* ST: *(size *)(dst + off) = imm */
++ case BPF_ST | BPF_MEM | BPF_W:
++ case BPF_ST | BPF_MEM | BPF_H:
++ case BPF_ST | BPF_MEM | BPF_B:
++ case BPF_ST | BPF_MEM | BPF_DW:
++ switch (BPF_SIZE(code)) {
++ case BPF_DW:
++ /* Sign-extend immediate value into temporary reg */
++ emit_mov_se_i64(ctx, tmp, imm);
++ break;
++ case BPF_W:
++ case BPF_H:
++ case BPF_B:
++ emit_mov_i(ctx, lo(tmp), imm);
++ break;
++ }
++ emit_stx(ctx, lo(dst), tmp, off, BPF_SIZE(code));
++ break;
++ /* STX: *(size *)(dst + off) = src */
++ case BPF_STX | BPF_MEM | BPF_W:
++ case BPF_STX | BPF_MEM | BPF_H:
++ case BPF_STX | BPF_MEM | BPF_B:
++ case BPF_STX | BPF_MEM | BPF_DW:
++ emit_stx(ctx, lo(dst), src, off, BPF_SIZE(code));
++ break;
++ /* Speculation barrier */
++ case BPF_ST | BPF_NOSPEC:
++ break;
++ /* Atomics */
++ case BPF_STX | BPF_XADD | BPF_W:
++ switch (imm) {
++ case BPF_ADD:
++ case BPF_AND:
++ case BPF_OR:
++ case BPF_XOR:
++ if (cpu_has_llsc)
++ emit_atomic_r(ctx, lo(dst), lo(src), off, imm);
++ else /* Non-ll/sc fallback */
++ emit_atomic_r32(ctx, lo(dst), lo(src),
++ off, imm);
++ break;
++ default:
++ goto notyet;
++ }
++ break;
++ /* Atomics (64-bit) */
++ case BPF_STX | BPF_XADD | BPF_DW:
++ switch (imm) {
++ case BPF_ADD:
++ case BPF_AND:
++ case BPF_OR:
++ case BPF_XOR:
++ emit_atomic_r64(ctx, lo(dst), src, off, imm);
++ break;
++ default:
++ goto notyet;
++ }
++ break;
++ /* PC += off if dst == src */
++ /* PC += off if dst != src */
++ /* PC += off if dst & src */
++ /* PC += off if dst > src */
++ /* PC += off if dst >= src */
++ /* PC += off if dst < src */
++ /* PC += off if dst <= src */
++ /* PC += off if dst > src (signed) */
++ /* PC += off if dst >= src (signed) */
++ /* PC += off if dst < src (signed) */
++ /* PC += off if dst <= src (signed) */
++ case BPF_JMP32 | BPF_JEQ | BPF_X:
++ case BPF_JMP32 | BPF_JNE | BPF_X:
++ case BPF_JMP32 | BPF_JSET | BPF_X:
++ case BPF_JMP32 | BPF_JGT | BPF_X:
++ case BPF_JMP32 | BPF_JGE | BPF_X:
++ case BPF_JMP32 | BPF_JLT | BPF_X:
++ case BPF_JMP32 | BPF_JLE | BPF_X:
++ case BPF_JMP32 | BPF_JSGT | BPF_X:
++ case BPF_JMP32 | BPF_JSGE | BPF_X:
++ case BPF_JMP32 | BPF_JSLT | BPF_X:
++ case BPF_JMP32 | BPF_JSLE | BPF_X:
++ if (off == 0)
++ break;
++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel);
++ emit_jmp_r(ctx, lo(dst), lo(src), rel, jmp);
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == imm */
++ /* PC += off if dst != imm */
++ /* PC += off if dst & imm */
++ /* PC += off if dst > imm */
++ /* PC += off if dst >= imm */
++ /* PC += off if dst < imm */
++ /* PC += off if dst <= imm */
++ /* PC += off if dst > imm (signed) */
++ /* PC += off if dst >= imm (signed) */
++ /* PC += off if dst < imm (signed) */
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JMP32 | BPF_JEQ | BPF_K:
++ case BPF_JMP32 | BPF_JNE | BPF_K:
++ case BPF_JMP32 | BPF_JSET | BPF_K:
++ case BPF_JMP32 | BPF_JGT | BPF_K:
++ case BPF_JMP32 | BPF_JGE | BPF_K:
++ case BPF_JMP32 | BPF_JLT | BPF_K:
++ case BPF_JMP32 | BPF_JLE | BPF_K:
++ case BPF_JMP32 | BPF_JSGT | BPF_K:
++ case BPF_JMP32 | BPF_JSGE | BPF_K:
++ case BPF_JMP32 | BPF_JSLT | BPF_K:
++ case BPF_JMP32 | BPF_JSLE | BPF_K:
++ if (off == 0)
++ break;
++ setup_jmp_i(ctx, imm, 32, BPF_OP(code), off, &jmp, &rel);
++ if (valid_jmp_i(jmp, imm)) {
++ emit_jmp_i(ctx, lo(dst), imm, rel, jmp);
++ } else {
++ /* Move large immediate to register */
++ emit_mov_i(ctx, MIPS_R_T6, imm);
++ emit_jmp_r(ctx, lo(dst), MIPS_R_T6, rel, jmp);
++ }
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == src */
++ /* PC += off if dst != src */
++ /* PC += off if dst & src */
++ /* PC += off if dst > src */
++ /* PC += off if dst >= src */
++ /* PC += off if dst < src */
++ /* PC += off if dst <= src */
++ /* PC += off if dst > src (signed) */
++ /* PC += off if dst >= src (signed) */
++ /* PC += off if dst < src (signed) */
++ /* PC += off if dst <= src (signed) */
++ case BPF_JMP | BPF_JEQ | BPF_X:
++ case BPF_JMP | BPF_JNE | BPF_X:
++ case BPF_JMP | BPF_JSET | BPF_X:
++ case BPF_JMP | BPF_JGT | BPF_X:
++ case BPF_JMP | BPF_JGE | BPF_X:
++ case BPF_JMP | BPF_JLT | BPF_X:
++ case BPF_JMP | BPF_JLE | BPF_X:
++ case BPF_JMP | BPF_JSGT | BPF_X:
++ case BPF_JMP | BPF_JSGE | BPF_X:
++ case BPF_JMP | BPF_JSLT | BPF_X:
++ case BPF_JMP | BPF_JSLE | BPF_X:
++ if (off == 0)
++ break;
++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel);
++ emit_jmp_r64(ctx, dst, src, rel, jmp);
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == imm */
++ /* PC += off if dst != imm */
++ /* PC += off if dst & imm */
++ /* PC += off if dst > imm */
++ /* PC += off if dst >= imm */
++ /* PC += off if dst < imm */
++ /* PC += off if dst <= imm */
++ /* PC += off if dst > imm (signed) */
++ /* PC += off if dst >= imm (signed) */
++ /* PC += off if dst < imm (signed) */
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JMP | BPF_JEQ | BPF_K:
++ case BPF_JMP | BPF_JNE | BPF_K:
++ case BPF_JMP | BPF_JSET | BPF_K:
++ case BPF_JMP | BPF_JGT | BPF_K:
++ case BPF_JMP | BPF_JGE | BPF_K:
++ case BPF_JMP | BPF_JLT | BPF_K:
++ case BPF_JMP | BPF_JLE | BPF_K:
++ case BPF_JMP | BPF_JSGT | BPF_K:
++ case BPF_JMP | BPF_JSGE | BPF_K:
++ case BPF_JMP | BPF_JSLT | BPF_K:
++ case BPF_JMP | BPF_JSLE | BPF_K:
++ if (off == 0)
++ break;
++ setup_jmp_i(ctx, imm, 64, BPF_OP(code), off, &jmp, &rel);
++ emit_jmp_i64(ctx, dst, imm, rel, jmp);
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off */
++ case BPF_JMP | BPF_JA:
++ if (off == 0)
++ break;
++ if (emit_ja(ctx, off) < 0)
++ goto toofar;
++ break;
++ /* Tail call */
++ case BPF_JMP | BPF_TAIL_CALL:
++ if (emit_tail_call(ctx) < 0)
++ goto invalid;
++ break;
++ /* Function call */
++ case BPF_JMP | BPF_CALL:
++ if (emit_call(ctx, insn) < 0)
++ goto invalid;
++ break;
++ /* Function return */
++ case BPF_JMP | BPF_EXIT:
++ /*
++ * Optimization: when last instruction is EXIT
++ * simply continue to epilogue.
++ */
++ if (ctx->bpf_index == ctx->program->len - 1)
++ break;
++ if (emit_exit(ctx) < 0)
++ goto toofar;
++ break;
++
++ default:
++invalid:
++ pr_err_once("unknown opcode %02x\n", code);
++ return -EINVAL;
++notyet:
++ pr_info_once("*** NOT YET: opcode %02x ***\n", code);
++ return -EFAULT;
++toofar:
++ pr_info_once("*** TOO FAR: jump at %u opcode %02x ***\n",
++ ctx->bpf_index, code);
++ return -E2BIG;
++ }
++ return 0;
++}
--- /dev/null
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:05 +0200
+Subject: [PATCH] mips: bpf: Add new eBPF JIT for 64-bit MIPS
+
+This is an implementation on of an eBPF JIT for 64-bit MIPS III-V and
+MIPS64r1-r6. It uses the same framework introduced by the 32-bit JIT.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+---
+ create mode 100644 arch/mips/net/bpf_jit_comp64.c
+
+--- /dev/null
++++ b/arch/mips/net/bpf_jit_comp64.c
+@@ -0,0 +1,991 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Just-In-Time compiler for eBPF bytecode on MIPS.
++ * Implementation of JIT functions for 64-bit CPUs.
++ *
++ * Copyright (c) 2021 Anyfi Networks AB.
++ * Author: Johan Almbladh <johan.almbladh@gmail.com>
++ *
++ * Based on code and ideas from
++ * Copyright (c) 2017 Cavium, Inc.
++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com>
++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
++ */
++
++#include <linux/errno.h>
++#include <linux/filter.h>
++#include <linux/bpf.h>
++#include <asm/cpu-features.h>
++#include <asm/isa-rev.h>
++#include <asm/uasm.h>
++
++#include "bpf_jit_comp.h"
++
++/* MIPS t0-t3 are not available in the n64 ABI */
++#undef MIPS_R_T0
++#undef MIPS_R_T1
++#undef MIPS_R_T2
++#undef MIPS_R_T3
++
++/* Stack is 16-byte aligned in n64 ABI */
++#define MIPS_STACK_ALIGNMENT 16
++
++/* Extra 64-bit eBPF registers used by JIT */
++#define JIT_REG_TC (MAX_BPF_JIT_REG + 0)
++#define JIT_REG_ZX (MAX_BPF_JIT_REG + 1)
++
++/* Number of prologue bytes to skip when doing a tail call */
++#define JIT_TCALL_SKIP 4
++
++/* Callee-saved CPU registers that the JIT must preserve */
++#define JIT_CALLEE_REGS \
++ (BIT(MIPS_R_S0) | \
++ BIT(MIPS_R_S1) | \
++ BIT(MIPS_R_S2) | \
++ BIT(MIPS_R_S3) | \
++ BIT(MIPS_R_S4) | \
++ BIT(MIPS_R_S5) | \
++ BIT(MIPS_R_S6) | \
++ BIT(MIPS_R_S7) | \
++ BIT(MIPS_R_GP) | \
++ BIT(MIPS_R_FP) | \
++ BIT(MIPS_R_RA))
++
++/* Caller-saved CPU registers available for JIT use */
++#define JIT_CALLER_REGS \
++ (BIT(MIPS_R_A5) | \
++ BIT(MIPS_R_A6) | \
++ BIT(MIPS_R_A7))
++/*
++ * Mapping of 64-bit eBPF registers to 64-bit native MIPS registers.
++ * MIPS registers t4 - t7 may be used by the JIT as temporary registers.
++ * MIPS registers t8 - t9 are reserved for single-register common functions.
++ */
++static const u8 bpf2mips64[] = {
++ /* Return value from in-kernel function, and exit value from eBPF */
++ [BPF_REG_0] = MIPS_R_V0,
++ /* Arguments from eBPF program to in-kernel function */
++ [BPF_REG_1] = MIPS_R_A0,
++ [BPF_REG_2] = MIPS_R_A1,
++ [BPF_REG_3] = MIPS_R_A2,
++ [BPF_REG_4] = MIPS_R_A3,
++ [BPF_REG_5] = MIPS_R_A4,
++ /* Callee-saved registers that in-kernel function will preserve */
++ [BPF_REG_6] = MIPS_R_S0,
++ [BPF_REG_7] = MIPS_R_S1,
++ [BPF_REG_8] = MIPS_R_S2,
++ [BPF_REG_9] = MIPS_R_S3,
++ /* Read-only frame pointer to access the eBPF stack */
++ [BPF_REG_FP] = MIPS_R_FP,
++ /* Temporary register for blinding constants */
++ [BPF_REG_AX] = MIPS_R_AT,
++ /* Tail call count register, caller-saved */
++ [JIT_REG_TC] = MIPS_R_A5,
++ /* Constant for register zero-extension */
++ [JIT_REG_ZX] = MIPS_R_V1,
++};
++
++/*
++ * MIPS 32-bit operations on 64-bit registers generate a sign-extended
++ * result. However, the eBPF ISA mandates zero-extension, so we rely on the
++ * verifier to add that for us (emit_zext_ver). In addition, ALU arithmetic
++ * operations, right shift and byte swap require properly sign-extended
++ * operands or the result is unpredictable. We emit explicit sign-extensions
++ * in those cases.
++ */
++
++/* Sign extension */
++static void emit_sext(struct jit_context *ctx, u8 dst, u8 src)
++{
++ emit(ctx, sll, dst, src, 0);
++ clobber_reg(ctx, dst);
++}
++
++/* Zero extension */
++static void emit_zext(struct jit_context *ctx, u8 dst)
++{
++ if (cpu_has_mips64r2 || cpu_has_mips64r6) {
++ emit(ctx, dinsu, dst, MIPS_R_ZERO, 32, 32);
++ } else {
++ emit(ctx, and, dst, dst, bpf2mips64[JIT_REG_ZX]);
++ access_reg(ctx, JIT_REG_ZX); /* We need the ZX register */
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Zero extension, if verifier does not do it for us */
++static void emit_zext_ver(struct jit_context *ctx, u8 dst)
++{
++ if (!ctx->program->aux->verifier_zext)
++ emit_zext(ctx, dst);
++}
++
++/* dst = imm (64-bit) */
++static void emit_mov_i64(struct jit_context *ctx, u8 dst, u64 imm64)
++{
++ if (imm64 >= 0xffffffffffff8000ULL || imm64 < 0x8000ULL) {
++ emit(ctx, daddiu, dst, MIPS_R_ZERO, (s16)imm64);
++ } else if (imm64 >= 0xffffffff80000000ULL ||
++ (imm64 < 0x80000000 && imm64 > 0xffff)) {
++ emit(ctx, lui, dst, (s16)(imm64 >> 16));
++ emit(ctx, ori, dst, dst, (u16)imm64 & 0xffff);
++ } else {
++ u8 acc = MIPS_R_ZERO;
++ int k;
++
++ for (k = 0; k < 4; k++) {
++ u16 half = imm64 >> (48 - 16 * k);
++
++ if (acc == dst)
++ emit(ctx, dsll, dst, dst, 16);
++
++ if (half) {
++ emit(ctx, ori, dst, acc, half);
++ acc = dst;
++ }
++ }
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* ALU immediate operation (64-bit) */
++static void emit_alu_i64(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = dst | imm */
++ case BPF_OR:
++ emit(ctx, ori, dst, dst, (u16)imm);
++ break;
++ /* dst = dst ^ imm */
++ case BPF_XOR:
++ emit(ctx, xori, dst, dst, (u16)imm);
++ break;
++ /* dst = -dst */
++ case BPF_NEG:
++ emit(ctx, dsubu, dst, MIPS_R_ZERO, dst);
++ break;
++ /* dst = dst << imm */
++ case BPF_LSH:
++ emit(ctx, dsll_safe, dst, dst, imm);
++ break;
++ /* dst = dst >> imm */
++ case BPF_RSH:
++ emit(ctx, dsrl_safe, dst, dst, imm);
++ break;
++ /* dst = dst >> imm (arithmetic) */
++ case BPF_ARSH:
++ emit(ctx, dsra_safe, dst, dst, imm);
++ break;
++ /* dst = dst + imm */
++ case BPF_ADD:
++ emit(ctx, daddiu, dst, dst, imm);
++ break;
++ /* dst = dst - imm */
++ case BPF_SUB:
++ emit(ctx, daddiu, dst, dst, -imm);
++ break;
++ default:
++ /* Width-generic operations */
++ emit_alu_i(ctx, dst, imm, op);
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* ALU register operation (64-bit) */
++static void emit_alu_r64(struct jit_context *ctx, u8 dst, u8 src, u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = dst << src */
++ case BPF_LSH:
++ emit(ctx, dsllv, dst, dst, src);
++ break;
++ /* dst = dst >> src */
++ case BPF_RSH:
++ emit(ctx, dsrlv, dst, dst, src);
++ break;
++ /* dst = dst >> src (arithmetic) */
++ case BPF_ARSH:
++ emit(ctx, dsrav, dst, dst, src);
++ break;
++ /* dst = dst + src */
++ case BPF_ADD:
++ emit(ctx, daddu, dst, dst, src);
++ break;
++ /* dst = dst - src */
++ case BPF_SUB:
++ emit(ctx, dsubu, dst, dst, src);
++ break;
++ /* dst = dst * src */
++ case BPF_MUL:
++ if (cpu_has_mips64r6) {
++ emit(ctx, dmulu, dst, dst, src);
++ } else {
++ emit(ctx, dmultu, dst, src);
++ emit(ctx, mflo, dst);
++ }
++ break;
++ /* dst = dst / src */
++ case BPF_DIV:
++ if (cpu_has_mips64r6) {
++ emit(ctx, ddivu_r6, dst, dst, src);
++ } else {
++ emit(ctx, ddivu, dst, src);
++ emit(ctx, mflo, dst);
++ }
++ break;
++ /* dst = dst % src */
++ case BPF_MOD:
++ if (cpu_has_mips64r6) {
++ emit(ctx, dmodu, dst, dst, src);
++ } else {
++ emit(ctx, ddivu, dst, src);
++ emit(ctx, mfhi, dst);
++ }
++ break;
++ default:
++ /* Width-generic operations */
++ emit_alu_r(ctx, dst, src, op);
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Swap sub words in a register double word */
++static void emit_swap_r64(struct jit_context *ctx, u8 dst, u8 mask, u32 bits)
++{
++ u8 tmp = MIPS_R_T9;
++
++ emit(ctx, and, tmp, dst, mask); /* tmp = dst & mask */
++ emit(ctx, dsll, tmp, tmp, bits); /* tmp = tmp << bits */
++ emit(ctx, dsrl, dst, dst, bits); /* dst = dst >> bits */
++ emit(ctx, and, dst, dst, mask); /* dst = dst & mask */
++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */
++}
++
++/* Swap bytes and truncate a register double word, word or half word */
++static void emit_bswap_r64(struct jit_context *ctx, u8 dst, u32 width)
++{
++ switch (width) {
++ /* Swap bytes in a double word */
++ case 64:
++ if (cpu_has_mips64r2 || cpu_has_mips64r6) {
++ emit(ctx, dsbh, dst, dst);
++ emit(ctx, dshd, dst, dst);
++ } else {
++ u8 t1 = MIPS_R_T6;
++ u8 t2 = MIPS_R_T7;
++
++ emit(ctx, dsll32, t2, dst, 0); /* t2 = dst << 32 */
++ emit(ctx, dsrl32, dst, dst, 0); /* dst = dst >> 32 */
++ emit(ctx, or, dst, dst, t2); /* dst = dst | t2 */
++
++ emit(ctx, ori, t2, MIPS_R_ZERO, 0xffff);
++ emit(ctx, dsll32, t1, t2, 0); /* t1 = t2 << 32 */
++ emit(ctx, or, t1, t1, t2); /* t1 = t1 | t2 */
++ emit_swap_r64(ctx, dst, t1, 16);/* dst = swap16(dst) */
++
++ emit(ctx, lui, t2, 0xff); /* t2 = 0x00ff0000 */
++ emit(ctx, ori, t2, t2, 0xff); /* t2 = t2 | 0x00ff */
++ emit(ctx, dsll32, t1, t2, 0); /* t1 = t2 << 32 */
++ emit(ctx, or, t1, t1, t2); /* t1 = t1 | t2 */
++ emit_swap_r64(ctx, dst, t1, 8); /* dst = swap8(dst) */
++ }
++ break;
++ /* Swap bytes in a half word */
++ /* Swap bytes in a word */
++ case 32:
++ case 16:
++ emit_sext(ctx, dst, dst);
++ emit_bswap_r(ctx, dst, width);
++ if (cpu_has_mips64r2 || cpu_has_mips64r6)
++ emit_zext(ctx, dst);
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Truncate a register double word, word or half word */
++static void emit_trunc_r64(struct jit_context *ctx, u8 dst, u32 width)
++{
++ switch (width) {
++ case 64:
++ break;
++ /* Zero-extend a word */
++ case 32:
++ emit_zext(ctx, dst);
++ break;
++ /* Zero-extend a half word */
++ case 16:
++ emit(ctx, andi, dst, dst, 0xffff);
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Load operation: dst = *(size*)(src + off) */
++static void emit_ldx(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 size)
++{
++ switch (size) {
++ /* Load a byte */
++ case BPF_B:
++ emit(ctx, lbu, dst, off, src);
++ break;
++ /* Load a half word */
++ case BPF_H:
++ emit(ctx, lhu, dst, off, src);
++ break;
++ /* Load a word */
++ case BPF_W:
++ emit(ctx, lwu, dst, off, src);
++ break;
++ /* Load a double word */
++ case BPF_DW:
++ emit(ctx, ld, dst, off, src);
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Store operation: *(size *)(dst + off) = src */
++static void emit_stx(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 size)
++{
++ switch (size) {
++ /* Store a byte */
++ case BPF_B:
++ emit(ctx, sb, src, off, dst);
++ break;
++ /* Store a half word */
++ case BPF_H:
++ emit(ctx, sh, src, off, dst);
++ break;
++ /* Store a word */
++ case BPF_W:
++ emit(ctx, sw, src, off, dst);
++ break;
++ /* Store a double word */
++ case BPF_DW:
++ emit(ctx, sd, src, off, dst);
++ break;
++ }
++}
++
++/* Atomic read-modify-write */
++static void emit_atomic_r64(struct jit_context *ctx,
++ u8 dst, u8 src, s16 off, u8 code)
++{
++ u8 t1 = MIPS_R_T6;
++ u8 t2 = MIPS_R_T7;
++
++ emit(ctx, lld, t1, off, dst);
++ switch (code) {
++ case BPF_ADD:
++ emit(ctx, daddu, t2, t1, src);
++ break;
++ case BPF_AND:
++ emit(ctx, and, t2, t1, src);
++ break;
++ case BPF_OR:
++ emit(ctx, or, t2, t1, src);
++ break;
++ case BPF_XOR:
++ emit(ctx, xor, t2, t1, src);
++ break;
++ }
++ emit(ctx, scd, t2, off, dst);
++ emit(ctx, beqz, t2, -16);
++ emit(ctx, nop); /* Delay slot */
++}
++
++/* Function call */
++static int emit_call(struct jit_context *ctx, const struct bpf_insn *insn)
++{
++ u8 zx = bpf2mips64[JIT_REG_ZX];
++ u8 tmp = MIPS_R_T6;
++ bool fixed;
++ u64 addr;
++
++ /* Decode the call address */
++ if (bpf_jit_get_func_addr(ctx->program, insn, false,
++ &addr, &fixed) < 0)
++ return -1;
++ if (!fixed)
++ return -1;
++
++ /* Push caller-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, 0, 0);
++
++ /* Emit function call */
++ emit_mov_i64(ctx, tmp, addr);
++ emit(ctx, jalr, MIPS_R_RA, tmp);
++ emit(ctx, nop); /* Delay slot */
++
++ /* Restore caller-saved registers */
++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, 0, 0);
++
++ /* Re-initialize the JIT zero-extension register if accessed */
++ if (ctx->accessed & BIT(JIT_REG_ZX)) {
++ emit(ctx, daddiu, zx, MIPS_R_ZERO, -1);
++ emit(ctx, dsrl32, zx, zx, 0);
++ }
++
++ clobber_reg(ctx, MIPS_R_RA);
++ clobber_reg(ctx, MIPS_R_V0);
++ clobber_reg(ctx, MIPS_R_V1);
++ return 0;
++}
++
++/* Function tail call */
++static int emit_tail_call(struct jit_context *ctx)
++{
++ u8 ary = bpf2mips64[BPF_REG_2];
++ u8 ind = bpf2mips64[BPF_REG_3];
++ u8 tcc = bpf2mips64[JIT_REG_TC];
++ u8 tmp = MIPS_R_T6;
++ int off;
++
++ /*
++ * Tail call:
++ * eBPF R1 - function argument (context ptr), passed in a0-a1
++ * eBPF R2 - ptr to object with array of function entry points
++ * eBPF R3 - array index of function to be called
++ */
++
++ /* if (ind >= ary->map.max_entries) goto out */
++ off = offsetof(struct bpf_array, map.max_entries);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, lwu, tmp, off, ary); /* tmp = ary->map.max_entrs*/
++ emit(ctx, sltu, tmp, ind, tmp); /* tmp = ind < t1 */
++ emit(ctx, beqz, tmp, get_offset(ctx, 1)); /* PC += off(1) if tmp == 0*/
++
++ /* if (--TCC < 0) goto out */
++ emit(ctx, daddiu, tcc, tcc, -1); /* tcc-- (delay slot) */
++ emit(ctx, bltz, tcc, get_offset(ctx, 1)); /* PC += off(1) if tcc < 0 */
++ /* (next insn delay slot) */
++ /* prog = ary->ptrs[ind] */
++ off = offsetof(struct bpf_array, ptrs);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, dsll, tmp, ind, 3); /* tmp = ind << 3 */
++ emit(ctx, daddu, tmp, tmp, ary); /* tmp += ary */
++ emit(ctx, ld, tmp, off, tmp); /* tmp = *(tmp + off) */
++
++ /* if (prog == 0) goto out */
++ emit(ctx, beqz, tmp, get_offset(ctx, 1)); /* PC += off(1) if tmp == 0*/
++ emit(ctx, nop); /* Delay slot */
++
++ /* func = prog->bpf_func + 8 (prologue skip offset) */
++ off = offsetof(struct bpf_prog, bpf_func);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, ld, tmp, off, tmp); /* tmp = *(tmp + off) */
++ emit(ctx, daddiu, tmp, tmp, JIT_TCALL_SKIP); /* tmp += skip (4) */
++
++ /* goto func */
++ build_epilogue(ctx, tmp);
++ access_reg(ctx, JIT_REG_TC);
++ return 0;
++}
++
++/*
++ * Stack frame layout for a JITed program (stack grows down).
++ *
++ * Higher address : Previous stack frame :
++ * +===========================+ <--- MIPS sp before call
++ * | Callee-saved registers, |
++ * | including RA and FP |
++ * +---------------------------+ <--- eBPF FP (MIPS fp)
++ * | Local eBPF variables |
++ * | allocated by program |
++ * +---------------------------+
++ * | Reserved for caller-saved |
++ * | registers |
++ * Lower address +===========================+ <--- MIPS sp
++ */
++
++/* Build program prologue to set up the stack and registers */
++void build_prologue(struct jit_context *ctx)
++{
++ u8 fp = bpf2mips64[BPF_REG_FP];
++ u8 tc = bpf2mips64[JIT_REG_TC];
++ u8 zx = bpf2mips64[JIT_REG_ZX];
++ int stack, saved, locals, reserved;
++
++ /*
++ * The first instruction initializes the tail call count register.
++ * On a tail call, the calling function jumps into the prologue
++ * after this instruction.
++ */
++ emit(ctx, addiu, tc, MIPS_R_ZERO, min(MAX_TAIL_CALL_CNT + 1, 0xffff));
++
++ /* === Entry-point for tail calls === */
++
++ /*
++ * If the eBPF frame pointer and tail call count registers were
++ * accessed they must be preserved. Mark them as clobbered here
++ * to save and restore them on the stack as needed.
++ */
++ if (ctx->accessed & BIT(BPF_REG_FP))
++ clobber_reg(ctx, fp);
++ if (ctx->accessed & BIT(JIT_REG_TC))
++ clobber_reg(ctx, tc);
++ if (ctx->accessed & BIT(JIT_REG_ZX))
++ clobber_reg(ctx, zx);
++
++ /* Compute the stack space needed for callee-saved registers */
++ saved = hweight32(ctx->clobbered & JIT_CALLEE_REGS) * sizeof(u64);
++ saved = ALIGN(saved, MIPS_STACK_ALIGNMENT);
++
++ /* Stack space used by eBPF program local data */
++ locals = ALIGN(ctx->program->aux->stack_depth, MIPS_STACK_ALIGNMENT);
++
++ /*
++ * If we are emitting function calls, reserve extra stack space for
++ * caller-saved registers needed by the JIT. The required space is
++ * computed automatically during resource usage discovery (pass 1).
++ */
++ reserved = ctx->stack_used;
++
++ /* Allocate the stack frame */
++ stack = ALIGN(saved + locals + reserved, MIPS_STACK_ALIGNMENT);
++ if (stack)
++ emit(ctx, daddiu, MIPS_R_SP, MIPS_R_SP, -stack);
++
++ /* Store callee-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0, stack - saved);
++
++ /* Initialize the eBPF frame pointer if accessed */
++ if (ctx->accessed & BIT(BPF_REG_FP))
++ emit(ctx, daddiu, fp, MIPS_R_SP, stack - saved);
++
++ /* Initialize the ePF JIT zero-extension register if accessed */
++ if (ctx->accessed & BIT(JIT_REG_ZX)) {
++ emit(ctx, daddiu, zx, MIPS_R_ZERO, -1);
++ emit(ctx, dsrl32, zx, zx, 0);
++ }
++
++ ctx->saved_size = saved;
++ ctx->stack_size = stack;
++}
++
++/* Build the program epilogue to restore the stack and registers */
++void build_epilogue(struct jit_context *ctx, int dest_reg)
++{
++ /* Restore callee-saved registers from stack */
++ pop_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0,
++ ctx->stack_size - ctx->saved_size);
++
++ /* Release the stack frame */
++ if (ctx->stack_size)
++ emit(ctx, daddiu, MIPS_R_SP, MIPS_R_SP, ctx->stack_size);
++
++ /* Jump to return address and sign-extend the 32-bit return value */
++ emit(ctx, jr, dest_reg);
++ emit(ctx, sll, MIPS_R_V0, MIPS_R_V0, 0); /* Delay slot */
++}
++
++/* Build one eBPF instruction */
++int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
++{
++ u8 dst = bpf2mips64[insn->dst_reg];
++ u8 src = bpf2mips64[insn->src_reg];
++ u8 code = insn->code;
++ s16 off = insn->off;
++ s32 imm = insn->imm;
++ s32 val, rel;
++ u8 alu, jmp;
++
++ switch (code) {
++ /* ALU operations */
++ /* dst = imm */
++ case BPF_ALU | BPF_MOV | BPF_K:
++ emit_mov_i(ctx, dst, imm);
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = src */
++ case BPF_ALU | BPF_MOV | BPF_X:
++ if (imm == 1) {
++ /* Special mov32 for zext */
++ emit_zext(ctx, dst);
++ } else {
++ emit_mov_r(ctx, dst, src);
++ emit_zext_ver(ctx, dst);
++ }
++ break;
++ /* dst = -dst */
++ case BPF_ALU | BPF_NEG:
++ emit_sext(ctx, dst, dst);
++ emit_alu_i(ctx, dst, 0, BPF_NEG);
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst & imm */
++ /* dst = dst | imm */
++ /* dst = dst ^ imm */
++ /* dst = dst << imm */
++ case BPF_ALU | BPF_OR | BPF_K:
++ case BPF_ALU | BPF_AND | BPF_K:
++ case BPF_ALU | BPF_XOR | BPF_K:
++ case BPF_ALU | BPF_LSH | BPF_K:
++ if (!valid_alu_i(BPF_OP(code), imm)) {
++ emit_mov_i(ctx, MIPS_R_T4, imm);
++ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
++ emit_alu_i(ctx, dst, val, alu);
++ }
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst >> imm */
++ /* dst = dst >> imm (arithmetic) */
++ /* dst = dst + imm */
++ /* dst = dst - imm */
++ /* dst = dst * imm */
++ /* dst = dst / imm */
++ /* dst = dst % imm */
++ case BPF_ALU | BPF_RSH | BPF_K:
++ case BPF_ALU | BPF_ARSH | BPF_K:
++ case BPF_ALU | BPF_ADD | BPF_K:
++ case BPF_ALU | BPF_SUB | BPF_K:
++ case BPF_ALU | BPF_MUL | BPF_K:
++ case BPF_ALU | BPF_DIV | BPF_K:
++ case BPF_ALU | BPF_MOD | BPF_K:
++ if (!valid_alu_i(BPF_OP(code), imm)) {
++ emit_sext(ctx, dst, dst);
++ emit_mov_i(ctx, MIPS_R_T4, imm);
++ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
++ emit_sext(ctx, dst, dst);
++ emit_alu_i(ctx, dst, val, alu);
++ }
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst & src */
++ /* dst = dst | src */
++ /* dst = dst ^ src */
++ /* dst = dst << src */
++ case BPF_ALU | BPF_AND | BPF_X:
++ case BPF_ALU | BPF_OR | BPF_X:
++ case BPF_ALU | BPF_XOR | BPF_X:
++ case BPF_ALU | BPF_LSH | BPF_X:
++ emit_alu_r(ctx, dst, src, BPF_OP(code));
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst >> src */
++ /* dst = dst >> src (arithmetic) */
++ /* dst = dst + src */
++ /* dst = dst - src */
++ /* dst = dst * src */
++ /* dst = dst / src */
++ /* dst = dst % src */
++ case BPF_ALU | BPF_RSH | BPF_X:
++ case BPF_ALU | BPF_ARSH | BPF_X:
++ case BPF_ALU | BPF_ADD | BPF_X:
++ case BPF_ALU | BPF_SUB | BPF_X:
++ case BPF_ALU | BPF_MUL | BPF_X:
++ case BPF_ALU | BPF_DIV | BPF_X:
++ case BPF_ALU | BPF_MOD | BPF_X:
++ emit_sext(ctx, dst, dst);
++ emit_sext(ctx, MIPS_R_T4, src);
++ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = imm (64-bit) */
++ case BPF_ALU64 | BPF_MOV | BPF_K:
++ emit_mov_i(ctx, dst, imm);
++ break;
++ /* dst = src (64-bit) */
++ case BPF_ALU64 | BPF_MOV | BPF_X:
++ emit_mov_r(ctx, dst, src);
++ break;
++ /* dst = -dst (64-bit) */
++ case BPF_ALU64 | BPF_NEG:
++ emit_alu_i64(ctx, dst, 0, BPF_NEG);
++ break;
++ /* dst = dst & imm (64-bit) */
++ /* dst = dst | imm (64-bit) */
++ /* dst = dst ^ imm (64-bit) */
++ /* dst = dst << imm (64-bit) */
++ /* dst = dst >> imm (64-bit) */
++ /* dst = dst >> imm ((64-bit, arithmetic) */
++ /* dst = dst + imm (64-bit) */
++ /* dst = dst - imm (64-bit) */
++ /* dst = dst * imm (64-bit) */
++ /* dst = dst / imm (64-bit) */
++ /* dst = dst % imm (64-bit) */
++ case BPF_ALU64 | BPF_AND | BPF_K:
++ case BPF_ALU64 | BPF_OR | BPF_K:
++ case BPF_ALU64 | BPF_XOR | BPF_K:
++ case BPF_ALU64 | BPF_LSH | BPF_K:
++ case BPF_ALU64 | BPF_RSH | BPF_K:
++ case BPF_ALU64 | BPF_ARSH | BPF_K:
++ case BPF_ALU64 | BPF_ADD | BPF_K:
++ case BPF_ALU64 | BPF_SUB | BPF_K:
++ case BPF_ALU64 | BPF_MUL | BPF_K:
++ case BPF_ALU64 | BPF_DIV | BPF_K:
++ case BPF_ALU64 | BPF_MOD | BPF_K:
++ if (!valid_alu_i(BPF_OP(code), imm)) {
++ emit_mov_i(ctx, MIPS_R_T4, imm);
++ emit_alu_r64(ctx, dst, MIPS_R_T4, BPF_OP(code));
++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
++ emit_alu_i64(ctx, dst, val, alu);
++ }
++ break;
++ /* dst = dst & src (64-bit) */
++ /* dst = dst | src (64-bit) */
++ /* dst = dst ^ src (64-bit) */
++ /* dst = dst << src (64-bit) */
++ /* dst = dst >> src (64-bit) */
++ /* dst = dst >> src (64-bit, arithmetic) */
++ /* dst = dst + src (64-bit) */
++ /* dst = dst - src (64-bit) */
++ /* dst = dst * src (64-bit) */
++ /* dst = dst / src (64-bit) */
++ /* dst = dst % src (64-bit) */
++ case BPF_ALU64 | BPF_AND | BPF_X:
++ case BPF_ALU64 | BPF_OR | BPF_X:
++ case BPF_ALU64 | BPF_XOR | BPF_X:
++ case BPF_ALU64 | BPF_LSH | BPF_X:
++ case BPF_ALU64 | BPF_RSH | BPF_X:
++ case BPF_ALU64 | BPF_ARSH | BPF_X:
++ case BPF_ALU64 | BPF_ADD | BPF_X:
++ case BPF_ALU64 | BPF_SUB | BPF_X:
++ case BPF_ALU64 | BPF_MUL | BPF_X:
++ case BPF_ALU64 | BPF_DIV | BPF_X:
++ case BPF_ALU64 | BPF_MOD | BPF_X:
++ emit_alu_r64(ctx, dst, src, BPF_OP(code));
++ break;
++ /* dst = htole(dst) */
++ /* dst = htobe(dst) */
++ case BPF_ALU | BPF_END | BPF_FROM_LE:
++ case BPF_ALU | BPF_END | BPF_FROM_BE:
++ if (BPF_SRC(code) ==
++#ifdef __BIG_ENDIAN
++ BPF_FROM_LE
++#else
++ BPF_FROM_BE
++#endif
++ )
++ emit_bswap_r64(ctx, dst, imm);
++ else
++ emit_trunc_r64(ctx, dst, imm);
++ break;
++ /* dst = imm64 */
++ case BPF_LD | BPF_IMM | BPF_DW:
++ emit_mov_i64(ctx, dst, (u32)imm | ((u64)insn[1].imm << 32));
++ return 1;
++ /* LDX: dst = *(size *)(src + off) */
++ case BPF_LDX | BPF_MEM | BPF_W:
++ case BPF_LDX | BPF_MEM | BPF_H:
++ case BPF_LDX | BPF_MEM | BPF_B:
++ case BPF_LDX | BPF_MEM | BPF_DW:
++ emit_ldx(ctx, dst, src, off, BPF_SIZE(code));
++ break;
++ /* ST: *(size *)(dst + off) = imm */
++ case BPF_ST | BPF_MEM | BPF_W:
++ case BPF_ST | BPF_MEM | BPF_H:
++ case BPF_ST | BPF_MEM | BPF_B:
++ case BPF_ST | BPF_MEM | BPF_DW:
++ emit_mov_i(ctx, MIPS_R_T4, imm);
++ emit_stx(ctx, dst, MIPS_R_T4, off, BPF_SIZE(code));
++ break;
++ /* STX: *(size *)(dst + off) = src */
++ case BPF_STX | BPF_MEM | BPF_W:
++ case BPF_STX | BPF_MEM | BPF_H:
++ case BPF_STX | BPF_MEM | BPF_B:
++ case BPF_STX | BPF_MEM | BPF_DW:
++ emit_stx(ctx, dst, src, off, BPF_SIZE(code));
++ break;
++ /* Speculation barrier */
++ case BPF_ST | BPF_NOSPEC:
++ break;
++ /* Atomics */
++ case BPF_STX | BPF_XADD | BPF_W:
++ case BPF_STX | BPF_XADD | BPF_DW:
++ switch (imm) {
++ case BPF_ADD:
++ case BPF_AND:
++ case BPF_OR:
++ case BPF_XOR:
++ if (BPF_SIZE(code) == BPF_DW) {
++ emit_atomic_r64(ctx, dst, src, off, imm);
++ } else { /* 32-bit, no fetch */
++ emit_sext(ctx, MIPS_R_T4, src);
++ emit_atomic_r(ctx, dst, MIPS_R_T4, off, imm);
++ }
++ break;
++ default:
++ goto notyet;
++ }
++ break;
++ /* PC += off if dst == src */
++ /* PC += off if dst != src */
++ /* PC += off if dst & src */
++ /* PC += off if dst > src */
++ /* PC += off if dst >= src */
++ /* PC += off if dst < src */
++ /* PC += off if dst <= src */
++ /* PC += off if dst > src (signed) */
++ /* PC += off if dst >= src (signed) */
++ /* PC += off if dst < src (signed) */
++ /* PC += off if dst <= src (signed) */
++ case BPF_JMP32 | BPF_JEQ | BPF_X:
++ case BPF_JMP32 | BPF_JNE | BPF_X:
++ case BPF_JMP32 | BPF_JSET | BPF_X:
++ case BPF_JMP32 | BPF_JGT | BPF_X:
++ case BPF_JMP32 | BPF_JGE | BPF_X:
++ case BPF_JMP32 | BPF_JLT | BPF_X:
++ case BPF_JMP32 | BPF_JLE | BPF_X:
++ case BPF_JMP32 | BPF_JSGT | BPF_X:
++ case BPF_JMP32 | BPF_JSGE | BPF_X:
++ case BPF_JMP32 | BPF_JSLT | BPF_X:
++ case BPF_JMP32 | BPF_JSLE | BPF_X:
++ if (off == 0)
++ break;
++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel);
++ emit_sext(ctx, MIPS_R_T4, dst); /* Sign-extended dst */
++ emit_sext(ctx, MIPS_R_T5, src); /* Sign-extended src */
++ emit_jmp_r(ctx, MIPS_R_T4, MIPS_R_T5, rel, jmp);
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == imm */
++ /* PC += off if dst != imm */
++ /* PC += off if dst & imm */
++ /* PC += off if dst > imm */
++ /* PC += off if dst >= imm */
++ /* PC += off if dst < imm */
++ /* PC += off if dst <= imm */
++ /* PC += off if dst > imm (signed) */
++ /* PC += off if dst >= imm (signed) */
++ /* PC += off if dst < imm (signed) */
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JMP32 | BPF_JEQ | BPF_K:
++ case BPF_JMP32 | BPF_JNE | BPF_K:
++ case BPF_JMP32 | BPF_JSET | BPF_K:
++ case BPF_JMP32 | BPF_JGT | BPF_K:
++ case BPF_JMP32 | BPF_JGE | BPF_K:
++ case BPF_JMP32 | BPF_JLT | BPF_K:
++ case BPF_JMP32 | BPF_JLE | BPF_K:
++ case BPF_JMP32 | BPF_JSGT | BPF_K:
++ case BPF_JMP32 | BPF_JSGE | BPF_K:
++ case BPF_JMP32 | BPF_JSLT | BPF_K:
++ case BPF_JMP32 | BPF_JSLE | BPF_K:
++ if (off == 0)
++ break;
++ setup_jmp_i(ctx, imm, 32, BPF_OP(code), off, &jmp, &rel);
++ emit_sext(ctx, MIPS_R_T4, dst); /* Sign-extended dst */
++ if (valid_jmp_i(jmp, imm)) {
++ emit_jmp_i(ctx, MIPS_R_T4, imm, rel, jmp);
++ } else {
++ /* Move large immediate to register, sign-extended */
++ emit_mov_i(ctx, MIPS_R_T5, imm);
++ emit_jmp_r(ctx, MIPS_R_T4, MIPS_R_T5, rel, jmp);
++ }
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == src */
++ /* PC += off if dst != src */
++ /* PC += off if dst & src */
++ /* PC += off if dst > src */
++ /* PC += off if dst >= src */
++ /* PC += off if dst < src */
++ /* PC += off if dst <= src */
++ /* PC += off if dst > src (signed) */
++ /* PC += off if dst >= src (signed) */
++ /* PC += off if dst < src (signed) */
++ /* PC += off if dst <= src (signed) */
++ case BPF_JMP | BPF_JEQ | BPF_X:
++ case BPF_JMP | BPF_JNE | BPF_X:
++ case BPF_JMP | BPF_JSET | BPF_X:
++ case BPF_JMP | BPF_JGT | BPF_X:
++ case BPF_JMP | BPF_JGE | BPF_X:
++ case BPF_JMP | BPF_JLT | BPF_X:
++ case BPF_JMP | BPF_JLE | BPF_X:
++ case BPF_JMP | BPF_JSGT | BPF_X:
++ case BPF_JMP | BPF_JSGE | BPF_X:
++ case BPF_JMP | BPF_JSLT | BPF_X:
++ case BPF_JMP | BPF_JSLE | BPF_X:
++ if (off == 0)
++ break;
++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel);
++ emit_jmp_r(ctx, dst, src, rel, jmp);
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == imm */
++ /* PC += off if dst != imm */
++ /* PC += off if dst & imm */
++ /* PC += off if dst > imm */
++ /* PC += off if dst >= imm */
++ /* PC += off if dst < imm */
++ /* PC += off if dst <= imm */
++ /* PC += off if dst > imm (signed) */
++ /* PC += off if dst >= imm (signed) */
++ /* PC += off if dst < imm (signed) */
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JMP | BPF_JEQ | BPF_K:
++ case BPF_JMP | BPF_JNE | BPF_K:
++ case BPF_JMP | BPF_JSET | BPF_K:
++ case BPF_JMP | BPF_JGT | BPF_K:
++ case BPF_JMP | BPF_JGE | BPF_K:
++ case BPF_JMP | BPF_JLT | BPF_K:
++ case BPF_JMP | BPF_JLE | BPF_K:
++ case BPF_JMP | BPF_JSGT | BPF_K:
++ case BPF_JMP | BPF_JSGE | BPF_K:
++ case BPF_JMP | BPF_JSLT | BPF_K:
++ case BPF_JMP | BPF_JSLE | BPF_K:
++ if (off == 0)
++ break;
++ setup_jmp_i(ctx, imm, 64, BPF_OP(code), off, &jmp, &rel);
++ if (valid_jmp_i(jmp, imm)) {
++ emit_jmp_i(ctx, dst, imm, rel, jmp);
++ } else {
++ /* Move large immediate to register */
++ emit_mov_i(ctx, MIPS_R_T4, imm);
++ emit_jmp_r(ctx, dst, MIPS_R_T4, rel, jmp);
++ }
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off */
++ case BPF_JMP | BPF_JA:
++ if (off == 0)
++ break;
++ if (emit_ja(ctx, off) < 0)
++ goto toofar;
++ break;
++ /* Tail call */
++ case BPF_JMP | BPF_TAIL_CALL:
++ if (emit_tail_call(ctx) < 0)
++ goto invalid;
++ break;
++ /* Function call */
++ case BPF_JMP | BPF_CALL:
++ if (emit_call(ctx, insn) < 0)
++ goto invalid;
++ break;
++ /* Function return */
++ case BPF_JMP | BPF_EXIT:
++ /*
++ * Optimization: when last instruction is EXIT
++ * simply continue to epilogue.
++ */
++ if (ctx->bpf_index == ctx->program->len - 1)
++ break;
++ if (emit_exit(ctx) < 0)
++ goto toofar;
++ break;
++
++ default:
++invalid:
++ pr_err_once("unknown opcode %02x\n", code);
++ return -EINVAL;
++notyet:
++ pr_info_once("*** NOT YET: opcode %02x ***\n", code);
++ return -EFAULT;
++toofar:
++ pr_info_once("*** TOO FAR: jump at %u opcode %02x ***\n",
++ ctx->bpf_index, code);
++ return -E2BIG;
++ }
++ return 0;
++}
--- /dev/null
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:06 +0200
+Subject: [PATCH] mips: bpf: Add JIT workarounds for CPU errata
+
+This patch adds workarounds for the following CPU errata to the MIPS
+eBPF JIT, if enabled in the kernel configuration.
+
+ - R10000 ll/sc weak ordering
+ - Loongson-3 ll/sc weak ordering
+ - Loongson-2F jump hang
+
+The Loongson-2F nop errata is implemented in uasm, which the JIT uses,
+so no additional mitigations are needed for that.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
+---
+
+--- a/arch/mips/net/bpf_jit_comp.c
++++ b/arch/mips/net/bpf_jit_comp.c
+@@ -404,6 +404,7 @@ void emit_alu_r(struct jit_context *ctx,
+ /* Atomic read-modify-write (32-bit) */
+ void emit_atomic_r(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 code)
+ {
++ LLSC_sync(ctx);
+ emit(ctx, ll, MIPS_R_T9, off, dst);
+ switch (code) {
+ case BPF_ADD:
+@@ -420,18 +421,19 @@ void emit_atomic_r(struct jit_context *c
+ break;
+ }
+ emit(ctx, sc, MIPS_R_T8, off, dst);
+- emit(ctx, beqz, MIPS_R_T8, -16);
++ emit(ctx, LLSC_beqz, MIPS_R_T8, -16 - LLSC_offset);
+ emit(ctx, nop); /* Delay slot */
+ }
+
+ /* Atomic compare-and-exchange (32-bit) */
+ void emit_cmpxchg_r(struct jit_context *ctx, u8 dst, u8 src, u8 res, s16 off)
+ {
++ LLSC_sync(ctx);
+ emit(ctx, ll, MIPS_R_T9, off, dst);
+ emit(ctx, bne, MIPS_R_T9, res, 12);
+ emit(ctx, move, MIPS_R_T8, src); /* Delay slot */
+ emit(ctx, sc, MIPS_R_T8, off, dst);
+- emit(ctx, beqz, MIPS_R_T8, -20);
++ emit(ctx, LLSC_beqz, MIPS_R_T8, -20 - LLSC_offset);
+ emit(ctx, move, res, MIPS_R_T9); /* Delay slot */
+ clobber_reg(ctx, res);
+ }
+--- a/arch/mips/net/bpf_jit_comp.h
++++ b/arch/mips/net/bpf_jit_comp.h
+@@ -87,7 +87,7 @@ struct jit_context {
+ };
+
+ /* Emit the instruction if the JIT memory space has been allocated */
+-#define emit(ctx, func, ...) \
++#define __emit(ctx, func, ...) \
+ do { \
+ if ((ctx)->target != NULL) { \
+ u32 *p = &(ctx)->target[ctx->jit_index]; \
+@@ -95,6 +95,30 @@ do { \
+ } \
+ (ctx)->jit_index++; \
+ } while (0)
++#define emit(...) __emit(__VA_ARGS__)
++
++/* Workaround for R10000 ll/sc errata */
++#ifdef CONFIG_WAR_R10000
++#define LLSC_beqz beqzl
++#else
++#define LLSC_beqz beqz
++#endif
++
++/* Workaround for Loongson-3 ll/sc errata */
++#ifdef CONFIG_CPU_LOONGSON3_WORKAROUNDS
++#define LLSC_sync(ctx) emit(ctx, sync, 0)
++#define LLSC_offset 4
++#else
++#define LLSC_sync(ctx)
++#define LLSC_offset 0
++#endif
++
++/* Workaround for Loongson-2F jump errata */
++#ifdef CONFIG_CPU_JUMP_WORKAROUNDS
++#define JALR_MASK 0xffffffffcfffffffULL
++#else
++#define JALR_MASK (~0ULL)
++#endif
+
+ /*
+ * Mark a BPF register as accessed, it needs to be
+--- a/arch/mips/net/bpf_jit_comp64.c
++++ b/arch/mips/net/bpf_jit_comp64.c
+@@ -375,6 +375,7 @@ static void emit_atomic_r64(struct jit_c
+ u8 t1 = MIPS_R_T6;
+ u8 t2 = MIPS_R_T7;
+
++ LLSC_sync(ctx);
+ emit(ctx, lld, t1, off, dst);
+ switch (code) {
+ case BPF_ADD:
+@@ -391,7 +392,7 @@ static void emit_atomic_r64(struct jit_c
+ break;
+ }
+ emit(ctx, scd, t2, off, dst);
+- emit(ctx, beqz, t2, -16);
++ emit(ctx, LLSC_beqz, t2, -16 - LLSC_offset);
+ emit(ctx, nop); /* Delay slot */
+ }
+
+@@ -414,7 +415,7 @@ static int emit_call(struct jit_context
+ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, 0, 0);
+
+ /* Emit function call */
+- emit_mov_i64(ctx, tmp, addr);
++ emit_mov_i64(ctx, tmp, addr & JALR_MASK);
+ emit(ctx, jalr, MIPS_R_RA, tmp);
+ emit(ctx, nop); /* Delay slot */
+
--- /dev/null
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:07 +0200
+Subject: [PATCH] mips: bpf: Enable eBPF JITs
+
+This patch enables the new eBPF JITs for 32-bit and 64-bit MIPS. It also
+disables the old cBPF JIT to so cBPF programs are converted to use the
+new JIT.
+
+Workarounds for R4000 CPU errata are not implemented by the JIT, so the
+JIT is disabled if any of those workarounds are configured.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+---
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -3294,6 +3294,7 @@ S: Supported
+ F: arch/arm64/net/
+
+ BPF JIT for MIPS (32-BIT AND 64-BIT)
++M: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+ M: Paul Burton <paulburton@kernel.org>
+ L: netdev@vger.kernel.org
+ L: bpf@vger.kernel.org
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -49,7 +49,6 @@ config MIPS
+ select HAVE_ARCH_TRACEHOOK
+ select HAVE_ARCH_TRANSPARENT_HUGEPAGE if CPU_SUPPORTS_HUGEPAGES
+ select HAVE_ASM_MODVERSIONS
+- select HAVE_CBPF_JIT if !64BIT && !CPU_MICROMIPS
+ select HAVE_CONTEXT_TRACKING
+ select HAVE_TIF_NOHZ
+ select HAVE_C_RECORDMCOUNT
+@@ -57,7 +56,10 @@ config MIPS
+ select HAVE_DEBUG_STACKOVERFLOW
+ select HAVE_DMA_CONTIGUOUS
+ select HAVE_DYNAMIC_FTRACE
+- select HAVE_EBPF_JIT if 64BIT && !CPU_MICROMIPS && TARGET_ISA_REV >= 2
++ select HAVE_EBPF_JIT if !CPU_MICROMIPS && \
++ !CPU_DADDI_WORKAROUNDS && \
++ !CPU_R4000_WORKAROUNDS && \
++ !CPU_R4400_WORKAROUNDS
+ select HAVE_EXIT_THREAD
+ select HAVE_FAST_GUP
+ select HAVE_FTRACE_MCOUNT_RECORD
+--- a/arch/mips/net/Makefile
++++ b/arch/mips/net/Makefile
+@@ -2,9 +2,10 @@
+ # MIPS networking code
+
+ obj-$(CONFIG_MIPS_CBPF_JIT) += bpf_jit.o bpf_jit_asm.o
++obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp.o
+
+ ifeq ($(CONFIG_32BIT),y)
+- obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp.o bpf_jit_comp32.o
++ obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp32.o
+ else
+- obj-$(CONFIG_MIPS_EBPF_JIT) += ebpf_jit.o
++ obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp64.o
+ endif
--- /dev/null
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:08 +0200
+Subject: [PATCH] mips: bpf: Remove old BPF JIT implementations
+
+This patch removes the old 32-bit cBPF and 64-bit eBPF JIT implementations.
+They are replaced by a new eBPF implementation that supports both 32-bit
+and 64-bit MIPS CPUs.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+---
+ delete mode 100644 arch/mips/net/bpf_jit.c
+ delete mode 100644 arch/mips/net/bpf_jit.h
+ delete mode 100644 arch/mips/net/bpf_jit_asm.S
+ delete mode 100644 arch/mips/net/ebpf_jit.c
+
+--- a/arch/mips/net/bpf_jit.h
++++ /dev/null
+@@ -1,81 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-only */
+-/*
+- * Just-In-Time compiler for BPF filters on MIPS
+- *
+- * Copyright (c) 2014 Imagination Technologies Ltd.
+- * Author: Markos Chandras <markos.chandras@imgtec.com>
+- */
+-
+-#ifndef BPF_JIT_MIPS_OP_H
+-#define BPF_JIT_MIPS_OP_H
+-
+-/* Registers used by JIT */
+-#define MIPS_R_ZERO 0
+-#define MIPS_R_V0 2
+-#define MIPS_R_A0 4
+-#define MIPS_R_A1 5
+-#define MIPS_R_T4 12
+-#define MIPS_R_T5 13
+-#define MIPS_R_T6 14
+-#define MIPS_R_T7 15
+-#define MIPS_R_S0 16
+-#define MIPS_R_S1 17
+-#define MIPS_R_S2 18
+-#define MIPS_R_S3 19
+-#define MIPS_R_S4 20
+-#define MIPS_R_S5 21
+-#define MIPS_R_S6 22
+-#define MIPS_R_S7 23
+-#define MIPS_R_SP 29
+-#define MIPS_R_RA 31
+-
+-/* Conditional codes */
+-#define MIPS_COND_EQ 0x1
+-#define MIPS_COND_GE (0x1 << 1)
+-#define MIPS_COND_GT (0x1 << 2)
+-#define MIPS_COND_NE (0x1 << 3)
+-#define MIPS_COND_ALL (0x1 << 4)
+-/* Conditionals on X register or K immediate */
+-#define MIPS_COND_X (0x1 << 5)
+-#define MIPS_COND_K (0x1 << 6)
+-
+-#define r_ret MIPS_R_V0
+-
+-/*
+- * Use 2 scratch registers to avoid pipeline interlocks.
+- * There is no overhead during epilogue and prologue since
+- * any of the $s0-$s6 registers will only be preserved if
+- * they are going to actually be used.
+- */
+-#define r_skb_hl MIPS_R_S0 /* skb header length */
+-#define r_skb_data MIPS_R_S1 /* skb actual data */
+-#define r_off MIPS_R_S2
+-#define r_A MIPS_R_S3
+-#define r_X MIPS_R_S4
+-#define r_skb MIPS_R_S5
+-#define r_M MIPS_R_S6
+-#define r_skb_len MIPS_R_S7
+-#define r_s0 MIPS_R_T4 /* scratch reg 1 */
+-#define r_s1 MIPS_R_T5 /* scratch reg 2 */
+-#define r_tmp_imm MIPS_R_T6 /* No need to preserve this */
+-#define r_tmp MIPS_R_T7 /* No need to preserve this */
+-#define r_zero MIPS_R_ZERO
+-#define r_sp MIPS_R_SP
+-#define r_ra MIPS_R_RA
+-
+-#ifndef __ASSEMBLY__
+-
+-/* Declare ASM helpers */
+-
+-#define DECLARE_LOAD_FUNC(func) \
+- extern u8 func(unsigned long *skb, int offset); \
+- extern u8 func##_negative(unsigned long *skb, int offset); \
+- extern u8 func##_positive(unsigned long *skb, int offset)
+-
+-DECLARE_LOAD_FUNC(sk_load_word);
+-DECLARE_LOAD_FUNC(sk_load_half);
+-DECLARE_LOAD_FUNC(sk_load_byte);
+-
+-#endif
+-
+-#endif /* BPF_JIT_MIPS_OP_H */
+--- a/arch/mips/net/bpf_jit_asm.S
++++ /dev/null
+@@ -1,285 +0,0 @@
+-/*
+- * bpf_jib_asm.S: Packet/header access helper functions for MIPS/MIPS64 BPF
+- * compiler.
+- *
+- * Copyright (C) 2015 Imagination Technologies Ltd.
+- * Author: Markos Chandras <markos.chandras@imgtec.com>
+- *
+- * This program is free software; you can redistribute it and/or modify it
+- * under the terms of the GNU General Public License as published by the
+- * Free Software Foundation; version 2 of the License.
+- */
+-
+-#include <asm/asm.h>
+-#include <asm/isa-rev.h>
+-#include <asm/regdef.h>
+-#include "bpf_jit.h"
+-
+-/* ABI
+- *
+- * r_skb_hl skb header length
+- * r_skb_data skb data
+- * r_off(a1) offset register
+- * r_A BPF register A
+- * r_X PF register X
+- * r_skb(a0) *skb
+- * r_M *scratch memory
+- * r_skb_le skb length
+- * r_s0 Scratch register 0
+- * r_s1 Scratch register 1
+- *
+- * On entry:
+- * a0: *skb
+- * a1: offset (imm or imm + X)
+- *
+- * All non-BPF-ABI registers are free for use. On return, we only
+- * care about r_ret. The BPF-ABI registers are assumed to remain
+- * unmodified during the entire filter operation.
+- */
+-
+-#define skb a0
+-#define offset a1
+-#define SKF_LL_OFF (-0x200000) /* Can't include linux/filter.h in assembly */
+-
+- /* We know better :) so prevent assembler reordering etc */
+- .set noreorder
+-
+-#define is_offset_negative(TYPE) \
+- /* If offset is negative we have more work to do */ \
+- slti t0, offset, 0; \
+- bgtz t0, bpf_slow_path_##TYPE##_neg; \
+- /* Be careful what follows in DS. */
+-
+-#define is_offset_in_header(SIZE, TYPE) \
+- /* Reading from header? */ \
+- addiu $r_s0, $r_skb_hl, -SIZE; \
+- slt t0, $r_s0, offset; \
+- bgtz t0, bpf_slow_path_##TYPE; \
+-
+-LEAF(sk_load_word)
+- is_offset_negative(word)
+-FEXPORT(sk_load_word_positive)
+- is_offset_in_header(4, word)
+- /* Offset within header boundaries */
+- PTR_ADDU t1, $r_skb_data, offset
+- .set reorder
+- lw $r_A, 0(t1)
+- .set noreorder
+-#ifdef CONFIG_CPU_LITTLE_ENDIAN
+-# if MIPS_ISA_REV >= 2
+- wsbh t0, $r_A
+- rotr $r_A, t0, 16
+-# else
+- sll t0, $r_A, 24
+- srl t1, $r_A, 24
+- srl t2, $r_A, 8
+- or t0, t0, t1
+- andi t2, t2, 0xff00
+- andi t1, $r_A, 0xff00
+- or t0, t0, t2
+- sll t1, t1, 8
+- or $r_A, t0, t1
+-# endif
+-#endif
+- jr $r_ra
+- move $r_ret, zero
+- END(sk_load_word)
+-
+-LEAF(sk_load_half)
+- is_offset_negative(half)
+-FEXPORT(sk_load_half_positive)
+- is_offset_in_header(2, half)
+- /* Offset within header boundaries */
+- PTR_ADDU t1, $r_skb_data, offset
+- lhu $r_A, 0(t1)
+-#ifdef CONFIG_CPU_LITTLE_ENDIAN
+-# if MIPS_ISA_REV >= 2
+- wsbh $r_A, $r_A
+-# else
+- sll t0, $r_A, 8
+- srl t1, $r_A, 8
+- andi t0, t0, 0xff00
+- or $r_A, t0, t1
+-# endif
+-#endif
+- jr $r_ra
+- move $r_ret, zero
+- END(sk_load_half)
+-
+-LEAF(sk_load_byte)
+- is_offset_negative(byte)
+-FEXPORT(sk_load_byte_positive)
+- is_offset_in_header(1, byte)
+- /* Offset within header boundaries */
+- PTR_ADDU t1, $r_skb_data, offset
+- lbu $r_A, 0(t1)
+- jr $r_ra
+- move $r_ret, zero
+- END(sk_load_byte)
+-
+-/*
+- * call skb_copy_bits:
+- * (prototype in linux/skbuff.h)
+- *
+- * int skb_copy_bits(sk_buff *skb, int offset, void *to, int len)
+- *
+- * o32 mandates we leave 4 spaces for argument registers in case
+- * the callee needs to use them. Even though we don't care about
+- * the argument registers ourselves, we need to allocate that space
+- * to remain ABI compliant since the callee may want to use that space.
+- * We also allocate 2 more spaces for $r_ra and our return register (*to).
+- *
+- * n64 is a bit different. The *caller* will allocate the space to preserve
+- * the arguments. So in 64-bit kernels, we allocate the 4-arg space for no
+- * good reason but it does not matter that much really.
+- *
+- * (void *to) is returned in r_s0
+- *
+- */
+-#ifdef CONFIG_CPU_LITTLE_ENDIAN
+-#define DS_OFFSET(SIZE) (4 * SZREG)
+-#else
+-#define DS_OFFSET(SIZE) ((4 * SZREG) + (4 - SIZE))
+-#endif
+-#define bpf_slow_path_common(SIZE) \
+- /* Quick check. Are we within reasonable boundaries? */ \
+- LONG_ADDIU $r_s1, $r_skb_len, -SIZE; \
+- sltu $r_s0, offset, $r_s1; \
+- beqz $r_s0, fault; \
+- /* Load 4th argument in DS */ \
+- LONG_ADDIU a3, zero, SIZE; \
+- PTR_ADDIU $r_sp, $r_sp, -(6 * SZREG); \
+- PTR_LA t0, skb_copy_bits; \
+- PTR_S $r_ra, (5 * SZREG)($r_sp); \
+- /* Assign low slot to a2 */ \
+- PTR_ADDIU a2, $r_sp, DS_OFFSET(SIZE); \
+- jalr t0; \
+- /* Reset our destination slot (DS but it's ok) */ \
+- INT_S zero, (4 * SZREG)($r_sp); \
+- /* \
+- * skb_copy_bits returns 0 on success and -EFAULT \
+- * on error. Our data live in a2. Do not bother with \
+- * our data if an error has been returned. \
+- */ \
+- /* Restore our frame */ \
+- PTR_L $r_ra, (5 * SZREG)($r_sp); \
+- INT_L $r_s0, (4 * SZREG)($r_sp); \
+- bltz v0, fault; \
+- PTR_ADDIU $r_sp, $r_sp, 6 * SZREG; \
+- move $r_ret, zero; \
+-
+-NESTED(bpf_slow_path_word, (6 * SZREG), $r_sp)
+- bpf_slow_path_common(4)
+-#ifdef CONFIG_CPU_LITTLE_ENDIAN
+-# if MIPS_ISA_REV >= 2
+- wsbh t0, $r_s0
+- jr $r_ra
+- rotr $r_A, t0, 16
+-# else
+- sll t0, $r_s0, 24
+- srl t1, $r_s0, 24
+- srl t2, $r_s0, 8
+- or t0, t0, t1
+- andi t2, t2, 0xff00
+- andi t1, $r_s0, 0xff00
+- or t0, t0, t2
+- sll t1, t1, 8
+- jr $r_ra
+- or $r_A, t0, t1
+-# endif
+-#else
+- jr $r_ra
+- move $r_A, $r_s0
+-#endif
+-
+- END(bpf_slow_path_word)
+-
+-NESTED(bpf_slow_path_half, (6 * SZREG), $r_sp)
+- bpf_slow_path_common(2)
+-#ifdef CONFIG_CPU_LITTLE_ENDIAN
+-# if MIPS_ISA_REV >= 2
+- jr $r_ra
+- wsbh $r_A, $r_s0
+-# else
+- sll t0, $r_s0, 8
+- andi t1, $r_s0, 0xff00
+- andi t0, t0, 0xff00
+- srl t1, t1, 8
+- jr $r_ra
+- or $r_A, t0, t1
+-# endif
+-#else
+- jr $r_ra
+- move $r_A, $r_s0
+-#endif
+-
+- END(bpf_slow_path_half)
+-
+-NESTED(bpf_slow_path_byte, (6 * SZREG), $r_sp)
+- bpf_slow_path_common(1)
+- jr $r_ra
+- move $r_A, $r_s0
+-
+- END(bpf_slow_path_byte)
+-
+-/*
+- * Negative entry points
+- */
+- .macro bpf_is_end_of_data
+- li t0, SKF_LL_OFF
+- /* Reading link layer data? */
+- slt t1, offset, t0
+- bgtz t1, fault
+- /* Be careful what follows in DS. */
+- .endm
+-/*
+- * call skb_copy_bits:
+- * (prototype in linux/filter.h)
+- *
+- * void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb,
+- * int k, unsigned int size)
+- *
+- * see above (bpf_slow_path_common) for ABI restrictions
+- */
+-#define bpf_negative_common(SIZE) \
+- PTR_ADDIU $r_sp, $r_sp, -(6 * SZREG); \
+- PTR_LA t0, bpf_internal_load_pointer_neg_helper; \
+- PTR_S $r_ra, (5 * SZREG)($r_sp); \
+- jalr t0; \
+- li a2, SIZE; \
+- PTR_L $r_ra, (5 * SZREG)($r_sp); \
+- /* Check return pointer */ \
+- beqz v0, fault; \
+- PTR_ADDIU $r_sp, $r_sp, 6 * SZREG; \
+- /* Preserve our pointer */ \
+- move $r_s0, v0; \
+- /* Set return value */ \
+- move $r_ret, zero; \
+-
+-bpf_slow_path_word_neg:
+- bpf_is_end_of_data
+-NESTED(sk_load_word_negative, (6 * SZREG), $r_sp)
+- bpf_negative_common(4)
+- jr $r_ra
+- lw $r_A, 0($r_s0)
+- END(sk_load_word_negative)
+-
+-bpf_slow_path_half_neg:
+- bpf_is_end_of_data
+-NESTED(sk_load_half_negative, (6 * SZREG), $r_sp)
+- bpf_negative_common(2)
+- jr $r_ra
+- lhu $r_A, 0($r_s0)
+- END(sk_load_half_negative)
+-
+-bpf_slow_path_byte_neg:
+- bpf_is_end_of_data
+-NESTED(sk_load_byte_negative, (6 * SZREG), $r_sp)
+- bpf_negative_common(1)
+- jr $r_ra
+- lbu $r_A, 0($r_s0)
+- END(sk_load_byte_negative)
+-
+-fault:
+- jr $r_ra
+- addiu $r_ret, zero, 1
--- /dev/null
+From 03662fcd41f4b764857f17b95f9a2a63c24bddd4 Mon Sep 17 00:00:00 2001
+From: Ard Biesheuvel <ardb@kernel.org>
+Date: Tue, 3 Nov 2020 17:28:09 +0100
+Subject: [PATCH 1/2] crypto: arm/chacha-neon - optimize for non-block size
+ multiples
+
+commit 86cd97ec4b943af35562a74688bc4e909b32c3d1 upstream.
+
+The current NEON based ChaCha implementation for ARM is optimized for
+multiples of 4x the ChaCha block size (64 bytes). This makes sense for
+block encryption, but given that ChaCha is also often used in the
+context of networking, it makes sense to consider arbitrary length
+inputs as well.
+
+For example, WireGuard typically uses 1420 byte packets, and performing
+ChaCha encryption involves 5 invocations of chacha_4block_xor_neon()
+and 3 invocations of chacha_block_xor_neon(), where the last one also
+involves a memcpy() using a buffer on the stack to process the final
+chunk of 1420 % 64 == 12 bytes.
+
+Let's optimize for this case as well, by letting chacha_4block_xor_neon()
+deal with any input size between 64 and 256 bytes, using NEON permutation
+instructions and overlapping loads and stores. This way, the 140 byte
+tail of a 1420 byte input buffer can simply be processed in one go.
+
+This results in the following performance improvements for 1420 byte
+blocks, without significant impact on power-of-2 input sizes. (Note
+that Raspberry Pi is widely used in combination with a 32-bit kernel,
+even though the core is 64-bit capable)
+
+ Cortex-A8 (BeagleBone) : 7%
+ Cortex-A15 (Calxeda Midway) : 21%
+ Cortex-A53 (Raspberry Pi 3) : 3%
+ Cortex-A72 (Raspberry Pi 4) : 19%
+
+Cc: Eric Biggers <ebiggers@google.com>
+Cc: "Jason A . Donenfeld" <Jason@zx2c4.com>
+Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+---
+ arch/arm/crypto/chacha-glue.c | 34 +++++------
+ arch/arm/crypto/chacha-neon-core.S | 97 +++++++++++++++++++++++++++---
+ 2 files changed, 107 insertions(+), 24 deletions(-)
+
+--- a/arch/arm/crypto/chacha-glue.c
++++ b/arch/arm/crypto/chacha-glue.c
+@@ -23,7 +23,7 @@
+ asmlinkage void chacha_block_xor_neon(const u32 *state, u8 *dst, const u8 *src,
+ int nrounds);
+ asmlinkage void chacha_4block_xor_neon(const u32 *state, u8 *dst, const u8 *src,
+- int nrounds);
++ int nrounds, unsigned int nbytes);
+ asmlinkage void hchacha_block_arm(const u32 *state, u32 *out, int nrounds);
+ asmlinkage void hchacha_block_neon(const u32 *state, u32 *out, int nrounds);
+
+@@ -42,24 +42,24 @@ static void chacha_doneon(u32 *state, u8
+ {
+ u8 buf[CHACHA_BLOCK_SIZE];
+
+- while (bytes >= CHACHA_BLOCK_SIZE * 4) {
+- chacha_4block_xor_neon(state, dst, src, nrounds);
+- bytes -= CHACHA_BLOCK_SIZE * 4;
+- src += CHACHA_BLOCK_SIZE * 4;
+- dst += CHACHA_BLOCK_SIZE * 4;
+- state[12] += 4;
+- }
+- while (bytes >= CHACHA_BLOCK_SIZE) {
+- chacha_block_xor_neon(state, dst, src, nrounds);
+- bytes -= CHACHA_BLOCK_SIZE;
+- src += CHACHA_BLOCK_SIZE;
+- dst += CHACHA_BLOCK_SIZE;
+- state[12]++;
++ while (bytes > CHACHA_BLOCK_SIZE) {
++ unsigned int l = min(bytes, CHACHA_BLOCK_SIZE * 4U);
++
++ chacha_4block_xor_neon(state, dst, src, nrounds, l);
++ bytes -= l;
++ src += l;
++ dst += l;
++ state[12] += DIV_ROUND_UP(l, CHACHA_BLOCK_SIZE);
+ }
+ if (bytes) {
+- memcpy(buf, src, bytes);
+- chacha_block_xor_neon(state, buf, buf, nrounds);
+- memcpy(dst, buf, bytes);
++ const u8 *s = src;
++ u8 *d = dst;
++
++ if (bytes != CHACHA_BLOCK_SIZE)
++ s = d = memcpy(buf, src, bytes);
++ chacha_block_xor_neon(state, d, s, nrounds);
++ if (d != dst)
++ memcpy(dst, buf, bytes);
+ }
+ }
+
+--- a/arch/arm/crypto/chacha-neon-core.S
++++ b/arch/arm/crypto/chacha-neon-core.S
+@@ -47,6 +47,7 @@
+ */
+
+ #include <linux/linkage.h>
++#include <asm/cache.h>
+
+ .text
+ .fpu neon
+@@ -205,7 +206,7 @@ ENDPROC(hchacha_block_neon)
+
+ .align 5
+ ENTRY(chacha_4block_xor_neon)
+- push {r4-r5}
++ push {r4, lr}
+ mov r4, sp // preserve the stack pointer
+ sub ip, sp, #0x20 // allocate a 32 byte buffer
+ bic ip, ip, #0x1f // aligned to 32 bytes
+@@ -229,10 +230,10 @@ ENTRY(chacha_4block_xor_neon)
+ vld1.32 {q0-q1}, [r0]
+ vld1.32 {q2-q3}, [ip]
+
+- adr r5, .Lctrinc
++ adr lr, .Lctrinc
+ vdup.32 q15, d7[1]
+ vdup.32 q14, d7[0]
+- vld1.32 {q4}, [r5, :128]
++ vld1.32 {q4}, [lr, :128]
+ vdup.32 q13, d6[1]
+ vdup.32 q12, d6[0]
+ vdup.32 q11, d5[1]
+@@ -455,7 +456,7 @@ ENTRY(chacha_4block_xor_neon)
+
+ // Re-interleave the words in the first two rows of each block (x0..7).
+ // Also add the counter values 0-3 to x12[0-3].
+- vld1.32 {q8}, [r5, :128] // load counter values 0-3
++ vld1.32 {q8}, [lr, :128] // load counter values 0-3
+ vzip.32 q0, q1 // => (0 1 0 1) (0 1 0 1)
+ vzip.32 q2, q3 // => (2 3 2 3) (2 3 2 3)
+ vzip.32 q4, q5 // => (4 5 4 5) (4 5 4 5)
+@@ -493,6 +494,8 @@ ENTRY(chacha_4block_xor_neon)
+
+ // Re-interleave the words in the last two rows of each block (x8..15).
+ vld1.32 {q8-q9}, [sp, :256]
++ mov sp, r4 // restore original stack pointer
++ ldr r4, [r4, #8] // load number of bytes
+ vzip.32 q12, q13 // => (12 13 12 13) (12 13 12 13)
+ vzip.32 q14, q15 // => (14 15 14 15) (14 15 14 15)
+ vzip.32 q8, q9 // => (8 9 8 9) (8 9 8 9)
+@@ -520,41 +523,121 @@ ENTRY(chacha_4block_xor_neon)
+ // XOR the rest of the data with the keystream
+
+ vld1.8 {q0-q1}, [r2]!
++ subs r4, r4, #96
+ veor q0, q0, q8
+ veor q1, q1, q12
++ ble .Lle96
+ vst1.8 {q0-q1}, [r1]!
+
+ vld1.8 {q0-q1}, [r2]!
++ subs r4, r4, #32
+ veor q0, q0, q2
+ veor q1, q1, q6
++ ble .Lle128
+ vst1.8 {q0-q1}, [r1]!
+
+ vld1.8 {q0-q1}, [r2]!
++ subs r4, r4, #32
+ veor q0, q0, q10
+ veor q1, q1, q14
++ ble .Lle160
+ vst1.8 {q0-q1}, [r1]!
+
+ vld1.8 {q0-q1}, [r2]!
++ subs r4, r4, #32
+ veor q0, q0, q4
+ veor q1, q1, q5
++ ble .Lle192
+ vst1.8 {q0-q1}, [r1]!
+
+ vld1.8 {q0-q1}, [r2]!
++ subs r4, r4, #32
+ veor q0, q0, q9
+ veor q1, q1, q13
++ ble .Lle224
+ vst1.8 {q0-q1}, [r1]!
+
+ vld1.8 {q0-q1}, [r2]!
++ subs r4, r4, #32
+ veor q0, q0, q3
+ veor q1, q1, q7
++ blt .Llt256
++.Lout:
+ vst1.8 {q0-q1}, [r1]!
+
+ vld1.8 {q0-q1}, [r2]
+- mov sp, r4 // restore original stack pointer
+ veor q0, q0, q11
+ veor q1, q1, q15
+ vst1.8 {q0-q1}, [r1]
+
+- pop {r4-r5}
+- bx lr
++ pop {r4, pc}
++
++.Lle192:
++ vmov q4, q9
++ vmov q5, q13
++
++.Lle160:
++ // nothing to do
++
++.Lfinalblock:
++ // Process the final block if processing less than 4 full blocks.
++ // Entered with 32 bytes of ChaCha cipher stream in q4-q5, and the
++ // previous 32 byte output block that still needs to be written at
++ // [r1] in q0-q1.
++ beq .Lfullblock
++
++.Lpartialblock:
++ adr lr, .Lpermute + 32
++ add r2, r2, r4
++ add lr, lr, r4
++ add r4, r4, r1
++
++ vld1.8 {q2-q3}, [lr]
++ vld1.8 {q6-q7}, [r2]
++
++ add r4, r4, #32
++
++ vtbl.8 d4, {q4-q5}, d4
++ vtbl.8 d5, {q4-q5}, d5
++ vtbl.8 d6, {q4-q5}, d6
++ vtbl.8 d7, {q4-q5}, d7
++
++ veor q6, q6, q2
++ veor q7, q7, q3
++
++ vst1.8 {q6-q7}, [r4] // overlapping stores
++ vst1.8 {q0-q1}, [r1]
++ pop {r4, pc}
++
++.Lfullblock:
++ vmov q11, q4
++ vmov q15, q5
++ b .Lout
++.Lle96:
++ vmov q4, q2
++ vmov q5, q6
++ b .Lfinalblock
++.Lle128:
++ vmov q4, q10
++ vmov q5, q14
++ b .Lfinalblock
++.Lle224:
++ vmov q4, q3
++ vmov q5, q7
++ b .Lfinalblock
++.Llt256:
++ vmov q4, q11
++ vmov q5, q15
++ b .Lpartialblock
+ ENDPROC(chacha_4block_xor_neon)
++
++ .align L1_CACHE_SHIFT
++.Lpermute:
++ .byte 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
++ .byte 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
++ .byte 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
++ .byte 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
++ .byte 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
++ .byte 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
++ .byte 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
++ .byte 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
--- /dev/null
+From 7f63462faf9eab69132bea9abd48c2c05a93145b Mon Sep 17 00:00:00 2001
+From: Ard Biesheuvel <ardb@kernel.org>
+Date: Sun, 13 Dec 2020 15:39:29 +0100
+Subject: [PATCH 2/2] crypto: arm/chacha-neon - add missing counter increment
+
+commit fd16931a2f518a32753920ff20895e5cf04c8ff1 upstream.
+
+Commit 86cd97ec4b943af3 ("crypto: arm/chacha-neon - optimize for non-block
+size multiples") refactored the chacha block handling in the glue code in
+a way that may result in the counter increment to be omitted when calling
+chacha_block_xor_neon() to process a full block. This violates the skcipher
+API, which requires that the output IV is suitable for handling more input
+as long as the preceding input has been presented in round multiples of the
+block size. Also, the same code is exposed via the chacha library interface
+whose callers may actually rely on this increment to occur even for final
+blocks that are smaller than the chacha block size.
+
+So increment the counter after calling chacha_block_xor_neon().
+
+Fixes: 86cd97ec4b943af3 ("crypto: arm/chacha-neon - optimize for non-block size multiples")
+Reported-by: Eric Biggers <ebiggers@kernel.org>
+Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+---
+ arch/arm/crypto/chacha-glue.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/crypto/chacha-glue.c
++++ b/arch/arm/crypto/chacha-glue.c
+@@ -60,6 +60,7 @@ static void chacha_doneon(u32 *state, u8
+ chacha_block_xor_neon(state, d, s, nrounds);
+ if (d != dst)
+ memcpy(dst, buf, bytes);
++ state[12]++;
+ }
+ }
+
--- /dev/null
+From a13827e9091c07e25cdeec9a402d74a27e2a1111 Mon Sep 17 00:00:00 2001
+From: "Jason A. Donenfeld" <Jason@zx2c4.com>
+Date: Mon, 22 Feb 2021 17:25:46 +0100
+Subject: [PATCH] wireguard: peer: put frequently used members above cache
+ lines
+
+commit 5a0598695634a6bb4126818902dd9140cd9df8b6 upstream.
+
+The is_dead boolean is checked for every single packet, while the
+internal_id member is used basically only for pr_debug messages. So it
+makes sense to hoist up is_dead into some space formerly unused by a
+struct hole, while demoting internal_api to below the lowest struct
+cache line.
+
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+---
+ drivers/net/wireguard/peer.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/wireguard/peer.h
++++ b/drivers/net/wireguard/peer.h
+@@ -39,6 +39,7 @@ struct wg_peer {
+ struct prev_queue tx_queue, rx_queue;
+ struct sk_buff_head staged_packet_queue;
+ int serial_work_cpu;
++ bool is_dead;
+ struct noise_keypairs keypairs;
+ struct endpoint endpoint;
+ struct dst_cache endpoint_cache;
+@@ -61,9 +62,8 @@ struct wg_peer {
+ struct rcu_head rcu;
+ struct list_head peer_list;
+ struct list_head allowedips_list;
+- u64 internal_id;
+ struct napi_struct napi;
+- bool is_dead;
++ u64 internal_id;
+ };
+
+ struct wg_peer *wg_peer_create(struct wg_device *wg,
--- /dev/null
+From 02d6fdecb9c38de19065f6bed8d5214556fd061d Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 4 Nov 2021 16:00:40 +0100
+Subject: regmap: allow to define reg_update_bits for no bus configuration
+
+Some device requires a special handling for reg_update_bits and can't use
+the normal regmap read write logic. An example is when locking is
+handled by the device and rmw operations requires to do atomic operations.
+Allow to declare a dedicated function in regmap_config for
+reg_update_bits in no bus configuration.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Link: https://lore.kernel.org/r/20211104150040.1260-1-ansuelsmth@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/base/regmap/regmap.c | 1 +
+ include/linux/regmap.h | 7 +++++++
+ 2 files changed, 8 insertions(+)
+
+--- a/drivers/base/regmap/regmap.c
++++ b/drivers/base/regmap/regmap.c
+@@ -842,6 +842,7 @@ struct regmap *__regmap_init(struct devi
+ if (!bus) {
+ map->reg_read = config->reg_read;
+ map->reg_write = config->reg_write;
++ map->reg_update_bits = config->reg_update_bits;
+
+ map->defer_caching = false;
+ goto skip_format_initialization;
+--- a/include/linux/regmap.h
++++ b/include/linux/regmap.h
+@@ -289,6 +289,11 @@ typedef void (*regmap_unlock)(void *);
+ * read operation on a bus such as SPI, I2C, etc. Most of the
+ * devices do not need this.
+ * @reg_write: Same as above for writing.
++ * @reg_update_bits: Optional callback that if filled will be used to perform
++ * all the update_bits(rmw) operation. Should only be provided
++ * if the function require special handling with lock and reg
++ * handling and the operation cannot be represented as a simple
++ * update_bits operation on a bus such as SPI, I2C, etc.
+ * @fast_io: Register IO is fast. Use a spinlock instead of a mutex
+ * to perform locking. This field is ignored if custom lock/unlock
+ * functions are used (see fields lock/unlock of struct regmap_config).
+@@ -366,6 +371,8 @@ struct regmap_config {
+
+ int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
+ int (*reg_write)(void *context, unsigned int reg, unsigned int val);
++ int (*reg_update_bits)(void *context, unsigned int reg,
++ unsigned int mask, unsigned int val);
+
+ bool fast_io;
+
--- /dev/null
+From 6523061868212473f63812a0c477a161742bed42 Mon Sep 17 00:00:00 2001
+From: "Jason A. Donenfeld" <Jason@zx2c4.com>
+Date: Sat, 27 Feb 2021 13:20:24 +0100
+Subject: [PATCH] MIPS: select CPU_MIPS64 for remaining MIPS64 CPUs
+
+The CPU_MIPS64 and CPU_MIPS32 variables are supposed to be able to
+distinguish broadly between 64-bit and 32-bit MIPS CPUs. However, they
+weren't selected by the specialty CPUs, Octeon and Loongson, which meant
+it was possible to hit a weird state of:
+
+ MIPS=y, CONFIG_64BIT=y, CPU_MIPS64=n
+
+This commit rectifies the issue by having CPU_MIPS64 be selected when
+the missing Octeon or Loongson models are selected.
+
+Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Cc: Ralf Baechle <ralf@linux-mips.org>
+Cc: George Cherian <gcherian@marvell.com>
+Cc: Huacai Chen <chenhuacai@kernel.org>
+Cc: Jiaxun Yang <jiaxun.yang@flygoat.com>
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+---
+ arch/mips/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -2088,7 +2088,7 @@ config CPU_MIPS32
+ config CPU_MIPS64
+ bool
+ default y if CPU_MIPS64_R1 || CPU_MIPS64_R2 || CPU_MIPS64_R5 || \
+- CPU_MIPS64_R6
++ CPU_MIPS64_R6 || CPU_LOONGSON64 || CPU_CAVIUM_OCTEON
+
+ #
+ # These indicate the revision of the architecture
--- /dev/null
+From 7d1531c81c0fb4c93bea8dc316043ad0e4d0c270 Mon Sep 17 00:00:00 2001
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Sun, 25 Oct 2020 23:19:40 +0800
+Subject: [PATCH] MIPS: zboot: put appended dtb into a section
+
+This will make a separated section for dtb appear in ELF, and we can
+then use objcopy to patch a dtb into vmlinuz when RAW_APPENDED_DTB
+is set in kernel config.
+
+command to patch a dtb:
+objcopy --set-section-flags=.appended_dtb=alloc,contents \
+ --update-section=.appended_dtb=<target>.dtb vmlinuz vmlinuz-dtb
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ arch/mips/boot/compressed/ld.script | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+--- a/arch/mips/boot/compressed/ld.script
++++ b/arch/mips/boot/compressed/ld.script
+@@ -31,9 +31,12 @@ SECTIONS
+ CONSTRUCTORS
+ . = ALIGN(16);
+ }
+- __appended_dtb = .;
+- /* leave space for appended DTB */
+- . += 0x100000;
++
++ .appended_dtb : {
++ __appended_dtb = .;
++ /* leave space for appended DTB */
++ . += 0x100000;
++ }
+
+ _edata = .;
+ /* End of data section */
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Thu, 25 Jan 2018 12:58:55 +0100
+Subject: [PATCH] netfilter: nft_flow_offload: handle netdevice events from
+ nf_flow_table
+
+Move the code that deals with device events to the core.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -576,13 +576,41 @@ void nf_flow_table_free(struct nf_flowta
+ }
+ EXPORT_SYMBOL_GPL(nf_flow_table_free);
+
++static int nf_flow_table_netdev_event(struct notifier_block *this,
++ unsigned long event, void *ptr)
++{
++ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
++
++ if (event != NETDEV_DOWN)
++ return NOTIFY_DONE;
++
++ nf_flow_table_cleanup(dev);
++
++ return NOTIFY_DONE;
++}
++
++static struct notifier_block flow_offload_netdev_notifier = {
++ .notifier_call = nf_flow_table_netdev_event,
++};
++
+ static int __init nf_flow_table_module_init(void)
+ {
+- return nf_flow_table_offload_init();
++ int ret;
++
++ ret = nf_flow_table_offload_init();
++ if (ret)
++ return ret;
++
++ ret = register_netdevice_notifier(&flow_offload_netdev_notifier);
++ if (ret)
++ nf_flow_table_offload_exit();
++
++ return ret;
+ }
+
+ static void __exit nf_flow_table_module_exit(void)
+ {
++ unregister_netdevice_notifier(&flow_offload_netdev_notifier);
+ nf_flow_table_offload_exit();
+ }
+
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -237,47 +237,14 @@ static struct nft_expr_type nft_flow_off
+ .owner = THIS_MODULE,
+ };
+
+-static int flow_offload_netdev_event(struct notifier_block *this,
+- unsigned long event, void *ptr)
+-{
+- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+-
+- if (event != NETDEV_DOWN)
+- return NOTIFY_DONE;
+-
+- nf_flow_table_cleanup(dev);
+-
+- return NOTIFY_DONE;
+-}
+-
+-static struct notifier_block flow_offload_netdev_notifier = {
+- .notifier_call = flow_offload_netdev_event,
+-};
+-
+ static int __init nft_flow_offload_module_init(void)
+ {
+- int err;
+-
+- err = register_netdevice_notifier(&flow_offload_netdev_notifier);
+- if (err)
+- goto err;
+-
+- err = nft_register_expr(&nft_flow_offload_type);
+- if (err < 0)
+- goto register_expr;
+-
+- return 0;
+-
+-register_expr:
+- unregister_netdevice_notifier(&flow_offload_netdev_notifier);
+-err:
+- return err;
++ return nft_register_expr(&nft_flow_offload_type);
+ }
+
+ static void __exit nft_flow_offload_module_exit(void)
+ {
+ nft_unregister_expr(&nft_flow_offload_type);
+- unregister_netdevice_notifier(&flow_offload_netdev_notifier);
+ }
+
+ module_init(nft_flow_offload_module_init);
--- /dev/null
+From 04e9ab75267489224364fa510a88ada83e11c325 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 10 Dec 2020 18:23:52 +0100
+Subject: [PATCH] dt-bindings: mtd: convert "fixed-partitions" to the
+ json-schema
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This standardizes its documentation, allows validating with Makefile
+checks and helps writing DTS files.
+
+Noticeable changes:
+1. Dropped "Partitions can be represented by sub-nodes of a flash
+ device." as we also support subpartitions (don't have to be part of
+ flash device node)
+2. Dropped "to Linux" as bindings are meant to be os agnostic.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Link: https://lore.kernel.org/r/20201210172352.31632-1-zajec5@gmail.com
+Signed-off-by: Rob Herring <robh@kernel.org>
+---
+ .../devicetree/bindings/mtd/partition.txt | 131 +--------------
+ .../mtd/partitions/fixed-partitions.yaml | 152 ++++++++++++++++++
+ 2 files changed, 154 insertions(+), 129 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/mtd/partitions/fixed-partitions.yaml
+
+--- a/Documentation/devicetree/bindings/mtd/partition.txt
++++ b/Documentation/devicetree/bindings/mtd/partition.txt
+@@ -24,137 +24,10 @@ another partitioning method.
+ Available bindings are listed in the "partitions" subdirectory.
+
+
+-Fixed Partitions
+-================
+-
+-Partitions can be represented by sub-nodes of a flash device. This can be used
+-on platforms which have strong conventions about which portions of a flash are
+-used for what purposes, but which don't use an on-flash partition table such
+-as RedBoot.
+-
+-The partition table should be a subnode of the flash node and should be named
+-'partitions'. This node should have the following property:
+-- compatible : (required) must be "fixed-partitions"
+-Partitions are then defined in subnodes of the partitions node.
++Deprecated: partitions defined in flash node
++============================================
+
+ For backwards compatibility partitions as direct subnodes of the flash device are
+ supported. This use is discouraged.
+ NOTE: also for backwards compatibility, direct subnodes that have a compatible
+ string are not considered partitions, as they may be used for other bindings.
+-
+-#address-cells & #size-cells must both be present in the partitions subnode of the
+-flash device. There are two valid values for both:
+-<1>: for partitions that require a single 32-bit cell to represent their
+- size/address (aka the value is below 4 GiB)
+-<2>: for partitions that require two 32-bit cells to represent their
+- size/address (aka the value is 4 GiB or greater).
+-
+-Required properties:
+-- reg : The partition's offset and size within the flash
+-
+-Optional properties:
+-- label : The label / name for this partition. If omitted, the label is taken
+- from the node name (excluding the unit address).
+-- read-only : This parameter, if present, is a hint to Linux that this
+- partition should only be mounted read-only. This is usually used for flash
+- partitions containing early-boot firmware images or data which should not be
+- clobbered.
+-- lock : Do not unlock the partition at initialization time (not supported on
+- all devices)
+-- slc-mode: This parameter, if present, allows one to emulate SLC mode on a
+- partition attached to an MLC NAND thus making this partition immune to
+- paired-pages corruptions
+-
+-Examples:
+-
+-
+-flash@0 {
+- partitions {
+- compatible = "fixed-partitions";
+- #address-cells = <1>;
+- #size-cells = <1>;
+-
+- partition@0 {
+- label = "u-boot";
+- reg = <0x0000000 0x100000>;
+- read-only;
+- };
+-
+- uimage@100000 {
+- reg = <0x0100000 0x200000>;
+- };
+- };
+-};
+-
+-flash@1 {
+- partitions {
+- compatible = "fixed-partitions";
+- #address-cells = <1>;
+- #size-cells = <2>;
+-
+- /* a 4 GiB partition */
+- partition@0 {
+- label = "filesystem";
+- reg = <0x00000000 0x1 0x00000000>;
+- };
+- };
+-};
+-
+-flash@2 {
+- partitions {
+- compatible = "fixed-partitions";
+- #address-cells = <2>;
+- #size-cells = <2>;
+-
+- /* an 8 GiB partition */
+- partition@0 {
+- label = "filesystem #1";
+- reg = <0x0 0x00000000 0x2 0x00000000>;
+- };
+-
+- /* a 4 GiB partition */
+- partition@200000000 {
+- label = "filesystem #2";
+- reg = <0x2 0x00000000 0x1 0x00000000>;
+- };
+- };
+-};
+-
+-flash@3 {
+- partitions {
+- compatible = "fixed-partitions";
+- #address-cells = <1>;
+- #size-cells = <1>;
+-
+- partition@0 {
+- label = "bootloader";
+- reg = <0x000000 0x100000>;
+- read-only;
+- };
+-
+- firmware@100000 {
+- label = "firmware";
+- reg = <0x100000 0xe00000>;
+- compatible = "brcm,trx";
+- };
+-
+- calibration@f00000 {
+- label = "calibration";
+- reg = <0xf00000 0x100000>;
+- compatible = "fixed-partitions";
+- ranges = <0 0xf00000 0x100000>;
+- #address-cells = <1>;
+- #size-cells = <1>;
+-
+- partition@0 {
+- label = "wifi0";
+- reg = <0x000000 0x080000>;
+- };
+-
+- partition@80000 {
+- label = "wifi1";
+- reg = <0x080000 0x080000>;
+- };
+- };
+- };
+-};
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/partitions/fixed-partitions.yaml
+@@ -0,0 +1,152 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mtd/partitions/fixed-partitions.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Fixed partitions
++
++description: |
++ This binding can be used on platforms which have strong conventions about
++ which portions of a flash are used for what purposes, but which don't use an
++ on-flash partition table such as RedBoot.
++
++ The partition table should be a node named "partitions". Partitions are then
++ defined as subnodes.
++
++maintainers:
++ - Rafał Miłecki <rafal@milecki.pl>
++
++properties:
++ compatible:
++ const: fixed-partitions
++
++ "#address-cells": true
++
++ "#size-cells": true
++
++patternProperties:
++ "@[0-9a-f]+$":
++ description: node describing a single flash partition
++ type: object
++
++ properties:
++ reg:
++ description: partition's offset and size within the flash
++ maxItems: 1
++
++ label:
++ description: The label / name for this partition. If omitted, the label
++ is taken from the node name (excluding the unit address).
++
++ read-only:
++ description: This parameter, if present, is a hint that this partition
++ should only be mounted read-only. This is usually used for flash
++ partitions containing early-boot firmware images or data which should
++ not be clobbered.
++ type: boolean
++
++ lock:
++ description: Do not unlock the partition at initialization time (not
++ supported on all devices)
++ type: boolean
++
++ slc-mode:
++ description: This parameter, if present, allows one to emulate SLC mode
++ on a partition attached to an MLC NAND thus making this partition
++ immune to paired-pages corruptions
++ type: boolean
++
++ required:
++ - reg
++
++required:
++ - "#address-cells"
++ - "#size-cells"
++
++additionalProperties: true
++
++examples:
++ - |
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ label = "u-boot";
++ reg = <0x0000000 0x100000>;
++ read-only;
++ };
++
++ uimage@100000 {
++ reg = <0x0100000 0x200000>;
++ };
++ };
++ - |
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <2>;
++
++ /* a 4 GiB partition */
++ partition@0 {
++ label = "filesystem";
++ reg = <0x00000000 0x1 0x00000000>;
++ };
++ };
++ - |
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <2>;
++ #size-cells = <2>;
++
++ /* an 8 GiB partition */
++ partition@0 {
++ label = "filesystem #1";
++ reg = <0x0 0x00000000 0x2 0x00000000>;
++ };
++
++ /* a 4 GiB partition */
++ partition@200000000 {
++ label = "filesystem #2";
++ reg = <0x2 0x00000000 0x1 0x00000000>;
++ };
++ };
++ - |
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ label = "bootloader";
++ reg = <0x000000 0x100000>;
++ read-only;
++ };
++
++ firmware@100000 {
++ compatible = "brcm,trx";
++ label = "firmware";
++ reg = <0x100000 0xe00000>;
++ };
++
++ calibration@f00000 {
++ compatible = "fixed-partitions";
++ label = "calibration";
++ reg = <0xf00000 0x100000>;
++ ranges = <0 0xf00000 0x100000>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ label = "wifi0";
++ reg = <0x000000 0x080000>;
++ };
++
++ partition@80000 {
++ label = "wifi1";
++ reg = <0x080000 0x080000>;
++ };
++ };
++ };
--- /dev/null
+From 6418522022c706fd867b00b2571edba48b8fa8c7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 11 Feb 2021 23:04:25 +0100
+Subject: [PATCH] dt-bindings: mtd: move partition binding to its own file
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Single partition binding is quite common and may be:
+1. Used by multiple parsers
+2. Extended for more specific cases
+
+Move it to separated file to avoid code duplication.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ .../mtd/partitions/fixed-partitions.yaml | 33 +------------
+ .../bindings/mtd/partitions/partition.yaml | 47 +++++++++++++++++++
+ 2 files changed, 48 insertions(+), 32 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/mtd/partitions/partition.yaml
+
+--- a/Documentation/devicetree/bindings/mtd/partitions/fixed-partitions.yaml
++++ b/Documentation/devicetree/bindings/mtd/partitions/fixed-partitions.yaml
+@@ -27,38 +27,7 @@ properties:
+
+ patternProperties:
+ "@[0-9a-f]+$":
+- description: node describing a single flash partition
+- type: object
+-
+- properties:
+- reg:
+- description: partition's offset and size within the flash
+- maxItems: 1
+-
+- label:
+- description: The label / name for this partition. If omitted, the label
+- is taken from the node name (excluding the unit address).
+-
+- read-only:
+- description: This parameter, if present, is a hint that this partition
+- should only be mounted read-only. This is usually used for flash
+- partitions containing early-boot firmware images or data which should
+- not be clobbered.
+- type: boolean
+-
+- lock:
+- description: Do not unlock the partition at initialization time (not
+- supported on all devices)
+- type: boolean
+-
+- slc-mode:
+- description: This parameter, if present, allows one to emulate SLC mode
+- on a partition attached to an MLC NAND thus making this partition
+- immune to paired-pages corruptions
+- type: boolean
+-
+- required:
+- - reg
++ $ref: "partition.yaml#"
+
+ required:
+ - "#address-cells"
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/partitions/partition.yaml
+@@ -0,0 +1,47 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mtd/partitions/partition.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Partition
++
++description: |
++ This binding describes a single flash partition. Each partition must have its
++ relative offset and size specified. Depending on partition function extra
++ properties can be used.
++
++maintainers:
++ - Rafał Miłecki <rafal@milecki.pl>
++
++properties:
++ reg:
++ description: partition's offset and size within the flash
++ maxItems: 1
++
++ label:
++ description: The label / name for this partition. If omitted, the label
++ is taken from the node name (excluding the unit address).
++
++ read-only:
++ description: This parameter, if present, is a hint that this partition
++ should only be mounted read-only. This is usually used for flash
++ partitions containing early-boot firmware images or data which should
++ not be clobbered.
++ type: boolean
++
++ lock:
++ description: Do not unlock the partition at initialization time (not
++ supported on all devices)
++ type: boolean
++
++ slc-mode:
++ description: This parameter, if present, allows one to emulate SLC mode
++ on a partition attached to an MLC NAND thus making this partition
++ immune to paired-pages corruptions
++ type: boolean
++
++required:
++ - reg
++
++additionalProperties: true
--- /dev/null
+From 6e9dff6fe3fbc452f16566e4a7e293b0decefdba Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 11 Feb 2021 23:04:26 +0100
+Subject: [PATCH] dt-bindings: mtd: add binding for BCM4908 partitions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+BCM4908 uses fixed partitions layout but function of some partitions may
+vary. Some devices use multiple firmware partitions and those partitions
+should be marked to let system discover their purpose.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ .../partitions/brcm,bcm4908-partitions.yaml | 70 +++++++++++++++++++
+ 1 file changed, 70 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/partitions/brcm,bcm4908-partitions.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/partitions/brcm,bcm4908-partitions.yaml
+@@ -0,0 +1,70 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mtd/partitions/brcm,bcm4908-partitions.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom BCM4908 partitioning
++
++description: |
++ Broadcom BCM4908 CFE bootloader supports two firmware partitions. One is used
++ for regular booting, the other is treated as fallback.
++
++ This binding allows defining all fixed partitions and marking those containing
++ firmware. System can use that information e.g. for booting or flashing
++ purposes.
++
++maintainers:
++ - Rafał Miłecki <rafal@milecki.pl>
++
++properties:
++ compatible:
++ const: brcm,bcm4908-partitions
++
++ "#address-cells":
++ enum: [ 1, 2 ]
++
++ "#size-cells":
++ enum: [ 1, 2 ]
++
++patternProperties:
++ "^partition@[0-9a-f]+$":
++ $ref: "partition.yaml#"
++ properties:
++ compatible:
++ const: brcm,bcm4908-firmware
++ unevaluatedProperties: false
++
++required:
++ - "#address-cells"
++ - "#size-cells"
++
++additionalProperties: false
++
++examples:
++ - |
++ partitions {
++ compatible = "brcm,bcm4908-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ label = "cferom";
++ reg = <0x0 0x100000>;
++ };
++
++ partition@100000 {
++ compatible = "brcm,bcm4908-firmware";
++ reg = <0x100000 0xf00000>;
++ };
++
++ partition@1000000 {
++ compatible = "brcm,bcm4908-firmware";
++ reg = <0x1000000 0xf00000>;
++ };
++
++ partition@1f00000 {
++ label = "calibration";
++ reg = <0x1f00000 0x100000>;
++ };
++ };
--- /dev/null
+From afbef8efb591792579c633a7c545f914c6165f82 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 11 Feb 2021 23:04:27 +0100
+Subject: [PATCH] mtd: parsers: ofpart: support BCM4908 fixed partitions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Some devices use fixed partitioning with some partitions requiring some
+extra logic. E.g. BCM4908 may have multiple firmware partitions but
+detecting currently used one requires checking bootloader parameters.
+
+To support such cases without duplicating a lot of code (without copying
+most of the ofpart.c code) support for post-parsing callback was added.
+
+BCM4908 support in ofpart can be enabled using config option and results
+in compiling & executing a specific callback. It simply reads offset of
+currently used firmware partition from the DT. Bootloader specifies it
+using the "brcm_blparms" property.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+---
+ drivers/mtd/parsers/Kconfig | 9 +++
+ drivers/mtd/parsers/Makefile | 2 +
+ drivers/mtd/parsers/ofpart_bcm4908.c | 64 +++++++++++++++++++
+ drivers/mtd/parsers/ofpart_bcm4908.h | 15 +++++
+ .../mtd/parsers/{ofpart.c => ofpart_core.c} | 28 +++++++-
+ 5 files changed, 116 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/mtd/parsers/ofpart_bcm4908.c
+ create mode 100644 drivers/mtd/parsers/ofpart_bcm4908.h
+ rename drivers/mtd/parsers/{ofpart.c => ofpart_core.c} (88%)
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -67,6 +67,15 @@ config MTD_OF_PARTS
+ flash memory node, as described in
+ Documentation/devicetree/bindings/mtd/partition.txt.
+
++config MTD_OF_PARTS_BCM4908
++ bool "BCM4908 partitioning support"
++ depends on MTD_OF_PARTS && (ARCH_BCM4908 || COMPILE_TEST)
++ default ARCH_BCM4908
++ help
++ This provides partitions parser for BCM4908 family devices
++ that can have multiple "firmware" partitions. It takes care of
++ finding currently used one and backup ones.
++
+ config MTD_PARSER_IMAGETAG
+ tristate "Parser for BCM963XX Image Tag format partitions"
+ depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST
+--- a/drivers/mtd/parsers/Makefile
++++ b/drivers/mtd/parsers/Makefile
+@@ -4,6 +4,8 @@ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm4
+ obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
+ obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
+ obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
++ofpart-y += ofpart_core.o
++ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o
+ obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
+ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
+ obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
+--- /dev/null
++++ b/drivers/mtd/parsers/ofpart_bcm4908.c
+@@ -0,0 +1,64 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/of.h>
++#include <linux/mtd/mtd.h>
++#include <linux/slab.h>
++#include <linux/mtd/partitions.h>
++
++#include "ofpart_bcm4908.h"
++
++#define BLPARAMS_FW_OFFSET "NAND_RFS_OFS"
++
++static long long bcm4908_partitions_fw_offset(void)
++{
++ struct device_node *root;
++ struct property *prop;
++ const char *s;
++
++ root = of_find_node_by_path("/");
++ if (!root)
++ return -ENOENT;
++
++ of_property_for_each_string(root, "brcm_blparms", prop, s) {
++ size_t len = strlen(BLPARAMS_FW_OFFSET);
++ unsigned long offset;
++ int err;
++
++ if (strncmp(s, BLPARAMS_FW_OFFSET, len) || s[len] != '=')
++ continue;
++
++ err = kstrtoul(s + len + 1, 0, &offset);
++ if (err) {
++ pr_err("failed to parse %s\n", s + len + 1);
++ return err;
++ }
++
++ return offset << 10;
++ }
++
++ return -ENOENT;
++}
++
++int bcm4908_partitions_post_parse(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts)
++{
++ long long fw_offset;
++ int i;
++
++ fw_offset = bcm4908_partitions_fw_offset();
++
++ for (i = 0; i < nr_parts; i++) {
++ if (of_device_is_compatible(parts[i].of_node, "brcm,bcm4908-firmware")) {
++ if (fw_offset < 0 || parts[i].offset == fw_offset)
++ parts[i].name = "firmware";
++ else
++ parts[i].name = "backup";
++ }
++ }
++
++ return 0;
++}
+--- /dev/null
++++ b/drivers/mtd/parsers/ofpart_bcm4908.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef __BCM4908_PARTITIONS_H
++#define __BCM4908_PARTITIONS_H
++
++#ifdef CONFIG_MTD_OF_PARTS_BCM4908
++int bcm4908_partitions_post_parse(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts);
++#else
++static inline int bcm4908_partitions_post_parse(struct mtd_info *mtd, struct mtd_partition *parts,
++ int nr_parts)
++{
++ return -EOPNOTSUPP;
++}
++#endif
++
++#endif
+--- a/drivers/mtd/parsers/ofpart.c
++++ /dev/null
+@@ -1,239 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Flash partitions described by the OF (or flattened) device tree
+- *
+- * Copyright © 2006 MontaVista Software Inc.
+- * Author: Vitaly Wool <vwool@ru.mvista.com>
+- *
+- * Revised to handle newer style flash binding by:
+- * Copyright © 2007 David Gibson, IBM Corporation.
+- */
+-
+-#include <linux/module.h>
+-#include <linux/init.h>
+-#include <linux/of.h>
+-#include <linux/mtd/mtd.h>
+-#include <linux/slab.h>
+-#include <linux/mtd/partitions.h>
+-
+-static bool node_has_compatible(struct device_node *pp)
+-{
+- return of_get_property(pp, "compatible", NULL);
+-}
+-
+-static int parse_fixed_partitions(struct mtd_info *master,
+- const struct mtd_partition **pparts,
+- struct mtd_part_parser_data *data)
+-{
+- struct mtd_partition *parts;
+- struct device_node *mtd_node;
+- struct device_node *ofpart_node;
+- const char *partname;
+- struct device_node *pp;
+- int nr_parts, i, ret = 0;
+- bool dedicated = true;
+-
+-
+- /* Pull of_node from the master device node */
+- mtd_node = mtd_get_of_node(master);
+- if (!mtd_node)
+- return 0;
+-
+- ofpart_node = of_get_child_by_name(mtd_node, "partitions");
+- if (!ofpart_node) {
+- /*
+- * We might get here even when ofpart isn't used at all (e.g.,
+- * when using another parser), so don't be louder than
+- * KERN_DEBUG
+- */
+- pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n",
+- master->name, mtd_node);
+- ofpart_node = mtd_node;
+- dedicated = false;
+- } else if (!of_device_is_compatible(ofpart_node, "fixed-partitions")) {
+- /* The 'partitions' subnode might be used by another parser */
+- return 0;
+- }
+-
+- /* First count the subnodes */
+- nr_parts = 0;
+- for_each_child_of_node(ofpart_node, pp) {
+- if (!dedicated && node_has_compatible(pp))
+- continue;
+-
+- nr_parts++;
+- }
+-
+- if (nr_parts == 0)
+- return 0;
+-
+- parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);
+- if (!parts)
+- return -ENOMEM;
+-
+- i = 0;
+- for_each_child_of_node(ofpart_node, pp) {
+- const __be32 *reg;
+- int len;
+- int a_cells, s_cells;
+-
+- if (!dedicated && node_has_compatible(pp))
+- continue;
+-
+- reg = of_get_property(pp, "reg", &len);
+- if (!reg) {
+- if (dedicated) {
+- pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n",
+- master->name, pp,
+- mtd_node);
+- goto ofpart_fail;
+- } else {
+- nr_parts--;
+- continue;
+- }
+- }
+-
+- a_cells = of_n_addr_cells(pp);
+- s_cells = of_n_size_cells(pp);
+- if (len / 4 != a_cells + s_cells) {
+- pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n",
+- master->name, pp,
+- mtd_node);
+- goto ofpart_fail;
+- }
+-
+- parts[i].offset = of_read_number(reg, a_cells);
+- parts[i].size = of_read_number(reg + a_cells, s_cells);
+- parts[i].of_node = pp;
+-
+- partname = of_get_property(pp, "label", &len);
+- if (!partname)
+- partname = of_get_property(pp, "name", &len);
+- parts[i].name = partname;
+-
+- if (of_get_property(pp, "read-only", &len))
+- parts[i].mask_flags |= MTD_WRITEABLE;
+-
+- if (of_get_property(pp, "lock", &len))
+- parts[i].mask_flags |= MTD_POWERUP_LOCK;
+-
+- if (of_property_read_bool(pp, "slc-mode"))
+- parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION;
+-
+- i++;
+- }
+-
+- if (!nr_parts)
+- goto ofpart_none;
+-
+- *pparts = parts;
+- return nr_parts;
+-
+-ofpart_fail:
+- pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n",
+- master->name, pp, mtd_node);
+- ret = -EINVAL;
+-ofpart_none:
+- of_node_put(pp);
+- kfree(parts);
+- return ret;
+-}
+-
+-static const struct of_device_id parse_ofpart_match_table[] = {
+- { .compatible = "fixed-partitions" },
+- {},
+-};
+-MODULE_DEVICE_TABLE(of, parse_ofpart_match_table);
+-
+-static struct mtd_part_parser ofpart_parser = {
+- .parse_fn = parse_fixed_partitions,
+- .name = "fixed-partitions",
+- .of_match_table = parse_ofpart_match_table,
+-};
+-
+-static int parse_ofoldpart_partitions(struct mtd_info *master,
+- const struct mtd_partition **pparts,
+- struct mtd_part_parser_data *data)
+-{
+- struct mtd_partition *parts;
+- struct device_node *dp;
+- int i, plen, nr_parts;
+- const struct {
+- __be32 offset, len;
+- } *part;
+- const char *names;
+-
+- /* Pull of_node from the master device node */
+- dp = mtd_get_of_node(master);
+- if (!dp)
+- return 0;
+-
+- part = of_get_property(dp, "partitions", &plen);
+- if (!part)
+- return 0; /* No partitions found */
+-
+- pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp);
+-
+- nr_parts = plen / sizeof(part[0]);
+-
+- parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);
+- if (!parts)
+- return -ENOMEM;
+-
+- names = of_get_property(dp, "partition-names", &plen);
+-
+- for (i = 0; i < nr_parts; i++) {
+- parts[i].offset = be32_to_cpu(part->offset);
+- parts[i].size = be32_to_cpu(part->len) & ~1;
+- /* bit 0 set signifies read only partition */
+- if (be32_to_cpu(part->len) & 1)
+- parts[i].mask_flags = MTD_WRITEABLE;
+-
+- if (names && (plen > 0)) {
+- int len = strlen(names) + 1;
+-
+- parts[i].name = names;
+- plen -= len;
+- names += len;
+- } else {
+- parts[i].name = "unnamed";
+- }
+-
+- part++;
+- }
+-
+- *pparts = parts;
+- return nr_parts;
+-}
+-
+-static struct mtd_part_parser ofoldpart_parser = {
+- .parse_fn = parse_ofoldpart_partitions,
+- .name = "ofoldpart",
+-};
+-
+-static int __init ofpart_parser_init(void)
+-{
+- register_mtd_parser(&ofpart_parser);
+- register_mtd_parser(&ofoldpart_parser);
+- return 0;
+-}
+-
+-static void __exit ofpart_parser_exit(void)
+-{
+- deregister_mtd_parser(&ofpart_parser);
+- deregister_mtd_parser(&ofoldpart_parser);
+-}
+-
+-module_init(ofpart_parser_init);
+-module_exit(ofpart_parser_exit);
+-
+-MODULE_LICENSE("GPL");
+-MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree");
+-MODULE_AUTHOR("Vitaly Wool, David Gibson");
+-/*
+- * When MTD core cannot find the requested parser, it tries to load the module
+- * with the same name. Since we provide the ofoldpart parser, we should have
+- * the corresponding alias.
+- */
+-MODULE_ALIAS("fixed-partitions");
+-MODULE_ALIAS("ofoldpart");
+--- /dev/null
++++ b/drivers/mtd/parsers/ofpart_core.c
+@@ -0,0 +1,263 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Flash partitions described by the OF (or flattened) device tree
++ *
++ * Copyright © 2006 MontaVista Software Inc.
++ * Author: Vitaly Wool <vwool@ru.mvista.com>
++ *
++ * Revised to handle newer style flash binding by:
++ * Copyright © 2007 David Gibson, IBM Corporation.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/of.h>
++#include <linux/mtd/mtd.h>
++#include <linux/slab.h>
++#include <linux/mtd/partitions.h>
++
++#include "ofpart_bcm4908.h"
++
++struct fixed_partitions_quirks {
++ int (*post_parse)(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts);
++};
++
++struct fixed_partitions_quirks bcm4908_partitions_quirks = {
++ .post_parse = bcm4908_partitions_post_parse,
++};
++
++static const struct of_device_id parse_ofpart_match_table[];
++
++static bool node_has_compatible(struct device_node *pp)
++{
++ return of_get_property(pp, "compatible", NULL);
++}
++
++static int parse_fixed_partitions(struct mtd_info *master,
++ const struct mtd_partition **pparts,
++ struct mtd_part_parser_data *data)
++{
++ const struct fixed_partitions_quirks *quirks;
++ const struct of_device_id *of_id;
++ struct mtd_partition *parts;
++ struct device_node *mtd_node;
++ struct device_node *ofpart_node;
++ const char *partname;
++ struct device_node *pp;
++ int nr_parts, i, ret = 0;
++ bool dedicated = true;
++
++ /* Pull of_node from the master device node */
++ mtd_node = mtd_get_of_node(master);
++ if (!mtd_node)
++ return 0;
++
++ ofpart_node = of_get_child_by_name(mtd_node, "partitions");
++ if (!ofpart_node) {
++ /*
++ * We might get here even when ofpart isn't used at all (e.g.,
++ * when using another parser), so don't be louder than
++ * KERN_DEBUG
++ */
++ pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n",
++ master->name, mtd_node);
++ ofpart_node = mtd_node;
++ dedicated = false;
++ }
++
++ of_id = of_match_node(parse_ofpart_match_table, ofpart_node);
++ if (dedicated && !of_id) {
++ /* The 'partitions' subnode might be used by another parser */
++ return 0;
++ }
++
++ quirks = of_id ? of_id->data : NULL;
++
++ /* First count the subnodes */
++ nr_parts = 0;
++ for_each_child_of_node(ofpart_node, pp) {
++ if (!dedicated && node_has_compatible(pp))
++ continue;
++
++ nr_parts++;
++ }
++
++ if (nr_parts == 0)
++ return 0;
++
++ parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);
++ if (!parts)
++ return -ENOMEM;
++
++ i = 0;
++ for_each_child_of_node(ofpart_node, pp) {
++ const __be32 *reg;
++ int len;
++ int a_cells, s_cells;
++
++ if (!dedicated && node_has_compatible(pp))
++ continue;
++
++ reg = of_get_property(pp, "reg", &len);
++ if (!reg) {
++ if (dedicated) {
++ pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n",
++ master->name, pp,
++ mtd_node);
++ goto ofpart_fail;
++ } else {
++ nr_parts--;
++ continue;
++ }
++ }
++
++ a_cells = of_n_addr_cells(pp);
++ s_cells = of_n_size_cells(pp);
++ if (len / 4 != a_cells + s_cells) {
++ pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n",
++ master->name, pp,
++ mtd_node);
++ goto ofpart_fail;
++ }
++
++ parts[i].offset = of_read_number(reg, a_cells);
++ parts[i].size = of_read_number(reg + a_cells, s_cells);
++ parts[i].of_node = pp;
++
++ partname = of_get_property(pp, "label", &len);
++ if (!partname)
++ partname = of_get_property(pp, "name", &len);
++ parts[i].name = partname;
++
++ if (of_get_property(pp, "read-only", &len))
++ parts[i].mask_flags |= MTD_WRITEABLE;
++
++ if (of_get_property(pp, "lock", &len))
++ parts[i].mask_flags |= MTD_POWERUP_LOCK;
++
++ if (of_property_read_bool(pp, "slc-mode"))
++ parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION;
++
++ i++;
++ }
++
++ if (!nr_parts)
++ goto ofpart_none;
++
++ if (quirks && quirks->post_parse)
++ quirks->post_parse(master, parts, nr_parts);
++
++ *pparts = parts;
++ return nr_parts;
++
++ofpart_fail:
++ pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n",
++ master->name, pp, mtd_node);
++ ret = -EINVAL;
++ofpart_none:
++ of_node_put(pp);
++ kfree(parts);
++ return ret;
++}
++
++static const struct of_device_id parse_ofpart_match_table[] = {
++ /* Generic */
++ { .compatible = "fixed-partitions" },
++ /* Customized */
++ { .compatible = "brcm,bcm4908-partitions", .data = &bcm4908_partitions_quirks, },
++ {},
++};
++MODULE_DEVICE_TABLE(of, parse_ofpart_match_table);
++
++static struct mtd_part_parser ofpart_parser = {
++ .parse_fn = parse_fixed_partitions,
++ .name = "fixed-partitions",
++ .of_match_table = parse_ofpart_match_table,
++};
++
++static int parse_ofoldpart_partitions(struct mtd_info *master,
++ const struct mtd_partition **pparts,
++ struct mtd_part_parser_data *data)
++{
++ struct mtd_partition *parts;
++ struct device_node *dp;
++ int i, plen, nr_parts;
++ const struct {
++ __be32 offset, len;
++ } *part;
++ const char *names;
++
++ /* Pull of_node from the master device node */
++ dp = mtd_get_of_node(master);
++ if (!dp)
++ return 0;
++
++ part = of_get_property(dp, "partitions", &plen);
++ if (!part)
++ return 0; /* No partitions found */
++
++ pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp);
++
++ nr_parts = plen / sizeof(part[0]);
++
++ parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);
++ if (!parts)
++ return -ENOMEM;
++
++ names = of_get_property(dp, "partition-names", &plen);
++
++ for (i = 0; i < nr_parts; i++) {
++ parts[i].offset = be32_to_cpu(part->offset);
++ parts[i].size = be32_to_cpu(part->len) & ~1;
++ /* bit 0 set signifies read only partition */
++ if (be32_to_cpu(part->len) & 1)
++ parts[i].mask_flags = MTD_WRITEABLE;
++
++ if (names && (plen > 0)) {
++ int len = strlen(names) + 1;
++
++ parts[i].name = names;
++ plen -= len;
++ names += len;
++ } else {
++ parts[i].name = "unnamed";
++ }
++
++ part++;
++ }
++
++ *pparts = parts;
++ return nr_parts;
++}
++
++static struct mtd_part_parser ofoldpart_parser = {
++ .parse_fn = parse_ofoldpart_partitions,
++ .name = "ofoldpart",
++};
++
++static int __init ofpart_parser_init(void)
++{
++ register_mtd_parser(&ofpart_parser);
++ register_mtd_parser(&ofoldpart_parser);
++ return 0;
++}
++
++static void __exit ofpart_parser_exit(void)
++{
++ deregister_mtd_parser(&ofpart_parser);
++ deregister_mtd_parser(&ofoldpart_parser);
++}
++
++module_init(ofpart_parser_init);
++module_exit(ofpart_parser_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree");
++MODULE_AUTHOR("Vitaly Wool, David Gibson");
++/*
++ * When MTD core cannot find the requested parser, it tries to load the module
++ * with the same name. Since we provide the ofoldpart parser, we should have
++ * the corresponding alias.
++ */
++MODULE_ALIAS("fixed-partitions");
++MODULE_ALIAS("ofoldpart");
--- /dev/null
+From 2d751203aacf86a1b301a188d8551c7da91043ab Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 2 Mar 2021 20:00:12 +0100
+Subject: [PATCH] mtd: parsers: ofpart: limit parsing of deprecated DT syntax
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+For backward compatibility ofpart still supports the old syntax like:
+spi-flash@0 {
+ compatible = "jedec,spi-nor";
+ reg = <0x0>;
+
+ partition@0 {
+ label = "bootloader";
+ reg = <0x0 0x100000>;
+ };
+};
+(without "partitions" subnode).
+
+There is no reason however to support nested partitions without a clear
+"compatible" string like:
+partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ label = "bootloader";
+ reg = <0x0 0x100000>;
+
+ partition@0 {
+ label = "config";
+ reg = <0x80000 0x80000>;
+ };
+ };
+};
+(we never officially supported or documented that).
+
+Make sure ofpart doesn't attempt to parse above.
+
+Cc: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20210302190012.1255-1-zajec5@gmail.com
+---
+ drivers/mtd/parsers/ofpart_core.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/parsers/ofpart_core.c
++++ b/drivers/mtd/parsers/ofpart_core.c
+@@ -53,7 +53,7 @@ static int parse_fixed_partitions(struct
+ return 0;
+
+ ofpart_node = of_get_child_by_name(mtd_node, "partitions");
+- if (!ofpart_node) {
++ if (!ofpart_node && !master->parent) {
+ /*
+ * We might get here even when ofpart isn't used at all (e.g.,
+ * when using another parser), so don't be louder than
+@@ -64,6 +64,8 @@ static int parse_fixed_partitions(struct
+ ofpart_node = mtd_node;
+ dedicated = false;
+ }
++ if (!ofpart_node)
++ return 0;
+
+ of_id = of_match_node(parse_ofpart_match_table, ofpart_node);
+ if (dedicated && !of_id) {
--- /dev/null
+From b87b6d2d6f540e29c3f98e1572d64e560d73d6c1 Mon Sep 17 00:00:00 2001
+From: Wei Yongjun <weiyongjun1@huawei.com>
+Date: Thu, 4 Mar 2021 06:46:00 +0000
+Subject: [PATCH] mtd: parsers: ofpart: make symbol 'bcm4908_partitions_quirks'
+ static
+
+The sparse tool complains as follows:
+
+drivers/mtd/parsers/ofpart_core.c:25:32: warning:
+ symbol 'bcm4908_partitions_quirks' was not declared. Should it be static?
+
+This symbol is not used outside of ofpart_core.c, so this
+commit marks it static.
+
+Fixes: 457da931b608 ("mtd: parsers: ofpart: support BCM4908 fixed partitions")
+Reported-by: Hulk Robot <hulkci@huawei.com>
+Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20210304064600.3279138-1-weiyongjun1@huawei.com
+---
+ drivers/mtd/parsers/ofpart_core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/parsers/ofpart_core.c
++++ b/drivers/mtd/parsers/ofpart_core.c
+@@ -22,7 +22,7 @@ struct fixed_partitions_quirks {
+ int (*post_parse)(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts);
+ };
+
+-struct fixed_partitions_quirks bcm4908_partitions_quirks = {
++static struct fixed_partitions_quirks bcm4908_partitions_quirks = {
+ .post_parse = bcm4908_partitions_post_parse,
+ };
+
--- /dev/null
+From a5d83d6e2bc747b13f347962d4b335d70b23559b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 12 Mar 2021 07:28:19 +0100
+Subject: [PATCH] mtd: core: add nvmem-cells compatible to parse mtd as nvmem
+ cells
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Partitions that contains the nvmem-cells compatible will register
+their direct subonodes as nvmem cells and the node will be treated as a
+nvmem provider.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Tested-by: Rafał Miłecki <rafal@milecki.pl>
+---
+ drivers/mtd/mtdcore.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -531,6 +531,7 @@ static int mtd_nvmem_reg_read(void *priv
+
+ static int mtd_nvmem_add(struct mtd_info *mtd)
+ {
++ struct device_node *node = mtd_get_of_node(mtd);
+ struct nvmem_config config = {};
+
+ config.id = -1;
+@@ -543,7 +544,7 @@ static int mtd_nvmem_add(struct mtd_info
+ config.stride = 1;
+ config.read_only = true;
+ config.root_only = true;
+- config.no_of_node = true;
++ config.no_of_node = !of_device_is_compatible(node, "nvmem-cells");
+ config.priv = mtd;
+
+ mtd->nvmem = nvmem_register(&config);
--- /dev/null
+From 42645976c3289b03a12f1bd2bc131fd98fc27170 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 12 Mar 2021 07:28:20 +0100
+Subject: [PATCH] devicetree: nvmem: nvmem: drop $nodename restriction
+
+Drop $nodename restriction as now mtd partition can also be used as
+nvmem provider.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+---
+ Documentation/devicetree/bindings/nvmem/nvmem.yaml | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/Documentation/devicetree/bindings/nvmem/nvmem.yaml
++++ b/Documentation/devicetree/bindings/nvmem/nvmem.yaml
+@@ -20,9 +20,6 @@ description: |
+ storage device.
+
+ properties:
+- $nodename:
+- pattern: "^(eeprom|efuse|nvram)(@.*|-[0-9a-f])*$"
+-
+ "#address-cells":
+ const: 1
+
--- /dev/null
+From 377aa0135dc8489312edd3184d143ce3a89ff7ee Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 12 Mar 2021 07:28:21 +0100
+Subject: [PATCH] dt-bindings: mtd: Document use of nvmem-cells compatible
+
+Document nvmem-cells compatible used to treat mtd partitions as a
+nvmem provider.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ .../bindings/mtd/partitions/nvmem-cells.yaml | 99 +++++++++++++++++++
+ 1 file changed, 99 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/partitions/nvmem-cells.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/partitions/nvmem-cells.yaml
+@@ -0,0 +1,99 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mtd/partitions/nvmem-cells.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Nvmem cells
++
++description: |
++ Any partition containing the compatible "nvmem-cells" will register as a
++ nvmem provider.
++ Each direct subnodes represents a nvmem cell following the nvmem binding.
++ Nvmem binding to declare nvmem-cells can be found in:
++ Documentation/devicetree/bindings/nvmem/nvmem.yaml
++
++maintainers:
++ - Ansuel Smith <ansuelsmth@gmail.com>
++
++allOf:
++ - $ref: /schemas/nvmem/nvmem.yaml#
++
++properties:
++ compatible:
++ const: nvmem-cells
++
++required:
++ - compatible
++
++additionalProperties: true
++
++examples:
++ - |
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ /* ... */
++
++ };
++ art: art@1200000 {
++ compatible = "nvmem-cells";
++ reg = <0x1200000 0x0140000>;
++ label = "art";
++ read-only;
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ macaddr_gmac1: macaddr_gmac1@0 {
++ reg = <0x0 0x6>;
++ };
++
++ macaddr_gmac2: macaddr_gmac2@6 {
++ reg = <0x6 0x6>;
++ };
++
++ pre_cal_24g: pre_cal_24g@1000 {
++ reg = <0x1000 0x2f20>;
++ };
++
++ pre_cal_5g: pre_cal_5g@5000{
++ reg = <0x5000 0x2f20>;
++ };
++ };
++ - |
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ label = "bootloader";
++ reg = <0x000000 0x100000>;
++ read-only;
++ };
++
++ firmware@100000 {
++ compatible = "brcm,trx";
++ label = "firmware";
++ reg = <0x100000 0xe00000>;
++ };
++
++ calibration@f00000 {
++ compatible = "nvmem-cells";
++ label = "calibration";
++ reg = <0xf00000 0x100000>;
++ ranges = <0 0xf00000 0x100000>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ wifi0@0 {
++ reg = <0x000000 0x080000>;
++ };
++
++ wifi1@80000 {
++ reg = <0x080000 0x080000>;
++ };
++ };
++ };
--- /dev/null
+From 2fa7294175c76e1ec568aa75c1891fd908728c8d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 12 Mar 2021 14:49:18 +0100
+Subject: [PATCH] dt-bindings: mtd: add binding for Linksys Northstar
+ partitions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Linksys on Broadcom Northstar devices uses fixed flash layout with
+multiple firmware partitions.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20210312134919.7767-1-zajec5@gmail.com
+---
+ .../mtd/partitions/linksys,ns-partitions.yaml | 74 +++++++++++++++++++
+ 1 file changed, 74 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/partitions/linksys,ns-partitions.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/partitions/linksys,ns-partitions.yaml
+@@ -0,0 +1,74 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mtd/partitions/linksys,ns-partitions.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Linksys Northstar partitioning
++
++description: |
++ Linksys devices based on Broadcom Northstar architecture often use two
++ firmware partitions. One is used for regular booting, the other is treated as
++ fallback.
++
++ This binding allows defining all fixed partitions and marking those containing
++ firmware. System can use that information e.g. for booting or flashing
++ purposes.
++
++maintainers:
++ - Rafał Miłecki <rafal@milecki.pl>
++
++properties:
++ compatible:
++ const: linksys,ns-partitions
++
++ "#address-cells":
++ enum: [ 1, 2 ]
++
++ "#size-cells":
++ enum: [ 1, 2 ]
++
++patternProperties:
++ "^partition@[0-9a-f]+$":
++ $ref: "partition.yaml#"
++ properties:
++ compatible:
++ items:
++ - const: linksys,ns-firmware
++ - const: brcm,trx
++ unevaluatedProperties: false
++
++required:
++ - "#address-cells"
++ - "#size-cells"
++
++additionalProperties: false
++
++examples:
++ - |
++ partitions {
++ compatible = "linksys,ns-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ label = "boot";
++ reg = <0x0 0x100000>;
++ read-only;
++ };
++
++ partition@100000 {
++ label = "nvram";
++ reg = <0x100000 0x100000>;
++ };
++
++ partition@200000 {
++ compatible = "linksys,ns-firmware", "brcm,trx";
++ reg = <0x200000 0xf00000>;
++ };
++
++ partition@1100000 {
++ compatible = "linksys,ns-firmware", "brcm,trx";
++ reg = <0x1100000 0xf00000>;
++ };
++ };
--- /dev/null
+From 7134a2d026d942210b4d26d6059c9d979ca7866e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 12 Mar 2021 14:49:19 +0100
+Subject: [PATCH] mtd: parsers: ofpart: support Linksys Northstar partitions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This allows extending ofpart parser with support for Linksys Northstar
+devices. That support uses recently added quirks mechanism.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20210312134919.7767-2-zajec5@gmail.com
+---
+ drivers/mtd/parsers/Kconfig | 10 +++++
+ drivers/mtd/parsers/Makefile | 1 +
+ drivers/mtd/parsers/ofpart_core.c | 6 +++
+ drivers/mtd/parsers/ofpart_linksys_ns.c | 50 +++++++++++++++++++++++++
+ drivers/mtd/parsers/ofpart_linksys_ns.h | 18 +++++++++
+ 5 files changed, 85 insertions(+)
+ create mode 100644 drivers/mtd/parsers/ofpart_linksys_ns.c
+ create mode 100644 drivers/mtd/parsers/ofpart_linksys_ns.h
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -76,6 +76,16 @@ config MTD_OF_PARTS_BCM4908
+ that can have multiple "firmware" partitions. It takes care of
+ finding currently used one and backup ones.
+
++config MTD_OF_PARTS_LINKSYS_NS
++ bool "Linksys Northstar partitioning support"
++ depends on MTD_OF_PARTS && (ARCH_BCM_5301X || ARCH_BCM4908 || COMPILE_TEST)
++ default ARCH_BCM_5301X
++ help
++ This provides partitions parser for Linksys devices based on Broadcom
++ Northstar architecture. Linksys commonly uses fixed flash layout with
++ two "firmware" partitions. Currently used firmware has to be detected
++ using CFE environment variable.
++
+ config MTD_PARSER_IMAGETAG
+ tristate "Parser for BCM963XX Image Tag format partitions"
+ depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST
+--- a/drivers/mtd/parsers/Makefile
++++ b/drivers/mtd/parsers/Makefile
+@@ -6,6 +6,7 @@ obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdl
+ obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
+ ofpart-y += ofpart_core.o
+ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o
++ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o
+ obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
+ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
+ obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
+--- a/drivers/mtd/parsers/ofpart_core.c
++++ b/drivers/mtd/parsers/ofpart_core.c
+@@ -17,6 +17,7 @@
+ #include <linux/mtd/partitions.h>
+
+ #include "ofpart_bcm4908.h"
++#include "ofpart_linksys_ns.h"
+
+ struct fixed_partitions_quirks {
+ int (*post_parse)(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts);
+@@ -26,6 +27,10 @@ static struct fixed_partitions_quirks bc
+ .post_parse = bcm4908_partitions_post_parse,
+ };
+
++static struct fixed_partitions_quirks linksys_ns_partitions_quirks = {
++ .post_parse = linksys_ns_partitions_post_parse,
++};
++
+ static const struct of_device_id parse_ofpart_match_table[];
+
+ static bool node_has_compatible(struct device_node *pp)
+@@ -167,6 +172,7 @@ static const struct of_device_id parse_o
+ { .compatible = "fixed-partitions" },
+ /* Customized */
+ { .compatible = "brcm,bcm4908-partitions", .data = &bcm4908_partitions_quirks, },
++ { .compatible = "linksys,ns-partitions", .data = &linksys_ns_partitions_quirks, },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, parse_ofpart_match_table);
+--- /dev/null
++++ b/drivers/mtd/parsers/ofpart_linksys_ns.c
+@@ -0,0 +1,50 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
++ */
++
++#include <linux/bcm47xx_nvram.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++
++#include "ofpart_linksys_ns.h"
++
++#define NVRAM_BOOT_PART "bootpartition"
++
++static int ofpart_linksys_ns_bootpartition(void)
++{
++ char buf[4];
++ int bootpartition;
++
++ /* Check CFE environment variable */
++ if (bcm47xx_nvram_getenv(NVRAM_BOOT_PART, buf, sizeof(buf)) > 0) {
++ if (!kstrtoint(buf, 0, &bootpartition))
++ return bootpartition;
++ pr_warn("Failed to parse %s value \"%s\"\n", NVRAM_BOOT_PART,
++ buf);
++ } else {
++ pr_warn("Failed to get NVRAM \"%s\"\n", NVRAM_BOOT_PART);
++ }
++
++ return 0;
++}
++
++int linksys_ns_partitions_post_parse(struct mtd_info *mtd,
++ struct mtd_partition *parts,
++ int nr_parts)
++{
++ int bootpartition = ofpart_linksys_ns_bootpartition();
++ int trx_idx = 0;
++ int i;
++
++ for (i = 0; i < nr_parts; i++) {
++ if (of_device_is_compatible(parts[i].of_node, "linksys,ns-firmware")) {
++ if (trx_idx++ == bootpartition)
++ parts[i].name = "firmware";
++ else
++ parts[i].name = "backup";
++ }
++ }
++
++ return 0;
++}
+--- /dev/null
++++ b/drivers/mtd/parsers/ofpart_linksys_ns.h
+@@ -0,0 +1,18 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef __OFPART_LINKSYS_NS_H
++#define __OFPART_LINKSYS_NS_H
++
++#ifdef CONFIG_MTD_OF_PARTS_LINKSYS_NS
++int linksys_ns_partitions_post_parse(struct mtd_info *mtd,
++ struct mtd_partition *parts,
++ int nr_parts);
++#else
++static inline int linksys_ns_partitions_post_parse(struct mtd_info *mtd,
++ struct mtd_partition *parts,
++ int nr_parts)
++{
++ return -EOPNOTSUPP;
++}
++#endif
++
++#endif
--- /dev/null
+From 7e4404113686868858a34210c28ae122e967aa64 Mon Sep 17 00:00:00 2001
+From: Mauri Sandberg <sandberg@mailfence.com>
+Date: Tue, 9 Mar 2021 19:48:59 +0200
+Subject: [PATCH] mtd: cfi_cmdset_0002: Disable buffered writes for AMD chip
+ 0x2201
+
+Buffer writes do not work with AMD chip 0x2201. The chip in question
+is a AMD/Spansion/Cypress Semiconductor S29GL256N and datasheet [1]
+talks about writing buffers being possible. While waiting for a neater
+solution resort to writing word-sized chunks only.
+
+Without the patch kernel logs will be flooded with entries like below:
+
+jffs2_scan_eraseblock(): End of filesystem marker found at 0x0
+jffs2_build_filesystem(): unlocking the mtd device...
+done.
+jffs2_build_filesystem(): erasing all blocks after the end marker...
+MTD do_write_buffer_wait(): software timeout, address:0x01ec000a.
+jffs2: Write clean marker to block at 0x01920000 failed: -5
+MTD do_write_buffer_wait(): software timeout, address:0x01e2000a.
+jffs2: Write clean marker to block at 0x01880000 failed: -5
+MTD do_write_buffer_wait(): software timeout, address:0x01e0000a.
+jffs2: Write clean marker to block at 0x01860000 failed: -5
+MTD do_write_buffer_wait(): software timeout, address:0x01dc000a.
+jffs2: Write clean marker to block at 0x01820000 failed: -5
+MTD do_write_buffer_wait(): software timeout, address:0x01da000a.
+jffs2: Write clean marker to block at 0x01800000 failed: -5
+...
+
+Tested on a Buffalo wzr-hp-g300nh running kernel 5.10.16.
+
+[1] https://www.cypress.com/file/219941/download
+or https://datasheetspdf.com/pdf-file/565708/SPANSION/S29GL256N/1
+
+Signed-off-by: Mauri Sandberg <sandberg@mailfence.com>
+Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
+Link: https://lore.kernel.org/r/20210309174859.362060-1-sandberg@mailfence.com
+---
+ drivers/mtd/chips/cfi_cmdset_0002.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/mtd/chips/cfi_cmdset_0002.c
++++ b/drivers/mtd/chips/cfi_cmdset_0002.c
+@@ -272,6 +272,10 @@ static void fixup_use_write_buffers(stru
+ {
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
++
++ if (cfi->mfr == CFI_MFR_AMD && cfi->id == 0x2201)
++ return;
++
+ if (cfi->cfiq->BufWriteTimeoutTyp) {
+ pr_debug("Using buffer write method\n");
+ mtd->_write = cfi_amdstd_write_buffers;
--- /dev/null
+From a4d82940ff85a7e307953dfa715f65d5ab487e10 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sun, 18 Apr 2021 23:46:14 +0200
+Subject: dt-bindings: mtd: brcm,trx: Add brcm,trx-magic
+
+This adds the description of an additional property which allows to
+specify a custom partition parser magic to detect a trx partition.
+Buffalo has multiple device which are using the trx format, but with
+different magic values.
+
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+Acked-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20210418214616.239574-2-hauke@hauke-m.de
+---
+ .../devicetree/bindings/mtd/partitions/brcm,trx.txt | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/Documentation/devicetree/bindings/mtd/partitions/brcm,trx.txt
++++ b/Documentation/devicetree/bindings/mtd/partitions/brcm,trx.txt
+@@ -28,6 +28,11 @@ detected by a software parsing TRX heade
+ Required properties:
+ - compatible : (required) must be "brcm,trx"
+
++Optional properties:
++
++- brcm,trx-magic: TRX magic, if it is different from the default magic
++ 0x30524448 as a u32.
++
+ Example:
+
+ flash@0 {
--- /dev/null
+From d7f7e04f8b67571a4bf5a0dcd4f9da4214f5262c Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sun, 18 Apr 2021 23:46:15 +0200
+Subject: mtd: parsers: trx: Allow to specify brcm, trx-magic in DT
+
+Buffalo uses a different TRX magic for every device, to be able to use
+this trx parser, make it possible to specify the TRX magic in device
+tree. If no TRX magic is specified in device tree, the standard value
+will be used. This value should only be specified if a vendor chooses to
+use a non standard TRX magic.
+
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20210418214616.239574-3-hauke@hauke-m.de
+---
+ drivers/mtd/parsers/parser_trx.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/parsers/parser_trx.c
++++ b/drivers/mtd/parsers/parser_trx.c
+@@ -51,13 +51,20 @@ static int parser_trx_parse(struct mtd_i
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+ {
++ struct device_node *np = mtd_get_of_node(mtd);
+ struct mtd_partition *parts;
+ struct mtd_partition *part;
+ struct trx_header trx;
+ size_t bytes_read;
+ uint8_t curr_part = 0, i = 0;
++ uint32_t trx_magic = TRX_MAGIC;
+ int err;
+
++ /* Get different magic from device tree if specified */
++ err = of_property_read_u32(np, "brcm,trx-magic", &trx_magic);
++ if (err != 0 && err != -EINVAL)
++ pr_err("failed to parse \"brcm,trx-magic\" DT attribute, using default: %d\n", err);
++
+ parts = kcalloc(TRX_PARSER_MAX_PARTS, sizeof(struct mtd_partition),
+ GFP_KERNEL);
+ if (!parts)
+@@ -70,7 +77,7 @@ static int parser_trx_parse(struct mtd_i
+ return err;
+ }
+
+- if (trx.magic != TRX_MAGIC) {
++ if (trx.magic != trx_magic) {
+ kfree(parts);
+ return -ENOENT;
+ }
--- /dev/null
+From 81bb218c829246962a6327c64eec18ddcc049936 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sun, 18 Apr 2021 23:46:16 +0200
+Subject: mtd: parsers: trx: Allow to use TRX parser on Mediatek SoCs
+
+Buffalo uses the TRX partition format also on Mediatek MT7622 SoCs.
+
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20210418214616.239574-4-hauke@hauke-m.de
+---
+ drivers/mtd/parsers/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -115,7 +115,7 @@ config MTD_AFS_PARTS
+
+ config MTD_PARSER_TRX
+ tristate "Parser for TRX format partitions"
+- depends on MTD && (BCM47XX || ARCH_BCM_5301X || COMPILE_TEST)
++ depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || COMPILE_TEST)
+ help
+ TRX is a firmware format used by Broadcom on their devices. It
+ may contain up to 3/4 partitions (depending on the version).
--- /dev/null
+From 2365f91c861cbfeef7141c69842848c7b2d3c2db Mon Sep 17 00:00:00 2001
+From: INAGAKI Hiroshi <musashino.open@gmail.com>
+Date: Sun, 13 Feb 2022 15:40:44 +0900
+Subject: [PATCH] mtd: parsers: trx: allow to use on MediaTek MIPS SoCs
+
+Buffalo sells some router devices which have trx-formatted firmware,
+based on MediaTek MIPS SoCs. To use parser_trx on those devices, add
+"RALINK" to dependency and allow to compile for MediaTek MIPS SoCs.
+
+examples:
+
+- WCR-1166DS (MT7628)
+- WSR-1166DHP (MT7621)
+- WSR-2533DHP (MT7621)
+
+Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220213064045.1781-1-musashino.open@gmail.com
+---
+ drivers/mtd/parsers/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -115,7 +115,7 @@ config MTD_AFS_PARTS
+
+ config MTD_PARSER_TRX
+ tristate "Parser for TRX format partitions"
+- depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || COMPILE_TEST)
++ depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST)
+ help
+ TRX is a firmware format used by Broadcom on their devices. It
+ may contain up to 3/4 partitions (depending on the version).
--- /dev/null
+From dcdf415b740923530dc71d89fecc8361078473f5 Mon Sep 17 00:00:00 2001
+From: Rui Salvaterra <rsalvaterra@gmail.com>
+Date: Mon, 5 Apr 2021 16:11:55 +0100
+Subject: [PATCH] ubifs: default to zstd compression
+
+Compared to lzo and zlib, zstd is the best all-around performer, both in terms
+of speed and compression ratio. Set it as the default, if available.
+
+Signed-off-by: Rui Salvaterra <rsalvaterra@gmail.com>
+---
+ fs/ubifs/sb.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/fs/ubifs/sb.c
++++ b/fs/ubifs/sb.c
+@@ -53,6 +53,9 @@
+
+ static int get_default_compressor(struct ubifs_info *c)
+ {
++ if (ubifs_compr_present(c, UBIFS_COMPR_ZSTD))
++ return UBIFS_COMPR_ZSTD;
++
+ if (ubifs_compr_present(c, UBIFS_COMPR_LZO))
+ return UBIFS_COMPR_LZO;
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 8 Feb 2021 11:34:08 -0800
+Subject: [PATCH] net: extract napi poll functionality to __napi_poll()
+
+This commit introduces a new function __napi_poll() which does the main
+logic of the existing napi_poll() function, and will be called by other
+functions in later commits.
+This idea and implementation is done by Felix Fietkau <nbd@nbd.name> and
+is proposed as part of the patch to move napi work to work_queue
+context.
+This commit by itself is a code restructure.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Wei Wang <weiwan@google.com>
+Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -6805,15 +6805,10 @@ void __netif_napi_del(struct napi_struct
+ }
+ EXPORT_SYMBOL(__netif_napi_del);
+
+-static int napi_poll(struct napi_struct *n, struct list_head *repoll)
++static int __napi_poll(struct napi_struct *n, bool *repoll)
+ {
+- void *have;
+ int work, weight;
+
+- list_del_init(&n->poll_list);
+-
+- have = netpoll_poll_lock(n);
+-
+ weight = n->weight;
+
+ /* This NAPI_STATE_SCHED test is for avoiding a race
+@@ -6833,7 +6828,7 @@ static int napi_poll(struct napi_struct
+ n->poll, work, weight);
+
+ if (likely(work < weight))
+- goto out_unlock;
++ return work;
+
+ /* Drivers must not modify the NAPI state if they
+ * consume the entire weight. In such cases this code
+@@ -6842,7 +6837,7 @@ static int napi_poll(struct napi_struct
+ */
+ if (unlikely(napi_disable_pending(n))) {
+ napi_complete(n);
+- goto out_unlock;
++ return work;
+ }
+
+ if (n->gro_bitmask) {
+@@ -6860,12 +6855,29 @@ static int napi_poll(struct napi_struct
+ if (unlikely(!list_empty(&n->poll_list))) {
+ pr_warn_once("%s: Budget exhausted after napi rescheduled\n",
+ n->dev ? n->dev->name : "backlog");
+- goto out_unlock;
++ return work;
+ }
+
+- list_add_tail(&n->poll_list, repoll);
++ *repoll = true;
++
++ return work;
++}
++
++static int napi_poll(struct napi_struct *n, struct list_head *repoll)
++{
++ bool do_repoll = false;
++ void *have;
++ int work;
++
++ list_del_init(&n->poll_list);
++
++ have = netpoll_poll_lock(n);
++
++ work = __napi_poll(n, &do_repoll);
++
++ if (do_repoll)
++ list_add_tail(&n->poll_list, repoll);
+
+-out_unlock:
+ netpoll_poll_unlock(have);
+
+ return work;
--- /dev/null
+From: Wei Wang <weiwan@google.com>
+Date: Mon, 8 Feb 2021 11:34:09 -0800
+Subject: [PATCH] net: implement threaded-able napi poll loop support
+
+This patch allows running each napi poll loop inside its own
+kernel thread.
+The kthread is created during netif_napi_add() if dev->threaded
+is set. And threaded mode is enabled in napi_enable(). We will
+provide a way to set dev->threaded and enable threaded mode
+without a device up/down in the following patch.
+
+Once that threaded mode is enabled and the kthread is
+started, napi_schedule() will wake-up such thread instead
+of scheduling the softirq.
+
+The threaded poll loop behaves quite likely the net_rx_action,
+but it does not have to manipulate local irqs and uses
+an explicit scheduling point based on netdev_budget.
+
+Co-developed-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Co-developed-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
+Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
+Co-developed-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Wei Wang <weiwan@google.com>
+Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -347,6 +347,7 @@ struct napi_struct {
+ struct list_head dev_list;
+ struct hlist_node napi_hash_node;
+ unsigned int napi_id;
++ struct task_struct *thread;
+ };
+
+ enum {
+@@ -357,6 +358,7 @@ enum {
+ NAPI_STATE_LISTED, /* NAPI added to system lists */
+ NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */
+ NAPI_STATE_IN_BUSY_POLL,/* sk_busy_loop() owns this NAPI */
++ NAPI_STATE_THREADED, /* The poll is performed inside its own thread*/
+ };
+
+ enum {
+@@ -367,6 +369,7 @@ enum {
+ NAPIF_STATE_LISTED = BIT(NAPI_STATE_LISTED),
+ NAPIF_STATE_NO_BUSY_POLL = BIT(NAPI_STATE_NO_BUSY_POLL),
+ NAPIF_STATE_IN_BUSY_POLL = BIT(NAPI_STATE_IN_BUSY_POLL),
++ NAPIF_STATE_THREADED = BIT(NAPI_STATE_THREADED),
+ };
+
+ enum gro_result {
+@@ -497,20 +500,7 @@ static inline bool napi_complete(struct
+ */
+ void napi_disable(struct napi_struct *n);
+
+-/**
+- * napi_enable - enable NAPI scheduling
+- * @n: NAPI context
+- *
+- * Resume NAPI from being scheduled on this context.
+- * Must be paired with napi_disable.
+- */
+-static inline void napi_enable(struct napi_struct *n)
+-{
+- BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state));
+- smp_mb__before_atomic();
+- clear_bit(NAPI_STATE_SCHED, &n->state);
+- clear_bit(NAPI_STATE_NPSVC, &n->state);
+-}
++void napi_enable(struct napi_struct *n);
+
+ /**
+ * napi_synchronize - wait until NAPI is not running
+@@ -1842,6 +1832,8 @@ enum netdev_ml_priv_type {
+ *
+ * @wol_enabled: Wake-on-LAN is enabled
+ *
++ * @threaded: napi threaded mode is enabled
++ *
+ * @net_notifier_list: List of per-net netdev notifier block
+ * that follow this device when it is moved
+ * to another network namespace.
+@@ -2161,6 +2153,7 @@ struct net_device {
+ struct lock_class_key *qdisc_running_key;
+ bool proto_down;
+ unsigned wol_enabled:1;
++ unsigned threaded:1;
+
+ struct list_head net_notifier_list;
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -91,6 +91,7 @@
+ #include <linux/etherdevice.h>
+ #include <linux/ethtool.h>
+ #include <linux/skbuff.h>
++#include <linux/kthread.h>
+ #include <linux/bpf.h>
+ #include <linux/bpf_trace.h>
+ #include <net/net_namespace.h>
+@@ -1500,6 +1501,27 @@ void netdev_notify_peers(struct net_devi
+ }
+ EXPORT_SYMBOL(netdev_notify_peers);
+
++static int napi_threaded_poll(void *data);
++
++static int napi_kthread_create(struct napi_struct *n)
++{
++ int err = 0;
++
++ /* Create and wake up the kthread once to put it in
++ * TASK_INTERRUPTIBLE mode to avoid the blocked task
++ * warning and work with loadavg.
++ */
++ n->thread = kthread_run(napi_threaded_poll, n, "napi/%s-%d",
++ n->dev->name, n->napi_id);
++ if (IS_ERR(n->thread)) {
++ err = PTR_ERR(n->thread);
++ pr_err("kthread_run failed with err %d\n", err);
++ n->thread = NULL;
++ }
++
++ return err;
++}
++
+ static int __dev_open(struct net_device *dev, struct netlink_ext_ack *extack)
+ {
+ const struct net_device_ops *ops = dev->netdev_ops;
+@@ -4267,6 +4289,21 @@ int gro_normal_batch __read_mostly = 8;
+ static inline void ____napi_schedule(struct softnet_data *sd,
+ struct napi_struct *napi)
+ {
++ struct task_struct *thread;
++
++ if (test_bit(NAPI_STATE_THREADED, &napi->state)) {
++ /* Paired with smp_mb__before_atomic() in
++ * napi_enable(). Use READ_ONCE() to guarantee
++ * a complete read on napi->thread. Only call
++ * wake_up_process() when it's not NULL.
++ */
++ thread = READ_ONCE(napi->thread);
++ if (thread) {
++ wake_up_process(thread);
++ return;
++ }
++ }
++
+ list_add_tail(&napi->poll_list, &sd->poll_list);
+ __raise_softirq_irqoff(NET_RX_SOFTIRQ);
+ }
+@@ -6758,6 +6795,12 @@ void netif_napi_add(struct net_device *d
+ set_bit(NAPI_STATE_NPSVC, &napi->state);
+ list_add_rcu(&napi->dev_list, &dev->napi_list);
+ napi_hash_add(napi);
++ /* Create kthread for this napi if dev->threaded is set.
++ * Clear dev->threaded if kthread creation failed so that
++ * threaded mode will not be enabled in napi_enable().
++ */
++ if (dev->threaded && napi_kthread_create(napi))
++ dev->threaded = 0;
+ }
+ EXPORT_SYMBOL(netif_napi_add);
+
+@@ -6774,9 +6817,28 @@ void napi_disable(struct napi_struct *n)
+ hrtimer_cancel(&n->timer);
+
+ clear_bit(NAPI_STATE_DISABLE, &n->state);
++ clear_bit(NAPI_STATE_THREADED, &n->state);
+ }
+ EXPORT_SYMBOL(napi_disable);
+
++/**
++ * napi_enable - enable NAPI scheduling
++ * @n: NAPI context
++ *
++ * Resume NAPI from being scheduled on this context.
++ * Must be paired with napi_disable.
++ */
++void napi_enable(struct napi_struct *n)
++{
++ BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state));
++ smp_mb__before_atomic();
++ clear_bit(NAPI_STATE_SCHED, &n->state);
++ clear_bit(NAPI_STATE_NPSVC, &n->state);
++ if (n->dev->threaded && n->thread)
++ set_bit(NAPI_STATE_THREADED, &n->state);
++}
++EXPORT_SYMBOL(napi_enable);
++
+ static void flush_gro_hash(struct napi_struct *napi)
+ {
+ int i;
+@@ -6802,6 +6864,11 @@ void __netif_napi_del(struct napi_struct
+
+ flush_gro_hash(napi);
+ napi->gro_bitmask = 0;
++
++ if (napi->thread) {
++ kthread_stop(napi->thread);
++ napi->thread = NULL;
++ }
+ }
+ EXPORT_SYMBOL(__netif_napi_del);
+
+@@ -6883,6 +6950,51 @@ static int napi_poll(struct napi_struct
+ return work;
+ }
+
++static int napi_thread_wait(struct napi_struct *napi)
++{
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ while (!kthread_should_stop() && !napi_disable_pending(napi)) {
++ if (test_bit(NAPI_STATE_SCHED, &napi->state)) {
++ WARN_ON(!list_empty(&napi->poll_list));
++ __set_current_state(TASK_RUNNING);
++ return 0;
++ }
++
++ schedule();
++ set_current_state(TASK_INTERRUPTIBLE);
++ }
++ __set_current_state(TASK_RUNNING);
++ return -1;
++}
++
++static int napi_threaded_poll(void *data)
++{
++ struct napi_struct *napi = data;
++ void *have;
++
++ while (!napi_thread_wait(napi)) {
++ for (;;) {
++ bool repoll = false;
++
++ local_bh_disable();
++
++ have = netpoll_poll_lock(napi);
++ __napi_poll(napi, &repoll);
++ netpoll_poll_unlock(have);
++
++ __kfree_skb_flush();
++ local_bh_enable();
++
++ if (!repoll)
++ break;
++
++ cond_resched();
++ }
++ }
++ return 0;
++}
++
+ static __latent_entropy void net_rx_action(struct softirq_action *h)
+ {
+ struct softnet_data *sd = this_cpu_ptr(&softnet_data);
--- /dev/null
+From: Wei Wang <weiwan@google.com>
+Date: Mon, 8 Feb 2021 11:34:10 -0800
+Subject: [PATCH] net: add sysfs attribute to control napi threaded mode
+
+This patch adds a new sysfs attribute to the network device class.
+Said attribute provides a per-device control to enable/disable the
+threaded mode for all the napi instances of the given network device,
+without the need for a device up/down.
+User sets it to 1 or 0 to enable or disable threaded mode.
+Note: when switching between threaded and the current softirq based mode
+for a napi instance, it will not immediately take effect if the napi is
+currently being polled. The mode switch will happen for the next time
+napi_schedule() is called.
+
+Co-developed-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Co-developed-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
+Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
+Co-developed-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Wei Wang <weiwan@google.com>
+Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/Documentation/ABI/testing/sysfs-class-net
++++ b/Documentation/ABI/testing/sysfs-class-net
+@@ -337,3 +337,18 @@ Contact: netdev@vger.kernel.org
+ Description:
+ 32-bit unsigned integer counting the number of times the link has
+ been down
++
++What: /sys/class/net/<iface>/threaded
++Date: Jan 2021
++KernelVersion: 5.12
++Contact: netdev@vger.kernel.org
++Description:
++ Boolean value to control the threaded mode per device. User could
++ set this value to enable/disable threaded mode for all napi
++ belonging to this device, without the need to do device up/down.
++
++ Possible values:
++ == ==================================
++ 0 threaded mode disabled for this dev
++ 1 threaded mode enabled for this dev
++ == ==================================
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -491,6 +491,8 @@ static inline bool napi_complete(struct
+ return napi_complete_done(n, 0);
+ }
+
++int dev_set_threaded(struct net_device *dev, bool threaded);
++
+ /**
+ * napi_disable - prevent NAPI from scheduling
+ * @n: NAPI context
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -4293,8 +4293,9 @@ static inline void ____napi_schedule(str
+
+ if (test_bit(NAPI_STATE_THREADED, &napi->state)) {
+ /* Paired with smp_mb__before_atomic() in
+- * napi_enable(). Use READ_ONCE() to guarantee
+- * a complete read on napi->thread. Only call
++ * napi_enable()/dev_set_threaded().
++ * Use READ_ONCE() to guarantee a complete
++ * read on napi->thread. Only call
+ * wake_up_process() when it's not NULL.
+ */
+ thread = READ_ONCE(napi->thread);
+@@ -6768,6 +6769,49 @@ static void init_gro_hash(struct napi_st
+ napi->gro_bitmask = 0;
+ }
+
++int dev_set_threaded(struct net_device *dev, bool threaded)
++{
++ struct napi_struct *napi;
++ int err = 0;
++
++ if (dev->threaded == threaded)
++ return 0;
++
++ if (threaded) {
++ list_for_each_entry(napi, &dev->napi_list, dev_list) {
++ if (!napi->thread) {
++ err = napi_kthread_create(napi);
++ if (err) {
++ threaded = false;
++ break;
++ }
++ }
++ }
++ }
++
++ dev->threaded = threaded;
++
++ /* Make sure kthread is created before THREADED bit
++ * is set.
++ */
++ smp_mb__before_atomic();
++
++ /* Setting/unsetting threaded mode on a napi might not immediately
++ * take effect, if the current napi instance is actively being
++ * polled. In this case, the switch between threaded mode and
++ * softirq mode will happen in the next round of napi_schedule().
++ * This should not cause hiccups/stalls to the live traffic.
++ */
++ list_for_each_entry(napi, &dev->napi_list, dev_list) {
++ if (threaded)
++ set_bit(NAPI_STATE_THREADED, &napi->state);
++ else
++ clear_bit(NAPI_STATE_THREADED, &napi->state);
++ }
++
++ return err;
++}
++
+ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
+ int (*poll)(struct napi_struct *, int), int weight)
+ {
+--- a/net/core/net-sysfs.c
++++ b/net/core/net-sysfs.c
+@@ -587,6 +587,45 @@ static ssize_t phys_switch_id_show(struc
+ }
+ static DEVICE_ATTR_RO(phys_switch_id);
+
++static ssize_t threaded_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct net_device *netdev = to_net_dev(dev);
++ ssize_t ret = -EINVAL;
++
++ if (!rtnl_trylock())
++ return restart_syscall();
++
++ if (dev_isalive(netdev))
++ ret = sprintf(buf, fmt_dec, netdev->threaded);
++
++ rtnl_unlock();
++ return ret;
++}
++
++static int modify_napi_threaded(struct net_device *dev, unsigned long val)
++{
++ int ret;
++
++ if (list_empty(&dev->napi_list))
++ return -EOPNOTSUPP;
++
++ if (val != 0 && val != 1)
++ return -EOPNOTSUPP;
++
++ ret = dev_set_threaded(dev, val);
++
++ return ret;
++}
++
++static ssize_t threaded_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t len)
++{
++ return netdev_store(dev, attr, buf, len, modify_napi_threaded);
++}
++static DEVICE_ATTR_RW(threaded);
++
+ static struct attribute *net_class_attrs[] __ro_after_init = {
+ &dev_attr_netdev_group.attr,
+ &dev_attr_type.attr,
+@@ -619,6 +658,7 @@ static struct attribute *net_class_attrs
+ &dev_attr_proto_down.attr,
+ &dev_attr_carrier_up_count.attr,
+ &dev_attr_carrier_down_count.attr,
++ &dev_attr_threaded.attr,
+ NULL,
+ };
+ ATTRIBUTE_GROUPS(net_class);
--- /dev/null
+From: Wei Wang <weiwan@google.com>
+Date: Mon, 1 Mar 2021 17:21:13 -0800
+Subject: [PATCH] net: fix race between napi kthread mode and busy poll
+
+Currently, napi_thread_wait() checks for NAPI_STATE_SCHED bit to
+determine if the kthread owns this napi and could call napi->poll() on
+it. However, if socket busy poll is enabled, it is possible that the
+busy poll thread grabs this SCHED bit (after the previous napi->poll()
+invokes napi_complete_done() and clears SCHED bit) and tries to poll
+on the same napi. napi_disable() could grab the SCHED bit as well.
+This patch tries to fix this race by adding a new bit
+NAPI_STATE_SCHED_THREADED in napi->state. This bit gets set in
+____napi_schedule() if the threaded mode is enabled, and gets cleared
+in napi_complete_done(), and we only poll the napi in kthread if this
+bit is set. This helps distinguish the ownership of the napi between
+kthread and other scenarios and fixes the race issue.
+
+Fixes: 29863d41bb6e ("net: implement threaded-able napi poll loop support")
+Reported-by: Martin Zaharinov <micron10@gmail.com>
+Suggested-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Wei Wang <weiwan@google.com>
+Cc: Alexander Duyck <alexanderduyck@fb.com>
+Cc: Eric Dumazet <edumazet@google.com>
+Cc: Paolo Abeni <pabeni@redhat.com>
+Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -359,6 +359,7 @@ enum {
+ NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */
+ NAPI_STATE_IN_BUSY_POLL,/* sk_busy_loop() owns this NAPI */
+ NAPI_STATE_THREADED, /* The poll is performed inside its own thread*/
++ NAPI_STATE_SCHED_THREADED, /* Napi is currently scheduled in threaded mode */
+ };
+
+ enum {
+@@ -370,6 +371,7 @@ enum {
+ NAPIF_STATE_NO_BUSY_POLL = BIT(NAPI_STATE_NO_BUSY_POLL),
+ NAPIF_STATE_IN_BUSY_POLL = BIT(NAPI_STATE_IN_BUSY_POLL),
+ NAPIF_STATE_THREADED = BIT(NAPI_STATE_THREADED),
++ NAPIF_STATE_SCHED_THREADED = BIT(NAPI_STATE_SCHED_THREADED),
+ };
+
+ enum gro_result {
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -4300,6 +4300,8 @@ static inline void ____napi_schedule(str
+ */
+ thread = READ_ONCE(napi->thread);
+ if (thread) {
++ if (thread->state != TASK_INTERRUPTIBLE)
++ set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
+ wake_up_process(thread);
+ return;
+ }
+@@ -6560,7 +6562,8 @@ bool napi_complete_done(struct napi_stru
+
+ WARN_ON_ONCE(!(val & NAPIF_STATE_SCHED));
+
+- new = val & ~(NAPIF_STATE_MISSED | NAPIF_STATE_SCHED);
++ new = val & ~(NAPIF_STATE_MISSED | NAPIF_STATE_SCHED |
++ NAPIF_STATE_SCHED_THREADED);
+
+ /* If STATE_MISSED was set, leave STATE_SCHED set,
+ * because we will call napi->poll() one more time.
+@@ -6996,16 +6999,25 @@ 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() && !napi_disable_pending(napi)) {
+- if (test_bit(NAPI_STATE_SCHED, &napi->state)) {
++ /* Testing SCHED_THREADED bit here to make sure the current
++ * kthread owns this napi and could poll on this 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) {
+ 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: Paolo Abeni <pabeni@redhat.com>
+Date: Fri, 9 Apr 2021 17:24:17 +0200
+Subject: [PATCH] net: fix hangup on napi_disable for threaded napi
+
+napi_disable() is subject to an hangup, when the threaded
+mode is enabled and the napi is under heavy traffic.
+
+If the relevant napi has been scheduled and the napi_disable()
+kicks in before the next napi_threaded_wait() completes - so
+that the latter quits due to the napi_disable_pending() condition,
+the existing code leaves the NAPI_STATE_SCHED bit set and the
+napi_disable() loop waiting for such bit will hang.
+
+This patch addresses the issue by dropping the NAPI_STATE_DISABLE
+bit test in napi_thread_wait(). The later napi_threaded_poll()
+iteration will take care of clearing the NAPI_STATE_SCHED.
+
+This also addresses a related problem reported by Jakub:
+before this patch a napi_disable()/napi_enable() pair killed
+the napi thread, effectively disabling the threaded mode.
+On the patched kernel napi_disable() simply stops scheduling
+the relevant thread.
+
+v1 -> v2:
+ - let the main napi_thread_poll() loop clear the SCHED bit
+
+Reported-by: Jakub Kicinski <kuba@kernel.org>
+Fixes: 29863d41bb6e ("net: implement threaded-able napi poll loop support")
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://lore.kernel.org/r/883923fa22745a9589e8610962b7dc59df09fb1f.1617981844.git.pabeni@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -7003,7 +7003,7 @@ static int napi_thread_wait(struct napi_
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+- while (!kthread_should_stop() && !napi_disable_pending(napi)) {
++ while (!kthread_should_stop()) {
+ /* Testing SCHED_THREADED bit here to make sure the current
+ * kthread owns this napi and could poll on this napi.
+ * Testing SCHED bit is not enough because SCHED bit might be
+@@ -7021,6 +7021,7 @@ static int napi_thread_wait(struct napi_
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ __set_current_state(TASK_RUNNING);
++
+ return -1;
+ }
+
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Fri, 20 Nov 2020 13:49:13 +0100
+Subject: [PATCH] netfilter: flowtable: add hash offset field to tuple
+
+Add a placeholder field to calculate hash tuple offset. Similar to
+2c407aca6497 ("netfilter: conntrack: avoid gcc-10 zero-length-bounds
+warning").
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -107,6 +107,10 @@ struct flow_offload_tuple {
+
+ u8 l3proto;
+ u8 l4proto;
++
++ /* All members above are keys for lookups, see flow_offload_hash(). */
++ struct { } __hash;
++
+ u8 dir;
+
+ u16 mtu;
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -191,14 +191,14 @@ static u32 flow_offload_hash(const void
+ {
+ const struct flow_offload_tuple *tuple = data;
+
+- return jhash(tuple, offsetof(struct flow_offload_tuple, dir), seed);
++ return jhash(tuple, offsetof(struct flow_offload_tuple, __hash), seed);
+ }
+
+ static u32 flow_offload_hash_obj(const void *data, u32 len, u32 seed)
+ {
+ const struct flow_offload_tuple_rhash *tuplehash = data;
+
+- return jhash(&tuplehash->tuple, offsetof(struct flow_offload_tuple, dir), seed);
++ return jhash(&tuplehash->tuple, offsetof(struct flow_offload_tuple, __hash), seed);
+ }
+
+ static int flow_offload_hash_cmp(struct rhashtable_compare_arg *arg,
+@@ -207,7 +207,7 @@ static int flow_offload_hash_cmp(struct
+ const struct flow_offload_tuple *tuple = arg->key;
+ const struct flow_offload_tuple_rhash *x = ptr;
+
+- if (memcmp(&x->tuple, tuple, offsetof(struct flow_offload_tuple, dir)))
++ if (memcmp(&x->tuple, tuple, offsetof(struct flow_offload_tuple, __hash)))
+ return 1;
+
+ return 0;
--- /dev/null
+From: Oz Shlomo <ozsh@nvidia.com>
+Date: Tue, 23 Mar 2021 00:56:19 +0100
+Subject: [PATCH] netfilter: flowtable: separate replace, destroy and
+ stats to different workqueues
+
+Currently the flow table offload replace, destroy and stats work items are
+executed on a single workqueue. As such, DESTROY and STATS commands may
+be backloged after a burst of REPLACE work items. This scenario can bloat
+up memory and may cause active connections to age.
+
+Instatiate add, del and stats workqueues to avoid backlogs of non-dependent
+actions. Provide sysfs control over the workqueue attributes, allowing
+userspace applications to control the workqueue cpumask.
+
+Signed-off-by: Oz Shlomo <ozsh@nvidia.com>
+Reviewed-by: Paul Blakey <paulb@nvidia.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -13,7 +13,9 @@
+ #include <net/netfilter/nf_conntrack_core.h>
+ #include <net/netfilter/nf_conntrack_tuple.h>
+
+-static struct workqueue_struct *nf_flow_offload_wq;
++static struct workqueue_struct *nf_flow_offload_add_wq;
++static struct workqueue_struct *nf_flow_offload_del_wq;
++static struct workqueue_struct *nf_flow_offload_stats_wq;
+
+ struct flow_offload_work {
+ struct list_head list;
+@@ -827,7 +829,12 @@ static void flow_offload_work_handler(st
+
+ static void flow_offload_queue_work(struct flow_offload_work *offload)
+ {
+- queue_work(nf_flow_offload_wq, &offload->work);
++ if (offload->cmd == FLOW_CLS_REPLACE)
++ queue_work(nf_flow_offload_add_wq, &offload->work);
++ else if (offload->cmd == FLOW_CLS_DESTROY)
++ queue_work(nf_flow_offload_del_wq, &offload->work);
++ else
++ queue_work(nf_flow_offload_stats_wq, &offload->work);
+ }
+
+ static struct flow_offload_work *
+@@ -899,8 +906,11 @@ void nf_flow_offload_stats(struct nf_flo
+
+ void nf_flow_table_offload_flush(struct nf_flowtable *flowtable)
+ {
+- if (nf_flowtable_hw_offload(flowtable))
+- flush_workqueue(nf_flow_offload_wq);
++ if (nf_flowtable_hw_offload(flowtable)) {
++ flush_workqueue(nf_flow_offload_add_wq);
++ flush_workqueue(nf_flow_offload_del_wq);
++ flush_workqueue(nf_flow_offload_stats_wq);
++ }
+ }
+
+ static int nf_flow_table_block_setup(struct nf_flowtable *flowtable,
+@@ -1013,15 +1023,33 @@ EXPORT_SYMBOL_GPL(nf_flow_table_offload_
+
+ int nf_flow_table_offload_init(void)
+ {
+- nf_flow_offload_wq = alloc_workqueue("nf_flow_table_offload",
+- WQ_UNBOUND, 0);
+- if (!nf_flow_offload_wq)
++ nf_flow_offload_add_wq = alloc_workqueue("nf_ft_offload_add",
++ WQ_UNBOUND | WQ_SYSFS, 0);
++ if (!nf_flow_offload_add_wq)
+ return -ENOMEM;
+
++ nf_flow_offload_del_wq = alloc_workqueue("nf_ft_offload_del",
++ WQ_UNBOUND | WQ_SYSFS, 0);
++ if (!nf_flow_offload_del_wq)
++ goto err_del_wq;
++
++ nf_flow_offload_stats_wq = alloc_workqueue("nf_ft_offload_stats",
++ WQ_UNBOUND | WQ_SYSFS, 0);
++ if (!nf_flow_offload_stats_wq)
++ goto err_stats_wq;
++
+ return 0;
++
++err_stats_wq:
++ destroy_workqueue(nf_flow_offload_del_wq);
++err_del_wq:
++ destroy_workqueue(nf_flow_offload_add_wq);
++ return -ENOMEM;
+ }
+
+ void nf_flow_table_offload_exit(void)
+ {
+- destroy_workqueue(nf_flow_offload_wq);
++ destroy_workqueue(nf_flow_offload_add_wq);
++ destroy_workqueue(nf_flow_offload_del_wq);
++ destroy_workqueue(nf_flow_offload_stats_wq);
+ }
--- /dev/null
+From: YueHaibing <yuehaibing@huawei.com>
+Date: Tue, 23 Mar 2021 00:56:21 +0100
+Subject: [PATCH] netfilter: conntrack: Remove unused variable
+ declaration
+
+commit e97c3e278e95 ("tproxy: split off ipv6 defragmentation to a separate
+module") left behind this.
+
+Signed-off-by: YueHaibing <yuehaibing@huawei.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h
++++ b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h
+@@ -4,7 +4,4 @@
+
+ extern const struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6;
+
+-#include <linux/sysctl.h>
+-extern struct ctl_table nf_ct_ipv6_sysctl_table[];
+-
+ #endif /* _NF_CONNTRACK_IPV6_H*/
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:22 +0100
+Subject: [PATCH] netfilter: flowtable: consolidate
+ skb_try_make_writable() call
+
+Fetch the layer 4 header size to be mangled by NAT when building the
+tuple, then use it to make writable the network and the transport
+headers. After this update, the NAT routines now assumes that the skbuff
+area is writable. Do the pointer refetch only after the single
+skb_try_make_writable() call.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -394,9 +394,6 @@ static int nf_flow_nat_port_tcp(struct s
+ {
+ struct tcphdr *tcph;
+
+- if (skb_try_make_writable(skb, thoff + sizeof(*tcph)))
+- return -1;
+-
+ tcph = (void *)(skb_network_header(skb) + thoff);
+ inet_proto_csum_replace2(&tcph->check, skb, port, new_port, false);
+
+@@ -408,9 +405,6 @@ static int nf_flow_nat_port_udp(struct s
+ {
+ struct udphdr *udph;
+
+- if (skb_try_make_writable(skb, thoff + sizeof(*udph)))
+- return -1;
+-
+ udph = (void *)(skb_network_header(skb) + thoff);
+ if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+ inet_proto_csum_replace2(&udph->check, skb, port,
+@@ -446,9 +440,6 @@ int nf_flow_snat_port(const struct flow_
+ struct flow_ports *hdr;
+ __be16 port, new_port;
+
+- if (skb_try_make_writable(skb, thoff + sizeof(*hdr)))
+- return -1;
+-
+ hdr = (void *)(skb_network_header(skb) + thoff);
+
+ switch (dir) {
+@@ -477,9 +468,6 @@ int nf_flow_dnat_port(const struct flow_
+ struct flow_ports *hdr;
+ __be16 port, new_port;
+
+- if (skb_try_make_writable(skb, thoff + sizeof(*hdr)))
+- return -1;
+-
+ hdr = (void *)(skb_network_header(skb) + thoff);
+
+ switch (dir) {
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -39,9 +39,6 @@ static int nf_flow_nat_ip_tcp(struct sk_
+ {
+ struct tcphdr *tcph;
+
+- if (skb_try_make_writable(skb, thoff + sizeof(*tcph)))
+- return -1;
+-
+ tcph = (void *)(skb_network_header(skb) + thoff);
+ inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, true);
+
+@@ -53,9 +50,6 @@ static int nf_flow_nat_ip_udp(struct sk_
+ {
+ struct udphdr *udph;
+
+- if (skb_try_make_writable(skb, thoff + sizeof(*udph)))
+- return -1;
+-
+ udph = (void *)(skb_network_header(skb) + thoff);
+ if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+ inet_proto_csum_replace4(&udph->check, skb, addr,
+@@ -136,19 +130,17 @@ static int nf_flow_dnat_ip(const struct
+ }
+
+ static int nf_flow_nat_ip(const struct flow_offload *flow, struct sk_buff *skb,
+- unsigned int thoff, enum flow_offload_tuple_dir dir)
++ unsigned int thoff, enum flow_offload_tuple_dir dir,
++ struct iphdr *iph)
+ {
+- struct iphdr *iph = ip_hdr(skb);
+-
+ if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
+ (nf_flow_snat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
+- nf_flow_snat_ip(flow, skb, ip_hdr(skb), thoff, dir) < 0))
++ nf_flow_snat_ip(flow, skb, iph, thoff, dir) < 0))
+ return -1;
+
+- iph = ip_hdr(skb);
+ if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
+ (nf_flow_dnat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
+- nf_flow_dnat_ip(flow, skb, ip_hdr(skb), thoff, dir) < 0))
++ nf_flow_dnat_ip(flow, skb, iph, thoff, dir) < 0))
+ return -1;
+
+ return 0;
+@@ -160,10 +152,10 @@ static bool ip_has_options(unsigned int
+ }
+
+ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
+- struct flow_offload_tuple *tuple)
++ struct flow_offload_tuple *tuple, u32 *hdrsize)
+ {
+- unsigned int thoff, hdrsize;
+ struct flow_ports *ports;
++ unsigned int thoff;
+ struct iphdr *iph;
+
+ if (!pskb_may_pull(skb, sizeof(*iph)))
+@@ -178,10 +170,10 @@ static int nf_flow_tuple_ip(struct sk_bu
+
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+- hdrsize = sizeof(struct tcphdr);
++ *hdrsize = sizeof(struct tcphdr);
+ break;
+ case IPPROTO_UDP:
+- hdrsize = sizeof(struct udphdr);
++ *hdrsize = sizeof(struct udphdr);
+ break;
+ default:
+ return -1;
+@@ -191,7 +183,7 @@ static int nf_flow_tuple_ip(struct sk_bu
+ return -1;
+
+ thoff = iph->ihl * 4;
+- if (!pskb_may_pull(skb, thoff + hdrsize))
++ if (!pskb_may_pull(skb, thoff + *hdrsize))
+ return -1;
+
+ iph = ip_hdr(skb);
+@@ -252,11 +244,12 @@ nf_flow_offload_ip_hook(void *priv, stru
+ unsigned int thoff;
+ struct iphdr *iph;
+ __be32 nexthop;
++ u32 hdrsize;
+
+ if (skb->protocol != htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+- if (nf_flow_tuple_ip(skb, state->in, &tuple) < 0)
++ if (nf_flow_tuple_ip(skb, state->in, &tuple, &hdrsize) < 0)
+ return NF_ACCEPT;
+
+ tuplehash = flow_offload_lookup(flow_table, &tuple);
+@@ -271,11 +264,13 @@ nf_flow_offload_ip_hook(void *priv, stru
+ if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
+ return NF_ACCEPT;
+
+- if (skb_try_make_writable(skb, sizeof(*iph)))
++ iph = ip_hdr(skb);
++ thoff = iph->ihl * 4;
++ if (skb_try_make_writable(skb, thoff + hdrsize))
+ return NF_DROP;
+
+- thoff = ip_hdr(skb)->ihl * 4;
+- if (nf_flow_state_check(flow, ip_hdr(skb)->protocol, skb, thoff))
++ iph = ip_hdr(skb);
++ if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+ return NF_ACCEPT;
+
+ flow_offload_refresh(flow_table, flow);
+@@ -285,10 +280,9 @@ nf_flow_offload_ip_hook(void *priv, stru
+ return NF_ACCEPT;
+ }
+
+- if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0)
++ if (nf_flow_nat_ip(flow, skb, thoff, dir, iph) < 0)
+ return NF_DROP;
+
+- iph = ip_hdr(skb);
+ ip_decrease_ttl(iph);
+ skb->tstamp = 0;
+
+@@ -317,9 +311,6 @@ static int nf_flow_nat_ipv6_tcp(struct s
+ {
+ struct tcphdr *tcph;
+
+- if (skb_try_make_writable(skb, thoff + sizeof(*tcph)))
+- return -1;
+-
+ tcph = (void *)(skb_network_header(skb) + thoff);
+ inet_proto_csum_replace16(&tcph->check, skb, addr->s6_addr32,
+ new_addr->s6_addr32, true);
+@@ -333,9 +324,6 @@ static int nf_flow_nat_ipv6_udp(struct s
+ {
+ struct udphdr *udph;
+
+- if (skb_try_make_writable(skb, thoff + sizeof(*udph)))
+- return -1;
+-
+ udph = (void *)(skb_network_header(skb) + thoff);
+ if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+ inet_proto_csum_replace16(&udph->check, skb, addr->s6_addr32,
+@@ -417,31 +405,30 @@ static int nf_flow_dnat_ipv6(const struc
+
+ static int nf_flow_nat_ipv6(const struct flow_offload *flow,
+ struct sk_buff *skb,
+- enum flow_offload_tuple_dir dir)
++ enum flow_offload_tuple_dir dir,
++ struct ipv6hdr *ip6h)
+ {
+- struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ unsigned int thoff = sizeof(*ip6h);
+
+ if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
+ (nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
+- nf_flow_snat_ipv6(flow, skb, ipv6_hdr(skb), thoff, dir) < 0))
++ nf_flow_snat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
+ return -1;
+
+- ip6h = ipv6_hdr(skb);
+ if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
+ (nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
+- nf_flow_dnat_ipv6(flow, skb, ipv6_hdr(skb), thoff, dir) < 0))
++ nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
+ return -1;
+
+ return 0;
+ }
+
+ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
+- struct flow_offload_tuple *tuple)
++ struct flow_offload_tuple *tuple, u32 *hdrsize)
+ {
+- unsigned int thoff, hdrsize;
+ struct flow_ports *ports;
+ struct ipv6hdr *ip6h;
++ unsigned int thoff;
+
+ if (!pskb_may_pull(skb, sizeof(*ip6h)))
+ return -1;
+@@ -450,10 +437,10 @@ static int nf_flow_tuple_ipv6(struct sk_
+
+ switch (ip6h->nexthdr) {
+ case IPPROTO_TCP:
+- hdrsize = sizeof(struct tcphdr);
++ *hdrsize = sizeof(struct tcphdr);
+ break;
+ case IPPROTO_UDP:
+- hdrsize = sizeof(struct udphdr);
++ *hdrsize = sizeof(struct udphdr);
+ break;
+ default:
+ return -1;
+@@ -463,7 +450,7 @@ static int nf_flow_tuple_ipv6(struct sk_
+ return -1;
+
+ thoff = sizeof(*ip6h);
+- if (!pskb_may_pull(skb, thoff + hdrsize))
++ if (!pskb_may_pull(skb, thoff + *hdrsize))
+ return -1;
+
+ ip6h = ipv6_hdr(skb);
+@@ -493,11 +480,12 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ struct net_device *outdev;
+ struct ipv6hdr *ip6h;
+ struct rt6_info *rt;
++ u32 hdrsize;
+
+ if (skb->protocol != htons(ETH_P_IPV6))
+ return NF_ACCEPT;
+
+- if (nf_flow_tuple_ipv6(skb, state->in, &tuple) < 0)
++ if (nf_flow_tuple_ipv6(skb, state->in, &tuple, &hdrsize) < 0)
+ return NF_ACCEPT;
+
+ tuplehash = flow_offload_lookup(flow_table, &tuple);
+@@ -523,13 +511,13 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ return NF_ACCEPT;
+ }
+
+- if (skb_try_make_writable(skb, sizeof(*ip6h)))
++ if (skb_try_make_writable(skb, sizeof(*ip6h) + hdrsize))
+ return NF_DROP;
+
+- if (nf_flow_nat_ipv6(flow, skb, dir) < 0)
++ ip6h = ipv6_hdr(skb);
++ if (nf_flow_nat_ipv6(flow, skb, dir, ip6h) < 0)
+ return NF_DROP;
+
+- ip6h = ipv6_hdr(skb);
+ ip6h->hop_limit--;
+ skb->tstamp = 0;
+
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:23 +0100
+Subject: [PATCH] netfilter: flowtable: move skb_try_make_writable()
+ before NAT in IPv4
+
+For consistency with the IPv6 flowtable datapath and to make sure the
+skbuff is writable right before the NAT header updates.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -266,10 +266,6 @@ nf_flow_offload_ip_hook(void *priv, stru
+
+ iph = ip_hdr(skb);
+ thoff = iph->ihl * 4;
+- if (skb_try_make_writable(skb, thoff + hdrsize))
+- return NF_DROP;
+-
+- iph = ip_hdr(skb);
+ if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+ return NF_ACCEPT;
+
+@@ -280,6 +276,10 @@ nf_flow_offload_ip_hook(void *priv, stru
+ return NF_ACCEPT;
+ }
+
++ if (skb_try_make_writable(skb, thoff + hdrsize))
++ return NF_DROP;
++
++ iph = ip_hdr(skb);
+ if (nf_flow_nat_ip(flow, skb, thoff, dir, iph) < 0)
+ return NF_DROP;
+
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:24 +0100
+Subject: [PATCH] netfilter: flowtable: move FLOW_OFFLOAD_DIR_MAX away
+ from enumeration
+
+This allows to remove the default case which should not ever happen and
+that was added to avoid gcc warnings on unhandled FLOW_OFFLOAD_DIR_MAX
+enumeration case.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -86,8 +86,8 @@ static inline bool nf_flowtable_hw_offlo
+ enum flow_offload_tuple_dir {
+ FLOW_OFFLOAD_DIR_ORIGINAL = IP_CT_DIR_ORIGINAL,
+ FLOW_OFFLOAD_DIR_REPLY = IP_CT_DIR_REPLY,
+- FLOW_OFFLOAD_DIR_MAX = IP_CT_DIR_MAX
+ };
++#define FLOW_OFFLOAD_DIR_MAX IP_CT_DIR_MAX
+
+ struct flow_offload_tuple {
+ union {
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -453,8 +453,6 @@ int nf_flow_snat_port(const struct flow_
+ new_port = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_port;
+ hdr->dest = new_port;
+ break;
+- default:
+- return -1;
+ }
+
+ return nf_flow_nat_port(skb, thoff, protocol, port, new_port);
+@@ -481,8 +479,6 @@ int nf_flow_dnat_port(const struct flow_
+ new_port = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_port;
+ hdr->source = new_port;
+ break;
+- default:
+- return -1;
+ }
+
+ return nf_flow_nat_port(skb, thoff, protocol, port, new_port);
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -96,8 +96,6 @@ static int nf_flow_snat_ip(const struct
+ new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v4.s_addr;
+ iph->daddr = new_addr;
+ break;
+- default:
+- return -1;
+ }
+ csum_replace4(&iph->check, addr, new_addr);
+
+@@ -121,8 +119,6 @@ static int nf_flow_dnat_ip(const struct
+ new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v4.s_addr;
+ iph->saddr = new_addr;
+ break;
+- default:
+- return -1;
+ }
+ csum_replace4(&iph->check, addr, new_addr);
+
+@@ -371,8 +367,6 @@ static int nf_flow_snat_ipv6(const struc
+ new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v6;
+ ip6h->daddr = new_addr;
+ break;
+- default:
+- return -1;
+ }
+
+ return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
+@@ -396,8 +390,6 @@ static int nf_flow_dnat_ipv6(const struc
+ new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v6;
+ ip6h->saddr = new_addr;
+ break;
+- default:
+- return -1;
+ }
+
+ return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:25 +0100
+Subject: [PATCH] netfilter: flowtable: fast NAT functions never fail
+
+Simplify existing fast NAT routines by returning void. After the
+skb_try_make_writable() call consolidation, these routines cannot ever
+fail.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -228,12 +228,12 @@ void nf_flow_table_free(struct nf_flowta
+
+ void flow_offload_teardown(struct flow_offload *flow);
+
+-int nf_flow_snat_port(const struct flow_offload *flow,
+- struct sk_buff *skb, unsigned int thoff,
+- u8 protocol, enum flow_offload_tuple_dir dir);
+-int nf_flow_dnat_port(const struct flow_offload *flow,
+- struct sk_buff *skb, unsigned int thoff,
+- u8 protocol, enum flow_offload_tuple_dir dir);
++void nf_flow_snat_port(const struct flow_offload *flow,
++ struct sk_buff *skb, unsigned int thoff,
++ u8 protocol, enum flow_offload_tuple_dir dir);
++void nf_flow_dnat_port(const struct flow_offload *flow,
++ struct sk_buff *skb, unsigned int thoff,
++ u8 protocol, enum flow_offload_tuple_dir dir);
+
+ struct flow_ports {
+ __be16 source, dest;
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -388,20 +388,17 @@ static void nf_flow_offload_work_gc(stru
+ queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ);
+ }
+
+-
+-static int nf_flow_nat_port_tcp(struct sk_buff *skb, unsigned int thoff,
+- __be16 port, __be16 new_port)
++static void nf_flow_nat_port_tcp(struct sk_buff *skb, unsigned int thoff,
++ __be16 port, __be16 new_port)
+ {
+ struct tcphdr *tcph;
+
+ tcph = (void *)(skb_network_header(skb) + thoff);
+ inet_proto_csum_replace2(&tcph->check, skb, port, new_port, false);
+-
+- return 0;
+ }
+
+-static int nf_flow_nat_port_udp(struct sk_buff *skb, unsigned int thoff,
+- __be16 port, __be16 new_port)
++static void nf_flow_nat_port_udp(struct sk_buff *skb, unsigned int thoff,
++ __be16 port, __be16 new_port)
+ {
+ struct udphdr *udph;
+
+@@ -412,30 +409,24 @@ static int nf_flow_nat_port_udp(struct s
+ if (!udph->check)
+ udph->check = CSUM_MANGLED_0;
+ }
+-
+- return 0;
+ }
+
+-static int nf_flow_nat_port(struct sk_buff *skb, unsigned int thoff,
+- u8 protocol, __be16 port, __be16 new_port)
++static void nf_flow_nat_port(struct sk_buff *skb, unsigned int thoff,
++ u8 protocol, __be16 port, __be16 new_port)
+ {
+ switch (protocol) {
+ case IPPROTO_TCP:
+- if (nf_flow_nat_port_tcp(skb, thoff, port, new_port) < 0)
+- return NF_DROP;
++ nf_flow_nat_port_tcp(skb, thoff, port, new_port);
+ break;
+ case IPPROTO_UDP:
+- if (nf_flow_nat_port_udp(skb, thoff, port, new_port) < 0)
+- return NF_DROP;
++ nf_flow_nat_port_udp(skb, thoff, port, new_port);
+ break;
+ }
+-
+- return 0;
+ }
+
+-int nf_flow_snat_port(const struct flow_offload *flow,
+- struct sk_buff *skb, unsigned int thoff,
+- u8 protocol, enum flow_offload_tuple_dir dir)
++void nf_flow_snat_port(const struct flow_offload *flow,
++ struct sk_buff *skb, unsigned int thoff,
++ u8 protocol, enum flow_offload_tuple_dir dir)
+ {
+ struct flow_ports *hdr;
+ __be16 port, new_port;
+@@ -455,13 +446,13 @@ int nf_flow_snat_port(const struct flow_
+ break;
+ }
+
+- return nf_flow_nat_port(skb, thoff, protocol, port, new_port);
++ nf_flow_nat_port(skb, thoff, protocol, port, new_port);
+ }
+ EXPORT_SYMBOL_GPL(nf_flow_snat_port);
+
+-int nf_flow_dnat_port(const struct flow_offload *flow,
+- struct sk_buff *skb, unsigned int thoff,
+- u8 protocol, enum flow_offload_tuple_dir dir)
++void nf_flow_dnat_port(const struct flow_offload *flow, struct sk_buff *skb,
++ unsigned int thoff, u8 protocol,
++ enum flow_offload_tuple_dir dir)
+ {
+ struct flow_ports *hdr;
+ __be16 port, new_port;
+@@ -481,7 +472,7 @@ int nf_flow_dnat_port(const struct flow_
+ break;
+ }
+
+- return nf_flow_nat_port(skb, thoff, protocol, port, new_port);
++ nf_flow_nat_port(skb, thoff, protocol, port, new_port);
+ }
+ EXPORT_SYMBOL_GPL(nf_flow_dnat_port);
+
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -34,19 +34,17 @@ static int nf_flow_state_check(struct fl
+ return 0;
+ }
+
+-static int nf_flow_nat_ip_tcp(struct sk_buff *skb, unsigned int thoff,
+- __be32 addr, __be32 new_addr)
++static void nf_flow_nat_ip_tcp(struct sk_buff *skb, unsigned int thoff,
++ __be32 addr, __be32 new_addr)
+ {
+ struct tcphdr *tcph;
+
+ tcph = (void *)(skb_network_header(skb) + thoff);
+ inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, true);
+-
+- return 0;
+ }
+
+-static int nf_flow_nat_ip_udp(struct sk_buff *skb, unsigned int thoff,
+- __be32 addr, __be32 new_addr)
++static void nf_flow_nat_ip_udp(struct sk_buff *skb, unsigned int thoff,
++ __be32 addr, __be32 new_addr)
+ {
+ struct udphdr *udph;
+
+@@ -57,31 +55,25 @@ static int nf_flow_nat_ip_udp(struct sk_
+ if (!udph->check)
+ udph->check = CSUM_MANGLED_0;
+ }
+-
+- return 0;
+ }
+
+-static int nf_flow_nat_ip_l4proto(struct sk_buff *skb, struct iphdr *iph,
+- unsigned int thoff, __be32 addr,
+- __be32 new_addr)
++static void nf_flow_nat_ip_l4proto(struct sk_buff *skb, struct iphdr *iph,
++ unsigned int thoff, __be32 addr,
++ __be32 new_addr)
+ {
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+- if (nf_flow_nat_ip_tcp(skb, thoff, addr, new_addr) < 0)
+- return NF_DROP;
++ nf_flow_nat_ip_tcp(skb, thoff, addr, new_addr);
+ break;
+ case IPPROTO_UDP:
+- if (nf_flow_nat_ip_udp(skb, thoff, addr, new_addr) < 0)
+- return NF_DROP;
++ nf_flow_nat_ip_udp(skb, thoff, addr, new_addr);
+ break;
+ }
+-
+- return 0;
+ }
+
+-static int nf_flow_snat_ip(const struct flow_offload *flow, struct sk_buff *skb,
+- struct iphdr *iph, unsigned int thoff,
+- enum flow_offload_tuple_dir dir)
++static void nf_flow_snat_ip(const struct flow_offload *flow,
++ struct sk_buff *skb, struct iphdr *iph,
++ unsigned int thoff, enum flow_offload_tuple_dir dir)
+ {
+ __be32 addr, new_addr;
+
+@@ -99,12 +91,12 @@ static int nf_flow_snat_ip(const struct
+ }
+ csum_replace4(&iph->check, addr, new_addr);
+
+- return nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
++ nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
+ }
+
+-static int nf_flow_dnat_ip(const struct flow_offload *flow, struct sk_buff *skb,
+- struct iphdr *iph, unsigned int thoff,
+- enum flow_offload_tuple_dir dir)
++static void nf_flow_dnat_ip(const struct flow_offload *flow,
++ struct sk_buff *skb, struct iphdr *iph,
++ unsigned int thoff, enum flow_offload_tuple_dir dir)
+ {
+ __be32 addr, new_addr;
+
+@@ -122,24 +114,21 @@ static int nf_flow_dnat_ip(const struct
+ }
+ csum_replace4(&iph->check, addr, new_addr);
+
+- return nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
++ nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
+ }
+
+-static int nf_flow_nat_ip(const struct flow_offload *flow, struct sk_buff *skb,
++static void nf_flow_nat_ip(const struct flow_offload *flow, struct sk_buff *skb,
+ unsigned int thoff, enum flow_offload_tuple_dir dir,
+ struct iphdr *iph)
+ {
+- if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
+- (nf_flow_snat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
+- nf_flow_snat_ip(flow, skb, iph, thoff, dir) < 0))
+- return -1;
+-
+- if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
+- (nf_flow_dnat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
+- nf_flow_dnat_ip(flow, skb, iph, thoff, dir) < 0))
+- return -1;
+-
+- return 0;
++ if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
++ nf_flow_snat_port(flow, skb, thoff, iph->protocol, dir);
++ nf_flow_snat_ip(flow, skb, iph, thoff, dir);
++ }
++ if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
++ nf_flow_dnat_port(flow, skb, thoff, iph->protocol, dir);
++ nf_flow_dnat_ip(flow, skb, iph, thoff, dir);
++ }
+ }
+
+ static bool ip_has_options(unsigned int thoff)
+@@ -276,8 +265,7 @@ nf_flow_offload_ip_hook(void *priv, stru
+ return NF_DROP;
+
+ iph = ip_hdr(skb);
+- if (nf_flow_nat_ip(flow, skb, thoff, dir, iph) < 0)
+- return NF_DROP;
++ nf_flow_nat_ip(flow, skb, thoff, dir, iph);
+
+ ip_decrease_ttl(iph);
+ skb->tstamp = 0;
+@@ -301,22 +289,21 @@ nf_flow_offload_ip_hook(void *priv, stru
+ }
+ EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
+
+-static int nf_flow_nat_ipv6_tcp(struct sk_buff *skb, unsigned int thoff,
+- struct in6_addr *addr,
+- struct in6_addr *new_addr)
++static void nf_flow_nat_ipv6_tcp(struct sk_buff *skb, unsigned int thoff,
++ struct in6_addr *addr,
++ struct in6_addr *new_addr,
++ struct ipv6hdr *ip6h)
+ {
+ struct tcphdr *tcph;
+
+ tcph = (void *)(skb_network_header(skb) + thoff);
+ inet_proto_csum_replace16(&tcph->check, skb, addr->s6_addr32,
+ new_addr->s6_addr32, true);
+-
+- return 0;
+ }
+
+-static int nf_flow_nat_ipv6_udp(struct sk_buff *skb, unsigned int thoff,
+- struct in6_addr *addr,
+- struct in6_addr *new_addr)
++static void nf_flow_nat_ipv6_udp(struct sk_buff *skb, unsigned int thoff,
++ struct in6_addr *addr,
++ struct in6_addr *new_addr)
+ {
+ struct udphdr *udph;
+
+@@ -327,32 +314,26 @@ static int nf_flow_nat_ipv6_udp(struct s
+ if (!udph->check)
+ udph->check = CSUM_MANGLED_0;
+ }
+-
+- return 0;
+ }
+
+-static int nf_flow_nat_ipv6_l4proto(struct sk_buff *skb, struct ipv6hdr *ip6h,
+- unsigned int thoff, struct in6_addr *addr,
+- struct in6_addr *new_addr)
++static void nf_flow_nat_ipv6_l4proto(struct sk_buff *skb, struct ipv6hdr *ip6h,
++ unsigned int thoff, struct in6_addr *addr,
++ struct in6_addr *new_addr)
+ {
+ switch (ip6h->nexthdr) {
+ case IPPROTO_TCP:
+- if (nf_flow_nat_ipv6_tcp(skb, thoff, addr, new_addr) < 0)
+- return NF_DROP;
++ nf_flow_nat_ipv6_tcp(skb, thoff, addr, new_addr, ip6h);
+ break;
+ case IPPROTO_UDP:
+- if (nf_flow_nat_ipv6_udp(skb, thoff, addr, new_addr) < 0)
+- return NF_DROP;
++ nf_flow_nat_ipv6_udp(skb, thoff, addr, new_addr);
+ break;
+ }
+-
+- return 0;
+ }
+
+-static int nf_flow_snat_ipv6(const struct flow_offload *flow,
+- struct sk_buff *skb, struct ipv6hdr *ip6h,
+- unsigned int thoff,
+- enum flow_offload_tuple_dir dir)
++static void nf_flow_snat_ipv6(const struct flow_offload *flow,
++ struct sk_buff *skb, struct ipv6hdr *ip6h,
++ unsigned int thoff,
++ enum flow_offload_tuple_dir dir)
+ {
+ struct in6_addr addr, new_addr;
+
+@@ -369,13 +350,13 @@ static int nf_flow_snat_ipv6(const struc
+ break;
+ }
+
+- return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
++ nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
+ }
+
+-static int nf_flow_dnat_ipv6(const struct flow_offload *flow,
+- struct sk_buff *skb, struct ipv6hdr *ip6h,
+- unsigned int thoff,
+- enum flow_offload_tuple_dir dir)
++static void nf_flow_dnat_ipv6(const struct flow_offload *flow,
++ struct sk_buff *skb, struct ipv6hdr *ip6h,
++ unsigned int thoff,
++ enum flow_offload_tuple_dir dir)
+ {
+ struct in6_addr addr, new_addr;
+
+@@ -392,27 +373,24 @@ static int nf_flow_dnat_ipv6(const struc
+ break;
+ }
+
+- return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
++ nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
+ }
+
+-static int nf_flow_nat_ipv6(const struct flow_offload *flow,
+- struct sk_buff *skb,
+- enum flow_offload_tuple_dir dir,
+- struct ipv6hdr *ip6h)
++static void nf_flow_nat_ipv6(const struct flow_offload *flow,
++ struct sk_buff *skb,
++ enum flow_offload_tuple_dir dir,
++ struct ipv6hdr *ip6h)
+ {
+ unsigned int thoff = sizeof(*ip6h);
+
+- if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
+- (nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
+- nf_flow_snat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
+- return -1;
+-
+- if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
+- (nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
+- nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
+- return -1;
+-
+- return 0;
++ if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
++ nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir);
++ nf_flow_snat_ipv6(flow, skb, ip6h, thoff, dir);
++ }
++ if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
++ nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir);
++ nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir);
++ }
+ }
+
+ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
+@@ -507,8 +485,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ return NF_DROP;
+
+ ip6h = ipv6_hdr(skb);
+- if (nf_flow_nat_ipv6(flow, skb, dir, ip6h) < 0)
+- return NF_DROP;
++ nf_flow_nat_ipv6(flow, skb, dir, ip6h);
+
+ ip6h->hop_limit--;
+ skb->tstamp = 0;
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:26 +0100
+Subject: [PATCH] netfilter: flowtable: call dst_check() to fall back to
+ classic forwarding
+
+In case the route is stale, pass up the packet to the classic forwarding
+path for re-evaluation and schedule this flow entry for removal.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -197,14 +197,6 @@ static bool nf_flow_exceeds_mtu(const st
+ return true;
+ }
+
+-static int nf_flow_offload_dst_check(struct dst_entry *dst)
+-{
+- if (unlikely(dst_xfrm(dst)))
+- return dst_check(dst, 0) ? 0 : -1;
+-
+- return 0;
+-}
+-
+ static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb,
+ const struct nf_hook_state *state,
+ struct dst_entry *dst)
+@@ -256,7 +248,7 @@ nf_flow_offload_ip_hook(void *priv, stru
+
+ flow_offload_refresh(flow_table, flow);
+
+- if (nf_flow_offload_dst_check(&rt->dst)) {
++ if (!dst_check(&rt->dst, 0)) {
+ flow_offload_teardown(flow);
+ return NF_ACCEPT;
+ }
+@@ -476,7 +468,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
+
+ flow_offload_refresh(flow_table, flow);
+
+- if (nf_flow_offload_dst_check(&rt->dst)) {
++ if (!dst_check(&rt->dst, 0)) {
+ flow_offload_teardown(flow);
+ return NF_ACCEPT;
+ }
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:27 +0100
+Subject: [PATCH] netfilter: flowtable: refresh timeout after dst and
+ writable checks
+
+Refresh the timeout (and retry hardware offload) once the skbuff dst
+is confirmed to be current and after the skbuff is made writable.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -246,8 +246,6 @@ nf_flow_offload_ip_hook(void *priv, stru
+ if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+ return NF_ACCEPT;
+
+- flow_offload_refresh(flow_table, flow);
+-
+ if (!dst_check(&rt->dst, 0)) {
+ flow_offload_teardown(flow);
+ return NF_ACCEPT;
+@@ -256,6 +254,8 @@ nf_flow_offload_ip_hook(void *priv, stru
+ if (skb_try_make_writable(skb, thoff + hdrsize))
+ return NF_DROP;
+
++ flow_offload_refresh(flow_table, flow);
++
+ iph = ip_hdr(skb);
+ nf_flow_nat_ip(flow, skb, thoff, dir, iph);
+
+@@ -466,8 +466,6 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ sizeof(*ip6h)))
+ return NF_ACCEPT;
+
+- flow_offload_refresh(flow_table, flow);
+-
+ if (!dst_check(&rt->dst, 0)) {
+ flow_offload_teardown(flow);
+ return NF_ACCEPT;
+@@ -476,6 +474,8 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ if (skb_try_make_writable(skb, sizeof(*ip6h) + hdrsize))
+ return NF_DROP;
+
++ flow_offload_refresh(flow_table, flow);
++
+ ip6h = ipv6_hdr(skb);
+ nf_flow_nat_ipv6(flow, skb, dir, ip6h);
+
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:28 +0100
+Subject: [PATCH] netfilter: nftables: update table flags from the commit
+ phase
+
+Do not update table flags from the preparation phase. Store the flags
+update into the transaction, then update the flags from the commit
+phase.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1470,13 +1470,16 @@ struct nft_trans_chain {
+
+ struct nft_trans_table {
+ bool update;
+- bool enable;
++ u8 state;
++ u32 flags;
+ };
+
+ #define nft_trans_table_update(trans) \
+ (((struct nft_trans_table *)trans->data)->update)
+-#define nft_trans_table_enable(trans) \
+- (((struct nft_trans_table *)trans->data)->enable)
++#define nft_trans_table_state(trans) \
++ (((struct nft_trans_table *)trans->data)->state)
++#define nft_trans_table_flags(trans) \
++ (((struct nft_trans_table *)trans->data)->flags)
+
+ struct nft_trans_elem {
+ struct nft_set *set;
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -891,6 +891,12 @@ static void nf_tables_table_disable(stru
+ nft_table_disable(net, table, 0);
+ }
+
++enum {
++ NFT_TABLE_STATE_UNCHANGED = 0,
++ NFT_TABLE_STATE_DORMANT,
++ NFT_TABLE_STATE_WAKEUP
++};
++
+ static int nf_tables_updtable(struct nft_ctx *ctx)
+ {
+ struct nft_trans *trans;
+@@ -914,19 +920,17 @@ static int nf_tables_updtable(struct nft
+
+ if ((flags & NFT_TABLE_F_DORMANT) &&
+ !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
+- nft_trans_table_enable(trans) = false;
++ nft_trans_table_state(trans) = NFT_TABLE_STATE_DORMANT;
+ } else if (!(flags & NFT_TABLE_F_DORMANT) &&
+ ctx->table->flags & NFT_TABLE_F_DORMANT) {
+- ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
+ ret = nf_tables_table_enable(ctx->net, ctx->table);
+ if (ret >= 0)
+- nft_trans_table_enable(trans) = true;
+- else
+- ctx->table->flags |= NFT_TABLE_F_DORMANT;
++ nft_trans_table_state(trans) = NFT_TABLE_STATE_WAKEUP;
+ }
+ if (ret < 0)
+ goto err;
+
++ nft_trans_table_flags(trans) = flags;
+ nft_trans_table_update(trans) = true;
+ list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+ return 0;
+@@ -7908,11 +7912,10 @@ static int nf_tables_commit(struct net *
+ switch (trans->msg_type) {
+ case NFT_MSG_NEWTABLE:
+ if (nft_trans_table_update(trans)) {
+- if (!nft_trans_table_enable(trans)) {
+- nf_tables_table_disable(net,
+- trans->ctx.table);
+- trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+- }
++ if (nft_trans_table_state(trans) == NFT_TABLE_STATE_DORMANT)
++ nf_tables_table_disable(net, trans->ctx.table);
++
++ trans->ctx.table->flags = nft_trans_table_flags(trans);
+ } else {
+ nft_clear(net, trans->ctx.table);
+ }
+@@ -8125,11 +8128,9 @@ static int __nf_tables_abort(struct net
+ switch (trans->msg_type) {
+ case NFT_MSG_NEWTABLE:
+ if (nft_trans_table_update(trans)) {
+- if (nft_trans_table_enable(trans)) {
+- nf_tables_table_disable(net,
+- trans->ctx.table);
+- trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+- }
++ if (nft_trans_table_state(trans) == NFT_TABLE_STATE_WAKEUP)
++ nf_tables_table_disable(net, trans->ctx.table);
++
+ nft_trans_destroy(trans);
+ } else {
+ list_del_rcu(&trans->ctx.table->list);
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:32 +0100
+Subject: [PATCH] net: resolve forwarding path from virtual netdevice and
+ HW destination address
+
+This patch adds dev_fill_forward_path() which resolves the path to reach
+the real netdevice from the IP forwarding side. This function takes as
+input the netdevice and the destination hardware address and it walks
+down the devices calling .ndo_fill_forward_path() for each device until
+the real device is found.
+
+For instance, assuming the following topology:
+
+ IP forwarding
+ / \
+ br0 eth0
+ / \
+ eth1 eth2
+ .
+ .
+ .
+ ethX
+ ab:cd:ef:ab:cd:ef
+
+where eth1 and eth2 are bridge ports and eth0 provides WAN connectivity.
+ethX is the interface in another box which is connected to the eth1
+bridge port.
+
+For packets going through IP forwarding to br0 whose destination MAC
+address is ab:cd:ef:ab:cd:ef, dev_fill_forward_path() provides the
+following path:
+
+ br0 -> eth1
+
+.ndo_fill_forward_path for br0 looks up at the FDB for the bridge port
+from the destination MAC address to get the bridge port eth1.
+
+This information allows to create a fast path that bypasses the classic
+bridge and IP forwarding paths, so packets go directly from the bridge
+port eth1 to eth0 (wan interface) and vice versa.
+
+ fast path
+ .------------------------.
+ / \
+ | IP forwarding |
+ | / \ \/
+ | br0 eth0
+ . / \
+ -> eth1 eth2
+ .
+ .
+ .
+ ethX
+ ab:cd:ef:ab:cd:ef
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -827,6 +827,27 @@ typedef u16 (*select_queue_fallback_t)(s
+ struct sk_buff *skb,
+ struct net_device *sb_dev);
+
++enum net_device_path_type {
++ DEV_PATH_ETHERNET = 0,
++};
++
++struct net_device_path {
++ enum net_device_path_type type;
++ const struct net_device *dev;
++};
++
++#define NET_DEVICE_PATH_STACK_MAX 5
++
++struct net_device_path_stack {
++ int num_paths;
++ struct net_device_path path[NET_DEVICE_PATH_STACK_MAX];
++};
++
++struct net_device_path_ctx {
++ const struct net_device *dev;
++ const u8 *daddr;
++};
++
+ enum tc_setup_type {
+ TC_SETUP_QDISC_MQPRIO,
+ TC_SETUP_CLSU32,
+@@ -1273,6 +1294,8 @@ struct netdev_net_notifier {
+ * struct net_device *(*ndo_get_peer_dev)(struct net_device *dev);
+ * If a device is paired with a peer device, return the peer instance.
+ * The caller must be under RCU read context.
++ * int (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx, struct net_device_path *path);
++ * Get the forwarding path to reach the real device from the HW destination address
+ */
+ struct net_device_ops {
+ int (*ndo_init)(struct net_device *dev);
+@@ -1481,6 +1504,8 @@ struct net_device_ops {
+ int (*ndo_tunnel_ctl)(struct net_device *dev,
+ struct ip_tunnel_parm *p, int cmd);
+ struct net_device * (*ndo_get_peer_dev)(struct net_device *dev);
++ int (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx,
++ struct net_device_path *path);
+ };
+
+ /**
+@@ -2828,6 +2853,8 @@ void dev_remove_offload(struct packet_of
+
+ int dev_get_iflink(const struct net_device *dev);
+ int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
++int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
++ struct net_device_path_stack *stack);
+ struct net_device *__dev_get_by_flags(struct net *net, unsigned short flags,
+ unsigned short mask);
+ struct net_device *dev_get_by_name(struct net *net, const char *name);
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -847,6 +847,52 @@ int dev_fill_metadata_dst(struct net_dev
+ }
+ EXPORT_SYMBOL_GPL(dev_fill_metadata_dst);
+
++static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)
++{
++ int k = stack->num_paths++;
++
++ if (WARN_ON_ONCE(k >= NET_DEVICE_PATH_STACK_MAX))
++ return NULL;
++
++ return &stack->path[k];
++}
++
++int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
++ struct net_device_path_stack *stack)
++{
++ const struct net_device *last_dev;
++ struct net_device_path_ctx ctx = {
++ .dev = dev,
++ .daddr = daddr,
++ };
++ struct net_device_path *path;
++ int ret = 0;
++
++ stack->num_paths = 0;
++ while (ctx.dev && ctx.dev->netdev_ops->ndo_fill_forward_path) {
++ last_dev = ctx.dev;
++ path = dev_fwd_path(stack);
++ if (!path)
++ return -1;
++
++ memset(path, 0, sizeof(struct net_device_path));
++ ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path);
++ if (ret < 0)
++ return -1;
++
++ if (WARN_ON_ONCE(last_dev == ctx.dev))
++ return -1;
++ }
++ path = dev_fwd_path(stack);
++ if (!path)
++ return -1;
++ path->type = DEV_PATH_ETHERNET;
++ path->dev = ctx.dev;
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(dev_fill_forward_path);
++
+ /**
+ * __dev_get_by_name - find a device by its name
+ * @net: the applicable net namespace
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:33 +0100
+Subject: [PATCH] net: 8021q: resolve forwarding path for vlan devices
+
+Add .ndo_fill_forward_path for vlan devices.
+
+For instance, assuming the following topology:
+
+ IP forwarding
+ / \
+ eth0.100 eth0
+ |
+ eth0
+ .
+ .
+ .
+ ethX
+ ab:cd:ef:ab:cd:ef
+
+For packets going through IP forwarding to eth0.100 whose destination
+MAC address is ab:cd:ef:ab:cd:ef, dev_fill_forward_path() provides the
+following path:
+
+ eth0.100 -> eth0
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -829,11 +829,18 @@ typedef u16 (*select_queue_fallback_t)(s
+
+ enum net_device_path_type {
+ DEV_PATH_ETHERNET = 0,
++ DEV_PATH_VLAN,
+ };
+
+ struct net_device_path {
+ enum net_device_path_type type;
+ const struct net_device *dev;
++ union {
++ struct {
++ u16 id;
++ __be16 proto;
++ } encap;
++ };
+ };
+
+ #define NET_DEVICE_PATH_STACK_MAX 5
+--- a/net/8021q/vlan_dev.c
++++ b/net/8021q/vlan_dev.c
+@@ -770,6 +770,20 @@ static int vlan_dev_get_iflink(const str
+ return real_dev->ifindex;
+ }
+
++static int vlan_dev_fill_forward_path(struct net_device_path_ctx *ctx,
++ struct net_device_path *path)
++{
++ struct vlan_dev_priv *vlan = vlan_dev_priv(ctx->dev);
++
++ path->type = DEV_PATH_VLAN;
++ path->encap.id = vlan->vlan_id;
++ path->encap.proto = vlan->vlan_proto;
++ path->dev = ctx->dev;
++ ctx->dev = vlan->real_dev;
++
++ return 0;
++}
++
+ static const struct ethtool_ops vlan_ethtool_ops = {
+ .get_link_ksettings = vlan_ethtool_get_link_ksettings,
+ .get_drvinfo = vlan_ethtool_get_drvinfo,
+@@ -808,6 +822,7 @@ static const struct net_device_ops vlan_
+ #endif
+ .ndo_fix_features = vlan_dev_fix_features,
+ .ndo_get_iflink = vlan_dev_get_iflink,
++ .ndo_fill_forward_path = vlan_dev_fill_forward_path,
+ };
+
+ static void vlan_dev_free(struct net_device *dev)
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:34 +0100
+Subject: [PATCH] net: bridge: resolve forwarding path for bridge devices
+
+Add .ndo_fill_forward_path for bridge devices.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -830,6 +830,7 @@ typedef u16 (*select_queue_fallback_t)(s
+ enum net_device_path_type {
+ DEV_PATH_ETHERNET = 0,
+ DEV_PATH_VLAN,
++ DEV_PATH_BRIDGE,
+ };
+
+ struct net_device_path {
+--- a/net/bridge/br_device.c
++++ b/net/bridge/br_device.c
+@@ -398,6 +398,32 @@ static int br_del_slave(struct net_devic
+ return br_del_if(br, slave_dev);
+ }
+
++static int br_fill_forward_path(struct net_device_path_ctx *ctx,
++ struct net_device_path *path)
++{
++ struct net_bridge_fdb_entry *f;
++ struct net_bridge_port *dst;
++ struct net_bridge *br;
++
++ if (netif_is_bridge_port(ctx->dev))
++ return -1;
++
++ br = netdev_priv(ctx->dev);
++ f = br_fdb_find_rcu(br, ctx->daddr, 0);
++ if (!f || !f->dst)
++ return -1;
++
++ dst = READ_ONCE(f->dst);
++ if (!dst)
++ return -1;
++
++ path->type = DEV_PATH_BRIDGE;
++ path->dev = dst->br->dev;
++ ctx->dev = dst->dev;
++
++ return 0;
++}
++
+ static const struct ethtool_ops br_ethtool_ops = {
+ .get_drvinfo = br_getinfo,
+ .get_link = ethtool_op_get_link,
+@@ -432,6 +458,7 @@ static const struct net_device_ops br_ne
+ .ndo_bridge_setlink = br_setlink,
+ .ndo_bridge_dellink = br_dellink,
+ .ndo_features_check = passthru_features_check,
++ .ndo_fill_forward_path = br_fill_forward_path,
+ };
+
+ static struct device_type br_type = {
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:35 +0100
+Subject: [PATCH] net: bridge: resolve forwarding path for VLAN tag
+ actions in bridge devices
+
+Depending on the VLAN settings of the bridge and the port, the bridge can
+either add or remove a tag. When vlan filtering is enabled, the fdb lookup
+also needs to know the VLAN tag/proto for the destination address
+To provide this, keep track of the stack of VLAN tags for the path in the
+lookup context
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -841,10 +841,20 @@ struct net_device_path {
+ u16 id;
+ __be16 proto;
+ } encap;
++ struct {
++ enum {
++ DEV_PATH_BR_VLAN_KEEP,
++ DEV_PATH_BR_VLAN_TAG,
++ DEV_PATH_BR_VLAN_UNTAG,
++ } vlan_mode;
++ u16 vlan_id;
++ __be16 vlan_proto;
++ } bridge;
+ };
+ };
+
+ #define NET_DEVICE_PATH_STACK_MAX 5
++#define NET_DEVICE_PATH_VLAN_MAX 2
+
+ struct net_device_path_stack {
+ int num_paths;
+@@ -854,6 +864,12 @@ struct net_device_path_stack {
+ struct net_device_path_ctx {
+ const struct net_device *dev;
+ const u8 *daddr;
++
++ int num_vlans;
++ struct {
++ u16 id;
++ __be16 proto;
++ } vlan[NET_DEVICE_PATH_VLAN_MAX];
+ };
+
+ enum tc_setup_type {
+--- a/net/8021q/vlan_dev.c
++++ b/net/8021q/vlan_dev.c
+@@ -780,6 +780,12 @@ static int vlan_dev_fill_forward_path(st
+ path->encap.proto = vlan->vlan_proto;
+ path->dev = ctx->dev;
+ ctx->dev = vlan->real_dev;
++ if (ctx->num_vlans >= ARRAY_SIZE(ctx->vlan))
++ return -ENOSPC;
++
++ ctx->vlan[ctx->num_vlans].id = vlan->vlan_id;
++ ctx->vlan[ctx->num_vlans].proto = vlan->vlan_proto;
++ ctx->num_vlans++;
+
+ return 0;
+ }
+--- a/net/bridge/br_device.c
++++ b/net/bridge/br_device.c
+@@ -409,7 +409,10 @@ static int br_fill_forward_path(struct n
+ return -1;
+
+ br = netdev_priv(ctx->dev);
+- f = br_fdb_find_rcu(br, ctx->daddr, 0);
++
++ br_vlan_fill_forward_path_pvid(br, ctx, path);
++
++ f = br_fdb_find_rcu(br, ctx->daddr, path->bridge.vlan_id);
+ if (!f || !f->dst)
+ return -1;
+
+@@ -417,10 +420,28 @@ static int br_fill_forward_path(struct n
+ if (!dst)
+ return -1;
+
++ if (br_vlan_fill_forward_path_mode(br, dst, path))
++ return -1;
++
+ path->type = DEV_PATH_BRIDGE;
+ path->dev = dst->br->dev;
+ ctx->dev = dst->dev;
+
++ switch (path->bridge.vlan_mode) {
++ case DEV_PATH_BR_VLAN_TAG:
++ if (ctx->num_vlans >= ARRAY_SIZE(ctx->vlan))
++ return -ENOSPC;
++ ctx->vlan[ctx->num_vlans].id = path->bridge.vlan_id;
++ ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto;
++ ctx->num_vlans++;
++ break;
++ case DEV_PATH_BR_VLAN_UNTAG:
++ ctx->num_vlans--;
++ break;
++ case DEV_PATH_BR_VLAN_KEEP:
++ break;
++ }
++
+ return 0;
+ }
+
+--- a/net/bridge/br_private.h
++++ b/net/bridge/br_private.h
+@@ -1093,6 +1093,13 @@ void br_vlan_notify(const struct net_bri
+ bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
+ const struct net_bridge_vlan *range_end);
+
++void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
++ struct net_device_path_ctx *ctx,
++ struct net_device_path *path);
++int br_vlan_fill_forward_path_mode(struct net_bridge *br,
++ struct net_bridge_port *dst,
++ struct net_device_path *path);
++
+ static inline struct net_bridge_vlan_group *br_vlan_group(
+ const struct net_bridge *br)
+ {
+@@ -1250,6 +1257,19 @@ static inline int nbp_get_num_vlan_infos
+ {
+ return 0;
+ }
++
++static inline void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
++ struct net_device_path_ctx *ctx,
++ struct net_device_path *path)
++{
++}
++
++static inline int br_vlan_fill_forward_path_mode(struct net_bridge *br,
++ struct net_bridge_port *dst,
++ struct net_device_path *path)
++{
++ return 0;
++}
+
+ static inline struct net_bridge_vlan_group *br_vlan_group(
+ const struct net_bridge *br)
+--- a/net/bridge/br_vlan.c
++++ b/net/bridge/br_vlan.c
+@@ -1327,6 +1327,59 @@ int br_vlan_get_pvid_rcu(const struct ne
+ }
+ EXPORT_SYMBOL_GPL(br_vlan_get_pvid_rcu);
+
++void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
++ struct net_device_path_ctx *ctx,
++ struct net_device_path *path)
++{
++ struct net_bridge_vlan_group *vg;
++ int idx = ctx->num_vlans - 1;
++ u16 vid;
++
++ path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
++
++ if (!br_opt_get(br, BROPT_VLAN_ENABLED))
++ return;
++
++ vg = br_vlan_group(br);
++
++ if (idx >= 0 &&
++ ctx->vlan[idx].proto == br->vlan_proto) {
++ vid = ctx->vlan[idx].id;
++ } else {
++ path->bridge.vlan_mode = DEV_PATH_BR_VLAN_TAG;
++ vid = br_get_pvid(vg);
++ }
++
++ path->bridge.vlan_id = vid;
++ path->bridge.vlan_proto = br->vlan_proto;
++}
++
++int br_vlan_fill_forward_path_mode(struct net_bridge *br,
++ struct net_bridge_port *dst,
++ struct net_device_path *path)
++{
++ struct net_bridge_vlan_group *vg;
++ struct net_bridge_vlan *v;
++
++ if (!br_opt_get(br, BROPT_VLAN_ENABLED))
++ return 0;
++
++ vg = nbp_vlan_group_rcu(dst);
++ v = br_vlan_find(vg, path->bridge.vlan_id);
++ if (!v || !br_vlan_should_use(v))
++ return -EINVAL;
++
++ if (!(v->flags & BRIDGE_VLAN_INFO_UNTAGGED))
++ return 0;
++
++ if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG)
++ path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
++ else
++ path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG;
++
++ return 0;
++}
++
+ int br_vlan_get_info(const struct net_device *dev, u16 vid,
+ struct bridge_vlan_info *p_vinfo)
+ {
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:36 +0100
+Subject: [PATCH] net: ppp: resolve forwarding path for bridge pppoe
+ devices
+
+Pass on the PPPoE session ID, destination hardware address and the real
+device.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/drivers/net/ppp/ppp_generic.c
++++ b/drivers/net/ppp/ppp_generic.c
+@@ -1466,12 +1466,34 @@ static void ppp_dev_priv_destructor(stru
+ ppp_destroy_interface(ppp);
+ }
+
++static int ppp_fill_forward_path(struct net_device_path_ctx *ctx,
++ struct net_device_path *path)
++{
++ struct ppp *ppp = netdev_priv(ctx->dev);
++ struct ppp_channel *chan;
++ struct channel *pch;
++
++ if (ppp->flags & SC_MULTILINK)
++ return -EOPNOTSUPP;
++
++ if (list_empty(&ppp->channels))
++ return -ENODEV;
++
++ pch = list_first_entry(&ppp->channels, struct channel, clist);
++ chan = pch->chan;
++ if (!chan->ops->fill_forward_path)
++ return -EOPNOTSUPP;
++
++ return chan->ops->fill_forward_path(ctx, path, chan);
++}
++
+ static const struct net_device_ops ppp_netdev_ops = {
+ .ndo_init = ppp_dev_init,
+ .ndo_uninit = ppp_dev_uninit,
+ .ndo_start_xmit = ppp_start_xmit,
+ .ndo_do_ioctl = ppp_net_ioctl,
+ .ndo_get_stats64 = ppp_get_stats64,
++ .ndo_fill_forward_path = ppp_fill_forward_path,
+ };
+
+ static struct device_type ppp_type = {
+--- a/drivers/net/ppp/pppoe.c
++++ b/drivers/net/ppp/pppoe.c
+@@ -972,8 +972,31 @@ static int pppoe_xmit(struct ppp_channel
+ return __pppoe_xmit(sk, skb);
+ }
+
++static int pppoe_fill_forward_path(struct net_device_path_ctx *ctx,
++ struct net_device_path *path,
++ const struct ppp_channel *chan)
++{
++ struct sock *sk = (struct sock *)chan->private;
++ struct pppox_sock *po = pppox_sk(sk);
++ struct net_device *dev = po->pppoe_dev;
++
++ if (sock_flag(sk, SOCK_DEAD) ||
++ !(sk->sk_state & PPPOX_CONNECTED) || !dev)
++ return -1;
++
++ path->type = DEV_PATH_PPPOE;
++ path->encap.proto = htons(ETH_P_PPP_SES);
++ path->encap.id = be16_to_cpu(po->num);
++ memcpy(path->encap.h_dest, po->pppoe_pa.remote, ETH_ALEN);
++ path->dev = ctx->dev;
++ ctx->dev = dev;
++
++ return 0;
++}
++
+ static const struct ppp_channel_ops pppoe_chan_ops = {
+ .start_xmit = pppoe_xmit,
++ .fill_forward_path = pppoe_fill_forward_path,
+ };
+
+ static int pppoe_recvmsg(struct socket *sock, struct msghdr *m,
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -831,6 +831,7 @@ enum net_device_path_type {
+ DEV_PATH_ETHERNET = 0,
+ DEV_PATH_VLAN,
+ DEV_PATH_BRIDGE,
++ DEV_PATH_PPPOE,
+ };
+
+ struct net_device_path {
+@@ -840,6 +841,7 @@ struct net_device_path {
+ struct {
+ u16 id;
+ __be16 proto;
++ u8 h_dest[ETH_ALEN];
+ } encap;
+ struct {
+ enum {
+--- a/include/linux/ppp_channel.h
++++ b/include/linux/ppp_channel.h
+@@ -28,6 +28,9 @@ struct ppp_channel_ops {
+ int (*start_xmit)(struct ppp_channel *, struct sk_buff *);
+ /* Handle an ioctl call that has come in via /dev/ppp. */
+ int (*ioctl)(struct ppp_channel *, unsigned int, unsigned long);
++ int (*fill_forward_path)(struct net_device_path_ctx *,
++ struct net_device_path *,
++ const struct ppp_channel *);
+ };
+
+ struct ppp_channel {
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:37 +0100
+Subject: [PATCH] net: dsa: resolve forwarding path for dsa slave ports
+
+Add .ndo_fill_forward_path for dsa slave port devices
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -832,6 +832,7 @@ enum net_device_path_type {
+ DEV_PATH_VLAN,
+ DEV_PATH_BRIDGE,
+ DEV_PATH_PPPOE,
++ DEV_PATH_DSA,
+ };
+
+ struct net_device_path {
+@@ -852,6 +853,10 @@ struct net_device_path {
+ u16 vlan_id;
+ __be16 vlan_proto;
+ } bridge;
++ struct {
++ int port;
++ u16 proto;
++ } dsa;
+ };
+ };
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1619,6 +1619,21 @@ static struct devlink_port *dsa_slave_ge
+ return dp->ds->devlink ? &dp->devlink_port : NULL;
+ }
+
++static int dsa_slave_fill_forward_path(struct net_device_path_ctx *ctx,
++ struct net_device_path *path)
++{
++ struct dsa_port *dp = dsa_slave_to_port(ctx->dev);
++ struct dsa_port *cpu_dp = dp->cpu_dp;
++
++ path->dev = ctx->dev;
++ path->type = DEV_PATH_DSA;
++ path->dsa.proto = cpu_dp->tag_ops->proto;
++ path->dsa.port = dp->index;
++ ctx->dev = cpu_dp->master;
++
++ return 0;
++}
++
+ static const struct net_device_ops dsa_slave_netdev_ops = {
+ .ndo_open = dsa_slave_open,
+ .ndo_stop = dsa_slave_close,
+@@ -1644,6 +1659,7 @@ static const struct net_device_ops dsa_s
+ .ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid,
+ .ndo_get_devlink_port = dsa_slave_get_devlink_port,
+ .ndo_change_mtu = dsa_slave_change_mtu,
++ .ndo_fill_forward_path = dsa_slave_fill_forward_path,
+ };
+
+ static struct device_type dsa_type = {
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:38 +0100
+Subject: [PATCH] netfilter: flowtable: add xmit path types
+
+Add the xmit_type field that defines the two supported xmit paths in the
+flowtable data plane, which are the neighbour and the xfrm xmit paths.
+This patch prepares for new flowtable xmit path types to come.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -89,6 +89,11 @@ enum flow_offload_tuple_dir {
+ };
+ #define FLOW_OFFLOAD_DIR_MAX IP_CT_DIR_MAX
+
++enum flow_offload_xmit_type {
++ FLOW_OFFLOAD_XMIT_NEIGH = 0,
++ FLOW_OFFLOAD_XMIT_XFRM,
++};
++
+ struct flow_offload_tuple {
+ union {
+ struct in_addr src_v4;
+@@ -111,7 +116,8 @@ struct flow_offload_tuple {
+ /* All members above are keys for lookups, see flow_offload_hash(). */
+ struct { } __hash;
+
+- u8 dir;
++ u8 dir:6,
++ xmit_type:2;
+
+ u16 mtu;
+
+@@ -157,7 +163,8 @@ static inline __s32 nf_flow_timeout_delt
+
+ struct nf_flow_route {
+ struct {
+- struct dst_entry *dst;
++ struct dst_entry *dst;
++ enum flow_offload_xmit_type xmit_type;
+ } tuple[FLOW_OFFLOAD_DIR_MAX];
+ };
+
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -95,6 +95,7 @@ static int flow_offload_fill_route(struc
+ }
+
+ flow_tuple->iifidx = other_dst->dev->ifindex;
++ flow_tuple->xmit_type = route->tuple[dir].xmit_type;
+ flow_tuple->dst_cache = dst;
+
+ return 0;
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -235,8 +235,6 @@ nf_flow_offload_ip_hook(void *priv, stru
+
+ dir = tuplehash->tuple.dir;
+ flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+- rt = (struct rtable *)flow->tuplehash[dir].tuple.dst_cache;
+- outdev = rt->dst.dev;
+
+ if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
+ return NF_ACCEPT;
+@@ -265,13 +263,16 @@ nf_flow_offload_ip_hook(void *priv, stru
+ if (flow_table->flags & NF_FLOWTABLE_COUNTER)
+ nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
+
+- if (unlikely(dst_xfrm(&rt->dst))) {
++ rt = (struct rtable *)tuplehash->tuple.dst_cache;
++
++ if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
+ memset(skb->cb, 0, sizeof(struct inet_skb_parm));
+ IPCB(skb)->iif = skb->dev->ifindex;
+ IPCB(skb)->flags = IPSKB_FORWARDED;
+ return nf_flow_xmit_xfrm(skb, state, &rt->dst);
+ }
+
++ outdev = rt->dst.dev;
+ skb->dev = outdev;
+ nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
+ skb_dst_set_noref(skb, &rt->dst);
+@@ -456,8 +457,6 @@ nf_flow_offload_ipv6_hook(void *priv, st
+
+ dir = tuplehash->tuple.dir;
+ flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+- rt = (struct rt6_info *)flow->tuplehash[dir].tuple.dst_cache;
+- outdev = rt->dst.dev;
+
+ if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
+ return NF_ACCEPT;
+@@ -485,13 +484,16 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ if (flow_table->flags & NF_FLOWTABLE_COUNTER)
+ nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
+
+- if (unlikely(dst_xfrm(&rt->dst))) {
++ rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
++
++ if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
+ memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
+ IP6CB(skb)->iif = skb->dev->ifindex;
+ IP6CB(skb)->flags = IP6SKB_FORWARDED;
+ return nf_flow_xmit_xfrm(skb, state, &rt->dst);
+ }
+
++ outdev = rt->dst.dev;
+ skb->dev = outdev;
+ nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
+ skb_dst_set_noref(skb, &rt->dst);
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -19,6 +19,22 @@ struct nft_flow_offload {
+ struct nft_flowtable *flowtable;
+ };
+
++static enum flow_offload_xmit_type nft_xmit_type(struct dst_entry *dst)
++{
++ if (dst_xfrm(dst))
++ return FLOW_OFFLOAD_XMIT_XFRM;
++
++ return FLOW_OFFLOAD_XMIT_NEIGH;
++}
++
++static void nft_default_forward_path(struct nf_flow_route *route,
++ struct dst_entry *dst_cache,
++ enum ip_conntrack_dir dir)
++{
++ route->tuple[dir].dst = dst_cache;
++ route->tuple[dir].xmit_type = nft_xmit_type(dst_cache);
++}
++
+ static int nft_flow_route(const struct nft_pktinfo *pkt,
+ const struct nf_conn *ct,
+ struct nf_flow_route *route,
+@@ -44,8 +60,8 @@ static int nft_flow_route(const struct n
+ if (!other_dst)
+ return -ENOENT;
+
+- route->tuple[dir].dst = this_dst;
+- route->tuple[!dir].dst = other_dst;
++ nft_default_forward_path(route, this_dst, dir);
++ nft_default_forward_path(route, other_dst, !dir);
+
+ return 0;
+ }
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:39 +0100
+Subject: [PATCH] netfilter: flowtable: use dev_fill_forward_path() to
+ obtain ingress device
+
+Obtain the ingress device in the tuple from the route in the reply
+direction. Use dev_fill_forward_path() instead to get the real ingress
+device for this flow.
+
+Fall back to use the ingress device that the IP forwarding route
+provides if:
+
+- dev_fill_forward_path() finds no real ingress device.
+- the ingress device that is obtained is not part of the flowtable
+ devices.
+- this route has a xfrm policy.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -164,6 +164,9 @@ static inline __s32 nf_flow_timeout_delt
+ struct nf_flow_route {
+ struct {
+ struct dst_entry *dst;
++ struct {
++ u32 ifindex;
++ } in;
+ enum flow_offload_xmit_type xmit_type;
+ } tuple[FLOW_OFFLOAD_DIR_MAX];
+ };
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -79,7 +79,6 @@ static int flow_offload_fill_route(struc
+ enum flow_offload_tuple_dir dir)
+ {
+ struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
+- struct dst_entry *other_dst = route->tuple[!dir].dst;
+ struct dst_entry *dst = route->tuple[dir].dst;
+
+ if (!dst_hold_safe(route->tuple[dir].dst))
+@@ -94,7 +93,7 @@ static int flow_offload_fill_route(struc
+ break;
+ }
+
+- flow_tuple->iifidx = other_dst->dev->ifindex;
++ flow_tuple->iifidx = route->tuple[dir].in.ifindex;
+ flow_tuple->xmit_type = route->tuple[dir].xmit_type;
+ flow_tuple->dst_cache = dst;
+
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -31,14 +31,104 @@ static void nft_default_forward_path(str
+ struct dst_entry *dst_cache,
+ enum ip_conntrack_dir dir)
+ {
++ route->tuple[!dir].in.ifindex = dst_cache->dev->ifindex;
+ route->tuple[dir].dst = dst_cache;
+ route->tuple[dir].xmit_type = nft_xmit_type(dst_cache);
+ }
+
++static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
++ const struct dst_entry *dst_cache,
++ const struct nf_conn *ct,
++ enum ip_conntrack_dir dir,
++ struct net_device_path_stack *stack)
++{
++ const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
++ struct net_device *dev = dst_cache->dev;
++ unsigned char ha[ETH_ALEN];
++ struct neighbour *n;
++ u8 nud_state;
++
++ n = dst_neigh_lookup(dst_cache, daddr);
++ if (!n)
++ return -1;
++
++ read_lock_bh(&n->lock);
++ nud_state = n->nud_state;
++ ether_addr_copy(ha, n->ha);
++ read_unlock_bh(&n->lock);
++ neigh_release(n);
++
++ if (!(nud_state & NUD_VALID))
++ return -1;
++
++ return dev_fill_forward_path(dev, ha, stack);
++}
++
++struct nft_forward_info {
++ const struct net_device *indev;
++};
++
++static void nft_dev_path_info(const struct net_device_path_stack *stack,
++ struct nft_forward_info *info)
++{
++ const struct net_device_path *path;
++ int i;
++
++ for (i = 0; i < stack->num_paths; i++) {
++ path = &stack->path[i];
++ switch (path->type) {
++ case DEV_PATH_ETHERNET:
++ info->indev = path->dev;
++ break;
++ case DEV_PATH_VLAN:
++ case DEV_PATH_BRIDGE:
++ default:
++ info->indev = NULL;
++ break;
++ }
++ }
++}
++
++static bool nft_flowtable_find_dev(const struct net_device *dev,
++ struct nft_flowtable *ft)
++{
++ struct nft_hook *hook;
++ bool found = false;
++
++ list_for_each_entry_rcu(hook, &ft->hook_list, list) {
++ if (hook->ops.dev != dev)
++ continue;
++
++ found = true;
++ break;
++ }
++
++ return found;
++}
++
++static void nft_dev_forward_path(struct nf_flow_route *route,
++ const struct nf_conn *ct,
++ enum ip_conntrack_dir dir,
++ struct nft_flowtable *ft)
++{
++ const struct dst_entry *dst = route->tuple[dir].dst;
++ struct net_device_path_stack stack;
++ struct nft_forward_info info = {};
++
++ if (nft_dev_fill_forward_path(route, dst, ct, dir, &stack) >= 0)
++ nft_dev_path_info(&stack, &info);
++
++ if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
++ return;
++
++ route->tuple[!dir].in.ifindex = info.indev->ifindex;
++}
++
+ static int nft_flow_route(const struct nft_pktinfo *pkt,
+ const struct nf_conn *ct,
+ struct nf_flow_route *route,
+- enum ip_conntrack_dir dir)
++ enum ip_conntrack_dir dir,
++ struct nft_flowtable *ft)
+ {
+ struct dst_entry *this_dst = skb_dst(pkt->skb);
+ struct dst_entry *other_dst = NULL;
+@@ -63,6 +153,12 @@ static int nft_flow_route(const struct n
+ nft_default_forward_path(route, this_dst, dir);
+ nft_default_forward_path(route, other_dst, !dir);
+
++ if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH &&
++ route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) {
++ nft_dev_forward_path(route, ct, dir, ft);
++ nft_dev_forward_path(route, ct, !dir, ft);
++ }
++
+ return 0;
+ }
+
+@@ -90,8 +186,8 @@ static void nft_flow_offload_eval(const
+ struct nft_flow_offload *priv = nft_expr_priv(expr);
+ struct nf_flowtable *flowtable = &priv->flowtable->data;
+ struct tcphdr _tcph, *tcph = NULL;
++ struct nf_flow_route route = {};
+ enum ip_conntrack_info ctinfo;
+- struct nf_flow_route route;
+ struct flow_offload *flow;
+ enum ip_conntrack_dir dir;
+ struct nf_conn *ct;
+@@ -128,7 +224,7 @@ static void nft_flow_offload_eval(const
+ goto out;
+
+ dir = CTINFO2DIR(ctinfo);
+- if (nft_flow_route(pkt, ct, &route, dir) < 0)
++ if (nft_flow_route(pkt, ct, &route, dir, priv->flowtable) < 0)
+ goto err_flow_route;
+
+ flow = flow_offload_alloc(ct);
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:40 +0100
+Subject: [PATCH] netfilter: flowtable: use dev_fill_forward_path() to
+ obtain egress device
+
+The egress device in the tuple is obtained from route. Use
+dev_fill_forward_path() instead to provide the real egress device for
+this flow whenever this is available.
+
+The new FLOW_OFFLOAD_XMIT_DIRECT type uses dev_queue_xmit() to transmit
+ethernet frames. Cache the source and destination hardware address to
+use dev_queue_xmit() to transfer packets.
+
+The FLOW_OFFLOAD_XMIT_DIRECT replaces FLOW_OFFLOAD_XMIT_NEIGH if
+dev_fill_forward_path() finds a direct transmit path.
+
+In case of topology updates, if peer is moved to different bridge port,
+the connection will time out, reconnect will result in a new entry with
+the correct path. Snooping fdb updates would allow for cleaning up stale
+flowtable entries.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -92,6 +92,7 @@ enum flow_offload_tuple_dir {
+ enum flow_offload_xmit_type {
+ FLOW_OFFLOAD_XMIT_NEIGH = 0,
+ FLOW_OFFLOAD_XMIT_XFRM,
++ FLOW_OFFLOAD_XMIT_DIRECT,
+ };
+
+ struct flow_offload_tuple {
+@@ -120,8 +121,14 @@ struct flow_offload_tuple {
+ xmit_type:2;
+
+ u16 mtu;
+-
+- struct dst_entry *dst_cache;
++ union {
++ struct dst_entry *dst_cache;
++ struct {
++ u32 ifidx;
++ u8 h_source[ETH_ALEN];
++ u8 h_dest[ETH_ALEN];
++ } out;
++ };
+ };
+
+ struct flow_offload_tuple_rhash {
+@@ -167,6 +174,11 @@ struct nf_flow_route {
+ struct {
+ u32 ifindex;
+ } in;
++ struct {
++ u32 ifindex;
++ u8 h_source[ETH_ALEN];
++ u8 h_dest[ETH_ALEN];
++ } out;
+ enum flow_offload_xmit_type xmit_type;
+ } tuple[FLOW_OFFLOAD_DIR_MAX];
+ };
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -81,9 +81,6 @@ static int flow_offload_fill_route(struc
+ struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
+ struct dst_entry *dst = route->tuple[dir].dst;
+
+- if (!dst_hold_safe(route->tuple[dir].dst))
+- return -1;
+-
+ switch (flow_tuple->l3proto) {
+ case NFPROTO_IPV4:
+ flow_tuple->mtu = ip_dst_mtu_maybe_forward(dst, true);
+@@ -94,12 +91,36 @@ static int flow_offload_fill_route(struc
+ }
+
+ flow_tuple->iifidx = route->tuple[dir].in.ifindex;
++
++ switch (route->tuple[dir].xmit_type) {
++ case FLOW_OFFLOAD_XMIT_DIRECT:
++ memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest,
++ ETH_ALEN);
++ memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source,
++ ETH_ALEN);
++ flow_tuple->out.ifidx = route->tuple[dir].out.ifindex;
++ break;
++ case FLOW_OFFLOAD_XMIT_XFRM:
++ case FLOW_OFFLOAD_XMIT_NEIGH:
++ if (!dst_hold_safe(route->tuple[dir].dst))
++ return -1;
++
++ flow_tuple->dst_cache = dst;
++ break;
++ }
+ flow_tuple->xmit_type = route->tuple[dir].xmit_type;
+- flow_tuple->dst_cache = dst;
+
+ return 0;
+ }
+
++static void nft_flow_dst_release(struct flow_offload *flow,
++ enum flow_offload_tuple_dir dir)
++{
++ if (flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
++ flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)
++ dst_release(flow->tuplehash[dir].tuple.dst_cache);
++}
++
+ int flow_offload_route_init(struct flow_offload *flow,
+ const struct nf_flow_route *route)
+ {
+@@ -118,7 +139,7 @@ int flow_offload_route_init(struct flow_
+ return 0;
+
+ err_route_reply:
+- dst_release(route->tuple[FLOW_OFFLOAD_DIR_ORIGINAL].dst);
++ nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_ORIGINAL);
+
+ return err;
+ }
+@@ -169,8 +190,8 @@ static void flow_offload_fixup_ct(struct
+
+ static void flow_offload_route_release(struct flow_offload *flow)
+ {
+- dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_cache);
+- dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_cache);
++ nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_ORIGINAL);
++ nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_REPLY);
+ }
+
+ void flow_offload_free(struct flow_offload *flow)
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -207,6 +207,24 @@ static unsigned int nf_flow_xmit_xfrm(st
+ return NF_STOLEN;
+ }
+
++static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,
++ const struct flow_offload_tuple_rhash *tuplehash,
++ unsigned short type)
++{
++ struct net_device *outdev;
++
++ outdev = dev_get_by_index_rcu(net, tuplehash->tuple.out.ifidx);
++ if (!outdev)
++ return NF_DROP;
++
++ skb->dev = outdev;
++ dev_hard_header(skb, skb->dev, type, tuplehash->tuple.out.h_dest,
++ tuplehash->tuple.out.h_source, skb->len);
++ dev_queue_xmit(skb);
++
++ return NF_STOLEN;
++}
++
+ unsigned int
+ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
+ const struct nf_hook_state *state)
+@@ -222,6 +240,7 @@ nf_flow_offload_ip_hook(void *priv, stru
+ struct iphdr *iph;
+ __be32 nexthop;
+ u32 hdrsize;
++ int ret;
+
+ if (skb->protocol != htons(ETH_P_IP))
+ return NF_ACCEPT;
+@@ -244,9 +263,13 @@ nf_flow_offload_ip_hook(void *priv, stru
+ if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+ return NF_ACCEPT;
+
+- if (!dst_check(&rt->dst, 0)) {
+- flow_offload_teardown(flow);
+- return NF_ACCEPT;
++ if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
++ tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
++ rt = (struct rtable *)tuplehash->tuple.dst_cache;
++ if (!dst_check(&rt->dst, 0)) {
++ flow_offload_teardown(flow);
++ return NF_ACCEPT;
++ }
+ }
+
+ if (skb_try_make_writable(skb, thoff + hdrsize))
+@@ -263,8 +286,6 @@ nf_flow_offload_ip_hook(void *priv, stru
+ if (flow_table->flags & NF_FLOWTABLE_COUNTER)
+ nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
+
+- rt = (struct rtable *)tuplehash->tuple.dst_cache;
+-
+ if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
+ memset(skb->cb, 0, sizeof(struct inet_skb_parm));
+ IPCB(skb)->iif = skb->dev->ifindex;
+@@ -272,13 +293,23 @@ nf_flow_offload_ip_hook(void *priv, stru
+ return nf_flow_xmit_xfrm(skb, state, &rt->dst);
+ }
+
+- outdev = rt->dst.dev;
+- skb->dev = outdev;
+- nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
+- skb_dst_set_noref(skb, &rt->dst);
+- neigh_xmit(NEIGH_ARP_TABLE, outdev, &nexthop, skb);
++ switch (tuplehash->tuple.xmit_type) {
++ case FLOW_OFFLOAD_XMIT_NEIGH:
++ outdev = rt->dst.dev;
++ skb->dev = outdev;
++ nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
++ skb_dst_set_noref(skb, &rt->dst);
++ neigh_xmit(NEIGH_ARP_TABLE, outdev, &nexthop, skb);
++ ret = NF_STOLEN;
++ break;
++ case FLOW_OFFLOAD_XMIT_DIRECT:
++ ret = nf_flow_queue_xmit(state->net, skb, tuplehash, ETH_P_IP);
++ if (ret == NF_DROP)
++ flow_offload_teardown(flow);
++ break;
++ }
+
+- return NF_STOLEN;
++ return ret;
+ }
+ EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
+
+@@ -444,6 +475,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ struct ipv6hdr *ip6h;
+ struct rt6_info *rt;
+ u32 hdrsize;
++ int ret;
+
+ if (skb->protocol != htons(ETH_P_IPV6))
+ return NF_ACCEPT;
+@@ -465,9 +497,13 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ sizeof(*ip6h)))
+ return NF_ACCEPT;
+
+- if (!dst_check(&rt->dst, 0)) {
+- flow_offload_teardown(flow);
+- return NF_ACCEPT;
++ if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
++ tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
++ rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
++ if (!dst_check(&rt->dst, 0)) {
++ flow_offload_teardown(flow);
++ return NF_ACCEPT;
++ }
+ }
+
+ if (skb_try_make_writable(skb, sizeof(*ip6h) + hdrsize))
+@@ -484,8 +520,6 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ if (flow_table->flags & NF_FLOWTABLE_COUNTER)
+ nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
+
+- rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
+-
+ if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
+ memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
+ IP6CB(skb)->iif = skb->dev->ifindex;
+@@ -493,12 +527,22 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ return nf_flow_xmit_xfrm(skb, state, &rt->dst);
+ }
+
+- outdev = rt->dst.dev;
+- skb->dev = outdev;
+- nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
+- skb_dst_set_noref(skb, &rt->dst);
+- neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb);
++ switch (tuplehash->tuple.xmit_type) {
++ case FLOW_OFFLOAD_XMIT_NEIGH:
++ outdev = rt->dst.dev;
++ skb->dev = outdev;
++ nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
++ skb_dst_set_noref(skb, &rt->dst);
++ neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb);
++ ret = NF_STOLEN;
++ break;
++ case FLOW_OFFLOAD_XMIT_DIRECT:
++ ret = nf_flow_queue_xmit(state->net, skb, tuplehash, ETH_P_IPV6);
++ if (ret == NF_DROP)
++ flow_offload_teardown(flow);
++ break;
++ }
+
+- return NF_STOLEN;
++ return ret;
+ }
+ EXPORT_SYMBOL_GPL(nf_flow_offload_ipv6_hook);
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -39,12 +39,11 @@ static void nft_default_forward_path(str
+ static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
+ const struct dst_entry *dst_cache,
+ const struct nf_conn *ct,
+- enum ip_conntrack_dir dir,
++ enum ip_conntrack_dir dir, u8 *ha,
+ struct net_device_path_stack *stack)
+ {
+ const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
+ struct net_device *dev = dst_cache->dev;
+- unsigned char ha[ETH_ALEN];
+ struct neighbour *n;
+ u8 nud_state;
+
+@@ -66,27 +65,43 @@ static int nft_dev_fill_forward_path(con
+
+ struct nft_forward_info {
+ const struct net_device *indev;
++ const struct net_device *outdev;
++ u8 h_source[ETH_ALEN];
++ u8 h_dest[ETH_ALEN];
++ enum flow_offload_xmit_type xmit_type;
+ };
+
+ static void nft_dev_path_info(const struct net_device_path_stack *stack,
+- struct nft_forward_info *info)
++ struct nft_forward_info *info,
++ unsigned char *ha)
+ {
+ const struct net_device_path *path;
+ int i;
+
++ memcpy(info->h_dest, ha, ETH_ALEN);
++
+ for (i = 0; i < stack->num_paths; i++) {
+ path = &stack->path[i];
+ switch (path->type) {
+ case DEV_PATH_ETHERNET:
+ info->indev = path->dev;
++ if (is_zero_ether_addr(info->h_source))
++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
+ break;
+- case DEV_PATH_VLAN:
+ case DEV_PATH_BRIDGE:
++ if (is_zero_ether_addr(info->h_source))
++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
++
++ info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
++ break;
++ case DEV_PATH_VLAN:
+ default:
+ info->indev = NULL;
+ break;
+ }
+ }
++ if (!info->outdev)
++ info->outdev = info->indev;
+ }
+
+ static bool nft_flowtable_find_dev(const struct net_device *dev,
+@@ -114,14 +129,22 @@ static void nft_dev_forward_path(struct
+ const struct dst_entry *dst = route->tuple[dir].dst;
+ struct net_device_path_stack stack;
+ struct nft_forward_info info = {};
++ unsigned char ha[ETH_ALEN];
+
+- if (nft_dev_fill_forward_path(route, dst, ct, dir, &stack) >= 0)
+- nft_dev_path_info(&stack, &info);
++ if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
++ nft_dev_path_info(&stack, &info, ha);
+
+ if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
+ return;
+
+ route->tuple[!dir].in.ifindex = info.indev->ifindex;
++
++ if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
++ memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
++ memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN);
++ route->tuple[dir].out.ifindex = info.outdev->ifindex;
++ route->tuple[dir].xmit_type = info.xmit_type;
++ }
+ }
+
+ static int nft_flow_route(const struct nft_pktinfo *pkt,
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:41 +0100
+Subject: [PATCH] netfilter: flowtable: add vlan support
+
+Add the vlan id and protocol to the flow tuple to uniquely identify
+flows from the receive path. For the transmit path, dev_hard_header() on
+the vlan device push the headers. This patch includes support for two
+vlan headers (QinQ) from the ingress path.
+
+Add a generic encap field to the flowtable entry which stores the
+protocol and the tag id. This allows to reuse these fields in the PPPoE
+support coming in a later patch.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -95,6 +95,8 @@ enum flow_offload_xmit_type {
+ FLOW_OFFLOAD_XMIT_DIRECT,
+ };
+
++#define NF_FLOW_TABLE_ENCAP_MAX 2
++
+ struct flow_offload_tuple {
+ union {
+ struct in_addr src_v4;
+@@ -113,13 +115,17 @@ struct flow_offload_tuple {
+
+ u8 l3proto;
+ u8 l4proto;
++ struct {
++ u16 id;
++ __be16 proto;
++ } encap[NF_FLOW_TABLE_ENCAP_MAX];
+
+ /* All members above are keys for lookups, see flow_offload_hash(). */
+ struct { } __hash;
+
+- u8 dir:6,
+- xmit_type:2;
+-
++ u8 dir:4,
++ xmit_type:2,
++ encap_num:2;
+ u16 mtu;
+ union {
+ struct dst_entry *dst_cache;
+@@ -173,6 +179,11 @@ struct nf_flow_route {
+ struct dst_entry *dst;
+ struct {
+ u32 ifindex;
++ struct {
++ u16 id;
++ __be16 proto;
++ } encap[NF_FLOW_TABLE_ENCAP_MAX];
++ u8 num_encaps;
+ } in;
+ struct {
+ u32 ifindex;
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -80,6 +80,7 @@ static int flow_offload_fill_route(struc
+ {
+ struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
+ struct dst_entry *dst = route->tuple[dir].dst;
++ int i, j = 0;
+
+ switch (flow_tuple->l3proto) {
+ case NFPROTO_IPV4:
+@@ -91,6 +92,12 @@ static int flow_offload_fill_route(struc
+ }
+
+ flow_tuple->iifidx = route->tuple[dir].in.ifindex;
++ for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) {
++ flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id;
++ flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto;
++ j++;
++ }
++ flow_tuple->encap_num = route->tuple[dir].in.num_encaps;
+
+ switch (route->tuple[dir].xmit_type) {
+ case FLOW_OFFLOAD_XMIT_DIRECT:
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -136,23 +136,44 @@ static bool ip_has_options(unsigned int
+ return thoff != sizeof(struct iphdr);
+ }
+
++static void nf_flow_tuple_encap(struct sk_buff *skb,
++ struct flow_offload_tuple *tuple)
++{
++ int i = 0;
++
++ if (skb_vlan_tag_present(skb)) {
++ tuple->encap[i].id = skb_vlan_tag_get(skb);
++ tuple->encap[i].proto = skb->vlan_proto;
++ i++;
++ }
++ if (skb->protocol == htons(ETH_P_8021Q)) {
++ struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
++
++ tuple->encap[i].id = ntohs(veth->h_vlan_TCI);
++ tuple->encap[i].proto = skb->protocol;
++ }
++}
++
+ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
+- struct flow_offload_tuple *tuple, u32 *hdrsize)
++ struct flow_offload_tuple *tuple, u32 *hdrsize,
++ u32 offset)
+ {
+ struct flow_ports *ports;
+ unsigned int thoff;
+ struct iphdr *iph;
+
+- if (!pskb_may_pull(skb, sizeof(*iph)))
++ if (!pskb_may_pull(skb, sizeof(*iph) + offset))
+ return -1;
+
+- iph = ip_hdr(skb);
+- thoff = iph->ihl * 4;
++ iph = (struct iphdr *)(skb_network_header(skb) + offset);
++ thoff = (iph->ihl * 4);
+
+ if (ip_is_fragment(iph) ||
+ unlikely(ip_has_options(thoff)))
+ return -1;
+
++ thoff += offset;
++
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ *hdrsize = sizeof(struct tcphdr);
+@@ -167,11 +188,10 @@ static int nf_flow_tuple_ip(struct sk_bu
+ if (iph->ttl <= 1)
+ return -1;
+
+- thoff = iph->ihl * 4;
+ if (!pskb_may_pull(skb, thoff + *hdrsize))
+ return -1;
+
+- iph = ip_hdr(skb);
++ iph = (struct iphdr *)(skb_network_header(skb) + offset);
+ ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
+
+ tuple->src_v4.s_addr = iph->saddr;
+@@ -181,6 +201,7 @@ static int nf_flow_tuple_ip(struct sk_bu
+ tuple->l3proto = AF_INET;
+ tuple->l4proto = iph->protocol;
+ tuple->iifidx = dev->ifindex;
++ nf_flow_tuple_encap(skb, tuple);
+
+ return 0;
+ }
+@@ -207,6 +228,43 @@ static unsigned int nf_flow_xmit_xfrm(st
+ return NF_STOLEN;
+ }
+
++static bool nf_flow_skb_encap_protocol(const struct sk_buff *skb, __be16 proto,
++ u32 *offset)
++{
++ if (skb->protocol == htons(ETH_P_8021Q)) {
++ struct vlan_ethhdr *veth;
++
++ veth = (struct vlan_ethhdr *)skb_mac_header(skb);
++ if (veth->h_vlan_encapsulated_proto == proto) {
++ *offset += VLAN_HLEN;
++ return true;
++ }
++ }
++
++ return false;
++}
++
++static void nf_flow_encap_pop(struct sk_buff *skb,
++ struct flow_offload_tuple_rhash *tuplehash)
++{
++ struct vlan_hdr *vlan_hdr;
++ int i;
++
++ for (i = 0; i < tuplehash->tuple.encap_num; i++) {
++ if (skb_vlan_tag_present(skb)) {
++ __vlan_hwaccel_clear_tag(skb);
++ continue;
++ }
++ if (skb->protocol == htons(ETH_P_8021Q)) {
++ vlan_hdr = (struct vlan_hdr *)skb->data;
++ __skb_pull(skb, VLAN_HLEN);
++ vlan_set_encap_proto(skb, vlan_hdr);
++ skb_reset_network_header(skb);
++ break;
++ }
++ }
++}
++
+ static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,
+ const struct flow_offload_tuple_rhash *tuplehash,
+ unsigned short type)
+@@ -235,17 +293,18 @@ nf_flow_offload_ip_hook(void *priv, stru
+ enum flow_offload_tuple_dir dir;
+ struct flow_offload *flow;
+ struct net_device *outdev;
++ u32 hdrsize, offset = 0;
++ unsigned int thoff, mtu;
+ struct rtable *rt;
+- unsigned int thoff;
+ struct iphdr *iph;
+ __be32 nexthop;
+- u32 hdrsize;
+ int ret;
+
+- if (skb->protocol != htons(ETH_P_IP))
++ if (skb->protocol != htons(ETH_P_IP) &&
++ !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IP), &offset))
+ return NF_ACCEPT;
+
+- if (nf_flow_tuple_ip(skb, state->in, &tuple, &hdrsize) < 0)
++ if (nf_flow_tuple_ip(skb, state->in, &tuple, &hdrsize, offset) < 0)
+ return NF_ACCEPT;
+
+ tuplehash = flow_offload_lookup(flow_table, &tuple);
+@@ -255,11 +314,12 @@ nf_flow_offload_ip_hook(void *priv, stru
+ dir = tuplehash->tuple.dir;
+ flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+
+- if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
++ mtu = flow->tuplehash[dir].tuple.mtu + offset;
++ if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
+ return NF_ACCEPT;
+
+- iph = ip_hdr(skb);
+- thoff = iph->ihl * 4;
++ iph = (struct iphdr *)(skb_network_header(skb) + offset);
++ thoff = (iph->ihl * 4) + offset;
+ if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+ return NF_ACCEPT;
+
+@@ -277,6 +337,9 @@ nf_flow_offload_ip_hook(void *priv, stru
+
+ flow_offload_refresh(flow_table, flow);
+
++ nf_flow_encap_pop(skb, tuplehash);
++ thoff -= offset;
++
+ iph = ip_hdr(skb);
+ nf_flow_nat_ip(flow, skb, thoff, dir, iph);
+
+@@ -418,16 +481,18 @@ static void nf_flow_nat_ipv6(const struc
+ }
+
+ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
+- struct flow_offload_tuple *tuple, u32 *hdrsize)
++ struct flow_offload_tuple *tuple, u32 *hdrsize,
++ u32 offset)
+ {
+ struct flow_ports *ports;
+ struct ipv6hdr *ip6h;
+ unsigned int thoff;
+
+- if (!pskb_may_pull(skb, sizeof(*ip6h)))
++ thoff = sizeof(*ip6h) + offset;
++ if (!pskb_may_pull(skb, thoff))
+ return -1;
+
+- ip6h = ipv6_hdr(skb);
++ ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
+
+ switch (ip6h->nexthdr) {
+ case IPPROTO_TCP:
+@@ -443,11 +508,10 @@ static int nf_flow_tuple_ipv6(struct sk_
+ if (ip6h->hop_limit <= 1)
+ return -1;
+
+- thoff = sizeof(*ip6h);
+ if (!pskb_may_pull(skb, thoff + *hdrsize))
+ return -1;
+
+- ip6h = ipv6_hdr(skb);
++ ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
+ ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
+
+ tuple->src_v6 = ip6h->saddr;
+@@ -457,6 +521,7 @@ static int nf_flow_tuple_ipv6(struct sk_
+ tuple->l3proto = AF_INET6;
+ tuple->l4proto = ip6h->nexthdr;
+ tuple->iifidx = dev->ifindex;
++ nf_flow_tuple_encap(skb, tuple);
+
+ return 0;
+ }
+@@ -472,15 +537,17 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ const struct in6_addr *nexthop;
+ struct flow_offload *flow;
+ struct net_device *outdev;
++ unsigned int thoff, mtu;
++ u32 hdrsize, offset = 0;
+ struct ipv6hdr *ip6h;
+ struct rt6_info *rt;
+- u32 hdrsize;
+ int ret;
+
+- if (skb->protocol != htons(ETH_P_IPV6))
++ if (skb->protocol != htons(ETH_P_IPV6) &&
++ !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IPV6), &offset))
+ return NF_ACCEPT;
+
+- if (nf_flow_tuple_ipv6(skb, state->in, &tuple, &hdrsize) < 0)
++ if (nf_flow_tuple_ipv6(skb, state->in, &tuple, &hdrsize, offset) < 0)
+ return NF_ACCEPT;
+
+ tuplehash = flow_offload_lookup(flow_table, &tuple);
+@@ -490,11 +557,13 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ dir = tuplehash->tuple.dir;
+ flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+
+- if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
++ mtu = flow->tuplehash[dir].tuple.mtu + offset;
++ if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
+ return NF_ACCEPT;
+
+- if (nf_flow_state_check(flow, ipv6_hdr(skb)->nexthdr, skb,
+- sizeof(*ip6h)))
++ ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
++ thoff = sizeof(*ip6h) + offset;
++ if (nf_flow_state_check(flow, ip6h->nexthdr, skb, thoff))
+ return NF_ACCEPT;
+
+ if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
+@@ -506,11 +575,13 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ }
+ }
+
+- if (skb_try_make_writable(skb, sizeof(*ip6h) + hdrsize))
++ if (skb_try_make_writable(skb, thoff + hdrsize))
+ return NF_DROP;
+
+ flow_offload_refresh(flow_table, flow);
+
++ nf_flow_encap_pop(skb, tuplehash);
++
+ ip6h = ipv6_hdr(skb);
+ nf_flow_nat_ipv6(flow, skb, dir, ip6h);
+
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -66,6 +66,11 @@ static int nft_dev_fill_forward_path(con
+ struct nft_forward_info {
+ const struct net_device *indev;
+ const struct net_device *outdev;
++ struct id {
++ __u16 id;
++ __be16 proto;
++ } encap[NF_FLOW_TABLE_ENCAP_MAX];
++ u8 num_encaps;
+ u8 h_source[ETH_ALEN];
+ u8 h_dest[ETH_ALEN];
+ enum flow_offload_xmit_type xmit_type;
+@@ -84,9 +89,23 @@ static void nft_dev_path_info(const stru
+ path = &stack->path[i];
+ switch (path->type) {
+ case DEV_PATH_ETHERNET:
++ case DEV_PATH_VLAN:
+ info->indev = path->dev;
+ if (is_zero_ether_addr(info->h_source))
+ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
++
++ if (path->type == DEV_PATH_ETHERNET)
++ break;
++
++ /* DEV_PATH_VLAN */
++ if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
++ info->indev = NULL;
++ break;
++ }
++ info->outdev = path->dev;
++ info->encap[info->num_encaps].id = path->encap.id;
++ info->encap[info->num_encaps].proto = path->encap.proto;
++ info->num_encaps++;
+ break;
+ case DEV_PATH_BRIDGE:
+ if (is_zero_ether_addr(info->h_source))
+@@ -94,7 +113,6 @@ static void nft_dev_path_info(const stru
+
+ info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
+ break;
+- case DEV_PATH_VLAN:
+ default:
+ info->indev = NULL;
+ break;
+@@ -130,6 +148,7 @@ static void nft_dev_forward_path(struct
+ struct net_device_path_stack stack;
+ struct nft_forward_info info = {};
+ unsigned char ha[ETH_ALEN];
++ int i;
+
+ if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
+ nft_dev_path_info(&stack, &info, ha);
+@@ -138,6 +157,11 @@ static void nft_dev_forward_path(struct
+ return;
+
+ route->tuple[!dir].in.ifindex = info.indev->ifindex;
++ for (i = 0; i < info.num_encaps; i++) {
++ route->tuple[!dir].in.encap[i].id = info.encap[i].id;
++ route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
++ }
++ route->tuple[!dir].in.num_encaps = info.num_encaps;
+
+ if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
+ memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:42 +0100
+Subject: [PATCH] netfilter: flowtable: add bridge vlan filtering support
+
+Add the vlan tag based when PVID is set on.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -111,6 +111,18 @@ static void nft_dev_path_info(const stru
+ if (is_zero_ether_addr(info->h_source))
+ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
+
++ switch (path->bridge.vlan_mode) {
++ case DEV_PATH_BR_VLAN_TAG:
++ info->encap[info->num_encaps].id = path->bridge.vlan_id;
++ info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
++ info->num_encaps++;
++ break;
++ case DEV_PATH_BR_VLAN_UNTAG:
++ info->num_encaps--;
++ break;
++ case DEV_PATH_BR_VLAN_KEEP:
++ break;
++ }
+ info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
+ break;
+ default:
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:43 +0100
+Subject: [PATCH] netfilter: flowtable: add pppoe support
+
+Add the PPPoE protocol and session id to the flow tuple using the encap
+fields to uniquely identify flows from the receive path. For the
+transmit path, dev_hard_header() on the vlan device push the headers.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -7,6 +7,9 @@
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>
+ #include <linux/netdevice.h>
++#include <linux/if_ether.h>
++#include <linux/if_pppox.h>
++#include <linux/ppp_defs.h>
+ #include <net/ip.h>
+ #include <net/ipv6.h>
+ #include <net/ip6_route.h>
+@@ -139,6 +142,8 @@ static bool ip_has_options(unsigned int
+ static void nf_flow_tuple_encap(struct sk_buff *skb,
+ struct flow_offload_tuple *tuple)
+ {
++ struct vlan_ethhdr *veth;
++ struct pppoe_hdr *phdr;
+ int i = 0;
+
+ if (skb_vlan_tag_present(skb)) {
+@@ -146,11 +151,17 @@ static void nf_flow_tuple_encap(struct s
+ tuple->encap[i].proto = skb->vlan_proto;
+ i++;
+ }
+- if (skb->protocol == htons(ETH_P_8021Q)) {
+- struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
+-
++ switch (skb->protocol) {
++ case htons(ETH_P_8021Q):
++ veth = (struct vlan_ethhdr *)skb_mac_header(skb);
+ tuple->encap[i].id = ntohs(veth->h_vlan_TCI);
+ tuple->encap[i].proto = skb->protocol;
++ break;
++ case htons(ETH_P_PPP_SES):
++ phdr = (struct pppoe_hdr *)skb_mac_header(skb);
++ tuple->encap[i].id = ntohs(phdr->sid);
++ tuple->encap[i].proto = skb->protocol;
++ break;
+ }
+ }
+
+@@ -228,17 +239,41 @@ static unsigned int nf_flow_xmit_xfrm(st
+ return NF_STOLEN;
+ }
+
++static inline __be16 nf_flow_pppoe_proto(const struct sk_buff *skb)
++{
++ __be16 proto;
++
++ proto = *((__be16 *)(skb_mac_header(skb) + ETH_HLEN +
++ sizeof(struct pppoe_hdr)));
++ switch (proto) {
++ case htons(PPP_IP):
++ return htons(ETH_P_IP);
++ case htons(PPP_IPV6):
++ return htons(ETH_P_IPV6);
++ }
++
++ return 0;
++}
++
+ static bool nf_flow_skb_encap_protocol(const struct sk_buff *skb, __be16 proto,
+ u32 *offset)
+ {
+- if (skb->protocol == htons(ETH_P_8021Q)) {
+- struct vlan_ethhdr *veth;
++ struct vlan_ethhdr *veth;
+
++ switch (skb->protocol) {
++ case htons(ETH_P_8021Q):
+ veth = (struct vlan_ethhdr *)skb_mac_header(skb);
+ if (veth->h_vlan_encapsulated_proto == proto) {
+ *offset += VLAN_HLEN;
+ return true;
+ }
++ break;
++ case htons(ETH_P_PPP_SES):
++ if (nf_flow_pppoe_proto(skb) == proto) {
++ *offset += PPPOE_SES_HLEN;
++ return true;
++ }
++ break;
+ }
+
+ return false;
+@@ -255,12 +290,18 @@ static void nf_flow_encap_pop(struct sk_
+ __vlan_hwaccel_clear_tag(skb);
+ continue;
+ }
+- if (skb->protocol == htons(ETH_P_8021Q)) {
++ switch (skb->protocol) {
++ case htons(ETH_P_8021Q):
+ vlan_hdr = (struct vlan_hdr *)skb->data;
+ __skb_pull(skb, VLAN_HLEN);
+ vlan_set_encap_proto(skb, vlan_hdr);
+ skb_reset_network_header(skb);
+ break;
++ case htons(ETH_P_PPP_SES):
++ skb->protocol = nf_flow_pppoe_proto(skb);
++ skb_pull(skb, PPPOE_SES_HLEN);
++ skb_reset_network_header(skb);
++ break;
+ }
+ }
+ }
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -90,6 +90,7 @@ static void nft_dev_path_info(const stru
+ switch (path->type) {
+ case DEV_PATH_ETHERNET:
+ case DEV_PATH_VLAN:
++ case DEV_PATH_PPPOE:
+ info->indev = path->dev;
+ if (is_zero_ether_addr(info->h_source))
+ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
+@@ -97,7 +98,7 @@ static void nft_dev_path_info(const stru
+ if (path->type == DEV_PATH_ETHERNET)
+ break;
+
+- /* DEV_PATH_VLAN */
++ /* DEV_PATH_VLAN and DEV_PATH_PPPOE */
+ if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
+ info->indev = NULL;
+ break;
+@@ -106,6 +107,8 @@ static void nft_dev_path_info(const stru
+ info->encap[info->num_encaps].id = path->encap.id;
+ info->encap[info->num_encaps].proto = path->encap.proto;
+ info->num_encaps++;
++ if (path->type == DEV_PATH_PPPOE)
++ memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN);
+ break;
+ case DEV_PATH_BRIDGE:
+ if (is_zero_ether_addr(info->h_source))
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:44 +0100
+Subject: [PATCH] netfilter: flowtable: add dsa support
+
+Replace the master ethernet device by the dsa slave port. Packets coming
+in from the software ingress path use the dsa slave port as input
+device.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -89,6 +89,7 @@ static void nft_dev_path_info(const stru
+ path = &stack->path[i];
+ switch (path->type) {
+ case DEV_PATH_ETHERNET:
++ case DEV_PATH_DSA:
+ case DEV_PATH_VLAN:
+ case DEV_PATH_PPPOE:
+ info->indev = path->dev;
+@@ -97,6 +98,10 @@ static void nft_dev_path_info(const stru
+
+ if (path->type == DEV_PATH_ETHERNET)
+ break;
++ if (path->type == DEV_PATH_DSA) {
++ i = stack->num_paths;
++ break;
++ }
+
+ /* DEV_PATH_VLAN and DEV_PATH_PPPOE */
+ if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:45 +0100
+Subject: [PATCH] selftests: netfilter: flowtable bridge and vlan support
+
+This patch adds two new tests to cover bridge and vlan support:
+
+- Add a bridge device to the Router1 (nsr1) container and attach the
+ veth0 device to the bridge. Set the IP address to the bridge device
+ to exercise the bridge forwarding path.
+
+- Add vlan encapsulation between to the bridge device in the Router1 and
+ one of the sender containers (ns1).
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/tools/testing/selftests/netfilter/nft_flowtable.sh
++++ b/tools/testing/selftests/netfilter/nft_flowtable.sh
+@@ -370,6 +370,88 @@ else
+ ip netns exec nsr1 nft list ruleset
+ fi
+
++# Another test:
++# Add bridge interface br0 to Router1, with NAT enabled.
++ip -net nsr1 link add name br0 type bridge
++ip -net nsr1 addr flush dev veth0
++ip -net nsr1 link set up dev veth0
++ip -net nsr1 link set veth0 master br0
++ip -net nsr1 addr add 10.0.1.1/24 dev br0
++ip -net nsr1 addr add dead:1::1/64 dev br0
++ip -net nsr1 link set up dev br0
++
++ip netns exec nsr1 sysctl net.ipv4.conf.br0.forwarding=1 > /dev/null
++
++# br0 with NAT enabled.
++ip netns exec nsr1 nft -f - <<EOF
++flush table ip nat
++table ip nat {
++ chain prerouting {
++ type nat hook prerouting priority 0; policy accept;
++ meta iif "br0" ip daddr 10.6.6.6 tcp dport 1666 counter dnat ip to 10.0.2.99:12345
++ }
++
++ chain postrouting {
++ type nat hook postrouting priority 0; policy accept;
++ meta oifname "veth1" counter masquerade
++ }
++}
++EOF
++
++if test_tcp_forwarding_nat ns1 ns2; then
++ echo "PASS: flow offloaded for ns1/ns2 with bridge NAT"
++else
++ echo "FAIL: flow offload for ns1/ns2 with bridge NAT" 1>&2
++ ip netns exec nsr1 nft list ruleset
++ ret=1
++fi
++
++# Another test:
++# Add bridge interface br0 to Router1, with NAT and VLAN.
++ip -net nsr1 link set veth0 nomaster
++ip -net nsr1 link set down dev veth0
++ip -net nsr1 link add link veth0 name veth0.10 type vlan id 10
++ip -net nsr1 link set up dev veth0
++ip -net nsr1 link set up dev veth0.10
++ip -net nsr1 link set veth0.10 master br0
++
++ip -net ns1 addr flush dev eth0
++ip -net ns1 link add link eth0 name eth0.10 type vlan id 10
++ip -net ns1 link set eth0 up
++ip -net ns1 link set eth0.10 up
++ip -net ns1 addr add 10.0.1.99/24 dev eth0.10
++ip -net ns1 route add default via 10.0.1.1
++ip -net ns1 addr add dead:1::99/64 dev eth0.10
++
++if test_tcp_forwarding_nat ns1 ns2; then
++ echo "PASS: flow offloaded for ns1/ns2 with bridge NAT and VLAN"
++else
++ echo "FAIL: flow offload for ns1/ns2 with bridge NAT and VLAN" 1>&2
++ ip netns exec nsr1 nft list ruleset
++ ret=1
++fi
++
++# restore test topology (remove bridge and VLAN)
++ip -net nsr1 link set veth0 nomaster
++ip -net nsr1 link set veth0 down
++ip -net nsr1 link set veth0.10 down
++ip -net nsr1 link delete veth0.10 type vlan
++ip -net nsr1 link delete br0 type bridge
++ip -net ns1 addr flush dev eth0.10
++ip -net ns1 link set eth0.10 down
++ip -net ns1 link set eth0 down
++ip -net ns1 link delete eth0.10 type vlan
++
++# restore address in ns1 and nsr1
++ip -net ns1 link set eth0 up
++ip -net ns1 addr add 10.0.1.99/24 dev eth0
++ip -net ns1 route add default via 10.0.1.1
++ip -net ns1 addr add dead:1::99/64 dev eth0
++ip -net ns1 route add default via dead:1::1
++ip -net nsr1 addr add 10.0.1.1/24 dev veth0
++ip -net nsr1 addr add dead:1::1/64 dev veth0
++ip -net nsr1 link set up dev veth0
++
+ KEY_SHA="0x"$(ps -xaf | sha1sum | cut -d " " -f 1)
+ KEY_AES="0x"$(ps -xaf | md5sum | cut -d " " -f 1)
+ SPI1=$RANDOM
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:46 +0100
+Subject: [PATCH] netfilter: flowtable: add offload support for xmit path
+ types
+
+When the flow tuple xmit_type is set to FLOW_OFFLOAD_XMIT_DIRECT, the
+dst_cache pointer is not valid, and the h_source/h_dest/ifidx out fields
+need to be used.
+
+This patch also adds the FLOW_ACTION_VLAN_PUSH action to pass the VLAN
+tag to the driver.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -177,28 +177,45 @@ static int flow_offload_eth_src(struct n
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
+ {
+- const struct flow_offload_tuple *tuple = &flow->tuplehash[!dir].tuple;
+ struct flow_action_entry *entry0 = flow_action_entry_next(flow_rule);
+ struct flow_action_entry *entry1 = flow_action_entry_next(flow_rule);
+- struct net_device *dev;
++ const struct flow_offload_tuple *other_tuple, *this_tuple;
++ struct net_device *dev = NULL;
++ const unsigned char *addr;
+ u32 mask, val;
+ u16 val16;
+
+- dev = dev_get_by_index(net, tuple->iifidx);
+- if (!dev)
+- return -ENOENT;
++ this_tuple = &flow->tuplehash[dir].tuple;
++
++ switch (this_tuple->xmit_type) {
++ case FLOW_OFFLOAD_XMIT_DIRECT:
++ addr = this_tuple->out.h_source;
++ break;
++ case FLOW_OFFLOAD_XMIT_NEIGH:
++ other_tuple = &flow->tuplehash[!dir].tuple;
++ dev = dev_get_by_index(net, other_tuple->iifidx);
++ if (!dev)
++ return -ENOENT;
++
++ addr = dev->dev_addr;
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
+
+ mask = ~0xffff0000;
+- memcpy(&val16, dev->dev_addr, 2);
++ memcpy(&val16, addr, 2);
+ val = val16 << 16;
+ flow_offload_mangle(entry0, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 4,
+ &val, &mask);
+
+ mask = ~0xffffffff;
+- memcpy(&val, dev->dev_addr + 2, 4);
++ memcpy(&val, addr + 2, 4);
+ flow_offload_mangle(entry1, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 8,
+ &val, &mask);
+- dev_put(dev);
++
++ if (dev)
++ dev_put(dev);
+
+ return 0;
+ }
+@@ -210,27 +227,40 @@ static int flow_offload_eth_dst(struct n
+ {
+ struct flow_action_entry *entry0 = flow_action_entry_next(flow_rule);
+ struct flow_action_entry *entry1 = flow_action_entry_next(flow_rule);
+- const void *daddr = &flow->tuplehash[!dir].tuple.src_v4;
++ const struct flow_offload_tuple *other_tuple, *this_tuple;
+ const struct dst_entry *dst_cache;
+ unsigned char ha[ETH_ALEN];
+ struct neighbour *n;
++ const void *daddr;
+ u32 mask, val;
+ u8 nud_state;
+ u16 val16;
+
+- dst_cache = flow->tuplehash[dir].tuple.dst_cache;
+- n = dst_neigh_lookup(dst_cache, daddr);
+- if (!n)
+- return -ENOENT;
+-
+- read_lock_bh(&n->lock);
+- nud_state = n->nud_state;
+- ether_addr_copy(ha, n->ha);
+- read_unlock_bh(&n->lock);
++ this_tuple = &flow->tuplehash[dir].tuple;
+
+- if (!(nud_state & NUD_VALID)) {
++ switch (this_tuple->xmit_type) {
++ case FLOW_OFFLOAD_XMIT_DIRECT:
++ ether_addr_copy(ha, this_tuple->out.h_dest);
++ break;
++ case FLOW_OFFLOAD_XMIT_NEIGH:
++ other_tuple = &flow->tuplehash[!dir].tuple;
++ daddr = &other_tuple->src_v4;
++ dst_cache = this_tuple->dst_cache;
++ n = dst_neigh_lookup(dst_cache, daddr);
++ if (!n)
++ return -ENOENT;
++
++ read_lock_bh(&n->lock);
++ nud_state = n->nud_state;
++ ether_addr_copy(ha, n->ha);
++ read_unlock_bh(&n->lock);
+ neigh_release(n);
+- return -ENOENT;
++
++ if (!(nud_state & NUD_VALID))
++ return -ENOENT;
++ break;
++ default:
++ return -EOPNOTSUPP;
+ }
+
+ mask = ~0xffffffff;
+@@ -243,7 +273,6 @@ static int flow_offload_eth_dst(struct n
+ val = val16;
+ flow_offload_mangle(entry1, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 4,
+ &val, &mask);
+- neigh_release(n);
+
+ return 0;
+ }
+@@ -465,27 +494,52 @@ static void flow_offload_ipv4_checksum(s
+ }
+ }
+
+-static void flow_offload_redirect(const struct flow_offload *flow,
++static void flow_offload_redirect(struct net *net,
++ const struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
+ {
+- struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
+- struct rtable *rt;
++ const struct flow_offload_tuple *this_tuple, *other_tuple;
++ struct flow_action_entry *entry;
++ struct net_device *dev;
++ int ifindex;
++
++ this_tuple = &flow->tuplehash[dir].tuple;
++ switch (this_tuple->xmit_type) {
++ case FLOW_OFFLOAD_XMIT_DIRECT:
++ this_tuple = &flow->tuplehash[dir].tuple;
++ ifindex = this_tuple->out.ifidx;
++ break;
++ case FLOW_OFFLOAD_XMIT_NEIGH:
++ other_tuple = &flow->tuplehash[!dir].tuple;
++ ifindex = other_tuple->iifidx;
++ break;
++ default:
++ return;
++ }
+
+- rt = (struct rtable *)flow->tuplehash[dir].tuple.dst_cache;
++ dev = dev_get_by_index(net, ifindex);
++ if (!dev)
++ return;
++
++ entry = flow_action_entry_next(flow_rule);
+ entry->id = FLOW_ACTION_REDIRECT;
+- entry->dev = rt->dst.dev;
+- dev_hold(rt->dst.dev);
++ entry->dev = dev;
+ }
+
+ static void flow_offload_encap_tunnel(const struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
+ {
++ const struct flow_offload_tuple *this_tuple;
+ struct flow_action_entry *entry;
+ struct dst_entry *dst;
+
+- dst = flow->tuplehash[dir].tuple.dst_cache;
++ this_tuple = &flow->tuplehash[dir].tuple;
++ if (this_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
++ return;
++
++ dst = this_tuple->dst_cache;
+ if (dst && dst->lwtstate) {
+ struct ip_tunnel_info *tun_info;
+
+@@ -502,10 +556,15 @@ static void flow_offload_decap_tunnel(co
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
+ {
++ const struct flow_offload_tuple *other_tuple;
+ struct flow_action_entry *entry;
+ struct dst_entry *dst;
+
+- dst = flow->tuplehash[!dir].tuple.dst_cache;
++ other_tuple = &flow->tuplehash[!dir].tuple;
++ if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
++ return;
++
++ dst = other_tuple->dst_cache;
+ if (dst && dst->lwtstate) {
+ struct ip_tunnel_info *tun_info;
+
+@@ -517,10 +576,14 @@ static void flow_offload_decap_tunnel(co
+ }
+ }
+
+-int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
+- enum flow_offload_tuple_dir dir,
+- struct nf_flow_rule *flow_rule)
++static int
++nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
++ enum flow_offload_tuple_dir dir,
++ struct nf_flow_rule *flow_rule)
+ {
++ const struct flow_offload_tuple *other_tuple;
++ int i;
++
+ flow_offload_decap_tunnel(flow, dir, flow_rule);
+ flow_offload_encap_tunnel(flow, dir, flow_rule);
+
+@@ -528,6 +591,26 @@ int nf_flow_rule_route_ipv4(struct net *
+ flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
+ return -1;
+
++ other_tuple = &flow->tuplehash[!dir].tuple;
++
++ for (i = 0; i < other_tuple->encap_num; i++) {
++ struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
++
++ entry->id = FLOW_ACTION_VLAN_PUSH;
++ entry->vlan.vid = other_tuple->encap[i].id;
++ entry->vlan.proto = other_tuple->encap[i].proto;
++ }
++
++ return 0;
++}
++
++int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
++ enum flow_offload_tuple_dir dir,
++ struct nf_flow_rule *flow_rule)
++{
++ if (nf_flow_rule_route_common(net, flow, dir, flow_rule) < 0)
++ return -1;
++
+ if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
+ flow_offload_ipv4_snat(net, flow, dir, flow_rule);
+ flow_offload_port_snat(net, flow, dir, flow_rule);
+@@ -540,7 +623,7 @@ int nf_flow_rule_route_ipv4(struct net *
+ test_bit(NF_FLOW_DNAT, &flow->flags))
+ flow_offload_ipv4_checksum(net, flow, flow_rule);
+
+- flow_offload_redirect(flow, dir, flow_rule);
++ flow_offload_redirect(net, flow, dir, flow_rule);
+
+ return 0;
+ }
+@@ -550,11 +633,7 @@ int nf_flow_rule_route_ipv6(struct net *
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
+ {
+- flow_offload_decap_tunnel(flow, dir, flow_rule);
+- flow_offload_encap_tunnel(flow, dir, flow_rule);
+-
+- if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 ||
+- flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
++ if (nf_flow_rule_route_common(net, flow, dir, flow_rule) < 0)
+ return -1;
+
+ if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
+@@ -566,7 +645,7 @@ int nf_flow_rule_route_ipv6(struct net *
+ flow_offload_port_dnat(net, flow, dir, flow_rule);
+ }
+
+- flow_offload_redirect(flow, dir, flow_rule);
++ flow_offload_redirect(net, flow, dir, flow_rule);
+
+ return 0;
+ }
+@@ -580,10 +659,10 @@ nf_flow_offload_rule_alloc(struct net *n
+ enum flow_offload_tuple_dir dir)
+ {
+ const struct nf_flowtable *flowtable = offload->flowtable;
++ const struct flow_offload_tuple *tuple, *other_tuple;
+ const struct flow_offload *flow = offload->flow;
+- const struct flow_offload_tuple *tuple;
++ struct dst_entry *other_dst = NULL;
+ struct nf_flow_rule *flow_rule;
+- struct dst_entry *other_dst;
+ int err = -ENOMEM;
+
+ flow_rule = kzalloc(sizeof(*flow_rule), GFP_KERNEL);
+@@ -599,7 +678,10 @@ nf_flow_offload_rule_alloc(struct net *n
+ flow_rule->rule->match.key = &flow_rule->match.key;
+
+ tuple = &flow->tuplehash[dir].tuple;
+- other_dst = flow->tuplehash[!dir].tuple.dst_cache;
++ other_tuple = &flow->tuplehash[!dir].tuple;
++ if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_NEIGH)
++ other_dst = other_tuple->dst_cache;
++
+ err = nf_flow_rule_match(&flow_rule->match, tuple, other_dst);
+ if (err < 0)
+ goto err_flow_match;
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:47 +0100
+Subject: [PATCH] netfilter: nft_flow_offload: use direct xmit if
+ hardware offload is enabled
+
+If there is a forward path to reach an ethernet device and hardware
+offload is enabled, then use the direct xmit path.
+
+Moreover, store the real device in the direct xmit path info since
+software datapath uses dev_hard_header() to push the layer encapsulation
+headers while hardware offload refers to the real device.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -131,6 +131,7 @@ struct flow_offload_tuple {
+ struct dst_entry *dst_cache;
+ struct {
+ u32 ifidx;
++ u32 hw_ifidx;
+ u8 h_source[ETH_ALEN];
+ u8 h_dest[ETH_ALEN];
+ } out;
+@@ -187,6 +188,7 @@ struct nf_flow_route {
+ } in;
+ struct {
+ u32 ifindex;
++ u32 hw_ifindex;
+ u8 h_source[ETH_ALEN];
+ u8 h_dest[ETH_ALEN];
+ } out;
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -106,6 +106,7 @@ static int flow_offload_fill_route(struc
+ memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source,
+ ETH_ALEN);
+ flow_tuple->out.ifidx = route->tuple[dir].out.ifindex;
++ flow_tuple->out.hw_ifidx = route->tuple[dir].out.hw_ifindex;
+ break;
+ case FLOW_OFFLOAD_XMIT_XFRM:
+ case FLOW_OFFLOAD_XMIT_NEIGH:
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -508,7 +508,7 @@ static void flow_offload_redirect(struct
+ switch (this_tuple->xmit_type) {
+ case FLOW_OFFLOAD_XMIT_DIRECT:
+ this_tuple = &flow->tuplehash[dir].tuple;
+- ifindex = this_tuple->out.ifidx;
++ ifindex = this_tuple->out.hw_ifidx;
+ break;
+ case FLOW_OFFLOAD_XMIT_NEIGH:
+ other_tuple = &flow->tuplehash[!dir].tuple;
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -66,6 +66,7 @@ static int nft_dev_fill_forward_path(con
+ struct nft_forward_info {
+ const struct net_device *indev;
+ const struct net_device *outdev;
++ const struct net_device *hw_outdev;
+ struct id {
+ __u16 id;
+ __be16 proto;
+@@ -76,9 +77,18 @@ struct nft_forward_info {
+ enum flow_offload_xmit_type xmit_type;
+ };
+
++static bool nft_is_valid_ether_device(const struct net_device *dev)
++{
++ if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER ||
++ dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr))
++ return false;
++
++ return true;
++}
++
+ static void nft_dev_path_info(const struct net_device_path_stack *stack,
+ struct nft_forward_info *info,
+- unsigned char *ha)
++ unsigned char *ha, struct nf_flowtable *flowtable)
+ {
+ const struct net_device_path *path;
+ int i;
+@@ -140,6 +150,12 @@ static void nft_dev_path_info(const stru
+ }
+ if (!info->outdev)
+ info->outdev = info->indev;
++
++ info->hw_outdev = info->indev;
++
++ if (nf_flowtable_hw_offload(flowtable) &&
++ nft_is_valid_ether_device(info->indev))
++ info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
+ }
+
+ static bool nft_flowtable_find_dev(const struct net_device *dev,
+@@ -171,7 +187,7 @@ static void nft_dev_forward_path(struct
+ int i;
+
+ if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
+- nft_dev_path_info(&stack, &info, ha);
++ nft_dev_path_info(&stack, &info, ha, &ft->data);
+
+ if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
+ return;
+@@ -187,6 +203,7 @@ static void nft_dev_forward_path(struct
+ memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
+ memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN);
+ route->tuple[dir].out.ifindex = info.outdev->ifindex;
++ route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex;
+ route->tuple[dir].xmit_type = info.xmit_type;
+ }
+ }
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:48 +0100
+Subject: [PATCH] netfilter: flowtable: bridge vlan hardware offload and
+ switchdev
+
+The switch might have already added the VLAN tag through PVID hardware
+offload. Keep this extra VLAN in the flowtable but skip it on egress.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -849,6 +849,7 @@ struct net_device_path {
+ DEV_PATH_BR_VLAN_KEEP,
+ DEV_PATH_BR_VLAN_TAG,
+ DEV_PATH_BR_VLAN_UNTAG,
++ DEV_PATH_BR_VLAN_UNTAG_HW,
+ } vlan_mode;
+ u16 vlan_id;
+ __be16 vlan_proto;
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -123,9 +123,10 @@ struct flow_offload_tuple {
+ /* All members above are keys for lookups, see flow_offload_hash(). */
+ struct { } __hash;
+
+- u8 dir:4,
++ u8 dir:2,
+ xmit_type:2,
+- encap_num:2;
++ encap_num:2,
++ in_vlan_ingress:2;
+ u16 mtu;
+ union {
+ struct dst_entry *dst_cache;
+@@ -184,7 +185,8 @@ struct nf_flow_route {
+ u16 id;
+ __be16 proto;
+ } encap[NF_FLOW_TABLE_ENCAP_MAX];
+- u8 num_encaps;
++ u8 num_encaps:2,
++ ingress_vlans:2;
+ } in;
+ struct {
+ u32 ifindex;
+--- a/net/bridge/br_device.c
++++ b/net/bridge/br_device.c
+@@ -435,6 +435,7 @@ static int br_fill_forward_path(struct n
+ ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto;
+ ctx->num_vlans++;
+ break;
++ case DEV_PATH_BR_VLAN_UNTAG_HW:
+ case DEV_PATH_BR_VLAN_UNTAG:
+ ctx->num_vlans--;
+ break;
+--- a/net/bridge/br_vlan.c
++++ b/net/bridge/br_vlan.c
+@@ -1374,6 +1374,8 @@ int br_vlan_fill_forward_path_mode(struc
+
+ if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG)
+ path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
++ else if (v->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
++ path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG_HW;
+ else
+ path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG;
+
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -95,6 +95,8 @@ static int flow_offload_fill_route(struc
+ for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) {
+ flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id;
+ flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto;
++ if (route->tuple[dir].in.ingress_vlans & BIT(i))
++ flow_tuple->in_vlan_ingress |= BIT(j);
+ j++;
+ }
+ flow_tuple->encap_num = route->tuple[dir].in.num_encaps;
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -594,8 +594,12 @@ nf_flow_rule_route_common(struct net *ne
+ other_tuple = &flow->tuplehash[!dir].tuple;
+
+ for (i = 0; i < other_tuple->encap_num; i++) {
+- struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
++ struct flow_action_entry *entry;
+
++ if (other_tuple->in_vlan_ingress & BIT(i))
++ continue;
++
++ entry = flow_action_entry_next(flow_rule);
+ entry->id = FLOW_ACTION_VLAN_PUSH;
+ entry->vlan.vid = other_tuple->encap[i].id;
+ entry->vlan.proto = other_tuple->encap[i].proto;
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -72,6 +72,7 @@ struct nft_forward_info {
+ __be16 proto;
+ } encap[NF_FLOW_TABLE_ENCAP_MAX];
+ u8 num_encaps;
++ u8 ingress_vlans;
+ u8 h_source[ETH_ALEN];
+ u8 h_dest[ETH_ALEN];
+ enum flow_offload_xmit_type xmit_type;
+@@ -130,6 +131,9 @@ static void nft_dev_path_info(const stru
+ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
+
+ switch (path->bridge.vlan_mode) {
++ case DEV_PATH_BR_VLAN_UNTAG_HW:
++ info->ingress_vlans |= BIT(info->num_encaps - 1);
++ break;
+ case DEV_PATH_BR_VLAN_TAG:
+ info->encap[info->num_encaps].id = path->bridge.vlan_id;
+ info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
+@@ -198,6 +202,7 @@ static void nft_dev_forward_path(struct
+ route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
+ }
+ route->tuple[!dir].in.num_encaps = info.num_encaps;
++ route->tuple[!dir].in.ingress_vlans = info.ingress_vlans;
+
+ if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
+ memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:49 +0100
+Subject: [PATCH] net: flow_offload: add FLOW_ACTION_PPPOE_PUSH
+
+Add an action to represent the PPPoE hardware offload support that
+includes the session ID.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/flow_offload.h
++++ b/include/net/flow_offload.h
+@@ -147,6 +147,7 @@ enum flow_action_id {
+ FLOW_ACTION_MPLS_POP,
+ FLOW_ACTION_MPLS_MANGLE,
+ FLOW_ACTION_GATE,
++ FLOW_ACTION_PPPOE_PUSH,
+ NUM_FLOW_ACTIONS,
+ };
+
+@@ -271,6 +272,9 @@ struct flow_action_entry {
+ u32 num_entries;
+ struct action_gate_entry *entries;
+ } gate;
++ struct { /* FLOW_ACTION_PPPOE_PUSH */
++ u16 sid;
++ } pppoe;
+ };
+ struct flow_action_cookie *cookie; /* user defined action cookie */
+ };
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:50 +0100
+Subject: [PATCH] netfilter: flowtable: support for
+ FLOW_ACTION_PPPOE_PUSH
+
+Add a PPPoE push action if layer 2 protocol is ETH_P_PPP_SES to add
+PPPoE flowtable hardware offload support.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -600,9 +600,18 @@ nf_flow_rule_route_common(struct net *ne
+ continue;
+
+ entry = flow_action_entry_next(flow_rule);
+- entry->id = FLOW_ACTION_VLAN_PUSH;
+- entry->vlan.vid = other_tuple->encap[i].id;
+- entry->vlan.proto = other_tuple->encap[i].proto;
++
++ switch (other_tuple->encap[i].proto) {
++ case htons(ETH_P_PPP_SES):
++ entry->id = FLOW_ACTION_PPPOE_PUSH;
++ entry->pppoe.sid = other_tuple->encap[i].id;
++ break;
++ case htons(ETH_P_8021Q):
++ entry->id = FLOW_ACTION_VLAN_PUSH;
++ entry->vlan.vid = other_tuple->encap[i].id;
++ entry->vlan.proto = other_tuple->encap[i].proto;
++ break;
++ }
+ }
+
+ return 0;
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:51 +0100
+Subject: [PATCH] dsa: slave: add support for TC_SETUP_FT
+
+The dsa infrastructure provides a well-defined hierarchy of devices,
+pass up the call to set up the flow block to the master device. From the
+software dataplane, the netfilter infrastructure uses the dsa slave
+devices to refer to the input and output device for the given skbuff.
+Similarly, the flowtable definition in the ruleset refers to the dsa
+slave port devices.
+
+This patch adds the glue code to call ndo_setup_tc with TC_SETUP_FT
+with the master device via the dsa slave devices.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1239,14 +1239,32 @@ static int dsa_slave_setup_tc_block(stru
+ }
+ }
+
++static int dsa_slave_setup_ft_block(struct dsa_switch *ds, int port,
++ void *type_data)
++{
++ struct dsa_port *cpu_dp = dsa_to_port(ds, port)->cpu_dp;
++ struct net_device *master = cpu_dp->master;
++
++ if (!master->netdev_ops->ndo_setup_tc)
++ return -EOPNOTSUPP;
++
++ return master->netdev_ops->ndo_setup_tc(master, TC_SETUP_FT, type_data);
++}
++
+ static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type,
+ void *type_data)
+ {
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+
+- if (type == TC_SETUP_BLOCK)
++ switch (type) {
++ case TC_SETUP_BLOCK:
+ return dsa_slave_setup_tc_block(dev, type_data);
++ case TC_SETUP_FT:
++ return dsa_slave_setup_ft_block(ds, dp->index, type_data);
++ default:
++ break;
++ }
+
+ if (!ds->ops->port_setup_tc)
+ return -EOPNOTSUPP;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:52 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix parsing packets in GDM
+
+When using DSA, set the special tag in GDM ingress control to allow the MAC
+to parse packets properly earlier. This affects rx DMA source port reporting.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -19,6 +19,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/pinctrl/devinfo.h>
+ #include <linux/phylink.h>
++#include <net/dsa.h>
+
+ #include "mtk_eth_soc.h"
+
+@@ -1285,13 +1286,12 @@ static int mtk_poll_rx(struct napi_struc
+ break;
+
+ /* find out which mac the packet come from. values start at 1 */
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) ||
++ (trxd.rxd4 & RX_DMA_SPECIAL_TAG))
+ mac = 0;
+- } else {
+- mac = (trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
+- RX_DMA_FPORT_MASK;
+- mac--;
+- }
++ else
++ mac = ((trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
++ RX_DMA_FPORT_MASK) - 1;
+
+ if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||
+ !eth->netdev[mac]))
+@@ -2254,6 +2254,9 @@ static void mtk_gdm_config(struct mtk_et
+
+ val |= config;
+
++ if (!i && eth->netdev[0] && netdev_uses_dsa(eth->netdev[0]))
++ val |= MTK_GDMA_SPECIAL_TAG;
++
+ mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
+ }
+ /* Reset and enable PSE */
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -81,6 +81,7 @@
+
+ /* GDM Exgress Control Register */
+ #define MTK_GDMA_FWD_CFG(x) (0x500 + (x * 0x1000))
++#define MTK_GDMA_SPECIAL_TAG BIT(24)
+ #define MTK_GDMA_ICS_EN BIT(22)
+ #define MTK_GDMA_TCS_EN BIT(21)
+ #define MTK_GDMA_UCS_EN BIT(20)
+@@ -318,6 +319,7 @@
+ #define RX_DMA_L4_VALID_PDMA BIT(30) /* when PDMA is used */
+ #define RX_DMA_FPORT_SHIFT 19
+ #define RX_DMA_FPORT_MASK 0x7
++#define RX_DMA_SPECIAL_TAG BIT(22)
+
+ /* PHY Indirect Access Control registers */
+ #define MTK_PHY_IAC 0x10004
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:53 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for
+ initializing the PPE
+
+The PPE (packet processing engine) is used to offload NAT/routed or even
+bridged flows. This patch brings up the PPE and uses it to get a packet
+hash. It also contains some functionality that will be used to bring up
+flow offloading.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.c
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.h
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -4,5 +4,5 @@
+ #
+
+ obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
+-mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o
++mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o
+ obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2279,12 +2279,17 @@ static int mtk_open(struct net_device *d
+
+ /* we run 2 netdevs on the same dma ring so we only bring it up once */
+ if (!refcount_read(ð->dma_refcnt)) {
+- int err = mtk_start_dma(eth);
++ u32 gdm_config = MTK_GDMA_TO_PDMA;
++ int err;
+
++ err = mtk_start_dma(eth);
+ if (err)
+ return err;
+
+- mtk_gdm_config(eth, MTK_GDMA_TO_PDMA);
++ if (eth->soc->offload_version && mtk_ppe_start(ð->ppe) == 0)
++ gdm_config = MTK_GDMA_TO_PPE;
++
++ mtk_gdm_config(eth, gdm_config);
+
+ napi_enable(ð->tx_napi);
+ napi_enable(ð->rx_napi);
+@@ -2351,6 +2356,9 @@ static int mtk_stop(struct net_device *d
+
+ mtk_dma_free(eth);
+
++ if (eth->soc->offload_version)
++ mtk_ppe_stop(ð->ppe);
++
+ return 0;
+ }
+
+@@ -3079,6 +3087,13 @@ static int mtk_probe(struct platform_dev
+ goto err_free_dev;
+ }
+
++ if (eth->soc->offload_version) {
++ err = mtk_ppe_init(ð->ppe, eth->dev,
++ eth->base + MTK_ETH_PPE_BASE, 2);
++ if (err)
++ goto err_free_dev;
++ }
++
+ for (i = 0; i < MTK_MAX_DEVS; i++) {
+ if (!eth->netdev[i])
+ continue;
+@@ -3153,6 +3168,7 @@ static const struct mtk_soc_data mt7621_
+ .hw_features = MTK_HW_FEATURES,
+ .required_clks = MT7621_CLKS_BITMAP,
+ .required_pctl = false,
++ .offload_version = 2,
+ };
+
+ static const struct mtk_soc_data mt7622_data = {
+@@ -3161,6 +3177,7 @@ static const struct mtk_soc_data mt7622_
+ .hw_features = MTK_HW_FEATURES,
+ .required_clks = MT7622_CLKS_BITMAP,
+ .required_pctl = false,
++ .offload_version = 2,
+ };
+
+ static const struct mtk_soc_data mt7623_data = {
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -15,6 +15,7 @@
+ #include <linux/u64_stats_sync.h>
+ #include <linux/refcount.h>
+ #include <linux/phylink.h>
++#include "mtk_ppe.h"
+
+ #define MTK_QDMA_PAGE_SIZE 2048
+ #define MTK_MAX_RX_LENGTH 1536
+@@ -86,6 +87,7 @@
+ #define MTK_GDMA_TCS_EN BIT(21)
+ #define MTK_GDMA_UCS_EN BIT(20)
+ #define MTK_GDMA_TO_PDMA 0x0
++#define MTK_GDMA_TO_PPE 0x4444
+ #define MTK_GDMA_DROP_ALL 0x7777
+
+ /* Unicast Filter MAC Address Register - Low */
+@@ -315,6 +317,12 @@
+ #define RX_DMA_VID(_x) ((_x) & 0xfff)
+
+ /* QDMA descriptor rxd4 */
++#define MTK_RXD4_FOE_ENTRY GENMASK(13, 0)
++#define MTK_RXD4_PPE_CPU_REASON GENMASK(18, 14)
++#define MTK_RXD4_SRC_PORT GENMASK(21, 19)
++#define MTK_RXD4_ALG GENMASK(31, 22)
++
++/* QDMA descriptor rxd4 */
+ #define RX_DMA_L4_VALID BIT(24)
+ #define RX_DMA_L4_VALID_PDMA BIT(30) /* when PDMA is used */
+ #define RX_DMA_FPORT_SHIFT 19
+@@ -819,6 +827,7 @@ struct mtk_soc_data {
+ u32 caps;
+ u32 required_clks;
+ bool required_pctl;
++ u8 offload_version;
+ netdev_features_t hw_features;
+ };
+
+@@ -918,6 +927,8 @@ struct mtk_eth {
+ u32 tx_int_status_reg;
+ u32 rx_dma_l4_valid;
+ int ip_align;
++
++ struct mtk_ppe ppe;
+ };
+
+ /* struct mtk_mac - the structure that holds the info about the MACs of the
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -0,0 +1,511 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#include <linux/kernel.h>
++#include <linux/jiffies.h>
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/etherdevice.h>
++#include <linux/platform_device.h>
++#include "mtk_ppe.h"
++#include "mtk_ppe_regs.h"
++
++static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
++{
++ writel(val, ppe->base + reg);
++}
++
++static u32 ppe_r32(struct mtk_ppe *ppe, u32 reg)
++{
++ return readl(ppe->base + reg);
++}
++
++static u32 ppe_m32(struct mtk_ppe *ppe, u32 reg, u32 mask, u32 set)
++{
++ u32 val;
++
++ val = ppe_r32(ppe, reg);
++ val &= ~mask;
++ val |= set;
++ ppe_w32(ppe, reg, val);
++
++ return val;
++}
++
++static u32 ppe_set(struct mtk_ppe *ppe, u32 reg, u32 val)
++{
++ return ppe_m32(ppe, reg, 0, val);
++}
++
++static u32 ppe_clear(struct mtk_ppe *ppe, u32 reg, u32 val)
++{
++ return ppe_m32(ppe, reg, val, 0);
++}
++
++static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
++{
++ unsigned long timeout = jiffies + HZ;
++
++ while (time_is_before_jiffies(timeout)) {
++ if (!(ppe_r32(ppe, MTK_PPE_GLO_CFG) & MTK_PPE_GLO_CFG_BUSY))
++ return 0;
++
++ usleep_range(10, 20);
++ }
++
++ dev_err(ppe->dev, "PPE table busy");
++
++ return -ETIMEDOUT;
++}
++
++static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
++{
++ ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
++ ppe_clear(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
++}
++
++static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable)
++{
++ mtk_ppe_cache_clear(ppe);
++
++ ppe_m32(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_EN,
++ enable * MTK_PPE_CACHE_CTL_EN);
++}
++
++static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e)
++{
++ u32 hv1, hv2, hv3;
++ u32 hash;
++
++ switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) {
++ case MTK_PPE_PKT_TYPE_BRIDGE:
++ hv1 = e->bridge.src_mac_lo;
++ hv1 ^= ((e->bridge.src_mac_hi & 0xffff) << 16);
++ hv2 = e->bridge.src_mac_hi >> 16;
++ hv2 ^= e->bridge.dest_mac_lo;
++ hv3 = e->bridge.dest_mac_hi;
++ break;
++ case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++ case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++ hv1 = e->ipv4.orig.ports;
++ hv2 = e->ipv4.orig.dest_ip;
++ hv3 = e->ipv4.orig.src_ip;
++ break;
++ case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
++ case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
++ hv1 = e->ipv6.src_ip[3] ^ e->ipv6.dest_ip[3];
++ hv1 ^= e->ipv6.ports;
++
++ hv2 = e->ipv6.src_ip[2] ^ e->ipv6.dest_ip[2];
++ hv2 ^= e->ipv6.dest_ip[0];
++
++ hv3 = e->ipv6.src_ip[1] ^ e->ipv6.dest_ip[1];
++ hv3 ^= e->ipv6.src_ip[0];
++ break;
++ case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++ case MTK_PPE_PKT_TYPE_IPV6_6RD:
++ default:
++ WARN_ON_ONCE(1);
++ return MTK_PPE_HASH_MASK;
++ }
++
++ hash = (hv1 & hv2) | ((~hv1) & hv3);
++ hash = (hash >> 24) | ((hash & 0xffffff) << 8);
++ hash ^= hv1 ^ hv2 ^ hv3;
++ hash ^= hash >> 16;
++ hash <<= 1;
++ hash &= MTK_PPE_ENTRIES - 1;
++
++ return hash;
++}
++
++static inline struct mtk_foe_mac_info *
++mtk_foe_entry_l2(struct mtk_foe_entry *entry)
++{
++ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++
++ if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
++ return &entry->ipv6.l2;
++
++ return &entry->ipv4.l2;
++}
++
++static inline u32 *
++mtk_foe_entry_ib2(struct mtk_foe_entry *entry)
++{
++ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++
++ if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
++ return &entry->ipv6.ib2;
++
++ return &entry->ipv4.ib2;
++}
++
++int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
++ u8 pse_port, u8 *src_mac, u8 *dest_mac)
++{
++ struct mtk_foe_mac_info *l2;
++ u32 ports_pad, val;
++
++ memset(entry, 0, sizeof(*entry));
++
++ val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
++ FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) |
++ FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
++ MTK_FOE_IB1_BIND_TTL |
++ MTK_FOE_IB1_BIND_CACHE;
++ entry->ib1 = val;
++
++ val = FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) |
++ FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f) |
++ FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port);
++
++ if (is_multicast_ether_addr(dest_mac))
++ val |= MTK_FOE_IB2_MULTICAST;
++
++ ports_pad = 0xa5a5a500 | (l4proto & 0xff);
++ if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
++ entry->ipv4.orig.ports = ports_pad;
++ if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
++ entry->ipv6.ports = ports_pad;
++
++ if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
++ entry->ipv6.ib2 = val;
++ l2 = &entry->ipv6.l2;
++ } else {
++ entry->ipv4.ib2 = val;
++ l2 = &entry->ipv4.l2;
++ }
++
++ l2->dest_mac_hi = get_unaligned_be32(dest_mac);
++ l2->dest_mac_lo = get_unaligned_be16(dest_mac + 4);
++ l2->src_mac_hi = get_unaligned_be32(src_mac);
++ l2->src_mac_lo = get_unaligned_be16(src_mac + 4);
++
++ if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
++ l2->etype = ETH_P_IPV6;
++ else
++ l2->etype = ETH_P_IP;
++
++ return 0;
++}
++
++int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port)
++{
++ u32 *ib2 = mtk_foe_entry_ib2(entry);
++ u32 val;
++
++ val = *ib2;
++ val &= ~MTK_FOE_IB2_DEST_PORT;
++ val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port);
++ *ib2 = val;
++
++ return 0;
++}
++
++int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool egress,
++ __be32 src_addr, __be16 src_port,
++ __be32 dest_addr, __be16 dest_port)
++{
++ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++ struct mtk_ipv4_tuple *t;
++
++ switch (type) {
++ case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++ if (egress) {
++ t = &entry->ipv4.new;
++ break;
++ }
++ fallthrough;
++ case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++ case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++ t = &entry->ipv4.orig;
++ break;
++ case MTK_PPE_PKT_TYPE_IPV6_6RD:
++ entry->ipv6_6rd.tunnel_src_ip = be32_to_cpu(src_addr);
++ entry->ipv6_6rd.tunnel_dest_ip = be32_to_cpu(dest_addr);
++ return 0;
++ default:
++ WARN_ON_ONCE(1);
++ return -EINVAL;
++ }
++
++ t->src_ip = be32_to_cpu(src_addr);
++ t->dest_ip = be32_to_cpu(dest_addr);
++
++ if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
++ return 0;
++
++ t->src_port = be16_to_cpu(src_port);
++ t->dest_port = be16_to_cpu(dest_port);
++
++ return 0;
++}
++
++int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
++ __be32 *src_addr, __be16 src_port,
++ __be32 *dest_addr, __be16 dest_port)
++{
++ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++ u32 *src, *dest;
++ int i;
++
++ switch (type) {
++ case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++ src = entry->dslite.tunnel_src_ip;
++ dest = entry->dslite.tunnel_dest_ip;
++ break;
++ case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
++ case MTK_PPE_PKT_TYPE_IPV6_6RD:
++ entry->ipv6.src_port = be16_to_cpu(src_port);
++ entry->ipv6.dest_port = be16_to_cpu(dest_port);
++ fallthrough;
++ case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
++ src = entry->ipv6.src_ip;
++ dest = entry->ipv6.dest_ip;
++ break;
++ default:
++ WARN_ON_ONCE(1);
++ return -EINVAL;
++ };
++
++ for (i = 0; i < 4; i++)
++ src[i] = be32_to_cpu(src_addr[i]);
++ for (i = 0; i < 4; i++)
++ dest[i] = be32_to_cpu(dest_addr[i]);
++
++ return 0;
++}
++
++int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port)
++{
++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++
++ l2->etype = BIT(port);
++
++ if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER))
++ entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++ else
++ l2->etype |= BIT(8);
++
++ entry->ib1 &= ~MTK_FOE_IB1_BIND_VLAN_TAG;
++
++ return 0;
++}
++
++int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid)
++{
++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++
++ switch (FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, entry->ib1)) {
++ case 0:
++ entry->ib1 |= MTK_FOE_IB1_BIND_VLAN_TAG |
++ FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++ l2->vlan1 = vid;
++ return 0;
++ case 1:
++ if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) {
++ l2->vlan1 = vid;
++ l2->etype |= BIT(8);
++ } else {
++ l2->vlan2 = vid;
++ entry->ib1 += FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++ }
++ return 0;
++ default:
++ return -ENOSPC;
++ }
++}
++
++int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid)
++{
++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++
++ if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER) ||
++ (entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG))
++ l2->etype = ETH_P_PPP_SES;
++
++ entry->ib1 |= MTK_FOE_IB1_BIND_PPPOE;
++ l2->pppoe_id = sid;
++
++ return 0;
++}
++
++static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry)
++{
++ return !(entry->ib1 & MTK_FOE_IB1_STATIC) &&
++ FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND;
++}
++
++int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
++ u16 timestamp)
++{
++ struct mtk_foe_entry *hwe;
++ u32 hash;
++
++ timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP;
++ entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
++ entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp);
++
++ hash = mtk_ppe_hash_entry(entry);
++ hwe = &ppe->foe_table[hash];
++ if (!mtk_foe_entry_usable(hwe)) {
++ hwe++;
++ hash++;
++
++ if (!mtk_foe_entry_usable(hwe))
++ return -ENOSPC;
++ }
++
++ memcpy(&hwe->data, &entry->data, sizeof(hwe->data));
++ wmb();
++ hwe->ib1 = entry->ib1;
++
++ dma_wmb();
++
++ mtk_ppe_cache_clear(ppe);
++
++ return hash;
++}
++
++int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
++ int version)
++{
++ struct mtk_foe_entry *foe;
++
++ /* need to allocate a separate device, since it PPE DMA access is
++ * not coherent.
++ */
++ ppe->base = base;
++ ppe->dev = dev;
++ ppe->version = version;
++
++ foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe),
++ &ppe->foe_phys, GFP_KERNEL);
++ if (!foe)
++ return -ENOMEM;
++
++ ppe->foe_table = foe;
++
++ mtk_ppe_debugfs_init(ppe);
++
++ return 0;
++}
++
++static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
++{
++ static const u8 skip[] = { 12, 25, 38, 51, 76, 89, 102 };
++ int i, k;
++
++ memset(ppe->foe_table, 0, MTK_PPE_ENTRIES * sizeof(ppe->foe_table));
++
++ if (!IS_ENABLED(CONFIG_SOC_MT7621))
++ return;
++
++ /* skip all entries that cross the 1024 byte boundary */
++ for (i = 0; i < MTK_PPE_ENTRIES; i += 128)
++ for (k = 0; k < ARRAY_SIZE(skip); k++)
++ ppe->foe_table[i + skip[k]].ib1 |= MTK_FOE_IB1_STATIC;
++}
++
++int mtk_ppe_start(struct mtk_ppe *ppe)
++{
++ u32 val;
++
++ mtk_ppe_init_foe_table(ppe);
++ ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys);
++
++ val = MTK_PPE_TB_CFG_ENTRY_80B |
++ MTK_PPE_TB_CFG_AGE_NON_L4 |
++ MTK_PPE_TB_CFG_AGE_UNBIND |
++ MTK_PPE_TB_CFG_AGE_TCP |
++ MTK_PPE_TB_CFG_AGE_UDP |
++ MTK_PPE_TB_CFG_AGE_TCP_FIN |
++ FIELD_PREP(MTK_PPE_TB_CFG_SEARCH_MISS,
++ MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD) |
++ FIELD_PREP(MTK_PPE_TB_CFG_KEEPALIVE,
++ MTK_PPE_KEEPALIVE_DISABLE) |
++ FIELD_PREP(MTK_PPE_TB_CFG_HASH_MODE, 1) |
++ FIELD_PREP(MTK_PPE_TB_CFG_SCAN_MODE,
++ MTK_PPE_SCAN_MODE_KEEPALIVE_AGE) |
++ FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM,
++ MTK_PPE_ENTRIES_SHIFT);
++ ppe_w32(ppe, MTK_PPE_TB_CFG, val);
++
++ ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK,
++ MTK_PPE_IP_PROTO_CHK_IPV4 | MTK_PPE_IP_PROTO_CHK_IPV6);
++
++ mtk_ppe_cache_enable(ppe, true);
++
++ val = MTK_PPE_FLOW_CFG_IP4_TCP_FRAG |
++ MTK_PPE_FLOW_CFG_IP4_UDP_FRAG |
++ MTK_PPE_FLOW_CFG_IP6_3T_ROUTE |
++ MTK_PPE_FLOW_CFG_IP6_5T_ROUTE |
++ MTK_PPE_FLOW_CFG_IP6_6RD |
++ MTK_PPE_FLOW_CFG_IP4_NAT |
++ MTK_PPE_FLOW_CFG_IP4_NAPT |
++ MTK_PPE_FLOW_CFG_IP4_DSLITE |
++ MTK_PPE_FLOW_CFG_L2_BRIDGE |
++ MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
++ ppe_w32(ppe, MTK_PPE_FLOW_CFG, val);
++
++ val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) |
++ FIELD_PREP(MTK_PPE_UNBIND_AGE_DELTA, 3);
++ ppe_w32(ppe, MTK_PPE_UNBIND_AGE, val);
++
++ val = FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_UDP, 12) |
++ FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_NON_L4, 1);
++ ppe_w32(ppe, MTK_PPE_BIND_AGE0, val);
++
++ val = FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP_FIN, 1) |
++ FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP, 7);
++ ppe_w32(ppe, MTK_PPE_BIND_AGE1, val);
++
++ val = MTK_PPE_BIND_LIMIT0_QUARTER | MTK_PPE_BIND_LIMIT0_HALF;
++ ppe_w32(ppe, MTK_PPE_BIND_LIMIT0, val);
++
++ val = MTK_PPE_BIND_LIMIT1_FULL |
++ FIELD_PREP(MTK_PPE_BIND_LIMIT1_NON_L4, 1);
++ ppe_w32(ppe, MTK_PPE_BIND_LIMIT1, val);
++
++ val = FIELD_PREP(MTK_PPE_BIND_RATE_BIND, 30) |
++ FIELD_PREP(MTK_PPE_BIND_RATE_PREBIND, 1);
++ ppe_w32(ppe, MTK_PPE_BIND_RATE, val);
++
++ /* enable PPE */
++ val = MTK_PPE_GLO_CFG_EN |
++ MTK_PPE_GLO_CFG_IP4_L4_CS_DROP |
++ MTK_PPE_GLO_CFG_IP4_CS_DROP |
++ MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE;
++ ppe_w32(ppe, MTK_PPE_GLO_CFG, val);
++
++ ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0);
++
++ return 0;
++}
++
++int mtk_ppe_stop(struct mtk_ppe *ppe)
++{
++ u32 val;
++ int i;
++
++ for (i = 0; i < MTK_PPE_ENTRIES; i++)
++ ppe->foe_table[i].ib1 = FIELD_PREP(MTK_FOE_IB1_STATE,
++ MTK_FOE_STATE_INVALID);
++
++ mtk_ppe_cache_enable(ppe, false);
++
++ /* disable offload engine */
++ ppe_clear(ppe, MTK_PPE_GLO_CFG, MTK_PPE_GLO_CFG_EN);
++ ppe_w32(ppe, MTK_PPE_FLOW_CFG, 0);
++
++ /* disable aging */
++ val = MTK_PPE_TB_CFG_AGE_NON_L4 |
++ MTK_PPE_TB_CFG_AGE_UNBIND |
++ MTK_PPE_TB_CFG_AGE_TCP |
++ MTK_PPE_TB_CFG_AGE_UDP |
++ MTK_PPE_TB_CFG_AGE_TCP_FIN;
++ ppe_clear(ppe, MTK_PPE_TB_CFG, val);
++
++ return mtk_ppe_wait_busy(ppe);
++}
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -0,0 +1,287 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#ifndef __MTK_PPE_H
++#define __MTK_PPE_H
++
++#include <linux/kernel.h>
++#include <linux/bitfield.h>
++
++#define MTK_ETH_PPE_BASE 0xc00
++
++#define MTK_PPE_ENTRIES_SHIFT 3
++#define MTK_PPE_ENTRIES (1024 << MTK_PPE_ENTRIES_SHIFT)
++#define MTK_PPE_HASH_MASK (MTK_PPE_ENTRIES - 1)
++
++#define MTK_FOE_IB1_UNBIND_TIMESTAMP GENMASK(7, 0)
++#define MTK_FOE_IB1_UNBIND_PACKETS GENMASK(23, 8)
++#define MTK_FOE_IB1_UNBIND_PREBIND BIT(24)
++
++#define MTK_FOE_IB1_BIND_TIMESTAMP GENMASK(14, 0)
++#define MTK_FOE_IB1_BIND_KEEPALIVE BIT(15)
++#define MTK_FOE_IB1_BIND_VLAN_LAYER GENMASK(18, 16)
++#define MTK_FOE_IB1_BIND_PPPOE BIT(19)
++#define MTK_FOE_IB1_BIND_VLAN_TAG BIT(20)
++#define MTK_FOE_IB1_BIND_PKT_SAMPLE BIT(21)
++#define MTK_FOE_IB1_BIND_CACHE BIT(22)
++#define MTK_FOE_IB1_BIND_TUNNEL_DECAP BIT(23)
++#define MTK_FOE_IB1_BIND_TTL BIT(24)
++
++#define MTK_FOE_IB1_PACKET_TYPE GENMASK(27, 25)
++#define MTK_FOE_IB1_STATE GENMASK(29, 28)
++#define MTK_FOE_IB1_UDP BIT(30)
++#define MTK_FOE_IB1_STATIC BIT(31)
++
++enum {
++ MTK_PPE_PKT_TYPE_IPV4_HNAPT = 0,
++ MTK_PPE_PKT_TYPE_IPV4_ROUTE = 1,
++ MTK_PPE_PKT_TYPE_BRIDGE = 2,
++ MTK_PPE_PKT_TYPE_IPV4_DSLITE = 3,
++ MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T = 4,
++ MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T = 5,
++ MTK_PPE_PKT_TYPE_IPV6_6RD = 7,
++};
++
++#define MTK_FOE_IB2_QID GENMASK(3, 0)
++#define MTK_FOE_IB2_PSE_QOS BIT(4)
++#define MTK_FOE_IB2_DEST_PORT GENMASK(7, 5)
++#define MTK_FOE_IB2_MULTICAST BIT(8)
++
++#define MTK_FOE_IB2_WHNAT_QID2 GENMASK(13, 12)
++#define MTK_FOE_IB2_WHNAT_DEVIDX BIT(16)
++#define MTK_FOE_IB2_WHNAT_NAT BIT(17)
++
++#define MTK_FOE_IB2_PORT_MG GENMASK(17, 12)
++
++#define MTK_FOE_IB2_PORT_AG GENMASK(23, 18)
++
++#define MTK_FOE_IB2_DSCP GENMASK(31, 24)
++
++#define MTK_FOE_VLAN2_WHNAT_BSS GEMMASK(5, 0)
++#define MTK_FOE_VLAN2_WHNAT_WCID GENMASK(13, 6)
++#define MTK_FOE_VLAN2_WHNAT_RING GENMASK(15, 14)
++
++enum {
++ MTK_FOE_STATE_INVALID,
++ MTK_FOE_STATE_UNBIND,
++ MTK_FOE_STATE_BIND,
++ MTK_FOE_STATE_FIN
++};
++
++struct mtk_foe_mac_info {
++ u16 vlan1;
++ u16 etype;
++
++ u32 dest_mac_hi;
++
++ u16 vlan2;
++ u16 dest_mac_lo;
++
++ u32 src_mac_hi;
++
++ u16 pppoe_id;
++ u16 src_mac_lo;
++};
++
++struct mtk_foe_bridge {
++ u32 dest_mac_hi;
++
++ u16 src_mac_lo;
++ u16 dest_mac_lo;
++
++ u32 src_mac_hi;
++
++ u32 ib2;
++
++ u32 _rsv[5];
++
++ u32 udf_tsid;
++ struct mtk_foe_mac_info l2;
++};
++
++struct mtk_ipv4_tuple {
++ u32 src_ip;
++ u32 dest_ip;
++ union {
++ struct {
++ u16 dest_port;
++ u16 src_port;
++ };
++ struct {
++ u8 protocol;
++ u8 _pad[3]; /* fill with 0xa5a5a5 */
++ };
++ u32 ports;
++ };
++};
++
++struct mtk_foe_ipv4 {
++ struct mtk_ipv4_tuple orig;
++
++ u32 ib2;
++
++ struct mtk_ipv4_tuple new;
++
++ u16 timestamp;
++ u16 _rsv0[3];
++
++ u32 udf_tsid;
++
++ struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_ipv4_dslite {
++ struct mtk_ipv4_tuple ip4;
++
++ u32 tunnel_src_ip[4];
++ u32 tunnel_dest_ip[4];
++
++ u8 flow_label[3];
++ u8 priority;
++
++ u32 udf_tsid;
++
++ u32 ib2;
++
++ struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_ipv6 {
++ u32 src_ip[4];
++ u32 dest_ip[4];
++
++ union {
++ struct {
++ u8 protocol;
++ u8 _pad[3]; /* fill with 0xa5a5a5 */
++ }; /* 3-tuple */
++ struct {
++ u16 dest_port;
++ u16 src_port;
++ }; /* 5-tuple */
++ u32 ports;
++ };
++
++ u32 _rsv[3];
++
++ u32 udf;
++
++ u32 ib2;
++ struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_ipv6_6rd {
++ u32 src_ip[4];
++ u32 dest_ip[4];
++ u16 dest_port;
++ u16 src_port;
++
++ u32 tunnel_src_ip;
++ u32 tunnel_dest_ip;
++
++ u16 hdr_csum;
++ u8 dscp;
++ u8 ttl;
++
++ u8 flag;
++ u8 pad;
++ u8 per_flow_6rd_id;
++ u8 pad2;
++
++ u32 ib2;
++ struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_entry {
++ u32 ib1;
++
++ union {
++ struct mtk_foe_bridge bridge;
++ struct mtk_foe_ipv4 ipv4;
++ struct mtk_foe_ipv4_dslite dslite;
++ struct mtk_foe_ipv6 ipv6;
++ struct mtk_foe_ipv6_6rd ipv6_6rd;
++ u32 data[19];
++ };
++};
++
++enum {
++ MTK_PPE_CPU_REASON_TTL_EXCEEDED = 0x02,
++ MTK_PPE_CPU_REASON_OPTION_HEADER = 0x03,
++ MTK_PPE_CPU_REASON_NO_FLOW = 0x07,
++ MTK_PPE_CPU_REASON_IPV4_FRAG = 0x08,
++ MTK_PPE_CPU_REASON_IPV4_DSLITE_FRAG = 0x09,
++ MTK_PPE_CPU_REASON_IPV4_DSLITE_NO_TCP_UDP = 0x0a,
++ MTK_PPE_CPU_REASON_IPV6_6RD_NO_TCP_UDP = 0x0b,
++ MTK_PPE_CPU_REASON_TCP_FIN_SYN_RST = 0x0c,
++ MTK_PPE_CPU_REASON_UN_HIT = 0x0d,
++ MTK_PPE_CPU_REASON_HIT_UNBIND = 0x0e,
++ MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED = 0x0f,
++ MTK_PPE_CPU_REASON_HIT_BIND_TCP_FIN = 0x10,
++ MTK_PPE_CPU_REASON_HIT_TTL_1 = 0x11,
++ MTK_PPE_CPU_REASON_HIT_BIND_VLAN_VIOLATION = 0x12,
++ MTK_PPE_CPU_REASON_KEEPALIVE_UC_OLD_HDR = 0x13,
++ MTK_PPE_CPU_REASON_KEEPALIVE_MC_NEW_HDR = 0x14,
++ MTK_PPE_CPU_REASON_KEEPALIVE_DUP_OLD_HDR = 0x15,
++ MTK_PPE_CPU_REASON_HIT_BIND_FORCE_CPU = 0x16,
++ MTK_PPE_CPU_REASON_TUNNEL_OPTION_HEADER = 0x17,
++ MTK_PPE_CPU_REASON_MULTICAST_TO_CPU = 0x18,
++ MTK_PPE_CPU_REASON_MULTICAST_TO_GMAC1_CPU = 0x19,
++ MTK_PPE_CPU_REASON_HIT_PRE_BIND = 0x1a,
++ MTK_PPE_CPU_REASON_PACKET_SAMPLING = 0x1b,
++ MTK_PPE_CPU_REASON_EXCEED_MTU = 0x1c,
++ MTK_PPE_CPU_REASON_PPE_BYPASS = 0x1e,
++ MTK_PPE_CPU_REASON_INVALID = 0x1f,
++};
++
++struct mtk_ppe {
++ struct device *dev;
++ void __iomem *base;
++ int version;
++
++ struct mtk_foe_entry *foe_table;
++ dma_addr_t foe_phys;
++
++ void *acct_table;
++};
++
++int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
++ int version);
++int mtk_ppe_start(struct mtk_ppe *ppe);
++int mtk_ppe_stop(struct mtk_ppe *ppe);
++
++static inline void
++mtk_foe_entry_clear(struct mtk_ppe *ppe, u16 hash)
++{
++ ppe->foe_table[hash].ib1 = 0;
++ dma_wmb();
++}
++
++static inline int
++mtk_foe_entry_timestamp(struct mtk_ppe *ppe, u16 hash)
++{
++ u32 ib1 = READ_ONCE(ppe->foe_table[hash].ib1);
++
++ if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND)
++ return -1;
++
++ return FIELD_GET(MTK_FOE_IB1_BIND_TIMESTAMP, ib1);
++}
++
++int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
++ u8 pse_port, u8 *src_mac, u8 *dest_mac);
++int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port);
++int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool orig,
++ __be32 src_addr, __be16 src_port,
++ __be32 dest_addr, __be16 dest_port);
++int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
++ __be32 *src_addr, __be16 src_port,
++ __be32 *dest_addr, __be16 dest_port);
++int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
++int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
++int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
++int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
++ u16 timestamp);
++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
++
++#endif
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+@@ -0,0 +1,217 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#include <linux/kernel.h>
++#include <linux/debugfs.h>
++#include "mtk_eth_soc.h"
++
++struct mtk_flow_addr_info
++{
++ void *src, *dest;
++ u16 *src_port, *dest_port;
++ bool ipv6;
++};
++
++static const char *mtk_foe_entry_state_str(int state)
++{
++ static const char * const state_str[] = {
++ [MTK_FOE_STATE_INVALID] = "INV",
++ [MTK_FOE_STATE_UNBIND] = "UNB",
++ [MTK_FOE_STATE_BIND] = "BND",
++ [MTK_FOE_STATE_FIN] = "FIN",
++ };
++
++ if (state >= ARRAY_SIZE(state_str) || !state_str[state])
++ return "UNK";
++
++ return state_str[state];
++}
++
++static const char *mtk_foe_pkt_type_str(int type)
++{
++ static const char * const type_str[] = {
++ [MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
++ [MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
++ [MTK_PPE_PKT_TYPE_BRIDGE] = "L2",
++ [MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
++ [MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
++ [MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
++ [MTK_PPE_PKT_TYPE_IPV6_6RD] = "6RD",
++ };
++
++ if (type >= ARRAY_SIZE(type_str) || !type_str[type])
++ return "UNKNOWN";
++
++ return type_str[type];
++}
++
++static void
++mtk_print_addr(struct seq_file *m, u32 *addr, bool ipv6)
++{
++ u32 n_addr[4];
++ int i;
++
++ if (!ipv6) {
++ seq_printf(m, "%pI4h", addr);
++ return;
++ }
++
++ for (i = 0; i < ARRAY_SIZE(n_addr); i++)
++ n_addr[i] = htonl(addr[i]);
++ seq_printf(m, "%pI6", n_addr);
++}
++
++static void
++mtk_print_addr_info(struct seq_file *m, struct mtk_flow_addr_info *ai)
++{
++ mtk_print_addr(m, ai->src, ai->ipv6);
++ if (ai->src_port)
++ seq_printf(m, ":%d", *ai->src_port);
++ seq_printf(m, "->");
++ mtk_print_addr(m, ai->dest, ai->ipv6);
++ if (ai->dest_port)
++ seq_printf(m, ":%d", *ai->dest_port);
++}
++
++static int
++mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind)
++{
++ struct mtk_ppe *ppe = m->private;
++ int i, count;
++
++ for (i = 0, count = 0; i < MTK_PPE_ENTRIES; i++) {
++ struct mtk_foe_entry *entry = &ppe->foe_table[i];
++ struct mtk_foe_mac_info *l2;
++ struct mtk_flow_addr_info ai = {};
++ unsigned char h_source[ETH_ALEN];
++ unsigned char h_dest[ETH_ALEN];
++ int type, state;
++ u32 ib2;
++
++
++ state = FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1);
++ if (!state)
++ continue;
++
++ if (bind && state != MTK_FOE_STATE_BIND)
++ continue;
++
++ type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++ seq_printf(m, "%05x %s %7s", i,
++ mtk_foe_entry_state_str(state),
++ mtk_foe_pkt_type_str(type));
++
++ switch (type) {
++ case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++ case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++ ai.src_port = &entry->ipv4.orig.src_port;
++ ai.dest_port = &entry->ipv4.orig.dest_port;
++ fallthrough;
++ case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++ ai.src = &entry->ipv4.orig.src_ip;
++ ai.dest = &entry->ipv4.orig.dest_ip;
++ break;
++ case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
++ ai.src_port = &entry->ipv6.src_port;
++ ai.dest_port = &entry->ipv6.dest_port;
++ fallthrough;
++ case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
++ case MTK_PPE_PKT_TYPE_IPV6_6RD:
++ ai.src = &entry->ipv6.src_ip;
++ ai.dest = &entry->ipv6.dest_ip;
++ ai.ipv6 = true;
++ break;
++ }
++
++ seq_printf(m, " orig=");
++ mtk_print_addr_info(m, &ai);
++
++ switch (type) {
++ case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++ case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++ ai.src_port = &entry->ipv4.new.src_port;
++ ai.dest_port = &entry->ipv4.new.dest_port;
++ fallthrough;
++ case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++ ai.src = &entry->ipv4.new.src_ip;
++ ai.dest = &entry->ipv4.new.dest_ip;
++ seq_printf(m, " new=");
++ mtk_print_addr_info(m, &ai);
++ break;
++ }
++
++ if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
++ l2 = &entry->ipv6.l2;
++ ib2 = entry->ipv6.ib2;
++ } else {
++ l2 = &entry->ipv4.l2;
++ ib2 = entry->ipv4.ib2;
++ }
++
++ *((__be32 *)h_source) = htonl(l2->src_mac_hi);
++ *((__be16 *)&h_source[4]) = htons(l2->src_mac_lo);
++ *((__be32 *)h_dest) = htonl(l2->dest_mac_hi);
++ *((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
++
++ seq_printf(m, " eth=%pM->%pM etype=%04x"
++ " vlan=%d,%d ib1=%08x ib2=%08x\n",
++ h_source, h_dest, ntohs(l2->etype),
++ l2->vlan1, l2->vlan2, entry->ib1, ib2);
++ }
++
++ return 0;
++}
++
++static int
++mtk_ppe_debugfs_foe_show_all(struct seq_file *m, void *private)
++{
++ return mtk_ppe_debugfs_foe_show(m, private, false);
++}
++
++static int
++mtk_ppe_debugfs_foe_show_bind(struct seq_file *m, void *private)
++{
++ return mtk_ppe_debugfs_foe_show(m, private, true);
++}
++
++static int
++mtk_ppe_debugfs_foe_open_all(struct inode *inode, struct file *file)
++{
++ return single_open(file, mtk_ppe_debugfs_foe_show_all,
++ inode->i_private);
++}
++
++static int
++mtk_ppe_debugfs_foe_open_bind(struct inode *inode, struct file *file)
++{
++ return single_open(file, mtk_ppe_debugfs_foe_show_bind,
++ inode->i_private);
++}
++
++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe)
++{
++ static const struct file_operations fops_all = {
++ .open = mtk_ppe_debugfs_foe_open_all,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++ };
++
++ static const struct file_operations fops_bind = {
++ .open = mtk_ppe_debugfs_foe_open_bind,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++ };
++
++ struct dentry *root;
++
++ root = debugfs_create_dir("mtk_ppe", NULL);
++ if (!root)
++ return -ENOMEM;
++
++ debugfs_create_file("entries", S_IRUGO, root, ppe, &fops_all);
++ debugfs_create_file("bind", S_IRUGO, root, ppe, &fops_bind);
++
++ return 0;
++}
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+@@ -0,0 +1,144 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#ifndef __MTK_PPE_REGS_H
++#define __MTK_PPE_REGS_H
++
++#define MTK_PPE_GLO_CFG 0x200
++#define MTK_PPE_GLO_CFG_EN BIT(0)
++#define MTK_PPE_GLO_CFG_TSID_EN BIT(1)
++#define MTK_PPE_GLO_CFG_IP4_L4_CS_DROP BIT(2)
++#define MTK_PPE_GLO_CFG_IP4_CS_DROP BIT(3)
++#define MTK_PPE_GLO_CFG_TTL0_DROP BIT(4)
++#define MTK_PPE_GLO_CFG_PPE_BSWAP BIT(5)
++#define MTK_PPE_GLO_CFG_PSE_HASH_OFS BIT(6)
++#define MTK_PPE_GLO_CFG_MCAST_TB_EN BIT(7)
++#define MTK_PPE_GLO_CFG_FLOW_DROP_KA BIT(8)
++#define MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE BIT(9)
++#define MTK_PPE_GLO_CFG_UDP_LITE_EN BIT(10)
++#define MTK_PPE_GLO_CFG_UDP_LEN_DROP BIT(11)
++#define MTK_PPE_GLO_CFG_MCAST_ENTRIES GNEMASK(13, 12)
++#define MTK_PPE_GLO_CFG_BUSY BIT(31)
++
++#define MTK_PPE_FLOW_CFG 0x204
++#define MTK_PPE_FLOW_CFG_IP4_TCP_FRAG BIT(6)
++#define MTK_PPE_FLOW_CFG_IP4_UDP_FRAG BIT(7)
++#define MTK_PPE_FLOW_CFG_IP6_3T_ROUTE BIT(8)
++#define MTK_PPE_FLOW_CFG_IP6_5T_ROUTE BIT(9)
++#define MTK_PPE_FLOW_CFG_IP6_6RD BIT(10)
++#define MTK_PPE_FLOW_CFG_IP4_NAT BIT(12)
++#define MTK_PPE_FLOW_CFG_IP4_NAPT BIT(13)
++#define MTK_PPE_FLOW_CFG_IP4_DSLITE BIT(14)
++#define MTK_PPE_FLOW_CFG_L2_BRIDGE BIT(15)
++#define MTK_PPE_FLOW_CFG_IP_PROTO_BLACKLIST BIT(16)
++#define MTK_PPE_FLOW_CFG_IP4_NAT_FRAG BIT(17)
++#define MTK_PPE_FLOW_CFG_IP4_HASH_FLOW_LABEL BIT(18)
++#define MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY BIT(19)
++#define MTK_PPE_FLOW_CFG_IP6_HASH_GRE_KEY BIT(20)
++
++#define MTK_PPE_IP_PROTO_CHK 0x208
++#define MTK_PPE_IP_PROTO_CHK_IPV4 GENMASK(15, 0)
++#define MTK_PPE_IP_PROTO_CHK_IPV6 GENMASK(31, 16)
++
++#define MTK_PPE_TB_CFG 0x21c
++#define MTK_PPE_TB_CFG_ENTRY_NUM GENMASK(2, 0)
++#define MTK_PPE_TB_CFG_ENTRY_80B BIT(3)
++#define MTK_PPE_TB_CFG_SEARCH_MISS GENMASK(5, 4)
++#define MTK_PPE_TB_CFG_AGE_PREBIND BIT(6)
++#define MTK_PPE_TB_CFG_AGE_NON_L4 BIT(7)
++#define MTK_PPE_TB_CFG_AGE_UNBIND BIT(8)
++#define MTK_PPE_TB_CFG_AGE_TCP BIT(9)
++#define MTK_PPE_TB_CFG_AGE_UDP BIT(10)
++#define MTK_PPE_TB_CFG_AGE_TCP_FIN BIT(11)
++#define MTK_PPE_TB_CFG_KEEPALIVE GENMASK(13, 12)
++#define MTK_PPE_TB_CFG_HASH_MODE GENMASK(15, 14)
++#define MTK_PPE_TB_CFG_SCAN_MODE GENMASK(17, 16)
++#define MTK_PPE_TB_CFG_HASH_DEBUG GENMASK(19, 18)
++
++enum {
++ MTK_PPE_SCAN_MODE_DISABLED,
++ MTK_PPE_SCAN_MODE_CHECK_AGE,
++ MTK_PPE_SCAN_MODE_KEEPALIVE_AGE,
++};
++
++enum {
++ MTK_PPE_KEEPALIVE_DISABLE,
++ MTK_PPE_KEEPALIVE_UNICAST_CPU,
++ MTK_PPE_KEEPALIVE_DUP_CPU = 3,
++};
++
++enum {
++ MTK_PPE_SEARCH_MISS_ACTION_DROP,
++ MTK_PPE_SEARCH_MISS_ACTION_FORWARD = 2,
++ MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD = 3,
++};
++
++#define MTK_PPE_TB_BASE 0x220
++
++#define MTK_PPE_TB_USED 0x224
++#define MTK_PPE_TB_USED_NUM GENMASK(13, 0)
++
++#define MTK_PPE_BIND_RATE 0x228
++#define MTK_PPE_BIND_RATE_BIND GENMASK(15, 0)
++#define MTK_PPE_BIND_RATE_PREBIND GENMASK(31, 16)
++
++#define MTK_PPE_BIND_LIMIT0 0x22c
++#define MTK_PPE_BIND_LIMIT0_QUARTER GENMASK(13, 0)
++#define MTK_PPE_BIND_LIMIT0_HALF GENMASK(29, 16)
++
++#define MTK_PPE_BIND_LIMIT1 0x230
++#define MTK_PPE_BIND_LIMIT1_FULL GENMASK(13, 0)
++#define MTK_PPE_BIND_LIMIT1_NON_L4 GENMASK(23, 16)
++
++#define MTK_PPE_KEEPALIVE 0x234
++#define MTK_PPE_KEEPALIVE_TIME GENMASK(15, 0)
++#define MTK_PPE_KEEPALIVE_TIME_TCP GENMASK(23, 16)
++#define MTK_PPE_KEEPALIVE_TIME_UDP GENMASK(31, 24)
++
++#define MTK_PPE_UNBIND_AGE 0x238
++#define MTK_PPE_UNBIND_AGE_MIN_PACKETS GENMASK(31, 16)
++#define MTK_PPE_UNBIND_AGE_DELTA GENMASK(7, 0)
++
++#define MTK_PPE_BIND_AGE0 0x23c
++#define MTK_PPE_BIND_AGE0_DELTA_NON_L4 GENMASK(30, 16)
++#define MTK_PPE_BIND_AGE0_DELTA_UDP GENMASK(14, 0)
++
++#define MTK_PPE_BIND_AGE1 0x240
++#define MTK_PPE_BIND_AGE1_DELTA_TCP_FIN GENMASK(30, 16)
++#define MTK_PPE_BIND_AGE1_DELTA_TCP GENMASK(14, 0)
++
++#define MTK_PPE_HASH_SEED 0x244
++
++#define MTK_PPE_DEFAULT_CPU_PORT 0x248
++#define MTK_PPE_DEFAULT_CPU_PORT_MASK(_n) (GENMASK(2, 0) << ((_n) * 4))
++
++#define MTK_PPE_MTU_DROP 0x308
++
++#define MTK_PPE_VLAN_MTU0 0x30c
++#define MTK_PPE_VLAN_MTU0_NONE GENMASK(13, 0)
++#define MTK_PPE_VLAN_MTU0_1TAG GENMASK(29, 16)
++
++#define MTK_PPE_VLAN_MTU1 0x310
++#define MTK_PPE_VLAN_MTU1_2TAG GENMASK(13, 0)
++#define MTK_PPE_VLAN_MTU1_3TAG GENMASK(29, 16)
++
++#define MTK_PPE_VPM_TPID 0x318
++
++#define MTK_PPE_CACHE_CTL 0x320
++#define MTK_PPE_CACHE_CTL_EN BIT(0)
++#define MTK_PPE_CACHE_CTL_LOCK_CLR BIT(4)
++#define MTK_PPE_CACHE_CTL_REQ BIT(8)
++#define MTK_PPE_CACHE_CTL_CLEAR BIT(9)
++#define MTK_PPE_CACHE_CTL_CMD GENMASK(13, 12)
++
++#define MTK_PPE_MIB_CFG 0x334
++#define MTK_PPE_MIB_CFG_EN BIT(0)
++#define MTK_PPE_MIB_CFG_RD_CLR BIT(1)
++
++#define MTK_PPE_MIB_TB_BASE 0x338
++
++#define MTK_PPE_MIB_CACHE_CTL 0x350
++#define MTK_PPE_MIB_CACHE_CTL_EN BIT(0)
++#define MTK_PPE_MIB_CACHE_CTL_FLUSH BIT(2)
++
++#endif
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:54 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add flow offloading support
+
+This adds support for offloading IPv4 routed flows, including SNAT/DNAT,
+one VLAN, PPPoE and DSA.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -4,5 +4,5 @@
+ #
+
+ obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
+-mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o
++mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
+ obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2834,6 +2834,7 @@ static const struct net_device_ops mtk_n
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = mtk_poll_controller,
+ #endif
++ .ndo_setup_tc = mtk_eth_setup_tc,
+ };
+
+ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+@@ -3092,6 +3093,10 @@ static int mtk_probe(struct platform_dev
+ eth->base + MTK_ETH_PPE_BASE, 2);
+ if (err)
+ goto err_free_dev;
++
++ err = mtk_eth_offload_init(eth);
++ if (err)
++ goto err_free_dev;
+ }
+
+ for (i = 0; i < MTK_MAX_DEVS; i++) {
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -15,6 +15,7 @@
+ #include <linux/u64_stats_sync.h>
+ #include <linux/refcount.h>
+ #include <linux/phylink.h>
++#include <linux/rhashtable.h>
+ #include "mtk_ppe.h"
+
+ #define MTK_QDMA_PAGE_SIZE 2048
+@@ -40,7 +41,8 @@
+ NETIF_F_HW_VLAN_CTAG_RX | \
+ NETIF_F_SG | NETIF_F_TSO | \
+ NETIF_F_TSO6 | \
+- NETIF_F_IPV6_CSUM)
++ NETIF_F_IPV6_CSUM |\
++ NETIF_F_HW_TC)
+ #define MTK_HW_FEATURES_MT7628 (NETIF_F_SG | NETIF_F_RXCSUM)
+ #define NEXT_DESP_IDX(X, Y) (((X) + 1) & ((Y) - 1))
+
+@@ -929,6 +931,7 @@ struct mtk_eth {
+ int ip_align;
+
+ struct mtk_ppe ppe;
++ struct rhashtable flow_table;
+ };
+
+ /* struct mtk_mac - the structure that holds the info about the MACs of the
+@@ -973,4 +976,9 @@ 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_setup_tc(struct net_device *dev, enum tc_setup_type type,
++ void *type_data);
++
++
+ #endif /* MTK_ETH_H */
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -0,0 +1,485 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
++ */
++
++#include <linux/if_ether.h>
++#include <linux/rhashtable.h>
++#include <linux/if_ether.h>
++#include <linux/ip.h>
++#include <net/flow_offload.h>
++#include <net/pkt_cls.h>
++#include <net/dsa.h>
++#include "mtk_eth_soc.h"
++
++struct mtk_flow_data {
++ struct ethhdr eth;
++
++ union {
++ struct {
++ __be32 src_addr;
++ __be32 dst_addr;
++ } v4;
++ };
++
++ __be16 src_port;
++ __be16 dst_port;
++
++ struct {
++ u16 id;
++ __be16 proto;
++ u8 num;
++ } vlan;
++ struct {
++ u16 sid;
++ u8 num;
++ } pppoe;
++};
++
++struct mtk_flow_entry {
++ struct rhash_head node;
++ unsigned long cookie;
++ u16 hash;
++};
++
++static const struct rhashtable_params mtk_flow_ht_params = {
++ .head_offset = offsetof(struct mtk_flow_entry, node),
++ .head_offset = offsetof(struct mtk_flow_entry, cookie),
++ .key_len = sizeof(unsigned long),
++ .automatic_shrinking = true,
++};
++
++static u32
++mtk_eth_timestamp(struct mtk_eth *eth)
++{
++ return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
++}
++
++static int
++mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data,
++ bool egress)
++{
++ return mtk_foe_entry_set_ipv4_tuple(foe, egress,
++ data->v4.src_addr, data->src_port,
++ data->v4.dst_addr, data->dst_port);
++}
++
++static void
++mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
++{
++ void *dest = eth + act->mangle.offset;
++ const void *src = &act->mangle.val;
++
++ if (act->mangle.offset > 8)
++ return;
++
++ if (act->mangle.mask == 0xffff) {
++ src += 2;
++ dest += 2;
++ }
++
++ memcpy(dest, src, act->mangle.mask ? 2 : 4);
++}
++
++
++static int
++mtk_flow_mangle_ports(const struct flow_action_entry *act,
++ struct mtk_flow_data *data)
++{
++ u32 val = ntohl(act->mangle.val);
++
++ switch (act->mangle.offset) {
++ case 0:
++ if (act->mangle.mask == ~htonl(0xffff))
++ data->dst_port = cpu_to_be16(val);
++ else
++ data->src_port = cpu_to_be16(val >> 16);
++ break;
++ case 2:
++ data->dst_port = cpu_to_be16(val);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int
++mtk_flow_mangle_ipv4(const struct flow_action_entry *act,
++ struct mtk_flow_data *data)
++{
++ __be32 *dest;
++
++ switch (act->mangle.offset) {
++ case offsetof(struct iphdr, saddr):
++ dest = &data->v4.src_addr;
++ break;
++ case offsetof(struct iphdr, daddr):
++ dest = &data->v4.dst_addr;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ memcpy(dest, &act->mangle.val, sizeof(u32));
++
++ return 0;
++}
++
++static int
++mtk_flow_get_dsa_port(struct net_device **dev)
++{
++#if IS_ENABLED(CONFIG_NET_DSA)
++ struct dsa_port *dp;
++
++ dp = dsa_port_from_netdev(*dev);
++ if (IS_ERR(dp))
++ return -ENODEV;
++
++ if (dp->cpu_dp->tag_ops->proto != DSA_TAG_PROTO_MTK)
++ return -ENODEV;
++
++ *dev = dp->cpu_dp->master;
++
++ return dp->index;
++#else
++ return -ENODEV;
++#endif
++}
++
++static int
++mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
++ struct net_device *dev)
++{
++ int pse_port, dsa_port;
++
++ dsa_port = mtk_flow_get_dsa_port(&dev);
++ if (dsa_port >= 0)
++ mtk_foe_entry_set_dsa(foe, dsa_port);
++
++ if (dev == eth->netdev[0])
++ pse_port = 1;
++ else if (dev == eth->netdev[1])
++ pse_port = 2;
++ else
++ return -EOPNOTSUPP;
++
++ mtk_foe_entry_set_pse_port(foe, pse_port);
++
++ return 0;
++}
++
++static int
++mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
++{
++ struct flow_rule *rule = flow_cls_offload_flow_rule(f);
++ 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;
++ u16 addr_type = 0;
++ u32 timestamp;
++ u8 l4proto = 0;
++ int err = 0;
++ int hash;
++ int i;
++
++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) {
++ struct flow_match_meta match;
++
++ flow_rule_match_meta(rule, &match);
++ } else {
++ return -EOPNOTSUPP;
++ }
++
++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
++ struct flow_match_control match;
++
++ flow_rule_match_control(rule, &match);
++ addr_type = match.key->addr_type;
++ } else {
++ return -EOPNOTSUPP;
++ }
++
++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
++ struct flow_match_basic match;
++
++ flow_rule_match_basic(rule, &match);
++ l4proto = match.key->ip_proto;
++ } else {
++ return -EOPNOTSUPP;
++ }
++
++ flow_action_for_each(i, act, &rule->action) {
++ switch (act->id) {
++ case FLOW_ACTION_MANGLE:
++ if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
++ mtk_flow_offload_mangle_eth(act, &data.eth);
++ break;
++ case FLOW_ACTION_REDIRECT:
++ odev = act->dev;
++ break;
++ case FLOW_ACTION_CSUM:
++ break;
++ case FLOW_ACTION_VLAN_PUSH:
++ if (data.vlan.num == 1 ||
++ act->vlan.proto != htons(ETH_P_8021Q))
++ return -EOPNOTSUPP;
++
++ data.vlan.id = act->vlan.vid;
++ data.vlan.proto = act->vlan.proto;
++ data.vlan.num++;
++ break;
++ case FLOW_ACTION_PPPOE_PUSH:
++ if (data.pppoe.num == 1)
++ return -EOPNOTSUPP;
++
++ data.pppoe.sid = act->pppoe.sid;
++ data.pppoe.num++;
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++ }
++
++ switch (addr_type) {
++ case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
++ offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ if (!is_valid_ether_addr(data.eth.h_source) ||
++ !is_valid_ether_addr(data.eth.h_dest))
++ return -EINVAL;
++
++ err = mtk_foe_entry_prepare(&foe, offload_type, l4proto, 0,
++ data.eth.h_source,
++ data.eth.h_dest);
++ if (err)
++ return err;
++
++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
++ struct flow_match_ports ports;
++
++ flow_rule_match_ports(rule, &ports);
++ data.src_port = ports.key->src;
++ data.dst_port = ports.key->dst;
++ } else {
++ return -EOPNOTSUPP;
++ }
++
++ if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
++ struct flow_match_ipv4_addrs addrs;
++
++ flow_rule_match_ipv4_addrs(rule, &addrs);
++
++ data.v4.src_addr = addrs.key->src;
++ data.v4.dst_addr = addrs.key->dst;
++
++ mtk_flow_set_ipv4_addr(&foe, &data, false);
++ }
++
++ flow_action_for_each(i, act, &rule->action) {
++ if (act->id != FLOW_ACTION_MANGLE)
++ continue;
++
++ switch (act->mangle.htype) {
++ case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
++ case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
++ err = mtk_flow_mangle_ports(act, &data);
++ break;
++ case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
++ err = mtk_flow_mangle_ipv4(act, &data);
++ break;
++ case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
++ /* handled earlier */
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ if (err)
++ return err;
++ }
++
++ if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
++ err = mtk_flow_set_ipv4_addr(&foe, &data, true);
++ if (err)
++ return err;
++ }
++
++ if (data.vlan.num == 1) {
++ if (data.vlan.proto != htons(ETH_P_8021Q))
++ return -EOPNOTSUPP;
++
++ mtk_foe_entry_set_vlan(&foe, data.vlan.id);
++ }
++ if (data.pppoe.num == 1)
++ mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
++
++ err = mtk_flow_set_output_device(eth, &foe, odev);
++ if (err)
++ return err;
++
++ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
++ if (!entry)
++ return -ENOMEM;
++
++ entry->cookie = f->cookie;
++ timestamp = mtk_eth_timestamp(eth);
++ hash = mtk_foe_entry_commit(ð->ppe, &foe, timestamp);
++ if (hash < 0) {
++ err = hash;
++ goto free;
++ }
++
++ entry->hash = hash;
++ err = rhashtable_insert_fast(ð->flow_table, &entry->node,
++ mtk_flow_ht_params);
++ if (err < 0)
++ goto clear_flow;
++
++ return 0;
++clear_flow:
++ mtk_foe_entry_clear(ð->ppe, hash);
++free:
++ kfree(entry);
++ return err;
++}
++
++static int
++mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f)
++{
++ struct mtk_flow_entry *entry;
++
++ entry = rhashtable_lookup(ð->flow_table, &f->cookie,
++ mtk_flow_ht_params);
++ if (!entry)
++ return -ENOENT;
++
++ mtk_foe_entry_clear(ð->ppe, entry->hash);
++ rhashtable_remove_fast(ð->flow_table, &entry->node,
++ mtk_flow_ht_params);
++ kfree(entry);
++
++ return 0;
++}
++
++static int
++mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
++{
++ struct mtk_flow_entry *entry;
++ int timestamp;
++ u32 idle;
++
++ entry = rhashtable_lookup(ð->flow_table, &f->cookie,
++ mtk_flow_ht_params);
++ if (!entry)
++ return -ENOENT;
++
++ timestamp = mtk_foe_entry_timestamp(ð->ppe, entry->hash);
++ if (timestamp < 0)
++ return -ETIMEDOUT;
++
++ idle = mtk_eth_timestamp(eth) - timestamp;
++ f->stats.lastused = jiffies - idle * HZ;
++
++ return 0;
++}
++
++static int
++mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
++{
++ struct flow_cls_offload *cls = type_data;
++ struct net_device *dev = cb_priv;
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_eth *eth = mac->hw;
++
++ if (!tc_can_offload(dev))
++ return -EOPNOTSUPP;
++
++ if (type != TC_SETUP_CLSFLOWER)
++ return -EOPNOTSUPP;
++
++ switch (cls->command) {
++ case FLOW_CLS_REPLACE:
++ return mtk_flow_offload_replace(eth, cls);
++ case FLOW_CLS_DESTROY:
++ return mtk_flow_offload_destroy(eth, cls);
++ case FLOW_CLS_STATS:
++ return mtk_flow_offload_stats(eth, cls);
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ return 0;
++}
++
++static int
++mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
++{
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_eth *eth = mac->hw;
++ static LIST_HEAD(block_cb_list);
++ struct flow_block_cb *block_cb;
++ flow_setup_cb_t *cb;
++
++ if (!eth->ppe.foe_table)
++ return -EOPNOTSUPP;
++
++ if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
++ return -EOPNOTSUPP;
++
++ cb = mtk_eth_setup_tc_block_cb;
++ f->driver_block_list = &block_cb_list;
++
++ switch (f->command) {
++ case FLOW_BLOCK_BIND:
++ block_cb = flow_block_cb_lookup(f->block, cb, dev);
++ if (block_cb) {
++ flow_block_cb_incref(block_cb);
++ return 0;
++ }
++ block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
++ if (IS_ERR(block_cb))
++ return PTR_ERR(block_cb);
++
++ flow_block_cb_add(block_cb, f);
++ list_add_tail(&block_cb->driver_list, &block_cb_list);
++ return 0;
++ case FLOW_BLOCK_UNBIND:
++ block_cb = flow_block_cb_lookup(f->block, cb, dev);
++ if (!block_cb)
++ return -ENOENT;
++
++ if (flow_block_cb_decref(block_cb)) {
++ flow_block_cb_remove(block_cb, f);
++ list_del(&block_cb->driver_list);
++ }
++ return 0;
++ default:
++ return -EOPNOTSUPP;
++ }
++}
++
++int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
++ void *type_data)
++{
++ if (type == TC_SETUP_FT)
++ return mtk_eth_setup_tc_block(dev, type_data);
++
++ return -EOPNOTSUPP;
++}
++
++int mtk_eth_offload_init(struct mtk_eth *eth)
++{
++ if (!eth->ppe.foe_table)
++ return 0;
++
++ return rhashtable_init(ð->flow_table, &mtk_flow_ht_params);
++}
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:55 +0100
+Subject: [PATCH] docs: nf_flowtable: update documentation with
+ enhancements
+
+This patch updates the flowtable documentation to describe recent
+enhancements:
+
+- Offload action is available after the first packets go through the
+ classic forwarding path.
+- IPv4 and IPv6 are supported. Only TCP and UDP layer 4 are supported at
+ this stage.
+- Tuple has been augmented to track VLAN id and PPPoE session id.
+- Bridge and IP forwarding integration, including bridge VLAN filtering
+ support.
+- Hardware offload support.
+- Describe the [OFFLOAD] and [HW_OFFLOAD] tags in the conntrack table
+ listing.
+- Replace 'flow offload' by 'flow add' in example rulesets (preferred
+ syntax).
+- Describe existing cache limitations.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/Documentation/networking/nf_flowtable.rst
++++ b/Documentation/networking/nf_flowtable.rst
+@@ -4,35 +4,38 @@
+ Netfilter's flowtable infrastructure
+ ====================================
+
+-This documentation describes the software flowtable infrastructure available in
+-Netfilter since Linux kernel 4.16.
++This documentation describes the Netfilter flowtable infrastructure which allows
++you to define a fastpath through the flowtable datapath. This infrastructure
++also provides hardware offload support. The flowtable supports for the layer 3
++IPv4 and IPv6 and the layer 4 TCP and UDP protocols.
+
+ Overview
+ --------
+
+-Initial packets follow the classic forwarding path, once the flow enters the
+-established state according to the conntrack semantics (ie. we have seen traffic
+-in both directions), then you can decide to offload the flow to the flowtable
+-from the forward chain via the 'flow offload' action available in nftables.
+-
+-Packets that find an entry in the flowtable (ie. flowtable hit) are sent to the
+-output netdevice via neigh_xmit(), hence, they bypass the classic forwarding
+-path (the visible effect is that you do not see these packets from any of the
+-netfilter hooks coming after the ingress). In case of flowtable miss, the packet
+-follows the classic forward path.
+-
+-The flowtable uses a resizable hashtable, lookups are based on the following
+-7-tuple selectors: source, destination, layer 3 and layer 4 protocols, source
+-and destination ports and the input interface (useful in case there are several
+-conntrack zones in place).
+-
+-Flowtables are populated via the 'flow offload' nftables action, so the user can
+-selectively specify what flows are placed into the flow table. Hence, packets
+-follow the classic forwarding path unless the user explicitly instruct packets
+-to use this new alternative forwarding path via nftables policy.
++Once the first packet of the flow successfully goes through the IP forwarding
++path, from the second packet on, you might decide to offload the flow to the
++flowtable through your ruleset. The flowtable infrastructure provides a rule
++action that allows you to specify when to add a flow to the flowtable.
++
++A packet that finds a matching entry in the flowtable (ie. flowtable hit) is
++transmitted to the output netdevice via neigh_xmit(), hence, packets bypass the
++classic IP forwarding path (the visible effect is that you do not see these
++packets from any of the Netfilter hooks coming after ingress). In case that
++there is no matching entry in the flowtable (ie. flowtable miss), the packet
++follows the classic IP forwarding path.
++
++The flowtable uses a resizable hashtable. Lookups are based on the following
++n-tuple selectors: layer 2 protocol encapsulation (VLAN and PPPoE), layer 3
++source and destination, layer 4 source and destination ports and the input
++interface (useful in case there are several conntrack zones in place).
++
++The 'flow add' action allows you to populate the flowtable, the user selectively
++specifies what flows are placed into the flowtable. Hence, packets follow the
++classic IP forwarding path unless the user explicitly instruct flows to use this
++new alternative forwarding path via policy.
+
+-This is represented in Fig.1, which describes the classic forwarding path
+-including the Netfilter hooks and the flowtable fastpath bypass.
++The flowtable datapath is represented in Fig.1, which describes the classic IP
++forwarding path including the Netfilter hooks and the flowtable fastpath bypass.
+
+ ::
+
+@@ -67,11 +70,13 @@ including the Netfilter hooks and the fl
+ Fig.1 Netfilter hooks and flowtable interactions
+
+ The flowtable entry also stores the NAT configuration, so all packets are
+-mangled according to the NAT policy that matches the initial packets that went
+-through the classic forwarding path. The TTL is decremented before calling
+-neigh_xmit(). Fragmented traffic is passed up to follow the classic forwarding
+-path given that the transport selectors are missing, therefore flowtable lookup
+-is not possible.
++mangled according to the NAT policy that is specified from the classic IP
++forwarding path. The TTL is decremented before calling neigh_xmit(). Fragmented
++traffic is passed up to follow the classic IP forwarding path given that the
++transport header is missing, in this case, flowtable lookups are not possible.
++TCP RST and FIN packets are also passed up to the classic IP forwarding path to
++release the flow gracefully. Packets that exceed the MTU are also passed up to
++the classic forwarding path to report packet-too-big ICMP errors to the sender.
+
+ Example configuration
+ ---------------------
+@@ -85,7 +90,7 @@ flowtable and add one rule to your forwa
+ }
+ chain y {
+ type filter hook forward priority 0; policy accept;
+- ip protocol tcp flow offload @f
++ ip protocol tcp flow add @f
+ counter packets 0 bytes 0
+ }
+ }
+@@ -103,6 +108,117 @@ flow is offloaded, you will observe that
+ does not get updated for the packets that are being forwarded through the
+ forwarding bypass.
+
++You can identify offloaded flows through the [OFFLOAD] tag when listing your
++connection tracking table.
++
++::
++ # conntrack -L
++ tcp 6 src=10.141.10.2 dst=192.168.10.2 sport=52728 dport=5201 src=192.168.10.2 dst=192.168.10.1 sport=5201 dport=52728 [OFFLOAD] mark=0 use=2
++
++
++Layer 2 encapsulation
++---------------------
++
++Since Linux kernel 5.13, the flowtable infrastructure discovers the real
++netdevice behind VLAN and PPPoE netdevices. The flowtable software datapath
++parses the VLAN and PPPoE layer 2 headers to extract the ethertype and the
++VLAN ID / PPPoE session ID which are used for the flowtable lookups. The
++flowtable datapath also deals with layer 2 decapsulation.
++
++You do not need to add the PPPoE and the VLAN devices to your flowtable,
++instead the real device is sufficient for the flowtable to track your flows.
++
++Bridge and IP forwarding
++------------------------
++
++Since Linux kernel 5.13, you can add bridge ports to the flowtable. The
++flowtable infrastructure discovers the topology behind the bridge device. This
++allows the flowtable to define a fastpath bypass between the bridge ports
++(represented as eth1 and eth2 in the example figure below) and the gateway
++device (represented as eth0) in your switch/router.
++
++::
++ fastpath bypass
++ .-------------------------.
++ / \
++ | IP forwarding |
++ | / \ \/
++ | br0 eth0 ..... eth0
++ . / \ *host B*
++ -> eth1 eth2
++ . *switch/router*
++ .
++ .
++ eth0
++ *host A*
++
++The flowtable infrastructure also supports for bridge VLAN filtering actions
++such as PVID and untagged. You can also stack a classic VLAN device on top of
++your bridge port.
++
++If you would like that your flowtable defines a fastpath between your bridge
++ports and your IP forwarding path, you have to add your bridge ports (as
++represented by the real netdevice) to your flowtable definition.
++
++Counters
++--------
++
++The flowtable can synchronize packet and byte counters with the existing
++connection tracking entry by specifying the counter statement in your flowtable
++definition, e.g.
++
++::
++ table inet x {
++ flowtable f {
++ hook ingress priority 0; devices = { eth0, eth1 };
++ counter
++ }
++ ...
++ }
++
++Counter support is available since Linux kernel 5.7.
++
++Hardware offload
++----------------
++
++If your network device provides hardware offload support, you can turn it on by
++means of the 'offload' flag in your flowtable definition, e.g.
++
++::
++ table inet x {
++ flowtable f {
++ hook ingress priority 0; devices = { eth0, eth1 };
++ flags offload;
++ }
++ ...
++ }
++
++There is a workqueue that adds the flows to the hardware. Note that a few
++packets might still run over the flowtable software path until the workqueue has
++a chance to offload the flow to the network device.
++
++You can identify hardware offloaded flows through the [HW_OFFLOAD] tag when
++listing your connection tracking table. Please, note that the [OFFLOAD] tag
++refers to the software offload mode, so there is a distinction between [OFFLOAD]
++which refers to the software flowtable fastpath and [HW_OFFLOAD] which refers
++to the hardware offload datapath being used by the flow.
++
++The flowtable hardware offload infrastructure also supports for the DSA
++(Distributed Switch Architecture).
++
++Limitations
++-----------
++
++The flowtable behaves like a cache. The flowtable entries might get stale if
++either the destination MAC address or the egress netdevice that is used for
++transmission changes.
++
++This might be a problem if:
++
++- You run the flowtable in software mode and you combine bridge and IP
++ forwarding in your setup.
++- Hardware offload is enabled.
++
+ More reading
+ ------------
+
--- /dev/null
+From c5d66587b8900201e1530b7c18d41e87bd5812f4 Mon Sep 17 00:00:00 2001
+From: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Date: Thu, 15 Apr 2021 17:37:48 -0700
+Subject: [PATCH] net: ethernet: mediatek: ppe: fix busy wait loop
+
+The intention is for the loop to timeout if the body does not succeed.
+The current logic calls time_is_before_jiffies(timeout) which is false
+until after the timeout, so the loop body never executes.
+
+Fix by using readl_poll_timeout as a more standard and less error-prone
+solution.
+
+Fixes: ba37b7caf1ed ("net: ethernet: mtk_eth_soc: add support for initializing the PPE")
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Cc: Felix Fietkau <nbd@nbd.name>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_ppe.c | 20 +++++++++-----------
+ drivers/net/ethernet/mediatek/mtk_ppe.h | 1 +
+ 2 files changed, 10 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -2,9 +2,8 @@
+ /* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
+
+ #include <linux/kernel.h>
+-#include <linux/jiffies.h>
+-#include <linux/delay.h>
+ #include <linux/io.h>
++#include <linux/iopoll.h>
+ #include <linux/etherdevice.h>
+ #include <linux/platform_device.h>
+ #include "mtk_ppe.h"
+@@ -44,18 +43,17 @@ static u32 ppe_clear(struct mtk_ppe *ppe
+
+ static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
+ {
+- unsigned long timeout = jiffies + HZ;
+-
+- while (time_is_before_jiffies(timeout)) {
+- if (!(ppe_r32(ppe, MTK_PPE_GLO_CFG) & MTK_PPE_GLO_CFG_BUSY))
+- return 0;
++ int ret;
++ u32 val;
+
+- usleep_range(10, 20);
+- }
++ ret = readl_poll_timeout(ppe->base + MTK_PPE_GLO_CFG, val,
++ !(val & MTK_PPE_GLO_CFG_BUSY),
++ 20, MTK_PPE_WAIT_TIMEOUT_US);
+
+- dev_err(ppe->dev, "PPE table busy");
++ if (ret)
++ dev_err(ppe->dev, "PPE table busy");
+
+- return -ETIMEDOUT;
++ return ret;
+ }
+
+ static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -12,6 +12,7 @@
+ #define MTK_PPE_ENTRIES_SHIFT 3
+ #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
+
+ #define MTK_FOE_IB1_UNBIND_TIMESTAMP GENMASK(7, 0)
+ #define MTK_FOE_IB1_UNBIND_PACKETS GENMASK(23, 8)
--- /dev/null
+From 6ecaf81d4ac6365f9284f9d68d74f7c209e74f98 Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Sat, 17 Apr 2021 15:29:04 +0800
+Subject: [PATCH] net: ethernet: mediatek: fix a typo bug in flow offloading
+
+Issue was traffic problems after a while with increased ping times if
+flow offload is active. It turns out that key_offset with cookie is
+needed in rhashtable_params but was re-assigned to head_offset.
+Fix the assignment.
+
+Fixes: 502e84e2382d ("net: ethernet: mtk_eth_soc: add flow offloading support")
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Tested-by: Frank Wunderlich <frank-w@public-files.de>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ 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
+@@ -44,7 +44,7 @@ struct mtk_flow_entry {
+
+ static const struct rhashtable_params mtk_flow_ht_params = {
+ .head_offset = offsetof(struct mtk_flow_entry, node),
+- .head_offset = offsetof(struct mtk_flow_entry, cookie),
++ .key_offset = offsetof(struct mtk_flow_entry, cookie),
+ .key_len = sizeof(unsigned long),
+ .automatic_shrinking = true,
+ };
--- /dev/null
+From 5196c417854942e218a59ec87bf7d414b3bd581e Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Apr 2021 22:20:55 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: unmap RX data before calling
+ build_skb
+
+Since build_skb accesses the data area (for initializing shinfo), dma unmap
+needs to happen before that call
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+[Ilya: split build_skb cleanup fix into a separate commit]
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1319,6 +1319,9 @@ static int mtk_poll_rx(struct napi_struc
+ goto release_desc;
+ }
+
++ dma_unmap_single(eth->dev, trxd.rxd1,
++ ring->buf_size, DMA_FROM_DEVICE);
++
+ /* receive data */
+ skb = build_skb(data, ring->frag_size);
+ if (unlikely(!skb)) {
+@@ -1328,8 +1331,6 @@ static int mtk_poll_rx(struct napi_struc
+ }
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+
+- dma_unmap_single(eth->dev, trxd.rxd1,
+- ring->buf_size, DMA_FROM_DEVICE);
+ pktlen = RX_DMA_GET_PLEN0(trxd.rxd2);
+ skb->dev = netdev;
+ skb_put(skb, pktlen);
--- /dev/null
+From 787082ab9f7be4711e52f67c388535eda74a1269 Mon Sep 17 00:00:00 2001
+From: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Date: Thu, 22 Apr 2021 22:20:56 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix build_skb cleanup
+
+In case build_skb fails, call skb_free_frag on the correct pointer. Also
+update the DMA structures with the new mapping before exiting, because
+the mapping was successful
+
+Suggested-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1325,9 +1325,9 @@ static int mtk_poll_rx(struct napi_struc
+ /* receive data */
+ skb = build_skb(data, ring->frag_size);
+ if (unlikely(!skb)) {
+- skb_free_frag(new_data);
++ skb_free_frag(data);
+ netdev->stats.rx_dropped++;
+- goto release_desc;
++ goto skip_rx;
+ }
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+
+@@ -1347,6 +1347,7 @@ static int mtk_poll_rx(struct napi_struc
+ skb_record_rx_queue(skb, 0);
+ napi_gro_receive(napi, skb);
+
++skip_rx:
+ ring->data[idx] = new_data;
+ rxd->rxd1 = (unsigned int)dma_addr;
+
--- /dev/null
+From c30c4a82739090a2de4a4e3f245355ea4fb3ec14 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Apr 2021 22:20:57 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: use napi_consume_skb
+
+Should improve performance, since it can use bulk free
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 19 ++++++++++++-------
+ 1 file changed, 12 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -879,7 +879,8 @@ static int txd_to_idx(struct mtk_tx_ring
+ return ((void *)dma - (void *)ring->dma) / sizeof(*dma);
+ }
+
+-static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf)
++static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf,
++ bool napi)
+ {
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+ if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) {
+@@ -911,8 +912,12 @@ static void mtk_tx_unmap(struct mtk_eth
+
+ tx_buf->flags = 0;
+ if (tx_buf->skb &&
+- (tx_buf->skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC))
+- dev_kfree_skb_any(tx_buf->skb);
++ (tx_buf->skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC)) {
++ if (napi)
++ napi_consume_skb(tx_buf->skb, napi);
++ else
++ dev_kfree_skb_any(tx_buf->skb);
++ }
+ tx_buf->skb = NULL;
+ }
+
+@@ -1090,7 +1095,7 @@ err_dma:
+ tx_buf = mtk_desc_to_tx_buf(ring, itxd);
+
+ /* unmap dma */
+- mtk_tx_unmap(eth, tx_buf);
++ mtk_tx_unmap(eth, tx_buf, false);
+
+ itxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+@@ -1409,7 +1414,7 @@ static int mtk_poll_tx_qdma(struct mtk_e
+ done[mac]++;
+ budget--;
+ }
+- mtk_tx_unmap(eth, tx_buf);
++ mtk_tx_unmap(eth, tx_buf, true);
+
+ ring->last_free = desc;
+ atomic_inc(&ring->free_count);
+@@ -1446,7 +1451,7 @@ static int mtk_poll_tx_pdma(struct mtk_e
+ budget--;
+ }
+
+- mtk_tx_unmap(eth, tx_buf);
++ mtk_tx_unmap(eth, tx_buf, true);
+
+ desc = &ring->dma[cpu];
+ ring->last_free = desc;
+@@ -1648,7 +1653,7 @@ static void mtk_tx_clean(struct mtk_eth
+
+ if (ring->buf) {
+ for (i = 0; i < MTK_DMA_SIZE; i++)
+- mtk_tx_unmap(eth, &ring->buf[i]);
++ mtk_tx_unmap(eth, &ring->buf[i], false);
+ kfree(ring->buf);
+ ring->buf = NULL;
+ }
--- /dev/null
+From 3630d519d7c3eab92567658690e44ffe0517d109 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Apr 2021 22:20:58 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: reduce MDIO bus access latency
+
+usleep_range often ends up sleeping much longer than the 10-20us provided
+as a range here. This causes significant latency in mdio bus acceses,
+which easily adds multiple seconds to the boot time on MT7621 when polling
+DSA slave ports.
+Use cond_resched instead of usleep_range, since the MDIO access does not
+take much time
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ 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
+@@ -86,7 +86,7 @@ static int mtk_mdio_busy_wait(struct mtk
+ return 0;
+ if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT))
+ break;
+- usleep_range(10, 20);
++ cond_resched();
+ }
+
+ dev_err(eth->dev, "mdio: MDIO timeout\n");
--- /dev/null
+From 16ef670789b252b221700adc413497ed2f941d8a Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Apr 2021 22:20:59 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: remove unnecessary TX queue stops
+
+When running short on descriptors, only stop the queue for the netdev that
+tx was attempted for. By the time something tries to send on the other
+netdev, the ring might have some more room already.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 15 ++-------------
+ 1 file changed, 2 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1152,17 +1152,6 @@ static void mtk_wake_queue(struct mtk_et
+ }
+ }
+
+-static void mtk_stop_queue(struct mtk_eth *eth)
+-{
+- int i;
+-
+- for (i = 0; i < MTK_MAC_COUNT; i++) {
+- if (!eth->netdev[i])
+- continue;
+- netif_stop_queue(eth->netdev[i]);
+- }
+-}
+-
+ static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
+@@ -1183,7 +1172,7 @@ static netdev_tx_t mtk_start_xmit(struct
+
+ tx_num = mtk_cal_txd_req(skb);
+ if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
+- mtk_stop_queue(eth);
++ netif_stop_queue(dev);
+ netif_err(eth, tx_queued, dev,
+ "Tx Ring full when queue awake!\n");
+ spin_unlock(ð->page_lock);
+@@ -1209,7 +1198,7 @@ static netdev_tx_t mtk_start_xmit(struct
+ goto drop;
+
+ if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
+- mtk_stop_queue(eth);
++ netif_stop_queue(dev);
+
+ spin_unlock(ð->page_lock);
+
--- /dev/null
+From 59555a8d0dd39bf60b7ca1ba5e7393d293f7398d Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Apr 2021 22:21:00 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: use larger burst size for QDMA TX
+
+Improves tx performance
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2214,7 +2214,7 @@ static int mtk_start_dma(struct mtk_eth
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+ mtk_w32(eth,
+ MTK_TX_WB_DDONE | MTK_TX_DMA_EN |
+- MTK_DMA_SIZE_16DWORDS | MTK_NDP_CO_PRO |
++ MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO |
+ MTK_RX_DMA_EN | MTK_RX_2B_OFFSET |
+ MTK_RX_BT_32DWORDS,
+ MTK_QDMA_GLO_CFG);
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -202,7 +202,7 @@
+ #define MTK_RX_BT_32DWORDS (3 << 11)
+ #define MTK_NDP_CO_PRO BIT(10)
+ #define MTK_TX_WB_DDONE BIT(6)
+-#define MTK_DMA_SIZE_16DWORDS (2 << 4)
++#define MTK_TX_BT_32DWORDS (3 << 4)
+ #define MTK_RX_DMA_BUSY BIT(3)
+ #define MTK_TX_DMA_BUSY BIT(1)
+ #define MTK_RX_DMA_EN BIT(2)
--- /dev/null
+From 6b4423b258b91032c50a5efca15d3d9bb194ea1d Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Apr 2021 22:21:01 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: increase DMA ring sizes
+
+256 descriptors is not enough for multi-gigabit traffic under load on
+MT7622. Bump it to 512 to improve performance.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -21,7 +21,7 @@
+ #define MTK_QDMA_PAGE_SIZE 2048
+ #define MTK_MAX_RX_LENGTH 1536
+ #define MTK_TX_DMA_BUF_LEN 0x3fff
+-#define MTK_DMA_SIZE 256
++#define MTK_DMA_SIZE 512
+ #define MTK_NAPI_WEIGHT 64
+ #define MTK_MAC_COUNT 2
+ #define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
--- /dev/null
+From e9229ffd550b2d8c4997c67a501dbc3919fd4e26 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Apr 2021 22:21:02 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: implement dynamic interrupt
+ moderation
+
+Reduces the number of interrupts under load
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+[Ilya: add documentation for new struct fields]
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/Kconfig | 1 +
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 96 +++++++++++++++++++--
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 41 +++++++--
+ 3 files changed, 124 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/Kconfig
++++ b/drivers/net/ethernet/mediatek/Kconfig
+@@ -10,6 +10,7 @@ if NET_VENDOR_MEDIATEK
+ config NET_MEDIATEK_SOC
+ tristate "MediaTek SoC Gigabit Ethernet support"
+ select PHYLINK
++ select DIMLIB
+ help
+ This driver supports the gigabit ethernet MACs in the
+ MediaTek SoC family.
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1254,12 +1254,13 @@ static void mtk_update_rx_cpu_idx(struct
+ static int mtk_poll_rx(struct napi_struct *napi, int budget,
+ struct mtk_eth *eth)
+ {
++ struct dim_sample dim_sample = {};
+ struct mtk_rx_ring *ring;
+ int idx;
+ struct sk_buff *skb;
+ u8 *data, *new_data;
+ struct mtk_rx_dma *rxd, trxd;
+- int done = 0;
++ int done = 0, bytes = 0;
+
+ while (done < budget) {
+ struct net_device *netdev;
+@@ -1333,6 +1334,7 @@ static int mtk_poll_rx(struct napi_struc
+ else
+ skb_checksum_none_assert(skb);
+ skb->protocol = eth_type_trans(skb, netdev);
++ bytes += pktlen;
+
+ if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX &&
+ (trxd.rxd2 & RX_DMA_VTAG))
+@@ -1365,6 +1367,12 @@ rx_done:
+ mtk_update_rx_cpu_idx(eth);
+ }
+
++ eth->rx_packets += done;
++ eth->rx_bytes += bytes;
++ dim_update_sample(eth->rx_events, eth->rx_packets, eth->rx_bytes,
++ &dim_sample);
++ net_dim(ð->rx_dim, dim_sample);
++
+ return done;
+ }
+
+@@ -1457,6 +1465,7 @@ static int mtk_poll_tx_pdma(struct mtk_e
+ static int mtk_poll_tx(struct mtk_eth *eth, int budget)
+ {
+ struct mtk_tx_ring *ring = ð->tx_ring;
++ struct dim_sample dim_sample = {};
+ unsigned int done[MTK_MAX_DEVS];
+ unsigned int bytes[MTK_MAX_DEVS];
+ int total = 0, i;
+@@ -1474,8 +1483,14 @@ static int mtk_poll_tx(struct mtk_eth *e
+ continue;
+ netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
+ total += done[i];
++ eth->tx_packets += done[i];
++ eth->tx_bytes += bytes[i];
+ }
+
++ dim_update_sample(eth->tx_events, eth->tx_packets, eth->tx_bytes,
++ &dim_sample);
++ net_dim(ð->tx_dim, dim_sample);
++
+ if (mtk_queue_stopped(eth) &&
+ (atomic_read(&ring->free_count) > ring->thresh))
+ mtk_wake_queue(eth);
+@@ -2150,6 +2165,7 @@ static irqreturn_t mtk_handle_irq_rx(int
+ {
+ struct mtk_eth *eth = _eth;
+
++ eth->rx_events++;
+ if (likely(napi_schedule_prep(ð->rx_napi))) {
+ __napi_schedule(ð->rx_napi);
+ mtk_rx_irq_disable(eth, MTK_RX_DONE_INT);
+@@ -2162,6 +2178,7 @@ static irqreturn_t mtk_handle_irq_tx(int
+ {
+ struct mtk_eth *eth = _eth;
+
++ eth->tx_events++;
+ if (likely(napi_schedule_prep(ð->tx_napi))) {
+ __napi_schedule(ð->tx_napi);
+ mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+@@ -2346,6 +2363,9 @@ static int mtk_stop(struct net_device *d
+ napi_disable(ð->tx_napi);
+ napi_disable(ð->rx_napi);
+
++ cancel_work_sync(ð->rx_dim.work);
++ cancel_work_sync(ð->tx_dim.work);
++
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+ mtk_stop_dma(eth, MTK_QDMA_GLO_CFG);
+ mtk_stop_dma(eth, MTK_PDMA_GLO_CFG);
+@@ -2398,6 +2418,64 @@ err_disable_clks:
+ return ret;
+ }
+
++static void mtk_dim_rx(struct work_struct *work)
++{
++ struct dim *dim = container_of(work, struct dim, work);
++ struct mtk_eth *eth = container_of(dim, struct mtk_eth, rx_dim);
++ struct dim_cq_moder cur_profile;
++ u32 val, cur;
++
++ cur_profile = net_dim_get_rx_moderation(eth->rx_dim.mode,
++ dim->profile_ix);
++ spin_lock_bh(ð->dim_lock);
++
++ val = mtk_r32(eth, MTK_PDMA_DELAY_INT);
++ val &= MTK_PDMA_DELAY_TX_MASK;
++ val |= MTK_PDMA_DELAY_RX_EN;
++
++ cur = min_t(u32, DIV_ROUND_UP(cur_profile.usec, 20), MTK_PDMA_DELAY_PTIME_MASK);
++ val |= cur << MTK_PDMA_DELAY_RX_PTIME_SHIFT;
++
++ cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK);
++ val |= cur << MTK_PDMA_DELAY_RX_PINT_SHIFT;
++
++ mtk_w32(eth, val, MTK_PDMA_DELAY_INT);
++ mtk_w32(eth, val, MTK_QDMA_DELAY_INT);
++
++ spin_unlock_bh(ð->dim_lock);
++
++ dim->state = DIM_START_MEASURE;
++}
++
++static void mtk_dim_tx(struct work_struct *work)
++{
++ struct dim *dim = container_of(work, struct dim, work);
++ struct mtk_eth *eth = container_of(dim, struct mtk_eth, tx_dim);
++ struct dim_cq_moder cur_profile;
++ u32 val, cur;
++
++ cur_profile = net_dim_get_tx_moderation(eth->tx_dim.mode,
++ dim->profile_ix);
++ spin_lock_bh(ð->dim_lock);
++
++ val = mtk_r32(eth, MTK_PDMA_DELAY_INT);
++ val &= MTK_PDMA_DELAY_RX_MASK;
++ val |= MTK_PDMA_DELAY_TX_EN;
++
++ cur = min_t(u32, DIV_ROUND_UP(cur_profile.usec, 20), MTK_PDMA_DELAY_PTIME_MASK);
++ val |= cur << MTK_PDMA_DELAY_TX_PTIME_SHIFT;
++
++ cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK);
++ val |= cur << MTK_PDMA_DELAY_TX_PINT_SHIFT;
++
++ mtk_w32(eth, val, MTK_PDMA_DELAY_INT);
++ mtk_w32(eth, val, MTK_QDMA_DELAY_INT);
++
++ spin_unlock_bh(ð->dim_lock);
++
++ dim->state = DIM_START_MEASURE;
++}
++
+ static int mtk_hw_init(struct mtk_eth *eth)
+ {
+ int i, val, ret;
+@@ -2419,9 +2497,6 @@ static int mtk_hw_init(struct mtk_eth *e
+ goto err_disable_pm;
+ }
+
+- /* enable interrupt delay for RX */
+- mtk_w32(eth, MTK_PDMA_DELAY_RX_DELAY, MTK_PDMA_DELAY_INT);
+-
+ /* disable delay and normal interrupt */
+ mtk_tx_irq_disable(eth, ~0);
+ mtk_rx_irq_disable(eth, ~0);
+@@ -2460,11 +2535,11 @@ static int mtk_hw_init(struct mtk_eth *e
+ /* Enable RX VLan Offloading */
+ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
+
+- /* enable interrupt delay for RX */
+- mtk_w32(eth, MTK_PDMA_DELAY_RX_DELAY, MTK_PDMA_DELAY_INT);
++ /* set interrupt delays based on current Net DIM sample */
++ mtk_dim_rx(ð->rx_dim.work);
++ mtk_dim_tx(ð->tx_dim.work);
+
+ /* disable delay and normal interrupt */
+- mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
+ mtk_tx_irq_disable(eth, ~0);
+ mtk_rx_irq_disable(eth, ~0);
+
+@@ -2969,6 +3044,13 @@ static int mtk_probe(struct platform_dev
+ spin_lock_init(ð->page_lock);
+ spin_lock_init(ð->tx_irq_lock);
+ spin_lock_init(ð->rx_irq_lock);
++ spin_lock_init(ð->dim_lock);
++
++ eth->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
++ INIT_WORK(ð->rx_dim.work, mtk_dim_rx);
++
++ eth->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
++ INIT_WORK(ð->tx_dim.work, mtk_dim_tx);
+
+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
+ eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -16,6 +16,7 @@
+ #include <linux/refcount.h>
+ #include <linux/phylink.h>
+ #include <linux/rhashtable.h>
++#include <linux/dim.h>
+ #include "mtk_ppe.h"
+
+ #define MTK_QDMA_PAGE_SIZE 2048
+@@ -136,13 +137,18 @@
+
+ /* PDMA Delay Interrupt Register */
+ #define MTK_PDMA_DELAY_INT 0xa0c
++#define MTK_PDMA_DELAY_RX_MASK GENMASK(15, 0)
+ #define MTK_PDMA_DELAY_RX_EN BIT(15)
+-#define MTK_PDMA_DELAY_RX_PINT 4
+ #define MTK_PDMA_DELAY_RX_PINT_SHIFT 8
+-#define MTK_PDMA_DELAY_RX_PTIME 4
+-#define MTK_PDMA_DELAY_RX_DELAY \
+- (MTK_PDMA_DELAY_RX_EN | MTK_PDMA_DELAY_RX_PTIME | \
+- (MTK_PDMA_DELAY_RX_PINT << MTK_PDMA_DELAY_RX_PINT_SHIFT))
++#define MTK_PDMA_DELAY_RX_PTIME_SHIFT 0
++
++#define MTK_PDMA_DELAY_TX_MASK GENMASK(31, 16)
++#define MTK_PDMA_DELAY_TX_EN BIT(31)
++#define MTK_PDMA_DELAY_TX_PINT_SHIFT 24
++#define MTK_PDMA_DELAY_TX_PTIME_SHIFT 16
++
++#define MTK_PDMA_DELAY_PINT_MASK 0x7f
++#define MTK_PDMA_DELAY_PTIME_MASK 0xff
+
+ /* PDMA Interrupt Status Register */
+ #define MTK_PDMA_INT_STATUS 0xa20
+@@ -224,6 +230,7 @@
+ /* QDMA Interrupt Status Register */
+ #define MTK_QDMA_INT_STATUS 0x1A18
+ #define MTK_RX_DONE_DLY BIT(30)
++#define MTK_TX_DONE_DLY BIT(28)
+ #define MTK_RX_DONE_INT3 BIT(19)
+ #define MTK_RX_DONE_INT2 BIT(18)
+ #define MTK_RX_DONE_INT1 BIT(17)
+@@ -233,8 +240,7 @@
+ #define MTK_TX_DONE_INT1 BIT(1)
+ #define MTK_TX_DONE_INT0 BIT(0)
+ #define MTK_RX_DONE_INT MTK_RX_DONE_DLY
+-#define MTK_TX_DONE_INT (MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \
+- MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3)
++#define MTK_TX_DONE_INT MTK_TX_DONE_DLY
+
+ /* QDMA Interrupt grouping registers */
+ #define MTK_QDMA_INT_GRP1 0x1a20
+@@ -863,6 +869,7 @@ struct mtk_sgmii {
+ * @page_lock: Make sure that register operations are atomic
+ * @tx_irq__lock: Make sure that IRQ register operations are atomic
+ * @rx_irq__lock: Make sure that IRQ register operations are atomic
++ * @dim_lock: Make sure that Net DIM operations are atomic
+ * @dummy_dev: we run 2 netdevs on 1 physical DMA ring and need a
+ * dummy for NAPI to work
+ * @netdev: The netdev instances
+@@ -881,6 +888,14 @@ struct mtk_sgmii {
+ * @rx_ring_qdma: Pointer to the memory holding info about the QDMA RX ring
+ * @tx_napi: The TX NAPI struct
+ * @rx_napi: The RX NAPI struct
++ * @rx_events: Net DIM RX event counter
++ * @rx_packets: Net DIM RX packet counter
++ * @rx_bytes: Net DIM RX byte counter
++ * @rx_dim: Net DIM RX context
++ * @tx_events: Net DIM TX event counter
++ * @tx_packets: Net DIM TX packet counter
++ * @tx_bytes: Net DIM TX byte counter
++ * @tx_dim: Net DIM TX context
+ * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring
+ * @phy_scratch_ring: physical address of scratch_ring
+ * @scratch_head: The scratch memory that scratch_ring points to.
+@@ -925,6 +940,18 @@ struct mtk_eth {
+
+ const struct mtk_soc_data *soc;
+
++ spinlock_t dim_lock;
++
++ u32 rx_events;
++ u32 rx_packets;
++ u32 rx_bytes;
++ struct dim rx_dim;
++
++ u32 tx_events;
++ u32 tx_packets;
++ u32 tx_bytes;
++ struct dim tx_dim;
++
+ u32 tx_int_mask_reg;
+ u32 tx_int_status_reg;
+ u32 rx_dma_l4_valid;
--- /dev/null
+From 4e6bf609569c59b6bd6acf4a607c096cbd820d79 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Apr 2021 22:21:03 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: cache HW pointer of last freed TX
+ descriptor
+
+The value is only updated by the CPU, so it is cheaper to access from the
+ring data structure than from a hardware register.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 8 ++++----
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 ++
+ 2 files changed, 6 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1385,7 +1385,7 @@ static int mtk_poll_tx_qdma(struct mtk_e
+ struct mtk_tx_buf *tx_buf;
+ u32 cpu, dma;
+
+- cpu = mtk_r32(eth, MTK_QTX_CRX_PTR);
++ cpu = ring->last_free_ptr;
+ dma = mtk_r32(eth, MTK_QTX_DRX_PTR);
+
+ desc = mtk_qdma_phys_to_virt(ring, cpu);
+@@ -1419,6 +1419,7 @@ static int mtk_poll_tx_qdma(struct mtk_e
+ cpu = next_cpu;
+ }
+
++ ring->last_free_ptr = cpu;
+ mtk_w32(eth, cpu, MTK_QTX_CRX_PTR);
+
+ return budget;
+@@ -1619,6 +1620,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+ atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
+ ring->next_free = &ring->dma[0];
+ ring->last_free = &ring->dma[MTK_DMA_SIZE - 1];
++ ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz));
+ ring->thresh = MAX_SKB_FRAGS;
+
+ /* make sure that all changes to the dma ring are flushed before we
+@@ -1632,9 +1634,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+ mtk_w32(eth,
+ ring->phys + ((MTK_DMA_SIZE - 1) * sz),
+ MTK_QTX_CRX_PTR);
+- mtk_w32(eth,
+- ring->phys + ((MTK_DMA_SIZE - 1) * sz),
+- MTK_QTX_DRX_PTR);
++ mtk_w32(eth, ring->last_free_ptr, MTK_QTX_DRX_PTR);
+ mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES,
+ MTK_QTX_CFG(0));
+ } else {
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -656,6 +656,7 @@ struct mtk_tx_buf {
+ * @phys: The physical addr of tx_buf
+ * @next_free: Pointer to the next free descriptor
+ * @last_free: Pointer to the last free descriptor
++ * @last_free_ptr: Hardware pointer value of the last free descriptor
+ * @thresh: The threshold of minimum amount of free descriptors
+ * @free_count: QDMA uses a linked list. Track how many free descriptors
+ * are present
+@@ -666,6 +667,7 @@ struct mtk_tx_ring {
+ dma_addr_t phys;
+ struct mtk_tx_dma *next_free;
+ struct mtk_tx_dma *last_free;
++ u32 last_free_ptr;
+ u16 thresh;
+ atomic_t free_count;
+ int dma_size;
--- /dev/null
+From 816ac3e6e67bdd78d86226c6eb53619780750e92 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Apr 2021 22:21:04 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: only read the full RX descriptor
+ if DMA is done
+
+Uncached memory access is expensive, and there is no need to access all
+descriptor words if we can't process them anyway
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -798,13 +798,18 @@ static inline int mtk_max_buf_size(int f
+ return buf_size;
+ }
+
+-static inline void mtk_rx_get_desc(struct mtk_rx_dma *rxd,
++static inline bool mtk_rx_get_desc(struct mtk_rx_dma *rxd,
+ struct mtk_rx_dma *dma_rxd)
+ {
+- rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
+ rxd->rxd2 = READ_ONCE(dma_rxd->rxd2);
++ if (!(rxd->rxd2 & RX_DMA_DONE))
++ return false;
++
++ rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
+ rxd->rxd3 = READ_ONCE(dma_rxd->rxd3);
+ rxd->rxd4 = READ_ONCE(dma_rxd->rxd4);
++
++ return true;
+ }
+
+ /* the qdma core needs scratch memory to be setup */
+@@ -1276,8 +1281,7 @@ static int mtk_poll_rx(struct napi_struc
+ rxd = &ring->dma[idx];
+ data = ring->data[idx];
+
+- mtk_rx_get_desc(&trxd, rxd);
+- if (!(trxd.rxd2 & RX_DMA_DONE))
++ if (!mtk_rx_get_desc(&trxd, rxd))
+ break;
+
+ /* find out which mac the packet come from. values start at 1 */
--- /dev/null
+From 16769a8923fad5a5377253bcd76b0e0d64976c73 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Apr 2021 22:21:05 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: reduce unnecessary interrupts
+
+Avoid rearming interrupt if napi_complete returns false
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1540,8 +1540,8 @@ static int mtk_napi_tx(struct napi_struc
+ if (status & MTK_TX_DONE_INT)
+ return budget;
+
+- napi_complete(napi);
+- mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
++ if (napi_complete(napi))
++ mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+
+ return tx_done;
+ }
+@@ -1574,8 +1574,9 @@ poll_again:
+ remain_budget -= rx_done;
+ goto poll_again;
+ }
+- napi_complete(napi);
+- mtk_rx_irq_enable(eth, MTK_RX_DONE_INT);
++
++ if (napi_complete(napi))
++ mtk_rx_irq_enable(eth, MTK_RX_DONE_INT);
+
+ return rx_done + budget - remain_budget;
+ }
--- /dev/null
+From db2c7b353db3b3f71b55f9ff4627d8a786446fbe Mon Sep 17 00:00:00 2001
+From: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Date: Thu, 22 Apr 2021 22:21:06 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: rework NAPI callbacks
+
+Use napi_complete_done to communicate total TX and RX work done to NAPI.
+Count total RX work up instead of remaining work down for clarity.
+Remove unneeded local variables for clarity. Use do {} while instead of
+goto for clarity.
+
+Suggested-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 54 +++++++++------------
+ 1 file changed, 24 insertions(+), 30 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1517,7 +1517,6 @@ static void mtk_handle_status_irq(struct
+ static int mtk_napi_tx(struct napi_struct *napi, int budget)
+ {
+ struct mtk_eth *eth = container_of(napi, struct mtk_eth, tx_napi);
+- u32 status, mask;
+ int tx_done = 0;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+@@ -1526,21 +1525,19 @@ static int mtk_napi_tx(struct napi_struc
+ tx_done = mtk_poll_tx(eth, budget);
+
+ if (unlikely(netif_msg_intr(eth))) {
+- status = mtk_r32(eth, eth->tx_int_status_reg);
+- mask = mtk_r32(eth, eth->tx_int_mask_reg);
+ dev_info(eth->dev,
+- "done tx %d, intr 0x%08x/0x%x\n",
+- tx_done, status, mask);
++ "done tx %d, intr 0x%08x/0x%x\n", tx_done,
++ mtk_r32(eth, eth->tx_int_status_reg),
++ mtk_r32(eth, eth->tx_int_mask_reg));
+ }
+
+ if (tx_done == budget)
+ return budget;
+
+- status = mtk_r32(eth, eth->tx_int_status_reg);
+- if (status & MTK_TX_DONE_INT)
++ if (mtk_r32(eth, eth->tx_int_status_reg) & MTK_TX_DONE_INT)
+ return budget;
+
+- if (napi_complete(napi))
++ if (napi_complete_done(napi, tx_done))
+ mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+
+ return tx_done;
+@@ -1549,36 +1546,33 @@ static int mtk_napi_tx(struct napi_struc
+ static int mtk_napi_rx(struct napi_struct *napi, int budget)
+ {
+ struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
+- u32 status, mask;
+- int rx_done = 0;
+- int remain_budget = budget;
++ int rx_done_total = 0;
+
+ mtk_handle_status_irq(eth);
+
+-poll_again:
+- mtk_w32(eth, MTK_RX_DONE_INT, MTK_PDMA_INT_STATUS);
+- rx_done = mtk_poll_rx(napi, remain_budget, eth);
++ do {
++ int rx_done;
+
+- if (unlikely(netif_msg_intr(eth))) {
+- status = mtk_r32(eth, MTK_PDMA_INT_STATUS);
+- mask = mtk_r32(eth, MTK_PDMA_INT_MASK);
+- dev_info(eth->dev,
+- "done rx %d, intr 0x%08x/0x%x\n",
+- rx_done, status, mask);
+- }
+- if (rx_done == remain_budget)
+- return budget;
++ mtk_w32(eth, MTK_RX_DONE_INT, MTK_PDMA_INT_STATUS);
++ rx_done = mtk_poll_rx(napi, budget - rx_done_total, eth);
++ rx_done_total += rx_done;
++
++ if (unlikely(netif_msg_intr(eth))) {
++ dev_info(eth->dev,
++ "done rx %d, intr 0x%08x/0x%x\n", rx_done,
++ mtk_r32(eth, MTK_PDMA_INT_STATUS),
++ mtk_r32(eth, MTK_PDMA_INT_MASK));
++ }
+
+- status = mtk_r32(eth, MTK_PDMA_INT_STATUS);
+- if (status & MTK_RX_DONE_INT) {
+- remain_budget -= rx_done;
+- goto poll_again;
+- }
++ if (rx_done_total == budget)
++ return budget;
++
++ } while (mtk_r32(eth, MTK_PDMA_INT_STATUS) & MTK_RX_DONE_INT);
+
+- if (napi_complete(napi))
++ if (napi_complete_done(napi, rx_done_total))
+ mtk_rx_irq_enable(eth, MTK_RX_DONE_INT);
+
+- return rx_done + budget - remain_budget;
++ return rx_done_total;
+ }
+
+ static int mtk_tx_alloc(struct mtk_eth *eth)
--- /dev/null
+From fa817272c37ef78e25dc14e4760ac78a7043a18a Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Apr 2021 22:21:07 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: set PPE flow hash as skb hash if
+ present
+
+This improves GRO performance
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+[Ilya: Use MTK_RXD4_FOE_ENTRY instead of GENMASK(13, 0)]
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -19,6 +19,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/pinctrl/devinfo.h>
+ #include <linux/phylink.h>
++#include <linux/jhash.h>
+ #include <net/dsa.h>
+
+ #include "mtk_eth_soc.h"
+@@ -1271,6 +1272,7 @@ static int mtk_poll_rx(struct napi_struc
+ struct net_device *netdev;
+ unsigned int pktlen;
+ dma_addr_t dma_addr;
++ u32 hash;
+ int mac;
+
+ ring = mtk_get_rx_ring(eth);
+@@ -1340,6 +1342,12 @@ static int mtk_poll_rx(struct napi_struc
+ skb->protocol = eth_type_trans(skb, netdev);
+ bytes += pktlen;
+
++ hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY;
++ if (hash != MTK_RXD4_FOE_ENTRY) {
++ hash = jhash_1word(hash, 0);
++ skb_set_hash(skb, hash, PKT_HASH_TYPE_L4);
++ }
++
+ if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX &&
+ (trxd.rxd2 & RX_DMA_VTAG))
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
--- /dev/null
+From 3bc8e0aff23be0526af0dbc7973a8866a08d73f1 Mon Sep 17 00:00:00 2001
+From: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Date: Thu, 22 Apr 2021 22:21:08 -0700
+Subject: [PATCH] net: ethernet: mtk_eth_soc: use iopoll.h macro for DMA init
+
+Replace a tight busy-wait loop without a pause with a standard
+readx_poll_timeout_atomic routine with a 5 us poll period.
+
+Tested by booting a MT7621 device to ensure the driver initializes
+properly.
+
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 29 +++++++++------------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +-
+ 2 files changed, 14 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2054,25 +2054,22 @@ static int mtk_set_features(struct net_d
+ /* wait for DMA to finish whatever it is doing before we start using it again */
+ static int mtk_dma_busy_wait(struct mtk_eth *eth)
+ {
+- unsigned long t_start = jiffies;
++ unsigned int reg;
++ int ret;
++ u32 val;
+
+- while (1) {
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+- if (!(mtk_r32(eth, MTK_QDMA_GLO_CFG) &
+- (MTK_RX_DMA_BUSY | MTK_TX_DMA_BUSY)))
+- return 0;
+- } else {
+- if (!(mtk_r32(eth, MTK_PDMA_GLO_CFG) &
+- (MTK_RX_DMA_BUSY | MTK_TX_DMA_BUSY)))
+- return 0;
+- }
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
++ reg = MTK_QDMA_GLO_CFG;
++ else
++ reg = MTK_PDMA_GLO_CFG;
+
+- if (time_after(jiffies, t_start + MTK_DMA_BUSY_TIMEOUT))
+- break;
+- }
++ ret = readx_poll_timeout_atomic(__raw_readl, eth->base + reg, val,
++ !(val & (MTK_RX_DMA_BUSY | MTK_TX_DMA_BUSY)),
++ 5, MTK_DMA_BUSY_TIMEOUT_US);
++ if (ret)
++ dev_err(eth->dev, "DMA init timeout\n");
+
+- dev_err(eth->dev, "DMA init timeout\n");
+- return -1;
++ return ret;
+ }
+
+ static int mtk_dma_init(struct mtk_eth *eth)
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -213,7 +213,7 @@
+ #define MTK_TX_DMA_BUSY BIT(1)
+ #define MTK_RX_DMA_EN BIT(2)
+ #define MTK_TX_DMA_EN BIT(0)
+-#define MTK_DMA_BUSY_TIMEOUT HZ
++#define MTK_DMA_BUSY_TIMEOUT_US 1000000
+
+ /* QDMA Reset Index Register */
+ #define MTK_QDMA_RST_IDX 0x1A08
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sun, 18 Apr 2021 23:11:44 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: missing mutex
+
+Patch 2ed37183abb7 ("netfilter: flowtable: separate replace, destroy and
+stats to different workqueues") splits the workqueue per event type. Add
+a mutex to serialize updates.
+
+Fixes: 502e84e2382d ("net: ethernet: mtk_eth_soc: add flow offloading support")
+Reported-by: Frank Wunderlich <frank-w@public-files.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -392,6 +392,8 @@ mtk_flow_offload_stats(struct mtk_eth *e
+ return 0;
+ }
+
++static DEFINE_MUTEX(mtk_flow_offload_mutex);
++
+ static int
+ mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
+ {
+@@ -399,6 +401,7 @@ mtk_eth_setup_tc_block_cb(enum tc_setup_
+ struct net_device *dev = cb_priv;
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
++ int err;
+
+ if (!tc_can_offload(dev))
+ return -EOPNOTSUPP;
+@@ -406,18 +409,24 @@ mtk_eth_setup_tc_block_cb(enum tc_setup_
+ if (type != TC_SETUP_CLSFLOWER)
+ return -EOPNOTSUPP;
+
++ mutex_lock(&mtk_flow_offload_mutex);
+ switch (cls->command) {
+ case FLOW_CLS_REPLACE:
+- return mtk_flow_offload_replace(eth, cls);
++ err = mtk_flow_offload_replace(eth, cls);
++ break;
+ case FLOW_CLS_DESTROY:
+- return mtk_flow_offload_destroy(eth, cls);
++ err = mtk_flow_offload_destroy(eth, cls);
++ break;
+ case FLOW_CLS_STATS:
+- return mtk_flow_offload_stats(eth, cls);
++ err = mtk_flow_offload_stats(eth, cls);
++ break;
+ default:
+- return -EOPNOTSUPP;
++ err = -EOPNOTSUPP;
++ break;
+ }
++ mutex_unlock(&mtk_flow_offload_mutex);
+
+- return 0;
++ return err;
+ }
+
+ static int
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sun, 18 Apr 2021 23:11:45 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: handle VLAN pop action
+
+Do not hit EOPNOTSUPP when flowtable offload provides a VLAN pop action.
+
+Fixes: efce49dfe6a8 ("netfilter: flowtable: add vlan pop action offload support")
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -233,6 +233,8 @@ mtk_flow_offload_replace(struct mtk_eth
+ data.vlan.proto = act->vlan.proto;
+ data.vlan.num++;
+ break;
++ case FLOW_ACTION_VLAN_POP:
++ break;
+ case FLOW_ACTION_PPPOE_PUSH:
+ if (data.pppoe.num == 1)
+ return -EOPNOTSUPP;
--- /dev/null
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sun, 28 Mar 2021 23:08:55 +0200
+Subject: [PATCH] netfilter: flowtable: dst_check() from garbage collector path
+
+Move dst_check() to the garbage collector path. Stale routes trigger the
+flow entry teardown state which makes affected flows go back to the
+classic forwarding path to re-evaluate flow offloading.
+
+IPv6 requires the dst cookie to work, store it in the flow_tuple,
+otherwise dst_check() always fails.
+
+Fixes: e5075c0badaa ("netfilter: flowtable: call dst_check() to fall back to classic forwarding")
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -129,7 +129,10 @@ struct flow_offload_tuple {
+ in_vlan_ingress:2;
+ u16 mtu;
+ union {
+- struct dst_entry *dst_cache;
++ struct {
++ struct dst_entry *dst_cache;
++ u32 dst_cookie;
++ };
+ struct {
+ u32 ifidx;
+ u32 hw_ifidx;
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -74,6 +74,18 @@ err_ct_refcnt:
+ }
+ EXPORT_SYMBOL_GPL(flow_offload_alloc);
+
++static u32 flow_offload_dst_cookie(struct flow_offload_tuple *flow_tuple)
++{
++ const struct rt6_info *rt;
++
++ if (flow_tuple->l3proto == NFPROTO_IPV6) {
++ rt = (const struct rt6_info *)flow_tuple->dst_cache;
++ return rt6_get_cookie(rt);
++ }
++
++ return 0;
++}
++
+ static int flow_offload_fill_route(struct flow_offload *flow,
+ const struct nf_flow_route *route,
+ enum flow_offload_tuple_dir dir)
+@@ -116,6 +128,7 @@ static int flow_offload_fill_route(struc
+ return -1;
+
+ flow_tuple->dst_cache = dst;
++ flow_tuple->dst_cookie = flow_offload_dst_cookie(flow_tuple);
+ break;
+ }
+ flow_tuple->xmit_type = route->tuple[dir].xmit_type;
+@@ -389,11 +402,33 @@ nf_flow_table_iterate(struct nf_flowtabl
+ return err;
+ }
+
++static bool flow_offload_stale_dst(struct flow_offload_tuple *tuple)
++{
++ struct dst_entry *dst;
++
++ if (tuple->xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
++ tuple->xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
++ dst = tuple->dst_cache;
++ if (!dst_check(dst, tuple->dst_cookie))
++ return true;
++ }
++
++ return false;
++}
++
++static bool nf_flow_has_stale_dst(struct flow_offload *flow)
++{
++ return flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple) ||
++ flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple);
++}
++
+ static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data)
+ {
+ struct nf_flowtable *flow_table = data;
+
+- if (nf_flow_has_expired(flow) || nf_ct_is_dying(flow->ct))
++ if (nf_flow_has_expired(flow) ||
++ nf_ct_is_dying(flow->ct) ||
++ nf_flow_has_stale_dst(flow))
+ set_bit(NF_FLOW_TEARDOWN, &flow->flags);
+
+ if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -364,15 +364,6 @@ nf_flow_offload_ip_hook(void *priv, stru
+ if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+ return NF_ACCEPT;
+
+- if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
+- tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
+- rt = (struct rtable *)tuplehash->tuple.dst_cache;
+- if (!dst_check(&rt->dst, 0)) {
+- flow_offload_teardown(flow);
+- return NF_ACCEPT;
+- }
+- }
+-
+ if (skb_try_make_writable(skb, thoff + hdrsize))
+ return NF_DROP;
+
+@@ -391,6 +382,7 @@ nf_flow_offload_ip_hook(void *priv, stru
+ nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
+
+ if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
++ rt = (struct rtable *)tuplehash->tuple.dst_cache;
+ memset(skb->cb, 0, sizeof(struct inet_skb_parm));
+ IPCB(skb)->iif = skb->dev->ifindex;
+ IPCB(skb)->flags = IPSKB_FORWARDED;
+@@ -399,6 +391,7 @@ nf_flow_offload_ip_hook(void *priv, stru
+
+ switch (tuplehash->tuple.xmit_type) {
+ case FLOW_OFFLOAD_XMIT_NEIGH:
++ rt = (struct rtable *)tuplehash->tuple.dst_cache;
+ outdev = rt->dst.dev;
+ skb->dev = outdev;
+ nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
+@@ -607,15 +600,6 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ if (nf_flow_state_check(flow, ip6h->nexthdr, skb, thoff))
+ return NF_ACCEPT;
+
+- if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
+- tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
+- rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
+- if (!dst_check(&rt->dst, 0)) {
+- flow_offload_teardown(flow);
+- return NF_ACCEPT;
+- }
+- }
+-
+ if (skb_try_make_writable(skb, thoff + hdrsize))
+ return NF_DROP;
+
+@@ -633,6 +617,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
+ nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
+
+ if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
++ rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
+ memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
+ IP6CB(skb)->iif = skb->dev->ifindex;
+ IP6CB(skb)->flags = IP6SKB_FORWARDED;
+@@ -641,6 +626,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
+
+ switch (tuplehash->tuple.xmit_type) {
+ case FLOW_OFFLOAD_XMIT_NEIGH:
++ rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
+ outdev = rt->dst.dev;
+ skb->dev = outdev;
+ nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
--- /dev/null
+From: Oz Shlomo <ozsh@nvidia.com>
+Date: Thu, 3 Jun 2021 15:12:33 +0300
+Subject: [PATCH] netfilter: conntrack: Introduce tcp offload timeout
+ configuration
+
+TCP connections may be offloaded from nf conntrack to nf flow table.
+Offloaded connections are aged after 30 seconds of inactivity.
+Once aged, ownership is returned to conntrack with a hard coded pickup
+time of 120 seconds, after which the connection may be deleted.
+eted. The current aging intervals may be too aggressive for some users.
+
+Provide users with the ability to control the nf flow table offload
+aging and pickup time intervals via sysctl parameter as a pre-step for
+configuring the nf flow table GC timeout intervals.
+
+Signed-off-by: Oz Shlomo <ozsh@nvidia.com>
+Reviewed-by: Paul Blakey <paulb@nvidia.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netns/conntrack.h
++++ b/include/net/netns/conntrack.h
+@@ -27,6 +27,10 @@ struct nf_tcp_net {
+ int tcp_loose;
+ int tcp_be_liberal;
+ int tcp_max_retrans;
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++ unsigned int offload_timeout;
++ unsigned int offload_pickup;
++#endif
+ };
+
+ enum udp_conntrack {
+--- a/net/netfilter/nf_conntrack_proto_tcp.c
++++ b/net/netfilter/nf_conntrack_proto_tcp.c
+@@ -1438,6 +1438,11 @@ void nf_conntrack_tcp_init_net(struct ne
+ tn->tcp_loose = nf_ct_tcp_loose;
+ tn->tcp_be_liberal = nf_ct_tcp_be_liberal;
+ tn->tcp_max_retrans = nf_ct_tcp_max_retrans;
++
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++ tn->offload_timeout = 30 * HZ;
++ tn->offload_pickup = 120 * HZ;
++#endif
+ }
+
+ const struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp =
+--- a/net/netfilter/nf_conntrack_standalone.c
++++ b/net/netfilter/nf_conntrack_standalone.c
+@@ -567,6 +567,10 @@ enum nf_ct_sysctl_index {
+ NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_CLOSE,
+ NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_RETRANS,
+ NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_UNACK,
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++ NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD,
++ NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD_PICKUP,
++#endif
+ NF_SYSCTL_CT_PROTO_TCP_LOOSE,
+ NF_SYSCTL_CT_PROTO_TCP_LIBERAL,
+ NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS,
+@@ -758,6 +762,20 @@ static struct ctl_table nf_ct_sysctl_tab
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ },
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++ [NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD] = {
++ .procname = "nf_flowtable_tcp_timeout",
++ .maxlen = sizeof(unsigned int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec_jiffies,
++ },
++ [NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD_PICKUP] = {
++ .procname = "nf_flowtable_tcp_pickup",
++ .maxlen = sizeof(unsigned int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec_jiffies,
++ },
++#endif
+ [NF_SYSCTL_CT_PROTO_TCP_LOOSE] = {
+ .procname = "nf_conntrack_tcp_loose",
+ .maxlen = sizeof(int),
+@@ -967,6 +985,12 @@ static void nf_conntrack_standalone_init
+ XASSIGN(LIBERAL, &tn->tcp_be_liberal);
+ XASSIGN(MAX_RETRANS, &tn->tcp_max_retrans);
+ #undef XASSIGN
++
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++ table[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD].data = &tn->offload_timeout;
++ table[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD_PICKUP].data = &tn->offload_pickup;
++#endif
++
+ }
+
+ static void nf_conntrack_standalone_init_sctp_sysctl(struct net *net,
--- /dev/null
+From: Oz Shlomo <ozsh@nvidia.com>
+Date: Thu, 3 Jun 2021 15:12:34 +0300
+Subject: [PATCH] netfilter: conntrack: Introduce udp offload timeout
+ configuration
+
+UDP connections may be offloaded from nf conntrack to nf flow table.
+Offloaded connections are aged after 30 seconds of inactivity.
+Once aged, ownership is returned to conntrack with a hard coded pickup
+time of 30 seconds, after which the connection may be deleted.
+eted. The current aging intervals may be too aggressive for some users.
+
+Provide users with the ability to control the nf flow table offload
+aging and pickup time intervals via sysctl parameter as a pre-step for
+configuring the nf flow table GC timeout intervals.
+
+Signed-off-by: Oz Shlomo <ozsh@nvidia.com>
+Reviewed-by: Paul Blakey <paulb@nvidia.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netns/conntrack.h
++++ b/include/net/netns/conntrack.h
+@@ -41,6 +41,10 @@ enum udp_conntrack {
+
+ struct nf_udp_net {
+ unsigned int timeouts[UDP_CT_MAX];
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++ unsigned int offload_timeout;
++ unsigned int offload_pickup;
++#endif
+ };
+
+ struct nf_icmp_net {
+--- a/net/netfilter/nf_conntrack_proto_udp.c
++++ b/net/netfilter/nf_conntrack_proto_udp.c
+@@ -273,6 +273,11 @@ void nf_conntrack_udp_init_net(struct ne
+
+ for (i = 0; i < UDP_CT_MAX; i++)
+ un->timeouts[i] = udp_timeouts[i];
++
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++ un->offload_timeout = 30 * HZ;
++ un->offload_pickup = 30 * HZ;
++#endif
+ }
+
+ const struct nf_conntrack_l4proto nf_conntrack_l4proto_udp =
+--- a/net/netfilter/nf_conntrack_standalone.c
++++ b/net/netfilter/nf_conntrack_standalone.c
+@@ -576,6 +576,10 @@ enum nf_ct_sysctl_index {
+ NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS,
+ NF_SYSCTL_CT_PROTO_TIMEOUT_UDP,
+ NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_STREAM,
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++ NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD,
++ NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD_PICKUP,
++#endif
+ NF_SYSCTL_CT_PROTO_TIMEOUT_ICMP,
+ NF_SYSCTL_CT_PROTO_TIMEOUT_ICMPV6,
+ #ifdef CONFIG_NF_CT_PROTO_SCTP
+@@ -810,6 +814,20 @@ static struct ctl_table nf_ct_sysctl_tab
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ },
++#if IS_ENABLED(CONFIG_NFT_FLOW_OFFLOAD)
++ [NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD] = {
++ .procname = "nf_flowtable_udp_timeout",
++ .maxlen = sizeof(unsigned int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec_jiffies,
++ },
++ [NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD_PICKUP] = {
++ .procname = "nf_flowtable_udp_pickup",
++ .maxlen = sizeof(unsigned int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec_jiffies,
++ },
++#endif
+ [NF_SYSCTL_CT_PROTO_TIMEOUT_ICMP] = {
+ .procname = "nf_conntrack_icmp_timeout",
+ .maxlen = sizeof(unsigned int),
+@@ -1078,6 +1096,10 @@ static int nf_conntrack_standalone_init_
+ table[NF_SYSCTL_CT_PROTO_TIMEOUT_ICMPV6].data = &nf_icmpv6_pernet(net)->timeout;
+ table[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP].data = &un->timeouts[UDP_CT_UNREPLIED];
+ table[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_STREAM].data = &un->timeouts[UDP_CT_REPLIED];
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++ table[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD].data = &un->offload_timeout;
++ table[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD_PICKUP].data = &un->offload_pickup;
++#endif
+
+ nf_conntrack_standalone_init_tcp_sysctl(net, table);
+ nf_conntrack_standalone_init_sctp_sysctl(net, table);
--- /dev/null
+From: Oz Shlomo <ozsh@nvidia.com>
+Date: Thu, 3 Jun 2021 15:12:35 +0300
+Subject: [PATCH] netfilter: flowtable: Set offload timeouts according to proto
+ values
+
+Currently the aging period for tcp/udp connections is hard coded to
+30 seconds. Aged tcp/udp connections configure a hard coded 120/30
+seconds pickup timeout for conntrack.
+This configuration may be too aggressive or permissive for some users.
+
+Dynamically configure the nf flow table GC timeout intervals according
+to the user defined values.
+
+Signed-off-by: Oz Shlomo <ozsh@nvidia.com>
+Reviewed-by: Paul Blakey <paulb@nvidia.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -174,6 +174,8 @@ struct flow_offload {
+ #define NF_FLOW_TIMEOUT (30 * HZ)
+ #define nf_flowtable_time_stamp (u32)jiffies
+
++unsigned long flow_offload_get_timeout(struct flow_offload *flow);
++
+ static inline __s32 nf_flow_timeout_delta(unsigned int timeout)
+ {
+ return (__s32)(timeout - nf_flowtable_time_stamp);
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -175,12 +175,10 @@ static void flow_offload_fixup_tcp(struc
+ tcp->seen[1].td_maxwin = 0;
+ }
+
+-#define NF_FLOWTABLE_TCP_PICKUP_TIMEOUT (120 * HZ)
+-#define NF_FLOWTABLE_UDP_PICKUP_TIMEOUT (30 * HZ)
+-
+ static void flow_offload_fixup_ct_timeout(struct nf_conn *ct)
+ {
+ const struct nf_conntrack_l4proto *l4proto;
++ struct net *net = nf_ct_net(ct);
+ int l4num = nf_ct_protonum(ct);
+ unsigned int timeout;
+
+@@ -188,12 +186,17 @@ static void flow_offload_fixup_ct_timeou
+ if (!l4proto)
+ return;
+
+- if (l4num == IPPROTO_TCP)
+- timeout = NF_FLOWTABLE_TCP_PICKUP_TIMEOUT;
+- else if (l4num == IPPROTO_UDP)
+- timeout = NF_FLOWTABLE_UDP_PICKUP_TIMEOUT;
+- else
++ if (l4num == IPPROTO_TCP) {
++ struct nf_tcp_net *tn = nf_tcp_pernet(net);
++
++ timeout = tn->offload_pickup;
++ } else if (l4num == IPPROTO_UDP) {
++ struct nf_udp_net *tn = nf_udp_pernet(net);
++
++ timeout = tn->offload_pickup;
++ } else {
+ return;
++ }
+
+ if (nf_flow_timeout_delta(READ_ONCE(ct->timeout)) > (__s32)timeout)
+ WRITE_ONCE(ct->timeout, nfct_time_stamp + timeout);
+@@ -265,11 +268,35 @@ static const struct rhashtable_params nf
+ .automatic_shrinking = true,
+ };
+
++unsigned long flow_offload_get_timeout(struct flow_offload *flow)
++{
++ const struct nf_conntrack_l4proto *l4proto;
++ unsigned long timeout = NF_FLOW_TIMEOUT;
++ struct net *net = nf_ct_net(flow->ct);
++ int l4num = nf_ct_protonum(flow->ct);
++
++ l4proto = nf_ct_l4proto_find(l4num);
++ if (!l4proto)
++ return timeout;
++
++ if (l4num == IPPROTO_TCP) {
++ struct nf_tcp_net *tn = nf_tcp_pernet(net);
++
++ timeout = tn->offload_timeout;
++ } else if (l4num == IPPROTO_UDP) {
++ struct nf_udp_net *tn = nf_udp_pernet(net);
++
++ timeout = tn->offload_timeout;
++ }
++
++ return timeout;
++}
++
+ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
+ {
+ int err;
+
+- flow->timeout = nf_flowtable_time_stamp + NF_FLOW_TIMEOUT;
++ flow->timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow);
+
+ err = rhashtable_insert_fast(&flow_table->rhashtable,
+ &flow->tuplehash[0].node,
+@@ -301,7 +328,7 @@ EXPORT_SYMBOL_GPL(flow_offload_add);
+ void flow_offload_refresh(struct nf_flowtable *flow_table,
+ struct flow_offload *flow)
+ {
+- flow->timeout = nf_flowtable_time_stamp + NF_FLOW_TIMEOUT;
++ flow->timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow);
+
+ if (likely(!nf_flowtable_hw_offload(flow_table)))
+ return;
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -885,7 +885,7 @@ static void flow_offload_work_stats(stru
+
+ lastused = max_t(u64, stats[0].lastused, stats[1].lastused);
+ offload->flow->timeout = max_t(u64, offload->flow->timeout,
+- lastused + NF_FLOW_TIMEOUT);
++ lastused + flow_offload_get_timeout(offload->flow));
+
+ if (offload->flowtable->flags & NF_FLOWTABLE_COUNTER) {
+ if (stats[0].pkts)
+@@ -989,7 +989,7 @@ void nf_flow_offload_stats(struct nf_flo
+ __s32 delta;
+
+ delta = nf_flow_timeout_delta(flow->timeout);
+- if ((delta >= (9 * NF_FLOW_TIMEOUT) / 10))
++ if ((delta >= (9 * flow_offload_get_timeout(flow)) / 10))
+ return;
+
+ offload = nf_flow_offload_work_alloc(flowtable, flow, FLOW_CLS_STATS);
--- /dev/null
+From 4fd59792097a6b2fb949d41264386a7ecade469e Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Mon, 25 Jan 2021 12:20:46 +0800
+Subject: [PATCH] net: ethernet: mediatek: support setting MTU
+
+MT762x HW, except for MT7628, supports frame length up to 2048
+(maximum length on GDM), so allow setting MTU up to 2030.
+
+Also set the default frame length to the hardware default 1518.
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20210125042046.5599-1-dqfext@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 43 ++++++++++++++++++---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 12 ++++--
+ 2 files changed, 47 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -355,7 +355,7 @@ static void mtk_mac_config(struct phylin
+ /* Setup gmac */
+ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+ mcr_new = mcr_cur;
+- mcr_new |= MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
++ mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
+ MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK;
+
+ /* Only update control register when needed! */
+@@ -782,8 +782,8 @@ static void mtk_get_stats64(struct net_d
+ static inline int mtk_max_frag_size(int mtu)
+ {
+ /* make sure buf_size will be at least MTK_MAX_RX_LENGTH */
+- if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH)
+- mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
++ if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH_2K)
++ mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN;
+
+ return SKB_DATA_ALIGN(MTK_RX_HLEN + mtu) +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+@@ -794,7 +794,7 @@ static inline int mtk_max_buf_size(int f
+ int buf_size = frag_size - NET_SKB_PAD - NET_IP_ALIGN -
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+- WARN_ON(buf_size < MTK_MAX_RX_LENGTH);
++ WARN_ON(buf_size < MTK_MAX_RX_LENGTH_2K);
+
+ return buf_size;
+ }
+@@ -2606,6 +2606,35 @@ static void mtk_uninit(struct net_device
+ mtk_rx_irq_disable(eth, ~0);
+ }
+
++static int mtk_change_mtu(struct net_device *dev, int new_mtu)
++{
++ int length = new_mtu + MTK_RX_ETH_HLEN;
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_eth *eth = mac->hw;
++ u32 mcr_cur, mcr_new;
++
++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
++ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
++ mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK;
++
++ if (length <= 1518)
++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518);
++ else if (length <= 1536)
++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536);
++ else if (length <= 1552)
++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552);
++ else
++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048);
++
++ if (mcr_new != mcr_cur)
++ mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
++ }
++
++ dev->mtu = new_mtu;
++
++ return 0;
++}
++
+ static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
+@@ -2902,6 +2931,7 @@ static const struct net_device_ops mtk_n
+ .ndo_set_mac_address = mtk_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = mtk_do_ioctl,
++ .ndo_change_mtu = mtk_change_mtu,
+ .ndo_tx_timeout = mtk_tx_timeout,
+ .ndo_get_stats64 = mtk_get_stats64,
+ .ndo_fix_features = mtk_fix_features,
+@@ -3004,7 +3034,10 @@ static int mtk_add_mac(struct mtk_eth *e
+ eth->netdev[id]->irq = eth->irq[0];
+ eth->netdev[id]->dev.of_node = np;
+
+- eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
++ eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
++ else
++ eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN;
+
+ return 0;
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -20,12 +20,13 @@
+ #include "mtk_ppe.h"
+
+ #define MTK_QDMA_PAGE_SIZE 2048
+-#define MTK_MAX_RX_LENGTH 1536
++#define MTK_MAX_RX_LENGTH 1536
++#define MTK_MAX_RX_LENGTH_2K 2048
+ #define MTK_TX_DMA_BUF_LEN 0x3fff
+ #define MTK_DMA_SIZE 512
+ #define MTK_NAPI_WEIGHT 64
+ #define MTK_MAC_COUNT 2
+-#define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
++#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
+ #define MTK_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | \
+@@ -352,7 +353,12 @@
+
+ /* Mac control registers */
+ #define MTK_MAC_MCR(x) (0x10100 + (x * 0x100))
+-#define MAC_MCR_MAX_RX_1536 BIT(24)
++#define MAC_MCR_MAX_RX_MASK GENMASK(25, 24)
++#define MAC_MCR_MAX_RX(_x) (MAC_MCR_MAX_RX_MASK & ((_x) << 24))
++#define MAC_MCR_MAX_RX_1518 0x0
++#define MAC_MCR_MAX_RX_1536 0x1
++#define MAC_MCR_MAX_RX_1552 0x2
++#define MAC_MCR_MAX_RX_2048 0x3
+ #define MAC_MCR_IPG_CFG (BIT(18) | BIT(16))
+ #define MAC_MCR_FORCE_MODE BIT(15)
+ #define MAC_MCR_TX_EN BIT(14)
--- /dev/null
+From c329e5afb42ff0a88285eb4d8a391a18793e4777 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Thu, 15 Apr 2021 03:26:50 +0200
+Subject: [PATCH] net: phy: at803x: select correct page on config init
+
+The Atheros AR8031 and AR8033 expose different registers for SGMII/Fiber
+as well as the copper side of the PHY depending on the BT_BX_REG_SEL bit
+in the chip configure register.
+
+The driver assumes the copper side is selected on probe, but this might
+not be the case depending which page was last selected by the
+bootloader. Notably, Ubiquiti UniFi bootloaders show this behavior.
+
+Select the copper page when probing to circumvent this.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 50 +++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 49 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -139,6 +139,9 @@
+ #define ATH8035_PHY_ID 0x004dd072
+ #define AT8030_PHY_ID_MASK 0xffffffef
+
++#define AT803X_PAGE_FIBER 0
++#define AT803X_PAGE_COPPER 1
++
+ MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi");
+ MODULE_LICENSE("GPL");
+@@ -190,6 +193,35 @@ static int at803x_debug_reg_mask(struct
+ 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_REG_0, 0,
+@@ -508,6 +540,7 @@ static int at803x_probe(struct phy_devic
+ {
+ struct device *dev = &phydev->mdio.dev;
+ struct at803x_priv *priv;
++ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -515,7 +548,20 @@ static int at803x_probe(struct phy_devic
+
+ phydev->priv = priv;
+
+- return at803x_parse_dt(phydev);
++ ret = at803x_parse_dt(phydev);
++ if (ret)
++ return ret;
++
++ /* Some bootloaders leave the fiber page selected.
++ * Switch to the copper page, as otherwise we read
++ * the PHY capabilities from the fiber side.
++ */
++ if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) {
++ ret = phy_select_page(phydev, AT803X_PAGE_COPPER);
++ ret = phy_restore_page(phydev, AT803X_PAGE_COPPER, ret);
++ }
++
++ return ret;
+ }
+
+ static void at803x_remove(struct phy_device *phydev)
+@@ -1097,6 +1143,8 @@ static struct phy_driver at803x_driver[]
+ .get_wol = at803x_get_wol,
+ .suspend = at803x_suspend,
+ .resume = at803x_resume,
++ .read_page = at803x_read_page,
++ .write_page = at803x_write_page,
+ /* PHY_GBIT_FEATURES */
+ .read_status = at803x_read_status,
+ .aneg_done = at803x_aneg_done,
--- /dev/null
+From 8f7e876273e294b732b42af2e5e6bba91d798954 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Tue, 20 Apr 2021 12:29:29 +0200
+Subject: [PATCH] net: phy: at803x: fix probe error if copper page is selected
+
+The commit c329e5afb42f ("net: phy: at803x: select correct page on
+config init") selects the copper page during probe. This fails if the
+copper page was already selected. In this case, the value of the copper
+page (which is 1) is propagated through phy_restore_page() and is
+finally returned for at803x_probe(). Fix it, by just using the
+at803x_page_write() directly.
+
+Also in case of an error, the regulator is not disabled and leads to a
+WARN_ON() when the probe fails. This couldn't happen before, because
+at803x_parse_dt() was the last call in at803x_probe(). It is hard to
+see, that the parse_dt() actually enables the regulator. Thus move the
+regulator_enable() to the probe function and undo it in case of an
+error.
+
+Fixes: c329e5afb42f ("net: phy: at803x: select correct page on config init")
+Signed-off-by: Michael Walle <michael@walle.cc>
+Reviewed-by: David Bauer <mail@david-bauer.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 23 +++++++++++++++++------
+ 1 file changed, 17 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -527,10 +527,6 @@ static int at803x_parse_dt(struct phy_de
+ phydev_err(phydev, "failed to get VDDIO regulator\n");
+ return PTR_ERR(priv->vddio);
+ }
+-
+- ret = regulator_enable(priv->vddio);
+- if (ret < 0)
+- return ret;
+ }
+
+ return 0;
+@@ -552,15 +548,30 @@ static int at803x_probe(struct phy_devic
+ if (ret)
+ return ret;
+
++ if (priv->vddio) {
++ ret = regulator_enable(priv->vddio);
++ if (ret < 0)
++ return ret;
++ }
++
+ /* Some bootloaders leave the fiber page selected.
+ * Switch to the copper page, as otherwise we read
+ * the PHY capabilities from the fiber side.
+ */
+ if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) {
+- ret = phy_select_page(phydev, AT803X_PAGE_COPPER);
+- ret = phy_restore_page(phydev, AT803X_PAGE_COPPER, ret);
++ phy_lock_mdio_bus(phydev);
++ ret = at803x_write_page(phydev, AT803X_PAGE_COPPER);
++ phy_unlock_mdio_bus(phydev);
++ if (ret)
++ goto err;
+ }
+
++ return 0;
++
++err:
++ if (priv->vddio)
++ regulator_disable(priv->vddio);
++
+ return ret;
+ }
+
--- /dev/null
+From b1ae3587d16a8c8fc9453e147c8708d6f006ffbb Mon Sep 17 00:00:00 2001
+From: Bjarni Jonasson <bjarni.jonasson@microchip.com>
+Date: Wed, 13 Jan 2021 12:56:25 +0100
+Subject: [PATCH] net: phy: Add 100 base-x mode
+
+Sparx-5 supports this mode and it is missing in the PHY core.
+
+Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
+Reviewed-by: Russell King <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ Documentation/networking/phy.rst | 5 +++++
+ include/linux/phy.h | 4 ++++
+ 2 files changed, 9 insertions(+)
+
+--- a/Documentation/networking/phy.rst
++++ b/Documentation/networking/phy.rst
+@@ -286,6 +286,11 @@ Some of the interface modes are describe
+ Note: due to legacy usage, some 10GBASE-R usage incorrectly makes
+ use of this definition.
+
++``PHY_INTERFACE_MODE_100BASEX``
++ This defines IEEE 802.3 Clause 24. The link operates at a fixed data
++ rate of 125Mpbs using a 4B/5B encoding scheme, resulting in an underlying
++ data rate of 100Mpbs.
++
+ Pause frames / flow control
+ ===========================
+
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -104,6 +104,7 @@ extern const int phy_10gbit_features_arr
+ * @PHY_INTERFACE_MODE_MOCA: Multimedia over Coax
+ * @PHY_INTERFACE_MODE_QSGMII: Quad SGMII
+ * @PHY_INTERFACE_MODE_TRGMII: Turbo RGMII
++ * @PHY_INTERFACE_MODE_100BASEX: 100 BaseX
+ * @PHY_INTERFACE_MODE_1000BASEX: 1000 BaseX
+ * @PHY_INTERFACE_MODE_2500BASEX: 2500 BaseX
+ * @PHY_INTERFACE_MODE_RXAUI: Reduced XAUI
+@@ -135,6 +136,7 @@ typedef enum {
+ PHY_INTERFACE_MODE_MOCA,
+ PHY_INTERFACE_MODE_QSGMII,
+ PHY_INTERFACE_MODE_TRGMII,
++ PHY_INTERFACE_MODE_100BASEX,
+ PHY_INTERFACE_MODE_1000BASEX,
+ PHY_INTERFACE_MODE_2500BASEX,
+ PHY_INTERFACE_MODE_RXAUI,
+@@ -217,6 +219,8 @@ static inline const char *phy_modes(phy_
+ return "usxgmii";
+ case PHY_INTERFACE_MODE_10GKR:
+ return "10gbase-kr";
++ case PHY_INTERFACE_MODE_100BASEX:
++ return "100base-x";
+ default:
+ return "unknown";
+ }
--- /dev/null
+From 6e12f35cef6b8a458d7ecf507ae330e0bffaad8c Mon Sep 17 00:00:00 2001
+From: Bjarni Jonasson <bjarni.jonasson@microchip.com>
+Date: Wed, 13 Jan 2021 12:56:26 +0100
+Subject: [PATCH] sfp: add support for 100 base-x SFPs
+
+Add support for 100Base-FX, 100Base-LX, 100Base-PX and 100Base-BX10 modules
+This is needed for Sparx-5 switch.
+
+Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
+Reviewed-by: Russell King <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/sfp-bus.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/drivers/net/phy/sfp-bus.c
++++ b/drivers/net/phy/sfp-bus.c
+@@ -280,6 +280,12 @@ void sfp_parse_support(struct sfp_bus *b
+ br_min <= 1300 && br_max >= 1200)
+ phylink_set(modes, 1000baseX_Full);
+
++ /* 100Base-FX, 100Base-LX, 100Base-PX, 100Base-BX10 */
++ if (id->base.e100_base_fx || id->base.e100_base_lx)
++ phylink_set(modes, 100baseFX_Full);
++ if ((id->base.e_base_px || id->base.e_base_bx10) && br_nom == 100)
++ phylink_set(modes, 100baseFX_Full);
++
+ /* For active or passive cables, select the link modes
+ * based on the bit rates and the cable compliance bytes.
+ */
+@@ -399,6 +405,9 @@ phy_interface_t sfp_select_interface(str
+ if (phylink_test(link_modes, 1000baseX_Full))
+ return PHY_INTERFACE_MODE_1000BASEX;
+
++ if (phylink_test(link_modes, 100baseFX_Full))
++ return PHY_INTERFACE_MODE_100BASEX;
++
+ dev_warn(bus->sfp_dev, "Unable to ascertain link mode\n");
+
+ return PHY_INTERFACE_MODE_NA;
--- /dev/null
+From 41d26bf4aba070dfd2ab48866cc27a48ee6228c7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Tue, 20 Apr 2021 09:53:59 +0200
+Subject: [PATCH] net: phy: marvell: refactor HWMON OOP style
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Use a structure of Marvell PHY specific HWMON methods to reduce code
+duplication. Store a pointer to this structure into the PHY driver's
+driver_data member.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/marvell.c | 369 +++++++++++++-------------------------
+ 1 file changed, 125 insertions(+), 244 deletions(-)
+
+--- a/drivers/net/phy/marvell.c
++++ b/drivers/net/phy/marvell.c
+@@ -2141,6 +2141,19 @@ static int marvell_vct7_cable_test_get_s
+ }
+
+ #ifdef CONFIG_HWMON
++struct marvell_hwmon_ops {
++ int (*get_temp)(struct phy_device *phydev, long *temp);
++ int (*get_temp_critical)(struct phy_device *phydev, long *temp);
++ int (*set_temp_critical)(struct phy_device *phydev, long temp);
++ int (*get_temp_alarm)(struct phy_device *phydev, long *alarm);
++};
++
++static const struct marvell_hwmon_ops *
++to_marvell_hwmon_ops(const struct phy_device *phydev)
++{
++ return phydev->drv->driver_data;
++}
++
+ static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
+ {
+ int oldpage;
+@@ -2184,75 +2197,6 @@ error:
+ return phy_restore_page(phydev, oldpage, ret);
+ }
+
+-static int m88e1121_hwmon_read(struct device *dev,
+- enum hwmon_sensor_types type,
+- u32 attr, int channel, long *temp)
+-{
+- struct phy_device *phydev = dev_get_drvdata(dev);
+- int err;
+-
+- switch (attr) {
+- case hwmon_temp_input:
+- err = m88e1121_get_temp(phydev, temp);
+- break;
+- default:
+- return -EOPNOTSUPP;
+- }
+-
+- return err;
+-}
+-
+-static umode_t m88e1121_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:
+- return 0444;
+- default:
+- return 0;
+- }
+-}
+-
+-static u32 m88e1121_hwmon_chip_config[] = {
+- HWMON_C_REGISTER_TZ,
+- 0
+-};
+-
+-static const struct hwmon_channel_info m88e1121_hwmon_chip = {
+- .type = hwmon_chip,
+- .config = m88e1121_hwmon_chip_config,
+-};
+-
+-static u32 m88e1121_hwmon_temp_config[] = {
+- HWMON_T_INPUT,
+- 0
+-};
+-
+-static const struct hwmon_channel_info m88e1121_hwmon_temp = {
+- .type = hwmon_temp,
+- .config = m88e1121_hwmon_temp_config,
+-};
+-
+-static const struct hwmon_channel_info *m88e1121_hwmon_info[] = {
+- &m88e1121_hwmon_chip,
+- &m88e1121_hwmon_temp,
+- NULL
+-};
+-
+-static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = {
+- .is_visible = m88e1121_hwmon_is_visible,
+- .read = m88e1121_hwmon_read,
+-};
+-
+-static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
+- .ops = &m88e1121_hwmon_hwmon_ops,
+- .info = m88e1121_hwmon_info,
+-};
+-
+ static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
+ {
+ int ret;
+@@ -2315,92 +2259,6 @@ static int m88e1510_get_temp_alarm(struc
+ return 0;
+ }
+
+-static int m88e1510_hwmon_read(struct device *dev,
+- enum hwmon_sensor_types type,
+- u32 attr, int channel, long *temp)
+-{
+- struct phy_device *phydev = dev_get_drvdata(dev);
+- int err;
+-
+- switch (attr) {
+- case hwmon_temp_input:
+- err = m88e1510_get_temp(phydev, temp);
+- break;
+- case hwmon_temp_crit:
+- err = m88e1510_get_temp_critical(phydev, temp);
+- break;
+- case hwmon_temp_max_alarm:
+- err = m88e1510_get_temp_alarm(phydev, temp);
+- break;
+- default:
+- return -EOPNOTSUPP;
+- }
+-
+- return err;
+-}
+-
+-static int m88e1510_hwmon_write(struct device *dev,
+- enum hwmon_sensor_types type,
+- u32 attr, int channel, long temp)
+-{
+- struct phy_device *phydev = dev_get_drvdata(dev);
+- int err;
+-
+- switch (attr) {
+- case hwmon_temp_crit:
+- err = m88e1510_set_temp_critical(phydev, temp);
+- break;
+- default:
+- return -EOPNOTSUPP;
+- }
+- return err;
+-}
+-
+-static umode_t m88e1510_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_max_alarm:
+- return 0444;
+- case hwmon_temp_crit:
+- return 0644;
+- default:
+- return 0;
+- }
+-}
+-
+-static u32 m88e1510_hwmon_temp_config[] = {
+- HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
+- 0
+-};
+-
+-static const struct hwmon_channel_info m88e1510_hwmon_temp = {
+- .type = hwmon_temp,
+- .config = m88e1510_hwmon_temp_config,
+-};
+-
+-static const struct hwmon_channel_info *m88e1510_hwmon_info[] = {
+- &m88e1121_hwmon_chip,
+- &m88e1510_hwmon_temp,
+- NULL
+-};
+-
+-static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = {
+- .is_visible = m88e1510_hwmon_is_visible,
+- .read = m88e1510_hwmon_read,
+- .write = m88e1510_hwmon_write,
+-};
+-
+-static const struct hwmon_chip_info m88e1510_hwmon_chip_info = {
+- .ops = &m88e1510_hwmon_hwmon_ops,
+- .info = m88e1510_hwmon_info,
+-};
+-
+ static int m88e6390_get_temp(struct phy_device *phydev, long *temp)
+ {
+ int sum = 0;
+@@ -2459,63 +2317,112 @@ error:
+ return ret;
+ }
+
+-static int m88e6390_hwmon_read(struct device *dev,
+- enum hwmon_sensor_types type,
+- u32 attr, int channel, long *temp)
++static int marvell_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
++ u32 attr, int channel, long *temp)
+ {
+ struct phy_device *phydev = dev_get_drvdata(dev);
+- int err;
++ const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev);
++ int err = -EOPNOTSUPP;
+
+ switch (attr) {
+ case hwmon_temp_input:
+- err = m88e6390_get_temp(phydev, temp);
++ if (ops->get_temp)
++ err = ops->get_temp(phydev, temp);
++ break;
++ case hwmon_temp_crit:
++ if (ops->get_temp_critical)
++ err = ops->get_temp_critical(phydev, temp);
++ break;
++ case hwmon_temp_max_alarm:
++ if (ops->get_temp_alarm)
++ err = ops->get_temp_alarm(phydev, temp);
++ break;
++ }
++
++ return err;
++}
++
++static int marvell_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
++ u32 attr, int channel, long temp)
++{
++ struct phy_device *phydev = dev_get_drvdata(dev);
++ const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev);
++ int err = -EOPNOTSUPP;
++
++ switch (attr) {
++ case hwmon_temp_crit:
++ if (ops->set_temp_critical)
++ err = ops->set_temp_critical(phydev, temp);
+ break;
+ default:
+- return -EOPNOTSUPP;
++ fallthrough;
+ }
+
+ return err;
+ }
+
+-static umode_t m88e6390_hwmon_is_visible(const void *data,
+- enum hwmon_sensor_types type,
+- u32 attr, int channel)
++static umode_t marvell_hwmon_is_visible(const void *data,
++ enum hwmon_sensor_types type,
++ u32 attr, int channel)
+ {
++ const struct phy_device *phydev = data;
++ const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev);
++
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+- return 0444;
++ return ops->get_temp ? 0444 : 0;
++ case hwmon_temp_max_alarm:
++ return ops->get_temp_alarm ? 0444 : 0;
++ case hwmon_temp_crit:
++ return (ops->get_temp_critical ? 0444 : 0) |
++ (ops->set_temp_critical ? 0200 : 0);
+ default:
+ return 0;
+ }
+ }
+
+-static u32 m88e6390_hwmon_temp_config[] = {
+- HWMON_T_INPUT,
++static u32 marvell_hwmon_chip_config[] = {
++ HWMON_C_REGISTER_TZ,
+ 0
+ };
+
+-static const struct hwmon_channel_info m88e6390_hwmon_temp = {
++static const struct hwmon_channel_info marvell_hwmon_chip = {
++ .type = hwmon_chip,
++ .config = marvell_hwmon_chip_config,
++};
++
++/* we can define HWMON_T_CRIT and HWMON_T_MAX_ALARM even though these are not
++ * defined for all PHYs, because the hwmon code checks whether the attributes
++ * exists via the .is_visible method
++ */
++static u32 marvell_hwmon_temp_config[] = {
++ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
++ 0
++};
++
++static const struct hwmon_channel_info marvell_hwmon_temp = {
+ .type = hwmon_temp,
+- .config = m88e6390_hwmon_temp_config,
++ .config = marvell_hwmon_temp_config,
+ };
+
+-static const struct hwmon_channel_info *m88e6390_hwmon_info[] = {
+- &m88e1121_hwmon_chip,
+- &m88e6390_hwmon_temp,
++static const struct hwmon_channel_info *marvell_hwmon_info[] = {
++ &marvell_hwmon_chip,
++ &marvell_hwmon_temp,
+ NULL
+ };
+
+-static const struct hwmon_ops m88e6390_hwmon_hwmon_ops = {
+- .is_visible = m88e6390_hwmon_is_visible,
+- .read = m88e6390_hwmon_read,
++static const struct hwmon_ops marvell_hwmon_hwmon_ops = {
++ .is_visible = marvell_hwmon_is_visible,
++ .read = marvell_hwmon_read,
++ .write = marvell_hwmon_write,
+ };
+
+-static const struct hwmon_chip_info m88e6390_hwmon_chip_info = {
+- .ops = &m88e6390_hwmon_hwmon_ops,
+- .info = m88e6390_hwmon_info,
++static const struct hwmon_chip_info marvell_hwmon_chip_info = {
++ .ops = &marvell_hwmon_hwmon_ops,
++ .info = marvell_hwmon_info,
+ };
+
+ static int marvell_hwmon_name(struct phy_device *phydev)
+@@ -2538,49 +2445,48 @@ static int marvell_hwmon_name(struct phy
+ return 0;
+ }
+
+-static int marvell_hwmon_probe(struct phy_device *phydev,
+- const struct hwmon_chip_info *chip)
++static int marvell_hwmon_probe(struct phy_device *phydev)
+ {
++ const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev);
+ struct marvell_priv *priv = phydev->priv;
+ struct device *dev = &phydev->mdio.dev;
+ int err;
+
++ if (!ops)
++ return 0;
++
+ err = marvell_hwmon_name(phydev);
+ if (err)
+ return err;
+
+ priv->hwmon_dev = devm_hwmon_device_register_with_info(
+- dev, priv->hwmon_name, phydev, chip, NULL);
++ dev, priv->hwmon_name, phydev, &marvell_hwmon_chip_info, NULL);
+
+ return PTR_ERR_OR_ZERO(priv->hwmon_dev);
+ }
+
+-static int m88e1121_hwmon_probe(struct phy_device *phydev)
+-{
+- return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info);
+-}
++static const struct marvell_hwmon_ops m88e1121_hwmon_ops = {
++ .get_temp = m88e1121_get_temp,
++};
+
+-static int m88e1510_hwmon_probe(struct phy_device *phydev)
+-{
+- return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info);
+-}
++static const struct marvell_hwmon_ops m88e1510_hwmon_ops = {
++ .get_temp = m88e1510_get_temp,
++ .get_temp_critical = m88e1510_get_temp_critical,
++ .set_temp_critical = m88e1510_set_temp_critical,
++ .get_temp_alarm = m88e1510_get_temp_alarm,
++};
++
++static const struct marvell_hwmon_ops m88e6390_hwmon_ops = {
++ .get_temp = m88e6390_get_temp,
++};
++
++#define DEF_MARVELL_HWMON_OPS(s) (&(s))
+
+-static int m88e6390_hwmon_probe(struct phy_device *phydev)
+-{
+- return marvell_hwmon_probe(phydev, &m88e6390_hwmon_chip_info);
+-}
+ #else
+-static int m88e1121_hwmon_probe(struct phy_device *phydev)
+-{
+- return 0;
+-}
+
+-static int m88e1510_hwmon_probe(struct phy_device *phydev)
+-{
+- return 0;
+-}
++#define DEF_MARVELL_HWMON_OPS(s) NULL
+
+-static int m88e6390_hwmon_probe(struct phy_device *phydev)
++static int marvell_hwmon_probe(struct phy_device *phydev)
+ {
+ return 0;
+ }
+@@ -2596,40 +2502,7 @@ static int marvell_probe(struct phy_devi
+
+ phydev->priv = priv;
+
+- return 0;
+-}
+-
+-static int m88e1121_probe(struct phy_device *phydev)
+-{
+- int err;
+-
+- err = marvell_probe(phydev);
+- if (err)
+- return err;
+-
+- return m88e1121_hwmon_probe(phydev);
+-}
+-
+-static int m88e1510_probe(struct phy_device *phydev)
+-{
+- int err;
+-
+- err = marvell_probe(phydev);
+- if (err)
+- return err;
+-
+- return m88e1510_hwmon_probe(phydev);
+-}
+-
+-static int m88e6390_probe(struct phy_device *phydev)
+-{
+- int err;
+-
+- err = marvell_probe(phydev);
+- if (err)
+- return err;
+-
+- return m88e6390_hwmon_probe(phydev);
++ return marvell_hwmon_probe(phydev);
+ }
+
+ static struct phy_driver marvell_drivers[] = {
+@@ -2714,8 +2587,9 @@ static struct phy_driver marvell_drivers
+ .phy_id = MARVELL_PHY_ID_88E1121R,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .name = "Marvell 88E1121R",
++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1121_hwmon_ops),
+ /* PHY_GBIT_FEATURES */
+- .probe = m88e1121_probe,
++ .probe = marvell_probe,
+ .config_init = marvell_config_init,
+ .config_aneg = m88e1121_config_aneg,
+ .read_status = marvell_read_status,
+@@ -2834,9 +2708,10 @@ static struct phy_driver marvell_drivers
+ .phy_id = MARVELL_PHY_ID_88E1510,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .name = "Marvell 88E1510",
++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
+ .features = PHY_GBIT_FIBRE_FEATURES,
+ .flags = PHY_POLL_CABLE_TEST,
+- .probe = m88e1510_probe,
++ .probe = marvell_probe,
+ .config_init = m88e1510_config_init,
+ .config_aneg = m88e1510_config_aneg,
+ .read_status = marvell_read_status,
+@@ -2863,9 +2738,10 @@ static struct phy_driver marvell_drivers
+ .phy_id = MARVELL_PHY_ID_88E1540,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .name = "Marvell 88E1540",
++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
+ /* PHY_GBIT_FEATURES */
+ .flags = PHY_POLL_CABLE_TEST,
+- .probe = m88e1510_probe,
++ .probe = marvell_probe,
+ .config_init = marvell_config_init,
+ .config_aneg = m88e1510_config_aneg,
+ .read_status = marvell_read_status,
+@@ -2889,7 +2765,8 @@ static struct phy_driver marvell_drivers
+ .phy_id = MARVELL_PHY_ID_88E1545,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .name = "Marvell 88E1545",
+- .probe = m88e1510_probe,
++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
++ .probe = marvell_probe,
+ /* PHY_GBIT_FEATURES */
+ .flags = PHY_POLL_CABLE_TEST,
+ .config_init = marvell_config_init,
+@@ -2935,9 +2812,10 @@ static struct phy_driver marvell_drivers
+ .phy_id = MARVELL_PHY_ID_88E6341_FAMILY,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .name = "Marvell 88E6341 Family",
++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
+ /* PHY_GBIT_FEATURES */
+ .flags = PHY_POLL_CABLE_TEST,
+- .probe = m88e1510_probe,
++ .probe = marvell_probe,
+ .config_init = marvell_config_init,
+ .config_aneg = m88e6390_config_aneg,
+ .read_status = marvell_read_status,
+@@ -2961,9 +2839,10 @@ static struct phy_driver marvell_drivers
+ .phy_id = MARVELL_PHY_ID_88E6390_FAMILY,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .name = "Marvell 88E6390 Family",
++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e6390_hwmon_ops),
+ /* PHY_GBIT_FEATURES */
+ .flags = PHY_POLL_CABLE_TEST,
+- .probe = m88e6390_probe,
++ .probe = marvell_probe,
+ .config_init = marvell_config_init,
+ .config_aneg = m88e6390_config_aneg,
+ .read_status = marvell_read_status,
+@@ -2987,7 +2866,8 @@ static struct phy_driver marvell_drivers
+ .phy_id = MARVELL_PHY_ID_88E1340S,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .name = "Marvell 88E1340S",
+- .probe = m88e1510_probe,
++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
++ .probe = marvell_probe,
+ /* PHY_GBIT_FEATURES */
+ .config_init = marvell_config_init,
+ .config_aneg = m88e1510_config_aneg,
+@@ -3009,7 +2889,8 @@ static struct phy_driver marvell_drivers
+ .phy_id = MARVELL_PHY_ID_88E1548P,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .name = "Marvell 88E1548P",
+- .probe = m88e1510_probe,
++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
++ .probe = marvell_probe,
+ .features = PHY_GBIT_FIBRE_FEATURES,
+ .config_init = marvell_config_init,
+ .config_aneg = m88e1510_config_aneg,
--- /dev/null
+From b697d9d38a5a5ab405d7cc4743d39fe2c5d7517c Mon Sep 17 00:00:00 2001
+From: Ivan Bornyakov <i.bornyakov@metrotek.ru>
+Date: Thu, 12 Aug 2021 16:42:56 +0300
+Subject: [PATCH] net: phy: marvell: add SFP support for 88E1510
+
+Add support for SFP cages connected to the Marvell 88E1512 transceiver.
+88E1512 supports for SGMII/1000Base-X/100Base-FX media type with RGMII
+on system interface. Configure PHY to appropriate mode depending on the
+type of SFP inserted. On SFP removal configure PHY to the RGMII-copper
+mode so RJ-45 port can still work.
+
+Signed-off-by: Ivan Bornyakov <i.bornyakov@metrotek.ru>
+Link: https://lore.kernel.org/r/20210812134256.2436-1-i.bornyakov@metrotek.ru
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/marvell.c | 105 +++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 104 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/marvell.c
++++ b/drivers/net/phy/marvell.c
+@@ -32,6 +32,7 @@
+ #include <linux/marvell_phy.h>
+ #include <linux/bitfield.h>
+ #include <linux/of.h>
++#include <linux/sfp.h>
+
+ #include <linux/io.h>
+ #include <asm/irq.h>
+@@ -46,6 +47,7 @@
+ #define MII_MARVELL_MISC_TEST_PAGE 0x06
+ #define MII_MARVELL_VCT7_PAGE 0x07
+ #define MII_MARVELL_WOL_PAGE 0x11
++#define MII_MARVELL_MODE_PAGE 0x12
+
+ #define MII_M1011_IEVENT 0x13
+ #define MII_M1011_IEVENT_CLEAR 0x0000
+@@ -162,7 +164,14 @@
+
+ #define MII_88E1510_GEN_CTRL_REG_1 0x14
+ #define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK 0x7
++#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII 0x0 /* RGMII to copper */
+ #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */
++/* RGMII to 1000BASE-X */
++#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_1000X 0x2
++/* RGMII to 100BASE-FX */
++#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_100FX 0x3
++/* RGMII to SGMII */
++#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_SGMII 0x4
+ #define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */
+
+ #define MII_VCT5_TX_RX_MDI0_COUPLING 0x10
+@@ -2505,6 +2514,100 @@ static int marvell_probe(struct phy_devi
+ return marvell_hwmon_probe(phydev);
+ }
+
++static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
++{
++ struct phy_device *phydev = upstream;
++ phy_interface_t interface;
++ struct device *dev;
++ int oldpage;
++ int ret = 0;
++ u16 mode;
++
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
++
++ dev = &phydev->mdio.dev;
++
++ sfp_parse_support(phydev->sfp_bus, id, supported);
++ interface = sfp_select_interface(phydev->sfp_bus, supported);
++
++ dev_info(dev, "%s SFP module inserted\n", phy_modes(interface));
++
++ switch (interface) {
++ case PHY_INTERFACE_MODE_1000BASEX:
++ mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_1000X;
++
++ break;
++ case PHY_INTERFACE_MODE_100BASEX:
++ mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_100FX;
++
++ break;
++ case PHY_INTERFACE_MODE_SGMII:
++ mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_SGMII;
++
++ break;
++ default:
++ dev_err(dev, "Incompatible SFP module inserted\n");
++
++ return -EINVAL;
++ }
++
++ oldpage = phy_select_page(phydev, MII_MARVELL_MODE_PAGE);
++ if (oldpage < 0)
++ goto error;
++
++ ret = __phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1,
++ MII_88E1510_GEN_CTRL_REG_1_MODE_MASK, mode);
++ if (ret < 0)
++ goto error;
++
++ ret = __phy_set_bits(phydev, MII_88E1510_GEN_CTRL_REG_1,
++ MII_88E1510_GEN_CTRL_REG_1_RESET);
++
++error:
++ return phy_restore_page(phydev, oldpage, ret);
++}
++
++static void m88e1510_sfp_remove(void *upstream)
++{
++ struct phy_device *phydev = upstream;
++ int oldpage;
++ int ret = 0;
++
++ oldpage = phy_select_page(phydev, MII_MARVELL_MODE_PAGE);
++ if (oldpage < 0)
++ goto error;
++
++ ret = __phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1,
++ MII_88E1510_GEN_CTRL_REG_1_MODE_MASK,
++ MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII);
++ if (ret < 0)
++ goto error;
++
++ ret = __phy_set_bits(phydev, MII_88E1510_GEN_CTRL_REG_1,
++ MII_88E1510_GEN_CTRL_REG_1_RESET);
++
++error:
++ phy_restore_page(phydev, oldpage, ret);
++}
++
++static const struct sfp_upstream_ops m88e1510_sfp_ops = {
++ .module_insert = m88e1510_sfp_insert,
++ .module_remove = m88e1510_sfp_remove,
++ .attach = phy_sfp_attach,
++ .detach = phy_sfp_detach,
++};
++
++static int m88e1510_probe(struct phy_device *phydev)
++{
++ int err;
++
++ err = marvell_probe(phydev);
++ if (err)
++ return err;
++
++ return phy_sfp_probe(phydev, &m88e1510_sfp_ops);
++}
++
+ static struct phy_driver marvell_drivers[] = {
+ {
+ .phy_id = MARVELL_PHY_ID_88E1101,
+@@ -2711,7 +2814,7 @@ static struct phy_driver marvell_drivers
+ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
+ .features = PHY_GBIT_FIBRE_FEATURES,
+ .flags = PHY_POLL_CABLE_TEST,
+- .probe = marvell_probe,
++ .probe = m88e1510_probe,
+ .config_init = m88e1510_config_init,
+ .config_aneg = m88e1510_config_aneg,
+ .read_status = marvell_read_status,
--- /dev/null
+From 9d5ef190e5615a7b63af89f88c4106a5bc127974 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Fri, 5 Feb 2021 15:37:10 +0200
+Subject: [PATCH] net: dsa: automatically bring up DSA master when opening user
+ port
+
+DSA wants the master interface to be open before the user port is due to
+historical reasons. The promiscuity of interfaces that are down used to
+have issues, as referenced Lennert Buytenhek in commit df02c6ff2e39
+("dsa: fix master interface allmulti/promisc handling").
+
+The bugfix mentioned there, commit b6c40d68ff64 ("net: only invoke
+dev->change_rx_flags when device is UP"), was basically a "don't do
+that" approach to working around the promiscuity while down issue.
+
+Further work done by Vlad Yasevich in commit d2615bf45069 ("net: core:
+Always propagate flag changes to interfaces") has resolved the
+underlying issue, and it is strictly up to the DSA and 8021q drivers
+now, it is no longer mandated by the networking core that the master
+interface must be up when changing its promiscuity.
+
+From DSA's point of view, deciding to error out in dsa_slave_open
+because the master isn't up is
+(a) a bad user experience and
+(b) knocking at an open door.
+Even if there still was an issue with promiscuity while down, DSA could
+still just open the master and avoid it.
+
+Doing it this way has the additional benefit that user space can now
+remove DSA-specific workarounds, like systemd-networkd with BindCarrier:
+https://github.com/systemd/systemd/issues/7478
+
+And we can finally remove one of the 2 bullets in the "Common pitfalls
+using DSA setups" chapter.
+
+Tested with two cascaded DSA switches:
+
+$ ip link set sw0p2 up
+fsl_enetc 0000:00:00.2 eno2: configuring for fixed/internal link mode
+fsl_enetc 0000:00:00.2 eno2: Link is Up - 1Gbps/Full - flow control rx/tx
+mscc_felix 0000:00:00.5 swp0: configuring for fixed/sgmii link mode
+mscc_felix 0000:00:00.5 swp0: Link is Up - 1Gbps/Full - flow control off
+8021q: adding VLAN 0 to HW filter on device swp0
+sja1105 spi2.0 sw0p2: configuring for phy/rgmii-id link mode
+IPv6: ADDRCONF(NETDEV_CHANGE): eno2: link becomes ready
+IPv6: ADDRCONF(NETDEV_CHANGE): swp0: link becomes ready
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ Documentation/networking/dsa/dsa.rst | 4 ----
+ net/dsa/slave.c | 7 +++++--
+ 2 files changed, 5 insertions(+), 6 deletions(-)
+
+--- a/Documentation/networking/dsa/dsa.rst
++++ b/Documentation/networking/dsa/dsa.rst
+@@ -273,10 +273,6 @@ will not make us go through the switch t
+ the Ethernet switch on the other end, expecting a tag will typically drop this
+ frame.
+
+-Slave network devices check that the master network device is UP before allowing
+-you to administratively bring UP these slave network devices. A common
+-configuration mistake is forgetting to bring UP the master network device first.
+-
+ Interactions with other subsystems
+ ==================================
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -68,8 +68,11 @@ static int dsa_slave_open(struct net_dev
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ int err;
+
+- if (!(master->flags & IFF_UP))
+- return -ENETDOWN;
++ err = dev_open(master, NULL);
++ if (err < 0) {
++ netdev_err(dev, "failed to open master %s\n", master->name);
++ goto out;
++ }
+
+ if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) {
+ err = dev_uc_add(master, dev->dev_addr);
--- /dev/null
+From 90dc8fd36078a536671adae884d0b929cce6480a Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Wed, 6 Jan 2021 11:51:30 +0200
+Subject: [PATCH] net: bridge: notify switchdev of disappearance of old FDB
+ entry upon migration
+
+Currently the bridge emits atomic switchdev notifications for
+dynamically learnt FDB entries. Monitoring these notifications works
+wonders for switchdev drivers that want to keep their hardware FDB in
+sync with the bridge's FDB.
+
+For example station A wants to talk to station B in the diagram below,
+and we are concerned with the behavior of the bridge on the DUT device:
+
+ DUT
+ +-------------------------------------+
+ | br0 |
+ | +------+ +------+ +------+ +------+ |
+ | | | | | | | | | |
+ | | swp0 | | swp1 | | swp2 | | eth0 | |
+ +-------------------------------------+
+ | | |
+ Station A | |
+ | |
+ +--+------+--+ +--+------+--+
+ | | | | | | | |
+ | | swp0 | | | | swp0 | |
+ Another | +------+ | | +------+ | Another
+ switch | br0 | | br0 | switch
+ | +------+ | | +------+ |
+ | | | | | | | |
+ | | swp1 | | | | swp1 | |
+ +--+------+--+ +--+------+--+
+ |
+ Station B
+
+Interfaces swp0, swp1, swp2 are handled by a switchdev driver that has
+the following property: frames injected from its control interface bypass
+the internal address analyzer logic, and therefore, this hardware does
+not learn from the source address of packets transmitted by the network
+stack through it. So, since bridging between eth0 (where Station B is
+attached) and swp0 (where Station A is attached) is done in software,
+the switchdev hardware will never learn the source address of Station B.
+So the traffic towards that destination will be treated as unknown, i.e.
+flooded.
+
+This is where the bridge notifications come in handy. When br0 on the
+DUT sees frames with Station B's MAC address on eth0, the switchdev
+driver gets these notifications and can install a rule to send frames
+towards Station B's address that are incoming from swp0, swp1, swp2,
+only towards the control interface. This is all switchdev driver private
+business, which the notification makes possible.
+
+All is fine until someone unplugs Station B's cable and moves it to the
+other switch:
+
+ DUT
+ +-------------------------------------+
+ | br0 |
+ | +------+ +------+ +------+ +------+ |
+ | | | | | | | | | |
+ | | swp0 | | swp1 | | swp2 | | eth0 | |
+ +-------------------------------------+
+ | | |
+ Station A | |
+ | |
+ +--+------+--+ +--+------+--+
+ | | | | | | | |
+ | | swp0 | | | | swp0 | |
+ Another | +------+ | | +------+ | Another
+ switch | br0 | | br0 | switch
+ | +------+ | | +------+ |
+ | | | | | | | |
+ | | swp1 | | | | swp1 | |
+ +--+------+--+ +--+------+--+
+ |
+ Station B
+
+Luckily for the use cases we care about, Station B is noisy enough that
+the DUT hears it (on swp1 this time). swp1 receives the frames and
+delivers them to the bridge, who enters the unlikely path in br_fdb_update
+of updating an existing entry. It moves the entry in the software bridge
+to swp1 and emits an addition notification towards that.
+
+As far as the switchdev driver is concerned, all that it needs to ensure
+is that traffic between Station A and Station B is not forever broken.
+If it does nothing, then the stale rule to send frames for Station B
+towards the control interface remains in place. But Station B is no
+longer reachable via the control interface, but via a port that can
+offload the bridge port learning attribute. It's just that the port is
+prevented from learning this address, since the rule overrides FDB
+updates. So the rule needs to go. The question is via what mechanism.
+
+It sure would be possible for this switchdev driver to keep track of all
+addresses which are sent to the control interface, and then also listen
+for bridge notifier events on its own ports, searching for the ones that
+have a MAC address which was previously sent to the control interface.
+But this is cumbersome and inefficient. Instead, with one small change,
+the bridge could notify of the address deletion from the old port, in a
+symmetrical manner with how it did for the insertion. Then the switchdev
+driver would not be required to monitor learn/forget events for its own
+ports. It could just delete the rule towards the control interface upon
+bridge entry migration. This would make hardware address learning be
+possible again. Then it would take a few more packets until the hardware
+and software FDB would be in sync again.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ net/bridge/br_fdb.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/net/bridge/br_fdb.c
++++ b/net/bridge/br_fdb.c
+@@ -602,6 +602,7 @@ void br_fdb_update(struct net_bridge *br
+ /* fastpath: update of existing entry */
+ if (unlikely(source != fdb->dst &&
+ !test_bit(BR_FDB_STICKY, &fdb->flags))) {
++ br_switchdev_fdb_notify(fdb, RTM_DELNEIGH);
+ fdb->dst = source;
+ fdb_modified = true;
+ /* Take over HW learned entry */
--- /dev/null
+From 2fd186501b1cff155cc4a755c210793cfc0dffb5 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Wed, 6 Jan 2021 11:51:31 +0200
+Subject: [PATCH] net: dsa: be louder when a non-legacy FDB operation fails
+
+The dev_close() call was added in commit c9eb3e0f8701 ("net: dsa: Add
+support for learning FDB through notification") "to indicate inconsistent
+situation" when we could not delete an FDB entry from the port.
+
+bridge fdb del d8:58:d7:00:ca:6d dev swp0 self master
+
+It is a bit drastic and at the same time not helpful if the above fails
+to only print with netdev_dbg log level, but on the other hand to bring
+the interface down.
+
+So increase the verbosity of the error message, and drop dev_close().
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ net/dsa/slave.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -2112,7 +2112,9 @@ static void dsa_slave_switchdev_event_wo
+
+ err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid);
+ if (err) {
+- netdev_dbg(dev, "fdb add failed err=%d\n", err);
++ netdev_err(dev,
++ "failed to add %pM vid %d to fdb: %d\n",
++ fdb_info->addr, fdb_info->vid, err);
+ break;
+ }
+ fdb_info->offloaded = true;
+@@ -2127,9 +2129,11 @@ static void dsa_slave_switchdev_event_wo
+
+ err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid);
+ if (err) {
+- netdev_dbg(dev, "fdb del failed err=%d\n", err);
+- dev_close(dev);
++ netdev_err(dev,
++ "failed to delete %pM vid %d from fdb: %d\n",
++ fdb_info->addr, fdb_info->vid, err);
+ }
++
+ break;
+ }
+ rtnl_unlock();
--- /dev/null
+From c4bb76a9a0ef87c4cc1f636defed5f12deb9f5a7 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Wed, 6 Jan 2021 11:51:32 +0200
+Subject: [PATCH] net: dsa: don't use switchdev_notifier_fdb_info in
+ dsa_switchdev_event_work
+
+Currently DSA doesn't add FDB entries on the CPU port, because it only
+does so through switchdev, which is associated with a net_device, and
+there are none of those for the CPU port.
+
+But actually FDB addresses on the CPU port have some use cases of their
+own, if the switchdev operations are initiated from within the DSA
+layer. There is just one problem with the existing code: it passes a
+structure in dsa_switchdev_event_work which was retrieved directly from
+switchdev, so it contains a net_device. We need to generalize the
+contents to something that covers the CPU port as well: the "ds, port"
+tuple is fine for that.
+
+Note that the new procedure for notifying the successful FDB offload is
+inspired from the rocker model.
+
+Also, nothing was being done if added_by_user was false. Let's check for
+that a lot earlier, and don't actually bother to schedule the worker
+for nothing.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ net/dsa/dsa_priv.h | 12 +++++
+ net/dsa/slave.c | 106 ++++++++++++++++++++++-----------------------
+ 2 files changed, 65 insertions(+), 53 deletions(-)
+
+--- a/net/dsa/dsa_priv.h
++++ b/net/dsa/dsa_priv.h
+@@ -73,6 +73,18 @@ struct dsa_notifier_mtu_info {
+ int mtu;
+ };
+
++struct dsa_switchdev_event_work {
++ struct dsa_switch *ds;
++ int port;
++ struct work_struct work;
++ unsigned long event;
++ /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and
++ * SWITCHDEV_FDB_DEL_TO_DEVICE
++ */
++ unsigned char addr[ETH_ALEN];
++ u16 vid;
++};
++
+ struct dsa_slave_priv {
+ /* Copy of CPU port xmit for faster access in slave transmit hot path */
+ struct sk_buff * (*xmit)(struct sk_buff *skb,
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -2087,76 +2087,66 @@ static int dsa_slave_netdevice_event(str
+ return NOTIFY_DONE;
+ }
+
+-struct dsa_switchdev_event_work {
+- struct work_struct work;
+- struct switchdev_notifier_fdb_info fdb_info;
+- struct net_device *dev;
+- unsigned long event;
+-};
++static void
++dsa_fdb_offload_notify(struct dsa_switchdev_event_work *switchdev_work)
++{
++ struct dsa_switch *ds = switchdev_work->ds;
++ struct switchdev_notifier_fdb_info info;
++ struct dsa_port *dp;
++
++ if (!dsa_is_user_port(ds, switchdev_work->port))
++ return;
++
++ info.addr = switchdev_work->addr;
++ info.vid = switchdev_work->vid;
++ info.offloaded = true;
++ dp = dsa_to_port(ds, switchdev_work->port);
++ call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
++ dp->slave, &info.info, NULL);
++}
+
+ static void dsa_slave_switchdev_event_work(struct work_struct *work)
+ {
+ struct dsa_switchdev_event_work *switchdev_work =
+ container_of(work, struct dsa_switchdev_event_work, work);
+- struct net_device *dev = switchdev_work->dev;
+- struct switchdev_notifier_fdb_info *fdb_info;
+- struct dsa_port *dp = dsa_slave_to_port(dev);
++ struct dsa_switch *ds = switchdev_work->ds;
++ struct dsa_port *dp;
+ int err;
+
++ dp = dsa_to_port(ds, switchdev_work->port);
++
+ rtnl_lock();
+ switch (switchdev_work->event) {
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+- fdb_info = &switchdev_work->fdb_info;
+- if (!fdb_info->added_by_user)
+- break;
+-
+- err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid);
++ err = dsa_port_fdb_add(dp, switchdev_work->addr,
++ switchdev_work->vid);
+ if (err) {
+- netdev_err(dev,
+- "failed to add %pM vid %d to fdb: %d\n",
+- fdb_info->addr, fdb_info->vid, err);
++ dev_err(ds->dev,
++ "port %d failed to add %pM vid %d to fdb: %d\n",
++ dp->index, switchdev_work->addr,
++ switchdev_work->vid, err);
+ break;
+ }
+- fdb_info->offloaded = true;
+- call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
+- &fdb_info->info, NULL);
++ dsa_fdb_offload_notify(switchdev_work);
+ break;
+
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+- fdb_info = &switchdev_work->fdb_info;
+- if (!fdb_info->added_by_user)
+- break;
+-
+- err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid);
++ err = dsa_port_fdb_del(dp, switchdev_work->addr,
++ switchdev_work->vid);
+ if (err) {
+- netdev_err(dev,
+- "failed to delete %pM vid %d from fdb: %d\n",
+- fdb_info->addr, fdb_info->vid, err);
++ dev_err(ds->dev,
++ "port %d failed to delete %pM vid %d from fdb: %d\n",
++ dp->index, switchdev_work->addr,
++ switchdev_work->vid, err);
+ }
+
+ break;
+ }
+ rtnl_unlock();
+
+- kfree(switchdev_work->fdb_info.addr);
+ kfree(switchdev_work);
+- dev_put(dev);
+-}
+-
+-static int
+-dsa_slave_switchdev_fdb_work_init(struct dsa_switchdev_event_work *
+- switchdev_work,
+- const struct switchdev_notifier_fdb_info *
+- fdb_info)
+-{
+- memcpy(&switchdev_work->fdb_info, fdb_info,
+- sizeof(switchdev_work->fdb_info));
+- switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
+- if (!switchdev_work->fdb_info.addr)
+- return -ENOMEM;
+- ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
+- fdb_info->addr);
+- return 0;
++ if (dsa_is_user_port(ds, dp->index))
++ dev_put(dp->slave);
+ }
+
+ /* Called under rcu_read_lock() */
+@@ -2164,7 +2154,9 @@ static int dsa_slave_switchdev_event(str
+ unsigned long event, void *ptr)
+ {
+ struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
++ const struct switchdev_notifier_fdb_info *fdb_info;
+ struct dsa_switchdev_event_work *switchdev_work;
++ struct dsa_port *dp;
+ int err;
+
+ if (event == SWITCHDEV_PORT_ATTR_SET) {
+@@ -2177,20 +2169,32 @@ static int dsa_slave_switchdev_event(str
+ if (!dsa_slave_dev_check(dev))
+ return NOTIFY_DONE;
+
++ dp = dsa_slave_to_port(dev);
++
+ switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+ if (!switchdev_work)
+ return NOTIFY_BAD;
+
+ INIT_WORK(&switchdev_work->work,
+ dsa_slave_switchdev_event_work);
+- switchdev_work->dev = dev;
++ switchdev_work->ds = dp->ds;
++ switchdev_work->port = dp->index;
+ switchdev_work->event = event;
+
+ switch (event) {
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+- if (dsa_slave_switchdev_fdb_work_init(switchdev_work, ptr))
+- goto err_fdb_work_init;
++ fdb_info = ptr;
++
++ if (!fdb_info->added_by_user) {
++ kfree(switchdev_work);
++ return NOTIFY_OK;
++ }
++
++ ether_addr_copy(switchdev_work->addr,
++ fdb_info->addr);
++ switchdev_work->vid = fdb_info->vid;
++
+ dev_hold(dev);
+ break;
+ default:
+@@ -2200,10 +2204,6 @@ static int dsa_slave_switchdev_event(str
+
+ dsa_schedule_work(&switchdev_work->work);
+ return NOTIFY_OK;
+-
+-err_fdb_work_init:
+- kfree(switchdev_work);
+- return NOTIFY_BAD;
+ }
+
+ static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused,
--- /dev/null
+From 447d290a58bd335d68f665713842365d3d6447df Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Wed, 6 Jan 2021 11:51:33 +0200
+Subject: [PATCH] net: dsa: move switchdev event implementation under the same
+ switch/case statement
+
+We'll need to start listening to SWITCHDEV_FDB_{ADD,DEL}_TO_DEVICE
+events even for interfaces where dsa_slave_dev_check returns false, so
+we need that check inside the switch-case statement for SWITCHDEV_FDB_*.
+
+This movement also avoids a useless allocation / free of switchdev_work
+on the untreated "default event" case.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ net/dsa/slave.c | 35 ++++++++++++++++-------------------
+ 1 file changed, 16 insertions(+), 19 deletions(-)
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -2159,31 +2159,29 @@ static int dsa_slave_switchdev_event(str
+ struct dsa_port *dp;
+ int err;
+
+- if (event == SWITCHDEV_PORT_ATTR_SET) {
++ switch (event) {
++ case SWITCHDEV_PORT_ATTR_SET:
+ err = switchdev_handle_port_attr_set(dev, ptr,
+ dsa_slave_dev_check,
+ dsa_slave_port_attr_set);
+ return notifier_from_errno(err);
+- }
+-
+- if (!dsa_slave_dev_check(dev))
+- return NOTIFY_DONE;
++ case SWITCHDEV_FDB_ADD_TO_DEVICE:
++ case SWITCHDEV_FDB_DEL_TO_DEVICE:
++ if (!dsa_slave_dev_check(dev))
++ return NOTIFY_DONE;
+
+- dp = dsa_slave_to_port(dev);
++ dp = dsa_slave_to_port(dev);
+
+- switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+- if (!switchdev_work)
+- return NOTIFY_BAD;
+-
+- INIT_WORK(&switchdev_work->work,
+- dsa_slave_switchdev_event_work);
+- switchdev_work->ds = dp->ds;
+- switchdev_work->port = dp->index;
+- switchdev_work->event = event;
++ switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
++ if (!switchdev_work)
++ return NOTIFY_BAD;
++
++ INIT_WORK(&switchdev_work->work,
++ dsa_slave_switchdev_event_work);
++ switchdev_work->ds = dp->ds;
++ switchdev_work->port = dp->index;
++ switchdev_work->event = event;
+
+- switch (event) {
+- case SWITCHDEV_FDB_ADD_TO_DEVICE:
+- case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ fdb_info = ptr;
+
+ if (!fdb_info->added_by_user) {
+@@ -2196,13 +2194,12 @@ static int dsa_slave_switchdev_event(str
+ switchdev_work->vid = fdb_info->vid;
+
+ dev_hold(dev);
++ dsa_schedule_work(&switchdev_work->work);
+ break;
+ default:
+- kfree(switchdev_work);
+ return NOTIFY_DONE;
+ }
+
+- dsa_schedule_work(&switchdev_work->work);
+ return NOTIFY_OK;
+ }
+
--- /dev/null
+From 5fb4a451a87d8ed3363d28b63a3295399373d6c4 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Wed, 6 Jan 2021 11:51:34 +0200
+Subject: [PATCH] net: dsa: exit early in dsa_slave_switchdev_event if we can't
+ program the FDB
+
+Right now, the following would happen for a switch driver that does not
+implement .port_fdb_add or .port_fdb_del.
+
+dsa_slave_switchdev_event returns NOTIFY_OK and schedules:
+-> dsa_slave_switchdev_event_work
+ -> dsa_port_fdb_add
+ -> dsa_port_notify(DSA_NOTIFIER_FDB_ADD)
+ -> dsa_switch_fdb_add
+ -> if (!ds->ops->port_fdb_add) return -EOPNOTSUPP;
+ -> an error is printed with dev_dbg, and
+ dsa_fdb_offload_notify(switchdev_work) is not called.
+
+We can avoid scheduling the worker for nothing and say NOTIFY_DONE.
+Because we don't call dsa_fdb_offload_notify, the static FDB entry will
+remain just in the software bridge.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ net/dsa/slave.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -2172,6 +2172,9 @@ static int dsa_slave_switchdev_event(str
+
+ dp = dsa_slave_to_port(dev);
+
++ if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del)
++ return NOTIFY_DONE;
++
+ switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+ if (!switchdev_work)
+ return NOTIFY_BAD;
--- /dev/null
+From d5f19486cee79d04c054427577ac96ed123706db Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Wed, 6 Jan 2021 11:51:35 +0200
+Subject: [PATCH] net: dsa: listen for SWITCHDEV_{FDB,DEL}_ADD_TO_DEVICE on
+ foreign bridge neighbors
+
+Some DSA switches (and not only) cannot learn source MAC addresses from
+packets injected from the CPU. They only perform hardware address
+learning from inbound traffic.
+
+This can be problematic when we have a bridge spanning some DSA switch
+ports and some non-DSA ports (which we'll call "foreign interfaces" from
+DSA's perspective).
+
+There are 2 classes of problems created by the lack of learning on
+CPU-injected traffic:
+- excessive flooding, due to the fact that DSA treats those addresses as
+ unknown
+- the risk of stale routes, which can lead to temporary packet loss
+
+To illustrate the second class, consider the following situation, which
+is common in production equipment (wireless access points, where there
+is a WLAN interface and an Ethernet switch, and these form a single
+bridging domain).
+
+ AP 1:
+ +------------------------------------------------------------------------+
+ | br0 |
+ +------------------------------------------------------------------------+
+ +------------+ +------------+ +------------+ +------------+ +------------+
+ | swp0 | | swp1 | | swp2 | | swp3 | | wlan0 |
+ +------------+ +------------+ +------------+ +------------+ +------------+
+ | ^ ^
+ | | |
+ | | |
+ | Client A Client B
+ |
+ |
+ |
+ +------------+ +------------+ +------------+ +------------+ +------------+
+ | swp0 | | swp1 | | swp2 | | swp3 | | wlan0 |
+ +------------+ +------------+ +------------+ +------------+ +------------+
+ +------------------------------------------------------------------------+
+ | br0 |
+ +------------------------------------------------------------------------+
+ AP 2
+
+- br0 of AP 1 will know that Clients A and B are reachable via wlan0
+- the hardware fdb of a DSA switch driver today is not kept in sync with
+ the software entries on other bridge ports, so it will not know that
+ clients A and B are reachable via the CPU port UNLESS the hardware
+ switch itself performs SA learning from traffic injected from the CPU.
+ Nonetheless, a substantial number of switches don't.
+- the hardware fdb of the DSA switch on AP 2 may autonomously learn that
+ Client A and B are reachable through swp0. Therefore, the software br0
+ of AP 2 also may or may not learn this. In the example we're
+ illustrating, some Ethernet traffic has been going on, and br0 from AP
+ 2 has indeed learnt that it can reach Client B through swp0.
+
+One of the wireless clients, say Client B, disconnects from AP 1 and
+roams to AP 2. The topology now looks like this:
+
+ AP 1:
+ +------------------------------------------------------------------------+
+ | br0 |
+ +------------------------------------------------------------------------+
+ +------------+ +------------+ +------------+ +------------+ +------------+
+ | swp0 | | swp1 | | swp2 | | swp3 | | wlan0 |
+ +------------+ +------------+ +------------+ +------------+ +------------+
+ | ^
+ | |
+ | Client A
+ |
+ |
+ | Client B
+ | |
+ | v
+ +------------+ +------------+ +------------+ +------------+ +------------+
+ | swp0 | | swp1 | | swp2 | | swp3 | | wlan0 |
+ +------------+ +------------+ +------------+ +------------+ +------------+
+ +------------------------------------------------------------------------+
+ | br0 |
+ +------------------------------------------------------------------------+
+ AP 2
+
+- br0 of AP 1 still knows that Client A is reachable via wlan0 (no change)
+- br0 of AP 1 will (possibly) know that Client B has left wlan0. There
+ are cases where it might never find out though. Either way, DSA today
+ does not process that notification in any way.
+- the hardware FDB of the DSA switch on AP 1 may learn autonomously that
+ Client B can be reached via swp0, if it receives any packet with
+ Client 1's source MAC address over Ethernet.
+- the hardware FDB of the DSA switch on AP 2 still thinks that Client B
+ can be reached via swp0. It does not know that it has roamed to wlan0,
+ because it doesn't perform SA learning from the CPU port.
+
+Now Client A contacts Client B.
+AP 1 routes the packet fine towards swp0 and delivers it on the Ethernet
+segment.
+AP 2 sees a frame on swp0 and its fdb says that the destination is swp0.
+Hairpinning is disabled => drop.
+
+This problem comes from the fact that these switches have a 'blind spot'
+for addresses coming from software bridging. The generic solution is not
+to assume that hardware learning can be enabled somehow, but to listen
+to more bridge learning events. It turns out that the bridge driver does
+learn in software from all inbound frames, in __br_handle_local_finish.
+A proper SWITCHDEV_FDB_ADD_TO_DEVICE notification is emitted for the
+addresses serviced by the bridge on 'foreign' interfaces. The software
+bridge also does the right thing on migration, by notifying that the old
+entry is deleted, so that does not need to be special-cased in DSA. When
+it is deleted, we just need to delete our static FDB entry towards the
+CPU too, and wait.
+
+The problem is that DSA currently only cares about SWITCHDEV_FDB_ADD_TO_DEVICE
+events received on its own interfaces, such as static FDB entries.
+
+Luckily we can change that, and DSA can listen to all switchdev FDB
+add/del events in the system and figure out if those events were emitted
+by a bridge that spans at least one of DSA's own ports. In case that is
+true, DSA will also offload that address towards its own CPU port, in
+the eventuality that there might be bridge clients attached to the DSA
+switch who want to talk to the station connected to the foreign
+interface.
+
+In terms of implementation, we need to keep the fdb_info->added_by_user
+check for the case where the switchdev event was targeted directly at a
+DSA switch port. But we don't need to look at that flag for snooped
+events. So the check is currently too late, we need to move it earlier.
+This also simplifies the code a bit, since we avoid uselessly allocating
+and freeing switchdev_work.
+
+We could probably do some improvements in the future. For example,
+multi-bridge support is rudimentary at the moment. If there are two
+bridges spanning a DSA switch's ports, and both of them need to service
+the same MAC address, then what will happen is that the migration of one
+of those stations will trigger the deletion of the FDB entry from the
+CPU port while it is still used by other bridge. That could be improved
+with reference counting but is left for another time.
+
+This behavior needs to be enabled at driver level by setting
+ds->assisted_learning_on_cpu_port = true. This is because we don't want
+to inflict a potential performance penalty (accesses through
+MDIO/I2C/SPI are expensive) to hardware that really doesn't need it
+because address learning on the CPU port works there.
+
+Reported-by: DENG Qingfang <dqfext@gmail.com>
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ include/net/dsa.h | 5 +++++
+ net/dsa/slave.c | 66 +++++++++++++++++++++++++++++++++++++++++++++----------
+ 2 files changed, 60 insertions(+), 11 deletions(-)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -317,6 +317,11 @@ struct dsa_switch {
+ */
+ bool untag_bridge_pvid;
+
++ /* Let DSA manage the FDB entries towards the CPU, based on the
++ * software bridge database.
++ */
++ bool assisted_learning_on_cpu_port;
++
+ /* In case vlan_filtering_is_global is set, the VLAN awareness state
+ * should be retrieved from here and not from the per-port settings.
+ */
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -2149,6 +2149,28 @@ static void dsa_slave_switchdev_event_wo
+ dev_put(dp->slave);
+ }
+
++static int dsa_lower_dev_walk(struct net_device *lower_dev,
++ struct netdev_nested_priv *priv)
++{
++ if (dsa_slave_dev_check(lower_dev)) {
++ priv->data = (void *)netdev_priv(lower_dev);
++ return 1;
++ }
++
++ return 0;
++}
++
++static struct dsa_slave_priv *dsa_slave_dev_lower_find(struct net_device *dev)
++{
++ struct netdev_nested_priv priv = {
++ .data = NULL,
++ };
++
++ netdev_walk_all_lower_dev_rcu(dev, dsa_lower_dev_walk, &priv);
++
++ return (struct dsa_slave_priv *)priv.data;
++}
++
+ /* Called under rcu_read_lock() */
+ static int dsa_slave_switchdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+@@ -2167,10 +2189,37 @@ static int dsa_slave_switchdev_event(str
+ return notifier_from_errno(err);
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+- if (!dsa_slave_dev_check(dev))
+- return NOTIFY_DONE;
++ fdb_info = ptr;
++
++ if (dsa_slave_dev_check(dev)) {
++ if (!fdb_info->added_by_user)
++ return NOTIFY_OK;
++
++ dp = dsa_slave_to_port(dev);
++ } else {
++ /* Snoop addresses learnt on foreign interfaces
++ * bridged with us, for switches that don't
++ * automatically learn SA from CPU-injected traffic
++ */
++ struct net_device *br_dev;
++ struct dsa_slave_priv *p;
++
++ br_dev = netdev_master_upper_dev_get_rcu(dev);
++ if (!br_dev)
++ return NOTIFY_DONE;
++
++ if (!netif_is_bridge_master(br_dev))
++ return NOTIFY_DONE;
++
++ p = dsa_slave_dev_lower_find(br_dev);
++ if (!p)
++ return NOTIFY_DONE;
+
+- dp = dsa_slave_to_port(dev);
++ dp = p->dp->cpu_dp;
++
++ if (!dp->ds->assisted_learning_on_cpu_port)
++ return NOTIFY_DONE;
++ }
+
+ if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del)
+ return NOTIFY_DONE;
+@@ -2185,18 +2234,13 @@ static int dsa_slave_switchdev_event(str
+ switchdev_work->port = dp->index;
+ switchdev_work->event = event;
+
+- fdb_info = ptr;
+-
+- if (!fdb_info->added_by_user) {
+- kfree(switchdev_work);
+- return NOTIFY_OK;
+- }
+-
+ ether_addr_copy(switchdev_work->addr,
+ fdb_info->addr);
+ switchdev_work->vid = fdb_info->vid;
+
+- dev_hold(dev);
++ /* Hold a reference on the slave for dsa_fdb_offload_notify */
++ if (dsa_is_user_port(dp->ds, dp->index))
++ dev_hold(dev);
+ dsa_schedule_work(&switchdev_work->work);
+ break;
+ default:
--- /dev/null
+From c3b8e07909dbe67b0d580416c1a5257643a73be7 Mon Sep 17 00:00:00 2001
+From: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Date: Fri, 12 Mar 2021 00:07:03 -0800
+Subject: [PATCH] net: dsa: mt7530: setup core clock even in TRGMII mode
+
+A recent change to MIPS ralink reset logic made it so mt7530 actually
+resets the switch on platforms such as mt7621 (where bit 2 is the reset
+line for the switch). That exposed an issue where the switch would not
+function properly in TRGMII mode after a reset.
+
+Reconfigure core clock in TRGMII mode to fix the issue.
+
+Tested on Ubiquiti ER-X (MT7621) with TRGMII mode enabled.
+
+Fixes: 3f9ef7785a9c ("MIPS: ralink: manage low reset lines")
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 52 +++++++++++++++++++---------------------
+ 1 file changed, 25 insertions(+), 27 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -436,34 +436,32 @@ mt7530_pad_clk_setup(struct dsa_switch *
+ TD_DM_DRVP(8) | TD_DM_DRVN(8));
+
+ /* Setup core clock for MT7530 */
+- if (!trgint) {
+- /* Disable MT7530 core clock */
+- core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
+-
+- /* Disable PLL, since phy_device has not yet been created
+- * provided for phy_[read,write]_mmd_indirect is called, we
+- * provide our own core_write_mmd_indirect to complete this
+- * function.
+- */
+- core_write_mmd_indirect(priv,
+- CORE_GSWPLL_GRP1,
+- MDIO_MMD_VEND2,
+- 0);
+-
+- /* Set core clock into 500Mhz */
+- core_write(priv, CORE_GSWPLL_GRP2,
+- RG_GSWPLL_POSDIV_500M(1) |
+- RG_GSWPLL_FBKDIV_500M(25));
+-
+- /* Enable PLL */
+- core_write(priv, CORE_GSWPLL_GRP1,
+- RG_GSWPLL_EN_PRE |
+- RG_GSWPLL_POSDIV_200M(2) |
+- RG_GSWPLL_FBKDIV_200M(32));
+-
+- /* Enable MT7530 core clock */
+- core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
+- }
++ /* Disable MT7530 core clock */
++ core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
++
++ /* Disable PLL, since phy_device has not yet been created
++ * provided for phy_[read,write]_mmd_indirect is called, we
++ * provide our own core_write_mmd_indirect to complete this
++ * function.
++ */
++ core_write_mmd_indirect(priv,
++ CORE_GSWPLL_GRP1,
++ MDIO_MMD_VEND2,
++ 0);
++
++ /* Set core clock into 500Mhz */
++ core_write(priv, CORE_GSWPLL_GRP2,
++ RG_GSWPLL_POSDIV_500M(1) |
++ RG_GSWPLL_FBKDIV_500M(25));
++
++ /* Enable PLL */
++ core_write(priv, CORE_GSWPLL_GRP1,
++ RG_GSWPLL_EN_PRE |
++ RG_GSWPLL_POSDIV_200M(2) |
++ RG_GSWPLL_FBKDIV_200M(32));
++
++ /* Enable MT7530 core clock */
++ core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
+
+ /* Setup the MT7530 TRGMII Tx Clock */
+ core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
--- /dev/null
+From 429a0edeefd88cbfca5c417dfb8561047bb50769 Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Mon, 25 Jan 2021 12:43:22 +0800
+Subject: [PATCH] net: dsa: mt7530: MT7530 optional GPIO support
+
+MT7530's LED controller can drive up to 15 LED/GPIOs.
+
+Add support for GPIO control and allow users to use its GPIOs by
+setting gpio-controller property in device tree.
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 110 +++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/mt7530.h | 20 +++++++
+ 2 files changed, 130 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -18,6 +18,7 @@
+ #include <linux/regulator/consumer.h>
+ #include <linux/reset.h>
+ #include <linux/gpio/consumer.h>
++#include <linux/gpio/driver.h>
+ #include <net/dsa.h>
+
+ #include "mt7530.h"
+@@ -1534,6 +1535,109 @@ mtk_get_tag_protocol(struct dsa_switch *
+ }
+ }
+
++static inline u32
++mt7530_gpio_to_bit(unsigned int offset)
++{
++ /* Map GPIO offset to register bit
++ * [ 2: 0] port 0 LED 0..2 as GPIO 0..2
++ * [ 6: 4] port 1 LED 0..2 as GPIO 3..5
++ * [10: 8] port 2 LED 0..2 as GPIO 6..8
++ * [14:12] port 3 LED 0..2 as GPIO 9..11
++ * [18:16] port 4 LED 0..2 as GPIO 12..14
++ */
++ return BIT(offset + offset / 3);
++}
++
++static int
++mt7530_gpio_get(struct gpio_chip *gc, unsigned int offset)
++{
++ struct mt7530_priv *priv = gpiochip_get_data(gc);
++ u32 bit = mt7530_gpio_to_bit(offset);
++
++ return !!(mt7530_read(priv, MT7530_LED_GPIO_DATA) & bit);
++}
++
++static void
++mt7530_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
++{
++ struct mt7530_priv *priv = gpiochip_get_data(gc);
++ u32 bit = mt7530_gpio_to_bit(offset);
++
++ if (value)
++ mt7530_set(priv, MT7530_LED_GPIO_DATA, bit);
++ else
++ mt7530_clear(priv, MT7530_LED_GPIO_DATA, bit);
++}
++
++static int
++mt7530_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
++{
++ struct mt7530_priv *priv = gpiochip_get_data(gc);
++ u32 bit = mt7530_gpio_to_bit(offset);
++
++ return (mt7530_read(priv, MT7530_LED_GPIO_DIR) & bit) ?
++ GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
++}
++
++static int
++mt7530_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
++{
++ struct mt7530_priv *priv = gpiochip_get_data(gc);
++ u32 bit = mt7530_gpio_to_bit(offset);
++
++ mt7530_clear(priv, MT7530_LED_GPIO_OE, bit);
++ mt7530_clear(priv, MT7530_LED_GPIO_DIR, bit);
++
++ return 0;
++}
++
++static int
++mt7530_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
++{
++ struct mt7530_priv *priv = gpiochip_get_data(gc);
++ u32 bit = mt7530_gpio_to_bit(offset);
++
++ mt7530_set(priv, MT7530_LED_GPIO_DIR, bit);
++
++ if (value)
++ mt7530_set(priv, MT7530_LED_GPIO_DATA, bit);
++ else
++ mt7530_clear(priv, MT7530_LED_GPIO_DATA, bit);
++
++ mt7530_set(priv, MT7530_LED_GPIO_OE, bit);
++
++ return 0;
++}
++
++static int
++mt7530_setup_gpio(struct mt7530_priv *priv)
++{
++ struct device *dev = priv->dev;
++ struct gpio_chip *gc;
++
++ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
++ if (!gc)
++ return -ENOMEM;
++
++ mt7530_write(priv, MT7530_LED_GPIO_OE, 0);
++ mt7530_write(priv, MT7530_LED_GPIO_DIR, 0);
++ mt7530_write(priv, MT7530_LED_IO_MODE, 0);
++
++ gc->label = "mt7530";
++ gc->parent = dev;
++ gc->owner = THIS_MODULE;
++ gc->get_direction = mt7530_gpio_get_direction;
++ gc->direction_input = mt7530_gpio_direction_input;
++ gc->direction_output = mt7530_gpio_direction_output;
++ gc->get = mt7530_gpio_get;
++ gc->set = mt7530_gpio_set;
++ gc->base = -1;
++ gc->ngpio = 15;
++ gc->can_sleep = true;
++
++ return devm_gpiochip_add_data(dev, gc, priv);
++}
++
+ static int
+ mt7530_setup(struct dsa_switch *ds)
+ {
+@@ -1675,6 +1779,12 @@ mt7530_setup(struct dsa_switch *ds)
+ }
+ }
+
++ if (of_property_read_bool(priv->dev->of_node, "gpio-controller")) {
++ ret = mt7530_setup_gpio(priv);
++ if (ret)
++ return ret;
++ }
++
+ mt7530_setup_port5(ds, interface);
+
+ /* Flush the FDB table */
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -529,6 +529,26 @@ enum mt7531_clk_skew {
+ #define MT7531_GPIO12_RG_RXD3_MASK GENMASK(19, 16)
+ #define MT7531_EXT_P_MDIO_12 (2 << 16)
+
++/* Registers for LED GPIO control (MT7530 only)
++ * All registers follow this pattern:
++ * [ 2: 0] port 0
++ * [ 6: 4] port 1
++ * [10: 8] port 2
++ * [14:12] port 3
++ * [18:16] port 4
++ */
++
++/* LED enable, 0: Disable, 1: Enable (Default) */
++#define MT7530_LED_EN 0x7d00
++/* LED mode, 0: GPIO mode, 1: PHY mode (Default) */
++#define MT7530_LED_IO_MODE 0x7d04
++/* GPIO direction, 0: Input, 1: Output */
++#define MT7530_LED_GPIO_DIR 0x7d10
++/* GPIO output enable, 0: Disable, 1: Enable */
++#define MT7530_LED_GPIO_OE 0x7d14
++/* GPIO value, 0: Low, 1: High */
++#define MT7530_LED_GPIO_DATA 0x7d18
++
+ #define MT7530_CREV 0x7ffc
+ #define CHIP_NAME_SHIFT 16
+ #define MT7530_ID 0x7530
--- /dev/null
+From 40b5d2f15c091fa9c854acde91ad2acb504027d7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= <opensource@vdorst.com>
+Date: Mon, 12 Apr 2021 08:50:31 +0200
+Subject: [PATCH] net: dsa: mt7530: Add support for EEE features
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch adds EEE support.
+
+Signed-off-by: René van Dorst <opensource@vdorst.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 43 ++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/mt7530.h | 14 ++++++++++++-
+ 2 files changed, 56 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2371,6 +2371,17 @@ static void mt753x_phylink_mac_link_up(s
+ mcr |= PMCR_RX_FC_EN;
+ }
+
++ if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, 0) >= 0) {
++ switch (speed) {
++ case SPEED_1000:
++ mcr |= PMCR_FORCE_EEE1G;
++ break;
++ case SPEED_100:
++ mcr |= PMCR_FORCE_EEE100;
++ break;
++ }
++ }
++
+ mt7530_set(priv, MT7530_PMCR_P(port), mcr);
+ }
+
+@@ -2601,6 +2612,36 @@ mt753x_phy_write(struct dsa_switch *ds,
+ return priv->info->phy_write(ds, port, regnum, val);
+ }
+
++static int mt753x_get_mac_eee(struct dsa_switch *ds, int port,
++ struct ethtool_eee *e)
++{
++ struct mt7530_priv *priv = ds->priv;
++ u32 eeecr = mt7530_read(priv, MT7530_PMEEECR_P(port));
++
++ e->tx_lpi_enabled = !(eeecr & LPI_MODE_EN);
++ e->tx_lpi_timer = GET_LPI_THRESH(eeecr);
++
++ return 0;
++}
++
++static int mt753x_set_mac_eee(struct dsa_switch *ds, int port,
++ struct ethtool_eee *e)
++{
++ struct mt7530_priv *priv = ds->priv;
++ u32 set, mask = LPI_THRESH_MASK | LPI_MODE_EN;
++
++ if (e->tx_lpi_timer > 0xFFF)
++ return -EINVAL;
++
++ set = SET_LPI_THRESH(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);
++
++ return 0;
++}
++
+ static const struct dsa_switch_ops mt7530_switch_ops = {
+ .get_tag_protocol = mtk_get_tag_protocol,
+ .setup = mt753x_setup,
+@@ -2629,6 +2670,8 @@ static const struct dsa_switch_ops mt753
+ .phylink_mac_an_restart = mt753x_phylink_mac_an_restart,
+ .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,
+ };
+
+ static const struct mt753x_info mt753x_table[] = {
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -240,6 +240,8 @@ enum mt7530_vlan_port_attr {
+ #define PMCR_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_SPEED_1000 BIT(3)
+@@ -264,7 +266,8 @@ enum mt7530_vlan_port_attr {
+ #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_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 | \
+@@ -273,6 +276,15 @@ enum mt7530_vlan_port_attr {
+ 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)
++#define WAKEUP_TIME_100(x) (((x) & 0xFF) << 16)
++#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_MODE_EN BIT(0)
++
+ #define MT7530_PMSR_P(x) (0x3008 + (x) * 0x100)
+ #define PMSR_EEE1G BIT(7)
+ #define PMSR_EEE100M BIT(6)
--- /dev/null
+From 83216e3988cd196183542937c9bd58b279f946af Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 12 Apr 2021 19:47:17 +0200
+Subject: of: net: pass the dst buffer to of_get_mac_address()
+
+of_get_mac_address() returns a "const void*" pointer to a MAC address.
+Lately, support to fetch the MAC address by an NVMEM provider was added.
+But this will only work with platform devices. It will not work with
+PCI devices (e.g. of an integrated root complex) and esp. not with DSA
+ports.
+
+There is an of_* variant of the nvmem binding which works without
+devices. The returned data of a nvmem_cell_read() has to be freed after
+use. On the other hand the return of_get_mac_address() points to some
+static data without a lifetime. The trick for now, was to allocate a
+device resource managed buffer which is then returned. This will only
+work if we have an actual device.
+
+Change it, so that the caller of of_get_mac_address() has to supply a
+buffer where the MAC address is written to. Unfortunately, this will
+touch all drivers which use the of_get_mac_address().
+
+Usually the code looks like:
+
+ const char *addr;
+ addr = of_get_mac_address(np);
+ if (!IS_ERR(addr))
+ ether_addr_copy(ndev->dev_addr, addr);
+
+This can then be simply rewritten as:
+
+ of_get_mac_address(np, ndev->dev_addr);
+
+Sometimes is_valid_ether_addr() is used to test the MAC address.
+of_get_mac_address() already makes sure, it just returns a valid MAC
+address. Thus we can just test its return code. But we have to be
+careful if there are still other sources for the MAC address before the
+of_get_mac_address(). In this case we have to keep the
+is_valid_ether_addr() call.
+
+The following coccinelle patch was used to convert common cases to the
+new style. Afterwards, I've manually gone over the drivers and fixed the
+return code variable: either used a new one or if one was already
+available use that. Mansour Moufid, thanks for that coccinelle patch!
+
+<spml>
+@a@
+identifier x;
+expression y, z;
+@@
+- x = of_get_mac_address(y);
++ x = of_get_mac_address(y, z);
+ <...
+- ether_addr_copy(z, x);
+ ...>
+
+@@
+identifier a.x;
+@@
+- if (<+... x ...+>) {}
+
+@@
+identifier a.x;
+@@
+ if (<+... x ...+>) {
+ ...
+ }
+- else {}
+
+@@
+identifier a.x;
+expression e;
+@@
+- if (<+... x ...+>@e)
+- {}
+- else
++ if (!(e))
+ {...}
+
+@@
+expression x, y, z;
+@@
+- x = of_get_mac_address(y, z);
++ of_get_mac_address(y, z);
+ ... when != x
+</spml>
+
+All drivers, except drivers/net/ethernet/aeroflex/greth.c, were
+compile-time tested.
+
+Suggested-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Michael Walle <michael@walle.cc>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ arch/arm/mach-mvebu/kirkwood.c | 3 +-
+ arch/powerpc/sysdev/tsi108_dev.c | 5 +-
+ drivers/net/ethernet/aeroflex/greth.c | 6 +--
+ drivers/net/ethernet/allwinner/sun4i-emac.c | 10 ++--
+ drivers/net/ethernet/altera/altera_tse_main.c | 7 +--
+ drivers/net/ethernet/arc/emac_main.c | 8 +--
+ drivers/net/ethernet/atheros/ag71xx.c | 7 +--
+ drivers/net/ethernet/broadcom/bcm4908_enet.c | 7 +--
+ drivers/net/ethernet/broadcom/bcmsysport.c | 7 +--
+ drivers/net/ethernet/broadcom/bgmac-bcma.c | 10 ++--
+ drivers/net/ethernet/broadcom/bgmac-platform.c | 11 ++--
+ drivers/net/ethernet/cadence/macb_main.c | 11 ++--
+ drivers/net/ethernet/cavium/octeon/octeon_mgmt.c | 8 +--
+ drivers/net/ethernet/cavium/thunder/thunder_bgx.c | 5 +-
+ drivers/net/ethernet/davicom/dm9000.c | 10 ++--
+ drivers/net/ethernet/ethoc.c | 6 +--
+ drivers/net/ethernet/ezchip/nps_enet.c | 7 +--
+ drivers/net/ethernet/freescale/fec_main.c | 7 +--
+ drivers/net/ethernet/freescale/fec_mpc52xx.c | 7 +--
+ drivers/net/ethernet/freescale/fman/mac.c | 9 ++--
+ .../net/ethernet/freescale/fs_enet/fs_enet-main.c | 5 +-
+ drivers/net/ethernet/freescale/gianfar.c | 8 +--
+ drivers/net/ethernet/freescale/ucc_geth.c | 5 +-
+ drivers/net/ethernet/hisilicon/hisi_femac.c | 7 +--
+ drivers/net/ethernet/hisilicon/hix5hd2_gmac.c | 7 +--
+ drivers/net/ethernet/lantiq_xrx200.c | 7 +--
+ drivers/net/ethernet/marvell/mv643xx_eth.c | 5 +-
+ drivers/net/ethernet/marvell/mvneta.c | 6 +--
+ .../net/ethernet/marvell/prestera/prestera_main.c | 11 ++--
+ drivers/net/ethernet/marvell/pxa168_eth.c | 9 +---
+ drivers/net/ethernet/marvell/sky2.c | 8 ++-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 11 ++--
+ drivers/net/ethernet/micrel/ks8851_common.c | 7 ++-
+ drivers/net/ethernet/microchip/lan743x_main.c | 5 +-
+ drivers/net/ethernet/nxp/lpc_eth.c | 4 +-
+ drivers/net/ethernet/qualcomm/qca_spi.c | 10 ++--
+ drivers/net/ethernet/qualcomm/qca_uart.c | 9 +---
+ drivers/net/ethernet/renesas/ravb_main.c | 12 +++--
+ drivers/net/ethernet/renesas/sh_eth.c | 5 +-
+ .../net/ethernet/samsung/sxgbe/sxgbe_platform.c | 13 ++---
+ drivers/net/ethernet/socionext/sni_ave.c | 10 ++--
+ .../net/ethernet/stmicro/stmmac/dwmac-anarion.c | 2 +-
+ .../ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c | 2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-generic.c | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c | 2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-intel-plat.c | 2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-ipq806x.c | 2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c | 2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-mediatek.c | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c | 2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-meson8b.c | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c | 2 +-
+ .../ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-socfpga.c | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c | 2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-visconti.c | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +-
+ .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 14 ++---
+ .../net/ethernet/stmicro/stmmac/stmmac_platform.h | 2 +-
+ drivers/net/ethernet/ti/am65-cpsw-nuss.c | 19 ++++---
+ drivers/net/ethernet/ti/cpsw.c | 7 +--
+ drivers/net/ethernet/ti/cpsw_new.c | 7 +--
+ drivers/net/ethernet/ti/davinci_emac.c | 8 +--
+ drivers/net/ethernet/ti/netcp_core.c | 7 +--
+ drivers/net/ethernet/wiznet/w5100-spi.c | 8 ++-
+ drivers/net/ethernet/wiznet/w5100.c | 2 +-
+ drivers/net/ethernet/xilinx/ll_temac_main.c | 8 +--
+ drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 15 +++---
+ drivers/net/ethernet/xilinx/xilinx_emaclite.c | 8 +--
+ drivers/net/wireless/ath/ath9k/init.c | 5 +-
+ drivers/net/wireless/mediatek/mt76/eeprom.c | 9 +---
+ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 6 +--
+ drivers/of/of_net.c | 60 ++++++++++------------
+ drivers/staging/octeon/ethernet.c | 10 ++--
+ drivers/staging/wfx/main.c | 7 ++-
+ include/linux/of_net.h | 6 +--
+ include/net/dsa.h | 2 +-
+ net/dsa/dsa2.c | 2 +-
+ net/dsa/slave.c | 2 +-
+ net/ethernet/eth.c | 11 ++--
+ 85 files changed, 218 insertions(+), 364 deletions(-)
+
+--- a/arch/arm/mach-mvebu/kirkwood.c
++++ b/arch/arm/mach-mvebu/kirkwood.c
+@@ -84,6 +84,7 @@ static void __init kirkwood_dt_eth_fixup
+ struct device_node *pnp = of_get_parent(np);
+ struct clk *clk;
+ struct property *pmac;
++ u8 tmpmac[ETH_ALEN];
+ void __iomem *io;
+ u8 *macaddr;
+ u32 reg;
+@@ -93,7 +94,7 @@ static void __init kirkwood_dt_eth_fixup
+
+ /* skip disabled nodes or nodes with valid MAC address*/
+ if (!of_device_is_available(pnp) ||
+- !IS_ERR(of_get_mac_address(np)))
++ !of_get_mac_address(np, tmpmac))
+ goto eth_fixup_skip;
+
+ clk = of_clk_get(pnp, 0);
+--- a/arch/powerpc/sysdev/tsi108_dev.c
++++ b/arch/powerpc/sysdev/tsi108_dev.c
+@@ -73,7 +73,6 @@ static int __init tsi108_eth_of_init(voi
+ struct device_node *phy, *mdio;
+ hw_info tsi_eth_data;
+ const unsigned int *phy_id;
+- const void *mac_addr;
+ const phandle *ph;
+
+ memset(r, 0, sizeof(r));
+@@ -101,9 +100,7 @@ static int __init tsi108_eth_of_init(voi
+ goto err;
+ }
+
+- mac_addr = of_get_mac_address(np);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(tsi_eth_data.mac_addr, mac_addr);
++ of_get_mac_address(np, tsi_eth_data.mac_addr);
+
+ ph = of_get_property(np, "mdio-handle", NULL);
+ mdio = of_find_node_by_phandle(*ph);
+--- a/drivers/net/ethernet/aeroflex/greth.c
++++ b/drivers/net/ethernet/aeroflex/greth.c
+@@ -1449,10 +1449,10 @@ static int greth_of_probe(struct platfor
+ break;
+ }
+ if (i == 6) {
+- const u8 *addr;
++ u8 addr[ETH_ALEN];
+
+- addr = of_get_mac_address(ofdev->dev.of_node);
+- if (!IS_ERR(addr)) {
++ err = of_get_mac_address(ofdev->dev.of_node, addr);
++ if (!err) {
+ for (i = 0; i < 6; i++)
+ macaddr[i] = (unsigned int) addr[i];
+ } else {
+--- a/drivers/net/ethernet/allwinner/sun4i-emac.c
++++ b/drivers/net/ethernet/allwinner/sun4i-emac.c
+@@ -790,7 +790,6 @@ static int emac_probe(struct platform_de
+ struct emac_board_info *db;
+ struct net_device *ndev;
+ int ret = 0;
+- const char *mac_addr;
+
+ ndev = alloc_etherdev(sizeof(struct emac_board_info));
+ if (!ndev) {
+@@ -853,12 +852,9 @@ static int emac_probe(struct platform_de
+ }
+
+ /* Read MAC-address from DT */
+- mac_addr = of_get_mac_address(np);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(ndev->dev_addr, mac_addr);
+-
+- /* Check if the MAC address is valid, if not get a random one */
+- if (!is_valid_ether_addr(ndev->dev_addr)) {
++ ret = of_get_mac_address(np, ndev->dev_addr);
++ if (ret) {
++ /* if the MAC address is invalid get a random one */
+ eth_hw_addr_random(ndev);
+ dev_warn(&pdev->dev, "using random MAC address %pM\n",
+ ndev->dev_addr);
+--- a/drivers/net/ethernet/altera/altera_tse_main.c
++++ b/drivers/net/ethernet/altera/altera_tse_main.c
+@@ -1351,7 +1351,6 @@ static int altera_tse_probe(struct platf
+ struct resource *control_port;
+ struct resource *dma_res;
+ struct altera_tse_private *priv;
+- const unsigned char *macaddr;
+ void __iomem *descmap;
+ const struct of_device_id *of_id = NULL;
+
+@@ -1528,10 +1527,8 @@ static int altera_tse_probe(struct platf
+ priv->rx_dma_buf_sz = ALTERA_RXDMABUFFER_SIZE;
+
+ /* get default MAC address from device tree */
+- macaddr = of_get_mac_address(pdev->dev.of_node);
+- if (!IS_ERR(macaddr))
+- ether_addr_copy(ndev->dev_addr, macaddr);
+- else
++ ret = of_get_mac_address(pdev->dev.of_node, ndev->dev_addr);
++ if (ret)
+ eth_hw_addr_random(ndev);
+
+ /* get phy addr and create mdio */
+--- a/drivers/net/ethernet/arc/emac_main.c
++++ b/drivers/net/ethernet/arc/emac_main.c
+@@ -857,7 +857,6 @@ int arc_emac_probe(struct net_device *nd
+ struct device_node *phy_node;
+ struct phy_device *phydev = NULL;
+ struct arc_emac_priv *priv;
+- const char *mac_addr;
+ unsigned int id, clock_frequency, irq;
+ int err;
+
+@@ -942,11 +941,8 @@ int arc_emac_probe(struct net_device *nd
+ }
+
+ /* Get MAC address from device tree */
+- mac_addr = of_get_mac_address(dev->of_node);
+-
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(ndev->dev_addr, mac_addr);
+- else
++ err = of_get_mac_address(dev->of_node, ndev->dev_addr);
++ if (err)
+ eth_hw_addr_random(ndev);
+
+ arc_emac_set_address_internal(ndev);
+--- a/drivers/net/ethernet/atheros/ag71xx.c
++++ b/drivers/net/ethernet/atheros/ag71xx.c
+@@ -1856,7 +1856,6 @@ static int ag71xx_probe(struct platform_
+ const struct ag71xx_dcfg *dcfg;
+ struct net_device *ndev;
+ struct resource *res;
+- const void *mac_addr;
+ int tx_size, err, i;
+ struct ag71xx *ag;
+
+@@ -1952,10 +1951,8 @@ static int ag71xx_probe(struct platform_
+ ag->stop_desc->ctrl = 0;
+ ag->stop_desc->next = (u32)ag->stop_desc_dma;
+
+- mac_addr = of_get_mac_address(np);
+- if (!IS_ERR(mac_addr))
+- memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
+- if (IS_ERR(mac_addr) || !is_valid_ether_addr(ndev->dev_addr)) {
++ err = of_get_mac_address(np, ndev->dev_addr);
++ if (err) {
+ netif_err(ag, probe, ndev, "invalid MAC address, using random address\n");
+ eth_random_addr(ndev->dev_addr);
+ }
+--- a/drivers/net/ethernet/broadcom/bcmsysport.c
++++ b/drivers/net/ethernet/broadcom/bcmsysport.c
+@@ -2468,7 +2468,6 @@ static int bcm_sysport_probe(struct plat
+ struct bcm_sysport_priv *priv;
+ struct device_node *dn;
+ struct net_device *dev;
+- const void *macaddr;
+ u32 txq, rxq;
+ int ret;
+
+@@ -2563,12 +2562,10 @@ static int bcm_sysport_probe(struct plat
+ }
+
+ /* Initialize netdevice members */
+- macaddr = of_get_mac_address(dn);
+- if (IS_ERR(macaddr)) {
++ ret = of_get_mac_address(dn, dev->dev_addr);
++ if (ret) {
+ dev_warn(&pdev->dev, "using random Ethernet MAC\n");
+ eth_hw_addr_random(dev);
+- } else {
+- ether_addr_copy(dev->dev_addr, macaddr);
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+@@ -115,7 +115,7 @@ static int bgmac_probe(struct bcma_devic
+ struct ssb_sprom *sprom = &core->bus->sprom;
+ struct mii_bus *mii_bus;
+ struct bgmac *bgmac;
+- const u8 *mac = NULL;
++ const u8 *mac;
+ int err;
+
+ bgmac = bgmac_alloc(&core->dev);
+@@ -128,11 +128,10 @@ static int bgmac_probe(struct bcma_devic
+
+ bcma_set_drvdata(core, bgmac);
+
+- if (bgmac->dev->of_node)
+- mac = of_get_mac_address(bgmac->dev->of_node);
++ err = of_get_mac_address(bgmac->dev->of_node, bgmac->net_dev->dev_addr);
+
+ /* If no MAC address assigned via device tree, check SPROM */
+- if (IS_ERR_OR_NULL(mac)) {
++ if (err) {
+ switch (core->core_unit) {
+ case 0:
+ mac = sprom->et0mac;
+@@ -149,10 +148,9 @@ static int bgmac_probe(struct bcma_devic
+ err = -ENOTSUPP;
+ goto err;
+ }
++ ether_addr_copy(bgmac->net_dev->dev_addr, mac);
+ }
+
+- ether_addr_copy(bgmac->net_dev->dev_addr, mac);
+-
+ /* On BCM4706 we need common core to access PHY */
+ if (core->id.id == BCMA_CORE_4706_MAC_GBIT &&
+ !core->bus->drv_gmac_cmn.core) {
+--- a/drivers/net/ethernet/broadcom/bgmac-platform.c
++++ b/drivers/net/ethernet/broadcom/bgmac-platform.c
+@@ -173,7 +173,7 @@ static int bgmac_probe(struct platform_d
+ struct device_node *np = pdev->dev.of_node;
+ struct bgmac *bgmac;
+ struct resource *regs;
+- const u8 *mac_addr;
++ int ret;
+
+ bgmac = bgmac_alloc(&pdev->dev);
+ if (!bgmac)
+@@ -192,11 +192,10 @@ static int bgmac_probe(struct platform_d
+ bgmac->dev = &pdev->dev;
+ bgmac->dma_dev = &pdev->dev;
+
+- mac_addr = of_get_mac_address(np);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(bgmac->net_dev->dev_addr, mac_addr);
+- else
+- dev_warn(&pdev->dev, "MAC address not present in device tree\n");
++ ret = of_get_mac_address(np, bgmac->net_dev->dev_addr);
++ if (ret)
++ dev_warn(&pdev->dev,
++ "MAC address not present in device tree\n");
+
+ bgmac->irq = platform_get_irq(pdev, 0);
+ if (bgmac->irq < 0)
+--- a/drivers/net/ethernet/cadence/macb_main.c
++++ b/drivers/net/ethernet/cadence/macb_main.c
+@@ -4479,7 +4479,6 @@ static int macb_probe(struct platform_de
+ struct net_device *dev;
+ struct resource *regs;
+ void __iomem *mem;
+- const char *mac;
+ struct macb *bp;
+ int err, val;
+
+@@ -4592,15 +4591,11 @@ static int macb_probe(struct platform_de
+ if (bp->caps & MACB_CAPS_NEEDS_RSTONUBR)
+ bp->rx_intr_mask |= MACB_BIT(RXUBR);
+
+- mac = of_get_mac_address(np);
+- if (PTR_ERR(mac) == -EPROBE_DEFER) {
+- err = -EPROBE_DEFER;
++ err = of_get_mac_address(np, bp->dev->dev_addr);
++ if (err == -EPROBE_DEFER)
+ goto err_out_free_netdev;
+- } else if (!IS_ERR_OR_NULL(mac)) {
+- ether_addr_copy(bp->dev->dev_addr, mac);
+- } else {
++ else if (err)
+ macb_get_hwaddr(bp);
+- }
+
+ err = of_get_phy_mode(np, &interface);
+ if (err)
+--- a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
++++ b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
+@@ -1385,7 +1385,6 @@ static int octeon_mgmt_probe(struct plat
+ struct net_device *netdev;
+ struct octeon_mgmt *p;
+ const __be32 *data;
+- const u8 *mac;
+ struct resource *res_mix;
+ struct resource *res_agl;
+ struct resource *res_agl_prt_ctl;
+@@ -1502,11 +1501,8 @@ static int octeon_mgmt_probe(struct plat
+ netdev->min_mtu = 64 - OCTEON_MGMT_RX_HEADROOM;
+ netdev->max_mtu = 16383 - OCTEON_MGMT_RX_HEADROOM - VLAN_HLEN;
+
+- mac = of_get_mac_address(pdev->dev.of_node);
+-
+- if (!IS_ERR(mac))
+- ether_addr_copy(netdev->dev_addr, mac);
+- else
++ result = of_get_mac_address(pdev->dev.of_node, netdev->dev_addr);
++ if (result)
+ eth_hw_addr_random(netdev);
+
+ p->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
+--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
++++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
+@@ -1474,7 +1474,6 @@ static int bgx_init_of_phy(struct bgx *b
+ device_for_each_child_node(&bgx->pdev->dev, fwn) {
+ struct phy_device *pd;
+ struct device_node *phy_np;
+- const char *mac;
+
+ /* Should always be an OF node. But if it is not, we
+ * cannot handle it, so exit the loop.
+@@ -1483,9 +1482,7 @@ static int bgx_init_of_phy(struct bgx *b
+ if (!node)
+ break;
+
+- mac = of_get_mac_address(node);
+- if (!IS_ERR(mac))
+- ether_addr_copy(bgx->lmac[lmac].mac, mac);
++ of_get_mac_address(node, bgx->lmac[lmac].mac);
+
+ SET_NETDEV_DEV(&bgx->lmac[lmac].netdev, &bgx->pdev->dev);
+ bgx->lmac[lmac].lmacid = lmac;
+--- a/drivers/net/ethernet/davicom/dm9000.c
++++ b/drivers/net/ethernet/davicom/dm9000.c
+@@ -1388,7 +1388,7 @@ static struct dm9000_plat_data *dm9000_p
+ {
+ struct dm9000_plat_data *pdata;
+ struct device_node *np = dev->of_node;
+- const void *mac_addr;
++ int ret;
+
+ if (!IS_ENABLED(CONFIG_OF) || !np)
+ return ERR_PTR(-ENXIO);
+@@ -1402,11 +1402,9 @@ static struct dm9000_plat_data *dm9000_p
+ if (of_find_property(np, "davicom,no-eeprom", NULL))
+ pdata->flags |= DM9000_PLATF_NO_EEPROM;
+
+- mac_addr = of_get_mac_address(np);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(pdata->dev_addr, mac_addr);
+- else if (PTR_ERR(mac_addr) == -EPROBE_DEFER)
+- return ERR_CAST(mac_addr);
++ ret = of_get_mac_address(np, pdata->dev_addr);
++ if (ret == -EPROBE_DEFER)
++ return ERR_PTR(ret);
+
+ return pdata;
+ }
+--- a/drivers/net/ethernet/ethoc.c
++++ b/drivers/net/ethernet/ethoc.c
+@@ -1151,11 +1151,7 @@ static int ethoc_probe(struct platform_d
+ ether_addr_copy(netdev->dev_addr, pdata->hwaddr);
+ priv->phy_id = pdata->phy_id;
+ } else {
+- const void *mac;
+-
+- mac = of_get_mac_address(pdev->dev.of_node);
+- if (!IS_ERR(mac))
+- ether_addr_copy(netdev->dev_addr, mac);
++ of_get_mac_address(pdev->dev.of_node, netdev->dev_addr);
+ priv->phy_id = -1;
+ }
+
+--- a/drivers/net/ethernet/ezchip/nps_enet.c
++++ b/drivers/net/ethernet/ezchip/nps_enet.c
+@@ -575,7 +575,6 @@ static s32 nps_enet_probe(struct platfor
+ struct net_device *ndev;
+ struct nps_enet_priv *priv;
+ s32 err = 0;
+- const char *mac_addr;
+
+ if (!dev->of_node)
+ return -ENODEV;
+@@ -602,10 +601,8 @@ static s32 nps_enet_probe(struct platfor
+ dev_dbg(dev, "Registers base address is 0x%p\n", priv->regs_base);
+
+ /* set kernel MAC address to dev */
+- mac_addr = of_get_mac_address(dev->of_node);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(ndev->dev_addr, mac_addr);
+- else
++ err = of_get_mac_address(dev->of_node, ndev->dev_addr);
++ if (err)
+ eth_hw_addr_random(ndev);
+
+ /* Get IRQ number */
+--- a/drivers/net/ethernet/freescale/fec_main.c
++++ b/drivers/net/ethernet/freescale/fec_main.c
+@@ -1666,6 +1666,7 @@ static void fec_get_mac(struct net_devic
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev);
+ unsigned char *iap, tmpaddr[ETH_ALEN];
++ int ret;
+
+ /*
+ * try to get mac address in following order:
+@@ -1681,9 +1682,9 @@ static void fec_get_mac(struct net_devic
+ if (!is_valid_ether_addr(iap)) {
+ struct device_node *np = fep->pdev->dev.of_node;
+ if (np) {
+- const char *mac = of_get_mac_address(np);
+- if (!IS_ERR(mac))
+- iap = (unsigned char *) mac;
++ ret = of_get_mac_address(np, tmpaddr);
++ if (!ret)
++ iap = tmpaddr;
+ }
+ }
+
+--- a/drivers/net/ethernet/freescale/fec_mpc52xx.c
++++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c
+@@ -813,7 +813,6 @@ static int mpc52xx_fec_probe(struct plat
+ const u32 *prop;
+ int prop_size;
+ struct device_node *np = op->dev.of_node;
+- const char *mac_addr;
+
+ phys_addr_t rx_fifo;
+ phys_addr_t tx_fifo;
+@@ -891,10 +890,8 @@ static int mpc52xx_fec_probe(struct plat
+ *
+ * First try to read MAC address from DT
+ */
+- mac_addr = of_get_mac_address(np);
+- if (!IS_ERR(mac_addr)) {
+- ether_addr_copy(ndev->dev_addr, mac_addr);
+- } else {
++ rv = of_get_mac_address(np, ndev->dev_addr);
++ if (rv) {
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+
+ /*
+--- a/drivers/net/ethernet/freescale/fman/mac.c
++++ b/drivers/net/ethernet/freescale/fman/mac.c
+@@ -616,7 +616,6 @@ static int mac_probe(struct platform_dev
+ struct platform_device *of_dev;
+ struct resource res;
+ struct mac_priv_s *priv;
+- const u8 *mac_addr;
+ u32 val;
+ u8 fman_id;
+ phy_interface_t phy_if;
+@@ -734,11 +733,9 @@ static int mac_probe(struct platform_dev
+ priv->cell_index = (u8)val;
+
+ /* Get the MAC address */
+- mac_addr = of_get_mac_address(mac_node);
+- if (IS_ERR(mac_addr))
++ err = of_get_mac_address(mac_node, mac_dev->addr);
++ if (err)
+ dev_warn(dev, "of_get_mac_address(%pOF) failed\n", mac_node);
+- else
+- ether_addr_copy(mac_dev->addr, mac_addr);
+
+ /* Get the port handles */
+ nph = of_count_phandle_with_args(mac_node, "fsl,fman-ports", NULL);
+@@ -864,7 +861,7 @@ static int mac_probe(struct platform_dev
+ if (err < 0)
+ dev_err(dev, "fman_set_mac_active_pause() = %d\n", err);
+
+- if (!IS_ERR(mac_addr))
++ if (!is_zero_ether_addr(mac_dev->addr))
+ dev_info(dev, "FMan MAC address: %pM\n", mac_dev->addr);
+
+ priv->eth_dev = dpaa_eth_add_device(fman_id, mac_dev);
+--- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
++++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
+@@ -918,7 +918,6 @@ static int fs_enet_probe(struct platform
+ const u32 *data;
+ struct clk *clk;
+ int err;
+- const u8 *mac_addr;
+ const char *phy_connection_type;
+ int privsize, len, ret = -ENODEV;
+
+@@ -1006,9 +1005,7 @@ static int fs_enet_probe(struct platform
+ spin_lock_init(&fep->lock);
+ spin_lock_init(&fep->tx_lock);
+
+- mac_addr = of_get_mac_address(ofdev->dev.of_node);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(ndev->dev_addr, mac_addr);
++ of_get_mac_address(ofdev->dev.of_node, ndev->dev_addr);
+
+ ret = fep->ops->allocate_bd(ndev);
+ if (ret)
+--- a/drivers/net/ethernet/freescale/gianfar.c
++++ b/drivers/net/ethernet/freescale/gianfar.c
+@@ -641,7 +641,6 @@ static phy_interface_t gfar_get_interfac
+ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
+ {
+ const char *model;
+- const void *mac_addr;
+ int err = 0, i;
+ phy_interface_t interface;
+ struct net_device *dev = NULL;
+@@ -783,11 +782,8 @@ static int gfar_of_init(struct platform_
+ if (stash_len || stash_idx)
+ priv->device_flags |= FSL_GIANFAR_DEV_HAS_BUF_STASHING;
+
+- mac_addr = of_get_mac_address(np);
+-
+- if (!IS_ERR(mac_addr)) {
+- ether_addr_copy(dev->dev_addr, mac_addr);
+- } else {
++ err = of_get_mac_address(np, dev->dev_addr);
++ if (err) {
+ eth_hw_addr_random(dev);
+ dev_info(&ofdev->dev, "Using random MAC address: %pM\n", dev->dev_addr);
+ }
+--- a/drivers/net/ethernet/freescale/ucc_geth.c
++++ b/drivers/net/ethernet/freescale/ucc_geth.c
+@@ -3696,7 +3696,6 @@ static int ucc_geth_probe(struct platfor
+ int err, ucc_num, max_speed = 0;
+ const unsigned int *prop;
+ const char *sprop;
+- const void *mac_addr;
+ phy_interface_t phy_interface;
+ static const int enet_to_speed[] = {
+ SPEED_10, SPEED_10, SPEED_10,
+@@ -3906,9 +3905,7 @@ static int ucc_geth_probe(struct platfor
+ goto err_free_netdev;
+ }
+
+- mac_addr = of_get_mac_address(np);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(dev->dev_addr, mac_addr);
++ of_get_mac_address(np, dev->dev_addr);
+
+ ugeth->ug_info = ug_info;
+ ugeth->dev = device;
+--- a/drivers/net/ethernet/hisilicon/hisi_femac.c
++++ b/drivers/net/ethernet/hisilicon/hisi_femac.c
+@@ -772,7 +772,6 @@ static int hisi_femac_drv_probe(struct p
+ struct net_device *ndev;
+ struct hisi_femac_priv *priv;
+ struct phy_device *phy;
+- const char *mac_addr;
+ int ret;
+
+ ndev = alloc_etherdev(sizeof(*priv));
+@@ -842,10 +841,8 @@ static int hisi_femac_drv_probe(struct p
+ (unsigned long)phy->phy_id,
+ phy_modes(phy->interface));
+
+- mac_addr = of_get_mac_address(node);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(ndev->dev_addr, mac_addr);
+- if (!is_valid_ether_addr(ndev->dev_addr)) {
++ ret = of_get_mac_address(node, ndev->dev_addr);
++ if (ret) {
+ eth_hw_addr_random(ndev);
+ dev_warn(dev, "using random MAC address %pM\n",
+ ndev->dev_addr);
+--- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
++++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
+@@ -1098,7 +1098,6 @@ static int hix5hd2_dev_probe(struct plat
+ struct net_device *ndev;
+ struct hix5hd2_priv *priv;
+ struct mii_bus *bus;
+- const char *mac_addr;
+ int ret;
+
+ ndev = alloc_etherdev(sizeof(struct hix5hd2_priv));
+@@ -1220,10 +1219,8 @@ static int hix5hd2_dev_probe(struct plat
+ goto out_phy_node;
+ }
+
+- mac_addr = of_get_mac_address(node);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(ndev->dev_addr, mac_addr);
+- if (!is_valid_ether_addr(ndev->dev_addr)) {
++ ret = of_get_mac_address(node, ndev->dev_addr);
++ if (ret) {
+ eth_hw_addr_random(ndev);
+ netdev_warn(ndev, "using random MAC address %pM\n",
+ ndev->dev_addr);
+--- a/drivers/net/ethernet/lantiq_xrx200.c
++++ b/drivers/net/ethernet/lantiq_xrx200.c
+@@ -440,7 +440,6 @@ static int xrx200_probe(struct platform_
+ struct resource *res;
+ struct xrx200_priv *priv;
+ struct net_device *net_dev;
+- const u8 *mac;
+ int err;
+
+ /* alloc the network device */
+@@ -484,10 +483,8 @@ static int xrx200_probe(struct platform_
+ return PTR_ERR(priv->clk);
+ }
+
+- mac = of_get_mac_address(np);
+- if (!IS_ERR(mac))
+- ether_addr_copy(net_dev->dev_addr, mac);
+- else
++ err = of_get_mac_address(np, net_dev->dev_addr);
++ if (err)
+ eth_hw_addr_random(net_dev);
+
+ /* bring up the dma engine and IP core */
+--- a/drivers/net/ethernet/marvell/mv643xx_eth.c
++++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
+@@ -2700,7 +2700,6 @@ static int mv643xx_eth_shared_of_add_por
+ struct platform_device *ppdev;
+ struct mv643xx_eth_platform_data ppd;
+ struct resource res;
+- const char *mac_addr;
+ int ret;
+ int dev_num = 0;
+
+@@ -2731,9 +2730,7 @@ static int mv643xx_eth_shared_of_add_por
+ return -EINVAL;
+ }
+
+- mac_addr = of_get_mac_address(pnp);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(ppd.mac_addr, mac_addr);
++ of_get_mac_address(pnp, ppd.mac_addr);
+
+ mv643xx_eth_property(pnp, "tx-queue-size", ppd.tx_queue_size);
+ mv643xx_eth_property(pnp, "tx-sram-addr", ppd.tx_sram_addr);
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -5062,7 +5062,6 @@ static int mvneta_probe(struct platform_
+ struct net_device *dev;
+ struct phylink *phylink;
+ struct phy *comphy;
+- const char *dt_mac_addr;
+ char hw_mac_addr[ETH_ALEN];
+ phy_interface_t phy_mode;
+ const char *mac_from;
+@@ -5158,10 +5157,9 @@ static int mvneta_probe(struct platform_
+ goto err_free_ports;
+ }
+
+- dt_mac_addr = of_get_mac_address(dn);
+- if (!IS_ERR(dt_mac_addr)) {
++ err = of_get_mac_address(dn, dev->dev_addr);
++ if (!err) {
+ mac_from = "device tree";
+- ether_addr_copy(dev->dev_addr, dt_mac_addr);
+ } else {
+ mvneta_get_mac_addr(pp, hw_mac_addr);
+ if (is_valid_ether_addr(hw_mac_addr)) {
+--- a/drivers/net/ethernet/marvell/prestera/prestera_main.c
++++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c
+@@ -466,20 +466,17 @@ static int prestera_switch_set_base_mac_
+ {
+ struct device_node *base_mac_np;
+ struct device_node *np;
+- const char *base_mac;
++ int ret;
+
+ np = of_find_compatible_node(NULL, NULL, "marvell,prestera");
+ base_mac_np = of_parse_phandle(np, "base-mac-provider", 0);
+
+- base_mac = of_get_mac_address(base_mac_np);
+- of_node_put(base_mac_np);
+- if (!IS_ERR(base_mac))
+- ether_addr_copy(sw->base_mac, base_mac);
+-
+- if (!is_valid_ether_addr(sw->base_mac)) {
++ ret = of_get_mac_address(base_mac_np, sw->base_mac);
++ if (ret) {
+ eth_random_addr(sw->base_mac);
+ dev_info(prestera_dev(sw), "using random base mac address\n");
+ }
++ of_node_put(base_mac_np);
+
+ return prestera_hw_switch_mac_set(sw, sw->base_mac);
+ }
+--- a/drivers/net/ethernet/marvell/pxa168_eth.c
++++ b/drivers/net/ethernet/marvell/pxa168_eth.c
+@@ -1392,7 +1392,6 @@ static int pxa168_eth_probe(struct platf
+ struct resource *res;
+ struct clk *clk;
+ struct device_node *np;
+- const unsigned char *mac_addr = NULL;
+ int err;
+
+ printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
+@@ -1435,12 +1434,8 @@ static int pxa168_eth_probe(struct platf
+
+ INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
+
+- if (pdev->dev.of_node)
+- mac_addr = of_get_mac_address(pdev->dev.of_node);
+-
+- if (!IS_ERR_OR_NULL(mac_addr)) {
+- ether_addr_copy(dev->dev_addr, mac_addr);
+- } else {
++ err = of_get_mac_address(pdev->dev.of_node, dev->dev_addr);
++ if (err) {
+ /* try reading the mac address, if set by the bootloader */
+ pxa168_eth_get_mac_address(dev, dev->dev_addr);
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+--- a/drivers/net/ethernet/marvell/sky2.c
++++ b/drivers/net/ethernet/marvell/sky2.c
+@@ -4725,7 +4725,7 @@ static struct net_device *sky2_init_netd
+ {
+ struct sky2_port *sky2;
+ struct net_device *dev = alloc_etherdev(sizeof(*sky2));
+- const void *iap;
++ int ret;
+
+ if (!dev)
+ return NULL;
+@@ -4795,10 +4795,8 @@ static struct net_device *sky2_init_netd
+ * 1) from device tree data
+ * 2) from internal registers set by bootloader
+ */
+- iap = of_get_mac_address(hw->pdev->dev.of_node);
+- if (!IS_ERR(iap))
+- ether_addr_copy(dev->dev_addr, iap);
+- else
++ ret = of_get_mac_address(hw->pdev->dev.of_node, dev->dev_addr);
++ if (ret)
+ memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8,
+ ETH_ALEN);
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2580,14 +2580,11 @@ static int __init mtk_init(struct net_de
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+- const char *mac_addr;
++ int ret;
+
+- mac_addr = of_get_mac_address(mac->of_node);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(dev->dev_addr, mac_addr);
+-
+- /* If the mac address is invalid, use random mac address */
+- if (!is_valid_ether_addr(dev->dev_addr)) {
++ ret = of_get_mac_address(mac->of_node, dev->dev_addr);
++ if (ret) {
++ /* If the mac address is invalid, use random mac address */
+ eth_hw_addr_random(dev);
+ dev_err(eth->dev, "generated random MAC address %pM\n",
+ dev->dev_addr);
+--- a/drivers/net/ethernet/micrel/ks8851_common.c
++++ b/drivers/net/ethernet/micrel/ks8851_common.c
+@@ -194,11 +194,10 @@ static void ks8851_read_mac_addr(struct
+ static void ks8851_init_mac(struct ks8851_net *ks, struct device_node *np)
+ {
+ struct net_device *dev = ks->netdev;
+- const u8 *mac_addr;
++ int ret;
+
+- mac_addr = of_get_mac_address(np);
+- if (!IS_ERR(mac_addr)) {
+- ether_addr_copy(dev->dev_addr, mac_addr);
++ ret = of_get_mac_address(np, dev->dev_addr);
++ if (!ret) {
+ ks8851_write_mac_addr(dev);
+ return;
+ }
+--- a/drivers/net/ethernet/microchip/lan743x_main.c
++++ b/drivers/net/ethernet/microchip/lan743x_main.c
+@@ -2831,7 +2831,6 @@ static int lan743x_pcidev_probe(struct p
+ {
+ struct lan743x_adapter *adapter = NULL;
+ struct net_device *netdev = NULL;
+- const void *mac_addr;
+ int ret = -ENODEV;
+
+ netdev = devm_alloc_etherdev(&pdev->dev,
+@@ -2848,9 +2847,7 @@ static int lan743x_pcidev_probe(struct p
+ NETIF_MSG_IFDOWN | NETIF_MSG_TX_QUEUED;
+ netdev->max_mtu = LAN743X_MAX_FRAME_SIZE;
+
+- mac_addr = of_get_mac_address(pdev->dev.of_node);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(adapter->mac_address, mac_addr);
++ of_get_mac_address(pdev->dev.of_node, adapter->mac_address);
+
+ ret = lan743x_pci_init(adapter, pdev);
+ if (ret)
+--- a/drivers/net/ethernet/nxp/lpc_eth.c
++++ b/drivers/net/ethernet/nxp/lpc_eth.c
+@@ -1347,9 +1347,7 @@ static int lpc_eth_drv_probe(struct plat
+ __lpc_get_mac(pldat, ndev->dev_addr);
+
+ if (!is_valid_ether_addr(ndev->dev_addr)) {
+- const char *macaddr = of_get_mac_address(np);
+- if (!IS_ERR(macaddr))
+- ether_addr_copy(ndev->dev_addr, macaddr);
++ of_get_mac_address(np, ndev->dev_addr);
+ }
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ eth_hw_addr_random(ndev);
+--- a/drivers/net/ethernet/qualcomm/qca_spi.c
++++ b/drivers/net/ethernet/qualcomm/qca_spi.c
+@@ -885,7 +885,7 @@ qca_spi_probe(struct spi_device *spi)
+ struct net_device *qcaspi_devs = NULL;
+ u8 legacy_mode = 0;
+ u16 signature;
+- const char *mac;
++ int ret;
+
+ if (!spi->dev.of_node) {
+ dev_err(&spi->dev, "Missing device tree\n");
+@@ -962,12 +962,8 @@ qca_spi_probe(struct spi_device *spi)
+
+ spi_set_drvdata(spi, qcaspi_devs);
+
+- mac = of_get_mac_address(spi->dev.of_node);
+-
+- if (!IS_ERR(mac))
+- ether_addr_copy(qca->net_dev->dev_addr, mac);
+-
+- if (!is_valid_ether_addr(qca->net_dev->dev_addr)) {
++ ret = of_get_mac_address(spi->dev.of_node, qca->net_dev->dev_addr);
++ if (ret) {
+ eth_hw_addr_random(qca->net_dev);
+ dev_info(&spi->dev, "Using random MAC address: %pM\n",
+ qca->net_dev->dev_addr);
+--- a/drivers/net/ethernet/qualcomm/qca_uart.c
++++ b/drivers/net/ethernet/qualcomm/qca_uart.c
+@@ -323,7 +323,6 @@ static int qca_uart_probe(struct serdev_
+ {
+ struct net_device *qcauart_dev = alloc_etherdev(sizeof(struct qcauart));
+ struct qcauart *qca;
+- const char *mac;
+ u32 speed = 115200;
+ int ret;
+
+@@ -348,12 +347,8 @@ static int qca_uart_probe(struct serdev_
+
+ of_property_read_u32(serdev->dev.of_node, "current-speed", &speed);
+
+- mac = of_get_mac_address(serdev->dev.of_node);
+-
+- if (!IS_ERR(mac))
+- ether_addr_copy(qca->net_dev->dev_addr, mac);
+-
+- if (!is_valid_ether_addr(qca->net_dev->dev_addr)) {
++ ret = of_get_mac_address(serdev->dev.of_node, qca->net_dev->dev_addr);
++ if (ret) {
+ eth_hw_addr_random(qca->net_dev);
+ dev_info(&serdev->dev, "Using random MAC address: %pM\n",
+ qca->net_dev->dev_addr);
+--- a/drivers/net/ethernet/renesas/ravb_main.c
++++ b/drivers/net/ethernet/renesas/ravb_main.c
+@@ -109,11 +109,13 @@ static void ravb_set_buffer_align(struct
+ * Ethernet AVB device doesn't have ROM for MAC address.
+ * This function gets the MAC address that was used by a bootloader.
+ */
+-static void ravb_read_mac_address(struct net_device *ndev, const u8 *mac)
++static void ravb_read_mac_address(struct device_node *np,
++ struct net_device *ndev)
+ {
+- if (!IS_ERR(mac)) {
+- ether_addr_copy(ndev->dev_addr, mac);
+- } else {
++ int ret;
++
++ ret = of_get_mac_address(np, ndev->dev_addr);
++ if (ret) {
+ u32 mahr = ravb_read(ndev, MAHR);
+ u32 malr = ravb_read(ndev, MALR);
+
+@@ -2189,7 +2191,7 @@ static int ravb_probe(struct platform_de
+ priv->msg_enable = RAVB_DEF_MSG_ENABLE;
+
+ /* Read and set MAC address */
+- ravb_read_mac_address(ndev, of_get_mac_address(np));
++ ravb_read_mac_address(np, ndev);
+ if (!is_valid_ether_addr(ndev->dev_addr)) {
+ dev_warn(&pdev->dev,
+ "no valid MAC address supplied, using a random one\n");
+--- a/drivers/net/ethernet/renesas/sh_eth.c
++++ b/drivers/net/ethernet/renesas/sh_eth.c
+@@ -3145,7 +3145,6 @@ static struct sh_eth_plat_data *sh_eth_p
+ struct device_node *np = dev->of_node;
+ struct sh_eth_plat_data *pdata;
+ phy_interface_t interface;
+- const char *mac_addr;
+ int ret;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+@@ -3157,9 +3156,7 @@ static struct sh_eth_plat_data *sh_eth_p
+ return NULL;
+ pdata->phy_interface = interface;
+
+- mac_addr = of_get_mac_address(np);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(pdata->mac_addr, mac_addr);
++ of_get_mac_address(np, pdata->mac_addr);
+
+ pdata->no_ether_link =
+ of_property_read_bool(np, "renesas,no-ether-link");
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c
+@@ -25,8 +25,7 @@
+
+ #ifdef CONFIG_OF
+ static int sxgbe_probe_config_dt(struct platform_device *pdev,
+- struct sxgbe_plat_data *plat,
+- const char **mac)
++ struct sxgbe_plat_data *plat)
+ {
+ struct device_node *np = pdev->dev.of_node;
+ struct sxgbe_dma_cfg *dma_cfg;
+@@ -35,7 +34,6 @@ static int sxgbe_probe_config_dt(struct
+ if (!np)
+ return -ENODEV;
+
+- *mac = of_get_mac_address(np);
+ err = of_get_phy_mode(np, &plat->interface);
+ if (err && err != -ENODEV)
+ return err;
+@@ -63,8 +61,7 @@ static int sxgbe_probe_config_dt(struct
+ }
+ #else
+ static int sxgbe_probe_config_dt(struct platform_device *pdev,
+- struct sxgbe_plat_data *plat,
+- const char **mac)
++ struct sxgbe_plat_data *plat)
+ {
+ return -ENOSYS;
+ }
+@@ -85,7 +82,6 @@ static int sxgbe_platform_probe(struct p
+ void __iomem *addr;
+ struct sxgbe_priv_data *priv = NULL;
+ struct sxgbe_plat_data *plat_dat = NULL;
+- const char *mac = NULL;
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct device_node *node = dev->of_node;
+
+@@ -101,7 +97,7 @@ static int sxgbe_platform_probe(struct p
+ if (!plat_dat)
+ return -ENOMEM;
+
+- ret = sxgbe_probe_config_dt(pdev, plat_dat, &mac);
++ ret = sxgbe_probe_config_dt(pdev, plat_dat);
+ if (ret) {
+ pr_err("%s: main dt probe failed\n", __func__);
+ return ret;
+@@ -122,8 +118,7 @@ static int sxgbe_platform_probe(struct p
+ }
+
+ /* Get MAC address if available (DT) */
+- if (!IS_ERR_OR_NULL(mac))
+- ether_addr_copy(priv->dev->dev_addr, mac);
++ of_get_mac_address(node, priv->dev->dev_addr);
+
+ /* Get the TX/RX IRQ numbers */
+ for (i = 0, chan = 1; i < SXGBE_TX_QUEUES; i++) {
+--- a/drivers/net/ethernet/socionext/sni_ave.c
++++ b/drivers/net/ethernet/socionext/sni_ave.c
+@@ -1559,7 +1559,6 @@ static int ave_probe(struct platform_dev
+ struct ave_private *priv;
+ struct net_device *ndev;
+ struct device_node *np;
+- const void *mac_addr;
+ void __iomem *base;
+ const char *name;
+ int i, irq, ret;
+@@ -1600,12 +1599,9 @@ static int ave_probe(struct platform_dev
+
+ ndev->max_mtu = AVE_MAX_ETHFRAME - (ETH_HLEN + ETH_FCS_LEN);
+
+- mac_addr = of_get_mac_address(np);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(ndev->dev_addr, mac_addr);
+-
+- /* if the mac address is invalid, use random mac address */
+- if (!is_valid_ether_addr(ndev->dev_addr)) {
++ ret = of_get_mac_address(np, ndev->dev_addr);
++ if (ret) {
++ /* if the mac address is invalid, use random mac address */
+ eth_hw_addr_random(ndev);
+ dev_warn(dev, "Using random MAC address: %pM\n",
+ ndev->dev_addr);
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c
+@@ -115,7 +115,7 @@ static int anarion_dwmac_probe(struct pl
+ if (IS_ERR(gmac))
+ return PTR_ERR(gmac);
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c
+@@ -444,7 +444,7 @@ static int dwc_eth_dwmac_probe(struct pl
+ if (IS_ERR(stmmac_res.addr))
+ return PTR_ERR(stmmac_res.addr);
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c
+@@ -27,7 +27,7 @@ static int dwmac_generic_probe(struct pl
+ return ret;
+
+ if (pdev->dev.of_node) {
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat)) {
+ dev_err(&pdev->dev, "dt configuration failed\n");
+ return PTR_ERR(plat_dat);
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c
+@@ -226,7 +226,7 @@ static int imx_dwmac_probe(struct platfo
+ if (!dwmac)
+ return -ENOMEM;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c
+@@ -88,7 +88,7 @@ static int intel_eth_plat_probe(struct p
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat)) {
+ dev_err(&pdev->dev, "dt configuration failed\n");
+ return PTR_ERR(plat_dat);
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
+@@ -255,7 +255,7 @@ static int ipq806x_gmac_probe(struct pla
+ if (val)
+ return val;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c
+@@ -37,7 +37,7 @@ static int lpc18xx_dwmac_probe(struct pl
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
+@@ -407,7 +407,7 @@ static int mediatek_dwmac_probe(struct p
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c
+@@ -52,7 +52,7 @@ static int meson6_dwmac_probe(struct pla
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
+@@ -372,7 +372,7 @@ static int meson8b_dwmac_probe(struct pl
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c
+@@ -118,7 +118,7 @@ static int oxnas_dwmac_probe(struct plat
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+@@ -461,7 +461,7 @@ static int qcom_ethqos_probe(struct plat
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat)) {
+ dev_err(&pdev->dev, "dt configuration failed\n");
+ return PTR_ERR(plat_dat);
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
+@@ -1392,7 +1392,7 @@ static int rk_gmac_probe(struct platform
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+@@ -398,7 +398,7 @@ static int socfpga_dwmac_probe(struct pl
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
+@@ -325,7 +325,7 @@ static int sti_dwmac_probe(struct platfo
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
+@@ -371,7 +371,7 @@ static int stm32_dwmac_probe(struct plat
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+@@ -1202,7 +1202,7 @@ static int sun8i_dwmac_probe(struct plat
+ if (ret)
+ return -EINVAL;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
+@@ -108,7 +108,7 @@ static int sun7i_gmac_probe(struct platf
+ if (ret)
+ return ret;
+
+- plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
++ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+@@ -25,7 +25,7 @@
+
+ struct stmmac_resources {
+ void __iomem *addr;
+- const char *mac;
++ u8 mac[ETH_ALEN];
+ int wol_irq;
+ int lpi_irq;
+ int irq;
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -4988,7 +4988,7 @@ int stmmac_dvr_probe(struct device *devi
+ priv->wol_irq = res->wol_irq;
+ priv->lpi_irq = res->lpi_irq;
+
+- if (!IS_ERR_OR_NULL(res->mac))
++ if (!is_zero_ether_addr(res->mac))
+ memcpy(priv->dev->dev_addr, res->mac, ETH_ALEN);
+
+ dev_set_drvdata(device, priv->dev);
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+@@ -395,7 +395,7 @@ static int stmmac_of_get_mac_mode(struct
+ * set some private fields that will be used by the main at runtime.
+ */
+ struct plat_stmmacenet_data *
+-stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
++stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
+ {
+ struct device_node *np = pdev->dev.of_node;
+ struct plat_stmmacenet_data *plat;
+@@ -407,12 +407,12 @@ stmmac_probe_config_dt(struct platform_d
+ if (!plat)
+ return ERR_PTR(-ENOMEM);
+
+- *mac = of_get_mac_address(np);
+- if (IS_ERR(*mac)) {
+- if (PTR_ERR(*mac) == -EPROBE_DEFER)
+- return ERR_CAST(*mac);
++ rc = of_get_mac_address(np, mac);
++ if (rc) {
++ if (rc == -EPROBE_DEFER)
++ return ERR_PTR(rc);
+
+- *mac = NULL;
++ eth_zero_addr(mac);
+ }
+
+ phy_mode = device_get_phy_mode(&pdev->dev);
+@@ -644,7 +644,7 @@ void stmmac_remove_config_dt(struct plat
+ }
+ #else
+ struct plat_stmmacenet_data *
+-stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
++stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
+ {
+ return ERR_PTR(-EINVAL);
+ }
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
+@@ -12,7 +12,7 @@
+ #include "stmmac.h"
+
+ struct plat_stmmacenet_data *
+-stmmac_probe_config_dt(struct platform_device *pdev, const char **mac);
++stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac);
+ void stmmac_remove_config_dt(struct platform_device *pdev,
+ struct plat_stmmacenet_data *plat);
+
+--- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c
++++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c
+@@ -1741,7 +1741,6 @@ static int am65_cpsw_nuss_init_slave_por
+
+ for_each_child_of_node(node, port_np) {
+ struct am65_cpsw_port *port;
+- const void *mac_addr;
+ u32 port_id;
+
+ /* it is not a slave port node, continue */
+@@ -1820,15 +1819,15 @@ static int am65_cpsw_nuss_init_slave_por
+ return ret;
+ }
+
+- mac_addr = of_get_mac_address(port_np);
+- if (!IS_ERR(mac_addr)) {
+- ether_addr_copy(port->slave.mac_addr, mac_addr);
+- } else if (am65_cpsw_am654_get_efuse_macid(port_np,
+- port->port_id,
+- port->slave.mac_addr) ||
+- !is_valid_ether_addr(port->slave.mac_addr)) {
+- random_ether_addr(port->slave.mac_addr);
+- dev_err(dev, "Use random MAC address\n");
++ ret = of_get_mac_address(port_np, port->slave.mac_addr);
++ if (ret) {
++ am65_cpsw_am654_get_efuse_macid(port_np,
++ port->port_id,
++ port->slave.mac_addr);
++ if (!is_valid_ether_addr(port->slave.mac_addr)) {
++ random_ether_addr(port->slave.mac_addr);
++ dev_err(dev, "Use random MAC address\n");
++ }
+ }
+ }
+ of_node_put(node);
+--- a/drivers/net/ethernet/ti/cpsw.c
++++ b/drivers/net/ethernet/ti/cpsw.c
+@@ -1306,7 +1306,6 @@ static int cpsw_probe_dt(struct cpsw_pla
+
+ for_each_available_child_of_node(node, slave_node) {
+ struct cpsw_slave_data *slave_data = data->slave_data + i;
+- const void *mac_addr = NULL;
+ int lenp;
+ const __be32 *parp;
+
+@@ -1378,10 +1377,8 @@ static int cpsw_probe_dt(struct cpsw_pla
+ }
+
+ no_phy_slave:
+- mac_addr = of_get_mac_address(slave_node);
+- if (!IS_ERR(mac_addr)) {
+- ether_addr_copy(slave_data->mac_addr, mac_addr);
+- } else {
++ ret = of_get_mac_address(slave_node, slave_data->mac_addr);
++ if (ret) {
+ ret = ti_cm_get_macid(&pdev->dev, i,
+ slave_data->mac_addr);
+ if (ret)
+--- a/drivers/net/ethernet/ti/cpsw_new.c
++++ b/drivers/net/ethernet/ti/cpsw_new.c
+@@ -1267,7 +1267,6 @@ static int cpsw_probe_dt(struct cpsw_com
+
+ for_each_child_of_node(tmp_node, port_np) {
+ struct cpsw_slave_data *slave_data;
+- const void *mac_addr;
+ u32 port_id;
+
+ ret = of_property_read_u32(port_np, "reg", &port_id);
+@@ -1326,10 +1325,8 @@ static int cpsw_probe_dt(struct cpsw_com
+ goto err_node_put;
+ }
+
+- mac_addr = of_get_mac_address(port_np);
+- if (!IS_ERR(mac_addr)) {
+- ether_addr_copy(slave_data->mac_addr, mac_addr);
+- } else {
++ ret = of_get_mac_address(port_np, slave_data->mac_addr);
++ if (ret) {
+ ret = ti_cm_get_macid(dev, port_id - 1,
+ slave_data->mac_addr);
+ if (ret)
+--- a/drivers/net/ethernet/ti/davinci_emac.c
++++ b/drivers/net/ethernet/ti/davinci_emac.c
+@@ -1699,7 +1699,6 @@ davinci_emac_of_get_pdata(struct platfor
+ const struct of_device_id *match;
+ const struct emac_platform_data *auxdata;
+ struct emac_platform_data *pdata = NULL;
+- const u8 *mac_addr;
+
+ if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
+ return dev_get_platdata(&pdev->dev);
+@@ -1711,11 +1710,8 @@ davinci_emac_of_get_pdata(struct platfor
+ np = pdev->dev.of_node;
+ pdata->version = EMAC_VERSION_2;
+
+- if (!is_valid_ether_addr(pdata->mac_addr)) {
+- mac_addr = of_get_mac_address(np);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(pdata->mac_addr, mac_addr);
+- }
++ if (!is_valid_ether_addr(pdata->mac_addr))
++ of_get_mac_address(np, pdata->mac_addr);
+
+ of_property_read_u32(np, "ti,davinci-ctrl-reg-offset",
+ &pdata->ctrl_reg_offset);
+--- a/drivers/net/ethernet/ti/netcp_core.c
++++ b/drivers/net/ethernet/ti/netcp_core.c
+@@ -1966,7 +1966,6 @@ static int netcp_create_interface(struct
+ struct resource res;
+ void __iomem *efuse = NULL;
+ u32 efuse_mac = 0;
+- const void *mac_addr;
+ u8 efuse_mac_addr[6];
+ u32 temp[2];
+ int ret = 0;
+@@ -2036,10 +2035,8 @@ static int netcp_create_interface(struct
+ devm_iounmap(dev, efuse);
+ devm_release_mem_region(dev, res.start, size);
+ } else {
+- mac_addr = of_get_mac_address(node_interface);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(ndev->dev_addr, mac_addr);
+- else
++ ret = of_get_mac_address(node_interface, ndev->dev_addr);
++ if (ret)
+ eth_random_addr(ndev->dev_addr);
+ }
+
+--- a/drivers/net/ethernet/wiznet/w5100-spi.c
++++ b/drivers/net/ethernet/wiznet/w5100-spi.c
+@@ -423,8 +423,14 @@ static int w5100_spi_probe(struct spi_de
+ const struct of_device_id *of_id;
+ const struct w5100_ops *ops;
+ kernel_ulong_t driver_data;
++ const void *mac = NULL;
++ u8 tmpmac[ETH_ALEN];
+ int priv_size;
+- const void *mac = of_get_mac_address(spi->dev.of_node);
++ int ret;
++
++ ret = of_get_mac_address(spi->dev.of_node, tmpmac);
++ if (!ret)
++ mac = tmpmac;
+
+ if (spi->dev.of_node) {
+ of_id = of_match_device(w5100_of_match, &spi->dev);
+--- a/drivers/net/ethernet/wiznet/w5100.c
++++ b/drivers/net/ethernet/wiznet/w5100.c
+@@ -1159,7 +1159,7 @@ int w5100_probe(struct device *dev, cons
+ INIT_WORK(&priv->setrx_work, w5100_setrx_work);
+ INIT_WORK(&priv->restart_work, w5100_restart_work);
+
+- if (!IS_ERR_OR_NULL(mac_addr))
++ if (mac_addr)
+ memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
+ else
+ eth_hw_addr_random(ndev);
+--- a/drivers/net/ethernet/xilinx/ll_temac_main.c
++++ b/drivers/net/ethernet/xilinx/ll_temac_main.c
+@@ -438,7 +438,7 @@ static void temac_do_set_mac_address(str
+
+ static int temac_init_mac_address(struct net_device *ndev, const void *address)
+ {
+- ether_addr_copy(ndev->dev_addr, address);
++ memcpy(ndev->dev_addr, address, ETH_ALEN);
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ eth_hw_addr_random(ndev);
+ temac_do_set_mac_address(ndev);
+@@ -1370,7 +1370,7 @@ static int temac_probe(struct platform_d
+ struct device_node *temac_np = dev_of_node(&pdev->dev), *dma_np;
+ struct temac_local *lp;
+ struct net_device *ndev;
+- const void *addr;
++ u8 addr[ETH_ALEN];
+ __be32 *p;
+ bool little_endian;
+ int rc = 0;
+@@ -1563,8 +1563,8 @@ static int temac_probe(struct platform_d
+
+ if (temac_np) {
+ /* Retrieve the MAC address */
+- addr = of_get_mac_address(temac_np);
+- if (IS_ERR(addr)) {
++ rc = of_get_mac_address(temac_np, addr);
++ if (rc) {
+ dev_err(&pdev->dev, "could not find MAC address\n");
+ return -ENODEV;
+ }
+--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
++++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+@@ -1831,8 +1831,8 @@ static int axienet_probe(struct platform
+ struct device_node *np;
+ struct axienet_local *lp;
+ struct net_device *ndev;
+- const void *mac_addr;
+ struct resource *ethres;
++ u8 mac_addr[ETH_ALEN];
+ int addr_width = 32;
+ u32 value;
+
+@@ -2032,13 +2032,14 @@ static int axienet_probe(struct platform
+ dev_info(&pdev->dev, "Ethernet core IRQ not defined\n");
+
+ /* Retrieve the MAC address */
+- mac_addr = of_get_mac_address(pdev->dev.of_node);
+- if (IS_ERR(mac_addr)) {
+- dev_warn(&pdev->dev, "could not find MAC address property: %ld\n",
+- PTR_ERR(mac_addr));
+- mac_addr = NULL;
++ ret = of_get_mac_address(pdev->dev.of_node, mac_addr);
++ if (!ret) {
++ axienet_set_mac_address(ndev, mac_addr);
++ } else {
++ dev_warn(&pdev->dev, "could not find MAC address property: %d\n",
++ ret);
++ axienet_set_mac_address(ndev, NULL);
+ }
+- axienet_set_mac_address(ndev, mac_addr);
+
+ lp->coalesce_count_rx = XAXIDMA_DFT_RX_THRESHOLD;
+ lp->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD;
+--- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
++++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
+@@ -1113,7 +1113,6 @@ static int xemaclite_of_probe(struct pla
+ struct net_device *ndev = NULL;
+ struct net_local *lp = NULL;
+ struct device *dev = &ofdev->dev;
+- const void *mac_address;
+
+ int rc = 0;
+
+@@ -1155,12 +1154,9 @@ static int xemaclite_of_probe(struct pla
+ lp->next_rx_buf_to_use = 0x0;
+ lp->tx_ping_pong = get_bool(ofdev, "xlnx,tx-ping-pong");
+ lp->rx_ping_pong = get_bool(ofdev, "xlnx,rx-ping-pong");
+- mac_address = of_get_mac_address(ofdev->dev.of_node);
+
+- if (!IS_ERR(mac_address)) {
+- /* Set the MAC address. */
+- ether_addr_copy(ndev->dev_addr, mac_address);
+- } else {
++ rc = of_get_mac_address(ofdev->dev.of_node, ndev->dev_addr);
++ if (rc) {
+ dev_warn(dev, "No MAC address found, using random\n");
+ eth_hw_addr_random(ndev);
+ }
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -618,7 +618,6 @@ static int ath9k_of_init(struct ath_soft
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ enum ath_bus_type bus_type = common->bus_ops->ath_bus_type;
+- const char *mac;
+ char eeprom_name[100];
+ int ret;
+
+@@ -641,9 +640,7 @@ static int ath9k_of_init(struct ath_soft
+ ah->ah_flags |= AH_NO_EEP_SWAP;
+ }
+
+- mac = of_get_mac_address(np);
+- if (!IS_ERR(mac))
+- ether_addr_copy(common->macaddr, mac);
++ of_get_mac_address(np, common->macaddr);
+
+ return 0;
+ }
+--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
++++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
+@@ -90,15 +90,9 @@ out_put_node:
+ void
+ mt76_eeprom_override(struct mt76_dev *dev)
+ {
+-#ifdef CONFIG_OF
+ struct device_node *np = dev->dev->of_node;
+- const u8 *mac = NULL;
+
+- if (np)
+- mac = of_get_mac_address(np);
+- if (!IS_ERR_OR_NULL(mac))
+- ether_addr_copy(dev->macaddr, mac);
+-#endif
++ of_get_mac_address(np, dev->macaddr);
+
+ if (!is_valid_ether_addr(dev->macaddr)) {
+ eth_random_addr(dev->macaddr);
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+@@ -990,11 +990,7 @@ static void rt2x00lib_rate(struct ieee80
+
+ void rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr)
+ {
+- const char *mac_addr;
+-
+- mac_addr = of_get_mac_address(rt2x00dev->dev->of_node);
+- if (!IS_ERR(mac_addr))
+- ether_addr_copy(eeprom_mac_addr, mac_addr);
++ of_get_mac_address(rt2x00dev->dev->of_node, eeprom_mac_addr);
+
+ if (!is_valid_ether_addr(eeprom_mac_addr)) {
+ eth_random_addr(eeprom_mac_addr);
+--- a/drivers/of/of_net.c
++++ b/drivers/of/of_net.c
+@@ -45,37 +45,29 @@ int of_get_phy_mode(struct device_node *
+ }
+ EXPORT_SYMBOL_GPL(of_get_phy_mode);
+
+-static const void *of_get_mac_addr(struct device_node *np, const char *name)
++static int of_get_mac_addr(struct device_node *np, const char *name, u8 *addr)
+ {
+ struct property *pp = of_find_property(np, name, NULL);
+
+- if (pp && pp->length == ETH_ALEN && is_valid_ether_addr(pp->value))
+- return pp->value;
+- return NULL;
++ if (pp && pp->length == ETH_ALEN && is_valid_ether_addr(pp->value)) {
++ memcpy(addr, pp->value, ETH_ALEN);
++ return 0;
++ }
++ return -ENODEV;
+ }
+
+-static const void *of_get_mac_addr_nvmem(struct device_node *np)
++static int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr)
+ {
+- int ret;
+- const void *mac;
+- u8 nvmem_mac[ETH_ALEN];
+ struct platform_device *pdev = of_find_device_by_node(np);
++ int ret;
+
+ if (!pdev)
+- return ERR_PTR(-ENODEV);
++ return -ENODEV;
+
+- ret = nvmem_get_mac_address(&pdev->dev, &nvmem_mac);
+- if (ret) {
+- put_device(&pdev->dev);
+- return ERR_PTR(ret);
+- }
+-
+- mac = devm_kmemdup(&pdev->dev, nvmem_mac, ETH_ALEN, GFP_KERNEL);
++ ret = nvmem_get_mac_address(&pdev->dev, addr);
+ put_device(&pdev->dev);
+- if (!mac)
+- return ERR_PTR(-ENOMEM);
+
+- return mac;
++ return ret;
+ }
+
+ /**
+@@ -98,24 +90,27 @@ static const void *of_get_mac_addr_nvmem
+ * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists
+ * but is all zeros.
+ *
+- * Return: Will be a valid pointer on success and ERR_PTR in case of error.
++ * Return: 0 on success and errno in case of error.
+ */
+-const void *of_get_mac_address(struct device_node *np)
++int of_get_mac_address(struct device_node *np, u8 *addr)
+ {
+- const void *addr;
+-
+- addr = of_get_mac_addr(np, "mac-address");
+- if (addr)
+- return addr;
++ int ret;
+
+- addr = of_get_mac_addr(np, "local-mac-address");
+- if (addr)
+- return addr;
++ if (!np)
++ return -ENODEV;
+
+- addr = of_get_mac_addr(np, "address");
+- if (addr)
+- return addr;
++ ret = of_get_mac_addr(np, "mac-address", addr);
++ if (!ret)
++ return 0;
++
++ ret = of_get_mac_addr(np, "local-mac-address", addr);
++ if (!ret)
++ return 0;
++
++ ret = of_get_mac_addr(np, "address", addr);
++ if (!ret)
++ return 0;
+
+- return of_get_mac_addr_nvmem(np);
++ return of_get_mac_addr_nvmem(np, addr);
+ }
+ EXPORT_SYMBOL(of_get_mac_address);
+--- a/drivers/staging/octeon/ethernet.c
++++ b/drivers/staging/octeon/ethernet.c
+@@ -407,14 +407,10 @@ static int cvm_oct_common_set_mac_addres
+ int cvm_oct_common_init(struct net_device *dev)
+ {
+ struct octeon_ethernet *priv = netdev_priv(dev);
+- const u8 *mac = NULL;
++ int ret;
+
+- if (priv->of_node)
+- mac = of_get_mac_address(priv->of_node);
+-
+- if (!IS_ERR_OR_NULL(mac))
+- ether_addr_copy(dev->dev_addr, mac);
+- else
++ ret = of_get_mac_address(priv->of_node, dev->dev_addr);
++ if (ret)
+ eth_hw_addr_random(dev);
+
+ /*
+--- a/drivers/staging/wfx/main.c
++++ b/drivers/staging/wfx/main.c
+@@ -334,7 +334,6 @@ int wfx_probe(struct wfx_dev *wdev)
+ {
+ int i;
+ int err;
+- const void *macaddr;
+ struct gpio_desc *gpio_saved;
+
+ // During first part of boot, gpio_wakeup cannot yet been used. So
+@@ -423,9 +422,9 @@ int wfx_probe(struct wfx_dev *wdev)
+
+ for (i = 0; i < ARRAY_SIZE(wdev->addresses); i++) {
+ eth_zero_addr(wdev->addresses[i].addr);
+- macaddr = of_get_mac_address(wdev->dev->of_node);
+- if (!IS_ERR_OR_NULL(macaddr)) {
+- ether_addr_copy(wdev->addresses[i].addr, macaddr);
++ err = of_get_mac_address(wdev->dev->of_node,
++ wdev->addresses[i].addr);
++ if (!err) {
+ wdev->addresses[i].addr[ETH_ALEN - 1] += i;
+ } else {
+ ether_addr_copy(wdev->addresses[i].addr,
+--- a/include/linux/of_net.h
++++ b/include/linux/of_net.h
+@@ -13,7 +13,7 @@
+
+ struct net_device;
+ extern int of_get_phy_mode(struct device_node *np, phy_interface_t *interface);
+-extern const void *of_get_mac_address(struct device_node *np);
++extern int of_get_mac_address(struct device_node *np, u8 *mac);
+ extern struct net_device *of_find_net_device_by_node(struct device_node *np);
+ #else
+ static inline int of_get_phy_mode(struct device_node *np,
+@@ -22,9 +22,9 @@ static inline int of_get_phy_mode(struct
+ return -ENODEV;
+ }
+
+-static inline const void *of_get_mac_address(struct device_node *np)
++static inline int of_get_mac_address(struct device_node *np, u8 *mac)
+ {
+- return ERR_PTR(-ENODEV);
++ return -ENODEV;
+ }
+
+ static inline struct net_device *of_find_net_device_by_node(struct device_node *np)
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -208,7 +208,7 @@ struct dsa_port {
+ unsigned int index;
+ const char *name;
+ struct dsa_port *cpu_dp;
+- const char *mac;
++ u8 mac[ETH_ALEN];
+ struct device_node *dn;
+ unsigned int ageing_time;
+ bool vlan_filtering;
+--- a/net/dsa/dsa2.c
++++ b/net/dsa/dsa2.c
+@@ -288,7 +288,7 @@ static int dsa_port_setup(struct dsa_por
+
+ break;
+ case DSA_PORT_TYPE_USER:
+- dp->mac = of_get_mac_address(dp->dn);
++ of_get_mac_address(dp->dn, dp->mac);
+ err = dsa_slave_create(dp);
+ if (err)
+ break;
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1855,7 +1855,7 @@ int dsa_slave_create(struct dsa_port *po
+ slave_dev->hw_features |= NETIF_F_HW_TC;
+ slave_dev->features |= NETIF_F_LLTX;
+ slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
+- if (!IS_ERR_OR_NULL(port->mac))
++ if (!is_zero_ether_addr(port->mac))
+ ether_addr_copy(slave_dev->dev_addr, port->mac);
+ else
+ eth_hw_addr_inherit(slave_dev, master);
+--- a/net/ethernet/eth.c
++++ b/net/ethernet/eth.c
+@@ -506,13 +506,14 @@ unsigned char * __weak arch_get_platform
+
+ int eth_platform_get_mac_address(struct device *dev, u8 *mac_addr)
+ {
+- const unsigned char *addr = NULL;
++ unsigned char *addr;
++ int ret;
+
+- if (dev->of_node)
+- addr = of_get_mac_address(dev->of_node);
+- if (IS_ERR_OR_NULL(addr))
+- addr = arch_get_platform_mac_address();
++ ret = of_get_mac_address(dev->of_node, mac_addr);
++ if (!ret)
++ return 0;
+
++ addr = arch_get_platform_mac_address();
+ if (!addr)
+ return -ENODEV;
+
--- /dev/null
+From f10843e04a075202dbb39dfcee047e3a2fdf5a8d Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 12 Apr 2021 19:47:18 +0200
+Subject: of: net: fix of_get_mac_addr_nvmem() for non-platform devices
+
+of_get_mac_address() already supports fetching the MAC address by an
+nvmem provider. But until now, it was just working for platform devices.
+Esp. it was not working for DSA ports and PCI devices. It gets more
+common that PCI devices have a device tree binding since SoCs contain
+integrated root complexes.
+
+Use the nvmem of_* binding to fetch the nvmem cells by a struct
+device_node. We still have to try to read the cell by device first
+because there might be a nvmem_cell_lookup associated with that device.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/of/of_net.c | 35 ++++++++++++++++++++++++++++++-----
+ 1 file changed, 30 insertions(+), 5 deletions(-)
+
+--- a/drivers/of/of_net.c
++++ b/drivers/of/of_net.c
+@@ -11,6 +11,7 @@
+ #include <linux/phy.h>
+ #include <linux/export.h>
+ #include <linux/device.h>
++#include <linux/nvmem-consumer.h>
+
+ /**
+ * of_get_phy_mode - Get phy mode for given device_node
+@@ -59,15 +60,39 @@ static int of_get_mac_addr(struct device
+ static int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr)
+ {
+ struct platform_device *pdev = of_find_device_by_node(np);
++ struct nvmem_cell *cell;
++ const void *mac;
++ size_t len;
+ int ret;
+
+- if (!pdev)
+- return -ENODEV;
++ /* Try lookup by device first, there might be a nvmem_cell_lookup
++ * associated with a given device.
++ */
++ if (pdev) {
++ ret = nvmem_get_mac_address(&pdev->dev, addr);
++ put_device(&pdev->dev);
++ return ret;
++ }
++
++ cell = of_nvmem_cell_get(np, "mac-address");
++ if (IS_ERR(cell))
++ return PTR_ERR(cell);
++
++ mac = nvmem_cell_read(cell, &len);
++ nvmem_cell_put(cell);
++
++ if (IS_ERR(mac))
++ return PTR_ERR(mac);
++
++ if (len != ETH_ALEN || !is_valid_ether_addr(mac)) {
++ kfree(mac);
++ return -EINVAL;
++ }
+
+- ret = nvmem_get_mac_address(&pdev->dev, addr);
+- put_device(&pdev->dev);
++ memcpy(addr, mac, ETH_ALEN);
++ kfree(mac);
+
+- return ret;
++ return 0;
+ }
+
+ /**
--- /dev/null
+From 029497e66bdc762e001880e4c85a91f35a54b1e2 Mon Sep 17 00:00:00 2001
+From: Christian Lamparter <chunkeey@gmail.com>
+Date: Sun, 19 Sep 2021 13:57:25 +0200
+Subject: [PATCH] net: bgmac-bcma: handle deferred probe error due to
+ mac-address
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Due to the inclusion of nvmem handling into the mac-address getter
+function of_get_mac_address() by
+commit d01f449c008a ("of_net: add NVMEM support to of_get_mac_address")
+it is now possible to get a -EPROBE_DEFER return code. Which did cause
+bgmac to assign a random ethernet address.
+
+This exact issue happened on my Meraki MR32. The nvmem provider is
+an EEPROM (at24c64) which gets instantiated once the module
+driver is loaded... This happens once the filesystem becomes available.
+
+With this patch, bgmac_probe() will propagate the -EPROBE_DEFER error.
+Then the driver subsystem will reschedule the probe at a later time.
+
+Cc: Petr Štetiar <ynezz@true.cz>
+Cc: Michael Walle <michael@walle.cc>
+Fixes: d01f449c008a ("of_net: add NVMEM support to of_get_mac_address")
+Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/broadcom/bgmac-bcma.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+@@ -129,6 +129,8 @@ static int bgmac_probe(struct bcma_devic
+ bcma_set_drvdata(core, bgmac);
+
+ err = of_get_mac_address(bgmac->dev->of_node, bgmac->net_dev->dev_addr);
++ if (err == -EPROBE_DEFER)
++ return err;
+
+ /* If no MAC address assigned via device tree, check SPROM */
+ if (err) {
--- /dev/null
+From 763716a55cb1f480ffe1a9702e6b5d9ea1a80a24 Mon Sep 17 00:00:00 2001
+From: Matthew Hagan <mnhagan88@gmail.com>
+Date: Sat, 25 Sep 2021 11:36:27 +0000
+Subject: [PATCH] net: bgmac-platform: handle mac-address deferral
+
+This patch is a replication of Christian Lamparter's "net: bgmac-bcma:
+handle deferred probe error due to mac-address" patch for the
+bgmac-platform driver [1].
+
+As is the case with the bgmac-bcma driver, this change is to cover the
+scenario where the MAC address cannot yet be discovered due to reliance
+on an nvmem provider which is yet to be instantiated, resulting in a
+random address being assigned that has to be manually overridden.
+
+[1] https://lore.kernel.org/netdev/20210919115725.29064-1-chunkeey@gmail.com
+
+Signed-off-by: Matthew Hagan <mnhagan88@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/broadcom/bgmac-platform.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-platform.c
++++ b/drivers/net/ethernet/broadcom/bgmac-platform.c
+@@ -193,6 +193,9 @@ static int bgmac_probe(struct platform_d
+ bgmac->dma_dev = &pdev->dev;
+
+ ret = of_get_mac_address(np, bgmac->net_dev->dev_addr);
++ if (ret == -EPROBE_DEFER)
++ return ret;
++
+ if (ret)
+ dev_warn(&pdev->dev,
+ "MAC address not present in device tree\n");
--- /dev/null
+From b5375509184dc23d2b7fa0c5ed8763899ccc9674 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Sat, 2 Oct 2021 19:58:11 +0200
+Subject: [PATCH] net: bgmac: improve handling PHY
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+1. Use info from DT if available
+
+It allows describing for example a fixed link. It's more accurate than
+just guessing there may be one (depending on a chipset).
+
+2. Verify PHY ID before trying to connect PHY
+
+PHY addr 0x1e (30) is special in Broadcom routers and means a switch
+connected as MDIO devices instead of a real PHY. Don't try connecting to
+it.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/broadcom/bgmac-bcma.c | 33 ++++++++++++++--------
+ 1 file changed, 21 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+@@ -11,6 +11,7 @@
+ #include <linux/bcma/bcma.h>
+ #include <linux/brcmphy.h>
+ #include <linux/etherdevice.h>
++#include <linux/of_mdio.h>
+ #include <linux/of_net.h>
+ #include "bgmac.h"
+
+@@ -86,17 +87,28 @@ static int bcma_phy_connect(struct bgmac
+ struct phy_device *phy_dev;
+ char bus_id[MII_BUS_ID_SIZE + 3];
+
++ /* DT info should be the most accurate */
++ phy_dev = of_phy_get_and_connect(bgmac->net_dev, bgmac->dev->of_node,
++ bgmac_adjust_link);
++ if (phy_dev)
++ return 0;
++
+ /* Connect to the PHY */
+- snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, bgmac->mii_bus->id,
+- bgmac->phyaddr);
+- phy_dev = phy_connect(bgmac->net_dev, bus_id, bgmac_adjust_link,
+- PHY_INTERFACE_MODE_MII);
+- if (IS_ERR(phy_dev)) {
+- dev_err(bgmac->dev, "PHY connection failed\n");
+- return PTR_ERR(phy_dev);
++ if (bgmac->mii_bus && bgmac->phyaddr != BGMAC_PHY_NOREGS) {
++ snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, bgmac->mii_bus->id,
++ bgmac->phyaddr);
++ phy_dev = phy_connect(bgmac->net_dev, bus_id, bgmac_adjust_link,
++ PHY_INTERFACE_MODE_MII);
++ if (IS_ERR(phy_dev)) {
++ dev_err(bgmac->dev, "PHY connection failed\n");
++ return PTR_ERR(phy_dev);
++ }
++
++ return 0;
+ }
+
+- return 0;
++ /* Assume a fixed link to the switch port */
++ return bgmac_phy_connect_direct(bgmac);
+ }
+
+ static const struct bcma_device_id bgmac_bcma_tbl[] = {
+@@ -297,10 +309,7 @@ static int bgmac_probe(struct bcma_devic
+ bgmac->cco_ctl_maskset = bcma_bgmac_cco_ctl_maskset;
+ bgmac->get_bus_clock = bcma_bgmac_get_bus_clock;
+ bgmac->cmn_maskset32 = bcma_bgmac_cmn_maskset32;
+- if (bgmac->mii_bus)
+- bgmac->phy_connect = bcma_phy_connect;
+- else
+- bgmac->phy_connect = bgmac_phy_connect_direct;
++ bgmac->phy_connect = bcma_phy_connect;
+
+ err = bgmac_enet_probe(bgmac);
+ if (err)
--- /dev/null
+From 45c9d966688e7fad7f24bfc450547d91e4304d0b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Sat, 2 Oct 2021 19:58:12 +0200
+Subject: [PATCH] net: bgmac: support MDIO described in DT
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Check ethernet controller DT node for "mdio" subnode and use it with
+of_mdiobus_register() when present. That allows specifying MDIO and its
+PHY devices in a standard DT based way.
+
+This is required for BCM53573 SoC support. That family is sometimes
+called Northstar (by marketing?) but is quite different from it. It uses
+different CPU(s) and many different hw blocks.
+
+One of shared blocks in BCM53573 is Ethernet controller. Switch however
+is not SRAB accessible (as it Northstar) but is MDIO attached.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c
+@@ -10,6 +10,7 @@
+
+ #include <linux/bcma/bcma.h>
+ #include <linux/brcmphy.h>
++#include <linux/of_mdio.h>
+ #include "bgmac.h"
+
+ static bool bcma_mdio_wait_value(struct bcma_device *core, u16 reg, u32 mask,
+@@ -211,6 +212,7 @@ struct mii_bus *bcma_mdio_mii_register(s
+ {
+ struct bcma_device *core = bgmac->bcma.core;
+ struct mii_bus *mii_bus;
++ struct device_node *np;
+ int err;
+
+ mii_bus = mdiobus_alloc();
+@@ -229,7 +231,9 @@ struct mii_bus *bcma_mdio_mii_register(s
+ mii_bus->parent = &core->dev;
+ mii_bus->phy_mask = ~(1 << bgmac->phyaddr);
+
+- err = mdiobus_register(mii_bus);
++ np = of_get_child_by_name(core->dev.of_node, "mdio");
++
++ err = of_mdiobus_register(mii_bus, np);
+ if (err) {
+ dev_err(&core->dev, "Registration of mii bus failed\n");
+ goto err_free_bus;
--- /dev/null
+From 5d9e068402dcf7354cc8ee66c2152845306d2ccb Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 22:59:51 +0200
+Subject: [PATCH] net: dsa: qca8k: change simple print to dev variant
+
+Change pr_err and pr_warn to dev variant.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -701,7 +701,7 @@ qca8k_setup(struct dsa_switch *ds)
+
+ /* Make sure that port 0 is the cpu port */
+ if (!dsa_is_cpu_port(ds, 0)) {
+- pr_err("port 0 is not the CPU port\n");
++ dev_err(priv->dev, "port 0 is not the CPU port");
+ return -EINVAL;
+ }
+
+@@ -711,7 +711,7 @@ qca8k_setup(struct dsa_switch *ds)
+ priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
+ &qca8k_regmap_config);
+ if (IS_ERR(priv->regmap))
+- pr_warn("regmap initialization failed");
++ dev_warn(priv->dev, "regmap initialization failed");
+
+ ret = qca8k_setup_mdio_bus(priv);
+ if (ret)
--- /dev/null
+From 2ad255f2faaffb3af786031fba2e7955454b558a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 22:59:52 +0200
+Subject: [PATCH] net: dsa: qca8k: use iopoll macro for qca8k_busy_wait
+
+Use iopoll macro instead of while loop.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 23 +++++++++++------------
+ drivers/net/dsa/qca8k.h | 2 ++
+ 2 files changed, 13 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -262,21 +262,20 @@ static struct regmap_config qca8k_regmap
+ static int
+ qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+ {
+- unsigned long timeout;
++ u32 val;
++ int ret;
+
+- timeout = jiffies + msecs_to_jiffies(20);
++ ret = read_poll_timeout(qca8k_read, val, !(val & mask),
++ 0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
++ priv, reg);
+
+- /* loop until the busy flag has cleared */
+- do {
+- u32 val = qca8k_read(priv, reg);
+- int busy = val & mask;
++ /* Check if qca8k_read has failed for a different reason
++ * before returning -ETIMEDOUT
++ */
++ if (ret < 0 && val < 0)
++ return val;
+
+- if (!busy)
+- break;
+- cond_resched();
+- } while (!time_after_eq(jiffies, timeout));
+-
+- return time_after_eq(jiffies, timeout);
++ return ret;
+ }
+
+ static void
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -18,6 +18,8 @@
+ #define PHY_ID_QCA8337 0x004dd036
+ #define QCA8K_ID_QCA8337 0x13
+
++#define QCA8K_BUSY_WAIT_TIMEOUT 20
++
+ #define QCA8K_NUM_FDB_RECORDS 2048
+
+ #define QCA8K_CPU_PORT 0
--- /dev/null
+From 504bf65931824eda83494e5b5d75686e27ace03e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 22:59:53 +0200
+Subject: [PATCH] net: dsa: qca8k: improve qca8k read/write/rmw bus access
+
+Put bus in local variable to improve faster access to the mdio bus.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 29 ++++++++++++++++-------------
+ 1 file changed, 16 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -142,17 +142,18 @@ qca8k_set_page(struct mii_bus *bus, u16
+ static u32
+ qca8k_read(struct qca8k_priv *priv, u32 reg)
+ {
++ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ u32 val;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- qca8k_set_page(priv->bus, page);
+- val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
++ qca8k_set_page(bus, page);
++ val = qca8k_mii_read32(bus, 0x10 | r2, r1);
+
+- mutex_unlock(&priv->bus->mdio_lock);
++ mutex_unlock(&bus->mdio_lock);
+
+ return val;
+ }
+@@ -160,35 +161,37 @@ qca8k_read(struct qca8k_priv *priv, u32
+ static void
+ qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
+ {
++ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- qca8k_set_page(priv->bus, page);
+- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
++ qca8k_set_page(bus, page);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+
+- mutex_unlock(&priv->bus->mdio_lock);
++ mutex_unlock(&bus->mdio_lock);
+ }
+
+ static u32
+ qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 val)
+ {
++ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ u32 ret;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- qca8k_set_page(priv->bus, page);
+- ret = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
++ qca8k_set_page(bus, page);
++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1);
+ ret &= ~mask;
+ ret |= val;
+- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, ret);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, ret);
+
+- mutex_unlock(&priv->bus->mdio_lock);
++ mutex_unlock(&bus->mdio_lock);
+
+ return ret;
+ }
--- /dev/null
+From ba5707ec58cfb6853dff41c2aae72deb6a03d389 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 22:59:54 +0200
+Subject: [PATCH] net: dsa: qca8k: handle qca8k_set_page errors
+
+With a remote possibility, the set_page function can fail. Since this is
+a critical part of the write/read qca8k regs, propagate the error and
+terminate any read/write operation.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 33 ++++++++++++++++++++++++++-------
+ 1 file changed, 26 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -127,16 +127,23 @@ qca8k_mii_write32(struct mii_bus *bus, i
+ "failed to write qca8k 32bit register\n");
+ }
+
+-static void
++static int
+ qca8k_set_page(struct mii_bus *bus, u16 page)
+ {
++ int ret;
++
+ if (page == qca8k_current_page)
+- return;
++ return 0;
+
+- if (bus->write(bus, 0x18, 0, page) < 0)
++ ret = bus->write(bus, 0x18, 0, page);
++ if (ret < 0) {
+ dev_err_ratelimited(&bus->dev,
+ "failed to set qca8k page\n");
++ return ret;
++ }
++
+ qca8k_current_page = page;
++ return 0;
+ }
+
+ static u32
+@@ -150,11 +157,14 @@ qca8k_read(struct qca8k_priv *priv, u32
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- qca8k_set_page(bus, page);
++ val = qca8k_set_page(bus, page);
++ if (val < 0)
++ goto exit;
++
+ val = qca8k_mii_read32(bus, 0x10 | r2, r1);
+
++exit:
+ mutex_unlock(&bus->mdio_lock);
+-
+ return val;
+ }
+
+@@ -163,14 +173,19 @@ qca8k_write(struct qca8k_priv *priv, u32
+ {
+ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
++ int ret;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- qca8k_set_page(bus, page);
++ ret = qca8k_set_page(bus, page);
++ if (ret < 0)
++ goto exit;
++
+ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+
++exit:
+ mutex_unlock(&bus->mdio_lock);
+ }
+
+@@ -185,12 +200,16 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- qca8k_set_page(bus, page);
++ ret = qca8k_set_page(bus, page);
++ if (ret < 0)
++ goto exit;
++
+ ret = qca8k_mii_read32(bus, 0x10 | r2, r1);
+ ret &= ~mask;
+ ret |= val;
+ qca8k_mii_write32(bus, 0x10 | r2, r1, ret);
+
++exit:
+ mutex_unlock(&bus->mdio_lock);
+
+ return ret;
--- /dev/null
+From 028f5f8ef44fcf87a456772cbb9f0d90a0a22884 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 22:59:55 +0200
+Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_read operation
+
+qca8k_read can fail. Rework any user to handle error values and
+correctly return.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 73 ++++++++++++++++++++++++++++++++---------
+ 1 file changed, 58 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -231,8 +231,13 @@ static int
+ qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
++ int ret;
++
++ ret = qca8k_read(priv, reg);
++ if (ret < 0)
++ return ret;
+
+- *val = qca8k_read(priv, reg);
++ *val = ret;
+
+ return 0;
+ }
+@@ -300,15 +305,20 @@ qca8k_busy_wait(struct qca8k_priv *priv,
+ return ret;
+ }
+
+-static void
++static int
+ qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+ {
+- u32 reg[4];
++ u32 reg[4], val;
+ int i;
+
+ /* load the ARL table into an array */
+- for (i = 0; i < 4; i++)
+- reg[i] = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4));
++ for (i = 0; i < 4; i++) {
++ val = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4));
++ if (val < 0)
++ return val;
++
++ reg[i] = val;
++ }
+
+ /* vid - 83:72 */
+ fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M;
+@@ -323,6 +333,8 @@ qca8k_fdb_read(struct qca8k_priv *priv,
+ fdb->mac[3] = (reg[0] >> QCA8K_ATU_ADDR3_S) & 0xff;
+ fdb->mac[4] = (reg[0] >> QCA8K_ATU_ADDR4_S) & 0xff;
+ fdb->mac[5] = reg[0] & 0xff;
++
++ return 0;
+ }
+
+ static void
+@@ -374,6 +386,8 @@ qca8k_fdb_access(struct qca8k_priv *priv
+ /* Check for table full violation when adding an entry */
+ if (cmd == QCA8K_FDB_LOAD) {
+ reg = qca8k_read(priv, QCA8K_REG_ATU_FUNC);
++ if (reg < 0)
++ return reg;
+ if (reg & QCA8K_ATU_FUNC_FULL)
+ return -1;
+ }
+@@ -388,10 +402,10 @@ qca8k_fdb_next(struct qca8k_priv *priv,
+
+ qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging);
+ ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port);
+- if (ret >= 0)
+- qca8k_fdb_read(priv, fdb);
++ if (ret < 0)
++ return ret;
+
+- return ret;
++ return qca8k_fdb_read(priv, fdb);
+ }
+
+ static int
+@@ -449,6 +463,8 @@ qca8k_vlan_access(struct qca8k_priv *pri
+ /* Check for table full violation when adding an entry */
+ if (cmd == QCA8K_VLAN_LOAD) {
+ reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC1);
++ if (reg < 0)
++ return reg;
+ if (reg & QCA8K_VTU_FUNC1_FULL)
+ return -ENOMEM;
+ }
+@@ -475,6 +491,8 @@ qca8k_vlan_add(struct qca8k_priv *priv,
+ goto out;
+
+ reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0);
++ if (reg < 0)
++ return reg;
+ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
+ reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port));
+ if (untagged)
+@@ -506,6 +524,8 @@ qca8k_vlan_del(struct qca8k_priv *priv,
+ goto out;
+
+ reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0);
++ if (reg < 0)
++ return reg;
+ reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port));
+ reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT <<
+ QCA8K_VTU_FUNC0_EG_MODE_S(port);
+@@ -621,8 +641,11 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ QCA8K_MDIO_MASTER_BUSY))
+ return -ETIMEDOUT;
+
+- val = (qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL) &
+- QCA8K_MDIO_MASTER_DATA_MASK);
++ val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL);
++ if (val < 0)
++ return val;
++
++ val &= QCA8K_MDIO_MASTER_DATA_MASK;
+
+ return val;
+ }
+@@ -978,6 +1001,8 @@ qca8k_phylink_mac_link_state(struct dsa_
+ u32 reg;
+
+ reg = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port));
++ if (reg < 0)
++ return reg;
+
+ state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP);
+ state->an_complete = state->link;
+@@ -1078,18 +1103,26 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ const struct qca8k_mib_desc *mib;
+- u32 reg, i;
++ u32 reg, i, val;
+ u64 hi;
+
+ for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) {
+ mib = &ar8327_mib[i];
+ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
+
+- data[i] = qca8k_read(priv, reg);
++ val = qca8k_read(priv, reg);
++ if (val < 0)
++ continue;
++
+ if (mib->size == 2) {
+ hi = qca8k_read(priv, reg + 4);
+- data[i] |= hi << 32;
++ if (hi < 0)
++ continue;
+ }
++
++ data[i] = val;
++ if (mib->size == 2)
++ data[i] |= hi << 32;
+ }
+ }
+
+@@ -1107,18 +1140,25 @@ qca8k_set_mac_eee(struct dsa_switch *ds,
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
++ int ret = 0;
+ u32 reg;
+
+ mutex_lock(&priv->reg_mutex);
+ reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL);
++ if (reg < 0) {
++ ret = reg;
++ goto exit;
++ }
++
+ if (eee->eee_enabled)
+ reg |= lpi_en;
+ else
+ reg &= ~lpi_en;
+ qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
+- mutex_unlock(&priv->reg_mutex);
+
+- return 0;
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
+ }
+
+ static int
+@@ -1456,6 +1496,9 @@ qca8k_sw_probe(struct mdio_device *mdiod
+
+ /* read the switches ID register */
+ id = qca8k_read(priv, QCA8K_REG_MASK_CTRL);
++ if (id < 0)
++ return id;
++
+ id >>= QCA8K_MASK_CTRL_ID_S;
+ id &= QCA8K_MASK_CTRL_ID_M;
+ if (id != QCA8K_ID_QCA8337)
--- /dev/null
+From d7805757c75c76e9518fc1023a29f0c4eed5b581 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 22:59:56 +0200
+Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_write operation
+
+qca8k_write can fail. Rework any user to handle error values and
+correctly return.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 102 ++++++++++++++++++++++++++--------------
+ 1 file changed, 67 insertions(+), 35 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -168,7 +168,7 @@ exit:
+ return val;
+ }
+
+-static void
++static int
+ qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
+ {
+ struct mii_bus *bus = priv->bus;
+@@ -187,6 +187,7 @@ qca8k_write(struct qca8k_priv *priv, u32
+
+ exit:
+ mutex_unlock(&bus->mdio_lock);
++ return ret;
+ }
+
+ static u32
+@@ -247,9 +248,7 @@ qca8k_regmap_write(void *ctx, uint32_t r
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+
+- qca8k_write(priv, reg, val);
+-
+- return 0;
++ return qca8k_write(priv, reg, val);
+ }
+
+ static const struct regmap_range qca8k_readable_ranges[] = {
+@@ -367,6 +366,7 @@ static int
+ qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
+ {
+ u32 reg;
++ int ret;
+
+ /* Set the command and FDB index */
+ reg = QCA8K_ATU_FUNC_BUSY;
+@@ -377,7 +377,9 @@ qca8k_fdb_access(struct qca8k_priv *priv
+ }
+
+ /* Write the function register triggering the table access */
+- qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
++ if (ret)
++ return ret;
+
+ /* wait for completion */
+ if (qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY))
+@@ -447,6 +449,7 @@ static int
+ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
+ {
+ u32 reg;
++ int ret;
+
+ /* Set the command and VLAN index */
+ reg = QCA8K_VTU_FUNC1_BUSY;
+@@ -454,7 +457,9 @@ qca8k_vlan_access(struct qca8k_priv *pri
+ reg |= vid << QCA8K_VTU_FUNC1_VID_S;
+
+ /* Write the function register triggering the table access */
+- qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
++ if (ret)
++ return ret;
+
+ /* wait for completion */
+ if (qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY))
+@@ -502,7 +507,9 @@ qca8k_vlan_add(struct qca8k_priv *priv,
+ reg |= QCA8K_VTU_FUNC0_EG_MODE_TAG <<
+ QCA8K_VTU_FUNC0_EG_MODE_S(port);
+
+- qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++ if (ret)
++ return ret;
+ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+
+ out:
+@@ -545,7 +552,9 @@ qca8k_vlan_del(struct qca8k_priv *priv,
+ if (del) {
+ ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
+ } else {
+- qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++ if (ret)
++ return ret;
+ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+ }
+
+@@ -555,15 +564,20 @@ out:
+ return ret;
+ }
+
+-static void
++static int
+ qca8k_mib_init(struct qca8k_priv *priv)
+ {
++ int ret;
++
+ mutex_lock(&priv->reg_mutex);
+ qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
+ qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
+ qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+- qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
++
++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
++
+ mutex_unlock(&priv->reg_mutex);
++ return ret;
+ }
+
+ static void
+@@ -600,6 +614,7 @@ static int
+ qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data)
+ {
+ u32 phy, val;
++ int ret;
+
+ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+ return -EINVAL;
+@@ -613,7 +628,9 @@ qca8k_mdio_write(struct qca8k_priv *priv
+ QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
+ QCA8K_MDIO_MASTER_DATA(data);
+
+- qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
++ ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
++ if (ret)
++ return ret;
+
+ return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY);
+@@ -623,6 +640,7 @@ static int
+ qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum)
+ {
+ u32 phy, val;
++ int ret;
+
+ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+ return -EINVAL;
+@@ -635,7 +653,9 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+ QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+
+- qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
++ ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
++ if (ret)
++ return ret;
+
+ if (qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY))
+@@ -766,12 +786,18 @@ qca8k_setup(struct dsa_switch *ds)
+ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+
+ /* Enable MIB counters */
+- qca8k_mib_init(priv);
++ ret = qca8k_mib_init(priv);
++ if (ret)
++ dev_warn(priv->dev, "mib init failed");
+
+ /* Enable QCA header mode on the cpu port */
+- qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_CPU_PORT),
+- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
+- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_CPU_PORT),
++ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
++ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
++ if (ret) {
++ dev_err(priv->dev, "failed enabling QCA header mode");
++ return ret;
++ }
+
+ /* Disable forwarding by default on all ports */
+ for (i = 0; i < QCA8K_NUM_PORTS; i++)
+@@ -783,11 +809,13 @@ qca8k_setup(struct dsa_switch *ds)
+ qca8k_port_set_status(priv, i, 0);
+
+ /* Forward all unknown frames to CPU port for Linux processing */
+- qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
++ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
++ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
++ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
++ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
++ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
++ if (ret)
++ return ret;
+
+ /* Setup connection between CPU port & user ports */
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+@@ -815,16 +843,20 @@ qca8k_setup(struct dsa_switch *ds)
+ qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
+ 0xfff << shift,
+ QCA8K_PORT_VID_DEF << shift);
+- qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i),
+- QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
+- QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i),
++ QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
++ QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
++ if (ret)
++ return ret;
+ }
+ }
+
+ /* Setup our port MTUs to match power on defaults */
+ for (i = 0; i < QCA8K_NUM_PORTS; i++)
+ priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
+- qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
++ if (ret)
++ dev_warn(priv->dev, "failed setting MTU settings");
+
+ /* Flush the FDB table */
+ qca8k_fdb_flush(priv);
+@@ -1140,8 +1172,8 @@ qca8k_set_mac_eee(struct dsa_switch *ds,
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
+- int ret = 0;
+ u32 reg;
++ int ret;
+
+ mutex_lock(&priv->reg_mutex);
+ reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL);
+@@ -1154,7 +1186,7 @@ qca8k_set_mac_eee(struct dsa_switch *ds,
+ reg |= lpi_en;
+ else
+ reg &= ~lpi_en;
+- qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
+
+ exit:
+ mutex_unlock(&priv->reg_mutex);
+@@ -1284,9 +1316,7 @@ qca8k_port_change_mtu(struct dsa_switch
+ mtu = priv->port_mtu[i];
+
+ /* Include L2 header / FCS length */
+- qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN);
+-
+- return 0;
++ return qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN);
+ }
+
+ static int
--- /dev/null
+From aaf421425cbdec4eb6fd75a29e65c2867b0b7bbd Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 22:59:57 +0200
+Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_rmw operation
+
+qca8k_rmw can fail. Rework any user to handle error values and
+correctly return. Change qca8k_rmw to return the error code or 0 instead
+of the reg value. The reg returned by qca8k_rmw wasn't used anywhere,
+so this doesn't cause any functional change.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 133 +++++++++++++++++++++++++---------------
+ 1 file changed, 83 insertions(+), 50 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -190,12 +190,13 @@ exit:
+ return ret;
+ }
+
+-static u32
+-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 val)
++static int
++qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
+ {
+ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+- u32 ret;
++ u32 val;
++ int ret;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+@@ -205,10 +206,15 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r
+ if (ret < 0)
+ goto exit;
+
+- ret = qca8k_mii_read32(bus, 0x10 | r2, r1);
+- ret &= ~mask;
+- ret |= val;
+- qca8k_mii_write32(bus, 0x10 | r2, r1, ret);
++ val = qca8k_mii_read32(bus, 0x10 | r2, r1);
++ if (val < 0) {
++ ret = val;
++ goto exit;
++ }
++
++ val &= ~mask;
++ val |= write_val;
++ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+
+ exit:
+ mutex_unlock(&bus->mdio_lock);
+@@ -216,16 +222,16 @@ exit:
+ return ret;
+ }
+
+-static void
++static int
+ qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val)
+ {
+- qca8k_rmw(priv, reg, 0, val);
++ return qca8k_rmw(priv, reg, 0, val);
+ }
+
+-static void
++static int
+ qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val)
+ {
+- qca8k_rmw(priv, reg, val, 0);
++ return qca8k_rmw(priv, reg, val, 0);
+ }
+
+ static int
+@@ -570,12 +576,19 @@ qca8k_mib_init(struct qca8k_priv *priv)
+ int ret;
+
+ mutex_lock(&priv->reg_mutex);
+- qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
++ ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
++ if (ret)
++ goto exit;
++
+ qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
+- qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
++
++ ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
++ if (ret)
++ goto exit;
+
+ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
+
++exit:
+ mutex_unlock(&priv->reg_mutex);
+ return ret;
+ }
+@@ -747,9 +760,8 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ * a dt-overlay and driver reload changed the configuration
+ */
+
+- qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_EN);
+- return 0;
++ return qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_EN);
+ }
+
+ priv->ops.phy_read = qca8k_phy_read;
+@@ -782,8 +794,12 @@ qca8k_setup(struct dsa_switch *ds)
+ return ret;
+
+ /* Enable CPU Port */
+- qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
+- QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
++ ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
++ if (ret) {
++ dev_err(priv->dev, "failed enabling CPU port");
++ return ret;
++ }
+
+ /* Enable MIB counters */
+ ret = qca8k_mib_init(priv);
+@@ -800,9 +816,12 @@ qca8k_setup(struct dsa_switch *ds)
+ }
+
+ /* Disable forwarding by default on all ports */
+- for (i = 0; i < QCA8K_NUM_PORTS; i++)
+- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_MEMBER, 0);
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_MEMBER, 0);
++ if (ret)
++ return ret;
++ }
+
+ /* Disable MAC by default on all ports */
+ for (i = 1; i < QCA8K_NUM_PORTS; i++)
+@@ -821,28 +840,37 @@ qca8k_setup(struct dsa_switch *ds)
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+ /* CPU port gets connected to all user ports of the switch */
+ if (dsa_is_cpu_port(ds, i)) {
+- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT),
+- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT),
++ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
++ if (ret)
++ return ret;
+ }
+
+ /* Individual user ports get connected to CPU port only */
+ if (dsa_is_user_port(ds, i)) {
+ int shift = 16 * (i % 2);
+
+- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_MEMBER,
+- BIT(QCA8K_CPU_PORT));
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_MEMBER,
++ BIT(QCA8K_CPU_PORT));
++ if (ret)
++ return ret;
+
+ /* Enable ARP Auto-learning by default */
+- qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_LEARN);
++ ret = qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_LEARN);
++ if (ret)
++ return ret;
+
+ /* For port based vlans to work we need to set the
+ * default egress vid
+ */
+- qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
+- 0xfff << shift,
+- QCA8K_PORT_VID_DEF << shift);
++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
++ 0xfff << shift,
++ QCA8K_PORT_VID_DEF << shift);
++ if (ret)
++ return ret;
++
+ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i),
+ QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
+ QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
+@@ -1234,7 +1262,7 @@ qca8k_port_bridge_join(struct dsa_switch
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ int port_mask = BIT(QCA8K_CPU_PORT);
+- int i;
++ int i, ret;
+
+ for (i = 1; i < QCA8K_NUM_PORTS; i++) {
+ if (dsa_to_port(ds, i)->bridge_dev != br)
+@@ -1242,17 +1270,20 @@ qca8k_port_bridge_join(struct dsa_switch
+ /* Add this port to the portvlan mask of the other ports
+ * in the bridge
+ */
+- qca8k_reg_set(priv,
+- QCA8K_PORT_LOOKUP_CTRL(i),
+- BIT(port));
++ ret = qca8k_reg_set(priv,
++ QCA8K_PORT_LOOKUP_CTRL(i),
++ BIT(port));
++ if (ret)
++ return ret;
+ if (i != port)
+ port_mask |= BIT(i);
+ }
++
+ /* Add all other ports to this ports portvlan mask */
+- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_MEMBER, port_mask);
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_MEMBER, port_mask);
+
+- return 0;
++ return ret;
+ }
+
+ static void
--- /dev/null
+From b7c818d194927bdc60ed15db55bb8654496a36b7 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 22:59:58 +0200
+Subject: [PATCH] net: dsa: qca8k: handle error from qca8k_busy_wait
+
+Propagate errors from qca8k_busy_wait instead of hardcoding return
+value.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 21 +++++++++++++--------
+ 1 file changed, 13 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -388,8 +388,9 @@ qca8k_fdb_access(struct qca8k_priv *priv
+ return ret;
+
+ /* wait for completion */
+- if (qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY))
+- return -1;
++ ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY);
++ if (ret)
++ return ret;
+
+ /* Check for table full violation when adding an entry */
+ if (cmd == QCA8K_FDB_LOAD) {
+@@ -468,8 +469,9 @@ qca8k_vlan_access(struct qca8k_priv *pri
+ return ret;
+
+ /* wait for completion */
+- if (qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY))
+- return -ETIMEDOUT;
++ ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
++ if (ret)
++ return ret;
+
+ /* Check for table full violation when adding an entry */
+ if (cmd == QCA8K_VLAN_LOAD) {
+@@ -580,7 +582,9 @@ qca8k_mib_init(struct qca8k_priv *priv)
+ if (ret)
+ goto exit;
+
+- qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
++ if (ret)
++ goto exit;
+
+ ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+ if (ret)
+@@ -670,9 +674,10 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ if (ret)
+ return ret;
+
+- if (qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_BUSY))
+- return -ETIMEDOUT;
++ ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_BUSY);
++ if (ret)
++ return ret;
+
+ val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL);
+ if (val < 0)
--- /dev/null
+From 6e82a457e06252b59102486767539cc9c2aba60b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 22:59:59 +0200
+Subject: [PATCH] net: dsa: qca8k: add support for qca8327 switch
+
+qca8327 switch is a low tier version of the more recent qca8337.
+It does share the same regs used by the qca8k driver and can be
+supported with minimal change.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 23 ++++++++++++++++++++---
+ drivers/net/dsa/qca8k.h | 6 ++++++
+ 2 files changed, 26 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1533,6 +1533,7 @@ static const struct dsa_switch_ops qca8k
+ static int
+ qca8k_sw_probe(struct mdio_device *mdiodev)
+ {
++ const struct qca8k_match_data *data;
+ struct qca8k_priv *priv;
+ u32 id;
+
+@@ -1560,6 +1561,11 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
+ }
+
++ /* get the switches ID from the compatible */
++ data = of_device_get_match_data(&mdiodev->dev);
++ if (!data)
++ return -ENODEV;
++
+ /* read the switches ID register */
+ id = qca8k_read(priv, QCA8K_REG_MASK_CTRL);
+ if (id < 0)
+@@ -1567,8 +1573,10 @@ qca8k_sw_probe(struct mdio_device *mdiod
+
+ id >>= QCA8K_MASK_CTRL_ID_S;
+ id &= QCA8K_MASK_CTRL_ID_M;
+- if (id != QCA8K_ID_QCA8337)
++ if (id != data->id) {
++ dev_err(&mdiodev->dev, "Switch id detected %x but expected %x", id, data->id);
+ return -ENODEV;
++ }
+
+ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
+ if (!priv->ds)
+@@ -1634,9 +1642,18 @@ static int qca8k_resume(struct device *d
+ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+ qca8k_suspend, qca8k_resume);
+
++static const struct qca8k_match_data qca832x = {
++ .id = QCA8K_ID_QCA8327,
++};
++
++static const struct qca8k_match_data qca833x = {
++ .id = QCA8K_ID_QCA8337,
++};
++
+ static const struct of_device_id qca8k_of_match[] = {
+- { .compatible = "qca,qca8334" },
+- { .compatible = "qca,qca8337" },
++ { .compatible = "qca,qca8327", .data = &qca832x },
++ { .compatible = "qca,qca8334", .data = &qca833x },
++ { .compatible = "qca,qca8337", .data = &qca833x },
+ { /* sentinel */ },
+ };
+
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -15,6 +15,8 @@
+ #define QCA8K_NUM_PORTS 7
+ #define QCA8K_MAX_MTU 9000
+
++#define PHY_ID_QCA8327 0x004dd034
++#define QCA8K_ID_QCA8327 0x12
+ #define PHY_ID_QCA8337 0x004dd036
+ #define QCA8K_ID_QCA8337 0x13
+
+@@ -213,6 +215,10 @@ struct ar8xxx_port_status {
+ int enabled;
+ };
+
++struct qca8k_match_data {
++ u8 id;
++};
++
+ struct qca8k_priv {
+ struct regmap *regmap;
+ struct mii_bus *bus;
--- /dev/null
+From 227a9ffc1bc77037339530607fe129af3824620e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:00 +0200
+Subject: [PATCH] devicetree: net: dsa: qca8k: Document new compatible qca8327
+
+Add support for qca8327 in the compatible list.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Acked-by: Rob Herring <robh@kernel.org>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -3,6 +3,7 @@
+ Required properties:
+
+ - compatible: should be one of:
++ "qca,qca8327"
+ "qca,qca8334"
+ "qca,qca8337"
+
--- /dev/null
+From 83a3ceb39b2495171aabe9446271b94c678354f3 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:01 +0200
+Subject: [PATCH] net: dsa: qca8k: add priority tweak to qca8337 switch
+
+The port 5 of the qca8337 have some problem in flood condition. The
+original legacy driver had some specific buffer and priority settings
+for the different port suggested by the QCA switch team. Add this
+missing settings to improve switch stability under load condition.
+The packet priority tweak is only needed for the qca8337 switch and
+other qca8k switch are not affected.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 47 +++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 25 ++++++++++++++++++++++
+ 2 files changed, 72 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -779,6 +779,7 @@ qca8k_setup(struct dsa_switch *ds)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ int ret, i;
++ u32 mask;
+
+ /* Make sure that port 0 is the cpu port */
+ if (!dsa_is_cpu_port(ds, 0)) {
+@@ -884,6 +885,51 @@ qca8k_setup(struct dsa_switch *ds)
+ }
+ }
+
++ /* The port 5 of the qca8337 have some problem in flood condition. The
++ * original legacy driver had some specific buffer and priority settings
++ * for the different port suggested by the QCA switch team. Add this
++ * missing settings to improve switch stability under load condition.
++ * This problem is limited to qca8337 and other qca8k switch are not affected.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8337) {
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ switch (i) {
++ /* The 2 CPU port and port 5 requires some different
++ * priority than any other ports.
++ */
++ case 0:
++ case 5:
++ case 6:
++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) |
++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e);
++ break;
++ default:
++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
++ }
++ qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask);
++
++ mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) |
++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_WRED_EN;
++ qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i),
++ QCA8K_PORT_HOL_CTRL1_ING_BUF |
++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_WRED_EN,
++ mask);
++ }
++ }
++
+ /* Setup our port MTUs to match power on defaults */
+ for (i = 0; i < QCA8K_NUM_PORTS; i++)
+ priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
+@@ -1578,6 +1624,7 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ return -ENODEV;
+ }
+
++ priv->switch_id = id;
+ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
+ if (!priv->ds)
+ return -ENOMEM;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -168,6 +168,30 @@
+ #define QCA8K_PORT_LOOKUP_STATE GENMASK(18, 16)
+ #define QCA8K_PORT_LOOKUP_LEARN BIT(20)
+
++#define QCA8K_REG_PORT_HOL_CTRL0(_i) (0x970 + (_i) * 0x8)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF GENMASK(3, 0)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) ((x) << 0)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF GENMASK(7, 4)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) ((x) << 4)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF GENMASK(11, 8)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) ((x) << 8)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF GENMASK(15, 12)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) ((x) << 12)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF GENMASK(19, 16)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) ((x) << 16)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF GENMASK(23, 20)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) ((x) << 20)
++#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF GENMASK(29, 24)
++#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) ((x) << 24)
++
++#define QCA8K_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8)
++#define QCA8K_PORT_HOL_CTRL1_ING_BUF GENMASK(3, 0)
++#define QCA8K_PORT_HOL_CTRL1_ING(x) ((x) << 0)
++#define QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN BIT(6)
++#define QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN BIT(7)
++#define QCA8K_PORT_HOL_CTRL1_WRED_EN BIT(8)
++#define QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16)
++
+ /* Pkt edit registers */
+ #define QCA8K_EGRESS_VLAN(x) (0x0c70 + (4 * (x / 2)))
+
+@@ -220,6 +244,7 @@ struct qca8k_match_data {
+ };
+
+ struct qca8k_priv {
++ u8 switch_id;
+ struct regmap *regmap;
+ struct mii_bus *bus;
+ struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
--- /dev/null
+From 5bf9ff3b9fb5ecb67a1a3517b26db3a00f2a2f11 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:02 +0200
+Subject: [PATCH] net: dsa: qca8k: limit port5 delay to qca8337
+
+Limit port5 rx delay to qca8337. This is taken from the legacy QSDK code
+that limits the rx delay on port5 to only this particular switch version,
+on other switch only the tx and rx delay for port0 are needed.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1003,8 +1003,10 @@ qca8k_phylink_mac_config(struct dsa_swit
+ QCA8K_PORT_PAD_RGMII_EN |
+ QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) |
+ QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY));
+- qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
++ /* QCA8337 requires to set rgmii rx delay */
++ if (priv->switch_id == QCA8K_ID_QCA8337)
++ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
--- /dev/null
+From 0fc57e4b5e39461fc0a54aae0afe4241363a7267 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:03 +0200
+Subject: [PATCH] net: dsa: qca8k: add GLOBAL_FC settings needed for qca8327
+
+Switch qca8327 needs special settings for the GLOBAL_FC_THRES regs.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 10 ++++++++++
+ drivers/net/dsa/qca8k.h | 6 ++++++
+ 2 files changed, 16 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -930,6 +930,16 @@ qca8k_setup(struct dsa_switch *ds)
+ }
+ }
+
++ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
++ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496);
++ qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH,
++ QCA8K_GLOBAL_FC_GOL_XON_THRES_S |
++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S,
++ mask);
++ }
++
+ /* Setup our port MTUs to match power on defaults */
+ for (i = 0; i < QCA8K_NUM_PORTS; i++)
+ priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -168,6 +168,12 @@
+ #define QCA8K_PORT_LOOKUP_STATE GENMASK(18, 16)
+ #define QCA8K_PORT_LOOKUP_LEARN BIT(20)
+
++#define QCA8K_REG_GLOBAL_FC_THRESH 0x800
++#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) ((x) << 16)
++#define QCA8K_GLOBAL_FC_GOL_XON_THRES_S GENMASK(24, 16)
++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) ((x) << 0)
++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S GENMASK(8, 0)
++
+ #define QCA8K_REG_PORT_HOL_CTRL0(_i) (0x970 + (_i) * 0x8)
+ #define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF GENMASK(3, 0)
+ #define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) ((x) << 0)
--- /dev/null
+From 95ffeaf18b3bb90eeef52cbf7d79ccc9d0345ff5 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:04 +0200
+Subject: [PATCH] net: dsa: qca8k: add support for switch rev
+
+qca8k internal phy driver require some special debug value to be set
+based on the switch revision. Rework the switch id read function to
+also read the chip revision.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 53 ++++++++++++++++++++++++++---------------
+ drivers/net/dsa/qca8k.h | 7 ++++--
+ 2 files changed, 39 insertions(+), 21 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1588,12 +1588,40 @@ static const struct dsa_switch_ops qca8k
+ .phylink_mac_link_up = qca8k_phylink_mac_link_up,
+ };
+
++static int qca8k_read_switch_id(struct qca8k_priv *priv)
++{
++ const struct qca8k_match_data *data;
++ u32 val;
++ u8 id;
++
++ /* get the switches ID from the compatible */
++ data = of_device_get_match_data(priv->dev);
++ if (!data)
++ return -ENODEV;
++
++ val = qca8k_read(priv, QCA8K_REG_MASK_CTRL);
++ if (val < 0)
++ return -ENODEV;
++
++ id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK);
++ if (id != data->id) {
++ dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
++ return -ENODEV;
++ }
++
++ priv->switch_id = id;
++
++ /* Save revision to communicate to the internal PHY driver */
++ priv->switch_revision = (val & QCA8K_MASK_CTRL_REV_ID_MASK);
++
++ return 0;
++}
++
+ static int
+ qca8k_sw_probe(struct mdio_device *mdiodev)
+ {
+- const struct qca8k_match_data *data;
+ struct qca8k_priv *priv;
+- u32 id;
++ int ret;
+
+ /* allocate the private data struct so that we can probe the switches
+ * ID register
+@@ -1619,24 +1647,11 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
+ }
+
+- /* get the switches ID from the compatible */
+- data = of_device_get_match_data(&mdiodev->dev);
+- if (!data)
+- return -ENODEV;
++ /* Check the detected switch id */
++ ret = qca8k_read_switch_id(priv);
++ if (ret)
++ return ret;
+
+- /* read the switches ID register */
+- id = qca8k_read(priv, QCA8K_REG_MASK_CTRL);
+- if (id < 0)
+- return id;
+-
+- id >>= QCA8K_MASK_CTRL_ID_S;
+- id &= QCA8K_MASK_CTRL_ID_M;
+- if (id != data->id) {
+- dev_err(&mdiodev->dev, "Switch id detected %x but expected %x", id, data->id);
+- return -ENODEV;
+- }
+-
+- priv->switch_id = id;
+ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
+ if (!priv->ds)
+ return -ENOMEM;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -30,8 +30,10 @@
+
+ /* Global control registers */
+ #define QCA8K_REG_MASK_CTRL 0x000
+-#define QCA8K_MASK_CTRL_ID_M 0xff
+-#define QCA8K_MASK_CTRL_ID_S 8
++#define QCA8K_MASK_CTRL_REV_ID_MASK GENMASK(7, 0)
++#define QCA8K_MASK_CTRL_REV_ID(x) ((x) >> 0)
++#define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8)
++#define QCA8K_MASK_CTRL_DEVICE_ID(x) ((x) >> 8)
+ #define QCA8K_REG_PORT0_PAD_CTRL 0x004
+ #define QCA8K_REG_PORT5_PAD_CTRL 0x008
+ #define QCA8K_REG_PORT6_PAD_CTRL 0x00c
+@@ -251,6 +253,7 @@ struct qca8k_match_data {
+
+ struct qca8k_priv {
+ u8 switch_id;
++ u8 switch_revision;
+ struct regmap *regmap;
+ struct mii_bus *bus;
+ struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
--- /dev/null
+From 1ee0591a1093c2448642c33433483e9260275f7b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:05 +0200
+Subject: [PATCH] net: dsa: qca8k: add ethernet-ports fallback to
+ setup_mdio_bus
+
+Dsa now also supports ethernet-ports. Add this new binding as a fallback
+if the ports node can't be found.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -719,6 +719,9 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+
+ ports = of_get_child_by_name(priv->dev->of_node, "ports");
+ if (!ports)
++ ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports");
++
++ if (!ports)
+ return -EINVAL;
+
+ for_each_available_child_of_node(ports, port) {
--- /dev/null
+From e4b9977cee1583da38a6e9118078bb728aaccf7b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:06 +0200
+Subject: [PATCH] net: dsa: qca8k: make rgmii delay configurable
+
+The legacy qsdk code used a different delay instead of the max value.
+Qsdk use 1 ns for rx and 2 ns for tx. Make these values configurable
+using the standard rx/tx-internal-delay-ps ethernet binding and apply
+qsdk values by default. The connected gmac doesn't add any delay so no
+additional delay is added to tx/rx.
+On this switch the delay is actually in ns so value should be in the
+1000 order. Any value converted from ps to ns by dividing it by 1000
+as the switch max value for delay is 3ns.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 82 ++++++++++++++++++++++++++++++++++++++++-
+ drivers/net/dsa/qca8k.h | 11 +++---
+ 2 files changed, 86 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -778,6 +778,68 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ }
+
+ static int
++qca8k_setup_of_rgmii_delay(struct qca8k_priv *priv)
++{
++ struct device_node *port_dn;
++ phy_interface_t mode;
++ struct dsa_port *dp;
++ u32 val;
++
++ /* CPU port is already checked */
++ dp = dsa_to_port(priv->ds, 0);
++
++ port_dn = dp->dn;
++
++ /* Check if port 0 is set to the correct type */
++ of_get_phy_mode(port_dn, &mode);
++ if (mode != PHY_INTERFACE_MODE_RGMII_ID &&
++ mode != PHY_INTERFACE_MODE_RGMII_RXID &&
++ mode != PHY_INTERFACE_MODE_RGMII_TXID) {
++ return 0;
++ }
++
++ switch (mode) {
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ if (of_property_read_u32(port_dn, "rx-internal-delay-ps", &val))
++ val = 2;
++ else
++ /* Switch regs accept value in ns, convert ps to ns */
++ val = val / 1000;
++
++ if (val > QCA8K_MAX_DELAY) {
++ dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
++ val = 3;
++ }
++
++ priv->rgmii_rx_delay = val;
++ /* Stop here if we need to check only for rx delay */
++ if (mode != PHY_INTERFACE_MODE_RGMII_ID)
++ break;
++
++ fallthrough;
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ if (of_property_read_u32(port_dn, "tx-internal-delay-ps", &val))
++ val = 1;
++ else
++ /* Switch regs accept value in ns, convert ps to ns */
++ val = val / 1000;
++
++ if (val > QCA8K_MAX_DELAY) {
++ dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
++ val = 3;
++ }
++
++ priv->rgmii_tx_delay = val;
++ break;
++ default:
++ return 0;
++ }
++
++ return 0;
++}
++
++static int
+ qca8k_setup(struct dsa_switch *ds)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+@@ -802,6 +864,10 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
++ ret = qca8k_setup_of_rgmii_delay(priv);
++ if (ret)
++ return ret;
++
+ /* Enable CPU Port */
+ ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
+ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+@@ -970,6 +1036,8 @@ qca8k_phylink_mac_config(struct dsa_swit
+ case 0: /* 1st CPU port */
+ if (state->interface != PHY_INTERFACE_MODE_RGMII &&
+ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+ state->interface != PHY_INTERFACE_MODE_SGMII)
+ return;
+
+@@ -985,6 +1053,8 @@ qca8k_phylink_mac_config(struct dsa_swit
+ case 6: /* 2nd CPU port / external PHY */
+ if (state->interface != PHY_INTERFACE_MODE_RGMII &&
+ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+ state->interface != PHY_INTERFACE_MODE_SGMII &&
+ state->interface != PHY_INTERFACE_MODE_1000BASEX)
+ return;
+@@ -1008,14 +1078,18 @@ qca8k_phylink_mac_config(struct dsa_swit
+ qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
+ /* RGMII_ID needs internal delay. This is enabled through
+ * PORT5_PAD_CTRL for all ports, rather than individual port
+ * registers
+ */
+ qca8k_write(priv, reg,
+ QCA8K_PORT_PAD_RGMII_EN |
+- QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY));
++ QCA8K_PORT_PAD_RGMII_TX_DELAY(priv->rgmii_tx_delay) |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY(priv->rgmii_rx_delay) |
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+ /* QCA8337 requires to set rgmii rx delay */
+ if (priv->switch_id == QCA8K_ID_QCA8337)
+ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
+@@ -1073,6 +1147,8 @@ qca8k_phylink_validate(struct dsa_switch
+ if (state->interface != PHY_INTERFACE_MODE_NA &&
+ state->interface != PHY_INTERFACE_MODE_RGMII &&
+ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+ state->interface != PHY_INTERFACE_MODE_SGMII)
+ goto unsupported;
+ break;
+@@ -1090,6 +1166,8 @@ qca8k_phylink_validate(struct dsa_switch
+ if (state->interface != PHY_INTERFACE_MODE_NA &&
+ state->interface != PHY_INTERFACE_MODE_RGMII &&
+ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+ state->interface != PHY_INTERFACE_MODE_SGMII &&
+ state->interface != PHY_INTERFACE_MODE_1000BASEX)
+ goto unsupported;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -38,12 +38,11 @@
+ #define QCA8K_REG_PORT5_PAD_CTRL 0x008
+ #define QCA8K_REG_PORT6_PAD_CTRL 0x00c
+ #define QCA8K_PORT_PAD_RGMII_EN BIT(26)
+-#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) \
+- ((0x8 + (x & 0x3)) << 22)
+-#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) \
+- ((0x10 + (x & 0x3)) << 20)
+-#define QCA8K_MAX_DELAY 3
++#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) ((x) << 22)
++#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) ((x) << 20)
++#define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
+ #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
++#define QCA8K_MAX_DELAY 3
+ #define QCA8K_PORT_PAD_SGMII_EN BIT(7)
+ #define QCA8K_REG_PWS 0x010
+ #define QCA8K_PWS_SERDES_AEN_DIS BIT(7)
+@@ -254,6 +253,8 @@ struct qca8k_match_data {
+ struct qca8k_priv {
+ u8 switch_id;
+ u8 switch_revision;
++ u8 rgmii_tx_delay;
++ u8 rgmii_rx_delay;
+ struct regmap *regmap;
+ struct mii_bus *bus;
+ struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
--- /dev/null
+From 63c33bbfeb6842a956a0eb12901e28eb335bdb18 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:07 +0200
+Subject: [PATCH] net: dsa: qca8k: clear MASTER_EN after phy read/write
+
+Clear MDIO_MASTER_EN bit from MDIO_MASTER_CTRL after read/write
+operation. The MDIO_MASTER_EN bit is not reset after read/write
+operation and the next operation can be wrongly interpreted by the
+switch as a mdio operation. This cause a production of wrong/garbage
+data from the switch and underfined bheavior. (random port drop,
+unplugged port flagged with link up, wrong port speed)
+Also on driver remove the MASTER_CTRL can be left set and cause the
+malfunction of any next driver using the mdio device.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -649,8 +649,14 @@ qca8k_mdio_write(struct qca8k_priv *priv
+ if (ret)
+ return ret;
+
+- return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_BUSY);
++ ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_BUSY);
++
++ /* even if the busy_wait timeouts try to clear the MASTER_EN */
++ qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_EN);
++
++ return ret;
+ }
+
+ static int
+@@ -685,6 +691,10 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+
+ val &= QCA8K_MDIO_MASTER_DATA_MASK;
+
++ /* even if the busy_wait timeouts try to clear the MASTER_EN */
++ qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_EN);
++
+ return val;
+ }
+
--- /dev/null
+From 60df02b6ea4581d72eb7a3ab7204504a54059b72 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:08 +0200
+Subject: [PATCH] net: dsa: qca8k: dsa: qca8k: protect MASTER busy_wait with
+ mdio mutex
+
+MDIO_MASTER operation have a dedicated busy wait that is not protected
+by the mdio mutex. This can cause situation where the MASTER operation
+is done and a normal operation is executed between the MASTER read/write
+and the MASTER busy_wait. Rework the qca8k_mdio_read/write function to
+address this issue by binding the lock for the whole MASTER operation
+and not only the mdio read/write common operation.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 68 +++++++++++++++++++++++++++++++++--------
+ 1 file changed, 55 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -628,8 +628,31 @@ qca8k_port_to_phy(int port)
+ }
+
+ static int
++qca8k_mdio_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
++{
++ u16 r1, r2, page;
++ u32 val;
++ int ret;
++
++ qca8k_split_addr(reg, &r1, &r2, &page);
++
++ ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0,
++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
++ priv->bus, 0x10 | r2, r1);
++
++ /* Check if qca8k_read has failed for a different reason
++ * before returnting -ETIMEDOUT
++ */
++ if (ret < 0 && val < 0)
++ return val;
++
++ return ret;
++}
++
++static int
+ qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data)
+ {
++ u16 r1, r2, page;
+ u32 phy, val;
+ int ret;
+
+@@ -645,12 +668,21 @@ qca8k_mdio_write(struct qca8k_priv *priv
+ QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
+ QCA8K_MDIO_MASTER_DATA(data);
+
+- ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
++
++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ ret = qca8k_set_page(priv->bus, page);
+ if (ret)
+- return ret;
++ goto exit;
++
++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
+
+- ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_BUSY);
++ ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_BUSY);
++
++exit:
++ mutex_unlock(&priv->bus->mdio_lock);
+
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+ qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+@@ -662,6 +694,7 @@ qca8k_mdio_write(struct qca8k_priv *priv
+ static int
+ qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum)
+ {
++ u16 r1, r2, page;
+ u32 phy, val;
+ int ret;
+
+@@ -676,21 +709,30 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+ QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+
+- ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
+- if (ret)
+- return ret;
++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
++
++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_BUSY);
++ ret = qca8k_set_page(priv->bus, page);
+ if (ret)
+- return ret;
++ goto exit;
+
+- val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL);
+- if (val < 0)
+- return val;
++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
++
++ ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_BUSY);
++ if (ret)
++ goto exit;
+
++ val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
+ val &= QCA8K_MDIO_MASTER_DATA_MASK;
+
++exit:
++ mutex_unlock(&priv->bus->mdio_lock);
++
++ if (val >= 0)
++ val &= QCA8K_MDIO_MASTER_DATA_MASK;
++
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+ qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_EN);
--- /dev/null
+From 617960d72e93de0f3fa52407e2d39e8c43e73b0a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:09 +0200
+Subject: [PATCH] net: dsa: qca8k: enlarge mdio delay and timeout
+
+The witch require some extra delay after setting page or the next
+read/write can use still use the old page. Add a delay after the
+set_page function to address this as it's done in QSDK legacy driver.
+Some timeouts were notice with VLAN and phy function, enlarge the
+mdio busy wait timeout to fix these problems.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 1 +
+ drivers/net/dsa/qca8k.h | 2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -143,6 +143,7 @@ qca8k_set_page(struct mii_bus *bus, u16
+ }
+
+ qca8k_current_page = page;
++ usleep_range(1000, 2000);
+ return 0;
+ }
+
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -20,7 +20,7 @@
+ #define PHY_ID_QCA8337 0x004dd036
+ #define QCA8K_ID_QCA8337 0x13
+
+-#define QCA8K_BUSY_WAIT_TIMEOUT 20
++#define QCA8K_BUSY_WAIT_TIMEOUT 2000
+
+ #define QCA8K_NUM_FDB_RECORDS 2048
+
--- /dev/null
+From 759bafb8a3226326ca357613bc90acf738f80c32 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:10 +0200
+Subject: [PATCH] net: dsa: qca8k: add support for internal phy and internal
+ mdio
+
+Add support to setup_mdio_bus for internal phy declaration. Introduce a
+flag to use the legacy port phy mapping by default and use the direct
+mapping if a mdio node is detected in the switch node. Register a
+dedicated mdio internal mdio bus to address the different mapping
+between port and phy if the mdio node is detected.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 112 +++++++++++++++++++++++++++++-----------
+ drivers/net/dsa/qca8k.h | 1 +
+ 2 files changed, 83 insertions(+), 30 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -11,6 +11,7 @@
+ #include <linux/netdevice.h>
+ #include <net/dsa.h>
+ #include <linux/of_net.h>
++#include <linux/of_mdio.h>
+ #include <linux/of_platform.h>
+ #include <linux/if_bridge.h>
+ #include <linux/mdio.h>
+@@ -629,7 +630,7 @@ qca8k_port_to_phy(int port)
+ }
+
+ static int
+-qca8k_mdio_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
++qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask)
+ {
+ u16 r1, r2, page;
+ u32 val;
+@@ -639,7 +640,7 @@ qca8k_mdio_busy_wait(struct qca8k_priv *
+
+ ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0,
+ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
+- priv->bus, 0x10 | r2, r1);
++ bus, 0x10 | r2, r1);
+
+ /* Check if qca8k_read has failed for a different reason
+ * before returnting -ETIMEDOUT
+@@ -651,19 +652,16 @@ qca8k_mdio_busy_wait(struct qca8k_priv *
+ }
+
+ static int
+-qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data)
++qca8k_mdio_write(struct mii_bus *salve_bus, int phy, int regnum, u16 data)
+ {
++ struct qca8k_priv *priv = salve_bus->priv;
+ u16 r1, r2, page;
+- u32 phy, val;
++ u32 val;
+ int ret;
+
+ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+ return -EINVAL;
+
+- /* callee is responsible for not passing bad ports,
+- * but we still would like to make spills impossible.
+- */
+- phy = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+ QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+ QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
+@@ -679,33 +677,29 @@ qca8k_mdio_write(struct qca8k_priv *priv
+
+ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
+
+- ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++ ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY);
+
+ exit:
+- mutex_unlock(&priv->bus->mdio_lock);
+-
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_EN);
++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0);
++
++ mutex_unlock(&priv->bus->mdio_lock);
+
+ return ret;
+ }
+
+ static int
+-qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum)
++qca8k_mdio_read(struct mii_bus *salve_bus, int phy, int regnum)
+ {
++ struct qca8k_priv *priv = salve_bus->priv;
+ u16 r1, r2, page;
+- u32 phy, val;
++ u32 val;
+ int ret;
+
+ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+ return -EINVAL;
+
+- /* callee is responsible for not passing bad ports,
+- * but we still would like to make spills impossible.
+- */
+- phy = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+ QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+ QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+@@ -720,24 +714,22 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+
+ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
+
+- ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++ ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY);
+ if (ret)
+ goto exit;
+
+ val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
+- val &= QCA8K_MDIO_MASTER_DATA_MASK;
+
+ exit:
++ /* even if the busy_wait timeouts try to clear the MASTER_EN */
++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0);
++
+ mutex_unlock(&priv->bus->mdio_lock);
+
+ if (val >= 0)
+ val &= QCA8K_MDIO_MASTER_DATA_MASK;
+
+- /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_EN);
+-
+ return val;
+ }
+
+@@ -746,7 +738,14 @@ qca8k_phy_write(struct dsa_switch *ds, i
+ {
+ struct qca8k_priv *priv = ds->priv;
+
+- return qca8k_mdio_write(priv, port, regnum, data);
++ /* Check if the legacy mapping should be used and the
++ * port is not correctly mapped to the right PHY in the
++ * devicetree
++ */
++ if (priv->legacy_phy_port_mapping)
++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
++
++ return qca8k_mdio_write(priv->bus, port, regnum, data);
+ }
+
+ static int
+@@ -755,7 +754,14 @@ qca8k_phy_read(struct dsa_switch *ds, in
+ struct qca8k_priv *priv = ds->priv;
+ int ret;
+
+- ret = qca8k_mdio_read(priv, port, regnum);
++ /* Check if the legacy mapping should be used and the
++ * port is not correctly mapped to the right PHY in the
++ * devicetree
++ */
++ if (priv->legacy_phy_port_mapping)
++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
++
++ ret = qca8k_mdio_read(priv->bus, port, regnum);
+
+ if (ret < 0)
+ return 0xffff;
+@@ -764,10 +770,37 @@ qca8k_phy_read(struct dsa_switch *ds, in
+ }
+
+ static int
++qca8k_mdio_register(struct qca8k_priv *priv, struct device_node *mdio)
++{
++ struct dsa_switch *ds = priv->ds;
++ struct mii_bus *bus;
++
++ bus = devm_mdiobus_alloc(ds->dev);
++
++ if (!bus)
++ return -ENOMEM;
++
++ bus->priv = (void *)priv;
++ bus->name = "qca8k slave mii";
++ bus->read = qca8k_mdio_read;
++ bus->write = qca8k_mdio_write;
++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d",
++ ds->index);
++
++ bus->parent = ds->dev;
++ bus->phy_mask = ~ds->phys_mii_mask;
++
++ ds->slave_mii_bus = bus;
++
++ return devm_of_mdiobus_register(priv->dev, bus, mdio);
++}
++
++static int
+ qca8k_setup_mdio_bus(struct qca8k_priv *priv)
+ {
+ u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg;
+- struct device_node *ports, *port;
++ struct device_node *ports, *port, *mdio;
++ phy_interface_t mode;
+ int err;
+
+ ports = of_get_child_by_name(priv->dev->of_node, "ports");
+@@ -788,7 +821,10 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ if (!dsa_is_user_port(priv->ds, reg))
+ continue;
+
+- if (of_property_read_bool(port, "phy-handle"))
++ of_get_phy_mode(port, &mode);
++
++ if (of_property_read_bool(port, "phy-handle") &&
++ mode != PHY_INTERFACE_MODE_INTERNAL)
+ external_mdio_mask |= BIT(reg);
+ else
+ internal_mdio_mask |= BIT(reg);
+@@ -825,8 +861,23 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ QCA8K_MDIO_MASTER_EN);
+ }
+
++ /* Check if the devicetree declare the port:phy mapping */
++ mdio = of_get_child_by_name(priv->dev->of_node, "mdio");
++ if (of_device_is_available(mdio)) {
++ err = qca8k_mdio_register(priv, mdio);
++ if (err)
++ of_node_put(mdio);
++
++ return err;
++ }
++
++ /* If a mapping can't be found the legacy mapping is used,
++ * using the qca8k_port_to_phy function
++ */
++ priv->legacy_phy_port_mapping = true;
+ priv->ops.phy_read = qca8k_phy_read;
+ priv->ops.phy_write = qca8k_phy_write;
++
+ return 0;
+ }
+
+@@ -1212,7 +1263,8 @@ qca8k_phylink_validate(struct dsa_switch
+ case 5:
+ /* Internal PHY */
+ if (state->interface != PHY_INTERFACE_MODE_NA &&
+- state->interface != PHY_INTERFACE_MODE_GMII)
++ state->interface != PHY_INTERFACE_MODE_GMII &&
++ state->interface != PHY_INTERFACE_MODE_INTERNAL)
+ goto unsupported;
+ break;
+ case 6: /* 2nd CPU port / external PHY */
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -255,6 +255,7 @@ struct qca8k_priv {
+ u8 switch_revision;
+ u8 rgmii_tx_delay;
+ u8 rgmii_rx_delay;
++ bool legacy_phy_port_mapping;
+ struct regmap *regmap;
+ struct mii_bus *bus;
+ struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
--- /dev/null
+From 0c994a28e7518f098c84a3049cb2915780db873a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:11 +0200
+Subject: [PATCH] devicetree: bindings: dsa: qca8k: Document internal mdio
+ definition
+
+Document new way of declare mapping of internal PHY to port.
+The new implementation directly declare the PHY connected to the port
+by adding a node in the switch node. The driver detect this and register
+an internal mdiobus using the mapping defined in the mdio node.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ .../devicetree/bindings/net/dsa/qca8k.txt | 39 +++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -21,6 +21,10 @@ described in dsa/dsa.txt. If the QCA8K s
+ mdio-bus each subnode describing a port needs to have a valid phandle
+ referencing the internal PHY it is connected to. This is because there's no
+ N:N mapping of port and PHY id.
++To declare the internal mdio-bus configuration, declare a mdio node in the
++switch node and declare the phandle for the port referencing the internal
++PHY is connected to. In this config a internal mdio-bus is registered and
++the mdio MASTER is used as communication.
+
+ Don't use mixed external and internal mdio-bus configurations, as this is
+ not supported by the hardware.
+@@ -150,26 +154,61 @@ for the internal master mdio-bus configu
+ port@1 {
+ reg = <1>;
+ label = "lan1";
++ phy-mode = "internal";
++ phy-handle = <&phy_port1>;
+ };
+
+ port@2 {
+ reg = <2>;
+ label = "lan2";
++ phy-mode = "internal";
++ phy-handle = <&phy_port2>;
+ };
+
+ port@3 {
+ reg = <3>;
+ label = "lan3";
++ phy-mode = "internal";
++ phy-handle = <&phy_port3>;
+ };
+
+ port@4 {
+ reg = <4>;
+ label = "lan4";
++ phy-mode = "internal";
++ phy-handle = <&phy_port4>;
+ };
+
+ port@5 {
+ reg = <5>;
+ label = "wan";
++ phy-mode = "internal";
++ phy-handle = <&phy_port5>;
++ };
++ };
++
++ mdio {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ phy_port1: phy@0 {
++ reg = <0>;
++ };
++
++ phy_port2: phy@1 {
++ reg = <1>;
++ };
++
++ phy_port3: phy@2 {
++ reg = <2>;
++ };
++
++ phy_port4: phy@3 {
++ reg = <3>;
++ };
++
++ phy_port5: phy@4 {
++ reg = <4>;
+ };
+ };
+ };
--- /dev/null
+From b7ebac354d54f1657bb89b7a7ca149db50203e6a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:12 +0200
+Subject: [PATCH] net: dsa: qca8k: improve internal mdio read/write bus access
+
+Improve the internal mdio read/write bus access by caching the value
+without accessing it for every read/write.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 28 +++++++++++++++-------------
+ 1 file changed, 15 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -655,6 +655,7 @@ static int
+ qca8k_mdio_write(struct mii_bus *salve_bus, int phy, int regnum, u16 data)
+ {
+ struct qca8k_priv *priv = salve_bus->priv;
++ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ u32 val;
+ int ret;
+@@ -669,22 +670,22 @@ qca8k_mdio_write(struct mii_bus *salve_b
+
+ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
+
+- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- ret = qca8k_set_page(priv->bus, page);
++ ret = qca8k_set_page(bus, page);
+ if (ret)
+ goto exit;
+
+- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+
+- ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL,
++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY);
+
+ exit:
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
+
+- mutex_unlock(&priv->bus->mdio_lock);
++ mutex_unlock(&bus->mdio_lock);
+
+ return ret;
+ }
+@@ -693,6 +694,7 @@ static int
+ qca8k_mdio_read(struct mii_bus *salve_bus, int phy, int regnum)
+ {
+ struct qca8k_priv *priv = salve_bus->priv;
++ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ u32 val;
+ int ret;
+@@ -706,26 +708,26 @@ qca8k_mdio_read(struct mii_bus *salve_bu
+
+ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
+
+- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- ret = qca8k_set_page(priv->bus, page);
++ ret = qca8k_set_page(bus, page);
+ if (ret)
+ goto exit;
+
+- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+
+- ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL,
++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY);
+ if (ret)
+ goto exit;
+
+- val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
++ val = qca8k_mii_read32(bus, 0x10 | r2, r1);
+
+ exit:
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
+
+- mutex_unlock(&priv->bus->mdio_lock);
++ mutex_unlock(&bus->mdio_lock);
+
+ if (val >= 0)
+ val &= QCA8K_MDIO_MASTER_DATA_MASK;
--- /dev/null
+From a46aec02bc06ac2c33f326339e4ef88c735dc30d Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:13 +0200
+Subject: [PATCH] net: dsa: qca8k: pass switch_revision info to phy dev_flags
+
+Define get_phy_flags to pass switch_Revision needed to tweak the
+internal PHY with debug values based on the revision.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1740,6 +1740,22 @@ qca8k_port_vlan_del(struct dsa_switch *d
+ return ret;
+ }
+
++static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ /* Communicate to the phy internal driver the switch revision.
++ * Based on the switch revision different values needs to be
++ * set to the dbg and mmd reg on the phy.
++ * The first 2 bit are used to communicate the switch revision
++ * to the phy driver.
++ */
++ if (port > 0 && port < 6)
++ return priv->switch_revision;
++
++ return 0;
++}
++
+ static enum dsa_tag_protocol
+ qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
+ enum dsa_tag_protocol mp)
+@@ -1774,6 +1790,7 @@ static const struct dsa_switch_ops qca8k
+ .phylink_mac_config = qca8k_phylink_mac_config,
+ .phylink_mac_link_down = qca8k_phylink_mac_link_down,
+ .phylink_mac_link_up = qca8k_phylink_mac_link_up,
++ .get_phy_flags = qca8k_get_phy_flags,
+ };
+
+ static int qca8k_read_switch_id(struct qca8k_priv *priv)
--- /dev/null
+From 272833b9b3b3969be7a91839121d86662c8c4253 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 14 May 2021 23:00:15 +0200
+Subject: [PATCH] net: phy: add support for qca8k switch internal PHY in at803x
+
+Since the at803x share the same regs, it's assumed they are based on the
+same implementation. Make it part of the at803x PHY driver to skip
+having redudant code.
+Add initial support for qca8k internal PHYs. The internal PHYs requires
+special mmd and debug values to be set based on the switch revision
+passwd using the dev_flags. Supports output of idle, receive and eee_wake
+errors stats.
+Some debug values sets can't be translated as the documentation lacks any
+reference about them.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/Kconfig | 5 +-
+ drivers/net/phy/at803x.c | 132 ++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 134 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -235,10 +235,11 @@ config NXP_TJA11XX_PHY
+ Currently supports the NXP TJA1100 and TJA1101 PHY.
+
+ config AT803X_PHY
+- tristate "Qualcomm Atheros AR803X PHYs"
++ tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
+ depends on REGULATOR
+ help
+- Currently supports the AR8030, AR8031, AR8033 and AR8035 model
++ Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
++ QCA8337(Internal qca8k PHY) model
+
+ config QSEMI_PHY
+ tristate "Quality Semiconductor PHYs"
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -92,10 +92,16 @@
+ #define AT803X_DEBUG_REG_5 0x05
+ #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
+
++#define AT803X_DEBUG_REG_3C 0x3C
++
++#define AT803X_DEBUG_REG_3D 0x3D
++
+ #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.
+@@ -142,10 +148,34 @@
+ #define AT803X_PAGE_FIBER 0
+ #define AT803X_PAGE_COPPER 1
+
++#define QCA8327_PHY_ID 0x004dd034
++#define QCA8337_PHY_ID 0x004dd036
++#define QCA8K_PHY_ID_MASK 0xffffffff
++
++#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros AR803x 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 at803x_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_priv {
+ int flags;
+ #define AT803X_KEEP_PLL_ENABLED BIT(0) /* don't turn off internal PLL */
+@@ -154,6 +184,7 @@ struct at803x_priv {
+ struct regulator_dev *vddio_rdev;
+ struct regulator_dev *vddh_rdev;
+ struct regulator *vddio;
++ u64 stats[ARRAY_SIZE(at803x_hw_stats)];
+ };
+
+ struct at803x_context {
+@@ -165,6 +196,17 @@ 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;
+@@ -327,6 +369,53 @@ static void at803x_get_wol(struct phy_de
+ wol->wolopts |= WAKE_MAGIC;
+ }
+
++static int at803x_get_sset_count(struct phy_device *phydev)
++{
++ return ARRAY_SIZE(at803x_hw_stats);
++}
++
++static void at803x_get_strings(struct phy_device *phydev, u8 *data)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) {
++ strscpy(data + i * ETH_GSTRING_LEN,
++ at803x_hw_stats[i].string, ETH_GSTRING_LEN);
++ }
++}
++
++static u64 at803x_get_stat(struct phy_device *phydev, int i)
++{
++ struct at803x_hw_stat stat = at803x_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 at803x_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);
++}
++
+ static int at803x_suspend(struct phy_device *phydev)
+ {
+ int value;
+@@ -1102,6 +1191,34 @@ static int at803x_cable_test_start(struc
+ 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_REG_0, 0x02ea);
++ /* Turn on Gigabit clock */
++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 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_3D, 0x6860);
++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_5, 0x2c46);
++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000);
++ break;
++ }
++
++ return 0;
++}
++
+ static struct phy_driver at803x_driver[] = {
+ {
+ /* Qualcomm Atheros AR8035 */
+@@ -1198,7 +1315,20 @@ static struct phy_driver at803x_driver[]
+ .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 = "QCA PHY 8337",
++ /* PHY_GBIT_FEATURES */
++ .probe = at803x_probe,
++ .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,
++}, };
+
+ module_phy_driver(at803x_driver);
+
--- /dev/null
+From 0d56e5c191b197e1d30a0a4c92628836dafced0f Mon Sep 17 00:00:00 2001
+From: Wei Yongjun <weiyongjun1@huawei.com>
+Date: Tue, 18 May 2021 11:24:13 +0000
+Subject: [PATCH] net: dsa: qca8k: fix missing unlock on error in
+ qca8k_vlan_(add|del)
+
+Add the missing unlock before return from function qca8k_vlan_add()
+and qca8k_vlan_del() in the error handling case.
+
+Fixes: 028f5f8ef44f ("net: dsa: qca8k: handle error with qca8k_read operation")
+Reported-by: Hulk Robot <hulkci@huawei.com>
+Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 16 ++++++++++------
+ 1 file changed, 10 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -506,8 +506,10 @@ qca8k_vlan_add(struct qca8k_priv *priv,
+ goto out;
+
+ reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0);
+- if (reg < 0)
+- return reg;
++ if (reg < 0) {
++ ret = reg;
++ goto out;
++ }
+ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
+ reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port));
+ if (untagged)
+@@ -519,7 +521,7 @@ qca8k_vlan_add(struct qca8k_priv *priv,
+
+ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+ if (ret)
+- return ret;
++ goto out;
+ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+
+ out:
+@@ -541,8 +543,10 @@ qca8k_vlan_del(struct qca8k_priv *priv,
+ goto out;
+
+ reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0);
+- if (reg < 0)
+- return reg;
++ if (reg < 0) {
++ ret = reg;
++ goto out;
++ }
+ reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port));
+ reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT <<
+ QCA8K_VTU_FUNC0_EG_MODE_S(port);
+@@ -564,7 +568,7 @@ qca8k_vlan_del(struct qca8k_priv *priv,
+ } else {
+ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+ if (ret)
+- return ret;
++ goto out;
+ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+ }
+
--- /dev/null
+From 7c9896e37807862e276064dd9331860f5d27affc Mon Sep 17 00:00:00 2001
+From: Yang Yingliang <yangyingliang@huawei.com>
+Date: Sat, 29 May 2021 11:04:38 +0800
+Subject: [PATCH] net: dsa: qca8k: check return value of read functions
+ correctly
+
+Current return type of qca8k_mii_read32() and qca8k_read() are
+unsigned, it can't be negative, so the return value check is
+unuseful. For check the return value correctly, change return
+type of the read functions and add a output parameter to store
+the read value.
+
+Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca8k.c | 130 +++++++++++++++++++---------------------
+ 1 file changed, 60 insertions(+), 70 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -89,26 +89,26 @@ qca8k_split_addr(u32 regaddr, u16 *r1, u
+ *page = regaddr & 0x3ff;
+ }
+
+-static u32
+-qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum)
++static int
++qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
+ {
+- u32 val;
+ int ret;
+
+ ret = bus->read(bus, phy_id, regnum);
+ if (ret >= 0) {
+- val = ret;
++ *val = ret;
+ ret = bus->read(bus, phy_id, regnum + 1);
+- val |= ret << 16;
++ *val |= ret << 16;
+ }
+
+ if (ret < 0) {
+ dev_err_ratelimited(&bus->dev,
+ "failed to read qca8k 32bit register\n");
++ *val = 0;
+ return ret;
+ }
+
+- return val;
++ return 0;
+ }
+
+ static void
+@@ -148,26 +148,26 @@ qca8k_set_page(struct mii_bus *bus, u16
+ return 0;
+ }
+
+-static u32
+-qca8k_read(struct qca8k_priv *priv, u32 reg)
++static int
++qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
+ {
+ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+- u32 val;
++ int ret;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- val = qca8k_set_page(bus, page);
+- if (val < 0)
++ ret = qca8k_set_page(bus, page);
++ if (ret < 0)
+ goto exit;
+
+- val = qca8k_mii_read32(bus, 0x10 | r2, r1);
++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val);
+
+ exit:
+ mutex_unlock(&bus->mdio_lock);
+- return val;
++ return ret;
+ }
+
+ static int
+@@ -208,11 +208,9 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r
+ if (ret < 0)
+ goto exit;
+
+- val = qca8k_mii_read32(bus, 0x10 | r2, r1);
+- if (val < 0) {
+- ret = val;
++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
++ if (ret < 0)
+ goto exit;
+- }
+
+ val &= ~mask;
+ val |= write_val;
+@@ -240,15 +238,8 @@ static int
+ qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+- int ret;
+-
+- ret = qca8k_read(priv, reg);
+- if (ret < 0)
+- return ret;
+-
+- *val = ret;
+
+- return 0;
++ return qca8k_read(priv, reg, val);
+ }
+
+ static int
+@@ -296,18 +287,18 @@ static struct regmap_config qca8k_regmap
+ static int
+ qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+ {
++ int ret, ret1;
+ u32 val;
+- int ret;
+
+- ret = read_poll_timeout(qca8k_read, val, !(val & mask),
++ ret = read_poll_timeout(qca8k_read, ret1, !(val & mask),
+ 0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
+- priv, reg);
++ priv, reg, &val);
+
+ /* Check if qca8k_read has failed for a different reason
+ * before returning -ETIMEDOUT
+ */
+- if (ret < 0 && val < 0)
+- return val;
++ if (ret < 0 && ret1 < 0)
++ return ret1;
+
+ return ret;
+ }
+@@ -316,13 +307,13 @@ static int
+ qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+ {
+ u32 reg[4], val;
+- int i;
++ int i, ret;
+
+ /* load the ARL table into an array */
+ for (i = 0; i < 4; i++) {
+- val = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4));
+- if (val < 0)
+- return val;
++ ret = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4), &val);
++ if (ret < 0)
++ return ret;
+
+ reg[i] = val;
+ }
+@@ -396,9 +387,9 @@ qca8k_fdb_access(struct qca8k_priv *priv
+
+ /* Check for table full violation when adding an entry */
+ if (cmd == QCA8K_FDB_LOAD) {
+- reg = qca8k_read(priv, QCA8K_REG_ATU_FUNC);
+- if (reg < 0)
+- return reg;
++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®);
++ if (ret < 0)
++ return ret;
+ if (reg & QCA8K_ATU_FUNC_FULL)
+ return -1;
+ }
+@@ -477,9 +468,9 @@ qca8k_vlan_access(struct qca8k_priv *pri
+
+ /* Check for table full violation when adding an entry */
+ if (cmd == QCA8K_VLAN_LOAD) {
+- reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC1);
+- if (reg < 0)
+- return reg;
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®);
++ if (ret < 0)
++ return ret;
+ if (reg & QCA8K_VTU_FUNC1_FULL)
+ return -ENOMEM;
+ }
+@@ -505,11 +496,9 @@ qca8k_vlan_add(struct qca8k_priv *priv,
+ if (ret < 0)
+ goto out;
+
+- reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0);
+- if (reg < 0) {
+- ret = reg;
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®);
++ if (ret < 0)
+ goto out;
+- }
+ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
+ reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port));
+ if (untagged)
+@@ -542,11 +531,9 @@ qca8k_vlan_del(struct qca8k_priv *priv,
+ if (ret < 0)
+ goto out;
+
+- reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0);
+- if (reg < 0) {
+- ret = reg;
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®);
++ if (ret < 0)
+ goto out;
+- }
+ reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port));
+ reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT <<
+ QCA8K_VTU_FUNC0_EG_MODE_S(port);
+@@ -638,19 +625,19 @@ qca8k_mdio_busy_wait(struct mii_bus *bus
+ {
+ u16 r1, r2, page;
+ u32 val;
+- int ret;
++ int ret, ret1;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+- ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0,
++ ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0,
+ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
+- bus, 0x10 | r2, r1);
++ bus, 0x10 | r2, r1, &val);
+
+ /* Check if qca8k_read has failed for a different reason
+ * before returnting -ETIMEDOUT
+ */
+- if (ret < 0 && val < 0)
+- return val;
++ if (ret < 0 && ret1 < 0)
++ return ret1;
+
+ return ret;
+ }
+@@ -725,7 +712,7 @@ qca8k_mdio_read(struct mii_bus *salve_bu
+ if (ret)
+ goto exit;
+
+- val = qca8k_mii_read32(bus, 0x10 | r2, r1);
++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
+
+ exit:
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+@@ -733,10 +720,10 @@ exit:
+
+ mutex_unlock(&bus->mdio_lock);
+
+- if (val >= 0)
+- val &= QCA8K_MDIO_MASTER_DATA_MASK;
++ if (ret >= 0)
++ ret = val & QCA8K_MDIO_MASTER_DATA_MASK;
+
+- return val;
++ return ret;
+ }
+
+ static int
+@@ -1211,7 +1198,7 @@ qca8k_phylink_mac_config(struct dsa_swit
+ qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN);
+
+ /* Enable/disable SerDes auto-negotiation as necessary */
+- val = qca8k_read(priv, QCA8K_REG_PWS);
++ qca8k_read(priv, QCA8K_REG_PWS, &val);
+ if (phylink_autoneg_inband(mode))
+ val &= ~QCA8K_PWS_SERDES_AEN_DIS;
+ else
+@@ -1219,7 +1206,7 @@ qca8k_phylink_mac_config(struct dsa_swit
+ qca8k_write(priv, QCA8K_REG_PWS, val);
+
+ /* Configure the SGMII parameters */
+- val = qca8k_read(priv, QCA8K_REG_SGMII_CTRL);
++ qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val);
+
+ val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
+ QCA8K_SGMII_EN_TX | QCA8K_SGMII_EN_SD;
+@@ -1314,10 +1301,11 @@ qca8k_phylink_mac_link_state(struct dsa_
+ {
+ struct qca8k_priv *priv = ds->priv;
+ u32 reg;
++ int ret;
+
+- reg = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port));
+- if (reg < 0)
+- return reg;
++ ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®);
++ if (ret < 0)
++ return ret;
+
+ state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP);
+ state->an_complete = state->link;
+@@ -1419,19 +1407,20 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ const struct qca8k_mib_desc *mib;
+ u32 reg, i, val;
+- u64 hi;
++ u64 hi = 0;
++ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) {
+ mib = &ar8327_mib[i];
+ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
+
+- val = qca8k_read(priv, reg);
+- if (val < 0)
++ ret = qca8k_read(priv, reg, &val);
++ if (ret < 0)
+ continue;
+
+ if (mib->size == 2) {
+- hi = qca8k_read(priv, reg + 4);
+- if (hi < 0)
++ ret = qca8k_read(priv, reg + 4, (u32 *)&hi);
++ if (ret < 0)
+ continue;
+ }
+
+@@ -1459,7 +1448,7 @@ qca8k_set_mac_eee(struct dsa_switch *ds,
+ int ret;
+
+ mutex_lock(&priv->reg_mutex);
+- reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL);
++ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®);
+ if (reg < 0) {
+ ret = reg;
+ goto exit;
+@@ -1802,14 +1791,15 @@ static int qca8k_read_switch_id(struct q
+ const struct qca8k_match_data *data;
+ u32 val;
+ u8 id;
++ int ret;
+
+ /* get the switches ID from the compatible */
+ data = of_device_get_match_data(priv->dev);
+ if (!data)
+ return -ENODEV;
+
+- val = qca8k_read(priv, QCA8K_REG_MASK_CTRL);
+- if (val < 0)
++ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
++ if (ret < 0)
+ return -ENODEV;
+
+ id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK);
--- /dev/null
+From 9fe99de01440d9ede74d447ac76e9c445d8daae9 Mon Sep 17 00:00:00 2001
+From: Yang Yingliang <yangyingliang@huawei.com>
+Date: Sat, 29 May 2021 11:04:39 +0800
+Subject: [PATCH] net: dsa: qca8k: add missing check return value in
+ qca8k_phylink_mac_config()
+
+Now we can check qca8k_read() return value correctly, so if
+it fails, we need return directly.
+
+Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca8k.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1128,6 +1128,7 @@ qca8k_phylink_mac_config(struct dsa_swit
+ {
+ struct qca8k_priv *priv = ds->priv;
+ u32 reg, val;
++ int ret;
+
+ switch (port) {
+ case 0: /* 1st CPU port */
+@@ -1198,7 +1199,9 @@ qca8k_phylink_mac_config(struct dsa_swit
+ qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN);
+
+ /* Enable/disable SerDes auto-negotiation as necessary */
+- qca8k_read(priv, QCA8K_REG_PWS, &val);
++ ret = qca8k_read(priv, QCA8K_REG_PWS, &val);
++ if (ret)
++ return;
+ if (phylink_autoneg_inband(mode))
+ val &= ~QCA8K_PWS_SERDES_AEN_DIS;
+ else
+@@ -1206,7 +1209,9 @@ qca8k_phylink_mac_config(struct dsa_swit
+ qca8k_write(priv, QCA8K_REG_PWS, val);
+
+ /* Configure the SGMII parameters */
+- qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val);
++ ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val);
++ if (ret)
++ return;
+
+ val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
+ QCA8K_SGMII_EN_TX | QCA8K_SGMII_EN_SD;
--- /dev/null
+From aa3d020b22cb844ab7bdbb9e5d861a64666e2b74 Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Wed, 9 Jun 2021 12:52:12 +0300
+Subject: [PATCH] net: dsa: qca8k: fix an endian bug in
+ qca8k_get_ethtool_stats()
+
+The "hi" variable is a u64 but the qca8k_read() writes to the top 32
+bits of it. That will work on little endian systems but it's a bit
+subtle. It's cleaner to make declare "hi" as a u32. We will still need
+to cast it when we shift it later on in the function but that's fine.
+
+Fixes: 7c9896e37807 ("net: dsa: qca8k: check return value of read functions correctly")
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1412,7 +1412,7 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ const struct qca8k_mib_desc *mib;
+ u32 reg, i, val;
+- u64 hi = 0;
++ u32 hi = 0;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) {
+@@ -1424,14 +1424,14 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ continue;
+
+ if (mib->size == 2) {
+- ret = qca8k_read(priv, reg + 4, (u32 *)&hi);
++ ret = qca8k_read(priv, reg + 4, &hi);
+ if (ret < 0)
+ continue;
+ }
+
+ data[i] = val;
+ if (mib->size == 2)
+- data[i] |= hi << 32;
++ data[i] |= (u64)hi << 32;
+ }
+ }
+
--- /dev/null
+From 3d0167f2a627528032821cdeb78b4eab0510460f Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Wed, 9 Jun 2021 12:53:03 +0300
+Subject: [PATCH] net: dsa: qca8k: check the correct variable in
+ qca8k_set_mac_eee()
+
+This code check "reg" but "ret" was intended so the error handling will
+never trigger.
+
+Fixes: 7c9896e37807 ("net: dsa: qca8k: check return value of read functions correctly")
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1454,10 +1454,8 @@ qca8k_set_mac_eee(struct dsa_switch *ds,
+
+ mutex_lock(&priv->reg_mutex);
+ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®);
+- if (reg < 0) {
+- ret = reg;
++ if (ret < 0)
+ goto exit;
+- }
+
+ if (eee->eee_enabled)
+ reg |= lpi_en;
--- /dev/null
+From ce062a0adbfe933b1932235fdfd874c4c91d1bb0 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sat, 11 Sep 2021 17:50:09 +0200
+Subject: net: dsa: qca8k: fix kernel panic with legacy mdio mapping
+
+When the mdio legacy mapping is used the mii_bus priv registered by DSA
+refer to the dsa switch struct instead of the qca8k_priv struct and
+causes a kernel panic. Create dedicated function when the internal
+dedicated mdio driver is used to properly handle the 2 different
+implementation.
+
+Fixes: 759bafb8a322 ("net: dsa: qca8k: add support for internal phy and internal mdio")
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 30 ++++++++++++++++++++++--------
+ 1 file changed, 22 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -643,10 +643,8 @@ qca8k_mdio_busy_wait(struct mii_bus *bus
+ }
+
+ static int
+-qca8k_mdio_write(struct mii_bus *salve_bus, int phy, int regnum, u16 data)
++qca8k_mdio_write(struct mii_bus *bus, int phy, int regnum, u16 data)
+ {
+- struct qca8k_priv *priv = salve_bus->priv;
+- struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ u32 val;
+ int ret;
+@@ -682,10 +680,8 @@ exit:
+ }
+
+ static int
+-qca8k_mdio_read(struct mii_bus *salve_bus, int phy, int regnum)
++qca8k_mdio_read(struct mii_bus *bus, int phy, int regnum)
+ {
+- struct qca8k_priv *priv = salve_bus->priv;
+- struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ u32 val;
+ int ret;
+@@ -727,6 +723,24 @@ exit:
+ }
+
+ static int
++qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data)
++{
++ struct qca8k_priv *priv = slave_bus->priv;
++ struct mii_bus *bus = priv->bus;
++
++ return qca8k_mdio_write(bus, phy, regnum, data);
++}
++
++static int
++qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum)
++{
++ struct qca8k_priv *priv = slave_bus->priv;
++ struct mii_bus *bus = priv->bus;
++
++ return qca8k_mdio_read(bus, phy, regnum);
++}
++
++static int
+ qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data)
+ {
+ struct qca8k_priv *priv = ds->priv;
+@@ -775,8 +789,8 @@ qca8k_mdio_register(struct qca8k_priv *p
+
+ bus->priv = (void *)priv;
+ bus->name = "qca8k slave mii";
+- bus->read = qca8k_mdio_read;
+- bus->write = qca8k_mdio_write;
++ bus->read = qca8k_internal_mdio_read;
++ bus->write = qca8k_internal_mdio_write;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d",
+ ds->index);
+
--- /dev/null
+From ee47ed08d75e8f16b3cf882061ee19c2ea19dd6c Mon Sep 17 00:00:00 2001
+From: Florian Fainelli <f.fainelli@gmail.com>
+Date: Wed, 10 Mar 2021 10:52:26 -0800
+Subject: [PATCH] net: dsa: b53: Add debug prints in b53_vlan_enable()
+
+Having dynamic debug prints in b53_vlan_enable() has been helpful to
+uncover a recent but update the function to indicate the port being
+configured (or -1 for initial setup) and include the global VLAN enabled
+and VLAN filtering enable status.
+
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/b53/b53_common.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -349,7 +349,7 @@ static void b53_set_forwarding(struct b5
+ b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt);
+ }
+
+-static void b53_enable_vlan(struct b53_device *dev, bool enable,
++static void b53_enable_vlan(struct b53_device *dev, int port, bool enable,
+ bool enable_filtering)
+ {
+ u8 mgmt, vc0, vc1, vc4 = 0, vc5;
+@@ -431,6 +431,9 @@ static void b53_enable_vlan(struct b53_d
+ b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+
+ dev->vlan_enabled = enable;
++
++ dev_dbg(dev->dev, "Port %d VLAN enabled: %d, filtering: %d\n",
++ port, enable, enable_filtering);
+ }
+
+ static int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100)
+@@ -708,7 +711,7 @@ int b53_configure_vlan(struct dsa_switch
+ b53_do_vlan_op(dev, VTA_CMD_CLEAR);
+ }
+
+- b53_enable_vlan(dev, dev->vlan_enabled, ds->vlan_filtering);
++ b53_enable_vlan(dev, -1, dev->vlan_enabled, ds->vlan_filtering);
+
+ b53_for_each_port(dev, i)
+ b53_write16(dev, B53_VLAN_PAGE,
+@@ -1390,7 +1393,7 @@ int b53_vlan_filtering(struct dsa_switch
+ if (switchdev_trans_ph_prepare(trans))
+ return 0;
+
+- b53_enable_vlan(dev, dev->vlan_enabled, vlan_filtering);
++ b53_enable_vlan(dev, port, dev->vlan_enabled, vlan_filtering);
+
+ return 0;
+ }
+@@ -1415,7 +1418,7 @@ int b53_vlan_prepare(struct dsa_switch *
+ if (vlan->vid_end >= dev->num_vlans)
+ return -ERANGE;
+
+- b53_enable_vlan(dev, true, ds->vlan_filtering);
++ b53_enable_vlan(dev, port, true, ds->vlan_filtering);
+
+ return 0;
+ }
--- /dev/null
+From 6d16eadab6db0c1d61e59fee7ed1ecc2d10269be Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
+Date: Mon, 15 Mar 2021 15:14:23 +0100
+Subject: [PATCH] net: dsa: b53: spi: allow device tree probing
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add missing of_match_table to allow device tree probing.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/b53/b53_spi.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/net/dsa/b53/b53_spi.c
++++ b/drivers/net/dsa/b53/b53_spi.c
+@@ -324,9 +324,22 @@ static int b53_spi_remove(struct spi_dev
+ return 0;
+ }
+
++static const struct of_device_id b53_spi_of_match[] = {
++ { .compatible = "brcm,bcm5325" },
++ { .compatible = "brcm,bcm5365" },
++ { .compatible = "brcm,bcm5395" },
++ { .compatible = "brcm,bcm5397" },
++ { .compatible = "brcm,bcm5398" },
++ { .compatible = "brcm,bcm53115" },
++ { .compatible = "brcm,bcm53125" },
++ { .compatible = "brcm,bcm53128" },
++ { /* sentinel */ }
++};
++
+ static struct spi_driver b53_spi_driver = {
+ .driver = {
+ .name = "b53-switch",
++ .of_match_table = b53_spi_of_match,
+ },
+ .probe = b53_spi_probe,
+ .remove = b53_spi_remove,
--- /dev/null
+From ad426d7d966b525b73ed5a1842dd830312bbba71 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
+Date: Wed, 17 Mar 2021 09:42:01 +0100
+Subject: [PATCH] net: dsa: b53: relax is63xx() condition
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+BCM63xx switches are present on bcm63xx and bmips devices.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/b53/b53_priv.h | 4 ----
+ 1 file changed, 4 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_priv.h
++++ b/drivers/net/dsa/b53/b53_priv.h
+@@ -186,11 +186,7 @@ static inline int is531x5(struct b53_dev
+
+ static inline int is63xx(struct b53_device *dev)
+ {
+-#ifdef CONFIG_BCM63XX
+ return dev->chip_id == BCM63XX_DEVICE_ID;
+-#else
+- return 0;
+-#endif
+ }
+
+ static inline int is5301x(struct b53_device *dev)
--- /dev/null
+From 964dbf186eaa84d409c359ddf09c827a3fbe8228 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
+Date: Wed, 17 Mar 2021 11:29:26 +0100
+Subject: [PATCH] net: dsa: tag_brcm: add support for legacy tags
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add support for legacy Broadcom tags, which are similar to DSA_TAG_PROTO_BRCM.
+These tags are used on BCM5325, BCM5365 and BCM63xx switches.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/dsa.h | 2 +
+ net/dsa/Kconfig | 7 +++
+ net/dsa/tag_brcm.c | 107 +++++++++++++++++++++++++++++++++++++++++++--
+ 3 files changed, 113 insertions(+), 3 deletions(-)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -45,10 +45,12 @@ struct phylink_link_state;
+ #define DSA_TAG_PROTO_OCELOT_VALUE 15
+ #define DSA_TAG_PROTO_AR9331_VALUE 16
+ #define DSA_TAG_PROTO_RTL4_A_VALUE 17
++#define DSA_TAG_PROTO_BRCM_LEGACY_VALUE 22
+
+ enum dsa_tag_protocol {
+ DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
+ DSA_TAG_PROTO_BRCM = DSA_TAG_PROTO_BRCM_VALUE,
++ DSA_TAG_PROTO_BRCM_LEGACY = DSA_TAG_PROTO_BRCM_LEGACY_VALUE,
+ DSA_TAG_PROTO_BRCM_PREPEND = DSA_TAG_PROTO_BRCM_PREPEND_VALUE,
+ DSA_TAG_PROTO_DSA = DSA_TAG_PROTO_DSA_VALUE,
+ DSA_TAG_PROTO_EDSA = DSA_TAG_PROTO_EDSA_VALUE,
+--- a/net/dsa/Kconfig
++++ b/net/dsa/Kconfig
+@@ -47,6 +47,13 @@ config NET_DSA_TAG_BRCM
+ Say Y if you want to enable support for tagging frames for the
+ Broadcom switches which place the tag after the MAC source address.
+
++config NET_DSA_TAG_BRCM_LEGACY
++ tristate "Tag driver for Broadcom legacy switches using in-frame headers"
++ select NET_DSA_TAG_BRCM_COMMON
++ help
++ Say Y if you want to enable support for tagging frames for the
++ Broadcom legacy switches which place the tag after the MAC source
++ address.
+
+ config NET_DSA_TAG_BRCM_PREPEND
+ tristate "Tag driver for Broadcom switches using prepended headers"
+--- a/net/dsa/tag_brcm.c
++++ b/net/dsa/tag_brcm.c
+@@ -11,9 +11,26 @@
+
+ #include "dsa_priv.h"
+
+-/* This tag length is 4 bytes, older ones were 6 bytes, we do not
+- * handle them
+- */
++/* Legacy Broadcom tag (6 bytes) */
++#define BRCM_LEG_TAG_LEN 6
++
++/* Type fields */
++/* 1st byte in the tag */
++#define BRCM_LEG_TYPE_HI 0x88
++/* 2nd byte in the tag */
++#define BRCM_LEG_TYPE_LO 0x74
++
++/* Tag fields */
++/* 3rd byte in the tag */
++#define BRCM_LEG_UNICAST (0 << 5)
++#define BRCM_LEG_MULTICAST (1 << 5)
++#define BRCM_LEG_EGRESS (2 << 5)
++#define BRCM_LEG_INGRESS (3 << 5)
++
++/* 6th byte in the tag */
++#define BRCM_LEG_PORT_ID (0xf)
++
++/* Newer Broadcom tag (4 bytes) */
+ #define BRCM_TAG_LEN 4
+
+ /* Tag is constructed and desconstructed using byte by byte access
+@@ -194,6 +211,87 @@ DSA_TAG_DRIVER(brcm_netdev_ops);
+ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM);
+ #endif
+
++#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY)
++static struct sk_buff *brcm_leg_tag_xmit(struct sk_buff *skb,
++ struct net_device *dev)
++{
++ struct dsa_port *dp = dsa_slave_to_port(dev);
++ u8 *brcm_tag;
++
++ /* The Ethernet switch we are interfaced with needs packets to be at
++ * least 64 bytes (including FCS) otherwise they will be discarded when
++ * they enter the switch port logic. When Broadcom tags are enabled, we
++ * need to make sure that packets are at least 70 bytes
++ * (including FCS and tag) because the length verification is done after
++ * the Broadcom tag is stripped off the ingress packet.
++ *
++ * Let dsa_slave_xmit() free the SKB
++ */
++ if (__skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN, false))
++ return NULL;
++
++ skb_push(skb, BRCM_LEG_TAG_LEN);
++
++ memmove(skb->data, skb->data + BRCM_LEG_TAG_LEN, 2 * ETH_ALEN);
++
++ brcm_tag = skb->data + 2 * ETH_ALEN;
++
++ /* Broadcom tag type */
++ brcm_tag[0] = BRCM_LEG_TYPE_HI;
++ brcm_tag[1] = BRCM_LEG_TYPE_LO;
++
++ /* Broadcom tag value */
++ brcm_tag[2] = BRCM_LEG_EGRESS;
++ brcm_tag[3] = 0;
++ brcm_tag[4] = 0;
++ brcm_tag[5] = dp->index & BRCM_LEG_PORT_ID;
++
++ return skb;
++}
++
++static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb,
++ struct net_device *dev,
++ struct packet_type *pt)
++{
++ int source_port;
++ u8 *brcm_tag;
++
++ if (unlikely(!pskb_may_pull(skb, BRCM_LEG_PORT_ID)))
++ return NULL;
++
++ brcm_tag = skb->data - 2;
++
++ source_port = brcm_tag[5] & BRCM_LEG_PORT_ID;
++
++ skb->dev = dsa_master_find_slave(dev, 0, source_port);
++ if (!skb->dev)
++ return NULL;
++
++ /* Remove Broadcom tag and update checksum */
++ skb_pull_rcsum(skb, BRCM_LEG_TAG_LEN);
++
++ skb->offload_fwd_mark = 1;
++
++ /* Move the Ethernet DA and SA */
++ memmove(skb->data - ETH_HLEN,
++ skb->data - ETH_HLEN - BRCM_LEG_TAG_LEN,
++ 2 * ETH_ALEN);
++
++ return skb;
++}
++
++static const struct dsa_device_ops brcm_legacy_netdev_ops = {
++ .name = "brcm-legacy",
++ .proto = DSA_TAG_PROTO_BRCM_LEGACY,
++ .xmit = brcm_leg_tag_xmit,
++ .rcv = brcm_leg_tag_rcv,
++ .overhead = BRCM_LEG_TAG_LEN,
++};
++
++DSA_TAG_DRIVER(brcm_legacy_netdev_ops);
++MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_LEGACY);
++#endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY */
++
+ #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND)
+ static struct sk_buff *brcm_tag_xmit_prepend(struct sk_buff *skb,
+ struct net_device *dev)
+@@ -226,6 +324,9 @@ static struct dsa_tag_driver *dsa_tag_dr
+ #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM)
+ &DSA_TAG_DRIVER_NAME(brcm_netdev_ops),
+ #endif
++#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY)
++ &DSA_TAG_DRIVER_NAME(brcm_legacy_netdev_ops),
++#endif
+ #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND)
+ &DSA_TAG_DRIVER_NAME(brcm_prepend_netdev_ops),
+ #endif
--- /dev/null
+From 46c5176c586c81306bf9e7024c13b95da775490f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
+Date: Wed, 17 Mar 2021 11:29:27 +0100
+Subject: [PATCH] net: dsa: b53: support legacy tags
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+These tags are used on BCM5325, BCM5365 and BCM63xx switches.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/b53/Kconfig | 1 +
+ drivers/net/dsa/b53/b53_common.c | 12 +++++++-----
+ 2 files changed, 8 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/dsa/b53/Kconfig
++++ b/drivers/net/dsa/b53/Kconfig
+@@ -3,6 +3,7 @@ menuconfig B53
+ tristate "Broadcom BCM53xx managed switch support"
+ depends on NET_DSA
+ select NET_DSA_TAG_BRCM
++ select NET_DSA_TAG_BRCM_LEGACY
+ select NET_DSA_TAG_BRCM_PREPEND
+ help
+ This driver adds support for Broadcom managed switch chips. It supports
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -2024,15 +2024,17 @@ enum dsa_tag_protocol b53_get_tag_protoc
+ {
+ struct b53_device *dev = ds->priv;
+
+- /* Older models (5325, 5365) support a different tag format that we do
+- * not support in net/dsa/tag_brcm.c yet.
+- */
+- if (is5325(dev) || is5365(dev) ||
+- !b53_can_enable_brcm_tags(ds, port, mprot)) {
++ if (!b53_can_enable_brcm_tags(ds, port, mprot)) {
+ dev->tag_protocol = DSA_TAG_PROTO_NONE;
+ goto out;
+ }
+
++ /* Older models require a different 6 byte tag */
++ if (is5325(dev) || is5365(dev) || is63xx(dev)) {
++ dev->tag_protocol = DSA_TAG_PROTO_BRCM_LEGACY;
++ goto out;
++ }
++
+ /* Broadcom BCM58xx chips have a flow accelerator on Port 8
+ * which requires us to use the prepended Broadcom tag type
+ */
--- /dev/null
+From a5538a777b73b35750ed1ffff8c1ef539e861624 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
+Date: Wed, 17 Mar 2021 10:23:17 +0100
+Subject: [PATCH] net: dsa: b53: mmap: Add device tree support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add device tree support to b53_mmap.c while keeping platform devices support.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/b53/b53_mmap.c | 55 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 55 insertions(+)
+
+--- a/drivers/net/dsa/b53/b53_mmap.c
++++ b/drivers/net/dsa/b53/b53_mmap.c
+@@ -16,6 +16,7 @@
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
++#include <linux/bits.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/io.h>
+@@ -228,11 +229,65 @@ static const struct b53_io_ops b53_mmap_
+ .write64 = b53_mmap_write64,
+ };
+
++static int b53_mmap_probe_of(struct platform_device *pdev,
++ struct b53_platform_data **ppdata)
++{
++ struct device_node *np = pdev->dev.of_node;
++ struct device_node *of_ports, *of_port;
++ struct device *dev = &pdev->dev;
++ struct b53_platform_data *pdata;
++ void __iomem *mem;
++
++ mem = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(mem))
++ return PTR_ERR(mem);
++
++ pdata = devm_kzalloc(dev, sizeof(struct b53_platform_data),
++ GFP_KERNEL);
++ if (!pdata)
++ return -ENOMEM;
++
++ pdata->regs = mem;
++ pdata->chip_id = BCM63XX_DEVICE_ID;
++ pdata->big_endian = of_property_read_bool(np, "big-endian");
++
++ of_ports = of_get_child_by_name(np, "ports");
++ if (!of_ports) {
++ dev_err(dev, "no ports child node found\n");
++ return -EINVAL;
++ }
++
++ for_each_available_child_of_node(of_ports, of_port) {
++ u32 reg;
++
++ if (of_property_read_u32(of_port, "reg", ®))
++ continue;
++
++ if (reg < B53_CPU_PORT)
++ pdata->enabled_ports |= BIT(reg);
++ }
++
++ of_node_put(of_ports);
++ *ppdata = pdata;
++
++ return 0;
++}
++
+ static int b53_mmap_probe(struct platform_device *pdev)
+ {
++ struct device_node *np = pdev->dev.of_node;
+ struct b53_platform_data *pdata = pdev->dev.platform_data;
+ struct b53_mmap_priv *priv;
+ struct b53_device *dev;
++ int ret;
++
++ if (!pdata && np) {
++ ret = b53_mmap_probe_of(pdev, &pdata);
++ if (ret) {
++ dev_err(&pdev->dev, "OF probe error\n");
++ return ret;
++ }
++ }
+
+ if (!pdata)
+ return -EINVAL;
--- /dev/null
+From 866f1577ba69bde2b9f36c300f603596c7d84a62 Mon Sep 17 00:00:00 2001
+From: Qinglang Miao <miaoqinglang@huawei.com>
+Date: Thu, 25 Mar 2021 17:19:54 +0800
+Subject: [PATCH] net: dsa: b53: spi: add missing MODULE_DEVICE_TABLE
+
+This patch adds missing MODULE_DEVICE_TABLE definition which generates
+correct modalias for automatic loading of this driver when it is built
+as an external module.
+
+Reported-by: Hulk Robot <hulkci@huawei.com>
+Signed-off-by: Qinglang Miao <miaoqinglang@huawei.com>
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/b53/b53_spi.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/dsa/b53/b53_spi.c
++++ b/drivers/net/dsa/b53/b53_spi.c
+@@ -335,6 +335,7 @@ static const struct of_device_id b53_spi
+ { .compatible = "brcm,bcm53128" },
+ { /* sentinel */ }
+ };
++MODULE_DEVICE_TABLE(of, b53_spi_of_match);
+
+ static struct spi_driver b53_spi_driver = {
+ .driver = {
--- /dev/null
+From 2c32a3d3c233b855943677609fe388f82b1f0975 Mon Sep 17 00:00:00 2001
+From: Florian Fainelli <f.fainelli@gmail.com>
+Date: Tue, 8 Jun 2021 14:22:04 -0700
+Subject: [PATCH] net: dsa: b53: Do not force CPU to be always tagged
+
+Commit ca8931948344 ("net: dsa: b53: Keep CPU port as tagged in all
+VLANs") forced the CPU port to be always tagged in any VLAN membership.
+This was necessary back then because we did not support Broadcom tags
+for all configurations so the only way to differentiate tagged and
+untagged traffic while DSA_TAG_PROTO_NONE was used was to force the CPU
+port into being always tagged.
+
+With most configurations enabling Broadcom tags, especially after
+8fab459e69ab ("net: dsa: b53: Enable Broadcom tags for 531x5/539x
+families") we do not need to apply this unconditional force tagging of
+the CPU port in all VLANs.
+
+A helper function is introduced to faciliate the encapsulation of the
+specific condition requiring the CPU port to be tagged in all VLANs and
+the dsa_switch_ops::untag_bridge_pvid boolean is moved to when
+dsa_switch_ops::setup is called when we have already determined the
+tagging protocol we will be using.
+
+Reported-by: Matthew Hagan <mnhagan88@gmail.com>
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Tested-by: Matthew Hagan <mnhagan88@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/b53/b53_common.c | 17 ++++++++++++++---
+ 1 file changed, 14 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -1049,6 +1049,11 @@ static int b53_setup(struct dsa_switch *
+ unsigned int port;
+ int ret;
+
++ /* Request bridge PVID untagged when DSA_TAG_PROTO_NONE is set
++ * which forces the CPU port to be tagged in all VLANs.
++ */
++ ds->untag_bridge_pvid = dev->tag_protocol == DSA_TAG_PROTO_NONE;
++
+ ret = b53_reset_switch(dev);
+ if (ret) {
+ dev_err(ds->dev, "failed to reset switch\n");
+@@ -1423,6 +1428,13 @@ int b53_vlan_prepare(struct dsa_switch *
+ return 0;
+ }
+ EXPORT_SYMBOL(b53_vlan_prepare);
++
++static bool b53_vlan_port_needs_forced_tagged(struct dsa_switch *ds, int port)
++{
++ struct b53_device *dev = ds->priv;
++
++ return dev->tag_protocol == DSA_TAG_PROTO_NONE && dsa_is_cpu_port(ds, port);
++}
+
+ void b53_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+@@ -1442,7 +1454,7 @@ void b53_vlan_add(struct dsa_switch *ds,
+ untagged = true;
+
+ vl->members |= BIT(port);
+- if (untagged && !dsa_is_cpu_port(ds, port))
++ if (untagged && !b53_vlan_port_needs_forced_tagged(ds, port))
+ vl->untag |= BIT(port);
+ else
+ vl->untag &= ~BIT(port);
+@@ -1480,7 +1492,7 @@ int b53_vlan_del(struct dsa_switch *ds,
+ if (pvid == vid)
+ pvid = b53_default_pvid(dev);
+
+- if (untagged && !dsa_is_cpu_port(ds, port))
++ if (untagged && !b53_vlan_port_needs_forced_tagged(ds, port))
+ vl->untag &= ~(BIT(port));
+
+ b53_set_vlan_entry(dev, vid, vl);
+@@ -2644,7 +2656,6 @@ struct b53_device *b53_switch_alloc(stru
+ dev->ops = ops;
+ ds->ops = &b53_switch_ops;
+ ds->configure_vlan_while_not_filtering = true;
+- ds->untag_bridge_pvid = true;
+ dev->vlan_enabled = ds->configure_vlan_while_not_filtering;
+ /* Let DSA handle the case were multiple bridges span the same switch
+ * device and different VLAN awareness settings are requested, which
--- /dev/null
+From 11b57faf951cd3a570e3d9e463fc7c41023bc8c6 Mon Sep 17 00:00:00 2001
+From: Colin Ian King <colin.king@canonical.com>
+Date: Tue, 15 Jun 2021 10:05:16 +0100
+Subject: [PATCH] net: dsa: b53: remove redundant null check on dev
+
+The pointer dev can never be null, the null check is redundant
+and can be removed. Cleans up a static analysis warning that
+pointer priv is dereferencing dev before dev is being null
+checked.
+
+Addresses-Coverity: ("Dereference before null check")
+Signed-off-by: Colin Ian King <colin.king@canonical.com>
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/b53/b53_srab.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_srab.c
++++ b/drivers/net/dsa/b53/b53_srab.c
+@@ -632,8 +632,7 @@ static int b53_srab_remove(struct platfo
+ struct b53_srab_priv *priv = dev->priv;
+
+ b53_srab_intr_set(priv, false);
+- if (dev)
+- b53_switch_remove(dev);
++ b53_switch_remove(dev);
+
+ return 0;
+ }
--- /dev/null
+From 64a81b24487f0d2fba0f033029eec2abc7d82cee Mon Sep 17 00:00:00 2001
+From: Florian Fainelli <f.fainelli@gmail.com>
+Date: Mon, 21 Jun 2021 15:10:55 -0700
+Subject: [PATCH] net: dsa: b53: Create default VLAN entry explicitly
+
+In case CONFIG_VLAN_8021Q is not set, there will be no call down to the
+b53 driver to ensure that the default PVID VLAN entry will be configured
+with the appropriate untagged attribute towards the CPU port. We were
+implicitly relying on dsa_slave_vlan_rx_add_vid() to do that for us,
+instead make it explicit.
+
+Reported-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/b53/b53_common.c | 27 +++++++++++++++++++--------
+ 1 file changed, 19 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -693,6 +693,13 @@ static u16 b53_default_pvid(struct b53_d
+ return 0;
+ }
+
++static bool b53_vlan_port_needs_forced_tagged(struct dsa_switch *ds, int port)
++{
++ struct b53_device *dev = ds->priv;
++
++ return dev->tag_protocol == DSA_TAG_PROTO_NONE && dsa_is_cpu_port(ds, port);
++}
++
+ int b53_configure_vlan(struct dsa_switch *ds)
+ {
+ struct b53_device *dev = ds->priv;
+@@ -713,9 +720,20 @@ int b53_configure_vlan(struct dsa_switch
+
+ b53_enable_vlan(dev, -1, dev->vlan_enabled, ds->vlan_filtering);
+
+- b53_for_each_port(dev, i)
++ /* Create an untagged VLAN entry for the default PVID in case
++ * CONFIG_VLAN_8021Q is disabled and there are no calls to
++ * dsa_slave_vlan_rx_add_vid() to create the default VLAN
++ * entry. Do this only when the tagging protocol is not
++ * DSA_TAG_PROTO_NONE
++ */
++ b53_for_each_port(dev, i) {
++ v = &dev->vlans[def_vid];
++ v->members |= BIT(i);
++ if (!b53_vlan_port_needs_forced_tagged(ds, i))
++ v->untag = v->members;
+ b53_write16(dev, B53_VLAN_PAGE,
+ B53_VLAN_PORT_DEF_TAG(i), def_vid);
++ }
+
+ /* Upon initial call we have not set-up any VLANs, but upon
+ * system resume, we need to restore all VLAN entries.
+@@ -1429,13 +1447,6 @@ int b53_vlan_prepare(struct dsa_switch *
+ }
+ EXPORT_SYMBOL(b53_vlan_prepare);
+
+-static bool b53_vlan_port_needs_forced_tagged(struct dsa_switch *ds, int port)
+-{
+- struct b53_device *dev = ds->priv;
+-
+- return dev->tag_protocol == DSA_TAG_PROTO_NONE && dsa_is_cpu_port(ds, port);
+-}
+-
+ void b53_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+ {
--- /dev/null
+From 0ccf8511182436183c031e8a2f740ae91a02c625 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 14 Sep 2021 14:33:45 +0200
+Subject: net: phy: at803x: add support for qca 8327 internal phy
+
+Add support for qca8327 internal phy needed for correct init of the
+switch port. It does use the same qca8337 function and reg just with a
+different id.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Tested-by: Rosen Penev <rosenp@gmail.com>
+Tested-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1328,6 +1328,19 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
++}, {
++ /* QCA8327 */
++ .phy_id = QCA8327_PHY_ID,
++ .phy_id_mask = QCA8K_PHY_ID_MASK,
++ .name = "QCA PHY 8327",
++ /* PHY_GBIT_FEATURES */
++ .probe = at803x_probe,
++ .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,
+ }, };
+
+ module_phy_driver(at803x_driver);
+@@ -1338,6 +1351,8 @@ 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_PHY_ID) },
+ { }
+ };
+
--- /dev/null
+From 983d96a9116a328668601555d96736261d33170c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 16 Sep 2021 14:03:51 +0200
+Subject: [PATCH] net: dsa: b53: Include all ports in "enabled_ports"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Make "enabled_ports" bitfield contain all available switch ports
+including a CPU port. This way there is no need for fixup during
+initialization.
+
+For BCM53010, BCM53018 and BCM53019 include also other available ports.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Tested-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/b53/b53_common.c | 23 +++++++++++------------
+ 1 file changed, 11 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -2288,7 +2288,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5325_DEVICE_ID,
+ .dev_name = "BCM5325",
+ .vlans = 16,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x3f,
+ .arl_bins = 2,
+ .arl_buckets = 1024,
+ .imp_port = 5,
+@@ -2299,7 +2299,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5365_DEVICE_ID,
+ .dev_name = "BCM5365",
+ .vlans = 256,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x3f,
+ .arl_bins = 2,
+ .arl_buckets = 1024,
+ .imp_port = 5,
+@@ -2310,7 +2310,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5389_DEVICE_ID,
+ .dev_name = "BCM5389",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x11f,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2324,7 +2324,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5395_DEVICE_ID,
+ .dev_name = "BCM5395",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x11f,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2338,7 +2338,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5397_DEVICE_ID,
+ .dev_name = "BCM5397",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x11f,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2352,7 +2352,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5398_DEVICE_ID,
+ .dev_name = "BCM5398",
+ .vlans = 4096,
+- .enabled_ports = 0x7f,
++ .enabled_ports = 0x17f,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2366,7 +2366,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM53115_DEVICE_ID,
+ .dev_name = "BCM53115",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x11f,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .vta_regs = B53_VTA_REGS,
+@@ -2380,7 +2380,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM53125_DEVICE_ID,
+ .dev_name = "BCM53125",
+ .vlans = 4096,
+- .enabled_ports = 0xff,
++ .enabled_ports = 0x1ff,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2422,7 +2422,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM53010_DEVICE_ID,
+ .dev_name = "BCM53010",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x1bf,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2464,7 +2464,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM53018_DEVICE_ID,
+ .dev_name = "BCM53018",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x1bf,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2478,7 +2478,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM53019_DEVICE_ID,
+ .dev_name = "BCM53019",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x1bf,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2605,7 +2605,6 @@ static int b53_switch_init(struct b53_de
+ dev->cpu_port = 5;
+ }
+
+- dev->enabled_ports |= BIT(dev->cpu_port);
+ dev->num_ports = fls(dev->enabled_ports);
+
+ dev->ds->num_ports = min_t(unsigned int, dev->num_ports, DSA_MAX_PORTS);
--- /dev/null
+From b290c6384afabbca5ae6e2af72fb1b2bc37922be Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 16 Sep 2021 14:03:52 +0200
+Subject: [PATCH] net: dsa: b53: Drop BCM5301x workaround for a wrong CPU/IMP
+ port
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+On BCM5301x port 8 requires a fixed link when used.
+
+Years ago when b53 was an OpenWrt downstream driver (with configuration
+based on sometimes bugged NVRAM) there was a need for a fixup. In case
+of forcing fixed link for (incorrectly specified) port 5 the code had to
+actually setup port 8 link.
+
+For upstream b53 driver with setup based on DT there is no need for that
+workaround. In DT we have and require correct ports setup.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Tested-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/b53/b53_common.c | 6 ------
+ 1 file changed, 6 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -1256,12 +1256,6 @@ static void b53_adjust_link(struct dsa_s
+ return;
+ }
+ }
+- } else if (is5301x(dev)) {
+- if (port != dev->cpu_port) {
+- b53_force_port_config(dev, dev->cpu_port, 2000,
+- DUPLEX_FULL, true, true);
+- b53_force_link(dev, dev->cpu_port, 1);
+- }
+ }
+
+ /* Re-negotiate EEE if it was enabled already */
--- /dev/null
+From 3ff26b29230c54fea2353b63124c589b61953e14 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 16 Sep 2021 14:03:53 +0200
+Subject: [PATCH] net: dsa: b53: Improve flow control setup on BCM5301x
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+According to the Broadcom's reference driver flow control needs to be
+enabled for any CPU switch port (5, 7 or 8 - depending on which one is
+used). Current code makes it work only for the port 5. Use
+dsa_is_cpu_port() which solved that problem.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Tested-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/b53/b53_common.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -1187,7 +1187,7 @@ static void b53_adjust_link(struct dsa_s
+ return;
+
+ /* Enable flow control on BCM5301x's CPU port */
+- if (is5301x(dev) && port == dev->cpu_port)
++ if (is5301x(dev) && dsa_is_cpu_port(ds, port))
+ tx_pause = rx_pause = true;
+
+ if (phydev->pause) {
--- /dev/null
+From 7d5af56418d7d01e43247a33b6fe6492ea871923 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 16 Sep 2021 14:03:54 +0200
+Subject: [PATCH] net: dsa: b53: Drop unused "cpu_port" field
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It's set but never used anymore.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Tested-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/b53/b53_common.c | 28 ----------------------------
+ drivers/net/dsa/b53/b53_priv.h | 1 -
+ 2 files changed, 29 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -2286,7 +2286,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 2,
+ .arl_buckets = 1024,
+ .imp_port = 5,
+- .cpu_port = B53_CPU_PORT_25,
+ .duplex_reg = B53_DUPLEX_STAT_FE,
+ },
+ {
+@@ -2297,7 +2296,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 2,
+ .arl_buckets = 1024,
+ .imp_port = 5,
+- .cpu_port = B53_CPU_PORT_25,
+ .duplex_reg = B53_DUPLEX_STAT_FE,
+ },
+ {
+@@ -2308,7 +2306,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2322,7 +2319,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2336,7 +2332,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS_9798,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2350,7 +2345,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS_9798,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2365,7 +2359,6 @@ static const struct b53_chip_data b53_sw
+ .arl_buckets = 1024,
+ .vta_regs = B53_VTA_REGS,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+@@ -2378,7 +2371,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2392,7 +2384,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2406,7 +2397,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS_63XX,
+ .duplex_reg = B53_DUPLEX_STAT_63XX,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
+@@ -2420,7 +2410,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2434,7 +2423,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2448,7 +2436,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2462,7 +2449,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2476,7 +2462,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2490,7 +2475,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2504,7 +2488,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2518,7 +2501,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2532,7 +2514,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 256,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2558,7 +2539,6 @@ static int b53_switch_init(struct b53_de
+ dev->vta_regs[2] = chip->vta_regs[2];
+ dev->jumbo_pm_reg = chip->jumbo_pm_reg;
+ dev->imp_port = chip->imp_port;
+- dev->cpu_port = chip->cpu_port;
+ dev->num_vlans = chip->vlans;
+ dev->num_arl_bins = chip->arl_bins;
+ dev->num_arl_buckets = chip->arl_buckets;
+@@ -2590,13 +2570,6 @@ static int b53_switch_init(struct b53_de
+ break;
+ #endif
+ }
+- } else if (dev->chip_id == BCM53115_DEVICE_ID) {
+- u64 strap_value;
+-
+- b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value);
+- /* use second IMP port if GMII is enabled */
+- if (strap_value & SV_GMII_CTRL_115)
+- dev->cpu_port = 5;
+ }
+
+ dev->num_ports = fls(dev->enabled_ports);
+--- a/drivers/net/dsa/b53/b53_priv.h
++++ b/drivers/net/dsa/b53/b53_priv.h
+@@ -123,7 +123,6 @@ struct b53_device {
+ /* used ports mask */
+ u16 enabled_ports;
+ unsigned int imp_port;
+- unsigned int cpu_port;
+
+ /* connect specific data */
+ u8 current_page;
--- /dev/null
+From bea7907837c57a0aaac009931eb14efb056dafab Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Thu, 29 Jul 2021 17:56:00 +0300
+Subject: [PATCH] net: dsa: don't set skb->offload_fwd_mark when not offloading
+ the bridge
+
+DSA has gained the recent ability to deal gracefully with upper
+interfaces it cannot offload, such as the bridge, bonding or team
+drivers. When such uppers exist, the ports are still in standalone mode
+as far as the hardware is concerned.
+
+But when we deliver packets to the software bridge in order for that to
+do the forwarding, there is an unpleasant surprise in that the bridge
+will refuse to forward them. This is because we unconditionally set
+skb->offload_fwd_mark = true, meaning that the bridge thinks the frames
+were already forwarded in hardware by us.
+
+Since dp->bridge_dev is populated only when there is hardware offload
+for it, but not in the software fallback case, let's introduce a new
+helper that can be called from the tagger data path which sets the
+skb->offload_fwd_mark accordingly to zero when there is no hardware
+offload for bridging. This lets the bridge forward packets back to other
+interfaces of our switch, if needed.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Tobias Waldekranz <tobias@waldekranz.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/dsa_priv.h | 14 ++++++++++++++
+ net/dsa/tag_brcm.c | 4 ++--
+ net/dsa/tag_dsa.c | 15 +++++++++++----
+ net/dsa/tag_ksz.c | 2 +-
+ net/dsa/tag_lan9303.c | 3 ++-
+ net/dsa/tag_mtk.c | 2 +-
+ net/dsa/tag_ocelot.c | 2 +-
+ net/dsa/tag_rtl4_a.c | 2 +-
+ net/dsa/tag_sja1105.c | 20 ++++++++++++++------
+ 9 files changed, 47 insertions(+), 17 deletions(-)
+
+--- a/net/dsa/dsa_priv.h
++++ b/net/dsa/dsa_priv.h
+@@ -266,6 +266,20 @@ static inline struct sk_buff *dsa_untag_
+ return skb;
+ }
+
++/* If the ingress port offloads the bridge, we mark the frame as autonomously
++ * forwarded by hardware, so the software bridge doesn't forward in twice, back
++ * to us, because we already did. However, if we're in fallback mode and we do
++ * software bridging, we are not offloading it, therefore the dp->bridge_dev
++ * pointer is not populated, and flooding needs to be done by software (we are
++ * effectively operating in standalone ports mode).
++ */
++static inline void dsa_default_offload_fwd_mark(struct sk_buff *skb)
++{
++ struct dsa_port *dp = dsa_slave_to_port(skb->dev);
++
++ skb->offload_fwd_mark = !!(dp->bridge_dev);
++}
++
+ /* switch.c */
+ int dsa_switch_register_notifier(struct dsa_switch *ds);
+ void dsa_switch_unregister_notifier(struct dsa_switch *ds);
+--- a/net/dsa/tag_brcm.c
++++ b/net/dsa/tag_brcm.c
+@@ -166,7 +166,7 @@ static struct sk_buff *brcm_tag_rcv_ll(s
+ /* Remove Broadcom tag and update checksum */
+ skb_pull_rcsum(skb, BRCM_TAG_LEN);
+
+- skb->offload_fwd_mark = 1;
++ dsa_default_offload_fwd_mark(skb);
+
+ return skb;
+ }
+@@ -270,7 +270,7 @@ static struct sk_buff *brcm_leg_tag_rcv(
+ /* Remove Broadcom tag and update checksum */
+ skb_pull_rcsum(skb, BRCM_LEG_TAG_LEN);
+
+- skb->offload_fwd_mark = 1;
++ dsa_default_offload_fwd_mark(skb);
+
+ /* Move the Ethernet DA and SA */
+ memmove(skb->data - ETH_HLEN,
+--- a/net/dsa/tag_ksz.c
++++ b/net/dsa/tag_ksz.c
+@@ -24,7 +24,7 @@ static struct sk_buff *ksz_common_rcv(st
+
+ pskb_trim_rcsum(skb, skb->len - len);
+
+- skb->offload_fwd_mark = true;
++ dsa_default_offload_fwd_mark(skb);
+
+ return skb;
+ }
+--- a/net/dsa/tag_lan9303.c
++++ b/net/dsa/tag_lan9303.c
+@@ -115,7 +115,8 @@ static struct sk_buff *lan9303_rcv(struc
+ skb_pull_rcsum(skb, 2 + 2);
+ memmove(skb->data - ETH_HLEN, skb->data - (ETH_HLEN + LAN9303_TAG_LEN),
+ 2 * ETH_ALEN);
+- skb->offload_fwd_mark = !(lan9303_tag1 & LAN9303_TAG_RX_TRAPPED_TO_CPU);
++ if (!(lan9303_tag1 & LAN9303_TAG_RX_TRAPPED_TO_CPU))
++ dsa_default_offload_fwd_mark(skb);
+
+ return skb;
+ }
+--- a/net/dsa/tag_mtk.c
++++ b/net/dsa/tag_mtk.c
+@@ -104,7 +104,7 @@ static struct sk_buff *mtk_tag_rcv(struc
+
+ /* Only unicast or broadcast frames are offloaded */
+ if (likely(!is_multicast_skb))
+- skb->offload_fwd_mark = 1;
++ dsa_default_offload_fwd_mark(skb);
+
+ return skb;
+ }
+--- a/net/dsa/tag_ocelot.c
++++ b/net/dsa/tag_ocelot.c
+@@ -225,7 +225,7 @@ static struct sk_buff *ocelot_rcv(struct
+ */
+ return NULL;
+
+- skb->offload_fwd_mark = 1;
++ dsa_default_offload_fwd_mark(skb);
+ skb->priority = qos_class;
+
+ /* Ocelot switches copy frames unmodified to the CPU. However, it is
+--- a/net/dsa/tag_rtl4_a.c
++++ b/net/dsa/tag_rtl4_a.c
+@@ -115,7 +115,7 @@ static struct sk_buff *rtl4a_tag_rcv(str
+ skb->data - ETH_HLEN - RTL4_A_HDR_LEN,
+ 2 * ETH_ALEN);
+
+- skb->offload_fwd_mark = 1;
++ dsa_default_offload_fwd_mark(skb);
+
+ return skb;
+ }
--- /dev/null
+From b4df02b562f4aa14ff6811f30e1b4d2159585c59 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 19 Sep 2021 18:28:15 +0200
+Subject: net: phy: at803x: add support for qca 8327 A variant internal phy
+
+For qca8327 internal phy there are 2 different switch variant with 2
+different phy id. Add this missing variant so the internal phy can be
+correctly identified and fixed.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 25 ++++++++++++++++++++-----
+ 1 file changed, 20 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -148,7 +148,8 @@
+ #define AT803X_PAGE_FIBER 0
+ #define AT803X_PAGE_COPPER 1
+
+-#define QCA8327_PHY_ID 0x004dd034
++#define QCA8327_A_PHY_ID 0x004dd033
++#define QCA8327_B_PHY_ID 0x004dd034
+ #define QCA8337_PHY_ID 0x004dd036
+ #define QCA8K_PHY_ID_MASK 0xffffffff
+
+@@ -1329,10 +1330,23 @@ static struct phy_driver at803x_driver[]
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
+ }, {
+- /* QCA8327 */
+- .phy_id = QCA8327_PHY_ID,
++ /* QCA8327-A from switch QCA8327-AL1A */
++ .phy_id = QCA8327_A_PHY_ID,
+ .phy_id_mask = QCA8K_PHY_ID_MASK,
+- .name = "QCA PHY 8327",
++ .name = "QCA PHY 8327-A",
++ /* PHY_GBIT_FEATURES */
++ .probe = at803x_probe,
++ .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,
++}, {
++ /* QCA8327-B from switch QCA8327-BL1A */
++ .phy_id = QCA8327_B_PHY_ID,
++ .phy_id_mask = QCA8K_PHY_ID_MASK,
++ .name = "QCA PHY 8327-B",
+ /* PHY_GBIT_FEATURES */
+ .probe = at803x_probe,
+ .flags = PHY_IS_INTERNAL,
+@@ -1352,7 +1366,8 @@ 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(QCA8337_PHY_ID) },
+- { PHY_ID_MATCH_EXACT(QCA8327_PHY_ID) },
++ { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) },
++ { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
+ { }
+ };
+
--- /dev/null
+From 15b9df4ece17d084f14eb0ca1cf05f2ad497e425 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 19 Sep 2021 18:28:16 +0200
+Subject: net: phy: at803x: add resume/suspend function to qca83xx phy
+
+Add resume/suspend function to qca83xx internal phy.
+We can't use the at803x generic function as the documentation lacks of
+any support for WoL regs.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1329,6 +1329,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
++ .suspend = genphy_suspend,
++ .resume = genphy_resume,
+ }, {
+ /* QCA8327-A from switch QCA8327-AL1A */
+ .phy_id = QCA8327_A_PHY_ID,
+@@ -1342,6 +1344,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
++ .suspend = genphy_suspend,
++ .resume = genphy_resume,
+ }, {
+ /* QCA8327-B from switch QCA8327-BL1A */
+ .phy_id = QCA8327_B_PHY_ID,
+@@ -1355,6 +1359,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
++ .suspend = genphy_suspend,
++ .resume = genphy_resume,
+ }, };
+
+ module_phy_driver(at803x_driver);
--- /dev/null
+From d44fd8604a4ab92119adb35f05fd87612af722b5 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 19 Sep 2021 18:28:17 +0200
+Subject: net: phy: at803x: fix spacing and improve name for 83xx phy
+
+Fix spacing and improve name for 83xx phy following other phy in the
+same driver.
+
+Signed-off-by: Ansuel Smith <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, 30 insertions(+), 30 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1318,47 +1318,47 @@ static struct phy_driver at803x_driver[]
+ .config_aneg = at803x_config_aneg,
+ }, {
+ /* QCA8337 */
+- .phy_id = QCA8337_PHY_ID,
+- .phy_id_mask = QCA8K_PHY_ID_MASK,
+- .name = "QCA PHY 8337",
++ .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 = at803x_get_sset_count,
+- .get_strings = at803x_get_strings,
+- .get_stats = at803x_get_stats,
++ .probe = at803x_probe,
++ .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,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ /* QCA8327-A from switch QCA8327-AL1A */
+- .phy_id = QCA8327_A_PHY_ID,
+- .phy_id_mask = QCA8K_PHY_ID_MASK,
+- .name = "QCA PHY 8327-A",
++ .phy_id = QCA8327_A_PHY_ID,
++ .phy_id_mask = QCA8K_PHY_ID_MASK,
++ .name = "Qualcomm Atheros 8327-A 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 = at803x_get_sset_count,
+- .get_strings = at803x_get_strings,
+- .get_stats = at803x_get_stats,
++ .probe = at803x_probe,
++ .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,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ /* QCA8327-B from switch QCA8327-BL1A */
+- .phy_id = QCA8327_B_PHY_ID,
+- .phy_id_mask = QCA8K_PHY_ID_MASK,
+- .name = "QCA PHY 8327-B",
++ .phy_id = QCA8327_B_PHY_ID,
++ .phy_id_mask = QCA8K_PHY_ID_MASK,
++ .name = "Qualcomm Atheros 8327-B 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 = at803x_get_sset_count,
+- .get_strings = at803x_get_strings,
+- .get_stats = at803x_get_stats,
++ .probe = at803x_probe,
++ .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,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, };
--- /dev/null
+From ba3c01ee02ed0d821c9f241f179bbc9457542b8f Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 10 Oct 2021 00:46:15 +0200
+Subject: net: phy: at803x: fix resume for QCA8327 phy
+
+From Documentation phy resume triggers phy reset and restart
+auto-negotiation. Add a dedicated function to wait reset to finish as
+it was notice a regression where port sometime are not reliable after a
+suspend/resume session. The reset wait logic is copied from phy_poll_reset.
+Add dedicated suspend function to use genphy_suspend only with QCA8337
+phy and set only additional debug settings for QCA8327. With more test
+it was reported that QCA8327 doesn't proprely support this mode and
+using this cause the unreliability of the switch ports, especially the
+malfunction of the port0.
+
+Fixes: 15b9df4ece17 ("net: phy: at803x: add resume/suspend function to qca83xx phy")
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 69 +++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 63 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -92,9 +92,14 @@
+ #define AT803X_DEBUG_REG_5 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_REG_3C 0x3C
+
+ #define AT803X_DEBUG_REG_3D 0x3D
++#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6)
+
+ #define AT803X_DEBUG_REG_1F 0x1F
+ #define AT803X_DEBUG_PLL_ON BIT(2)
+@@ -1220,6 +1225,58 @@ static int qca83xx_config_init(struct ph
+ return 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;
++
++ msleep(1);
++
++ return 0;
++}
++
++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_3D,
++ 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 struct phy_driver at803x_driver[] = {
+ {
+ /* Qualcomm Atheros AR8035 */
+@@ -1329,8 +1386,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
+- .suspend = genphy_suspend,
+- .resume = genphy_resume,
++ .suspend = qca83xx_suspend,
++ .resume = qca83xx_resume,
+ }, {
+ /* QCA8327-A from switch QCA8327-AL1A */
+ .phy_id = QCA8327_A_PHY_ID,
+@@ -1344,8 +1401,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
+- .suspend = genphy_suspend,
+- .resume = genphy_resume,
++ .suspend = qca83xx_suspend,
++ .resume = qca83xx_resume,
+ }, {
+ /* QCA8327-B from switch QCA8327-BL1A */
+ .phy_id = QCA8327_B_PHY_ID,
+@@ -1359,8 +1416,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
+- .suspend = genphy_suspend,
+- .resume = genphy_resume,
++ .suspend = qca83xx_suspend,
++ .resume = qca83xx_resume,
+ }, };
+
+ module_phy_driver(at803x_driver);
--- /dev/null
+From 1ca8311949aec5c9447645731ef1c6bc5bd71350 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 10 Oct 2021 00:46:16 +0200
+Subject: net: phy: at803x: add DAC amplitude fix for 8327 phy
+
+QCA8327 internal phy require DAC amplitude adjustement set to +6% with
+100m speed. Also add additional define to report a change of the same
+reg in QCA8337. (different scope it does set 1000m voltage)
+Add link_change_notify function to set the proper amplitude adjustement
+on PHY_RUNNING state and disable on any other state.
+
+Fixes: b4df02b562f4 ("net: phy: at803x: add support for qca 8327 A variant internal phy")
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 33 +++++++++++++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -87,6 +87,8 @@
+ #define AT803X_PSSR_MR_AN_COMPLETE 0x0200
+
+ #define AT803X_DEBUG_REG_0 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_REG_5 0x05
+@@ -1222,9 +1224,37 @@ static int qca83xx_config_init(struct ph
+ break;
+ }
+
++ /* 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_REG_0,
++ QCA8327_DEBUG_MANU_CTRL_EN, 0);
++
+ return 0;
+ }
+
++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)
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
++ QCA8327_DEBUG_MANU_CTRL_EN,
++ QCA8327_DEBUG_MANU_CTRL_EN);
++ } else {
++ /* Reset DAC Amplitude adjustment */
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
++ QCA8327_DEBUG_MANU_CTRL_EN, 0);
++ }
++}
++
+ static int qca83xx_resume(struct phy_device *phydev)
+ {
+ int ret, val;
+@@ -1379,6 +1409,7 @@ 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,
+@@ -1394,6 +1425,7 @@ static struct phy_driver at803x_driver[]
+ .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 = qca83xx_config_init,
+@@ -1409,6 +1441,7 @@ static struct phy_driver at803x_driver[]
+ .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 = qca83xx_config_init,
--- /dev/null
+From 9d1c29b4028557a496be9c5eb2b4b86063700636 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 10 Oct 2021 00:46:17 +0200
+Subject: net: phy: at803x: enable prefer master for 83xx internal phy
+
+From original QCA source code the port was set to prefer master as port
+type in 1000BASE-T mode. Apply the same settings also here.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1233,6 +1233,9 @@ static int qca83xx_config_init(struct ph
+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
+ 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;
+ }
+
--- /dev/null
+From 67999555ff42e91de7654488d9a7735bd9e84555 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 10 Oct 2021 00:46:18 +0200
+Subject: net: phy: at803x: better describe debug regs
+
+Give a name to known debug regs from Documentation instead of using
+unknown hex values.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 30 +++++++++++++++---------------
+ 1 file changed, 15 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -86,12 +86,12 @@
+ #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
+ #define AT803X_PSSR_MR_AN_COMPLETE 0x0200
+
+-#define AT803X_DEBUG_REG_0 0x00
++#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_REG_5 0x05
++#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05
+ #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
+
+ #define AT803X_DEBUG_REG_HIB_CTRL 0x0b
+@@ -100,7 +100,7 @@
+
+ #define AT803X_DEBUG_REG_3C 0x3C
+
+-#define AT803X_DEBUG_REG_3D 0x3D
++#define AT803X_DEBUG_REG_GREEN 0x3D
+ #define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6)
+
+ #define AT803X_DEBUG_REG_1F 0x1F
+@@ -274,25 +274,25 @@ static int at803x_read_page(struct phy_d
+
+ static int at803x_enable_rx_delay(struct phy_device *phydev)
+ {
+- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0,
++ 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_REG_5, 0,
++ 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_REG_0,
++ 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_REG_5,
++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE,
+ AT803X_DEBUG_TX_CLK_DLY_EN, 0);
+ }
+
+@@ -1208,9 +1208,9 @@ static int qca83xx_config_init(struct ph
+ switch (switch_revision) {
+ case 1:
+ /* For 100M waveform */
+- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_0, 0x02ea);
++ at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
+ /* Turn on Gigabit clock */
+- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x68a0);
++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
+ break;
+
+ case 2:
+@@ -1218,8 +1218,8 @@ static int qca83xx_config_init(struct ph
+ fallthrough;
+ case 4:
+ phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
+- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x6860);
+- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_5, 0x2c46);
++ 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;
+ }
+@@ -1230,7 +1230,7 @@ static int qca83xx_config_init(struct ph
+ */
+ if (phydev->drv->phy_id == QCA8327_A_PHY_ID ||
+ phydev->drv->phy_id == QCA8327_B_PHY_ID)
+- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_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 */
+@@ -1248,12 +1248,12 @@ static void qca83xx_link_change_notify(s
+ /* 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_REG_0,
++ 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_REG_0,
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
+ }
+ }
+@@ -1300,7 +1300,7 @@ static int qca83xx_suspend(struct phy_de
+ phy_modify(phydev, MII_BMCR, mask, 0);
+ }
+
+- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_3D,
++ 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,
--- /dev/null
+From d8b6f5bae6d3b648a67b6958cb98e4e97256d652 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:06 +0200
+Subject: dsa: qca8k: add mac_power_sel support
+
+Add missing mac power sel support needed for ipq8064/5 SoC that require
+1.8v for the internal regulator port instead of the default 1.5v.
+If other device needs this, consider adding a dedicated binding to
+support this.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 31 +++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 5 +++++
+ 2 files changed, 36 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -951,6 +951,33 @@ qca8k_setup_of_rgmii_delay(struct qca8k_
+ }
+
+ static int
++qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
++{
++ u32 mask = 0;
++ int ret = 0;
++
++ /* SoC specific settings for ipq8064.
++ * If more device require this consider adding
++ * a dedicated binding.
++ */
++ if (of_machine_is_compatible("qcom,ipq8064"))
++ mask |= QCA8K_MAC_PWR_RGMII0_1_8V;
++
++ /* SoC specific settings for ipq8065 */
++ if (of_machine_is_compatible("qcom,ipq8065"))
++ mask |= QCA8K_MAC_PWR_RGMII1_1_8V;
++
++ if (mask) {
++ ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL,
++ QCA8K_MAC_PWR_RGMII0_1_8V |
++ QCA8K_MAC_PWR_RGMII1_1_8V,
++ mask);
++ }
++
++ return ret;
++}
++
++static int
+ qca8k_setup(struct dsa_switch *ds)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+@@ -979,6 +1006,10 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
++ ret = qca8k_setup_mac_pwr_sel(priv);
++ if (ret)
++ return ret;
++
+ /* Enable CPU Port */
+ ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
+ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -100,6 +100,11 @@
+ #define QCA8K_SGMII_MODE_CTRL_PHY (1 << 22)
+ #define QCA8K_SGMII_MODE_CTRL_MAC (2 << 22)
+
++/* MAC_PWR_SEL registers */
++#define QCA8K_REG_MAC_PWR_SEL 0x0e4
++#define QCA8K_MAC_PWR_RGMII1_1_8V BIT(18)
++#define QCA8K_MAC_PWR_RGMII0_1_8V BIT(19)
++
+ /* EEE control registers */
+ #define QCA8K_REG_EEE_CTRL 0x100
+ #define QCA8K_REG_EEE_CTRL_LPI_EN(_i) ((_i + 1) * 2)
--- /dev/null
+From fdbf35df9c091db9c46e57e9938e3f7a4f603a7c Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:07 +0200
+Subject: dt-bindings: net: dsa: qca8k: Add SGMII clock phase properties
+
+Add names and descriptions of additional PORT0_PAD_CTRL properties.
+qca,sgmii-(rx|tx)clk-falling-edge are for setting the respective clock
+phase to failling edge.
+
+Co-developed-by: Matthew Hagan <mnhagan88@gmail.com>
+Signed-off-by: Matthew Hagan <mnhagan88@gmail.com>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -37,6 +37,10 @@ A CPU port node has the following option
+ managed entity. See
+ Documentation/devicetree/bindings/net/fixed-link.txt
+ for details.
++- qca,sgmii-rxclk-falling-edge: Set the receive clock phase to falling edge.
++ Mostly used in qca8327 with CPU port 0 set to
++ sgmii.
++- qca,sgmii-txclk-falling-edge: Set the transmit clock phase to falling edge.
+
+ For QCA8K the 'fixed-link' sub-node supports only the following properties:
+
--- /dev/null
+From 6c43809bf1bee76c434e365a26546a92a5fbec14 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:08 +0200
+Subject: net: dsa: qca8k: add support for sgmii falling edge
+
+Add support for this in the qca8k driver. Also add support for SGMII
+rx/tx clock falling edge. This is only present for pad0, pad5 and
+pad6 have these bit reserved from Documentation. Add a comment that this
+is hardcoded to PAD0 as qca8327/28/34/37 have an unique sgmii line and
+setting falling in port0 applies to both configuration with sgmii used
+for port0 or port6.
+
+Co-developed-by: Matthew Hagan <mnhagan88@gmail.com>
+Signed-off-by: Matthew Hagan <mnhagan88@gmail.com>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 4 ++++
+ 2 files changed, 67 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -978,6 +978,42 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri
+ }
+
+ static int
++qca8k_parse_port_config(struct qca8k_priv *priv)
++{
++ struct device_node *port_dn;
++ phy_interface_t mode;
++ struct dsa_port *dp;
++ int port, ret;
++
++ /* We have 2 CPU port. Check them */
++ for (port = 0; port < QCA8K_NUM_PORTS; port++) {
++ /* Skip every other port */
++ if (port != 0 && port != 6)
++ continue;
++
++ dp = dsa_to_port(priv->ds, port);
++ port_dn = dp->dn;
++
++ if (!of_device_is_available(port_dn))
++ continue;
++
++ ret = of_get_phy_mode(port_dn, &mode);
++ if (ret)
++ continue;
++
++ if (mode == PHY_INTERFACE_MODE_SGMII) {
++ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
++ priv->sgmii_tx_clk_falling_edge = true;
++
++ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
++ priv->sgmii_rx_clk_falling_edge = true;
++ }
++ }
++
++ return 0;
++}
++
++static int
+ qca8k_setup(struct dsa_switch *ds)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+@@ -990,6 +1026,11 @@ qca8k_setup(struct dsa_switch *ds)
+ return -EINVAL;
+ }
+
++ /* Parse CPU port config to be later used in phy_link mac_config */
++ ret = qca8k_parse_port_config(priv);
++ if (ret)
++ return ret;
++
+ mutex_init(&priv->reg_mutex);
+
+ /* Start by setting up the register mapping */
+@@ -1274,6 +1315,28 @@ qca8k_phylink_mac_config(struct dsa_swit
+ }
+
+ qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val);
++
++ /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and
++ * falling edge is set writing in the PORT0 PAD reg
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8327 ||
++ priv->switch_id == QCA8K_ID_QCA8337)
++ reg = QCA8K_REG_PORT0_PAD_CTRL;
++
++ val = 0;
++
++ /* SGMII Clock phase configuration */
++ if (priv->sgmii_rx_clk_falling_edge)
++ val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE;
++
++ if (priv->sgmii_tx_clk_falling_edge)
++ val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
++
++ if (val)
++ ret = qca8k_rmw(priv, reg,
++ QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
++ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
++ val);
+ break;
+ default:
+ dev_err(ds->dev, "xMII mode %s not supported for port %d\n",
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -35,6 +35,8 @@
+ #define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8)
+ #define QCA8K_MASK_CTRL_DEVICE_ID(x) ((x) >> 8)
+ #define QCA8K_REG_PORT0_PAD_CTRL 0x004
++#define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19)
++#define QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE BIT(18)
+ #define QCA8K_REG_PORT5_PAD_CTRL 0x008
+ #define QCA8K_REG_PORT6_PAD_CTRL 0x00c
+ #define QCA8K_PORT_PAD_RGMII_EN BIT(26)
+@@ -260,6 +262,8 @@ struct qca8k_priv {
+ u8 switch_revision;
+ u8 rgmii_tx_delay;
+ u8 rgmii_rx_delay;
++ bool sgmii_rx_clk_falling_edge;
++ bool sgmii_tx_clk_falling_edge;
+ bool legacy_phy_port_mapping;
+ struct regmap *regmap;
+ struct mii_bus *bus;
--- /dev/null
+From 731d613338ec6de482053ffa3f71be2325b0f8eb Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:09 +0200
+Subject: dt-bindings: net: dsa: qca8k: Document support for CPU port 6
+
+The switch now support CPU port to be set 6 instead of be hardcoded to
+0. Document support for it and describe logic selection.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -29,7 +29,11 @@ the mdio MASTER is used as communication
+ Don't use mixed external and internal mdio-bus configurations, as this is
+ not supported by the hardware.
+
+-The CPU port of this switch is always port 0.
++This switch support 2 CPU port. Normally and advised configuration is with
++CPU port set to port 0. It is also possible to set the CPU port to port 6
++if the device requires it. The driver will configure the switch to the defined
++port. With both CPU port declared the first CPU port is selected as primary
++and the secondary CPU ignored.
+
+ A CPU port node has the following optional node:
+
--- /dev/null
+From 3fcf734aa482487df83cf8f18608438fcf59127f Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:10 +0200
+Subject: net: dsa: qca8k: add support for cpu port 6
+
+Currently CPU port is always hardcoded to port 0. This switch have 2 CPU
+ports. The original intention of this driver seems to be use the
+mac06_exchange bit to swap MAC0 with MAC6 in the strange configuration
+where device have connected only the CPU port 6. To skip the
+introduction of a new binding, rework the driver to address the
+secondary CPU port as primary and drop any reference of hardcoded port.
+With configuration of mac06 exchange, just skip the definition of port0
+and define the CPU port as a secondary. The driver will autoconfigure
+the switch to use that as the primary CPU port.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 51 ++++++++++++++++++++++++++++++++++---------------
+ drivers/net/dsa/qca8k.h | 2 --
+ 2 files changed, 36 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -977,6 +977,22 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri
+ return ret;
+ }
+
++static int qca8k_find_cpu_port(struct dsa_switch *ds)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ /* Find the connected cpu port. Valid port are 0 or 6 */
++ if (dsa_is_cpu_port(ds, 0))
++ return 0;
++
++ dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
++
++ if (dsa_is_cpu_port(ds, 6))
++ return 6;
++
++ return -EINVAL;
++}
++
+ static int
+ qca8k_parse_port_config(struct qca8k_priv *priv)
+ {
+@@ -1017,13 +1033,13 @@ static int
+ qca8k_setup(struct dsa_switch *ds)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int ret, i;
++ int cpu_port, ret, i;
+ u32 mask;
+
+- /* Make sure that port 0 is the cpu port */
+- if (!dsa_is_cpu_port(ds, 0)) {
+- dev_err(priv->dev, "port 0 is not the CPU port");
+- return -EINVAL;
++ cpu_port = qca8k_find_cpu_port(ds);
++ if (cpu_port < 0) {
++ dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
++ return cpu_port;
+ }
+
+ /* Parse CPU port config to be later used in phy_link mac_config */
+@@ -1065,7 +1081,7 @@ qca8k_setup(struct dsa_switch *ds)
+ dev_warn(priv->dev, "mib init failed");
+
+ /* Enable QCA header mode on the cpu port */
+- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_CPU_PORT),
++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(cpu_port),
+ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
+ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
+ if (ret) {
+@@ -1087,10 +1103,10 @@ qca8k_setup(struct dsa_switch *ds)
+
+ /* Forward all unknown frames to CPU port for Linux processing */
+ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
+ if (ret)
+ return ret;
+
+@@ -1098,7 +1114,7 @@ qca8k_setup(struct dsa_switch *ds)
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+ /* CPU port gets connected to all user ports of the switch */
+ if (dsa_is_cpu_port(ds, i)) {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT),
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
+ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
+ if (ret)
+ return ret;
+@@ -1110,7 +1126,7 @@ qca8k_setup(struct dsa_switch *ds)
+
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+ QCA8K_PORT_LOOKUP_MEMBER,
+- BIT(QCA8K_CPU_PORT));
++ BIT(cpu_port));
+ if (ret)
+ return ret;
+
+@@ -1616,9 +1632,12 @@ static int
+ qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int port_mask = BIT(QCA8K_CPU_PORT);
++ 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 = 1; i < QCA8K_NUM_PORTS; i++) {
+ if (dsa_to_port(ds, i)->bridge_dev != br)
+ continue;
+@@ -1645,7 +1664,9 @@ static void
+ qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int i;
++ int cpu_port, i;
++
++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+
+ for (i = 1; i < QCA8K_NUM_PORTS; i++) {
+ if (dsa_to_port(ds, i)->bridge_dev != br)
+@@ -1662,7 +1683,7 @@ qca8k_port_bridge_leave(struct dsa_switc
+ * this port
+ */
+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_MEMBER, BIT(QCA8K_CPU_PORT));
++ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+ }
+
+ static int
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -24,8 +24,6 @@
+
+ #define QCA8K_NUM_FDB_RECORDS 2048
+
+-#define QCA8K_CPU_PORT 0
+-
+ #define QCA8K_PORT_VID_DEF 1
+
+ /* Global control registers */
--- /dev/null
+From 5654ec78dd7e64b1e04777b24007344329e6a63b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:11 +0200
+Subject: net: dsa: qca8k: rework rgmii delay logic and scan for cpu port 6
+
+Future proof commit. This switch have 2 CPU ports and one valid
+configuration is first CPU port set to sgmii and second CPU port set to
+rgmii-id. The current implementation detects delay only for CPU port
+zero set to rgmii and doesn't count any delay set in a secondary CPU
+port. Drop the current delay scan function and move it to the sgmii
+parser function to generalize and implicitly add support for secondary
+CPU port set to rgmii-id. Introduce new logic where delay is enabled
+also with internal delay binding declared and rgmii set as PHY mode.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 165 ++++++++++++++++++++++++------------------------
+ drivers/net/dsa/qca8k.h | 10 ++-
+ 2 files changed, 89 insertions(+), 86 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -889,68 +889,6 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ }
+
+ static int
+-qca8k_setup_of_rgmii_delay(struct qca8k_priv *priv)
+-{
+- struct device_node *port_dn;
+- phy_interface_t mode;
+- struct dsa_port *dp;
+- u32 val;
+-
+- /* CPU port is already checked */
+- dp = dsa_to_port(priv->ds, 0);
+-
+- port_dn = dp->dn;
+-
+- /* Check if port 0 is set to the correct type */
+- of_get_phy_mode(port_dn, &mode);
+- if (mode != PHY_INTERFACE_MODE_RGMII_ID &&
+- mode != PHY_INTERFACE_MODE_RGMII_RXID &&
+- mode != PHY_INTERFACE_MODE_RGMII_TXID) {
+- return 0;
+- }
+-
+- switch (mode) {
+- case PHY_INTERFACE_MODE_RGMII_ID:
+- case PHY_INTERFACE_MODE_RGMII_RXID:
+- if (of_property_read_u32(port_dn, "rx-internal-delay-ps", &val))
+- val = 2;
+- else
+- /* Switch regs accept value in ns, convert ps to ns */
+- val = val / 1000;
+-
+- if (val > QCA8K_MAX_DELAY) {
+- dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
+- val = 3;
+- }
+-
+- priv->rgmii_rx_delay = val;
+- /* Stop here if we need to check only for rx delay */
+- if (mode != PHY_INTERFACE_MODE_RGMII_ID)
+- break;
+-
+- fallthrough;
+- case PHY_INTERFACE_MODE_RGMII_TXID:
+- if (of_property_read_u32(port_dn, "tx-internal-delay-ps", &val))
+- val = 1;
+- else
+- /* Switch regs accept value in ns, convert ps to ns */
+- val = val / 1000;
+-
+- if (val > QCA8K_MAX_DELAY) {
+- dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
+- val = 3;
+- }
+-
+- priv->rgmii_tx_delay = val;
+- break;
+- default:
+- return 0;
+- }
+-
+- return 0;
+-}
+-
+-static int
+ qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
+ {
+ u32 mask = 0;
+@@ -996,19 +934,21 @@ static int qca8k_find_cpu_port(struct ds
+ static int
+ qca8k_parse_port_config(struct qca8k_priv *priv)
+ {
++ int port, cpu_port_index = 0, ret;
+ struct device_node *port_dn;
+ phy_interface_t mode;
+ struct dsa_port *dp;
+- int port, ret;
++ u32 delay;
+
+ /* We have 2 CPU port. Check them */
+- for (port = 0; port < QCA8K_NUM_PORTS; port++) {
++ for (port = 0; port < QCA8K_NUM_PORTS && cpu_port_index < QCA8K_NUM_CPU_PORTS; port++) {
+ /* Skip every other port */
+ if (port != 0 && port != 6)
+ continue;
+
+ dp = dsa_to_port(priv->ds, port);
+ port_dn = dp->dn;
++ cpu_port_index++;
+
+ if (!of_device_is_available(port_dn))
+ continue;
+@@ -1017,12 +957,54 @@ qca8k_parse_port_config(struct qca8k_pri
+ if (ret)
+ continue;
+
+- if (mode == PHY_INTERFACE_MODE_SGMII) {
++ switch (mode) {
++ case PHY_INTERFACE_MODE_RGMII:
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ delay = 0;
++
++ if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay))
++ /* Switch regs accept value in ns, convert ps to ns */
++ delay = delay / 1000;
++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_TXID)
++ delay = 1;
++
++ if (delay > QCA8K_MAX_DELAY) {
++ dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
++ delay = 3;
++ }
++
++ priv->rgmii_tx_delay[cpu_port_index] = delay;
++
++ delay = 0;
++
++ if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay))
++ /* Switch regs accept value in ns, convert ps to ns */
++ delay = delay / 1000;
++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_RXID)
++ delay = 2;
++
++ if (delay > QCA8K_MAX_DELAY) {
++ dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
++ delay = 3;
++ }
++
++ priv->rgmii_rx_delay[cpu_port_index] = delay;
++
++ break;
++ case PHY_INTERFACE_MODE_SGMII:
+ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
+ priv->sgmii_tx_clk_falling_edge = true;
+
+ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
+ priv->sgmii_rx_clk_falling_edge = true;
++
++ break;
++ default:
++ continue;
+ }
+ }
+
+@@ -1059,10 +1041,6 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
+- ret = qca8k_setup_of_rgmii_delay(priv);
+- if (ret)
+- return ret;
+-
+ ret = qca8k_setup_mac_pwr_sel(priv);
+ if (ret)
+ return ret;
+@@ -1229,8 +1207,8 @@ qca8k_phylink_mac_config(struct dsa_swit
+ const struct phylink_link_state *state)
+ {
+ struct qca8k_priv *priv = ds->priv;
+- u32 reg, val;
+- int ret;
++ int cpu_port_index, ret;
++ u32 reg, val, delay;
+
+ switch (port) {
+ case 0: /* 1st CPU port */
+@@ -1242,6 +1220,7 @@ qca8k_phylink_mac_config(struct dsa_swit
+ return;
+
+ reg = QCA8K_REG_PORT0_PAD_CTRL;
++ cpu_port_index = QCA8K_CPU_PORT0;
+ break;
+ case 1:
+ case 2:
+@@ -1260,6 +1239,7 @@ qca8k_phylink_mac_config(struct dsa_swit
+ return;
+
+ reg = QCA8K_REG_PORT6_PAD_CTRL;
++ cpu_port_index = QCA8K_CPU_PORT6;
+ break;
+ default:
+ dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port);
+@@ -1274,23 +1254,40 @@ qca8k_phylink_mac_config(struct dsa_swit
+
+ switch (state->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+- /* RGMII mode means no delay so don't enable the delay */
+- qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
+- break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+- /* RGMII_ID needs internal delay. This is enabled through
+- * PORT5_PAD_CTRL for all ports, rather than individual port
+- * registers
++ val = QCA8K_PORT_PAD_RGMII_EN;
++
++ /* Delay can be declared in 3 different way.
++ * Mode to rgmii and internal-delay standard binding defined
++ * rgmii-id or rgmii-tx/rx phy mode set.
++ * The parse logic set a delay different than 0 only when one
++ * of the 3 different way is used. In all other case delay is
++ * not enabled. With ID or TX/RXID delay is enabled and set
++ * to the default and recommended value.
++ */
++ if (priv->rgmii_tx_delay[cpu_port_index]) {
++ delay = priv->rgmii_tx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
++ }
++
++ if (priv->rgmii_rx_delay[cpu_port_index]) {
++ delay = priv->rgmii_rx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
++ }
++
++ /* Set RGMII delay based on the selected values */
++ qca8k_write(priv, reg, val);
++
++ /* QCA8337 requires to set rgmii rx delay for all ports.
++ * This is enabled through PORT5_PAD_CTRL for all ports,
++ * rather than individual port registers.
+ */
+- qca8k_write(priv, reg,
+- QCA8K_PORT_PAD_RGMII_EN |
+- QCA8K_PORT_PAD_RGMII_TX_DELAY(priv->rgmii_tx_delay) |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY(priv->rgmii_rx_delay) |
+- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+- /* QCA8337 requires to set rgmii rx delay */
+ if (priv->switch_id == QCA8K_ID_QCA8337)
+ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
+ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -13,6 +13,7 @@
+ #include <linux/gpio.h>
+
+ #define QCA8K_NUM_PORTS 7
++#define QCA8K_NUM_CPU_PORTS 2
+ #define QCA8K_MAX_MTU 9000
+
+ #define PHY_ID_QCA8327 0x004dd034
+@@ -255,13 +256,18 @@ struct qca8k_match_data {
+ u8 id;
+ };
+
++enum {
++ QCA8K_CPU_PORT0,
++ QCA8K_CPU_PORT6,
++};
++
+ struct qca8k_priv {
+ u8 switch_id;
+ u8 switch_revision;
+- u8 rgmii_tx_delay;
+- u8 rgmii_rx_delay;
+ bool sgmii_rx_clk_falling_edge;
+ bool sgmii_tx_clk_falling_edge;
++ u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
++ u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
+ bool legacy_phy_port_mapping;
+ struct regmap *regmap;
+ struct mii_bus *bus;
--- /dev/null
+From 13ad5ccc093ff448b99ac7e138e91e78796adb48 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:12 +0200
+Subject: dt-bindings: net: dsa: qca8k: Document qca,sgmii-enable-pll
+
+Document qca,sgmii-enable-pll binding used in the CPU nodes to
+enable SGMII PLL on MAC config.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -45,6 +45,16 @@ A CPU port node has the following option
+ Mostly used in qca8327 with CPU port 0 set to
+ sgmii.
+ - qca,sgmii-txclk-falling-edge: Set the transmit clock phase to falling edge.
++- qca,sgmii-enable-pll : For SGMII CPU port, explicitly enable PLL, TX and RX
++ chain along with Signal Detection.
++ This should NOT be enabled for qca8327. If enabled with
++ qca8327 the sgmii port won't correctly init and an err
++ is printed.
++ This can be required for qca8337 switch with revision 2.
++ A warning is displayed when used with revision greater
++ 2.
++ With CPU port set to sgmii and qca8337 it is advised
++ to set this unless a communication problem is observed.
+
+ For QCA8K the 'fixed-link' sub-node supports only the following properties:
+
--- /dev/null
+From bbc4799e8bb6c397e3b3fec13de68e179f5db9ff Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:13 +0200
+Subject: net: dsa: qca8k: add explicit SGMII PLL enable
+
+Support enabling PLL on the SGMII CPU port. Some device require this
+special configuration or no traffic is transmitted and the switch
+doesn't work at all. A dedicated binding is added to the CPU node
+port to apply the correct reg on mac config.
+Fail to correctly configure sgmii with qca8327 switch and warn if pll is
+used on qca8337 with a revision greater than 1.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 19 +++++++++++++++++--
+ drivers/net/dsa/qca8k.h | 1 +
+ 2 files changed, 18 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1002,6 +1002,18 @@ qca8k_parse_port_config(struct qca8k_pri
+ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
+ priv->sgmii_rx_clk_falling_edge = true;
+
++ if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) {
++ priv->sgmii_enable_pll = true;
++
++ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling");
++ priv->sgmii_enable_pll = false;
++ }
++
++ if (priv->switch_revision < 2)
++ dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more.");
++ }
++
+ break;
+ default:
+ continue;
+@@ -1312,8 +1324,11 @@ qca8k_phylink_mac_config(struct dsa_swit
+ if (ret)
+ return;
+
+- val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
+- QCA8K_SGMII_EN_TX | QCA8K_SGMII_EN_SD;
++ val |= QCA8K_SGMII_EN_SD;
++
++ if (priv->sgmii_enable_pll)
++ val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
++ QCA8K_SGMII_EN_TX;
+
+ if (dsa_is_cpu_port(ds, port)) {
+ /* CPU port, we're talking to the CPU MAC, be a PHY */
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -266,6 +266,7 @@ struct qca8k_priv {
+ u8 switch_revision;
+ bool sgmii_rx_clk_falling_edge;
+ bool sgmii_tx_clk_falling_edge;
++ bool sgmii_enable_pll;
+ u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
+ u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
+ bool legacy_phy_port_mapping;
--- /dev/null
+From 924087c5c3d41553700b0eb83ca2a53b91643dca Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:14 +0200
+Subject: dt-bindings: net: dsa: qca8k: Document qca,led-open-drain binding
+
+Document new binding qca,ignore-power-on-sel used to ignore
+power on strapping and use sw regs instead.
+Document qca,led-open.drain to set led to open drain mode, the
+qca,ignore-power-on-sel is mandatory with this enabled or an error will
+be reported.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -13,6 +13,17 @@ Required properties:
+ Optional properties:
+
+ - reset-gpios: GPIO to be used to reset the whole device
++- qca,ignore-power-on-sel: Ignore power on pin strapping to configure led open
++ drain or eeprom presence. This is needed for broken
++ devices that have wrong configuration or when the oem
++ decided to not use pin strapping and fallback to sw
++ regs.
++- qca,led-open-drain: Set leds to open-drain mode. This requires the
++ qca,ignore-power-on-sel to be set or the driver will fail
++ to probe. This is needed if the oem doesn't use pin
++ strapping to set this mode and prefers to set it using sw
++ regs. The pin strapping related to led open drain mode is
++ the pin B68 for QCA832x and B49 for QCA833x
+
+ Subnodes:
+
--- /dev/null
+From 362bb238d8bf1470424214a8a5968d9c6cce68fa Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:15 +0200
+Subject: net: dsa: qca8k: add support for pws config reg
+
+Some qca8327 switch require to force the ignore of power on sel
+strapping. Some switch require to set the led open drain mode in regs
+instead of using strapping. While most of the device implements this
+using the correct way using pin strapping, there are still some broken
+device that require to be set using sw regs.
+Introduce a new binding and support these special configuration.
+As led open drain require to ignore pin strapping to work, the probe
+fails with EINVAL error with incorrect configuration.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 39 +++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 6 ++++++
+ 2 files changed, 45 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -932,6 +932,41 @@ static int qca8k_find_cpu_port(struct ds
+ }
+
+ static int
++qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
++{
++ struct device_node *node = priv->dev->of_node;
++ u32 val = 0;
++ int ret;
++
++ /* QCA8327 require to set to the correct mode.
++ * His bigger brother QCA8328 have the 172 pin layout.
++ * Should be applied by default but we set this just to make sure.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN,
++ QCA8327_PWS_PACKAGE148_EN);
++ if (ret)
++ return ret;
++ }
++
++ if (of_property_read_bool(node, "qca,ignore-power-on-sel"))
++ val |= QCA8K_PWS_POWER_ON_SEL;
++
++ if (of_property_read_bool(node, "qca,led-open-drain")) {
++ if (!(val & QCA8K_PWS_POWER_ON_SEL)) {
++ dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set.");
++ return -EINVAL;
++ }
++
++ val |= QCA8K_PWS_LED_OPEN_EN_CSR;
++ }
++
++ return qca8k_rmw(priv, QCA8K_REG_PWS,
++ QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL,
++ val);
++}
++
++static int
+ qca8k_parse_port_config(struct qca8k_priv *priv)
+ {
+ int port, cpu_port_index = 0, ret;
+@@ -1053,6 +1088,10 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
++ ret = qca8k_setup_of_pws_reg(priv);
++ if (ret)
++ return ret;
++
+ ret = qca8k_setup_mac_pwr_sel(priv);
+ if (ret)
+ return ret;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -46,6 +46,12 @@
+ #define QCA8K_MAX_DELAY 3
+ #define QCA8K_PORT_PAD_SGMII_EN BIT(7)
+ #define QCA8K_REG_PWS 0x010
++#define QCA8K_PWS_POWER_ON_SEL BIT(31)
++/* This reg is only valid for QCA832x and toggle the package
++ * type from 176 pin (by default) to 148 pin used on QCA8327
++ */
++#define QCA8327_PWS_PACKAGE148_EN BIT(30)
++#define QCA8K_PWS_LED_OPEN_EN_CSR BIT(24)
+ #define QCA8K_PWS_SERDES_AEN_DIS BIT(7)
+ #define QCA8K_REG_MODULE_EN 0x030
+ #define QCA8K_MODULE_EN_MIB BIT(0)
--- /dev/null
+From ed7988d77fbfb79366b68f9e7fa60a6080da23d4 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:16 +0200
+Subject: dt-bindings: net: dsa: qca8k: document support for qca8328
+
+QCA8328 is the bigger brother of qca8327. Document the new compatible
+binding and add some information to understand the various switch
+compatible.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -3,9 +3,10 @@
+ Required properties:
+
+ - compatible: should be one of:
+- "qca,qca8327"
+- "qca,qca8334"
+- "qca,qca8337"
++ "qca,qca8328": referenced as AR8328(N)-AK1(A/B) QFN 176 pin package
++ "qca,qca8327": referenced as AR8327(N)-AL1A DR-QFN 148 pin package
++ "qca,qca8334": referenced as QCA8334-AL3C QFN 88 pin package
++ "qca,qca8337": referenced as QCA8337N-AL3(B/C) DR-QFN 148 pin package
+
+ - #size-cells: must be 0
+ - #address-cells: must be 1
--- /dev/null
+From f477d1c8bdbef4f400718238e350f16f521d2a3e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:17 +0200
+Subject: net: dsa: qca8k: add support for QCA8328
+
+QCA8328 switch is the bigger brother of the qca8327. Same regs different
+chip. Change the function to set the correct pin layout and introduce a
+new match_data to differentiate the 2 switch as they have the same ID
+and their internal PHY have the same ID.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 19 ++++++++++++++++---
+ drivers/net/dsa/qca8k.h | 1 +
+ 2 files changed, 17 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -935,6 +935,7 @@ static int
+ qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
+ {
+ struct device_node *node = priv->dev->of_node;
++ const struct qca8k_match_data *data;
+ u32 val = 0;
+ int ret;
+
+@@ -943,8 +944,14 @@ qca8k_setup_of_pws_reg(struct qca8k_priv
+ * Should be applied by default but we set this just to make sure.
+ */
+ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ data = of_device_get_match_data(priv->dev);
++
++ /* Set the correct package of 148 pin for QCA8327 */
++ if (data->reduced_package)
++ val |= QCA8327_PWS_PACKAGE148_EN;
++
+ ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN,
+- QCA8327_PWS_PACKAGE148_EN);
++ val);
+ if (ret)
+ return ret;
+ }
+@@ -2098,7 +2105,12 @@ static int qca8k_resume(struct device *d
+ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+ qca8k_suspend, qca8k_resume);
+
+-static const struct qca8k_match_data qca832x = {
++static const struct qca8k_match_data qca8327 = {
++ .id = QCA8K_ID_QCA8327,
++ .reduced_package = true,
++};
++
++static const struct qca8k_match_data qca8328 = {
+ .id = QCA8K_ID_QCA8327,
+ };
+
+@@ -2107,7 +2119,8 @@ static const struct qca8k_match_data qca
+ };
+
+ static const struct of_device_id qca8k_of_match[] = {
+- { .compatible = "qca,qca8327", .data = &qca832x },
++ { .compatible = "qca,qca8327", .data = &qca8327 },
++ { .compatible = "qca,qca8328", .data = &qca8328 },
+ { .compatible = "qca,qca8334", .data = &qca833x },
+ { .compatible = "qca,qca8337", .data = &qca833x },
+ { /* sentinel */ },
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -260,6 +260,7 @@ struct ar8xxx_port_status {
+
+ struct qca8k_match_data {
+ u8 id;
++ bool reduced_package;
+ };
+
+ enum {
--- /dev/null
+From cef08115846e581f80ff99abf7bf218da1840616 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:18 +0200
+Subject: net: dsa: qca8k: set internal delay also for sgmii
+
+QCA original code report port instability and sa that SGMII also require
+to set internal delay. Generalize the rgmii delay function and apply the
+advised value if they are not defined in DT.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 88 +++++++++++++++++++++++++++++++++----------------
+ drivers/net/dsa/qca8k.h | 2 ++
+ 2 files changed, 62 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1004,6 +1004,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
++ case PHY_INTERFACE_MODE_SGMII:
+ delay = 0;
+
+ if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay))
+@@ -1036,8 +1037,13 @@ qca8k_parse_port_config(struct qca8k_pri
+
+ priv->rgmii_rx_delay[cpu_port_index] = delay;
+
+- break;
+- case PHY_INTERFACE_MODE_SGMII:
++ /* Skip sgmii parsing for rgmii* mode */
++ if (mode == PHY_INTERFACE_MODE_RGMII ||
++ mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_TXID ||
++ mode == PHY_INTERFACE_MODE_RGMII_RXID)
++ break;
++
+ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
+ priv->sgmii_tx_clk_falling_edge = true;
+
+@@ -1261,12 +1267,53 @@ qca8k_setup(struct dsa_switch *ds)
+ }
+
+ static void
++qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index,
++ u32 reg)
++{
++ u32 delay, val = 0;
++ int ret;
++
++ /* Delay can be declared in 3 different way.
++ * Mode to rgmii and internal-delay standard binding defined
++ * rgmii-id or rgmii-tx/rx phy mode set.
++ * The parse logic set a delay different than 0 only when one
++ * of the 3 different way is used. In all other case delay is
++ * not enabled. With ID or TX/RXID delay is enabled and set
++ * to the default and recommended value.
++ */
++ if (priv->rgmii_tx_delay[cpu_port_index]) {
++ delay = priv->rgmii_tx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
++ }
++
++ if (priv->rgmii_rx_delay[cpu_port_index]) {
++ delay = priv->rgmii_rx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
++ }
++
++ /* Set RGMII delay based on the selected values */
++ ret = qca8k_rmw(priv, reg,
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK |
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN,
++ val);
++ if (ret)
++ dev_err(priv->dev, "Failed to set internal delay for CPU port%d",
++ cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6);
++}
++
++static void
+ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+ const struct phylink_link_state *state)
+ {
+ struct qca8k_priv *priv = ds->priv;
+ int cpu_port_index, ret;
+- u32 reg, val, delay;
++ u32 reg, val;
+
+ switch (port) {
+ case 0: /* 1st CPU port */
+@@ -1315,32 +1362,10 @@ qca8k_phylink_mac_config(struct dsa_swit
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+- val = QCA8K_PORT_PAD_RGMII_EN;
+-
+- /* Delay can be declared in 3 different way.
+- * Mode to rgmii and internal-delay standard binding defined
+- * rgmii-id or rgmii-tx/rx phy mode set.
+- * The parse logic set a delay different than 0 only when one
+- * of the 3 different way is used. In all other case delay is
+- * not enabled. With ID or TX/RXID delay is enabled and set
+- * to the default and recommended value.
+- */
+- if (priv->rgmii_tx_delay[cpu_port_index]) {
+- delay = priv->rgmii_tx_delay[cpu_port_index];
+-
+- val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
+- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
+- }
+-
+- if (priv->rgmii_rx_delay[cpu_port_index]) {
+- delay = priv->rgmii_rx_delay[cpu_port_index];
+-
+- val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
+- }
++ qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
+
+- /* Set RGMII delay based on the selected values */
+- qca8k_write(priv, reg, val);
++ /* Configure rgmii delay */
++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
+
+ /* QCA8337 requires to set rgmii rx delay for all ports.
+ * This is enabled through PORT5_PAD_CTRL for all ports,
+@@ -1411,6 +1436,13 @@ qca8k_phylink_mac_config(struct dsa_swit
+ QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
+ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
+ val);
++
++ /* From original code is reported port instability as SGMII also
++ * require delay set. Apply advised values here or take them from DT.
++ */
++ if (state->interface == PHY_INTERFACE_MODE_SGMII)
++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
++
+ break;
+ default:
+ dev_err(ds->dev, "xMII mode %s not supported for port %d\n",
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -39,7 +39,9 @@
+ #define QCA8K_REG_PORT5_PAD_CTRL 0x008
+ #define QCA8K_REG_PORT6_PAD_CTRL 0x00c
+ #define QCA8K_PORT_PAD_RGMII_EN BIT(26)
++#define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 22)
+ #define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) ((x) << 22)
++#define QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK GENMASK(21, 20)
+ #define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) ((x) << 20)
+ #define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
+ #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
--- /dev/null
+From fd0bb28c547f7c8affb1691128cece38f5b626a1 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:19 +0200
+Subject: net: dsa: qca8k: move port config to dedicated struct
+
+Move ports related config to dedicated struct to keep things organized.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 26 +++++++++++++-------------
+ drivers/net/dsa/qca8k.h | 10 +++++++---
+ 2 files changed, 20 insertions(+), 16 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1019,7 +1019,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ delay = 3;
+ }
+
+- priv->rgmii_tx_delay[cpu_port_index] = delay;
++ priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay;
+
+ delay = 0;
+
+@@ -1035,7 +1035,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ delay = 3;
+ }
+
+- priv->rgmii_rx_delay[cpu_port_index] = delay;
++ priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay;
+
+ /* Skip sgmii parsing for rgmii* mode */
+ if (mode == PHY_INTERFACE_MODE_RGMII ||
+@@ -1045,17 +1045,17 @@ qca8k_parse_port_config(struct qca8k_pri
+ break;
+
+ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
+- priv->sgmii_tx_clk_falling_edge = true;
++ priv->ports_config.sgmii_tx_clk_falling_edge = true;
+
+ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
+- priv->sgmii_rx_clk_falling_edge = true;
++ priv->ports_config.sgmii_rx_clk_falling_edge = true;
+
+ if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) {
+- priv->sgmii_enable_pll = true;
++ priv->ports_config.sgmii_enable_pll = true;
+
+ if (priv->switch_id == QCA8K_ID_QCA8327) {
+ dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling");
+- priv->sgmii_enable_pll = false;
++ priv->ports_config.sgmii_enable_pll = false;
+ }
+
+ if (priv->switch_revision < 2)
+@@ -1281,15 +1281,15 @@ qca8k_mac_config_setup_internal_delay(st
+ * not enabled. With ID or TX/RXID delay is enabled and set
+ * to the default and recommended value.
+ */
+- if (priv->rgmii_tx_delay[cpu_port_index]) {
+- delay = priv->rgmii_tx_delay[cpu_port_index];
++ if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) {
++ delay = priv->ports_config.rgmii_tx_delay[cpu_port_index];
+
+ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
+ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
+ }
+
+- if (priv->rgmii_rx_delay[cpu_port_index]) {
+- delay = priv->rgmii_rx_delay[cpu_port_index];
++ if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) {
++ delay = priv->ports_config.rgmii_rx_delay[cpu_port_index];
+
+ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
+ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
+@@ -1397,7 +1397,7 @@ qca8k_phylink_mac_config(struct dsa_swit
+
+ val |= QCA8K_SGMII_EN_SD;
+
+- if (priv->sgmii_enable_pll)
++ if (priv->ports_config.sgmii_enable_pll)
+ val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
+ QCA8K_SGMII_EN_TX;
+
+@@ -1425,10 +1425,10 @@ qca8k_phylink_mac_config(struct dsa_swit
+ val = 0;
+
+ /* SGMII Clock phase configuration */
+- if (priv->sgmii_rx_clk_falling_edge)
++ if (priv->ports_config.sgmii_rx_clk_falling_edge)
+ val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE;
+
+- if (priv->sgmii_tx_clk_falling_edge)
++ if (priv->ports_config.sgmii_tx_clk_falling_edge)
+ val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
+
+ if (val)
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -270,15 +270,19 @@ enum {
+ QCA8K_CPU_PORT6,
+ };
+
+-struct qca8k_priv {
+- u8 switch_id;
+- u8 switch_revision;
++struct qca8k_ports_config {
+ bool sgmii_rx_clk_falling_edge;
+ bool sgmii_tx_clk_falling_edge;
+ bool sgmii_enable_pll;
+ u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
+ u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
++};
++
++struct qca8k_priv {
++ u8 switch_id;
++ u8 switch_revision;
+ bool legacy_phy_port_mapping;
++ struct qca8k_ports_config ports_config;
+ struct regmap *regmap;
+ struct mii_bus *bus;
+ struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
--- /dev/null
+From e52073a8e3086046a098b8a7cbeb282ff0cdb424 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:20 +0200
+Subject: dt-bindings: net: ipq8064-mdio: fix warning with new qca8k switch
+
+Fix warning now that we have qca8k switch Documentation using yaml.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/qcom,ipq8064-mdio.yaml | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/net/qcom,ipq8064-mdio.yaml
++++ b/Documentation/devicetree/bindings/net/qcom,ipq8064-mdio.yaml
+@@ -51,6 +51,9 @@ examples:
+ switch@10 {
+ compatible = "qca,qca8337";
+ reg = <0x10>;
+- /* ... */
++
++ ports {
++ /* ... */
++ };
+ };
+ };
--- /dev/null
+From d291fbb8245d5ba04979fed85575860a5cea7196 Mon Sep 17 00:00:00 2001
+From: Matthew Hagan <mnhagan88@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:21 +0200
+Subject: dt-bindings: net: dsa: qca8k: convert to YAML schema
+
+Convert the qca8k bindings to YAML format.
+
+Signed-off-by: Matthew Hagan <mnhagan88@gmail.com>
+Co-developed-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ .../devicetree/bindings/net/dsa/qca8k.txt | 245 --------------
+ .../devicetree/bindings/net/dsa/qca8k.yaml | 362 +++++++++++++++++++++
+ 2 files changed, 362 insertions(+), 245 deletions(-)
+ delete mode 100644 Documentation/devicetree/bindings/net/dsa/qca8k.txt
+ create mode 100644 Documentation/devicetree/bindings/net/dsa/qca8k.yaml
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ /dev/null
+@@ -1,245 +0,0 @@
+-* Qualcomm Atheros QCA8xxx switch family
+-
+-Required properties:
+-
+-- compatible: should be one of:
+- "qca,qca8328": referenced as AR8328(N)-AK1(A/B) QFN 176 pin package
+- "qca,qca8327": referenced as AR8327(N)-AL1A DR-QFN 148 pin package
+- "qca,qca8334": referenced as QCA8334-AL3C QFN 88 pin package
+- "qca,qca8337": referenced as QCA8337N-AL3(B/C) DR-QFN 148 pin package
+-
+-- #size-cells: must be 0
+-- #address-cells: must be 1
+-
+-Optional properties:
+-
+-- reset-gpios: GPIO to be used to reset the whole device
+-- qca,ignore-power-on-sel: Ignore power on pin strapping to configure led open
+- drain or eeprom presence. This is needed for broken
+- devices that have wrong configuration or when the oem
+- decided to not use pin strapping and fallback to sw
+- regs.
+-- qca,led-open-drain: Set leds to open-drain mode. This requires the
+- qca,ignore-power-on-sel to be set or the driver will fail
+- to probe. This is needed if the oem doesn't use pin
+- strapping to set this mode and prefers to set it using sw
+- regs. The pin strapping related to led open drain mode is
+- the pin B68 for QCA832x and B49 for QCA833x
+-
+-Subnodes:
+-
+-The integrated switch subnode should be specified according to the binding
+-described in dsa/dsa.txt. If the QCA8K switch is connect to a SoC's external
+-mdio-bus each subnode describing a port needs to have a valid phandle
+-referencing the internal PHY it is connected to. This is because there's no
+-N:N mapping of port and PHY id.
+-To declare the internal mdio-bus configuration, declare a mdio node in the
+-switch node and declare the phandle for the port referencing the internal
+-PHY is connected to. In this config a internal mdio-bus is registered and
+-the mdio MASTER is used as communication.
+-
+-Don't use mixed external and internal mdio-bus configurations, as this is
+-not supported by the hardware.
+-
+-This switch support 2 CPU port. Normally and advised configuration is with
+-CPU port set to port 0. It is also possible to set the CPU port to port 6
+-if the device requires it. The driver will configure the switch to the defined
+-port. With both CPU port declared the first CPU port is selected as primary
+-and the secondary CPU ignored.
+-
+-A CPU port node has the following optional node:
+-
+-- fixed-link : Fixed-link subnode describing a link to a non-MDIO
+- managed entity. See
+- Documentation/devicetree/bindings/net/fixed-link.txt
+- for details.
+-- qca,sgmii-rxclk-falling-edge: Set the receive clock phase to falling edge.
+- Mostly used in qca8327 with CPU port 0 set to
+- sgmii.
+-- qca,sgmii-txclk-falling-edge: Set the transmit clock phase to falling edge.
+-- qca,sgmii-enable-pll : For SGMII CPU port, explicitly enable PLL, TX and RX
+- chain along with Signal Detection.
+- This should NOT be enabled for qca8327. If enabled with
+- qca8327 the sgmii port won't correctly init and an err
+- is printed.
+- This can be required for qca8337 switch with revision 2.
+- A warning is displayed when used with revision greater
+- 2.
+- With CPU port set to sgmii and qca8337 it is advised
+- to set this unless a communication problem is observed.
+-
+-For QCA8K the 'fixed-link' sub-node supports only the following properties:
+-
+-- 'speed' (integer, mandatory), to indicate the link speed. Accepted
+- values are 10, 100 and 1000
+-- 'full-duplex' (boolean, optional), to indicate that full duplex is
+- used. When absent, half duplex is assumed.
+-
+-Examples:
+-
+-for the external mdio-bus configuration:
+-
+- &mdio0 {
+- phy_port1: phy@0 {
+- reg = <0>;
+- };
+-
+- phy_port2: phy@1 {
+- reg = <1>;
+- };
+-
+- phy_port3: phy@2 {
+- reg = <2>;
+- };
+-
+- phy_port4: phy@3 {
+- reg = <3>;
+- };
+-
+- phy_port5: phy@4 {
+- reg = <4>;
+- };
+-
+- switch@10 {
+- compatible = "qca,qca8337";
+- #address-cells = <1>;
+- #size-cells = <0>;
+-
+- reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>;
+- reg = <0x10>;
+-
+- ports {
+- #address-cells = <1>;
+- #size-cells = <0>;
+- port@0 {
+- reg = <0>;
+- label = "cpu";
+- ethernet = <&gmac1>;
+- phy-mode = "rgmii";
+- fixed-link {
+- speed = 1000;
+- full-duplex;
+- };
+- };
+-
+- port@1 {
+- reg = <1>;
+- label = "lan1";
+- phy-handle = <&phy_port1>;
+- };
+-
+- port@2 {
+- reg = <2>;
+- label = "lan2";
+- phy-handle = <&phy_port2>;
+- };
+-
+- port@3 {
+- reg = <3>;
+- label = "lan3";
+- phy-handle = <&phy_port3>;
+- };
+-
+- port@4 {
+- reg = <4>;
+- label = "lan4";
+- phy-handle = <&phy_port4>;
+- };
+-
+- port@5 {
+- reg = <5>;
+- label = "wan";
+- phy-handle = <&phy_port5>;
+- };
+- };
+- };
+- };
+-
+-for the internal master mdio-bus configuration:
+-
+- &mdio0 {
+- switch@10 {
+- compatible = "qca,qca8337";
+- #address-cells = <1>;
+- #size-cells = <0>;
+-
+- reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>;
+- reg = <0x10>;
+-
+- ports {
+- #address-cells = <1>;
+- #size-cells = <0>;
+-
+- port@0 {
+- reg = <0>;
+- label = "cpu";
+- ethernet = <&gmac1>;
+- phy-mode = "rgmii";
+- fixed-link {
+- speed = 1000;
+- full-duplex;
+- };
+- };
+-
+- port@1 {
+- reg = <1>;
+- label = "lan1";
+- phy-mode = "internal";
+- phy-handle = <&phy_port1>;
+- };
+-
+- port@2 {
+- reg = <2>;
+- label = "lan2";
+- phy-mode = "internal";
+- phy-handle = <&phy_port2>;
+- };
+-
+- port@3 {
+- reg = <3>;
+- label = "lan3";
+- phy-mode = "internal";
+- phy-handle = <&phy_port3>;
+- };
+-
+- port@4 {
+- reg = <4>;
+- label = "lan4";
+- phy-mode = "internal";
+- phy-handle = <&phy_port4>;
+- };
+-
+- port@5 {
+- reg = <5>;
+- label = "wan";
+- phy-mode = "internal";
+- phy-handle = <&phy_port5>;
+- };
+- };
+-
+- mdio {
+- #address-cells = <1>;
+- #size-cells = <0>;
+-
+- phy_port1: phy@0 {
+- reg = <0>;
+- };
+-
+- phy_port2: phy@1 {
+- reg = <1>;
+- };
+-
+- phy_port3: phy@2 {
+- reg = <2>;
+- };
+-
+- phy_port4: phy@3 {
+- reg = <3>;
+- };
+-
+- phy_port5: phy@4 {
+- reg = <4>;
+- };
+- };
+- };
+- };
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.yaml
+@@ -0,0 +1,362 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/net/dsa/qca8k.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Qualcomm Atheros QCA83xx switch family
++
++maintainers:
++ - John Crispin <john@phrozen.org>
++
++description:
++ If the QCA8K switch is connect to an SoC's external mdio-bus, each subnode
++ describing a port needs to have a valid phandle referencing the internal PHY
++ it is connected to. This is because there is no N:N mapping of port and PHY
++ ID. To declare the internal mdio-bus configuration, declare an MDIO node in
++ the switch node and declare the phandle for the port, referencing the internal
++ PHY it is connected to. In this config, an internal mdio-bus is registered and
++ the MDIO master is used for communication. Mixed external and internal
++ mdio-bus configurations are not supported by the hardware.
++
++properties:
++ compatible:
++ oneOf:
++ - enum:
++ - qca,qca8327
++ - qca,qca8328
++ - qca,qca8334
++ - qca,qca8337
++ description: |
++ qca,qca8328: referenced as AR8328(N)-AK1(A/B) QFN 176 pin package
++ qca,qca8327: referenced as AR8327(N)-AL1A DR-QFN 148 pin package
++ qca,qca8334: referenced as QCA8334-AL3C QFN 88 pin package
++ qca,qca8337: referenced as QCA8337N-AL3(B/C) DR-QFN 148 pin package
++
++ reg:
++ maxItems: 1
++
++ reset-gpios:
++ description:
++ GPIO to be used to reset the whole device
++ maxItems: 1
++
++ qca,ignore-power-on-sel:
++ $ref: /schemas/types.yaml#/definitions/flag
++ description:
++ Ignore power-on pin strapping to configure LED open-drain or EEPROM
++ presence. This is needed for devices with incorrect configuration or when
++ the OEM has decided not to use pin strapping and falls back to SW regs.
++
++ qca,led-open-drain:
++ $ref: /schemas/types.yaml#/definitions/flag
++ description:
++ Set LEDs to open-drain mode. This requires the qca,ignore-power-on-sel to
++ be set, otherwise the driver will fail at probe. This is required if the
++ OEM does not use pin strapping to set this mode and prefers to set it
++ using SW regs. The pin strappings related to LED open-drain mode are
++ B68 on the QCA832x and B49 on the QCA833x.
++
++ mdio:
++ type: object
++ description: Qca8k switch have an internal mdio to access switch port.
++ If this is not present, the legacy mapping is used and the
++ internal mdio access is used.
++ With the legacy mapping the reg corresponding to the internal
++ mdio is the switch reg with an offset of -1.
++
++ properties:
++ '#address-cells':
++ const: 1
++ '#size-cells':
++ const: 0
++
++ patternProperties:
++ "^(ethernet-)?phy@[0-4]$":
++ type: object
++
++ allOf:
++ - $ref: "http://devicetree.org/schemas/net/mdio.yaml#"
++
++ properties:
++ reg:
++ maxItems: 1
++
++ required:
++ - reg
++
++patternProperties:
++ "^(ethernet-)?ports$":
++ type: object
++ properties:
++ '#address-cells':
++ const: 1
++ '#size-cells':
++ const: 0
++
++ patternProperties:
++ "^(ethernet-)?port@[0-6]$":
++ type: object
++ description: Ethernet switch ports
++
++ properties:
++ reg:
++ description: Port number
++
++ label:
++ description:
++ Describes the label associated with this port, which will become
++ the netdev name
++ $ref: /schemas/types.yaml#/definitions/string
++
++ link:
++ description:
++ Should be a list of phandles to other switch's DSA port. This
++ port is used as the outgoing port towards the phandle ports. The
++ full routing information must be given, not just the one hop
++ routes to neighbouring switches
++ $ref: /schemas/types.yaml#/definitions/phandle-array
++
++ ethernet:
++ description:
++ Should be a phandle to a valid Ethernet device node. This host
++ device is what the switch port is connected to
++ $ref: /schemas/types.yaml#/definitions/phandle
++
++ phy-handle: true
++
++ phy-mode: true
++
++ fixed-link: true
++
++ mac-address: true
++
++ sfp: true
++
++ qca,sgmii-rxclk-falling-edge:
++ $ref: /schemas/types.yaml#/definitions/flag
++ description:
++ Set the receive clock phase to falling edge. Mostly commonly used on
++ the QCA8327 with CPU port 0 set to SGMII.
++
++ qca,sgmii-txclk-falling-edge:
++ $ref: /schemas/types.yaml#/definitions/flag
++ description:
++ Set the transmit clock phase to falling edge.
++
++ qca,sgmii-enable-pll:
++ $ref: /schemas/types.yaml#/definitions/flag
++ description:
++ For SGMII CPU port, explicitly enable PLL, TX and RX chain along with
++ Signal Detection. On the QCA8327 this should not be enabled, otherwise
++ the SGMII port will not initialize. When used on the QCA8337, revision 3
++ or greater, a warning will be displayed. When the CPU port is set to
++ SGMII on the QCA8337, it is advised to set this unless a communication
++ issue is observed.
++
++ required:
++ - reg
++
++ additionalProperties: false
++
++oneOf:
++ - required:
++ - ports
++ - required:
++ - ethernet-ports
++
++required:
++ - compatible
++ - reg
++
++additionalProperties: true
++
++examples:
++ - |
++ #include <dt-bindings/gpio/gpio.h>
++
++ mdio {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ external_phy_port1: ethernet-phy@0 {
++ reg = <0>;
++ };
++
++ external_phy_port2: ethernet-phy@1 {
++ reg = <1>;
++ };
++
++ external_phy_port3: ethernet-phy@2 {
++ reg = <2>;
++ };
++
++ external_phy_port4: ethernet-phy@3 {
++ reg = <3>;
++ };
++
++ external_phy_port5: ethernet-phy@4 {
++ reg = <4>;
++ };
++
++ switch@10 {
++ compatible = "qca,qca8337";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>;
++ reg = <0x10>;
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ port@0 {
++ reg = <0>;
++ label = "cpu";
++ ethernet = <&gmac1>;
++ phy-mode = "rgmii";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ port@1 {
++ reg = <1>;
++ label = "lan1";
++ phy-handle = <&external_phy_port1>;
++ };
++
++ port@2 {
++ reg = <2>;
++ label = "lan2";
++ phy-handle = <&external_phy_port2>;
++ };
++
++ port@3 {
++ reg = <3>;
++ label = "lan3";
++ phy-handle = <&external_phy_port3>;
++ };
++
++ port@4 {
++ reg = <4>;
++ label = "lan4";
++ phy-handle = <&external_phy_port4>;
++ };
++
++ port@5 {
++ reg = <5>;
++ label = "wan";
++ phy-handle = <&external_phy_port5>;
++ };
++ };
++ };
++ };
++ - |
++ #include <dt-bindings/gpio/gpio.h>
++
++ mdio {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ switch@10 {
++ compatible = "qca,qca8337";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>;
++ reg = <0x10>;
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ port@0 {
++ reg = <0>;
++ label = "cpu";
++ ethernet = <&gmac1>;
++ phy-mode = "rgmii";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ port@1 {
++ reg = <1>;
++ label = "lan1";
++ phy-mode = "internal";
++ phy-handle = <&internal_phy_port1>;
++ };
++
++ port@2 {
++ reg = <2>;
++ label = "lan2";
++ phy-mode = "internal";
++ phy-handle = <&internal_phy_port2>;
++ };
++
++ port@3 {
++ reg = <3>;
++ label = "lan3";
++ phy-mode = "internal";
++ phy-handle = <&internal_phy_port3>;
++ };
++
++ port@4 {
++ reg = <4>;
++ label = "lan4";
++ phy-mode = "internal";
++ phy-handle = <&internal_phy_port4>;
++ };
++
++ port@5 {
++ reg = <5>;
++ label = "wan";
++ phy-mode = "internal";
++ phy-handle = <&internal_phy_port5>;
++ };
++
++ port@6 {
++ reg = <0>;
++ label = "cpu";
++ ethernet = <&gmac1>;
++ phy-mode = "sgmii";
++
++ qca,sgmii-rxclk-falling-edge;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++ };
++
++ mdio {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ internal_phy_port1: ethernet-phy@0 {
++ reg = <0>;
++ };
++
++ internal_phy_port2: ethernet-phy@1 {
++ reg = <1>;
++ };
++
++ internal_phy_port3: ethernet-phy@2 {
++ reg = <2>;
++ };
++
++ internal_phy_port4: ethernet-phy@3 {
++ reg = <3>;
++ };
++
++ internal_phy_port5: ethernet-phy@4 {
++ reg = <4>;
++ };
++ };
++ };
++ };
--- /dev/null
+From 06dd34a628ae5b6a839b757e746de165d6789ca8 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 17 Oct 2021 16:56:46 +0200
+Subject: net: dsa: qca8k: fix delay applied to wrong cpu in parse_port_config
+
+Fix delay settings applied to wrong cpu in parse_port_config. The delay
+values is set to the wrong index as the cpu_port_index is incremented
+too early. Start the cpu_port_index to -1 so the correct value is
+applied to address also the case with invalid phy mode and not available
+port.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -976,7 +976,7 @@ qca8k_setup_of_pws_reg(struct qca8k_priv
+ static int
+ qca8k_parse_port_config(struct qca8k_priv *priv)
+ {
+- int port, cpu_port_index = 0, ret;
++ int port, cpu_port_index = -1, ret;
+ struct device_node *port_dn;
+ phy_interface_t mode;
+ struct dsa_port *dp;
--- /dev/null
+From 040e926f5813a5f4cc18dbff7c942d1e52f368f2 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 19 Oct 2021 02:08:50 +0200
+Subject: net: dsa: qca8k: tidy for loop in setup and add cpu port check
+
+Tidy and organize qca8k setup function from multiple for loop.
+Change for loop in bridge leave/join to scan all port and skip cpu port.
+No functional change intended.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 74 +++++++++++++++++++++++++++++--------------------
+ 1 file changed, 44 insertions(+), 30 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1122,28 +1122,34 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ dev_warn(priv->dev, "mib init failed");
+
+- /* Enable QCA header mode on the cpu port */
+- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(cpu_port),
+- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
+- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
+- if (ret) {
+- dev_err(priv->dev, "failed enabling QCA header mode");
+- return ret;
+- }
+-
+- /* Disable forwarding by default on all ports */
++ /* Initial setup of all ports */
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ /* Disable forwarding by default on all ports */
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+ QCA8K_PORT_LOOKUP_MEMBER, 0);
+ if (ret)
+ return ret;
+- }
+
+- /* Disable MAC by default on all ports */
+- for (i = 1; i < QCA8K_NUM_PORTS; i++)
+- qca8k_port_set_status(priv, i, 0);
++ /* Enable QCA header mode on all cpu ports */
++ if (dsa_is_cpu_port(ds, i)) {
++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i),
++ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
++ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
++ if (ret) {
++ dev_err(priv->dev, "failed enabling QCA header mode");
++ return ret;
++ }
++ }
++
++ /* Disable MAC by default on all user ports */
++ if (dsa_is_user_port(ds, i))
++ qca8k_port_set_status(priv, i, 0);
++ }
+
+- /* Forward all unknown frames to CPU port for Linux processing */
++ /* Forward all unknown frames to CPU port for Linux processing
++ * Notice that in multi-cpu config only one port should be set
++ * for igmp, unknown, multicast and broadcast packet
++ */
+ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
+ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
+@@ -1152,11 +1158,13 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
+- /* Setup connection between CPU port & user ports */
++ /* Setup connection between CPU port & user ports
++ * Configure specific switch configuration for ports
++ */
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+ /* CPU port gets connected to all user ports of the switch */
+ if (dsa_is_cpu_port(ds, i)) {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
+ if (ret)
+ return ret;
+@@ -1193,16 +1201,14 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+ }
+- }
+
+- /* The port 5 of the qca8337 have some problem in flood condition. The
+- * original legacy driver had some specific buffer and priority settings
+- * for the different port suggested by the QCA switch team. Add this
+- * missing settings to improve switch stability under load condition.
+- * This problem is limited to qca8337 and other qca8k switch are not affected.
+- */
+- if (priv->switch_id == QCA8K_ID_QCA8337) {
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ /* The port 5 of the qca8337 have some problem in flood condition. The
++ * original legacy driver had some specific buffer and priority settings
++ * for the different port suggested by the QCA switch team. Add this
++ * missing settings to improve switch stability under load condition.
++ * This problem is limited to qca8337 and other qca8k switch are not affected.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8337) {
+ switch (i) {
+ /* The 2 CPU port and port 5 requires some different
+ * priority than any other ports.
+@@ -1238,6 +1244,12 @@ qca8k_setup(struct dsa_switch *ds)
+ QCA8K_PORT_HOL_CTRL1_WRED_EN,
+ mask);
+ }
++
++ /* Set initial MTU for every port.
++ * We have only have a general MTU setting. So track
++ * every port and set the max across all port.
++ */
++ priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
+ }
+
+ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
+@@ -1251,8 +1263,6 @@ qca8k_setup(struct dsa_switch *ds)
+ }
+
+ /* Setup our port MTUs to match power on defaults */
+- for (i = 0; i < QCA8K_NUM_PORTS; i++)
+- priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
+ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
+ if (ret)
+ dev_warn(priv->dev, "failed setting MTU settings");
+@@ -1728,7 +1738,9 @@ qca8k_port_bridge_join(struct dsa_switch
+ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+ port_mask = BIT(cpu_port);
+
+- for (i = 1; i < QCA8K_NUM_PORTS; i++) {
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ if (dsa_is_cpu_port(ds, i))
++ continue;
+ if (dsa_to_port(ds, i)->bridge_dev != br)
+ continue;
+ /* Add this port to the portvlan mask of the other ports
+@@ -1758,7 +1770,9 @@ qca8k_port_bridge_leave(struct dsa_switc
+
+ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+
+- for (i = 1; i < QCA8K_NUM_PORTS; i++) {
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ if (dsa_is_cpu_port(ds, i))
++ continue;
+ if (dsa_to_port(ds, i)->bridge_dev != br)
+ continue;
+ /* Remove this port to the portvlan mask of the other ports
--- /dev/null
+From 5f15d392dcb4aa250a63d6f2c5adfc26c0aedc78 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 2 Nov 2021 19:30:41 +0100
+Subject: net: dsa: qca8k: make sure PAD0 MAC06 exchange is disabled
+
+Some device set MAC06 exchange in the bootloader. This cause some
+problem as we don't support this strange mode and we just set the port6
+as the primary CPU port. With MAC06 exchange, PAD0 reg configure port6
+instead of port0. Add an extra check and explicitly disable MAC06 exchange
+to correctly configure the port PAD config.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Fixes: 3fcf734aa482 ("net: dsa: qca8k: add support for cpu port 6")
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 8 ++++++++
+ drivers/net/dsa/qca8k.h | 1 +
+ 2 files changed, 9 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1109,6 +1109,14 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
++ /* Make sure MAC06 is disabled */
++ ret = qca8k_reg_clear(priv, QCA8K_REG_PORT0_PAD_CTRL,
++ QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
++ if (ret) {
++ dev_err(priv->dev, "failed disabling MAC06 exchange");
++ return ret;
++ }
++
+ /* Enable CPU Port */
+ ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
+ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -34,6 +34,7 @@
+ #define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8)
+ #define QCA8K_MASK_CTRL_DEVICE_ID(x) ((x) >> 8)
+ #define QCA8K_REG_PORT0_PAD_CTRL 0x004
++#define QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN BIT(31)
+ #define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19)
+ #define QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE BIT(18)
+ #define QCA8K_REG_PORT5_PAD_CTRL 0x008
--- /dev/null
+From 3b00a07c2443745d62babfe08dbb2ad8e649526e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 19 Nov 2021 03:03:49 +0100
+Subject: [PATCH] net: dsa: qca8k: fix internal delay applied to the wrong PAD
+ config
+
+With SGMII phy the internal delay is always applied to the PAD0 config.
+This is caused by the falling edge configuration that hardcode the reg
+to PAD0 (as the falling edge bits are present only in PAD0 reg)
+Move the delay configuration before the reg overwrite to correctly apply
+the delay.
+
+Fixes: cef08115846e ("net: dsa: qca8k: set internal delay also for sgmii")
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1433,6 +1433,12 @@ qca8k_phylink_mac_config(struct dsa_swit
+
+ qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val);
+
++ /* From original code is reported port instability as SGMII also
++ * require delay set. Apply advised values here or take them from DT.
++ */
++ if (state->interface == PHY_INTERFACE_MODE_SGMII)
++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
++
+ /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and
+ * falling edge is set writing in the PORT0 PAD reg
+ */
+@@ -1455,12 +1461,6 @@ qca8k_phylink_mac_config(struct dsa_swit
+ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
+ val);
+
+- /* From original code is reported port instability as SGMII also
+- * require delay set. Apply advised values here or take them from DT.
+- */
+- if (state->interface == PHY_INTERFACE_MODE_SGMII)
+- qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
+-
+ break;
+ default:
+ dev_err(ds->dev, "xMII mode %s not supported for port %d\n",
--- /dev/null
+From 65258b9d8cde45689bdc86ca39b50f01f983733b Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Fri, 19 Nov 2021 03:03:50 +0100
+Subject: [PATCH] net: dsa: qca8k: fix MTU calculation
+
+qca8k has a global MTU, so its tracking the MTU per port to make sure
+that the largest MTU gets applied.
+Since it uses the frame size instead of MTU the driver MTU change function
+will then add the size of Ethernet header and checksum on top of MTU.
+
+The driver currently populates the per port MTU size as Ethernet frame
+length + checksum which equals 1518.
+
+The issue is that then MTU change function will go through all of the
+ports, find the largest MTU and apply the Ethernet header + checksum on
+top of it again, so for a desired MTU of 1500 you will end up with 1536.
+
+This is obviously incorrect, so to correct it populate the per port struct
+MTU with just the MTU and not include the Ethernet header + checksum size
+as those will be added by the MTU change function.
+
+Fixes: f58d2598cf70 ("net: dsa: qca8k: implement the port MTU callbacks")
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1256,8 +1256,12 @@ qca8k_setup(struct dsa_switch *ds)
+ /* Set initial MTU for every port.
+ * We have only have a general MTU setting. So track
+ * every port and set the max across all port.
++ * Set per port MTU to 1500 as the MTU change function
++ * will add the overhead and if its set to 1518 then it
++ * will apply the overhead again and we will end up with
++ * MTU of 1536 instead of 1518
+ */
+- priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
++ priv->port_mtu[i] = ETH_DATA_LEN;
+ }
+
+ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
--- /dev/null
+From b9133f3ef5a2659730cf47a74bd0a9259f1cf8ff Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:40 +0100
+Subject: net: dsa: qca8k: remove redundant check in parse_port_config
+
+The very next check for port 0 and 6 already makes sure we don't go out
+of bounds with the ports_config delay table.
+Remove the redundant check.
+
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -983,7 +983,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ u32 delay;
+
+ /* We have 2 CPU port. Check them */
+- for (port = 0; port < QCA8K_NUM_PORTS && cpu_port_index < QCA8K_NUM_CPU_PORTS; port++) {
++ for (port = 0; port < QCA8K_NUM_PORTS; port++) {
+ /* Skip every other port */
+ if (port != 0 && port != 6)
+ continue;
--- /dev/null
+From 90ae68bfc2ffcb54a4ba4f64edbeb84a80cbb57c Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:41 +0100
+Subject: net: dsa: qca8k: convert to GENMASK/FIELD_PREP/FIELD_GET
+
+Convert and try to standardize bit fields using
+GENMASK/FIELD_PREP/FIELD_GET macros. Rework some logic to support the
+standard macro and tidy things up. No functional change intended.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 98 +++++++++++++++----------------
+ drivers/net/dsa/qca8k.h | 153 ++++++++++++++++++++++++++----------------------
+ 2 files changed, 130 insertions(+), 121 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -9,6 +9,7 @@
+ #include <linux/module.h>
+ #include <linux/phy.h>
+ #include <linux/netdevice.h>
++#include <linux/bitfield.h>
+ #include <net/dsa.h>
+ #include <linux/of_net.h>
+ #include <linux/of_mdio.h>
+@@ -319,18 +320,18 @@ qca8k_fdb_read(struct qca8k_priv *priv,
+ }
+
+ /* vid - 83:72 */
+- fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M;
++ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
+ /* aging - 67:64 */
+- fdb->aging = reg[2] & QCA8K_ATU_STATUS_M;
++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
+ /* portmask - 54:48 */
+- fdb->port_mask = (reg[1] >> QCA8K_ATU_PORT_S) & QCA8K_ATU_PORT_M;
++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
+ /* mac - 47:0 */
+- fdb->mac[0] = (reg[1] >> QCA8K_ATU_ADDR0_S) & 0xff;
+- fdb->mac[1] = reg[1] & 0xff;
+- fdb->mac[2] = (reg[0] >> QCA8K_ATU_ADDR2_S) & 0xff;
+- fdb->mac[3] = (reg[0] >> QCA8K_ATU_ADDR3_S) & 0xff;
+- fdb->mac[4] = (reg[0] >> QCA8K_ATU_ADDR4_S) & 0xff;
+- fdb->mac[5] = reg[0] & 0xff;
++ fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
++ fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
++ fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
++ fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
++ fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
++ fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
+
+ return 0;
+ }
+@@ -343,18 +344,18 @@ qca8k_fdb_write(struct qca8k_priv *priv,
+ int i;
+
+ /* vid - 83:72 */
+- reg[2] = (vid & QCA8K_ATU_VID_M) << QCA8K_ATU_VID_S;
++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
+ /* aging - 67:64 */
+- reg[2] |= aging & QCA8K_ATU_STATUS_M;
++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
+ /* portmask - 54:48 */
+- reg[1] = (port_mask & QCA8K_ATU_PORT_M) << QCA8K_ATU_PORT_S;
++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
+ /* mac - 47:0 */
+- reg[1] |= mac[0] << QCA8K_ATU_ADDR0_S;
+- reg[1] |= mac[1];
+- reg[0] |= mac[2] << QCA8K_ATU_ADDR2_S;
+- reg[0] |= mac[3] << QCA8K_ATU_ADDR3_S;
+- reg[0] |= mac[4] << QCA8K_ATU_ADDR4_S;
+- reg[0] |= mac[5];
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
+
+ /* load the array into the ARL table */
+ for (i = 0; i < 3; i++)
+@@ -372,7 +373,7 @@ qca8k_fdb_access(struct qca8k_priv *priv
+ reg |= cmd;
+ if (port >= 0) {
+ reg |= QCA8K_ATU_FUNC_PORT_EN;
+- reg |= (port & QCA8K_ATU_FUNC_PORT_M) << QCA8K_ATU_FUNC_PORT_S;
++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
+ }
+
+ /* Write the function register triggering the table access */
+@@ -454,7 +455,7 @@ qca8k_vlan_access(struct qca8k_priv *pri
+ /* Set the command and VLAN index */
+ reg = QCA8K_VTU_FUNC1_BUSY;
+ reg |= cmd;
+- reg |= vid << QCA8K_VTU_FUNC1_VID_S;
++ reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
+
+ /* Write the function register triggering the table access */
+ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
+@@ -500,13 +501,11 @@ qca8k_vlan_add(struct qca8k_priv *priv,
+ if (ret < 0)
+ goto out;
+ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
+- reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port));
++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+ if (untagged)
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_UNTAG <<
+- QCA8K_VTU_FUNC0_EG_MODE_S(port);
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
+ else
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_TAG <<
+- QCA8K_VTU_FUNC0_EG_MODE_S(port);
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
+
+ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+ if (ret)
+@@ -534,15 +533,13 @@ qca8k_vlan_del(struct qca8k_priv *priv,
+ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®);
+ if (ret < 0)
+ goto out;
+- reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port));
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT <<
+- QCA8K_VTU_FUNC0_EG_MODE_S(port);
++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
+
+ /* Check if we're the last member to be removed */
+ del = true;
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- mask = QCA8K_VTU_FUNC0_EG_MODE_NOT;
+- mask <<= QCA8K_VTU_FUNC0_EG_MODE_S(i);
++ mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
+
+ if ((reg & mask) != mask) {
+ del = false;
+@@ -1014,7 +1011,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ mode == PHY_INTERFACE_MODE_RGMII_TXID)
+ delay = 1;
+
+- if (delay > QCA8K_MAX_DELAY) {
++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) {
+ dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
+ delay = 3;
+ }
+@@ -1030,7 +1027,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ mode == PHY_INTERFACE_MODE_RGMII_RXID)
+ delay = 2;
+
+- if (delay > QCA8K_MAX_DELAY) {
++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) {
+ dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
+ delay = 3;
+ }
+@@ -1141,8 +1138,8 @@ qca8k_setup(struct dsa_switch *ds)
+ /* Enable QCA header mode on all cpu ports */
+ if (dsa_is_cpu_port(ds, i)) {
+ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i),
+- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
+- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) |
++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));
+ if (ret) {
+ dev_err(priv->dev, "failed enabling QCA header mode");
+ return ret;
+@@ -1159,10 +1156,10 @@ qca8k_setup(struct dsa_switch *ds)
+ * for igmp, unknown, multicast and broadcast packet
+ */
+ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
+- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
+- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
+- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port)));
+ if (ret)
+ return ret;
+
+@@ -1180,8 +1177,6 @@ qca8k_setup(struct dsa_switch *ds)
+
+ /* Individual user ports get connected to CPU port only */
+ if (dsa_is_user_port(ds, i)) {
+- int shift = 16 * (i % 2);
+-
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+ QCA8K_PORT_LOOKUP_MEMBER,
+ BIT(cpu_port));
+@@ -1198,8 +1193,8 @@ qca8k_setup(struct dsa_switch *ds)
+ * default egress vid
+ */
+ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
+- 0xfff << shift,
+- QCA8K_PORT_VID_DEF << shift);
++ QCA8K_EGREES_VLAN_PORT_MASK(i),
++ QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF));
+ if (ret)
+ return ret;
+
+@@ -1246,7 +1241,7 @@ qca8k_setup(struct dsa_switch *ds)
+ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
+ QCA8K_PORT_HOL_CTRL1_WRED_EN;
+ qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i),
+- QCA8K_PORT_HOL_CTRL1_ING_BUF |
++ QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK |
+ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
+ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
+ QCA8K_PORT_HOL_CTRL1_WRED_EN,
+@@ -1269,8 +1264,8 @@ qca8k_setup(struct dsa_switch *ds)
+ mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
+ QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496);
+ qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH,
+- QCA8K_GLOBAL_FC_GOL_XON_THRES_S |
+- QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S,
++ QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK |
++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK,
+ mask);
+ }
+
+@@ -1918,11 +1913,11 @@ qca8k_port_vlan_filtering(struct dsa_swi
+
+ if (vlan_filtering) {
+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_VLAN_MODE,
++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+ QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
+ } else {
+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_VLAN_MODE,
++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+ QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
+ }
+
+@@ -1953,11 +1948,9 @@ qca8k_port_vlan_add(struct dsa_switch *d
+ dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
+
+ if (pvid) {
+- int shift = 16 * (port % 2);
+-
+ qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
+- 0xfff << shift,
+- vlan->vid_end << shift);
++ QCA8K_EGREES_VLAN_PORT_MASK(port),
++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid_end));
+ qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
+ QCA8K_PORT_VLAN_CVID(vlan->vid_end) |
+ QCA8K_PORT_VLAN_SVID(vlan->vid_end));
+@@ -2050,7 +2043,7 @@ static int qca8k_read_switch_id(struct q
+ if (ret < 0)
+ return -ENODEV;
+
+- id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK);
++ id = QCA8K_MASK_CTRL_DEVICE_ID(val);
+ if (id != data->id) {
+ dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
+ return -ENODEV;
+@@ -2059,7 +2052,7 @@ static int qca8k_read_switch_id(struct q
+ priv->switch_id = id;
+
+ /* Save revision to communicate to the internal PHY driver */
+- priv->switch_revision = (val & QCA8K_MASK_CTRL_REV_ID_MASK);
++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
+
+ return 0;
+ }
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -30,9 +30,9 @@
+ /* Global control registers */
+ #define QCA8K_REG_MASK_CTRL 0x000
+ #define QCA8K_MASK_CTRL_REV_ID_MASK GENMASK(7, 0)
+-#define QCA8K_MASK_CTRL_REV_ID(x) ((x) >> 0)
++#define QCA8K_MASK_CTRL_REV_ID(x) FIELD_GET(QCA8K_MASK_CTRL_REV_ID_MASK, x)
+ #define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8)
+-#define QCA8K_MASK_CTRL_DEVICE_ID(x) ((x) >> 8)
++#define QCA8K_MASK_CTRL_DEVICE_ID(x) FIELD_GET(QCA8K_MASK_CTRL_DEVICE_ID_MASK, x)
+ #define QCA8K_REG_PORT0_PAD_CTRL 0x004
+ #define QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN BIT(31)
+ #define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19)
+@@ -41,12 +41,11 @@
+ #define QCA8K_REG_PORT6_PAD_CTRL 0x00c
+ #define QCA8K_PORT_PAD_RGMII_EN BIT(26)
+ #define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 22)
+-#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) ((x) << 22)
++#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, x)
+ #define QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK GENMASK(21, 20)
+-#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) ((x) << 20)
++#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, x)
+ #define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
+ #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
+-#define QCA8K_MAX_DELAY 3
+ #define QCA8K_PORT_PAD_SGMII_EN BIT(7)
+ #define QCA8K_REG_PWS 0x010
+ #define QCA8K_PWS_POWER_ON_SEL BIT(31)
+@@ -68,10 +67,12 @@
+ #define QCA8K_MDIO_MASTER_READ BIT(27)
+ #define QCA8K_MDIO_MASTER_WRITE 0
+ #define QCA8K_MDIO_MASTER_SUP_PRE BIT(26)
+-#define QCA8K_MDIO_MASTER_PHY_ADDR(x) ((x) << 21)
+-#define QCA8K_MDIO_MASTER_REG_ADDR(x) ((x) << 16)
+-#define QCA8K_MDIO_MASTER_DATA(x) (x)
++#define QCA8K_MDIO_MASTER_PHY_ADDR_MASK GENMASK(25, 21)
++#define QCA8K_MDIO_MASTER_PHY_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_PHY_ADDR_MASK, x)
++#define QCA8K_MDIO_MASTER_REG_ADDR_MASK GENMASK(20, 16)
++#define QCA8K_MDIO_MASTER_REG_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_REG_ADDR_MASK, x)
+ #define QCA8K_MDIO_MASTER_DATA_MASK GENMASK(15, 0)
++#define QCA8K_MDIO_MASTER_DATA(x) FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x)
+ #define QCA8K_MDIO_MASTER_MAX_PORTS 5
+ #define QCA8K_MDIO_MASTER_MAX_REG 32
+ #define QCA8K_GOL_MAC_ADDR0 0x60
+@@ -93,9 +94,7 @@
+ #define QCA8K_PORT_STATUS_FLOW_AUTO BIT(12)
+ #define QCA8K_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4))
+ #define QCA8K_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2)
+-#define QCA8K_PORT_HDR_CTRL_RX_S 2
+ #define QCA8K_PORT_HDR_CTRL_TX_MASK GENMASK(1, 0)
+-#define QCA8K_PORT_HDR_CTRL_TX_S 0
+ #define QCA8K_PORT_HDR_CTRL_ALL 2
+ #define QCA8K_PORT_HDR_CTRL_MGMT 1
+ #define QCA8K_PORT_HDR_CTRL_NONE 0
+@@ -105,10 +104,11 @@
+ #define QCA8K_SGMII_EN_TX BIT(3)
+ #define QCA8K_SGMII_EN_SD BIT(4)
+ #define QCA8K_SGMII_CLK125M_DELAY BIT(7)
+-#define QCA8K_SGMII_MODE_CTRL_MASK (BIT(22) | BIT(23))
+-#define QCA8K_SGMII_MODE_CTRL_BASEX (0 << 22)
+-#define QCA8K_SGMII_MODE_CTRL_PHY (1 << 22)
+-#define QCA8K_SGMII_MODE_CTRL_MAC (2 << 22)
++#define QCA8K_SGMII_MODE_CTRL_MASK GENMASK(23, 22)
++#define QCA8K_SGMII_MODE_CTRL(x) FIELD_PREP(QCA8K_SGMII_MODE_CTRL_MASK, x)
++#define QCA8K_SGMII_MODE_CTRL_BASEX QCA8K_SGMII_MODE_CTRL(0x0)
++#define QCA8K_SGMII_MODE_CTRL_PHY QCA8K_SGMII_MODE_CTRL(0x1)
++#define QCA8K_SGMII_MODE_CTRL_MAC QCA8K_SGMII_MODE_CTRL(0x2)
+
+ /* MAC_PWR_SEL registers */
+ #define QCA8K_REG_MAC_PWR_SEL 0x0e4
+@@ -121,100 +121,115 @@
+
+ /* ACL registers */
+ #define QCA8K_REG_PORT_VLAN_CTRL0(_i) (0x420 + (_i * 8))
+-#define QCA8K_PORT_VLAN_CVID(x) (x << 16)
+-#define QCA8K_PORT_VLAN_SVID(x) x
++#define QCA8K_PORT_VLAN_CVID_MASK GENMASK(27, 16)
++#define QCA8K_PORT_VLAN_CVID(x) FIELD_PREP(QCA8K_PORT_VLAN_CVID_MASK, x)
++#define QCA8K_PORT_VLAN_SVID_MASK GENMASK(11, 0)
++#define QCA8K_PORT_VLAN_SVID(x) FIELD_PREP(QCA8K_PORT_VLAN_SVID_MASK, x)
+ #define QCA8K_REG_PORT_VLAN_CTRL1(_i) (0x424 + (_i * 8))
+ #define QCA8K_REG_IPV4_PRI_BASE_ADDR 0x470
+ #define QCA8K_REG_IPV4_PRI_ADDR_MASK 0x474
+
+ /* Lookup registers */
+ #define QCA8K_REG_ATU_DATA0 0x600
+-#define QCA8K_ATU_ADDR2_S 24
+-#define QCA8K_ATU_ADDR3_S 16
+-#define QCA8K_ATU_ADDR4_S 8
++#define QCA8K_ATU_ADDR2_MASK GENMASK(31, 24)
++#define QCA8K_ATU_ADDR3_MASK GENMASK(23, 16)
++#define QCA8K_ATU_ADDR4_MASK GENMASK(15, 8)
++#define QCA8K_ATU_ADDR5_MASK GENMASK(7, 0)
+ #define QCA8K_REG_ATU_DATA1 0x604
+-#define QCA8K_ATU_PORT_M 0x7f
+-#define QCA8K_ATU_PORT_S 16
+-#define QCA8K_ATU_ADDR0_S 8
++#define QCA8K_ATU_PORT_MASK GENMASK(22, 16)
++#define QCA8K_ATU_ADDR0_MASK GENMASK(15, 8)
++#define QCA8K_ATU_ADDR1_MASK GENMASK(7, 0)
+ #define QCA8K_REG_ATU_DATA2 0x608
+-#define QCA8K_ATU_VID_M 0xfff
+-#define QCA8K_ATU_VID_S 8
+-#define QCA8K_ATU_STATUS_M 0xf
++#define QCA8K_ATU_VID_MASK GENMASK(19, 8)
++#define QCA8K_ATU_STATUS_MASK GENMASK(3, 0)
+ #define QCA8K_ATU_STATUS_STATIC 0xf
+ #define QCA8K_REG_ATU_FUNC 0x60c
+ #define QCA8K_ATU_FUNC_BUSY BIT(31)
+ #define QCA8K_ATU_FUNC_PORT_EN BIT(14)
+ #define QCA8K_ATU_FUNC_MULTI_EN BIT(13)
+ #define QCA8K_ATU_FUNC_FULL BIT(12)
+-#define QCA8K_ATU_FUNC_PORT_M 0xf
+-#define QCA8K_ATU_FUNC_PORT_S 8
++#define QCA8K_ATU_FUNC_PORT_MASK GENMASK(11, 8)
+ #define QCA8K_REG_VTU_FUNC0 0x610
+ #define QCA8K_VTU_FUNC0_VALID BIT(20)
+ #define QCA8K_VTU_FUNC0_IVL_EN BIT(19)
+-#define QCA8K_VTU_FUNC0_EG_MODE_S(_i) (4 + (_i) * 2)
+-#define QCA8K_VTU_FUNC0_EG_MODE_MASK 3
+-#define QCA8K_VTU_FUNC0_EG_MODE_UNMOD 0
+-#define QCA8K_VTU_FUNC0_EG_MODE_UNTAG 1
+-#define QCA8K_VTU_FUNC0_EG_MODE_TAG 2
+-#define QCA8K_VTU_FUNC0_EG_MODE_NOT 3
++/* QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(17, 4)
++ * It does contain VLAN_MODE for each port [5:4] for port0,
++ * [7:6] for port1 ... [17:16] for port6. Use virtual port
++ * define to handle this.
++ */
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i) (4 + (_i) * 2)
++#define QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(1, 0)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(_i) (GENMASK(1, 0) << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_UNMOD FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x0)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNMOD(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNMOD << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_UNTAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x1)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNTAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_TAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x2)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_TAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_NOT FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x3)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(_i) (QCA8K_VTU_FUNC0_EG_MODE_NOT << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
+ #define QCA8K_REG_VTU_FUNC1 0x614
+ #define QCA8K_VTU_FUNC1_BUSY BIT(31)
+-#define QCA8K_VTU_FUNC1_VID_S 16
++#define QCA8K_VTU_FUNC1_VID_MASK GENMASK(27, 16)
+ #define QCA8K_VTU_FUNC1_FULL BIT(4)
+ #define QCA8K_REG_GLOBAL_FW_CTRL0 0x620
+ #define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10)
+ #define QCA8K_REG_GLOBAL_FW_CTRL1 0x624
+-#define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S 24
+-#define QCA8K_GLOBAL_FW_CTRL1_BC_DP_S 16
+-#define QCA8K_GLOBAL_FW_CTRL1_MC_DP_S 8
+-#define QCA8K_GLOBAL_FW_CTRL1_UC_DP_S 0
++#define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK GENMASK(30, 24)
++#define QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK GENMASK(22, 16)
++#define QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK GENMASK(14, 8)
++#define QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK GENMASK(6, 0)
+ #define QCA8K_PORT_LOOKUP_CTRL(_i) (0x660 + (_i) * 0xc)
+ #define QCA8K_PORT_LOOKUP_MEMBER GENMASK(6, 0)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE GENMASK(9, 8)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_NONE (0 << 8)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK (1 << 8)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK (2 << 8)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE (3 << 8)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_MASK GENMASK(9, 8)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, x)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_NONE QCA8K_PORT_LOOKUP_VLAN_MODE(0x0)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK QCA8K_PORT_LOOKUP_VLAN_MODE(0x1)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK QCA8K_PORT_LOOKUP_VLAN_MODE(0x2)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE QCA8K_PORT_LOOKUP_VLAN_MODE(0x3)
+ #define QCA8K_PORT_LOOKUP_STATE_MASK GENMASK(18, 16)
+-#define QCA8K_PORT_LOOKUP_STATE_DISABLED (0 << 16)
+-#define QCA8K_PORT_LOOKUP_STATE_BLOCKING (1 << 16)
+-#define QCA8K_PORT_LOOKUP_STATE_LISTENING (2 << 16)
+-#define QCA8K_PORT_LOOKUP_STATE_LEARNING (3 << 16)
+-#define QCA8K_PORT_LOOKUP_STATE_FORWARD (4 << 16)
+-#define QCA8K_PORT_LOOKUP_STATE GENMASK(18, 16)
++#define QCA8K_PORT_LOOKUP_STATE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_STATE_MASK, x)
++#define QCA8K_PORT_LOOKUP_STATE_DISABLED QCA8K_PORT_LOOKUP_STATE(0x0)
++#define QCA8K_PORT_LOOKUP_STATE_BLOCKING QCA8K_PORT_LOOKUP_STATE(0x1)
++#define QCA8K_PORT_LOOKUP_STATE_LISTENING QCA8K_PORT_LOOKUP_STATE(0x2)
++#define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3)
++#define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4)
+ #define QCA8K_PORT_LOOKUP_LEARN BIT(20)
+
+ #define QCA8K_REG_GLOBAL_FC_THRESH 0x800
+-#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) ((x) << 16)
+-#define QCA8K_GLOBAL_FC_GOL_XON_THRES_S GENMASK(24, 16)
+-#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) ((x) << 0)
+-#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S GENMASK(8, 0)
++#define QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK GENMASK(24, 16)
++#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x)
++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK GENMASK(8, 0)
++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, x)
+
+ #define QCA8K_REG_PORT_HOL_CTRL0(_i) (0x970 + (_i) * 0x8)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF GENMASK(3, 0)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) ((x) << 0)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF GENMASK(7, 4)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) ((x) << 4)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF GENMASK(11, 8)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) ((x) << 8)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF GENMASK(15, 12)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) ((x) << 12)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF GENMASK(19, 16)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) ((x) << 16)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF GENMASK(23, 20)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) ((x) << 20)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF GENMASK(29, 24)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) ((x) << 24)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK GENMASK(3, 0)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK GENMASK(7, 4)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK GENMASK(11, 8)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK GENMASK(15, 12)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK GENMASK(19, 16)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK GENMASK(23, 20)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK GENMASK(29, 24)
++#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK, x)
+
+ #define QCA8K_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8)
+-#define QCA8K_PORT_HOL_CTRL1_ING_BUF GENMASK(3, 0)
+-#define QCA8K_PORT_HOL_CTRL1_ING(x) ((x) << 0)
++#define QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK GENMASK(3, 0)
++#define QCA8K_PORT_HOL_CTRL1_ING(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK, x)
+ #define QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN BIT(6)
+ #define QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN BIT(7)
+ #define QCA8K_PORT_HOL_CTRL1_WRED_EN BIT(8)
+ #define QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16)
+
+ /* Pkt edit registers */
++#define QCA8K_EGREES_VLAN_PORT_SHIFT(_i) (16 * ((_i) % 2))
++#define QCA8K_EGREES_VLAN_PORT_MASK(_i) (GENMASK(11, 0) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i))
++#define QCA8K_EGREES_VLAN_PORT(_i, x) ((x) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i))
+ #define QCA8K_EGRESS_VLAN(x) (0x0c70 + (4 * (x / 2)))
+
+ /* L3 registers */
--- /dev/null
+From 994c28b6f971fa5db8ae977daea37eee87d93d51 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:42 +0100
+Subject: net: dsa: qca8k: remove extra mutex_init in qca8k_setup
+
+Mutex is already init in sw_probe. Remove the extra init in qca8k_setup.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1086,8 +1086,6 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
+- mutex_init(&priv->reg_mutex);
+-
+ /* Start by setting up the register mapping */
+ priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
+ &qca8k_regmap_config);
--- /dev/null
+From 36b8af12f424e7a7f60a935c60a0fd4aa0822378 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:43 +0100
+Subject: net: dsa: qca8k: move regmap init in probe and set it mandatory
+
+In preparation for regmap conversion, move regmap init in the probe
+function and make it mandatory as any read/write/rmw operation will be
+converted to regmap API.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1086,12 +1086,6 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
+- /* Start by setting up the register mapping */
+- priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
+- &qca8k_regmap_config);
+- if (IS_ERR(priv->regmap))
+- dev_warn(priv->dev, "regmap initialization failed");
+-
+ ret = qca8k_setup_mdio_bus(priv);
+ if (ret)
+ return ret;
+@@ -2085,6 +2079,14 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
+ }
+
++ /* Start by setting up the register mapping */
++ priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv,
++ &qca8k_regmap_config);
++ if (IS_ERR(priv->regmap)) {
++ dev_err(priv->dev, "regmap initialization failed");
++ return PTR_ERR(priv->regmap);
++ }
++
+ /* Check the detected switch id */
+ ret = qca8k_read_switch_id(priv);
+ if (ret)
--- /dev/null
+From 8b5f3f29a81a71934d004e21a1292c1148b05926 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:44 +0100
+Subject: net: dsa: qca8k: initial conversion to regmap helper
+
+Convert any qca8k set/clear/pool to regmap helper and add
+missing config to regmap_config struct.
+Read/write/rmw operation are reworked to use the regmap helper
+internally to keep the delta of this patch low. These additional
+function will then be dropped when the code split will be proposed.
+
+Ipq40xx SoC have the internal switch based on the qca8k regmap but use
+mmio for read/write/rmw operation instead of mdio.
+In preparation for the support of this internal switch, convert the
+driver to regmap API to later split the driver to common and specific
+code. The overhead introduced by the use of regamp API is marginal as the
+internal mdio will bypass it by using its direct access and regmap will be
+used only by configuration functions or fdb access.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 107 +++++++++++++++++++++---------------------------
+ 1 file changed, 47 insertions(+), 60 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -10,6 +10,7 @@
+ #include <linux/phy.h>
+ #include <linux/netdevice.h>
+ #include <linux/bitfield.h>
++#include <linux/regmap.h>
+ #include <net/dsa.h>
+ #include <linux/of_net.h>
+ #include <linux/of_mdio.h>
+@@ -152,6 +153,25 @@ qca8k_set_page(struct mii_bus *bus, u16
+ static int
+ qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
+ {
++ return regmap_read(priv->regmap, reg, val);
++}
++
++static int
++qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
++{
++ return regmap_write(priv->regmap, reg, val);
++}
++
++static int
++qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
++{
++ return regmap_update_bits(priv->regmap, reg, mask, write_val);
++}
++
++static int
++qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ int ret;
+@@ -172,8 +192,9 @@ exit:
+ }
+
+ static int
+-qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
++qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
+ {
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ int ret;
+@@ -194,8 +215,9 @@ exit:
+ }
+
+ static int
+-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
++qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)
+ {
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ u32 val;
+@@ -223,34 +245,6 @@ exit:
+ return ret;
+ }
+
+-static int
+-qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val)
+-{
+- return qca8k_rmw(priv, reg, 0, val);
+-}
+-
+-static int
+-qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val)
+-{
+- return qca8k_rmw(priv, reg, val, 0);
+-}
+-
+-static int
+-qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+-
+- return qca8k_read(priv, reg, val);
+-}
+-
+-static int
+-qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+-
+- return qca8k_write(priv, reg, val);
+-}
+-
+ static const struct regmap_range qca8k_readable_ranges[] = {
+ regmap_reg_range(0x0000, 0x00e4), /* Global control */
+ regmap_reg_range(0x0100, 0x0168), /* EEE control */
+@@ -282,26 +276,19 @@ static struct regmap_config qca8k_regmap
+ .max_register = 0x16ac, /* end MIB - Port6 range */
+ .reg_read = qca8k_regmap_read,
+ .reg_write = qca8k_regmap_write,
++ .reg_update_bits = qca8k_regmap_update_bits,
+ .rd_table = &qca8k_readable_table,
++ .disable_locking = true, /* Locking is handled by qca8k read/write */
++ .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */
+ };
+
+ static int
+ qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+ {
+- int ret, ret1;
+ u32 val;
+
+- ret = read_poll_timeout(qca8k_read, ret1, !(val & mask),
+- 0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
+- priv, reg, &val);
+-
+- /* Check if qca8k_read has failed for a different reason
+- * before returning -ETIMEDOUT
+- */
+- if (ret < 0 && ret1 < 0)
+- return ret1;
+-
+- return ret;
++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0,
++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
+ }
+
+ static int
+@@ -568,7 +555,7 @@ qca8k_mib_init(struct qca8k_priv *priv)
+ int ret;
+
+ mutex_lock(&priv->reg_mutex);
+- ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
+ if (ret)
+ goto exit;
+
+@@ -576,7 +563,7 @@ qca8k_mib_init(struct qca8k_priv *priv)
+ if (ret)
+ goto exit;
+
+- ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+ if (ret)
+ goto exit;
+
+@@ -597,9 +584,9 @@ qca8k_port_set_status(struct qca8k_priv
+ mask |= QCA8K_PORT_STATUS_LINK_AUTO;
+
+ if (enable)
+- qca8k_reg_set(priv, QCA8K_REG_PORT_STATUS(port), mask);
++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+ else
+- qca8k_reg_clear(priv, QCA8K_REG_PORT_STATUS(port), mask);
++ regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+ }
+
+ static u32
+@@ -861,8 +848,8 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ * a dt-overlay and driver reload changed the configuration
+ */
+
+- return qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_EN);
++ return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_EN);
+ }
+
+ /* Check if the devicetree declare the port:phy mapping */
+@@ -1099,16 +1086,16 @@ qca8k_setup(struct dsa_switch *ds)
+ return ret;
+
+ /* Make sure MAC06 is disabled */
+- ret = qca8k_reg_clear(priv, QCA8K_REG_PORT0_PAD_CTRL,
+- QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
++ ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL,
++ QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
+ if (ret) {
+ dev_err(priv->dev, "failed disabling MAC06 exchange");
+ return ret;
+ }
+
+ /* Enable CPU Port */
+- ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
+- QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+ if (ret) {
+ dev_err(priv->dev, "failed enabling CPU port");
+ return ret;
+@@ -1176,8 +1163,8 @@ qca8k_setup(struct dsa_switch *ds)
+ return ret;
+
+ /* Enable ARP Auto-learning by default */
+- ret = qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_LEARN);
++ ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_LEARN);
+ if (ret)
+ return ret;
+
+@@ -1745,9 +1732,9 @@ qca8k_port_bridge_join(struct dsa_switch
+ /* Add this port to the portvlan mask of the other ports
+ * in the bridge
+ */
+- ret = qca8k_reg_set(priv,
+- QCA8K_PORT_LOOKUP_CTRL(i),
+- BIT(port));
++ ret = regmap_set_bits(priv->regmap,
++ QCA8K_PORT_LOOKUP_CTRL(i),
++ BIT(port));
+ if (ret)
+ return ret;
+ if (i != port)
+@@ -1777,9 +1764,9 @@ qca8k_port_bridge_leave(struct dsa_switc
+ /* Remove this port to the portvlan mask of the other ports
+ * in the bridge
+ */
+- qca8k_reg_clear(priv,
+- QCA8K_PORT_LOOKUP_CTRL(i),
+- BIT(port));
++ regmap_clear_bits(priv->regmap,
++ QCA8K_PORT_LOOKUP_CTRL(i),
++ BIT(port));
+ }
+
+ /* Set the cpu port to be the only one in the portvlan mask of
--- /dev/null
+From c126f118b330ccf0db0dda4a4bd6c729865a205f Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:45 +0100
+Subject: net: dsa: qca8k: add additional MIB counter and make it dynamic
+
+We are currently missing 2 additionals MIB counter present in QCA833x
+switch.
+QC832x switch have 39 MIB counter and QCA833X have 41 MIB counter.
+Add the additional MIB counter and rework the MIB function to print the
+correct supported counter from the match_data struct.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 23 ++++++++++++++++++++---
+ drivers/net/dsa/qca8k.h | 4 ++++
+ 2 files changed, 24 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -70,6 +70,8 @@ static const struct qca8k_mib_desc ar832
+ MIB_DESC(1, 0x9c, "TxExcDefer"),
+ MIB_DESC(1, 0xa0, "TxDefer"),
+ MIB_DESC(1, 0xa4, "TxLateCol"),
++ MIB_DESC(1, 0xa8, "RXUnicast"),
++ MIB_DESC(1, 0xac, "TXUnicast"),
+ };
+
+ /* The 32bit switch registers are accessed indirectly. To achieve this we need
+@@ -1605,12 +1607,16 @@ qca8k_phylink_mac_link_up(struct dsa_swi
+ static void
+ qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
+ {
++ const struct qca8k_match_data *match_data;
++ struct qca8k_priv *priv = ds->priv;
+ int i;
+
+ if (stringset != ETH_SS_STATS)
+ return;
+
+- for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++)
++ match_data = of_device_get_match_data(priv->dev);
++
++ for (i = 0; i < match_data->mib_count; i++)
+ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
+ ETH_GSTRING_LEN);
+ }
+@@ -1620,12 +1626,15 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ uint64_t *data)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ const struct qca8k_match_data *match_data;
+ const struct qca8k_mib_desc *mib;
+ u32 reg, i, val;
+ u32 hi = 0;
+ int ret;
+
+- for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) {
++ match_data = of_device_get_match_data(priv->dev);
++
++ for (i = 0; i < match_data->mib_count; i++) {
+ mib = &ar8327_mib[i];
+ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
+
+@@ -1648,10 +1657,15 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ static int
+ qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
+ {
++ const struct qca8k_match_data *match_data;
++ struct qca8k_priv *priv = ds->priv;
++
+ if (sset != ETH_SS_STATS)
+ return 0;
+
+- return ARRAY_SIZE(ar8327_mib);
++ match_data = of_device_get_match_data(priv->dev);
++
++ return match_data->mib_count;
+ }
+
+ static int
+@@ -2146,14 +2160,17 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+ static const struct qca8k_match_data qca8327 = {
+ .id = QCA8K_ID_QCA8327,
+ .reduced_package = true,
++ .mib_count = QCA8K_QCA832X_MIB_COUNT,
+ };
+
+ static const struct qca8k_match_data qca8328 = {
+ .id = QCA8K_ID_QCA8327,
++ .mib_count = QCA8K_QCA832X_MIB_COUNT,
+ };
+
+ static const struct qca8k_match_data qca833x = {
+ .id = QCA8K_ID_QCA8337,
++ .mib_count = QCA8K_QCA833X_MIB_COUNT,
+ };
+
+ static const struct of_device_id qca8k_of_match[] = {
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -21,6 +21,9 @@
+ #define PHY_ID_QCA8337 0x004dd036
+ #define QCA8K_ID_QCA8337 0x13
+
++#define QCA8K_QCA832X_MIB_COUNT 39
++#define QCA8K_QCA833X_MIB_COUNT 41
++
+ #define QCA8K_BUSY_WAIT_TIMEOUT 2000
+
+ #define QCA8K_NUM_FDB_RECORDS 2048
+@@ -279,6 +282,7 @@ struct ar8xxx_port_status {
+ struct qca8k_match_data {
+ u8 id;
+ bool reduced_package;
++ u8 mib_count;
+ };
+
+ enum {
--- /dev/null
+From 4592538bfb0d5d3c3c8a1d7071724d081412ac91 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:46 +0100
+Subject: net: dsa: qca8k: add support for port fast aging
+
+The switch supports fast aging by flushing any rule in the ARL
+table for a specific port.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 11 +++++++++++
+ drivers/net/dsa/qca8k.h | 1 +
+ 2 files changed, 12 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1790,6 +1790,16 @@ qca8k_port_bridge_leave(struct dsa_switc
+ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+ }
+
++static void
++qca8k_port_fast_age(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
++ mutex_unlock(&priv->reg_mutex);
++}
++
+ static int
+ qca8k_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+@@ -2005,6 +2015,7 @@ static const struct dsa_switch_ops qca8k
+ .port_stp_state_set = qca8k_port_stp_state_set,
+ .port_bridge_join = qca8k_port_bridge_join,
+ .port_bridge_leave = qca8k_port_bridge_leave,
++ .port_fast_age = qca8k_port_fast_age,
+ .port_fdb_add = qca8k_port_fdb_add,
+ .port_fdb_del = qca8k_port_fdb_del,
+ .port_fdb_dump = qca8k_port_fdb_dump,
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -262,6 +262,7 @@ enum qca8k_fdb_cmd {
+ QCA8K_FDB_FLUSH = 1,
+ QCA8K_FDB_LOAD = 2,
+ QCA8K_FDB_PURGE = 3,
++ QCA8K_FDB_FLUSH_PORT = 5,
+ QCA8K_FDB_NEXT = 6,
+ QCA8K_FDB_SEARCH = 7,
+ };
--- /dev/null
+From 6a3bdc5209f45d2af83aa92433ab6e5cf2297aa4 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:47 +0100
+Subject: net: dsa: qca8k: add set_ageing_time support
+
+qca8k support setting ageing time in step of 7s. Add support for it and
+set the max value accepted of 7645m.
+Documentation talks about support for 10000m but that values doesn't
+make sense as the value doesn't match the max value in the reg.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 25 +++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 3 +++
+ 2 files changed, 28 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1261,6 +1261,10 @@ qca8k_setup(struct dsa_switch *ds)
+ /* We don't have interrupts for link changes, so we need to poll */
+ ds->pcs_poll = true;
+
++ /* Set min a max ageing value supported */
++ ds->ageing_time_min = 7000;
++ ds->ageing_time_max = 458745000;
++
+ return 0;
+ }
+
+@@ -1801,6 +1805,26 @@ qca8k_port_fast_age(struct dsa_switch *d
+ }
+
+ static int
++qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
++{
++ struct qca8k_priv *priv = ds->priv;
++ unsigned int secs = msecs / 1000;
++ u32 val;
++
++ /* AGE_TIME reg is set in 7s step */
++ val = secs / 7;
++
++ /* Handle case with 0 as val to NOT disable
++ * learning
++ */
++ if (!val)
++ val = 1;
++
++ return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK,
++ QCA8K_ATU_AGE_TIME(val));
++}
++
++static int
+ qca8k_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+ {
+@@ -2006,6 +2030,7 @@ static const struct dsa_switch_ops qca8k
+ .get_strings = qca8k_get_strings,
+ .get_ethtool_stats = qca8k_get_ethtool_stats,
+ .get_sset_count = qca8k_get_sset_count,
++ .set_ageing_time = qca8k_set_ageing_time,
+ .get_mac_eee = qca8k_get_mac_eee,
+ .set_mac_eee = qca8k_set_mac_eee,
+ .port_enable = qca8k_port_enable,
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -175,6 +175,9 @@
+ #define QCA8K_VTU_FUNC1_BUSY BIT(31)
+ #define QCA8K_VTU_FUNC1_VID_MASK GENMASK(27, 16)
+ #define QCA8K_VTU_FUNC1_FULL BIT(4)
++#define QCA8K_REG_ATU_CTRL 0x618
++#define QCA8K_ATU_AGE_TIME_MASK GENMASK(15, 0)
++#define QCA8K_ATU_AGE_TIME(x) FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x))
+ #define QCA8K_REG_GLOBAL_FW_CTRL0 0x620
+ #define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10)
+ #define QCA8K_REG_GLOBAL_FW_CTRL1 0x624
--- /dev/null
+From ba8f870dfa635113ce6e8095a5eb1835ecde2e9e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:48 +0100
+Subject: net: dsa: qca8k: add support for mdb_add/del
+
+Add support for mdb add/del function. The ARL table is used to insert
+the rule. The rule will be searched, deleted and reinserted with the
+port mask updated. The function will check if the rule has to be updated
+or insert directly with no deletion of the old rule.
+If every port is removed from the port mask, the rule is removed.
+The rule is set STATIC in the ARL table (aka it doesn't age) to not be
+flushed by fast age function.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 99 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -436,6 +436,81 @@ qca8k_fdb_flush(struct qca8k_priv *priv)
+ }
+
+ static int
++qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ ret = qca8k_fdb_read(priv, &fdb);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule exist. Delete first */
++ if (!fdb.aging) {
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++ }
++
++ /* Add port to fdb portmask */
++ fdb.port_mask |= port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int
++qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule doesn't exist. Why delete? */
++ if (!fdb.aging) {
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++
++ /* Only port in the rule is this port. Don't re insert */
++ if (fdb.port_mask == port_mask)
++ goto exit;
++
++ /* Remove port from port mask */
++ fdb.port_mask &= ~port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int
+ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
+ {
+ u32 reg;
+@@ -1929,6 +2004,28 @@ qca8k_port_fdb_dump(struct dsa_switch *d
+ return 0;
+ }
+
++static void
++qca8k_port_mdb_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid);
++}
++
++static int
++qca8k_port_mdb_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
++}
++
+ static int
+ qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+ struct switchdev_trans *trans)
+@@ -2044,6 +2141,8 @@ static const struct dsa_switch_ops qca8k
+ .port_fdb_add = qca8k_port_fdb_add,
+ .port_fdb_del = qca8k_port_fdb_del,
+ .port_fdb_dump = qca8k_port_fdb_dump,
++ .port_mdb_add = qca8k_port_mdb_add,
++ .port_mdb_del = qca8k_port_mdb_del,
+ .port_vlan_filtering = qca8k_port_vlan_filtering,
+ .port_vlan_prepare = qca8k_port_vlan_prepare,
+ .port_vlan_add = qca8k_port_vlan_add,
--- /dev/null
+From 9470174e7581e75a8ebd78964997314dfc2e706c Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Tue, 3 Nov 2020 13:06:18 +0800
+Subject: [PATCH] net: dsa: mt7530: support setting MTU
+
+MT7530/7531 has a global RX packet length register, which can be used
+to set MTU.
+
+Supported packet length values are 1522 (1518 if untagged), 1536,
+1552, and multiple of 1024 (from 2048 to 15360).
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Link: https://lore.kernel.org/r/20201103050618.11419-1-dqfext@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 49 ++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/mt7530.h | 12 ++++++++++
+ 2 files changed, 61 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1015,6 +1015,53 @@ mt7530_port_disable(struct dsa_switch *d
+ mutex_unlock(&priv->reg_mutex);
+ }
+
++static int
++mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
++{
++ struct mt7530_priv *priv = ds->priv;
++ struct mii_bus *bus = priv->bus;
++ int length;
++ u32 val;
++
++ /* When a new MTU is set, DSA always set the CPU port's MTU to the
++ * largest MTU of the slave ports. Because the switch only has a global
++ * RX length register, only allowing CPU port here is enough.
++ */
++ if (!dsa_is_cpu_port(ds, port))
++ return 0;
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ val = mt7530_mii_read(priv, MT7530_GMACCR);
++ val &= ~MAX_RX_PKT_LEN_MASK;
++
++ /* RX length also includes Ethernet header, MTK tag, and FCS length */
++ length = new_mtu + ETH_HLEN + MTK_HDR_LEN + ETH_FCS_LEN;
++ if (length <= 1522) {
++ val |= MAX_RX_PKT_LEN_1522;
++ } else if (length <= 1536) {
++ val |= MAX_RX_PKT_LEN_1536;
++ } else if (length <= 1552) {
++ val |= MAX_RX_PKT_LEN_1552;
++ } else {
++ val &= ~MAX_RX_JUMBO_MASK;
++ val |= MAX_RX_JUMBO(DIV_ROUND_UP(length, 1024));
++ val |= MAX_RX_PKT_LEN_JUMBO;
++ }
++
++ mt7530_mii_write(priv, MT7530_GMACCR, val);
++
++ mutex_unlock(&bus->mdio_lock);
++
++ return 0;
++}
++
++static int
++mt7530_port_max_mtu(struct dsa_switch *ds, int port)
++{
++ return MT7530_MAX_MTU;
++}
++
+ static void
+ mt7530_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+ {
+@@ -2652,6 +2699,8 @@ static const struct dsa_switch_ops mt753
+ .get_sset_count = mt7530_get_sset_count,
+ .port_enable = mt7530_port_enable,
+ .port_disable = mt7530_port_disable,
++ .port_change_mtu = mt7530_port_change_mtu,
++ .port_max_mtu = mt7530_port_max_mtu,
+ .port_stp_state_set = mt7530_stp_state_set,
+ .port_bridge_join = mt7530_port_bridge_join,
+ .port_bridge_leave = mt7530_port_bridge_leave,
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -11,6 +11,9 @@
+ #define MT7530_NUM_FDB_RECORDS 2048
+ #define MT7530_ALL_MEMBERS 0xff
+
++#define MTK_HDR_LEN 4
++#define MT7530_MAX_MTU (15 * 1024 - ETH_HLEN - ETH_FCS_LEN - MTK_HDR_LEN)
++
+ enum mt753x_id {
+ ID_MT7530 = 0,
+ ID_MT7621 = 1,
+@@ -301,6 +304,15 @@ enum mt7530_vlan_port_attr {
+ #define MT7531_DBG_CNT(x) (0x3018 + (x) * 0x100)
+ #define MT7531_DIS_CLR BIT(31)
+
++#define MT7530_GMACCR 0x30e0
++#define MAX_RX_JUMBO(x) ((x) << 2)
++#define MAX_RX_JUMBO_MASK GENMASK(5, 2)
++#define MAX_RX_PKT_LEN_MASK GENMASK(1, 0)
++#define MAX_RX_PKT_LEN_1522 0x0
++#define MAX_RX_PKT_LEN_1536 0x1
++#define MAX_RX_PKT_LEN_1552 0x2
++#define MAX_RX_PKT_LEN_JUMBO 0x3
++
+ /* Register for MIB */
+ #define MT7530_PORT_MIB_COUNTER(x) (0x4000 + (x) * 0x100)
+ #define MT7530_MIB_CCR 0x4fe0
--- /dev/null
+From 771c8901568dd8776a260aa93db41be88a60389e Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Fri, 11 Dec 2020 01:03:22 +0800
+Subject: [PATCH] net: dsa: mt7530: enable MTU normalization
+
+MT7530 has a global RX length register, so we are actually changing its
+MRU.
+Enable MTU normalization for this reason.
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Acked-by: Landen Chao <landen.chao@mediatek.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20201210170322.3433-1-dqfext@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1703,6 +1703,7 @@ mt7530_setup(struct dsa_switch *ds)
+ */
+ dn = dsa_to_port(ds, MT7530_CPU_PORT)->master->dev.of_node->parent;
+ ds->configure_vlan_while_not_filtering = true;
++ ds->mtu_enforcement_ingress = true;
+
+ if (priv->id == ID_MT7530) {
+ regulator_set_voltage(priv->core_pwr, 1000000, 1000000);
+@@ -1947,6 +1948,7 @@ mt7531_setup(struct dsa_switch *ds)
+ }
+
+ ds->configure_vlan_while_not_filtering = true;
++ ds->mtu_enforcement_ingress = true;
+
+ /* Flush the FDB table */
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL);
--- /dev/null
+From ea6d5c924e391872d402acac38461a5f8261e57f Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Tue, 8 Dec 2020 15:00:28 +0800
+Subject: [PATCH] net: dsa: mt7530: support setting ageing time
+
+MT7530 has a global address age control register, so use it to set
+ageing time.
+
+The applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 41 ++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/mt7530.h | 13 +++++++++++++
+ 2 files changed, 54 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -870,6 +870,46 @@ mt7530_get_sset_count(struct dsa_switch
+ return ARRAY_SIZE(mt7530_mib);
+ }
+
++static int
++mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
++{
++ struct mt7530_priv *priv = ds->priv;
++ unsigned int secs = msecs / 1000;
++ unsigned int tmp_age_count;
++ unsigned int error = -1;
++ unsigned int age_count;
++ unsigned int age_unit;
++
++ /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds */
++ if (secs < 1 || secs > (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1))
++ return -ERANGE;
++
++ /* iterate through all possible age_count to find the closest pair */
++ for (tmp_age_count = 0; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) {
++ unsigned int tmp_age_unit = secs / (tmp_age_count + 1) - 1;
++
++ if (tmp_age_unit <= AGE_UNIT_MAX) {
++ unsigned int tmp_error = secs -
++ (tmp_age_count + 1) * (tmp_age_unit + 1);
++
++ /* found a closer pair */
++ if (error > tmp_error) {
++ error = tmp_error;
++ age_count = tmp_age_count;
++ age_unit = tmp_age_unit;
++ }
++
++ /* found the exact match, so break the loop */
++ if (!error)
++ break;
++ }
++ }
++
++ mt7530_write(priv, MT7530_AAC, AGE_CNT(age_count) | AGE_UNIT(age_unit));
++
++ return 0;
++}
++
+ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface)
+ {
+ struct mt7530_priv *priv = ds->priv;
+@@ -2699,6 +2739,7 @@ static const struct dsa_switch_ops mt753
+ .phy_write = mt753x_phy_write,
+ .get_ethtool_stats = mt7530_get_ethtool_stats,
+ .get_sset_count = mt7530_get_sset_count,
++ .set_ageing_time = mt7530_set_ageing_time,
+ .port_enable = mt7530_port_enable,
+ .port_disable = mt7530_port_disable,
+ .port_change_mtu = mt7530_port_change_mtu,
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -161,6 +161,19 @@ enum mt7530_vlan_egress_attr {
+ MT7530_VLAN_EGRESS_STACK = 3,
+ };
+
++/* Register for address age control */
++#define MT7530_AAC 0xa0
++/* Disable ageing */
++#define AGE_DIS BIT(20)
++/* Age count */
++#define AGE_CNT_MASK GENMASK(19, 12)
++#define AGE_CNT_MAX 0xff
++#define AGE_CNT(x) (AGE_CNT_MASK & ((x) << 12))
++/* Age unit */
++#define AGE_UNIT_MASK GENMASK(11, 0)
++#define AGE_UNIT_MAX 0xfff
++#define AGE_UNIT(x) (AGE_UNIT_MASK & (x))
++
+ /* Register for port STP state control */
+ #define MT7530_SSP_P(x) (0x2000 + ((x) * 0x100))
+ #define FID_PST(x) ((x) & 0x3)
--- /dev/null
+From ba2203f36b981235556504fb7b62baee28512a40 Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Tue, 24 Aug 2021 11:37:50 +0800
+Subject: [PATCH] net: dsa: mt7530: disable learning on standalone ports
+
+This is a partial backport of commit 5a30833b9a16f8d1aa15de06636f9317ca51f9df
+("net: dsa: mt7530: support MDB and bridge flag operations") upstream.
+
+Make sure that the standalone ports start up with learning disabled.
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+---
+ drivers/net/dsa/mt7530.c | 16 ++++++++++++++--
+ 1 file changed, 14 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1163,6 +1163,8 @@ mt7530_port_bridge_join(struct dsa_switc
+ PCR_MATRIX_MASK, PCR_MATRIX(port_bitmap));
+ priv->ports[port].pm |= PCR_MATRIX(port_bitmap);
+
++ mt7530_clear(priv, MT7530_PSC_P(port), SA_DIS);
++
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+@@ -1260,6 +1262,8 @@ mt7530_port_bridge_leave(struct dsa_swit
+ PCR_MATRIX(BIT(MT7530_CPU_PORT)));
+ priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT));
+
++ mt7530_set(priv, MT7530_PSC_P(port), SA_DIS);
++
+ mutex_unlock(&priv->reg_mutex);
+ }
+
+@@ -1817,9 +1821,13 @@ mt7530_setup(struct dsa_switch *ds)
+ ret = mt753x_cpu_port_enable(ds, i);
+ if (ret)
+ return ret;
+- } else
++ } else {
+ mt7530_port_disable(ds, i);
+
++ /* Disable learning by default on all user ports */
++ mt7530_set(priv, MT7530_PSC_P(i), SA_DIS);
++ }
++
+ /* Enable consistent egress tag */
+ mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK,
+ PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
+@@ -1979,9 +1987,13 @@ mt7531_setup(struct dsa_switch *ds)
+ ret = mt753x_cpu_port_enable(ds, i);
+ if (ret)
+ return ret;
+- } else
++ } else {
+ mt7530_port_disable(ds, i);
+
++ /* Disable learning by default on all user ports */
++ mt7530_set(priv, MT7530_PSC_P(i), SA_DIS);
++ }
++
+ /* Enable consistent egress tag */
+ mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK,
+ PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
--- /dev/null
+From 59c8adbc8e2c7f6b46385f36962eadaad3ea2daa Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Wed, 4 Aug 2021 00:04:01 +0800
+Subject: [PATCH] net: dsa: mt7530: enable assisted learning on CPU port
+
+Consider the following bridge configuration, where bond0 is not
+offloaded:
+
+ +-- br0 --+
+ / / | \
+ / / | \
+ / | | bond0
+ / | | / \
+ swp0 swp1 swp2 swp3 swp4
+ . . .
+ . . .
+ A B C
+
+Address learning is enabled on offloaded ports (swp0~2) and the CPU
+port, so when client A sends a packet to C, the following will happen:
+
+1. The switch learns that client A can be reached at swp0.
+2. The switch probably already knows that client C can be reached at the
+ CPU port, so it forwards the packet to the CPU.
+3. The bridge core knows client C can be reached at bond0, so it
+ forwards the packet back to the switch.
+4. The switch learns that client A can be reached at the CPU port.
+5. The switch forwards the packet to either swp3 or swp4, according to
+ the packet's tag.
+
+That makes client A's MAC address flap between swp0 and the CPU port. If
+client B sends a packet to A, it is possible that the packet is
+forwarded to the CPU. With offload_fwd_mark = 1, the bridge core won't
+forward it back to the switch, resulting in packet loss.
+
+As we have the assisted_learning_on_cpu_port in DSA core now, enable
+that and disable hardware learning on the CPU port.
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Reviewed-by: Vladimir Oltean <oltean@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1747,6 +1747,7 @@ mt7530_setup(struct dsa_switch *ds)
+ */
+ dn = dsa_to_port(ds, MT7530_CPU_PORT)->master->dev.of_node->parent;
+ ds->configure_vlan_while_not_filtering = true;
++ ds->assisted_learning_on_cpu_port = true;
+ ds->mtu_enforcement_ingress = true;
+
+ if (priv->id == ID_MT7530) {
+@@ -1817,15 +1818,15 @@ mt7530_setup(struct dsa_switch *ds)
+ mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
+ PCR_MATRIX_CLR);
+
++ /* Disable learning by default on all ports */
++ 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;
+ } else {
+ mt7530_port_disable(ds, i);
+-
+- /* Disable learning by default on all user ports */
+- mt7530_set(priv, MT7530_PSC_P(i), SA_DIS);
+ }
+
+ /* Enable consistent egress tag */
+@@ -1981,6 +1982,9 @@ mt7531_setup(struct dsa_switch *ds)
+ mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
+ PCR_MATRIX_CLR);
+
++ /* Disable learning by default on all ports */
++ mt7530_set(priv, MT7530_PSC_P(i), SA_DIS);
++
+ mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR);
+
+ if (dsa_is_cpu_port(ds, i)) {
+@@ -1989,9 +1993,6 @@ mt7531_setup(struct dsa_switch *ds)
+ return ret;
+ } else {
+ mt7530_port_disable(ds, i);
+-
+- /* Disable learning by default on all user ports */
+- mt7530_set(priv, MT7530_PSC_P(i), SA_DIS);
+ }
+
+ /* Enable consistent egress tag */
+@@ -2000,6 +2001,7 @@ mt7531_setup(struct dsa_switch *ds)
+ }
+
+ ds->configure_vlan_while_not_filtering = true;
++ ds->assisted_learning_on_cpu_port = true;
+ ds->mtu_enforcement_ingress = true;
+
+ /* Flush the FDB table */
--- /dev/null
+From e3a402764c5753698e7a9e45d4d21f093faa7852 Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Wed, 4 Aug 2021 00:04:02 +0800
+Subject: [PATCH] net: dsa: mt7530: use independent VLAN learning on
+ VLAN-unaware bridges
+
+Consider the following bridge configuration, where bond0 is not
+offloaded:
+
+ +-- br0 --+
+ / / | \
+ / / | \
+ / | | bond0
+ / | | / \
+ swp0 swp1 swp2 swp3 swp4
+ . . .
+ . . .
+ A B C
+
+Ideally, when the switch receives a packet from swp3 or swp4, it should
+forward the packet to the CPU, according to the port matrix and unknown
+unicast flood settings.
+
+But packet loss will happen if the destination address is at one of the
+offloaded ports (swp0~2). For example, when client C sends a packet to
+A, the FDB lookup will indicate that it should be forwarded to swp0, but
+the port matrix of swp3 and swp4 is configured to only allow the CPU to
+be its destination, so it is dropped.
+
+However, this issue does not happen if the bridge is VLAN-aware. That is
+because VLAN-aware bridges use independent VLAN learning, i.e. use VID
+for FDB lookup, on offloaded ports. As swp3 and swp4 are not offloaded,
+shared VLAN learning with default filter ID of 0 is used instead. So the
+lookup for A with filter ID 0 never hits and the packet can be forwarded
+to the CPU.
+
+In the current code, only two combinations were used to toggle user
+ports' VLAN awareness: one is PCR.PORT_VLAN set to port matrix mode with
+PVC.VLAN_ATTR set to transparent port, the other is PCR.PORT_VLAN set to
+security mode with PVC.VLAN_ATTR set to user port.
+
+It turns out that only PVC.VLAN_ATTR contributes to VLAN awareness, and
+port matrix mode just skips the VLAN table lookup. The reference manual
+is somehow misleading when describing PORT_VLAN modes. It states that
+PORT_MEM (VLAN port member) is used for destination if the VLAN table
+lookup hits, but actually **PORT_MEM & PORT_MATRIX** (bitwise AND of
+VLAN port member and port matrix) is used instead, which means we can
+have two or more separate VLAN-aware bridges with the same PVID and
+traffic won't leak between them.
+
+Therefore, to solve this, enable independent VLAN learning with PVID 0
+on VLAN-unaware bridges, by setting their PCR.PORT_VLAN to fallback
+mode, while leaving standalone ports in port matrix mode. The CPU port
+is always set to fallback mode to serve those bridges.
+
+During testing, it is found that FDB lookup with filter ID of 0 will
+also hit entries with VID 0 even with independent VLAN learning. To
+avoid that, install all VLANs with filter ID of 1.
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 72 +++++++++++++++++++++++++++++-----------
+ drivers/net/dsa/mt7530.h | 9 ++++-
+ 2 files changed, 60 insertions(+), 21 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1011,6 +1011,10 @@ mt753x_cpu_port_enable(struct dsa_switch
+ mt7530_write(priv, MT7530_PCR_P(port),
+ PCR_MATRIX(dsa_user_ports(priv->ds)));
+
++ /* 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;
+ }
+
+@@ -1165,6 +1169,10 @@ mt7530_port_bridge_join(struct dsa_switc
+
+ mt7530_clear(priv, MT7530_PSC_P(port), SA_DIS);
+
++ /* Set to fallback mode for independent VLAN learning */
++ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
++ MT7530_PORT_FALLBACK_MODE);
++
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+@@ -1177,16 +1185,21 @@ mt7530_port_set_vlan_unaware(struct dsa_
+ bool all_user_ports_removed = true;
+ int i;
+
+- /* 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
+- * port.
++ /* This is called after .port_bridge_leave when leaving a VLAN-aware
++ * bridge. Don't set standalone ports to fallback mode.
+ */
+- mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
+- MT7530_PORT_MATRIX_MODE);
++ if (dsa_to_port(ds, port)->bridge_dev)
++ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
++ MT7530_PORT_FALLBACK_MODE);
++
+ mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK | PVC_EG_TAG_MASK,
+ VLAN_ATTR(MT7530_VLAN_TRANSPARENT) |
+ PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
+
++ /* Set PVID to 0 */
++ mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
++ G0_PORT_VID_DEF);
++
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ if (dsa_is_user_port(ds, i) &&
+ dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) {
+@@ -1212,15 +1225,14 @@ mt7530_port_set_vlan_aware(struct dsa_sw
+ struct mt7530_priv *priv = ds->priv;
+
+ /* Trapped into security mode allows packet forwarding through VLAN
+- * table lookup. CPU port is set to fallback mode to let untagged
+- * frames pass through.
++ * table lookup.
+ */
+- if (dsa_is_cpu_port(ds, port))
+- mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
+- MT7530_PORT_FALLBACK_MODE);
+- else
++ if (dsa_is_user_port(ds, port)) {
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
+ MT7530_PORT_SECURITY_MODE);
++ mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
++ G0_PORT_VID(priv->ports[port].pvid));
++ }
+
+ /* Set the port as a user port which is to be able to recognize VID
+ * from incoming packets before fetching entry within the VLAN table.
+@@ -1264,6 +1276,13 @@ mt7530_port_bridge_leave(struct dsa_swit
+
+ mt7530_set(priv, MT7530_PSC_P(port), SA_DIS);
+
++ /* 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
++ * port.
++ */
++ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
++ MT7530_PORT_MATRIX_MODE);
++
+ mutex_unlock(&priv->reg_mutex);
+ }
+
+@@ -1406,7 +1425,8 @@ mt7530_hw_vlan_add(struct mt7530_priv *p
+ /* Validate the entry with independent learning, create egress tag per
+ * VLAN and joining the port as one of the port members.
+ */
+- val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | VLAN_VALID;
++ val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | FID(FID_BRIDGED) |
++ VLAN_VALID;
+ mt7530_write(priv, MT7530_VAWD1, val);
+
+ /* Decide whether adding tag or not for those outgoing packets from the
+@@ -1499,9 +1519,13 @@ mt7530_port_vlan_add(struct dsa_switch *
+ }
+
+ if (pvid) {
+- mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
+- G0_PORT_VID(vlan->vid_end));
+ priv->ports[port].pvid = vlan->vid_end;
++
++ /* Only configure PVID if VLAN filtering is enabled */
++ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port)))
++ mt7530_rmw(priv, MT7530_PPBV1_P(port),
++ G0_PORT_VID_MASK,
++ G0_PORT_VID(vlan->vid_end));
+ }
+
+ mutex_unlock(&priv->reg_mutex);
+@@ -1513,11 +1537,10 @@ mt7530_port_vlan_del(struct dsa_switch *
+ {
+ struct mt7530_hw_vlan_entry target_entry;
+ struct mt7530_priv *priv = ds->priv;
+- u16 vid, pvid;
++ u16 vid;
+
+ mutex_lock(&priv->reg_mutex);
+
+- pvid = priv->ports[port].pvid;
+ for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+ mt7530_hw_vlan_entry_init(&target_entry, port, 0);
+ mt7530_hw_vlan_update(priv, vid, &target_entry,
+@@ -1526,12 +1549,13 @@ mt7530_port_vlan_del(struct dsa_switch *
+ /* PVID is being restored to the default whenever the PVID port
+ * is being removed from the VLAN.
+ */
+- if (pvid == vid)
+- pvid = G0_PORT_VID_DEF;
++ if (priv->ports[port].pvid == vid) {
++ priv->ports[port].pvid = G0_PORT_VID_DEF;
++ mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
++ G0_PORT_VID_DEF);
++ }
+ }
+
+- mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, pvid);
+- priv->ports[port].pvid = pvid;
+
+ mutex_unlock(&priv->reg_mutex);
+
+@@ -1827,6 +1851,10 @@ mt7530_setup(struct dsa_switch *ds)
+ return ret;
+ } else {
+ mt7530_port_disable(ds, i);
++
++ /* Set default PVID to 0 on all user ports */
++ mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK,
++ G0_PORT_VID_DEF);
+ }
+
+ /* Enable consistent egress tag */
+@@ -1993,6 +2021,10 @@ mt7531_setup(struct dsa_switch *ds)
+ return ret;
+ } else {
+ mt7530_port_disable(ds, i);
++
++ /* Set default PVID to 0 on all user ports */
++ mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK,
++ G0_PORT_VID_DEF);
+ }
+
+ /* Enable consistent egress tag */
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -145,11 +145,18 @@ enum mt7530_vlan_cmd {
+ #define VTAG_EN BIT(28)
+ /* VLAN Member Control */
+ #define PORT_MEM(x) (((x) & 0xff) << 16)
++/* Filter ID */
++#define FID(x) (((x) & 0x7) << 1)
+ /* VLAN Entry Valid */
+ #define VLAN_VALID BIT(0)
+ #define PORT_MEM_SHFT 16
+ #define PORT_MEM_MASK 0xff
+
++enum mt7530_fid {
++ FID_STANDALONE = 0,
++ FID_BRIDGED = 1,
++};
++
+ #define MT7530_VAWD2 0x98
+ /* Egress Tag Control */
+ #define ETAG_CTRL_P(p, x) (((x) & 0x3) << ((p) << 1))
+@@ -244,7 +251,7 @@ enum mt7530_vlan_port_attr {
+ #define MT7530_PPBV1_P(x) (0x2014 + ((x) * 0x100))
+ #define G0_PORT_VID(x) (((x) & 0xfff) << 0)
+ #define G0_PORT_VID_MASK G0_PORT_VID(0xfff)
+-#define G0_PORT_VID_DEF G0_PORT_VID(1)
++#define G0_PORT_VID_DEF G0_PORT_VID(0)
+
+ /* Register for port MAC control register */
+ #define MT7530_PMCR_P(x) (0x3000 + ((x) * 0x100))
--- /dev/null
+From c5ffcefcb40420528d04c63e7dfc88f2845c9831 Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Wed, 4 Aug 2021 00:04:03 +0800
+Subject: [PATCH] net: dsa: mt7530: set STP state on filter ID 1
+
+As filter ID 1 is the only one used for bridges, set STP state on it.
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 3 ++-
+ drivers/net/dsa/mt7530.h | 4 ++--
+ 2 files changed, 4 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1131,7 +1131,8 @@ mt7530_stp_state_set(struct dsa_switch *
+ break;
+ }
+
+- mt7530_rmw(priv, MT7530_SSP_P(port), FID_PST_MASK, stp_state);
++ mt7530_rmw(priv, MT7530_SSP_P(port), FID_PST_MASK(FID_BRIDGED),
++ FID_PST(FID_BRIDGED, stp_state));
+ }
+
+ static int
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -183,8 +183,8 @@ enum mt7530_vlan_egress_attr {
+
+ /* Register for port STP state control */
+ #define MT7530_SSP_P(x) (0x2000 + ((x) * 0x100))
+-#define FID_PST(x) ((x) & 0x3)
+-#define FID_PST_MASK FID_PST(0x3)
++#define FID_PST(fid, state) (((state) & 0x3) << ((fid) * 2))
++#define FID_PST_MASK(fid) FID_PST(fid, 0x3)
+
+ enum mt7530_stp_state {
+ MT7530_STP_DISABLED = 0,
--- /dev/null
+From 138c126a33f7564edb66b1da5b847e4a60740bfc Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Wed, 4 Aug 2021 00:04:04 +0800
+Subject: [PATCH] net: dsa: mt7530: always install FDB entries with IVL and FID
+ 1
+
+This reverts commit 7e777021780e ("mt7530 mt7530_fdb_write only set ivl
+bit vid larger than 1").
+
+Before this series, the default value of all ports' PVID is 1, which is
+copied into the FDB entry, even if the ports are VLAN unaware. So
+`bridge fdb show` will show entries like `dev swp0 vlan 1 self` even on
+a VLAN-unaware bridge.
+
+The blamed commit does not solve that issue completely, instead it may
+cause a new issue that FDB is inaccessible in a VLAN-aware bridge with
+PVID 1.
+
+This series sets PVID to 0 on VLAN-unaware ports, so `bridge fdb show`
+will no longer print `vlan 1` on VLAN-unaware bridges, and that special
+case in fdb_write is not required anymore.
+
+Set FDB entries' filter ID to 1 to match the VLAN table.
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 2 ++
+ drivers/net/dsa/mt7530.h | 2 ++
+ 2 files changed, 4 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -361,6 +361,8 @@ mt7530_fdb_write(struct mt7530_priv *pri
+ int i;
+
+ reg[1] |= vid & CVID_MASK;
++ reg[1] |= ATA2_IVL;
++ reg[1] |= ATA2_FID(FID_BRIDGED);
+ reg[2] |= (aging & AGE_TIMER_MASK) << AGE_TIMER;
+ reg[2] |= (port_mask & PORT_MAP_MASK) << PORT_MAP;
+ /* STATIC_ENT indicate that entry is static wouldn't
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -77,6 +77,8 @@ enum mt753x_bpdu_port_fw {
+ #define STATIC_EMP 0
+ #define STATIC_ENT 3
+ #define MT7530_ATA2 0x78
++#define ATA2_IVL BIT(15)
++#define ATA2_FID(x) (((x) & 0x7) << 12)
+
+ /* Register for address table write data */
+ #define MT7530_ATWD 0x7c
--- /dev/null
+From 1f11a07a33bc26997c18b633d63f088bf75d11f2 Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Tue, 24 Aug 2021 11:37:50 +0800
+Subject: [PATCH] net: dsa: mt7530: support MDB operations
+
+This is a partial backport of commit 5a30833b9a16f8d1aa15de06636f9317ca51f9df
+("net: dsa: mt7530: support MDB and bridge flag operations") upstream.
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+---
+ drivers/net/dsa/mt7530.c | 78 ++++++++++++++++++++++++++++++++++++++--
+ net/dsa/tag_mtk.c | 14 +-------
+ 2 files changed, 76 insertions(+), 16 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1000,9 +1000,6 @@ mt753x_cpu_port_enable(struct dsa_switch
+ mt7530_write(priv, MT7530_PVC_P(port),
+ PORT_SPEC_TAG);
+
+- /* Unknown multicast frame forwarding to the cpu port */
+- mt7530_rmw(priv, MT7530_MFC, UNM_FFP_MASK, UNM_FFP(BIT(port)));
+-
+ /* Set CPU port number */
+ if (priv->id == ID_MT7621)
+ mt7530_rmw(priv, MT7530_MFC, CPU_MASK, CPU_EN | CPU_PORT(port));
+@@ -1138,6 +1135,20 @@ mt7530_stp_state_set(struct dsa_switch *
+ }
+
+ static int
++mt7530_port_egress_floods(struct dsa_switch *ds, int port,
++ bool unicast, bool multicast)
++{
++ struct mt7530_priv *priv = ds->priv;
++
++ mt7530_rmw(priv, MT7530_MFC,
++ UNU_FFP(BIT(port)) | UNM_FFP(BIT(port)),
++ (unicast ? UNU_FFP(BIT(port)) : 0) |
++ (multicast ? UNM_FFP(BIT(port)) : 0));
++
++ return 0;
++}
++
++static int
+ mt7530_port_bridge_join(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+ {
+@@ -1357,6 +1368,63 @@ err:
+ }
+
+ static int
++mt7530_port_mdb_prepare(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ return 0;
++}
++
++static void
++mt7530_port_mdb_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct mt7530_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++ u8 port_mask = 0;
++
++ mutex_lock(&priv->reg_mutex);
++
++ mt7530_fdb_write(priv, vid, 0, addr, 0, STATIC_EMP);
++ if (!mt7530_fdb_cmd(priv, MT7530_FDB_READ, NULL))
++ port_mask = (mt7530_read(priv, MT7530_ATRD) >> PORT_MAP)
++ & PORT_MAP_MASK;
++
++ port_mask |= BIT(port);
++ mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_ENT);
++ mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, NULL);
++
++ mutex_unlock(&priv->reg_mutex);
++}
++
++static int
++mt7530_port_mdb_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct mt7530_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++ u8 port_mask = 0;
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ mt7530_fdb_write(priv, vid, 0, addr, 0, STATIC_EMP);
++ if (!mt7530_fdb_cmd(priv, MT7530_FDB_READ, NULL))
++ port_mask = (mt7530_read(priv, MT7530_ATRD) >> PORT_MAP)
++ & PORT_MAP_MASK;
++
++ port_mask &= ~BIT(port);
++ mt7530_fdb_write(priv, vid, port_mask, addr, -1,
++ port_mask ? STATIC_ENT : STATIC_EMP);
++ ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, NULL);
++
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static int
+ mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
+ {
+ struct mt7530_dummy_poll p;
+@@ -2794,11 +2862,15 @@ static const struct dsa_switch_ops mt753
+ .port_change_mtu = mt7530_port_change_mtu,
+ .port_max_mtu = mt7530_port_max_mtu,
+ .port_stp_state_set = mt7530_stp_state_set,
++ .port_egress_floods = mt7530_port_egress_floods,
+ .port_bridge_join = mt7530_port_bridge_join,
+ .port_bridge_leave = mt7530_port_bridge_leave,
+ .port_fdb_add = mt7530_port_fdb_add,
+ .port_fdb_del = mt7530_port_fdb_del,
+ .port_fdb_dump = mt7530_port_fdb_dump,
++ .port_mdb_prepare = mt7530_port_mdb_prepare,
++ .port_mdb_add = mt7530_port_mdb_add,
++ .port_mdb_del = mt7530_port_mdb_del,
+ .port_vlan_filtering = mt7530_port_vlan_filtering,
+ .port_vlan_prepare = mt7530_port_vlan_prepare,
+ .port_vlan_add = mt7530_port_vlan_add,
+--- a/net/dsa/tag_mtk.c
++++ b/net/dsa/tag_mtk.c
+@@ -24,9 +24,6 @@ static struct sk_buff *mtk_tag_xmit(stru
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ u8 xmit_tpid;
+ u8 *mtk_tag;
+- unsigned char *dest = eth_hdr(skb)->h_dest;
+- bool is_multicast_skb = is_multicast_ether_addr(dest) &&
+- !is_broadcast_ether_addr(dest);
+
+ /* Build the special tag after the MAC Source Address. If VLAN header
+ * is present, it's required that VLAN header and special tag is
+@@ -55,10 +52,6 @@ static struct sk_buff *mtk_tag_xmit(stru
+ mtk_tag[0] = xmit_tpid;
+ mtk_tag[1] = (1 << dp->index) & MTK_HDR_XMIT_DP_BIT_MASK;
+
+- /* Disable SA learning for multicast frames */
+- if (unlikely(is_multicast_skb))
+- mtk_tag[1] |= MTK_HDR_XMIT_SA_DIS;
+-
+ /* Tag control information is kept for 802.1Q */
+ if (xmit_tpid == MTK_HDR_XMIT_UNTAGGED) {
+ mtk_tag[2] = 0;
+@@ -74,9 +67,6 @@ static struct sk_buff *mtk_tag_rcv(struc
+ u16 hdr;
+ int port;
+ __be16 *phdr;
+- unsigned char *dest = eth_hdr(skb)->h_dest;
+- bool is_multicast_skb = is_multicast_ether_addr(dest) &&
+- !is_broadcast_ether_addr(dest);
+
+ if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN)))
+ return NULL;
+@@ -102,9 +92,7 @@ static struct sk_buff *mtk_tag_rcv(struc
+ if (!skb->dev)
+ return NULL;
+
+- /* Only unicast or broadcast frames are offloaded */
+- if (likely(!is_multicast_skb))
+- dsa_default_offload_fwd_mark(skb);
++ dsa_default_offload_fwd_mark(skb);
+
+ return skb;
+ }
--- /dev/null
+From e40d2cca01893c1941f5959b14bb0cd0d4f4d099 Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Wed, 19 May 2021 11:31:59 +0800
+Subject: [PATCH] net: phy: add MediaTek Gigabit Ethernet PHY driver
+
+Add support for MediaTek Gigabit Ethernet PHYs found in MT7530 and
+MT7531 switches.
+The initialization procedure is from the vendor driver, but due to lack
+of documentation, the function of some register values remains unknown.
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/Kconfig | 5 ++
+ drivers/net/phy/Makefile | 1 +
+ drivers/net/phy/mediatek-ge.c | 116 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 122 insertions(+)
+ create mode 100644 drivers/net/phy/mediatek-ge.c
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -201,6 +201,11 @@ config MARVELL_10G_PHY
+ help
+ Support for the Marvell Alaska MV88X3310 and compatible PHYs.
+
++config MEDIATEK_GE_PHY
++ tristate "MediaTek PHYs"
++ help
++ Supports the MediaTek switch integrated PHYs.
++
+ config MICREL_PHY
+ tristate "Micrel PHYs"
+ help
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -63,6 +63,7 @@ obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c
+ obj-$(CONFIG_LXT_PHY) += lxt.o
+ obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o
+ obj-$(CONFIG_MARVELL_PHY) += marvell.o
++obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o
+ obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o
+ obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o
+ obj-$(CONFIG_MICREL_PHY) += micrel.o
+--- /dev/null
++++ b/drivers/net/phy/mediatek-ge.c
+@@ -0,0 +1,113 @@
++// SPDX-License-Identifier: GPL-2.0+
++#include <linux/bitfield.h>
++#include <linux/module.h>
++#include <linux/phy.h>
++
++#define MTK_T10_TEST_CONTROL 0x145
++#define MTK_PHY_TP_MASK GENMASK(4, 3)
++#define MTK_PHY_TP_AUTO 0
++#define MTK_PHY_TP_MDI 2
++#define MTK_PHY_TP_MDIX 3
++
++#define MTK_EXT_PAGE_ACCESS 0x1f
++#define MTK_PHY_PAGE_STANDARD 0x0000
++#define MTK_PHY_PAGE_EXTENDED 0x0001
++#define MTK_PHY_PAGE_EXTENDED_2 0x0002
++#define MTK_PHY_PAGE_EXTENDED_3 0x0003
++#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30
++#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5
++
++static int mtk_gephy_read_page(struct phy_device *phydev)
++{
++ return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
++}
++
++static int mtk_gephy_write_page(struct phy_device *phydev, int page)
++{
++ return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
++}
++
++static void mtk_gephy_config_init(struct phy_device *phydev)
++{
++ /* Disable EEE */
++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0);
++
++ /* Enable HW auto downshift */
++ phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, 0x14, 0, BIT(4));
++
++ /* Increase SlvDPSready time */
++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
++ __phy_write(phydev, 0x10, 0xafae);
++ __phy_write(phydev, 0x12, 0x2f);
++ __phy_write(phydev, 0x10, 0x8fae);
++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
++
++ /* Adjust 100_mse_threshold */
++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x123, 0xffff);
++
++ /* Disable mcc */
++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xa6, 0x300);
++}
++
++static int mt7530_phy_config_init(struct phy_device *phydev)
++{
++ mtk_gephy_config_init(phydev);
++
++ /* Increase post_update_timer */
++ phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, 0x11, 0x4b);
++
++ return 0;
++}
++
++static int mt7531_phy_config_init(struct phy_device *phydev)
++{
++ mtk_gephy_config_init(phydev);
++
++ /* PHY link down power saving enable */
++ phy_set_bits(phydev, 0x17, BIT(4));
++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xc6, 0x300);
++
++ /* Set TX Pair delay selection */
++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404);
++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404);
++
++ return 0;
++}
++
++static struct phy_driver mtk_gephy_driver[] = {
++ {
++ PHY_ID_MATCH_EXACT(0x03a29412),
++ .name = "MediaTek MT7530 PHY",
++ .config_init = mt7530_phy_config_init,
++ /* Interrupts are handled by the switch, not the PHY
++ * itself.
++ */
++ .config_intr = genphy_no_config_intr,
++ .ack_interrupt = genphy_no_ack_interrupt,
++ .suspend = genphy_suspend,
++ .resume = genphy_resume,
++ .read_page = mtk_gephy_read_page,
++ .write_page = mtk_gephy_write_page,
++ },
++ {
++ PHY_ID_MATCH_EXACT(0x03a29441),
++ .name = "MediaTek MT7531 PHY",
++ .config_init = mt7531_phy_config_init,
++ /* Interrupts are handled by the switch, not the PHY
++ * itself.
++ */
++ .config_intr = genphy_no_config_intr,
++ .ack_interrupt = genphy_no_ack_interrupt,
++ .suspend = genphy_suspend,
++ .resume = genphy_resume,
++ .read_page = mtk_gephy_read_page,
++ .write_page = mtk_gephy_write_page,
++ },
++};
++
++module_phy_driver(mtk_gephy_driver);
++
++static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = {
++ { PHY_ID_MATCH_VENDOR(0x03a29400) },
++ { }
++};
--- /dev/null
+From ba751e28d44255744a30190faad0ca09b455c44d Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Wed, 19 May 2021 11:32:00 +0800
+Subject: [PATCH] net: dsa: mt7530: add interrupt support
+
+Add support for MT7530 interrupt controller to handle internal PHYs.
+In order to assign an IRQ number to each PHY, the registration of MDIO bus
+is also done in this driver.
+
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 264 +++++++++++++++++++++++++++++++++++----
+ drivers/net/dsa/mt7530.h | 20 ++-
+ 2 files changed, 256 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -10,6 +10,7 @@
+ #include <linux/mfd/syscon.h>
+ #include <linux/module.h>
+ #include <linux/netdevice.h>
++#include <linux/of_irq.h>
+ #include <linux/of_mdio.h>
+ #include <linux/of_net.h>
+ #include <linux/of_platform.h>
+@@ -602,18 +603,14 @@ mt7530_mib_reset(struct dsa_switch *ds)
+ mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_ACTIVATE);
+ }
+
+-static int mt7530_phy_read(struct dsa_switch *ds, int port, int regnum)
++static int mt7530_phy_read(struct mt7530_priv *priv, int port, int regnum)
+ {
+- struct mt7530_priv *priv = ds->priv;
+-
+ return mdiobus_read_nested(priv->bus, port, regnum);
+ }
+
+-static int mt7530_phy_write(struct dsa_switch *ds, int port, int regnum,
++static int mt7530_phy_write(struct mt7530_priv *priv, int port, int regnum,
+ u16 val)
+ {
+- struct mt7530_priv *priv = ds->priv;
+-
+ return mdiobus_write_nested(priv->bus, port, regnum, val);
+ }
+
+@@ -791,9 +788,8 @@ out:
+ }
+
+ static int
+-mt7531_ind_phy_read(struct dsa_switch *ds, int port, int regnum)
++mt7531_ind_phy_read(struct mt7530_priv *priv, int port, int regnum)
+ {
+- struct mt7530_priv *priv = ds->priv;
+ int devad;
+ int ret;
+
+@@ -809,10 +805,9 @@ mt7531_ind_phy_read(struct dsa_switch *d
+ }
+
+ static int
+-mt7531_ind_phy_write(struct dsa_switch *ds, int port, int regnum,
++mt7531_ind_phy_write(struct mt7530_priv *priv, int port, int regnum,
+ u16 data)
+ {
+- struct mt7530_priv *priv = ds->priv;
+ int devad;
+ int ret;
+
+@@ -828,6 +823,22 @@ mt7531_ind_phy_write(struct dsa_switch *
+ return ret;
+ }
+
++static int
++mt753x_phy_read(struct mii_bus *bus, int port, int regnum)
++{
++ struct mt7530_priv *priv = bus->priv;
++
++ return priv->info->phy_read(priv, port, regnum);
++}
++
++static int
++mt753x_phy_write(struct mii_bus *bus, int port, int regnum, u16 val)
++{
++ struct mt7530_priv *priv = bus->priv;
++
++ return priv->info->phy_write(priv, port, regnum, val);
++}
++
+ static void
+ mt7530_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ uint8_t *data)
+@@ -1824,6 +1835,210 @@ mt7530_setup_gpio(struct mt7530_priv *pr
+ return devm_gpiochip_add_data(dev, gc, priv);
+ }
+
++static irqreturn_t
++mt7530_irq_thread_fn(int irq, void *dev_id)
++{
++ struct mt7530_priv *priv = dev_id;
++ bool handled = false;
++ u32 val;
++ int p;
++
++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++ val = mt7530_mii_read(priv, MT7530_SYS_INT_STS);
++ mt7530_mii_write(priv, MT7530_SYS_INT_STS, val);
++ mutex_unlock(&priv->bus->mdio_lock);
++
++ for (p = 0; p < MT7530_NUM_PHYS; p++) {
++ if (BIT(p) & val) {
++ unsigned int irq;
++
++ irq = irq_find_mapping(priv->irq_domain, p);
++ handle_nested_irq(irq);
++ handled = true;
++ }
++ }
++
++ return IRQ_RETVAL(handled);
++}
++
++static void
++mt7530_irq_mask(struct irq_data *d)
++{
++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
++
++ priv->irq_enable &= ~BIT(d->hwirq);
++}
++
++static void
++mt7530_irq_unmask(struct irq_data *d)
++{
++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
++
++ priv->irq_enable |= BIT(d->hwirq);
++}
++
++static void
++mt7530_irq_bus_lock(struct irq_data *d)
++{
++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
++
++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++}
++
++static void
++mt7530_irq_bus_sync_unlock(struct irq_data *d)
++{
++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
++
++ mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable);
++ mutex_unlock(&priv->bus->mdio_lock);
++}
++
++static struct irq_chip mt7530_irq_chip = {
++ .name = KBUILD_MODNAME,
++ .irq_mask = mt7530_irq_mask,
++ .irq_unmask = mt7530_irq_unmask,
++ .irq_bus_lock = mt7530_irq_bus_lock,
++ .irq_bus_sync_unlock = mt7530_irq_bus_sync_unlock,
++};
++
++static int
++mt7530_irq_map(struct irq_domain *domain, unsigned int irq,
++ irq_hw_number_t hwirq)
++{
++ irq_set_chip_data(irq, domain->host_data);
++ irq_set_chip_and_handler(irq, &mt7530_irq_chip, handle_simple_irq);
++ irq_set_nested_thread(irq, true);
++ irq_set_noprobe(irq);
++
++ return 0;
++}
++
++static const struct irq_domain_ops mt7530_irq_domain_ops = {
++ .map = mt7530_irq_map,
++ .xlate = irq_domain_xlate_onecell,
++};
++
++static void
++mt7530_setup_mdio_irq(struct mt7530_priv *priv)
++{
++ struct dsa_switch *ds = priv->ds;
++ int p;
++
++ for (p = 0; p < MT7530_NUM_PHYS; p++) {
++ if (BIT(p) & ds->phys_mii_mask) {
++ unsigned int irq;
++
++ irq = irq_create_mapping(priv->irq_domain, p);
++ ds->slave_mii_bus->irq[p] = irq;
++ }
++ }
++}
++
++static int
++mt7530_setup_irq(struct mt7530_priv *priv)
++{
++ struct device *dev = priv->dev;
++ struct device_node *np = dev->of_node;
++ int ret;
++
++ if (!of_property_read_bool(np, "interrupt-controller")) {
++ dev_info(dev, "no interrupt support\n");
++ return 0;
++ }
++
++ priv->irq = of_irq_get(np, 0);
++ if (priv->irq <= 0) {
++ dev_err(dev, "failed to get parent IRQ: %d\n", priv->irq);
++ return priv->irq ? : -EINVAL;
++ }
++
++ priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS,
++ &mt7530_irq_domain_ops, priv);
++ if (!priv->irq_domain) {
++ dev_err(dev, "failed to create IRQ domain\n");
++ return -ENOMEM;
++ }
++
++ /* This register must be set for MT7530 to properly fire interrupts */
++ if (priv->id != ID_MT7531)
++ mt7530_set(priv, MT7530_TOP_SIG_CTRL, TOP_SIG_CTRL_NORMAL);
++
++ ret = request_threaded_irq(priv->irq, NULL, mt7530_irq_thread_fn,
++ IRQF_ONESHOT, KBUILD_MODNAME, priv);
++ if (ret) {
++ irq_domain_remove(priv->irq_domain);
++ dev_err(dev, "failed to request IRQ: %d\n", ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static void
++mt7530_free_mdio_irq(struct mt7530_priv *priv)
++{
++ int p;
++
++ for (p = 0; p < MT7530_NUM_PHYS; p++) {
++ if (BIT(p) & priv->ds->phys_mii_mask) {
++ unsigned int irq;
++
++ irq = irq_find_mapping(priv->irq_domain, p);
++ irq_dispose_mapping(irq);
++ }
++ }
++}
++
++static void
++mt7530_free_irq_common(struct mt7530_priv *priv)
++{
++ free_irq(priv->irq, priv);
++ irq_domain_remove(priv->irq_domain);
++}
++
++static void
++mt7530_free_irq(struct mt7530_priv *priv)
++{
++ mt7530_free_mdio_irq(priv);
++ mt7530_free_irq_common(priv);
++}
++
++static int
++mt7530_setup_mdio(struct mt7530_priv *priv)
++{
++ struct dsa_switch *ds = priv->ds;
++ struct device *dev = priv->dev;
++ struct mii_bus *bus;
++ static int idx;
++ int ret;
++
++ bus = devm_mdiobus_alloc(dev);
++ if (!bus)
++ return -ENOMEM;
++
++ ds->slave_mii_bus = bus;
++ bus->priv = priv;
++ bus->name = KBUILD_MODNAME "-mii";
++ snprintf(bus->id, MII_BUS_ID_SIZE, KBUILD_MODNAME "-%d", idx++);
++ bus->read = mt753x_phy_read;
++ bus->write = mt753x_phy_write;
++ bus->parent = dev;
++ bus->phy_mask = ~ds->phys_mii_mask;
++
++ if (priv->irq)
++ mt7530_setup_mdio_irq(priv);
++
++ ret = devm_mdiobus_register(dev, bus);
++ if (ret) {
++ dev_err(dev, "failed to register MDIO bus: %d\n", ret);
++ if (priv->irq)
++ mt7530_free_mdio_irq(priv);
++ }
++
++ return ret;
++}
++
+ static int
+ mt7530_setup(struct dsa_switch *ds)
+ {
+@@ -2798,24 +3013,20 @@ static int
+ mt753x_setup(struct dsa_switch *ds)
+ {
+ struct mt7530_priv *priv = ds->priv;
++ int ret = priv->info->sw_setup(ds);
+
+- return priv->info->sw_setup(ds);
+-}
+-
+-static int
+-mt753x_phy_read(struct dsa_switch *ds, int port, int regnum)
+-{
+- struct mt7530_priv *priv = ds->priv;
++ if (ret)
++ return ret;
+
+- return priv->info->phy_read(ds, port, regnum);
+-}
++ ret = mt7530_setup_irq(priv);
++ if (ret)
++ return ret;
+
+-static int
+-mt753x_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
+-{
+- struct mt7530_priv *priv = ds->priv;
++ ret = mt7530_setup_mdio(priv);
++ if (ret && priv->irq)
++ mt7530_free_irq_common(priv);
+
+- return priv->info->phy_write(ds, port, regnum, val);
++ return ret;
+ }
+
+ static int mt753x_get_mac_eee(struct dsa_switch *ds, int port,
+@@ -2852,8 +3063,6 @@ static const struct dsa_switch_ops mt753
+ .get_tag_protocol = mtk_get_tag_protocol,
+ .setup = mt753x_setup,
+ .get_strings = mt7530_get_strings,
+- .phy_read = mt753x_phy_read,
+- .phy_write = mt753x_phy_write,
+ .get_ethtool_stats = mt7530_get_ethtool_stats,
+ .get_sset_count = mt7530_get_sset_count,
+ .set_ageing_time = mt7530_set_ageing_time,
+@@ -3036,6 +3245,9 @@ mt7530_remove(struct mdio_device *mdiode
+ dev_err(priv->dev, "Failed to disable io pwr: %d\n",
+ ret);
+
++ if (priv->irq)
++ mt7530_free_irq(priv);
++
+ dsa_unregister_switch(priv->ds);
+ mutex_destroy(&priv->reg_mutex);
+ }
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -7,6 +7,7 @@
+ #define __MT7530_H
+
+ #define MT7530_NUM_PORTS 7
++#define MT7530_NUM_PHYS 5
+ #define MT7530_CPU_PORT 6
+ #define MT7530_NUM_FDB_RECORDS 2048
+ #define MT7530_ALL_MEMBERS 0xff
+@@ -401,6 +402,12 @@ enum mt7531_sgmii_force_duplex {
+ #define SYS_CTRL_SW_RST BIT(1)
+ #define SYS_CTRL_REG_RST BIT(0)
+
++/* Register for system interrupt */
++#define MT7530_SYS_INT_EN 0x7008
++
++/* Register for system interrupt status */
++#define MT7530_SYS_INT_STS 0x700c
++
+ /* Register for PHY Indirect Access Control */
+ #define MT7531_PHY_IAC 0x701C
+ #define MT7531_PHY_ACS_ST BIT(31)
+@@ -722,6 +729,8 @@ static const char *p5_intf_modes(unsigne
+ }
+ }
+
++struct mt7530_priv;
++
+ /* struct mt753x_info - This is the main data structure for holding the specific
+ * part for each supported device
+ * @sw_setup: Holding the handler to a device initialization
+@@ -746,8 +755,8 @@ struct mt753x_info {
+ enum mt753x_id id;
+
+ int (*sw_setup)(struct dsa_switch *ds);
+- int (*phy_read)(struct dsa_switch *ds, int port, int regnum);
+- int (*phy_write)(struct dsa_switch *ds, int port, int regnum, u16 val);
++ int (*phy_read)(struct mt7530_priv *priv, int port, int regnum);
++ int (*phy_write)(struct mt7530_priv *priv, int port, 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);
+ bool (*phy_mode_supported)(struct dsa_switch *ds, int port,
+@@ -781,6 +790,10 @@ struct mt753x_info {
+ * registers
+ * @p6_interface Holding the current port 6 interface
+ * @p5_intf_sel: Holding the current port 5 interface select
++ *
++ * @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
+ */
+ struct mt7530_priv {
+ struct device *dev;
+@@ -802,6 +815,9 @@ struct mt7530_priv {
+ struct mt7530_port ports[MT7530_NUM_PORTS];
+ /* protect among processes for registers access*/
+ struct mutex reg_mutex;
++ int irq;
++ struct irq_domain *irq_domain;
++ u32 irq_enable;
+ };
+
+ struct mt7530_hw_vlan_entry {
--- /dev/null
+From 7164a8cde4b42f76474088ccaf53f1e463d4e2f6 Mon Sep 17 00:00:00 2001
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Date: Mon, 24 Jan 2022 22:09:43 +0100
+Subject: [PATCH 5.10 1/2] net: dsa: Move VLAN filtering syncing out of
+ dsa_switch_bridge_leave
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+commit 381a730182f1d174e1950cd4e63e885b1c302051 upstream.
+
+Most of dsa_switch_bridge_leave was, in fact, dealing with the syncing
+of VLAN filtering for switches on which that is a global
+setting. Separate the two phases to prepare for the cross-chip related
+bugfix in the following commit.
+
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ net/dsa/switch.c | 39 ++++++++++++++++++++++++++-------------
+ 1 file changed, 26 insertions(+), 13 deletions(-)
+
+--- a/net/dsa/switch.c
++++ b/net/dsa/switch.c
+@@ -104,23 +104,12 @@ static int dsa_switch_bridge_join(struct
+ return 0;
+ }
+
+-static int dsa_switch_bridge_leave(struct dsa_switch *ds,
+- struct dsa_notifier_bridge_info *info)
++static int dsa_switch_sync_vlan_filtering(struct dsa_switch *ds,
++ struct dsa_notifier_bridge_info *info)
+ {
+ bool unset_vlan_filtering = br_vlan_enabled(info->br);
+- struct dsa_switch_tree *dst = ds->dst;
+ int err, i;
+
+- if (dst->index == info->tree_index && ds->index == info->sw_index &&
+- ds->ops->port_bridge_leave)
+- ds->ops->port_bridge_leave(ds, info->port, info->br);
+-
+- if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
+- ds->ops->crosschip_bridge_leave)
+- ds->ops->crosschip_bridge_leave(ds, info->tree_index,
+- info->sw_index, info->port,
+- info->br);
+-
+ /* If the bridge was vlan_filtering, the bridge core doesn't trigger an
+ * event for changing vlan_filtering setting upon slave ports leaving
+ * it. That is a good thing, because that lets us handle it and also
+@@ -153,6 +142,30 @@ static int dsa_switch_bridge_leave(struc
+ if (err && err != EOPNOTSUPP)
+ return err;
+ }
++
++ return 0;
++}
++
++static int dsa_switch_bridge_leave(struct dsa_switch *ds,
++ struct dsa_notifier_bridge_info *info)
++{
++ struct dsa_switch_tree *dst = ds->dst;
++ int err;
++
++ if (dst->index == info->tree_index && ds->index == info->sw_index &&
++ ds->ops->port_bridge_leave)
++ ds->ops->port_bridge_leave(ds, info->port, info->br);
++
++ if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
++ ds->ops->crosschip_bridge_leave)
++ ds->ops->crosschip_bridge_leave(ds, info->tree_index,
++ info->sw_index, info->port,
++ info->br);
++
++ err = dsa_switch_sync_vlan_filtering(ds, info);
++ if (err)
++ return err;
++
+ return 0;
+ }
+
--- /dev/null
+From 6948a6654ffc878fc0258b363da77e7fd775b2d9 Mon Sep 17 00:00:00 2001
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Date: Mon, 24 Jan 2022 22:09:44 +0100
+Subject: [PATCH 5.10 2/2] net: dsa: Avoid cross-chip syncing of VLAN filtering
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+commit 108dc8741c203e9d6ce4e973367f1bac20c7192b upstream.
+
+Changes to VLAN filtering are not applicable to cross-chip
+notifications.
+
+On a system like this:
+
+.-----. .-----. .-----.
+| sw1 +---+ sw2 +---+ sw3 |
+'-1-2-' '-1-2-' '-1-2-'
+
+Before this change, upon sw1p1 leaving a bridge, a call to
+dsa_port_vlan_filtering would also be made to sw2p1 and sw3p1.
+
+In this scenario:
+
+.---------. .-----. .-----.
+| sw1 +---+ sw2 +---+ sw3 |
+'-1-2-3-4-' '-1-2-' '-1-2-'
+
+When sw1p4 would leave a bridge, dsa_port_vlan_filtering would be
+called for sw2 and sw3 with a non-existing port - leading to array
+out-of-bounds accesses and crashes on mv88e6xxx.
+
+Fixes: d371b7c92d19 ("net: dsa: Unset vlan_filtering when ports leave the bridge")
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ net/dsa/switch.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/net/dsa/switch.c
++++ b/net/dsa/switch.c
+@@ -162,9 +162,11 @@ static int dsa_switch_bridge_leave(struc
+ info->sw_index, info->port,
+ info->br);
+
+- err = dsa_switch_sync_vlan_filtering(ds, info);
+- if (err)
+- return err;
++ if (dst->index == info->tree_index && ds->index == info->sw_index) {
++ err = dsa_switch_sync_vlan_filtering(ds, info);
++ if (err)
++ return err;
++ }
+
+ return 0;
+ }
--- /dev/null
+From 7c496de538eebd8212dc2a3c9a468386b264d0d4 Mon Sep 17 00:00:00 2001
+From: Sasha Neftin <sasha.neftin@intel.com>
+Date: Wed, 7 Jul 2021 08:14:40 +0300
+Subject: igc: Remove _I_PHY_ID checking
+
+i225 devices have only one PHY vendor. There is no point checking
+_I_PHY_ID during the link establishment and auto-negotiation process.
+This patch comes to clean up these pointless checkings.
+
+Signed-off-by: Sasha Neftin <sasha.neftin@intel.com>
+Tested-by: Dvora Fuxbrumer <dvorax.fuxbrumer@linux.intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+---
+ drivers/net/ethernet/intel/igc/igc_base.c | 10 +---------
+ drivers/net/ethernet/intel/igc/igc_main.c | 3 +--
+ drivers/net/ethernet/intel/igc/igc_phy.c | 6 ++----
+ 3 files changed, 4 insertions(+), 15 deletions(-)
+
+(limited to 'drivers/net/ethernet/intel/igc')
+
+--- a/drivers/net/ethernet/intel/igc/igc_base.c
++++ b/drivers/net/ethernet/intel/igc/igc_base.c
+@@ -187,15 +187,7 @@ static s32 igc_init_phy_params_base(stru
+
+ igc_check_for_copper_link(hw);
+
+- /* Verify phy id and set remaining function pointers */
+- switch (phy->id) {
+- case I225_I_PHY_ID:
+- phy->type = igc_phy_i225;
+- break;
+- default:
+- ret_val = -IGC_ERR_PHY;
+- goto out;
+- }
++ phy->type = igc_phy_i225;
+
+ out:
+ return ret_val;
+--- a/drivers/net/ethernet/intel/igc/igc_main.c
++++ b/drivers/net/ethernet/intel/igc/igc_main.c
+@@ -4189,8 +4189,7 @@ bool igc_has_link(struct igc_adapter *ad
+ break;
+ }
+
+- if (hw->mac.type == igc_i225 &&
+- hw->phy.id == I225_I_PHY_ID) {
++ if (hw->mac.type == igc_i225) {
+ if (!netif_carrier_ok(adapter->netdev)) {
+ adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE;
+ } else if (!(adapter->flags & IGC_FLAG_NEED_LINK_UPDATE)) {
+--- a/drivers/net/ethernet/intel/igc/igc_phy.c
++++ b/drivers/net/ethernet/intel/igc/igc_phy.c
+@@ -249,8 +249,7 @@ static s32 igc_phy_setup_autoneg(struct
+ return ret_val;
+ }
+
+- if ((phy->autoneg_mask & ADVERTISE_2500_FULL) &&
+- hw->phy.id == I225_I_PHY_ID) {
++ if (phy->autoneg_mask & ADVERTISE_2500_FULL) {
+ /* Read the MULTI GBT AN Control Register - reg 7.32 */
+ ret_val = phy->ops.read_reg(hw, (STANDARD_AN_REG_MASK <<
+ MMD_DEVADDR_SHIFT) |
+@@ -390,8 +389,7 @@ static s32 igc_phy_setup_autoneg(struct
+ ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL,
+ mii_1000t_ctrl_reg);
+
+- if ((phy->autoneg_mask & ADVERTISE_2500_FULL) &&
+- hw->phy.id == I225_I_PHY_ID)
++ if (phy->autoneg_mask & ADVERTISE_2500_FULL)
+ ret_val = phy->ops.write_reg(hw,
+ (STANDARD_AN_REG_MASK <<
+ MMD_DEVADDR_SHIFT) |
--- /dev/null
+From 47bca7de6a4fb8dcb564c7ca14d885c91ed19e03 Mon Sep 17 00:00:00 2001
+From: Sasha Neftin <sasha.neftin@intel.com>
+Date: Sat, 10 Jul 2021 20:57:50 +0300
+Subject: igc: Remove phy->type checking
+
+i225 devices have only one phy->type: copper. There is no point checking
+phy->type during the igc_has_link method from the watchdog that
+invoked every 2 seconds.
+This patch comes to clean up these pointless checkings.
+
+Signed-off-by: Sasha Neftin <sasha.neftin@intel.com>
+Tested-by: Dvora Fuxbrumer <dvorax.fuxbrumer@linux.intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+---
+ drivers/net/ethernet/intel/igc/igc_main.c | 15 ++++-----------
+ 1 file changed, 4 insertions(+), 11 deletions(-)
+
+(limited to 'drivers/net/ethernet/intel/igc')
+
+--- a/drivers/net/ethernet/intel/igc/igc_main.c
++++ b/drivers/net/ethernet/intel/igc/igc_main.c
+@@ -4177,17 +4177,10 @@ bool igc_has_link(struct igc_adapter *ad
+ * false until the igc_check_for_link establishes link
+ * for copper adapters ONLY
+ */
+- switch (hw->phy.media_type) {
+- case igc_media_type_copper:
+- if (!hw->mac.get_link_status)
+- return true;
+- hw->mac.ops.check_for_link(hw);
+- link_active = !hw->mac.get_link_status;
+- break;
+- default:
+- case igc_media_type_unknown:
+- break;
+- }
++ if (!hw->mac.get_link_status)
++ return true;
++ hw->mac.ops.check_for_link(hw);
++ link_active = !hw->mac.get_link_status;
+
+ if (hw->mac.type == igc_i225) {
+ if (!netif_carrier_ok(adapter->netdev)) {
--- /dev/null
+From 586f04ce6a391419ca3cc9cef6b6f38570cede88 Mon Sep 17 00:00:00 2001
+From: Lee Jones <lee.jones@linaro.org>
+Date: Mon, 2 Nov 2020 11:45:04 +0000
+Subject: [PATCH] net: usb: r8152: Provide missing documentation for
+ some struct members
+
+commit 34e653efb602e0651867fb5ab14369b555a61dcd upstream.
+
+Fixes the following W=1 kernel build warning(s):
+
+ drivers/net/usb/r8152.c:934: warning: Function parameter or member 'blk_hdr' not described in 'fw_mac'
+ drivers/net/usb/r8152.c:934: warning: Function parameter or member 'reserved' not described in 'fw_mac'
+ drivers/net/usb/r8152.c:947: warning: Function parameter or member 'blk_hdr' not described in 'fw_phy_patch_key'
+ drivers/net/usb/r8152.c:947: warning: Function parameter or member 'reserved' not described in 'fw_phy_patch_key'
+ drivers/net/usb/r8152.c:986: warning: Function parameter or member 'blk_hdr' not described in 'fw_phy_nc'
+ drivers/net/usb/r8152.c:986: warning: Function parameter or member 'mode_pre' not described in 'fw_phy_nc'
+ drivers/net/usb/r8152.c:986: warning: Function parameter or member 'mode_post' not described in 'fw_phy_nc'
+ drivers/net/usb/r8152.c:986: warning: Function parameter or member 'reserved' not described in 'fw_phy_nc'
+
+Signed-off-by: Lee Jones <lee.jones@linaro.org>
+Acked-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20201102114512.1062724-23-lee.jones@linaro.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -899,6 +899,7 @@ struct fw_header {
+ * struct fw_mac - a firmware block used by RTL_FW_PLA and RTL_FW_USB.
+ * The layout of the firmware block is:
+ * <struct fw_mac> + <info> + <firmware data>.
++ * @blk_hdr: firmware descriptor (type, length)
+ * @fw_offset: offset of the firmware binary data. The start address of
+ * the data would be the address of struct fw_mac + @fw_offset.
+ * @fw_reg: the register to load the firmware. Depends on chip.
+@@ -912,6 +913,7 @@ struct fw_header {
+ * @bp_num: the break point number which needs to be set for this firmware.
+ * Depends on the firmware.
+ * @bp: break points. Depends on firmware.
++ * @reserved: reserved space (unused)
+ * @fw_ver_reg: the register to store the fw version.
+ * @fw_ver_data: the firmware version of the current type.
+ * @info: additional information for debugging, and is followed by the
+@@ -937,8 +939,10 @@ struct fw_mac {
+ /**
+ * struct fw_phy_patch_key - a firmware block used by RTL_FW_PHY_START.
+ * This is used to set patch key when loading the firmware of PHY.
++ * @blk_hdr: firmware descriptor (type, length)
+ * @key_reg: the register to write the patch key.
+ * @key_data: patch key.
++ * @reserved: reserved space (unused)
+ */
+ struct fw_phy_patch_key {
+ struct fw_block blk_hdr;
+@@ -951,6 +955,7 @@ struct fw_phy_patch_key {
+ * struct fw_phy_nc - a firmware block used by RTL_FW_PHY_NC.
+ * The layout of the firmware block is:
+ * <struct fw_phy_nc> + <info> + <firmware data>.
++ * @blk_hdr: firmware descriptor (type, length)
+ * @fw_offset: offset of the firmware binary data. The start address of
+ * the data would be the address of struct fw_phy_nc + @fw_offset.
+ * @fw_reg: the register to load the firmware. Depends on chip.
+@@ -961,6 +966,7 @@ struct fw_phy_patch_key {
+ * @mode_reg: the regitster of switching the mode.
+ * @mod_pre: the mode needing to be set before loading the firmware.
+ * @mod_post: the mode to be set when finishing to load the firmware.
++ * @reserved: reserved space (unused)
+ * @bp_start: the start register of break points. Depends on chip.
+ * @bp_num: the break point number which needs to be set for this firmware.
+ * Depends on the firmware.
--- /dev/null
+From 5fcfa846181de6676509696c4cd7b60a22e74077 Mon Sep 17 00:00:00 2001
+From: Lee Jones <lee.jones@linaro.org>
+Date: Mon, 2 Nov 2020 11:45:09 +0000
+Subject: [PATCH] net: usb: r8152: Fix a couple of spelling errors in
+ fw_phy_nc's docs
+
+commit 9f07814d01ad085b2d9f1d55b4ce532fb2c27110 upstream.
+
+Fixes the following W=1 kernel build warning(s):
+
+ drivers/net/usb/r8152.c:992: warning: Function parameter or member 'mode_pre' not described in 'fw_phy_nc'
+ drivers/net/usb/r8152.c:992: warning: Function parameter or member 'mode_post' not described in 'fw_phy_nc'
+
+Signed-off-by: Lee Jones <lee.jones@linaro.org>
+Acked-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20201102114512.1062724-28-lee.jones@linaro.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -964,8 +964,8 @@ struct fw_phy_patch_key {
+ * @patch_en_addr: the register of enabling patch mode. Depends on chip.
+ * @patch_en_value: patch mode enabled mask. Depends on the firmware.
+ * @mode_reg: the regitster of switching the mode.
+- * @mod_pre: the mode needing to be set before loading the firmware.
+- * @mod_post: the mode to be set when finishing to load the firmware.
++ * @mode_pre: the mode needing to be set before loading the firmware.
++ * @mode_post: the mode to be set when finishing to load the firmware.
+ * @reserved: reserved space (unused)
+ * @bp_start: the start register of break points. Depends on chip.
+ * @bp_num: the break point number which needs to be set for this firmware.
--- /dev/null
+From 0ef50460f7f053bd2a911ec53e01bfda646a5574 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Wed, 4 Nov 2020 10:19:22 +0800
+Subject: [PATCH] net/usb/r8153_ecm: support ECM mode for RTL8153
+
+commit c1aedf015ebdd0232757a66e2daccf1246bd609c upstream.
+
+Support ECM mode based on cdc_ether with relative mii functions,
+when CONFIG_USB_RTL8152 is not set, or the device is not supported
+by r8152 driver.
+
+Both r8152 and r8153_ecm would check the return value of
+rtl8152_get_version() in porbe(). If rtl8152_get_version()
+return none zero value, the r8152 is used for the device
+with vendor mode. Otherwise, the r8153_ecm is used for the
+device with ECM mode.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/1394712342-15778-392-Taiwan-albertk@realtek.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/Makefile | 2 +-
+ drivers/net/usb/r8152.c | 30 +------
+ drivers/net/usb/r8153_ecm.c | 162 ++++++++++++++++++++++++++++++++++++
+ include/linux/usb/r8152.h | 37 ++++++++
+ 4 files changed, 204 insertions(+), 27 deletions(-)
+ create mode 100644 drivers/net/usb/r8153_ecm.c
+ create mode 100644 include/linux/usb/r8152.h
+
+--- a/drivers/net/usb/Makefile
++++ b/drivers/net/usb/Makefile
+@@ -13,7 +13,7 @@ obj-$(CONFIG_USB_LAN78XX) += lan78xx.o
+ obj-$(CONFIG_USB_NET_AX8817X) += asix.o
+ asix-y := asix_devices.o asix_common.o ax88172a.o
+ obj-$(CONFIG_USB_NET_AX88179_178A) += ax88179_178a.o
+-obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o
++obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o r8153_ecm.o
+ obj-$(CONFIG_USB_NET_CDC_EEM) += cdc_eem.o
+ obj-$(CONFIG_USB_NET_DM9601) += dm9601.o
+ obj-$(CONFIG_USB_NET_SR9700) += sr9700.o
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -27,6 +27,7 @@
+ #include <linux/acpi.h>
+ #include <linux/firmware.h>
+ #include <crypto/hash.h>
++#include <linux/usb/r8152.h>
+
+ /* Information for net-next */
+ #define NETNEXT_VERSION "11"
+@@ -654,18 +655,6 @@ enum rtl_register_content {
+
+ #define INTR_LINK 0x0004
+
+-#define RTL8152_REQT_READ 0xc0
+-#define RTL8152_REQT_WRITE 0x40
+-#define RTL8152_REQ_GET_REGS 0x05
+-#define RTL8152_REQ_SET_REGS 0x05
+-
+-#define BYTE_EN_DWORD 0xff
+-#define BYTE_EN_WORD 0x33
+-#define BYTE_EN_BYTE 0x11
+-#define BYTE_EN_SIX_BYTES 0x3f
+-#define BYTE_EN_START_MASK 0x0f
+-#define BYTE_EN_END_MASK 0xf0
+-
+ #define RTL8153_MAX_PACKET 9216 /* 9K */
+ #define RTL8153_MAX_MTU (RTL8153_MAX_PACKET - VLAN_ETH_HLEN - \
+ ETH_FCS_LEN)
+@@ -690,21 +679,9 @@ enum rtl8152_flags {
+ LENOVO_MACPASSTHRU,
+ };
+
+-/* Define these values to match your device */
+-#define VENDOR_ID_REALTEK 0x0bda
+-#define VENDOR_ID_MICROSOFT 0x045e
+-#define VENDOR_ID_SAMSUNG 0x04e8
+-#define VENDOR_ID_LENOVO 0x17ef
+-#define VENDOR_ID_LINKSYS 0x13b1
+-#define VENDOR_ID_NVIDIA 0x0955
+-#define VENDOR_ID_TPLINK 0x2357
+-
+ #define DEVICE_ID_THINKPAD_THUNDERBOLT3_DOCK_GEN2 0x3082
+ #define DEVICE_ID_THINKPAD_USB_C_DOCK_GEN2 0xa387
+
+-#define MCU_TYPE_PLA 0x0100
+-#define MCU_TYPE_USB 0x0000
+-
+ struct tally_counter {
+ __le64 tx_packets;
+ __le64 rx_packets;
+@@ -6625,7 +6602,7 @@ static int rtl_fw_init(struct r8152 *tp)
+ return 0;
+ }
+
+-static u8 rtl_get_version(struct usb_interface *intf)
++u8 rtl8152_get_version(struct usb_interface *intf)
+ {
+ struct usb_device *udev = interface_to_usbdev(intf);
+ u32 ocp_data = 0;
+@@ -6683,12 +6660,13 @@ static u8 rtl_get_version(struct usb_int
+
+ return version;
+ }
++EXPORT_SYMBOL_GPL(rtl8152_get_version);
+
+ static int rtl8152_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+ {
+ struct usb_device *udev = interface_to_usbdev(intf);
+- u8 version = rtl_get_version(intf);
++ u8 version = rtl8152_get_version(intf);
+ struct r8152 *tp;
+ struct net_device *netdev;
+ int ret;
+--- /dev/null
++++ b/drivers/net/usb/r8153_ecm.c
+@@ -0,0 +1,162 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++#include <linux/module.h>
++#include <linux/netdevice.h>
++#include <linux/mii.h>
++#include <linux/usb.h>
++#include <linux/usb/cdc.h>
++#include <linux/usb/usbnet.h>
++#include <linux/usb/r8152.h>
++
++#define OCP_BASE 0xe86c
++
++static int pla_read_word(struct usbnet *dev, u16 index)
++{
++ u16 byen = BYTE_EN_WORD;
++ u8 shift = index & 2;
++ __le32 tmp;
++ int ret;
++
++ if (shift)
++ byen <<= shift;
++
++ index &= ~3;
++
++ ret = usbnet_read_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, index,
++ MCU_TYPE_PLA | byen, &tmp, sizeof(tmp));
++ if (ret < 0)
++ goto out;
++
++ ret = __le32_to_cpu(tmp);
++ ret >>= (shift * 8);
++ ret &= 0xffff;
++
++out:
++ return ret;
++}
++
++static int pla_write_word(struct usbnet *dev, u16 index, u32 data)
++{
++ u32 mask = 0xffff;
++ u16 byen = BYTE_EN_WORD;
++ u8 shift = index & 2;
++ __le32 tmp;
++ int ret;
++
++ data &= mask;
++
++ if (shift) {
++ byen <<= shift;
++ mask <<= (shift * 8);
++ data <<= (shift * 8);
++ }
++
++ index &= ~3;
++
++ ret = usbnet_read_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, index,
++ MCU_TYPE_PLA | byen, &tmp, sizeof(tmp));
++
++ if (ret < 0)
++ goto out;
++
++ data |= __le32_to_cpu(tmp) & ~mask;
++ tmp = __cpu_to_le32(data);
++
++ ret = usbnet_write_cmd(dev, RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, index,
++ MCU_TYPE_PLA | byen, &tmp, sizeof(tmp));
++
++out:
++ return ret;
++}
++
++static int r8153_ecm_mdio_read(struct net_device *netdev, int phy_id, int reg)
++{
++ struct usbnet *dev = netdev_priv(netdev);
++ int ret;
++
++ ret = pla_write_word(dev, OCP_BASE, 0xa000);
++ if (ret < 0)
++ goto out;
++
++ ret = pla_read_word(dev, 0xb400 + reg * 2);
++
++out:
++ return ret;
++}
++
++static void r8153_ecm_mdio_write(struct net_device *netdev, int phy_id, int reg, int val)
++{
++ struct usbnet *dev = netdev_priv(netdev);
++ int ret;
++
++ ret = pla_write_word(dev, OCP_BASE, 0xa000);
++ if (ret < 0)
++ return;
++
++ ret = pla_write_word(dev, 0xb400 + reg * 2, val);
++}
++
++static int r8153_bind(struct usbnet *dev, struct usb_interface *intf)
++{
++ int status;
++
++ status = usbnet_cdc_bind(dev, intf);
++ if (status < 0)
++ return status;
++
++ dev->mii.dev = dev->net;
++ dev->mii.mdio_read = r8153_ecm_mdio_read;
++ dev->mii.mdio_write = r8153_ecm_mdio_write;
++ dev->mii.reg_num_mask = 0x1f;
++ dev->mii.supports_gmii = 1;
++
++ return status;
++}
++
++static const struct driver_info r8153_info = {
++ .description = "RTL8153 ECM Device",
++ .flags = FLAG_ETHER,
++ .bind = r8153_bind,
++ .unbind = usbnet_cdc_unbind,
++ .status = usbnet_cdc_status,
++ .manage_power = usbnet_manage_power,
++};
++
++static const struct usb_device_id products[] = {
++{
++ USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID_REALTEK, 0x8153, USB_CLASS_COMM,
++ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
++ .driver_info = (unsigned long)&r8153_info,
++},
++
++ { }, /* END */
++};
++MODULE_DEVICE_TABLE(usb, products);
++
++static int rtl8153_ecm_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++#if IS_REACHABLE(CONFIG_USB_RTL8152)
++ if (rtl8152_get_version(intf))
++ return -ENODEV;
++#endif
++
++ return usbnet_probe(intf, id);
++}
++
++static struct usb_driver r8153_ecm_driver = {
++ .name = "r8153_ecm",
++ .id_table = products,
++ .probe = rtl8153_ecm_probe,
++ .disconnect = usbnet_disconnect,
++ .suspend = usbnet_suspend,
++ .resume = usbnet_resume,
++ .reset_resume = usbnet_resume,
++ .supports_autosuspend = 1,
++ .disable_hub_initiated_lpm = 1,
++};
++
++module_usb_driver(r8153_ecm_driver);
++
++MODULE_AUTHOR("Hayes Wang");
++MODULE_DESCRIPTION("Realtek USB ECM device");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/include/linux/usb/r8152.h
+@@ -0,0 +1,37 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (c) 2020 Realtek Semiconductor Corp. All rights reserved.
++ */
++
++#ifndef __LINUX_R8152_H
++#define __LINUX_R8152_H
++
++#define RTL8152_REQT_READ 0xc0
++#define RTL8152_REQT_WRITE 0x40
++#define RTL8152_REQ_GET_REGS 0x05
++#define RTL8152_REQ_SET_REGS 0x05
++
++#define BYTE_EN_DWORD 0xff
++#define BYTE_EN_WORD 0x33
++#define BYTE_EN_BYTE 0x11
++#define BYTE_EN_SIX_BYTES 0x3f
++#define BYTE_EN_START_MASK 0x0f
++#define BYTE_EN_END_MASK 0xf0
++
++#define MCU_TYPE_PLA 0x0100
++#define MCU_TYPE_USB 0x0000
++
++/* Define these values to match your device */
++#define VENDOR_ID_REALTEK 0x0bda
++#define VENDOR_ID_MICROSOFT 0x045e
++#define VENDOR_ID_SAMSUNG 0x04e8
++#define VENDOR_ID_LENOVO 0x17ef
++#define VENDOR_ID_LINKSYS 0x13b1
++#define VENDOR_ID_NVIDIA 0x0955
++#define VENDOR_ID_TPLINK 0x2357
++
++#if IS_REACHABLE(CONFIG_USB_RTL8152)
++extern u8 rtl8152_get_version(struct usb_interface *intf);
++#endif
++
++#endif /* __LINUX_R8152_H */
--- /dev/null
+From 90f1afc7f96c8f7cf19c82e5f4b39e61a63b053d Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sun, 31 Jan 2021 00:47:29 +0100
+Subject: [PATCH] net: usb: r8152: use new tasklet API
+
+commit f3163f1cb87141c7a41a15a5d4c98b353f807b04 upstream.
+
+This converts the driver to use the new tasklet API introduced in
+commit 12cc923f1ccc ("tasklet: Introduce new initialization API")
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -2394,11 +2394,9 @@ static void tx_bottom(struct r8152 *tp)
+ } while (res == 0);
+ }
+
+-static void bottom_half(unsigned long data)
++static void bottom_half(struct tasklet_struct *t)
+ {
+- struct r8152 *tp;
+-
+- tp = (struct r8152 *)data;
++ struct r8152 *tp = from_tasklet(tp, t, tx_tl);
+
+ if (test_bit(RTL8152_UNPLUG, &tp->flags))
+ return;
+@@ -6718,7 +6716,7 @@ static int rtl8152_probe(struct usb_inte
+ mutex_init(&tp->control);
+ INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t);
+ INIT_DELAYED_WORK(&tp->hw_phy_work, rtl_hw_phy_work_func_t);
+- tasklet_init(&tp->tx_tl, bottom_half, (unsigned long)tp);
++ tasklet_setup(&tp->tx_tl, bottom_half);
+ tasklet_disable(&tp->tx_tl);
+
+ netdev->netdev_ops = &rtl8152_netdev_ops;
--- /dev/null
+From 86b98abf4f8c691c260c5113d6a2d32f5377caca Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Wed, 3 Feb 2021 17:14:28 +0800
+Subject: [PATCH] r8152: replace several functions about phy patch
+ request
+
+commit a08c0d309d8c078d22717d815cf9853f6f2c07bd upstream.
+
+Replace r8153_patch_request() with rtl_phy_patch_request().
+Replace r8153_pre_ram_code() with rtl_pre_ram_code().
+Replace r8153_post_ram_code() with rtl_post_ram_code().
+Add rtl_patch_key_set().
+
+The new functions have an additional parameter. It is used to wait
+the patch request command finished. When the PHY is resumed from
+the state of power cut, the PHY is at a safe mode and the
+OCP_PHY_PATCH_STAT wouldn't be updated. For this situation, it is
+safe to set patch request command without waiting OCP_PHY_PATCH_STAT.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 84 ++++++++++++++++++++++++-----------------
+ 1 file changed, 50 insertions(+), 34 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -3444,59 +3444,76 @@ static void rtl_clear_bp(struct r8152 *t
+ ocp_write_word(tp, type, PLA_BP_BA, 0);
+ }
+
+-static int r8153_patch_request(struct r8152 *tp, bool request)
++static int rtl_phy_patch_request(struct r8152 *tp, bool request, bool wait)
+ {
+- u16 data;
++ u16 data, check;
+ int i;
+
+ data = ocp_reg_read(tp, OCP_PHY_PATCH_CMD);
+- if (request)
++ if (request) {
+ data |= PATCH_REQUEST;
+- else
++ check = 0;
++ } else {
+ data &= ~PATCH_REQUEST;
++ check = PATCH_READY;
++ }
+ ocp_reg_write(tp, OCP_PHY_PATCH_CMD, data);
+
+- for (i = 0; request && i < 5000; i++) {
++ for (i = 0; wait && i < 5000; i++) {
++ u32 ocp_data;
++
+ usleep_range(1000, 2000);
+- if (ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)
++ ocp_data = ocp_reg_read(tp, OCP_PHY_PATCH_STAT);
++ if ((ocp_data & PATCH_READY) ^ check)
+ break;
+ }
+
+- if (request && !(ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)) {
+- netif_err(tp, drv, tp->netdev, "patch request fail\n");
+- r8153_patch_request(tp, false);
++ if (request && wait &&
++ !(ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)) {
++ dev_err(&tp->intf->dev, "PHY patch request fail\n");
++ rtl_phy_patch_request(tp, false, false);
+ return -ETIME;
+ } else {
+ return 0;
+ }
+ }
+
+-static int r8153_pre_ram_code(struct r8152 *tp, u16 key_addr, u16 patch_key)
++static void rtl_patch_key_set(struct r8152 *tp, u16 key_addr, u16 patch_key)
+ {
+- if (r8153_patch_request(tp, true)) {
+- dev_err(&tp->intf->dev, "patch request fail\n");
+- return -ETIME;
+- }
++ if (patch_key && key_addr) {
++ sram_write(tp, key_addr, patch_key);
++ sram_write(tp, SRAM_PHY_LOCK, PHY_PATCH_LOCK);
++ } else if (key_addr) {
++ u16 data;
+
+- sram_write(tp, key_addr, patch_key);
+- sram_write(tp, SRAM_PHY_LOCK, PHY_PATCH_LOCK);
++ sram_write(tp, 0x0000, 0x0000);
+
+- return 0;
++ data = ocp_reg_read(tp, OCP_PHY_LOCK);
++ data &= ~PATCH_LOCK;
++ ocp_reg_write(tp, OCP_PHY_LOCK, data);
++
++ sram_write(tp, key_addr, 0x0000);
++ } else {
++ WARN_ON_ONCE(1);
++ }
+ }
+
+-static int r8153_post_ram_code(struct r8152 *tp, u16 key_addr)
++static int
++rtl_pre_ram_code(struct r8152 *tp, u16 key_addr, u16 patch_key, bool wait)
+ {
+- u16 data;
++ if (rtl_phy_patch_request(tp, true, wait))
++ return -ETIME;
+
+- sram_write(tp, 0x0000, 0x0000);
++ rtl_patch_key_set(tp, key_addr, patch_key);
+
+- data = ocp_reg_read(tp, OCP_PHY_LOCK);
+- data &= ~PATCH_LOCK;
+- ocp_reg_write(tp, OCP_PHY_LOCK, data);
++ return 0;
++}
+
+- sram_write(tp, key_addr, 0x0000);
++static int rtl_post_ram_code(struct r8152 *tp, u16 key_addr, bool wait)
++{
++ rtl_patch_key_set(tp, key_addr, 0);
+
+- r8153_patch_request(tp, false);
++ rtl_phy_patch_request(tp, false, wait);
+
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, tp->ocp_base);
+
+@@ -3981,7 +3998,7 @@ static void rtl8152_fw_mac_apply(struct
+ dev_dbg(&tp->intf->dev, "successfully applied %s\n", mac->info);
+ }
+
+-static void rtl8152_apply_firmware(struct r8152 *tp)
++static void rtl8152_apply_firmware(struct r8152 *tp, bool power_cut)
+ {
+ struct rtl_fw *rtl_fw = &tp->rtl_fw;
+ const struct firmware *fw;
+@@ -4012,12 +4029,11 @@ static void rtl8152_apply_firmware(struc
+ case RTL_FW_PHY_START:
+ key = (struct fw_phy_patch_key *)block;
+ key_addr = __le16_to_cpu(key->key_reg);
+- r8153_pre_ram_code(tp, key_addr,
+- __le16_to_cpu(key->key_data));
++ rtl_pre_ram_code(tp, key_addr, __le16_to_cpu(key->key_data), !power_cut);
+ break;
+ case RTL_FW_PHY_STOP:
+ WARN_ON(!key_addr);
+- r8153_post_ram_code(tp, key_addr);
++ rtl_post_ram_code(tp, key_addr, !power_cut);
+ break;
+ case RTL_FW_PHY_NC:
+ rtl8152_fw_phy_nc_apply(tp, (struct fw_phy_nc *)block);
+@@ -4222,7 +4238,7 @@ static void rtl8152_disable(struct r8152
+
+ static void r8152b_hw_phy_cfg(struct r8152 *tp)
+ {
+- rtl8152_apply_firmware(tp);
++ rtl8152_apply_firmware(tp, false);
+ rtl_eee_enable(tp, tp->eee_en);
+ r8152_aldps_en(tp, true);
+ r8152b_enable_fc(tp);
+@@ -4504,7 +4520,7 @@ static void r8153_hw_phy_cfg(struct r815
+ /* disable EEE before updating the PHY parameters */
+ rtl_eee_enable(tp, false);
+
+- rtl8152_apply_firmware(tp);
++ rtl8152_apply_firmware(tp, false);
+
+ if (tp->version == RTL_VER_03) {
+ data = ocp_reg_read(tp, OCP_EEE_CFG);
+@@ -4578,7 +4594,7 @@ static void r8153b_hw_phy_cfg(struct r81
+ /* disable EEE before updating the PHY parameters */
+ rtl_eee_enable(tp, false);
+
+- rtl8152_apply_firmware(tp);
++ rtl8152_apply_firmware(tp, false);
+
+ r8153b_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
+
+@@ -4619,7 +4635,7 @@ static void r8153b_hw_phy_cfg(struct r81
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data);
+
+ /* Advnace EEE */
+- if (!r8153_patch_request(tp, true)) {
++ if (!rtl_phy_patch_request(tp, true, true)) {
+ data = ocp_reg_read(tp, OCP_POWER_CFG);
+ data |= EEE_CLKDIV_EN;
+ ocp_reg_write(tp, OCP_POWER_CFG, data);
+@@ -4636,7 +4652,7 @@ static void r8153b_hw_phy_cfg(struct r81
+ ocp_reg_write(tp, OCP_SYSCLK_CFG, clk_div_expo(5));
+ tp->ups_info._250m_ckdiv = true;
+
+- r8153_patch_request(tp, false);
++ rtl_phy_patch_request(tp, false, true);
+ }
+
+ if (tp->eee_en)
--- /dev/null
+From 29a61d8564ad3439d03c7ec135016a4e70072af1 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Wed, 3 Feb 2021 17:14:29 +0800
+Subject: [PATCH] r8152: adjust the flow of power cut for RTL8153B
+
+commit 80fd850b31f09263ad175b2f640d5c5c6f76ed41 upstream.
+
+For runtime resuming, the RTL8153B may be resumed from the state
+of power cut, when enabling the feature of UPS. Then, the PHY
+would be reset, so it is necessary to be initailized again.
+
+Besides, the USB_U1U2_TIMER also has to be set again, so I move
+it from r8153b_init() to r8153b_hw_phy_cfg().
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 68 ++++++++++++++++++++++++-----------------
+ 1 file changed, 40 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -1372,6 +1372,10 @@ void write_mii_word(struct net_device *n
+ static int
+ r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags);
+
++static int
++rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
++ u32 advertising);
++
+ static int rtl8152_set_mac_address(struct net_device *netdev, void *p)
+ {
+ struct r8152 *tp = netdev_priv(netdev);
+@@ -3183,8 +3187,6 @@ static void r8153b_ups_en(struct r8152 *
+ ocp_data |= BIT(0);
+ ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data);
+ } else {
+- u16 data;
+-
+ ocp_data &= ~(UPS_EN | USP_PREWAKE);
+ ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
+
+@@ -3192,31 +3194,20 @@ static void r8153b_ups_en(struct r8152 *
+ ocp_data &= ~BIT(0);
+ ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data);
+
+- ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
+- ocp_data &= ~PCUT_STATUS;
+- ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
++ if (ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0) & PCUT_STATUS) {
++ int i;
+
+- data = r8153_phy_status(tp, 0);
++ for (i = 0; i < 500; i++) {
++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
++ AUTOLOAD_DONE)
++ break;
++ msleep(20);
++ }
+
+- switch (data) {
+- case PHY_STAT_PWRDN:
+- case PHY_STAT_EXT_INIT:
+- r8153b_green_en(tp,
+- test_bit(GREEN_ETHERNET, &tp->flags));
+-
+- data = r8152_mdio_read(tp, MII_BMCR);
+- data &= ~BMCR_PDOWN;
+- data |= BMCR_RESET;
+- r8152_mdio_write(tp, MII_BMCR, data);
++ tp->rtl_ops.hw_phy_cfg(tp);
+
+- data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
+- fallthrough;
+-
+- default:
+- if (data != PHY_STAT_LAN_ON)
+- netif_warn(tp, link, tp->netdev,
+- "PHY not ready");
+- break;
++ rtl8152_set_speed(tp, tp->autoneg, tp->speed,
++ tp->duplex, tp->advertising);
+ }
+ }
+ }
+@@ -4588,13 +4579,37 @@ static void r8153b_hw_phy_cfg(struct r81
+ u32 ocp_data;
+ u16 data;
+
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
++ if (ocp_data & PCUT_STATUS) {
++ ocp_data &= ~PCUT_STATUS;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
++ }
++
+ /* disable ALDPS before updating the PHY parameters */
+ r8153_aldps_en(tp, false);
+
+ /* disable EEE before updating the PHY parameters */
+ rtl_eee_enable(tp, false);
+
+- rtl8152_apply_firmware(tp, false);
++ /* U1/U2/L1 idle timer. 500 us */
++ ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
++
++ data = r8153_phy_status(tp, 0);
++
++ switch (data) {
++ case PHY_STAT_PWRDN:
++ case PHY_STAT_EXT_INIT:
++ rtl8152_apply_firmware(tp, true);
++
++ data = r8152_mdio_read(tp, MII_BMCR);
++ data &= ~BMCR_PDOWN;
++ r8152_mdio_write(tp, MII_BMCR, data);
++ break;
++ case PHY_STAT_LAN_ON:
++ default:
++ rtl8152_apply_firmware(tp, false);
++ break;
++ }
+
+ r8153b_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
+
+@@ -5543,9 +5558,6 @@ static void r8153b_init(struct r8152 *tp
+ /* MSC timer = 0xfff * 8ms = 32760 ms */
+ ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
+
+- /* U1/U2/L1 idle timer. 500 us */
+- ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
+-
+ r8153b_power_cut_en(tp, false);
+ r8153b_ups_en(tp, false);
+ r8153_queue_wake(tp, false);
--- /dev/null
+From 69b4339c0b9f3edc6a8f681f05efaaf4add1bb0e Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Fri, 19 Feb 2021 17:04:40 +0800
+Subject: [PATCH] r8152: enable U1/U2 for USB_SPEED_SUPER
+
+commit 7a0ae61acde2cebd69665837170405eced86a6c7 upstream.
+
+U1/U2 shoued be enabled for USB 3.0 or later. The USB 2.0 doesn't
+support it.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -3336,7 +3336,7 @@ static void rtl8153b_runtime_enable(stru
+ r8153b_ups_en(tp, false);
+ r8153_queue_wake(tp, false);
+ rtl_runtime_suspend_enable(tp, false);
+- if (tp->udev->speed != USB_SPEED_HIGH)
++ if (tp->udev->speed >= USB_SPEED_SUPER)
+ r8153b_u1u2en(tp, true);
+ }
+ }
+@@ -5029,7 +5029,7 @@ static void rtl8153b_up(struct r8152 *tp
+
+ r8153_aldps_en(tp, true);
+
+- if (tp->udev->speed != USB_SPEED_HIGH)
++ if (tp->udev->speed >= USB_SPEED_SUPER)
+ r8153b_u1u2en(tp, true);
+ }
+
+@@ -5571,8 +5571,9 @@ static void r8153b_init(struct r8152 *tp
+ ocp_data |= POLL_LINK_CHG;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data);
+
+- if (tp->udev->speed != USB_SPEED_HIGH)
++ if (tp->udev->speed >= USB_SPEED_SUPER)
+ r8153b_u1u2en(tp, true);
++
+ usb_enable_lpm(tp->udev);
+
+ /* MAC clock speed down */
--- /dev/null
+From e78b75f5be204a0a235da995d01c778dc282bb42 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Fri, 19 Feb 2021 17:04:41 +0800
+Subject: [PATCH] r8152: check if the pointer of the function exists
+
+commit c79515e47935c747282c6ed2ee5b2ef039756eeb upstream.
+
+Return error code if autosuspend_en, eee_get, or eee_set don't exist.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -5758,6 +5758,9 @@ static int rtl8152_runtime_suspend(struc
+ struct net_device *netdev = tp->netdev;
+ int ret = 0;
+
++ if (!tp->rtl_ops.autosuspend_en)
++ return -EBUSY;
++
+ set_bit(SELECTIVE_SUSPEND, &tp->flags);
+ smp_mb__after_atomic();
+
+@@ -6157,6 +6160,11 @@ rtl_ethtool_get_eee(struct net_device *n
+ struct r8152 *tp = netdev_priv(net);
+ int ret;
+
++ if (!tp->rtl_ops.eee_get) {
++ ret = -EOPNOTSUPP;
++ goto out;
++ }
++
+ ret = usb_autopm_get_interface(tp->intf);
+ if (ret < 0)
+ goto out;
+@@ -6179,6 +6187,11 @@ rtl_ethtool_set_eee(struct net_device *n
+ struct r8152 *tp = netdev_priv(net);
+ int ret;
+
++ if (!tp->rtl_ops.eee_set) {
++ ret = -EOPNOTSUPP;
++ goto out;
++ }
++
+ ret = usb_autopm_get_interface(tp->intf);
+ if (ret < 0)
+ goto out;
--- /dev/null
+From 38e44c7926512cff0b2809dc329de2a8e769e523 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Fri, 19 Feb 2021 17:04:42 +0800
+Subject: [PATCH] r8152: replace netif_err with dev_err
+
+commit 156c3207611262266f0eea589ac3f00c5657320e upstream.
+
+Some messages are before calling register_netdev(), so replace
+netif_err() with dev_err().
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -6594,7 +6594,7 @@ static int rtl_ops_init(struct r8152 *tp
+
+ default:
+ ret = -ENODEV;
+- netif_err(tp, probe, tp->netdev, "Unknown Device\n");
++ dev_err(&tp->intf->dev, "Unknown Device\n");
+ break;
+ }
+
+@@ -6851,7 +6851,7 @@ static int rtl8152_probe(struct usb_inte
+
+ ret = register_netdev(netdev);
+ if (ret != 0) {
+- netif_err(tp, probe, netdev, "couldn't register the device\n");
++ dev_err(&intf->dev, "couldn't register the device\n");
+ goto out1;
+ }
+
--- /dev/null
+From 260814de2d6cb958767785ffcb2e76915d1be32b Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Fri, 19 Feb 2021 17:04:43 +0800
+Subject: [PATCH] r8152: spilt rtl_set_eee_plus and r8153b_green_en
+
+commit 40fa7568ac230446d888b7ad402cff9e20fe3ad5 upstream.
+
+Add rtl_eee_plus_en() and rtl_green_en().
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 43 ++++++++++++++++++++++++++---------------
+ 1 file changed, 27 insertions(+), 16 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -2633,21 +2633,24 @@ static inline u8 rtl8152_get_speed(struc
+ return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS);
+ }
+
+-static void rtl_set_eee_plus(struct r8152 *tp)
++static void rtl_eee_plus_en(struct r8152 *tp, bool enable)
+ {
+ u32 ocp_data;
+- u8 speed;
+
+- speed = rtl8152_get_speed(tp);
+- if (speed & _10bps) {
+- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR);
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR);
++ if (enable)
+ ocp_data |= EEEP_CR_EEEP_TX;
+- ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data);
+- } else {
+- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR);
++ else
+ ocp_data &= ~EEEP_CR_EEEP_TX;
+- ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data);
+- }
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data);
++}
++
++static void rtl_set_eee_plus(struct r8152 *tp)
++{
++ if (rtl8152_get_speed(tp) & _10bps)
++ rtl_eee_plus_en(tp, true);
++ else
++ rtl_eee_plus_en(tp, false);
+ }
+
+ static void rxdy_gated_en(struct r8152 *tp, bool enable)
+@@ -3128,10 +3131,22 @@ static void r8153b_ups_flags(struct r815
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_UPS_FLAGS, ups_flags);
+ }
+
+-static void r8153b_green_en(struct r8152 *tp, bool enable)
++static void rtl_green_en(struct r8152 *tp, bool enable)
+ {
+ u16 data;
+
++ data = sram_read(tp, SRAM_GREEN_CFG);
++ if (enable)
++ data |= GREEN_ETH_EN;
++ else
++ data &= ~GREEN_ETH_EN;
++ sram_write(tp, SRAM_GREEN_CFG, data);
++
++ tp->ups_info.green = enable;
++}
++
++static void r8153b_green_en(struct r8152 *tp, bool enable)
++{
+ if (enable) {
+ sram_write(tp, 0x8045, 0); /* 10M abiq&ldvbias */
+ sram_write(tp, 0x804d, 0x1222); /* 100M short abiq&ldvbias */
+@@ -3142,11 +3157,7 @@ static void r8153b_green_en(struct r8152
+ sram_write(tp, 0x805d, 0x2444); /* 1000M short abiq&ldvbias */
+ }
+
+- data = sram_read(tp, SRAM_GREEN_CFG);
+- data |= GREEN_ETH_EN;
+- sram_write(tp, SRAM_GREEN_CFG, data);
+-
+- tp->ups_info.green = enable;
++ rtl_green_en(tp, true);
+ }
+
+ static u16 r8153_phy_status(struct r8152 *tp, u16 desired)
--- /dev/null
+From f1bbbb260a8016373adf239c716d2da90e6ced0b Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Fri, 16 Apr 2021 16:04:32 +0800
+Subject: [PATCH] r8152: set inter fram gap time depending on speed
+
+commit 5133bcc7481528e36fff0a3b056601efb704fb32 upstream.
+
+Set the maximum inter frame gap time (144ns) for speed 10M/half and
+100M/half. It improves the performance for those speeds. And, there
+is no effect for the other speeds.
+
+For 10M/half and 100M/half, the fast inter frame gap time let the
+device couldn't use the feature of the aggregation effectively,
+because the transfer would be completed fastly. Therefore, use the
+maximum value to improve the effect of the aggregation. However, you
+may not feel the improvement for fast CPUs, because they compensate
+for the effect of the aggregation.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -250,6 +250,9 @@
+
+ /* PLA_TCR1 */
+ #define VERSION_MASK 0x7cf0
++#define IFG_MASK (BIT(3) | BIT(9) | BIT(8))
++#define IFG_144NS BIT(9)
++#define IFG_96NS (BIT(9) | BIT(8))
+
+ /* PLA_MTPS */
+ #define MTPS_JUMBO (12 * 1024 / 64)
+@@ -2748,6 +2751,29 @@ static int rtl_stop_rx(struct r8152 *tp)
+ return 0;
+ }
+
++static void rtl_set_ifg(struct r8152 *tp, u16 speed)
++{
++ u32 ocp_data;
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1);
++ ocp_data &= ~IFG_MASK;
++ if ((speed & (_10bps | _100bps)) && !(speed & FULL_DUP)) {
++ ocp_data |= IFG_144NS;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR1, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
++ ocp_data &= ~TX10MIDLE_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
++ } else {
++ ocp_data |= IFG_96NS;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR1, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
++ ocp_data |= TX10MIDLE_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
++ }
++}
++
+ static inline void r8153b_rx_agg_chg_indicate(struct r8152 *tp)
+ {
+ ocp_write_byte(tp, MCU_TYPE_USB, USB_UPT_RXDMA_OWN,
+@@ -2851,6 +2877,8 @@ static int rtl8153_enable(struct r8152 *
+ r8153_set_rx_early_timeout(tp);
+ r8153_set_rx_early_size(tp);
+
++ rtl_set_ifg(tp, rtl8152_get_speed(tp));
++
+ if (tp->version == RTL_VER_09) {
+ u32 ocp_data;
+
--- /dev/null
+From f10c9edf47d3fa240d965e151a48c670f5035b73 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Fri, 16 Apr 2021 16:04:33 +0800
+Subject: [PATCH] r8152: adjust rtl8152_check_firmware function
+
+commit a8a7be178e81a3d4b6972cbeb0ccd091ca2f9f89 upstream.
+
+Use bits operations to record and check the firmware.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 51 +++++++++++++++++++++++------------------
+ 1 file changed, 29 insertions(+), 22 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -875,6 +875,14 @@ struct fw_header {
+ struct fw_block blocks[];
+ } __packed;
+
++enum rtl8152_fw_flags {
++ FW_FLAGS_USB = 0,
++ FW_FLAGS_PLA,
++ FW_FLAGS_START,
++ FW_FLAGS_STOP,
++ FW_FLAGS_NC,
++};
++
+ /**
+ * struct fw_mac - a firmware block used by RTL_FW_PLA and RTL_FW_USB.
+ * The layout of the firmware block is:
+@@ -3801,10 +3809,7 @@ static long rtl8152_check_firmware(struc
+ {
+ const struct firmware *fw = rtl_fw->fw;
+ struct fw_header *fw_hdr = (struct fw_header *)fw->data;
+- struct fw_mac *pla = NULL, *usb = NULL;
+- struct fw_phy_patch_key *start = NULL;
+- struct fw_phy_nc *phy_nc = NULL;
+- struct fw_block *stop = NULL;
++ unsigned long fw_flags = 0;
+ long ret = -EFAULT;
+ int i;
+
+@@ -3833,50 +3838,52 @@ static long rtl8152_check_firmware(struc
+ goto fail;
+ goto fw_end;
+ case RTL_FW_PLA:
+- if (pla) {
++ if (test_bit(FW_FLAGS_PLA, &fw_flags)) {
+ dev_err(&tp->intf->dev,
+ "multiple PLA firmware encountered");
+ goto fail;
+ }
+
+- pla = (struct fw_mac *)block;
+- if (!rtl8152_is_fw_mac_ok(tp, pla)) {
++ if (!rtl8152_is_fw_mac_ok(tp, (struct fw_mac *)block)) {
+ dev_err(&tp->intf->dev,
+ "check PLA firmware failed\n");
+ goto fail;
+ }
++ __set_bit(FW_FLAGS_PLA, &fw_flags);
+ break;
+ case RTL_FW_USB:
+- if (usb) {
++ if (test_bit(FW_FLAGS_USB, &fw_flags)) {
+ dev_err(&tp->intf->dev,
+ "multiple USB firmware encountered");
+ goto fail;
+ }
+
+- usb = (struct fw_mac *)block;
+- if (!rtl8152_is_fw_mac_ok(tp, usb)) {
++ if (!rtl8152_is_fw_mac_ok(tp, (struct fw_mac *)block)) {
+ dev_err(&tp->intf->dev,
+ "check USB firmware failed\n");
+ goto fail;
+ }
++ __set_bit(FW_FLAGS_USB, &fw_flags);
+ break;
+ case RTL_FW_PHY_START:
+- if (start || phy_nc || stop) {
++ if (test_bit(FW_FLAGS_START, &fw_flags) ||
++ test_bit(FW_FLAGS_NC, &fw_flags) ||
++ test_bit(FW_FLAGS_STOP, &fw_flags)) {
+ dev_err(&tp->intf->dev,
+ "check PHY_START fail\n");
+ goto fail;
+ }
+
+- if (__le32_to_cpu(block->length) != sizeof(*start)) {
++ if (__le32_to_cpu(block->length) != sizeof(struct fw_phy_patch_key)) {
+ dev_err(&tp->intf->dev,
+ "Invalid length for PHY_START\n");
+ goto fail;
+ }
+-
+- start = (struct fw_phy_patch_key *)block;
++ __set_bit(FW_FLAGS_START, &fw_flags);
+ break;
+ case RTL_FW_PHY_STOP:
+- if (stop || !start) {
++ if (test_bit(FW_FLAGS_STOP, &fw_flags) ||
++ !test_bit(FW_FLAGS_START, &fw_flags)) {
+ dev_err(&tp->intf->dev,
+ "Check PHY_STOP fail\n");
+ goto fail;
+@@ -3887,28 +3894,28 @@ static long rtl8152_check_firmware(struc
+ "Invalid length for PHY_STOP\n");
+ goto fail;
+ }
+-
+- stop = block;
++ __set_bit(FW_FLAGS_STOP, &fw_flags);
+ break;
+ case RTL_FW_PHY_NC:
+- if (!start || stop) {
++ if (!test_bit(FW_FLAGS_START, &fw_flags) ||
++ test_bit(FW_FLAGS_STOP, &fw_flags)) {
+ dev_err(&tp->intf->dev,
+ "check PHY_NC fail\n");
+ goto fail;
+ }
+
+- if (phy_nc) {
++ if (test_bit(FW_FLAGS_NC, &fw_flags)) {
+ dev_err(&tp->intf->dev,
+ "multiple PHY NC encountered\n");
+ goto fail;
+ }
+
+- phy_nc = (struct fw_phy_nc *)block;
+- if (!rtl8152_is_fw_phy_nc_ok(tp, phy_nc)) {
++ if (!rtl8152_is_fw_phy_nc_ok(tp, (struct fw_phy_nc *)block)) {
+ dev_err(&tp->intf->dev,
+ "check PHY NC firmware failed\n");
+ goto fail;
+ }
++ __set_bit(FW_FLAGS_NC, &fw_flags);
+
+ break;
+ default:
+@@ -3922,7 +3929,7 @@ static long rtl8152_check_firmware(struc
+ }
+
+ fw_end:
+- if ((phy_nc || start) && !stop) {
++ if (test_bit(FW_FLAGS_START, &fw_flags) && !test_bit(FW_FLAGS_STOP, &fw_flags)) {
+ dev_err(&tp->intf->dev, "without PHY_STOP\n");
+ goto fail;
+ }
--- /dev/null
+From f010a7d51cbb42bdb956f0a28b8868b15d7a3816 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Fri, 16 Apr 2021 16:04:34 +0800
+Subject: [PATCH] r8152: add help function to change mtu
+
+commit 67ce1a806f164e59a074fea8809725d3411eaa20 upstream.
+
+The different chips may have different requests when changing mtu.
+Therefore, add a new help function of rtl_ops to change mtu. Besides,
+reset the tx/rx after changing mtu.
+
+Additionally, add mtu_to_size() and size_to_mtu() macros to simplify
+the code.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 53 ++++++++++++++++++++++++-----------------
+ 1 file changed, 31 insertions(+), 22 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -658,15 +658,13 @@ enum rtl_register_content {
+
+ #define INTR_LINK 0x0004
+
+-#define RTL8153_MAX_PACKET 9216 /* 9K */
+-#define RTL8153_MAX_MTU (RTL8153_MAX_PACKET - VLAN_ETH_HLEN - \
+- ETH_FCS_LEN)
+ #define RTL8152_RMS (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN)
+ #define RTL8153_RMS RTL8153_MAX_PACKET
+ #define RTL8152_TX_TIMEOUT (5 * HZ)
+ #define RTL8152_NAPI_WEIGHT 64
+-#define rx_reserved_size(x) ((x) + VLAN_ETH_HLEN + ETH_FCS_LEN + \
+- sizeof(struct rx_desc) + RX_ALIGN)
++#define mtu_to_size(m) ((m) + VLAN_ETH_HLEN + ETH_FCS_LEN)
++#define size_to_mtu(s) ((s) - VLAN_ETH_HLEN - ETH_FCS_LEN)
++#define rx_reserved_size(x) (mtu_to_size(x) + sizeof(struct rx_desc) + RX_ALIGN)
+
+ /* rtl8152 flags */
+ enum rtl8152_flags {
+@@ -796,6 +794,7 @@ struct r8152 {
+ bool (*in_nway)(struct r8152 *tp);
+ void (*hw_phy_cfg)(struct r8152 *tp);
+ void (*autosuspend_en)(struct r8152 *tp, bool enable);
++ void (*change_mtu)(struct r8152 *tp);
+ } rtl_ops;
+
+ struct ups_info {
+@@ -1022,8 +1021,7 @@ enum tx_csum_stat {
+ static const int multicast_filter_limit = 32;
+ static unsigned int agg_buf_sz = 16384;
+
+-#define RTL_LIMITED_TSO_SIZE (agg_buf_sz - sizeof(struct tx_desc) - \
+- VLAN_ETH_HLEN - ETH_FCS_LEN)
++#define RTL_LIMITED_TSO_SIZE (size_to_mtu(agg_buf_sz) - sizeof(struct tx_desc))
+
+ static
+ int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
+@@ -2633,10 +2631,7 @@ static void rtl8152_nic_reset(struct r81
+
+ static void set_tx_qlen(struct r8152 *tp)
+ {
+- struct net_device *netdev = tp->netdev;
+-
+- tp->tx_qlen = agg_buf_sz / (netdev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN +
+- sizeof(struct tx_desc));
++ tp->tx_qlen = agg_buf_sz / (mtu_to_size(tp->netdev->mtu) + sizeof(struct tx_desc));
+ }
+
+ static inline u8 rtl8152_get_speed(struct r8152 *tp)
+@@ -4725,6 +4720,12 @@ static void r8153b_hw_phy_cfg(struct r81
+ set_bit(PHY_RESET, &tp->flags);
+ }
+
++static void rtl8153_change_mtu(struct r8152 *tp)
++{
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, mtu_to_size(tp->netdev->mtu));
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO);
++}
++
+ static void r8153_first_init(struct r8152 *tp)
+ {
+ u32 ocp_data;
+@@ -4757,9 +4758,7 @@ static void r8153_first_init(struct r815
+
+ rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX);
+
+- ocp_data = tp->netdev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
+- ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, ocp_data);
+- ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO);
++ rtl8153_change_mtu(tp);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0);
+ ocp_data |= TCR0_AUTO_FIFO;
+@@ -4794,8 +4793,7 @@ static void r8153_enter_oob(struct r8152
+
+ wait_oob_link_list_ready(tp);
+
+- ocp_data = tp->netdev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
+- ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, ocp_data);
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, mtu_to_size(tp->netdev->mtu));
+
+ switch (tp->version) {
+ case RTL_VER_03:
+@@ -6518,12 +6516,21 @@ static int rtl8152_change_mtu(struct net
+ dev->mtu = new_mtu;
+
+ if (netif_running(dev)) {
+- u32 rms = new_mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
+-
+- ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, rms);
++ if (tp->rtl_ops.change_mtu)
++ tp->rtl_ops.change_mtu(tp);
+
+- if (netif_carrier_ok(dev))
+- r8153_set_rx_early_size(tp);
++ if (netif_carrier_ok(dev)) {
++ netif_stop_queue(dev);
++ napi_disable(&tp->napi);
++ tasklet_disable(&tp->tx_tl);
++ tp->rtl_ops.disable(tp);
++ tp->rtl_ops.enable(tp);
++ rtl_start_rx(tp);
++ tasklet_enable(&tp->tx_tl);
++ napi_enable(&tp->napi);
++ rtl8152_set_rx_mode(dev);
++ netif_wake_queue(dev);
++ }
+ }
+
+ mutex_unlock(&tp->control);
+@@ -6612,6 +6619,7 @@ static int rtl_ops_init(struct r8152 *tp
+ ops->in_nway = rtl8153_in_nway;
+ ops->hw_phy_cfg = r8153_hw_phy_cfg;
+ ops->autosuspend_en = rtl8153_runtime_enable;
++ ops->change_mtu = rtl8153_change_mtu;
+ if (tp->udev->speed < USB_SPEED_SUPER)
+ tp->rx_buf_sz = 16 * 1024;
+ else
+@@ -6633,6 +6641,7 @@ static int rtl_ops_init(struct r8152 *tp
+ ops->in_nway = rtl8153_in_nway;
+ ops->hw_phy_cfg = r8153b_hw_phy_cfg;
+ ops->autosuspend_en = rtl8153b_runtime_enable;
++ ops->change_mtu = rtl8153_change_mtu;
+ tp->rx_buf_sz = 32 * 1024;
+ tp->eee_en = true;
+ tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX;
+@@ -6853,7 +6862,7 @@ static int rtl8152_probe(struct usb_inte
+ netdev->max_mtu = ETH_DATA_LEN;
+ break;
+ default:
+- netdev->max_mtu = RTL8153_MAX_MTU;
++ netdev->max_mtu = size_to_mtu(9 * 1024);
+ break;
+ }
+
--- /dev/null
+From e7439e7fd384f55f55837f7e4866e74d8dca3827 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Fri, 16 Apr 2021 16:04:35 +0800
+Subject: [PATCH] r8152: support new chips
+
+commit 195aae321c829dd1945900d75561e6aa79cce208 upstream.
+
+Support RTL8153C, RTL8153D, RTL8156A, and RTL8156B. The RTL8156A
+and RTL8156B are the 2.5G ethernet.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 2634 +++++++++++++++++++++++++++++++++++----
+ 1 file changed, 2359 insertions(+), 275 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -44,10 +44,14 @@
+
+ #define PLA_IDR 0xc000
+ #define PLA_RCR 0xc010
++#define PLA_RCR1 0xc012
+ #define PLA_RMS 0xc016
+ #define PLA_RXFIFO_CTRL0 0xc0a0
++#define PLA_RXFIFO_FULL 0xc0a2
+ #define PLA_RXFIFO_CTRL1 0xc0a4
++#define PLA_RX_FIFO_FULL 0xc0a6
+ #define PLA_RXFIFO_CTRL2 0xc0a8
++#define PLA_RX_FIFO_EMPTY 0xc0aa
+ #define PLA_DMY_REG0 0xc0b0
+ #define PLA_FMC 0xc0b4
+ #define PLA_CFG_WOL 0xc0b6
+@@ -64,6 +68,8 @@
+ #define PLA_MACDBG_PRE 0xd38c /* RTL_VER_04 only */
+ #define PLA_MACDBG_POST 0xd38e /* RTL_VER_04 only */
+ #define PLA_EXTRA_STATUS 0xd398
++#define PLA_GPHY_CTRL 0xd3ae
++#define PLA_POL_GPIO_CTRL 0xdc6a
+ #define PLA_EFUSE_DATA 0xdd00
+ #define PLA_EFUSE_CMD 0xdd02
+ #define PLA_LEDSEL 0xdd90
+@@ -73,6 +79,8 @@
+ #define PLA_LWAKE_CTRL_REG 0xe007
+ #define PLA_GPHY_INTR_IMR 0xe022
+ #define PLA_EEE_CR 0xe040
++#define PLA_EEE_TXTWSYS 0xe04c
++#define PLA_EEE_TXTWSYS_2P5G 0xe058
+ #define PLA_EEEP_CR 0xe080
+ #define PLA_MAC_PWR_CTRL 0xe0c0
+ #define PLA_MAC_PWR_CTRL2 0xe0ca
+@@ -83,6 +91,7 @@
+ #define PLA_TCR1 0xe612
+ #define PLA_MTPS 0xe615
+ #define PLA_TXFIFO_CTRL 0xe618
++#define PLA_TXFIFO_FULL 0xe61a
+ #define PLA_RSTTALLY 0xe800
+ #define PLA_CR 0xe813
+ #define PLA_CRWECR 0xe81c
+@@ -99,6 +108,7 @@
+ #define PLA_SFF_STS_7 0xe8de
+ #define PLA_PHYSTATUS 0xe908
+ #define PLA_CONFIG6 0xe90a /* CONFIG6 */
++#define PLA_USB_CFG 0xe952
+ #define PLA_BP_BA 0xfc26
+ #define PLA_BP_0 0xfc28
+ #define PLA_BP_1 0xfc2a
+@@ -113,6 +123,7 @@
+ #define USB_USB2PHY 0xb41e
+ #define USB_SSPHYLINK1 0xb426
+ #define USB_SSPHYLINK2 0xb428
++#define USB_L1_CTRL 0xb45e
+ #define USB_U2P3_CTRL 0xb460
+ #define USB_CSR_DUMMY1 0xb464
+ #define USB_CSR_DUMMY2 0xb466
+@@ -123,7 +134,12 @@
+ #define USB_FW_FIX_EN0 0xcfca
+ #define USB_FW_FIX_EN1 0xcfcc
+ #define USB_LPM_CONFIG 0xcfd8
++#define USB_ECM_OPTION 0xcfee
+ #define USB_CSTMR 0xcfef /* RTL8153A */
++#define USB_MISC_2 0xcfff
++#define USB_ECM_OP 0xd26b
++#define USB_GPHY_CTRL 0xd284
++#define USB_SPEED_OPTION 0xd32a
+ #define USB_FW_CTRL 0xd334 /* RTL8153B */
+ #define USB_FC_TIMER 0xd340
+ #define USB_USB_CTRL 0xd406
+@@ -137,16 +153,20 @@
+ #define USB_RX_EXTRA_AGGR_TMR 0xd432 /* RTL8153B */
+ #define USB_TX_DMA 0xd434
+ #define USB_UPT_RXDMA_OWN 0xd437
++#define USB_UPHY3_MDCMDIO 0xd480
+ #define USB_TOLERANCE 0xd490
+ #define USB_LPM_CTRL 0xd41a
+ #define USB_BMU_RESET 0xd4b0
++#define USB_BMU_CONFIG 0xd4b4
+ #define USB_U1U2_TIMER 0xd4da
+ #define USB_FW_TASK 0xd4e8 /* RTL8153B */
++#define USB_RX_AGGR_NUM 0xd4ee
+ #define USB_UPS_CTRL 0xd800
+ #define USB_POWER_CUT 0xd80a
+ #define USB_MISC_0 0xd81a
+ #define USB_MISC_1 0xd81f
+ #define USB_AFE_CTRL2 0xd824
++#define USB_UPHY_XTAL 0xd826
+ #define USB_UPS_CFG 0xd842
+ #define USB_UPS_FLAGS 0xd848
+ #define USB_WDT1_CTRL 0xe404
+@@ -189,6 +209,9 @@
+ #define OCP_EEE_ABLE 0xa5c4
+ #define OCP_EEE_ADV 0xa5d0
+ #define OCP_EEE_LPABLE 0xa5d2
++#define OCP_10GBT_CTRL 0xa5d4
++#define OCP_10GBT_STAT 0xa5d6
++#define OCP_EEE_ADV2 0xa6d4
+ #define OCP_PHY_STATE 0xa708 /* nway state for 8153 */
+ #define OCP_PHY_PATCH_STAT 0xb800
+ #define OCP_PHY_PATCH_CMD 0xb820
+@@ -200,6 +223,7 @@
+ /* SRAM Register */
+ #define SRAM_GREEN_CFG 0x8011
+ #define SRAM_LPF_CFG 0x8012
++#define SRAM_GPHY_FW_VER 0x801e
+ #define SRAM_10M_AMP1 0x8080
+ #define SRAM_10M_AMP2 0x8082
+ #define SRAM_IMPEDANCE 0x8084
+@@ -211,11 +235,19 @@
+ #define RCR_AM 0x00000004
+ #define RCR_AB 0x00000008
+ #define RCR_ACPT_ALL (RCR_AAP | RCR_APM | RCR_AM | RCR_AB)
++#define SLOT_EN BIT(11)
++
++/* PLA_RCR1 */
++#define OUTER_VLAN BIT(7)
++#define INNER_VLAN BIT(6)
+
+ /* PLA_RXFIFO_CTRL0 */
+ #define RXFIFO_THR1_NORMAL 0x00080002
+ #define RXFIFO_THR1_OOB 0x01800003
+
++/* PLA_RXFIFO_FULL */
++#define RXFIFO_FULL_MASK 0xfff
++
+ /* PLA_RXFIFO_CTRL1 */
+ #define RXFIFO_THR2_FULL 0x00000060
+ #define RXFIFO_THR2_HIGH 0x00000038
+@@ -286,6 +318,7 @@
+ #define MCU_BORW_EN 0x4000
+
+ /* PLA_CPCR */
++#define FLOW_CTRL_EN BIT(0)
+ #define CPCR_RX_VLAN 0x0040
+
+ /* PLA_CFG_WOL */
+@@ -311,6 +344,10 @@
+ /* PLA_CONFIG6 */
+ #define LANWAKE_CLR_EN BIT(0)
+
++/* PLA_USB_CFG */
++#define EN_XG_LIP BIT(1)
++#define EN_G_LIP BIT(2)
++
+ /* PLA_CONFIG5 */
+ #define BWF_EN 0x0040
+ #define MWF_EN 0x0020
+@@ -334,6 +371,7 @@
+ /* PLA_MAC_PWR_CTRL2 */
+ #define EEE_SPDWN_RATIO 0x8007
+ #define MAC_CLK_SPDWN_EN BIT(15)
++#define EEE_SPDWN_RATIO_MASK 0xff
+
+ /* PLA_MAC_PWR_CTRL3 */
+ #define PLA_MCU_SPDWN_EN BIT(14)
+@@ -346,6 +384,7 @@
+ #define PWRSAVE_SPDWN_EN 0x1000
+ #define RXDV_SPDWN_EN 0x0800
+ #define TX10MIDLE_EN 0x0100
++#define IDLE_SPDWN_EN BIT(6)
+ #define TP100_SPDWN_EN 0x0020
+ #define TP500_SPDWN_EN 0x0010
+ #define TP1000_SPDWN_EN 0x0008
+@@ -386,6 +425,13 @@
+ #define LINK_CHANGE_FLAG BIT(8)
+ #define POLL_LINK_CHG BIT(0)
+
++/* PLA_GPHY_CTRL */
++#define GPHY_FLASH BIT(1)
++
++/* PLA_POL_GPIO_CTRL */
++#define DACK_DET_EN BIT(15)
++#define POL_GPHY_PATCH BIT(4)
++
+ /* USB_USB2PHY */
+ #define USB2PHY_SUSPEND 0x0001
+ #define USB2PHY_L1 0x0002
+@@ -434,6 +480,9 @@
+ #define BMU_RESET_EP_IN 0x01
+ #define BMU_RESET_EP_OUT 0x02
+
++/* USB_BMU_CONFIG */
++#define ACT_ODMA BIT(1)
++
+ /* USB_UPT_RXDMA_OWN */
+ #define OWN_UPDATE BIT(0)
+ #define OWN_CLEAR BIT(1)
+@@ -441,27 +490,52 @@
+ /* USB_FW_TASK */
+ #define FC_PATCH_TASK BIT(1)
+
++/* USB_RX_AGGR_NUM */
++#define RX_AGGR_NUM_MASK 0x1ff
++
+ /* USB_UPS_CTRL */
+ #define POWER_CUT 0x0100
+
+ /* USB_PM_CTRL_STATUS */
+ #define RESUME_INDICATE 0x0001
+
++/* USB_ECM_OPTION */
++#define BYPASS_MAC_RESET BIT(5)
++
+ /* USB_CSTMR */
+ #define FORCE_SUPER BIT(0)
+
++/* USB_MISC_2 */
++#define UPS_FORCE_PWR_DOWN BIT(0)
++
++/* USB_ECM_OP */
++#define EN_ALL_SPEED BIT(0)
++
++/* USB_GPHY_CTRL */
++#define GPHY_PATCH_DONE BIT(2)
++#define BYPASS_FLASH BIT(5)
++#define BACKUP_RESTRORE BIT(6)
++
++/* USB_SPEED_OPTION */
++#define RG_PWRDN_EN BIT(8)
++#define ALL_SPEED_OFF BIT(9)
++
+ /* USB_FW_CTRL */
+ #define FLOW_CTRL_PATCH_OPT BIT(1)
++#define AUTO_SPEEDUP BIT(3)
++#define FLOW_CTRL_PATCH_2 BIT(8)
+
+ /* USB_FC_TIMER */
+ #define CTRL_TIMER_EN BIT(15)
+
+ /* USB_USB_CTRL */
++#define CDC_ECM_EN BIT(3)
+ #define RX_AGG_DISABLE 0x0010
+ #define RX_ZERO_EN 0x0080
+
+ /* USB_U2P3_CTRL */
+ #define U2P3_ENABLE 0x0001
++#define RX_DETECT8 BIT(3)
+
+ /* USB_POWER_CUT */
+ #define PWR_EN 0x0001
+@@ -497,8 +571,12 @@
+ #define SEN_VAL_NORMAL 0xa000
+ #define SEL_RXIDLE 0x0100
+
++/* USB_UPHY_XTAL */
++#define OOBS_POLLING BIT(8)
++
+ /* USB_UPS_CFG */
+ #define SAW_CNT_1MS_MASK 0x0fff
++#define MID_REVERSE BIT(5) /* RTL8156A */
+
+ /* USB_UPS_FLAGS */
+ #define UPS_FLAGS_R_TUNE BIT(0)
+@@ -506,6 +584,7 @@
+ #define UPS_FLAGS_250M_CKDIV BIT(2)
+ #define UPS_FLAGS_EN_ALDPS BIT(3)
+ #define UPS_FLAGS_CTAP_SHORT_DIS BIT(4)
++#define UPS_FLAGS_SPEED_MASK (0xf << 16)
+ #define ups_flags_speed(x) ((x) << 16)
+ #define UPS_FLAGS_EN_EEE BIT(20)
+ #define UPS_FLAGS_EN_500M_EEE BIT(21)
+@@ -526,6 +605,8 @@ enum spd_duplex {
+ FORCE_10M_FULL,
+ FORCE_100M_HALF,
+ FORCE_100M_FULL,
++ FORCE_1000M_FULL,
++ NWAY_2500M_FULL,
+ };
+
+ /* OCP_ALDPS_CONFIG */
+@@ -590,6 +671,9 @@ enum spd_duplex {
+ #define EN_10M_CLKDIV BIT(11)
+ #define EN_10M_BGOFF 0x0080
+
++/* OCP_10GBT_CTRL */
++#define RTL_ADV2_5G_F_R BIT(5) /* Advertise 2.5GBASE-T fast-retrain */
++
+ /* OCP_PHY_STATE */
+ #define TXDIS_STATE 0x01
+ #define ABD_STATE 0x02
+@@ -609,7 +693,8 @@ enum spd_duplex {
+ #define EN_EMI_L 0x0040
+
+ /* OCP_SYSCLK_CFG */
+-#define clk_div_expo(x) (min(x, 5) << 8)
++#define sysclk_div_expo(x) (min(x, 5) << 8)
++#define clk_div_expo(x) (min(x, 5) << 4)
+
+ /* SRAM_GREEN_CFG */
+ #define GREEN_ETH_EN BIT(15)
+@@ -640,6 +725,11 @@ enum spd_duplex {
+ #define BP4_SUPER_ONLY 0x1578 /* RTL_VER_04 only */
+
+ enum rtl_register_content {
++ _2500bps = BIT(10),
++ _1250bps = BIT(9),
++ _500bps = BIT(8),
++ _tx_flow = BIT(6),
++ _rx_flow = BIT(5),
+ _1000bps = 0x10,
+ _100bps = 0x08,
+ _10bps = 0x04,
+@@ -647,6 +737,9 @@ enum rtl_register_content {
+ FULL_DUP = 0x01,
+ };
+
++#define is_speed_2500(_speed) (((_speed) & (_2500bps | LINK_STATUS)) == (_2500bps | LINK_STATUS))
++#define is_flow_control(_speed) (((_speed) & (_tx_flow | _rx_flow)) == (_tx_flow | _rx_flow))
++
+ #define RTL8152_MAX_TX 4
+ #define RTL8152_MAX_RX 10
+ #define INTBUFSIZE 2
+@@ -661,7 +754,6 @@ enum rtl_register_content {
+ #define RTL8152_RMS (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN)
+ #define RTL8153_RMS RTL8153_MAX_PACKET
+ #define RTL8152_TX_TIMEOUT (5 * HZ)
+-#define RTL8152_NAPI_WEIGHT 64
+ #define mtu_to_size(m) ((m) + VLAN_ETH_HLEN + ETH_FCS_LEN)
+ #define size_to_mtu(s) ((s) - VLAN_ETH_HLEN - ETH_FCS_LEN)
+ #define rx_reserved_size(x) (mtu_to_size(x) + sizeof(struct rx_desc) + RX_ALIGN)
+@@ -798,6 +890,7 @@ struct r8152 {
+ } rtl_ops;
+
+ struct ups_info {
++ u32 r_tune:1;
+ u32 _10m_ckdiv:1;
+ u32 _250m_ckdiv:1;
+ u32 aldps:1;
+@@ -839,7 +932,9 @@ struct r8152 {
+ u32 rx_buf_sz;
+ u32 rx_copybreak;
+ u32 rx_pending;
++ u32 fc_pause_on, fc_pause_off;
+
++ u32 support_2500full:1;
+ u16 ocp_base;
+ u16 speed;
+ u16 eee_adv;
+@@ -999,6 +1094,15 @@ enum rtl_version {
+ RTL_VER_07,
+ RTL_VER_08,
+ RTL_VER_09,
++
++ RTL_TEST_01,
++ RTL_VER_10,
++ RTL_VER_11,
++ RTL_VER_12,
++ RTL_VER_13,
++ RTL_VER_14,
++ RTL_VER_15,
++
+ RTL_VER_MAX
+ };
+
+@@ -1014,6 +1118,7 @@ enum tx_csum_stat {
+ #define RTL_ADVERTISED_100_FULL BIT(3)
+ #define RTL_ADVERTISED_1000_HALF BIT(4)
+ #define RTL_ADVERTISED_1000_FULL BIT(5)
++#define RTL_ADVERTISED_2500_FULL BIT(6)
+
+ /* 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.
+@@ -2607,7 +2712,7 @@ static netdev_tx_t rtl8152_start_xmit(st
+
+ static void r8152b_reset_packet_filter(struct r8152 *tp)
+ {
+- u32 ocp_data;
++ u32 ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_FMC);
+ ocp_data &= ~FMC_FCR_MCU_EN;
+@@ -2618,14 +2723,47 @@ static void r8152b_reset_packet_filter(s
+
+ static void rtl8152_nic_reset(struct r8152 *tp)
+ {
+- int i;
++ u32 ocp_data;
++ int i;
+
+- ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST);
++ switch (tp->version) {
++ case RTL_TEST_01:
++ case RTL_VER_10:
++ case RTL_VER_11:
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR);
++ ocp_data &= ~CR_TE;
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_BMU_RESET);
++ ocp_data &= ~BMU_RESET_EP_IN;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
++ ocp_data |= CDC_ECM_EN;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR);
++ ocp_data &= ~CR_RE;
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_BMU_RESET);
++ ocp_data |= BMU_RESET_EP_IN;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
++ ocp_data &= ~CDC_ECM_EN;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
++ break;
+
+- for (i = 0; i < 1000; i++) {
+- if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST))
+- break;
+- usleep_range(100, 400);
++ default:
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST);
++
++ for (i = 0; i < 1000; i++) {
++ if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST))
++ break;
++ usleep_range(100, 400);
++ }
++ break;
+ }
+ }
+
+@@ -2634,9 +2772,9 @@ static void set_tx_qlen(struct r8152 *tp
+ tp->tx_qlen = agg_buf_sz / (mtu_to_size(tp->netdev->mtu) + sizeof(struct tx_desc));
+ }
+
+-static inline u8 rtl8152_get_speed(struct r8152 *tp)
++static inline u16 rtl8152_get_speed(struct r8152 *tp)
+ {
+- return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS);
++ return ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHYSTATUS);
+ }
+
+ static void rtl_eee_plus_en(struct r8152 *tp, bool enable)
+@@ -2796,6 +2934,7 @@ static int rtl_enable(struct r8152 *tp)
+ switch (tp->version) {
+ case RTL_VER_08:
+ case RTL_VER_09:
++ case RTL_VER_14:
+ r8153b_rx_agg_chg_indicate(tp);
+ break;
+ default:
+@@ -2833,6 +2972,7 @@ static void r8153_set_rx_early_timeout(s
+
+ case RTL_VER_08:
+ case RTL_VER_09:
++ case RTL_VER_14:
+ /* The RTL8153B uses USB_RX_EXTRA_AGGR_TMR for rx timeout
+ * primarily. For USB_RX_EARLY_TIMEOUT, we fix it to 128ns.
+ */
+@@ -2842,6 +2982,18 @@ static void r8153_set_rx_early_timeout(s
+ ocp_data);
+ break;
+
++ case RTL_VER_10:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_15:
++ ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT,
++ 640 / 8);
++ ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EXTRA_AGGR_TMR,
++ ocp_data);
++ r8153b_rx_agg_chg_indicate(tp);
++ break;
++
+ default:
+ break;
+ }
+@@ -2861,8 +3013,19 @@ static void r8153_set_rx_early_size(stru
+ break;
+ case RTL_VER_08:
+ case RTL_VER_09:
++ case RTL_VER_14:
++ ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE,
++ ocp_data / 8);
++ break;
++ case RTL_TEST_01:
++ case RTL_VER_10:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_15:
+ ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE,
+ ocp_data / 8);
++ r8153b_rx_agg_chg_indicate(tp);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+@@ -2872,6 +3035,8 @@ static void r8153_set_rx_early_size(stru
+
+ static int rtl8153_enable(struct r8152 *tp)
+ {
++ u32 ocp_data;
++
+ if (test_bit(RTL8152_UNPLUG, &tp->flags))
+ return -ENODEV;
+
+@@ -2882,15 +3047,18 @@ static int rtl8153_enable(struct r8152 *
+
+ rtl_set_ifg(tp, rtl8152_get_speed(tp));
+
+- if (tp->version == RTL_VER_09) {
+- u32 ocp_data;
+-
++ switch (tp->version) {
++ case RTL_VER_09:
++ case RTL_VER_14:
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK);
+ ocp_data &= ~FC_PATCH_TASK;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
+ usleep_range(1000, 2000);
+ ocp_data |= FC_PATCH_TASK;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
++ break;
++ default:
++ break;
+ }
+
+ return rtl_enable(tp);
+@@ -2955,12 +3123,40 @@ static void rtl_rx_vlan_en(struct r8152
+ {
+ u32 ocp_data;
+
+- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR);
+- if (enable)
+- ocp_data |= CPCR_RX_VLAN;
+- else
+- ocp_data &= ~CPCR_RX_VLAN;
+- ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data);
++ switch (tp->version) {
++ case RTL_VER_01:
++ case RTL_VER_02:
++ case RTL_VER_03:
++ case RTL_VER_04:
++ case RTL_VER_05:
++ case RTL_VER_06:
++ case RTL_VER_07:
++ case RTL_VER_08:
++ case RTL_VER_09:
++ case RTL_VER_14:
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR);
++ if (enable)
++ ocp_data |= CPCR_RX_VLAN;
++ else
++ ocp_data &= ~CPCR_RX_VLAN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data);
++ break;
++
++ case RTL_TEST_01:
++ case RTL_VER_10:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_15:
++ default:
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RCR1);
++ if (enable)
++ ocp_data |= OUTER_VLAN | INNER_VLAN;
++ else
++ ocp_data &= ~(OUTER_VLAN | INNER_VLAN);
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RCR1, ocp_data);
++ break;
++ }
+ }
+
+ static int rtl8152_set_features(struct net_device *dev,
+@@ -3053,6 +3249,40 @@ static void __rtl_set_wol(struct r8152 *
+ device_set_wakeup_enable(&tp->udev->dev, false);
+ }
+
++static void r8153_mac_clk_speed_down(struct r8152 *tp, bool enable)
++{
++ u32 ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2);
++
++ /* MAC clock speed down */
++ if (enable)
++ ocp_data |= MAC_CLK_SPDWN_EN;
++ else
++ ocp_data &= ~MAC_CLK_SPDWN_EN;
++
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data);
++}
++
++static void r8156_mac_clk_spd(struct r8152 *tp, bool enable)
++{
++ u32 ocp_data;
++
++ /* MAC clock speed down */
++ if (enable) {
++ /* aldps_spdwn_ratio, tp10_spdwn_ratio */
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL,
++ 0x0403);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2);
++ ocp_data &= ~EEE_SPDWN_RATIO_MASK;
++ ocp_data |= MAC_CLK_SPDWN_EN | 0x03; /* eee_spdwn_ratio */
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data);
++ } else {
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2);
++ ocp_data &= ~MAC_CLK_SPDWN_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data);
++ }
++}
++
+ static void r8153_u1u2en(struct r8152 *tp, bool enable)
+ {
+ u8 u1u2[8];
+@@ -3112,6 +3342,9 @@ static void r8153b_ups_flags(struct r815
+ if (tp->ups_info.eee_cmod_lv)
+ ups_flags |= UPS_FLAGS_EEE_CMOD_LV_EN;
+
++ if (tp->ups_info.r_tune)
++ ups_flags |= UPS_FLAGS_R_TUNE;
++
+ if (tp->ups_info._10m_ckdiv)
+ ups_flags |= UPS_FLAGS_EN_10M_CKDIV;
+
+@@ -3162,6 +3395,88 @@ static void r8153b_ups_flags(struct r815
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_UPS_FLAGS, ups_flags);
+ }
+
++static void r8156_ups_flags(struct r8152 *tp)
++{
++ u32 ups_flags = 0;
++
++ if (tp->ups_info.green)
++ ups_flags |= UPS_FLAGS_EN_GREEN;
++
++ if (tp->ups_info.aldps)
++ ups_flags |= UPS_FLAGS_EN_ALDPS;
++
++ if (tp->ups_info.eee)
++ ups_flags |= UPS_FLAGS_EN_EEE;
++
++ if (tp->ups_info.flow_control)
++ ups_flags |= UPS_FLAGS_EN_FLOW_CTR;
++
++ if (tp->ups_info.eee_ckdiv)
++ ups_flags |= UPS_FLAGS_EN_EEE_CKDIV;
++
++ if (tp->ups_info._10m_ckdiv)
++ ups_flags |= UPS_FLAGS_EN_10M_CKDIV;
++
++ if (tp->ups_info.eee_plloff_100)
++ ups_flags |= UPS_FLAGS_EEE_PLLOFF_100;
++
++ if (tp->ups_info.eee_plloff_giga)
++ ups_flags |= UPS_FLAGS_EEE_PLLOFF_GIGA;
++
++ if (tp->ups_info._250m_ckdiv)
++ ups_flags |= UPS_FLAGS_250M_CKDIV;
++
++ switch (tp->ups_info.speed_duplex) {
++ case FORCE_10M_HALF:
++ ups_flags |= ups_flags_speed(0);
++ break;
++ case FORCE_10M_FULL:
++ ups_flags |= ups_flags_speed(1);
++ break;
++ case FORCE_100M_HALF:
++ ups_flags |= ups_flags_speed(2);
++ break;
++ case FORCE_100M_FULL:
++ ups_flags |= ups_flags_speed(3);
++ break;
++ case NWAY_10M_HALF:
++ ups_flags |= ups_flags_speed(4);
++ break;
++ case NWAY_10M_FULL:
++ ups_flags |= ups_flags_speed(5);
++ break;
++ case NWAY_100M_HALF:
++ ups_flags |= ups_flags_speed(6);
++ break;
++ case NWAY_100M_FULL:
++ ups_flags |= ups_flags_speed(7);
++ break;
++ case NWAY_1000M_FULL:
++ ups_flags |= ups_flags_speed(8);
++ break;
++ case NWAY_2500M_FULL:
++ ups_flags |= ups_flags_speed(9);
++ break;
++ default:
++ break;
++ }
++
++ switch (tp->ups_info.lite_mode) {
++ case 1:
++ ups_flags |= 0 << 5;
++ break;
++ case 2:
++ ups_flags |= 2 << 5;
++ break;
++ case 0:
++ default:
++ ups_flags |= 1 << 5;
++ break;
++ }
++
++ ocp_write_dword(tp, MCU_TYPE_USB, USB_UPS_FLAGS, ups_flags);
++}
++
+ static void rtl_green_en(struct r8152 *tp, bool enable)
+ {
+ u16 data;
+@@ -3225,16 +3540,16 @@ static void r8153b_ups_en(struct r8152 *
+ ocp_data |= UPS_EN | USP_PREWAKE | PHASE2_EN;
+ ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
+
+- ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, 0xcfff);
+- ocp_data |= BIT(0);
+- ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data);
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
++ ocp_data |= UPS_FORCE_PWR_DOWN;
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
+ } else {
+ ocp_data &= ~(UPS_EN | USP_PREWAKE);
+ ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
+
+- ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, 0xcfff);
+- ocp_data &= ~BIT(0);
+- ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data);
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
++ ocp_data &= ~UPS_FORCE_PWR_DOWN;
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
+
+ if (ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0) & PCUT_STATUS) {
+ int i;
+@@ -3254,6 +3569,95 @@ static void r8153b_ups_en(struct r8152 *
+ }
+ }
+
++static void r8153c_ups_en(struct r8152 *tp, bool enable)
++{
++ u32 ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_POWER_CUT);
++
++ if (enable) {
++ r8153b_ups_flags(tp);
++
++ ocp_data |= UPS_EN | USP_PREWAKE | PHASE2_EN;
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
++ ocp_data |= UPS_FORCE_PWR_DOWN;
++ ocp_data &= ~BIT(7);
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
++ } else {
++ ocp_data &= ~(UPS_EN | USP_PREWAKE);
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
++ ocp_data &= ~UPS_FORCE_PWR_DOWN;
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
++
++ if (ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0) & PCUT_STATUS) {
++ int i;
++
++ for (i = 0; i < 500; i++) {
++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
++ AUTOLOAD_DONE)
++ break;
++ msleep(20);
++ }
++
++ tp->rtl_ops.hw_phy_cfg(tp);
++
++ rtl8152_set_speed(tp, tp->autoneg, tp->speed,
++ tp->duplex, tp->advertising);
++ }
++
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34);
++ ocp_data |= BIT(8);
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data);
++
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
++ }
++}
++
++static void r8156_ups_en(struct r8152 *tp, bool enable)
++{
++ u32 ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_POWER_CUT);
++
++ if (enable) {
++ r8156_ups_flags(tp);
++
++ ocp_data |= UPS_EN | USP_PREWAKE | PHASE2_EN;
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
++ ocp_data |= UPS_FORCE_PWR_DOWN;
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
++
++ switch (tp->version) {
++ case RTL_VER_13:
++ case RTL_VER_15:
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPHY_XTAL);
++ ocp_data &= ~OOBS_POLLING;
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_UPHY_XTAL, ocp_data);
++ break;
++ default:
++ break;
++ }
++ } else {
++ ocp_data &= ~(UPS_EN | USP_PREWAKE);
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
++ ocp_data &= ~UPS_FORCE_PWR_DOWN;
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
++
++ if (ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0) & PCUT_STATUS) {
++ tp->rtl_ops.hw_phy_cfg(tp);
++
++ rtl8152_set_speed(tp, tp->autoneg, tp->speed,
++ tp->duplex, tp->advertising);
++ }
++ }
++}
++
+ static void r8153_power_cut_en(struct r8152 *tp, bool enable)
+ {
+ u32 ocp_data;
+@@ -3383,6 +3787,38 @@ static void rtl8153b_runtime_enable(stru
+ }
+ }
+
++static void rtl8153c_runtime_enable(struct r8152 *tp, bool enable)
++{
++ if (enable) {
++ r8153_queue_wake(tp, true);
++ r8153b_u1u2en(tp, false);
++ r8153_u2p3en(tp, false);
++ rtl_runtime_suspend_enable(tp, true);
++ r8153c_ups_en(tp, true);
++ } else {
++ r8153c_ups_en(tp, false);
++ r8153_queue_wake(tp, false);
++ rtl_runtime_suspend_enable(tp, false);
++ r8153b_u1u2en(tp, true);
++ }
++}
++
++static void rtl8156_runtime_enable(struct r8152 *tp, bool enable)
++{
++ if (enable) {
++ r8153_queue_wake(tp, true);
++ r8153b_u1u2en(tp, false);
++ r8153_u2p3en(tp, false);
++ rtl_runtime_suspend_enable(tp, true);
++ } else {
++ r8153_queue_wake(tp, false);
++ rtl_runtime_suspend_enable(tp, false);
++ r8153_u2p3en(tp, true);
++ if (tp->udev->speed >= USB_SPEED_SUPER)
++ r8153b_u1u2en(tp, true);
++ }
++}
++
+ static void r8153_teredo_off(struct r8152 *tp)
+ {
+ u32 ocp_data;
+@@ -3403,14 +3839,19 @@ static void r8153_teredo_off(struct r815
+
+ case RTL_VER_08:
+ case RTL_VER_09:
++ case RTL_TEST_01:
++ case RTL_VER_10:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_14:
++ case RTL_VER_15:
++ default:
+ /* The bit 0 ~ 7 are relative with teredo settings. They are
+ * W1C (write 1 to clear), so set all 1 to disable it.
+ */
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, 0xff);
+ break;
+-
+- default:
+- break;
+ }
+
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE);
+@@ -3445,6 +3886,12 @@ static void rtl_clear_bp(struct r8152 *t
+ break;
+ case RTL_VER_08:
+ case RTL_VER_09:
++ case RTL_VER_10:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_14:
++ case RTL_VER_15:
+ default:
+ if (type == MCU_TYPE_USB) {
+ ocp_write_word(tp, MCU_TYPE_USB, USB_BP2_EN, 0);
+@@ -3654,6 +4101,11 @@ static bool rtl8152_is_fw_mac_ok(struct
+ case RTL_VER_06:
+ case RTL_VER_08:
+ case RTL_VER_09:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_14:
++ case RTL_VER_15:
+ fw_reg = 0xf800;
+ bp_ba_addr = PLA_BP_BA;
+ bp_en_addr = PLA_BP_EN;
+@@ -3677,6 +4129,11 @@ static bool rtl8152_is_fw_mac_ok(struct
+ break;
+ case RTL_VER_08:
+ case RTL_VER_09:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_14:
++ case RTL_VER_15:
+ fw_reg = 0xe600;
+ bp_ba_addr = USB_BP_BA;
+ bp_en_addr = USB_BP2_EN;
+@@ -4216,6 +4673,22 @@ static void r8153_eee_en(struct r8152 *t
+ tp->ups_info.eee = enable;
+ }
+
++static void r8156_eee_en(struct r8152 *tp, bool enable)
++{
++ u16 config;
++
++ r8153_eee_en(tp, enable);
++
++ config = ocp_reg_read(tp, OCP_EEE_ADV2);
++
++ if (enable)
++ config |= MDIO_EEE_2_5GT;
++ else
++ config &= ~MDIO_EEE_2_5GT;
++
++ ocp_reg_write(tp, OCP_EEE_ADV2, config);
++}
++
+ static void rtl_eee_enable(struct r8152 *tp, bool enable)
+ {
+ switch (tp->version) {
+@@ -4237,6 +4710,7 @@ static void rtl_eee_enable(struct r8152
+ case RTL_VER_06:
+ case RTL_VER_08:
+ case RTL_VER_09:
++ case RTL_VER_14:
+ if (enable) {
+ r8153_eee_en(tp, true);
+ ocp_reg_write(tp, OCP_EEE_ADV, tp->eee_adv);
+@@ -4245,6 +4719,19 @@ static void rtl_eee_enable(struct r8152
+ ocp_reg_write(tp, OCP_EEE_ADV, 0);
+ }
+ break;
++ case RTL_VER_10:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_15:
++ if (enable) {
++ r8156_eee_en(tp, true);
++ ocp_reg_write(tp, OCP_EEE_ADV, tp->eee_adv);
++ } else {
++ r8156_eee_en(tp, false);
++ ocp_reg_write(tp, OCP_EEE_ADV, 0);
++ }
++ break;
+ default:
+ break;
+ }
+@@ -4291,6 +4778,20 @@ static void wait_oob_link_list_ready(str
+ }
+ }
+
++static void r8156b_wait_loading_flash(struct r8152 *tp)
++{
++ if ((ocp_read_word(tp, MCU_TYPE_PLA, PLA_GPHY_CTRL) & GPHY_FLASH) &&
++ !(ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL) & BYPASS_FLASH)) {
++ int i;
++
++ for (i = 0; i < 100; i++) {
++ if (ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL) & GPHY_PATCH_DONE)
++ break;
++ usleep_range(1000, 2000);
++ }
++ }
++}
++
+ static void r8152b_exit_oob(struct r8152 *tp)
+ {
+ u32 ocp_data;
+@@ -4341,7 +4842,7 @@ static void r8152b_exit_oob(struct r8152
+ }
+
+ /* TX share fifo free credit full threshold */
+- ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL);
++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL2);
+
+ ocp_write_byte(tp, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_HIGH);
+@@ -4518,6 +5019,21 @@ static int r8153b_post_firmware_1(struct
+ return 0;
+ }
+
++static int r8153c_post_firmware_1(struct r8152 *tp)
++{
++ u32 ocp_data;
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_CTRL);
++ ocp_data |= FLOW_CTRL_PATCH_2;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_CTRL, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK);
++ ocp_data |= FC_PATCH_TASK;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
++
++ return 0;
++}
++
+ static void r8153_aldps_en(struct r8152 *tp, bool enable)
+ {
+ u16 data;
+@@ -4720,6 +5236,13 @@ static void r8153b_hw_phy_cfg(struct r81
+ set_bit(PHY_RESET, &tp->flags);
+ }
+
++static void r8153c_hw_phy_cfg(struct r8152 *tp)
++{
++ r8153b_hw_phy_cfg(tp);
++
++ tp->ups_info.r_tune = true;
++}
++
+ static void rtl8153_change_mtu(struct r8152 *tp)
+ {
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, mtu_to_size(tp->netdev->mtu));
+@@ -4807,6 +5330,7 @@ static void r8153_enter_oob(struct r8152
+
+ case RTL_VER_08:
+ case RTL_VER_09:
++ case RTL_VER_14:
+ /* Clear teredo wake event. bit[15:8] is the teredo wakeup
+ * type. Set it to zero. bits[7:0] are the W1C bits about
+ * the events. Set them to all 1 to clear them.
+@@ -4843,6 +5367,96 @@ static void rtl8153_disable(struct r8152
+ r8153_aldps_en(tp, true);
+ }
+
++static int rtl8156_enable(struct r8152 *tp)
++{
++ u32 ocp_data;
++ u16 speed;
++
++ if (test_bit(RTL8152_UNPLUG, &tp->flags))
++ return -ENODEV;
++
++ set_tx_qlen(tp);
++ rtl_set_eee_plus(tp);
++ r8153_set_rx_early_timeout(tp);
++ r8153_set_rx_early_size(tp);
++
++ speed = rtl8152_get_speed(tp);
++ rtl_set_ifg(tp, speed);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
++ if (speed & _2500bps)
++ ocp_data &= ~IDLE_SPDWN_EN;
++ else
++ ocp_data |= IDLE_SPDWN_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
++
++ if (speed & _1000bps)
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x11);
++ else if (speed & _500bps)
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x3d);
++
++ if (tp->udev->speed == USB_SPEED_HIGH) {
++ /* USB 0xb45e[3:0] l1_nyet_hird */
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_L1_CTRL);
++ ocp_data &= ~0xf;
++ if (is_flow_control(speed))
++ ocp_data |= 0xf;
++ else
++ ocp_data |= 0x1;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_L1_CTRL, ocp_data);
++ }
++
++ return rtl_enable(tp);
++}
++
++static int rtl8156b_enable(struct r8152 *tp)
++{
++ u32 ocp_data;
++ u16 speed;
++
++ if (test_bit(RTL8152_UNPLUG, &tp->flags))
++ return -ENODEV;
++
++ set_tx_qlen(tp);
++ rtl_set_eee_plus(tp);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM);
++ ocp_data &= ~RX_AGGR_NUM_MASK;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM, ocp_data);
++
++ r8153_set_rx_early_timeout(tp);
++ r8153_set_rx_early_size(tp);
++
++ speed = rtl8152_get_speed(tp);
++ rtl_set_ifg(tp, speed);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
++ if (speed & _2500bps)
++ ocp_data &= ~IDLE_SPDWN_EN;
++ else
++ ocp_data |= IDLE_SPDWN_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
++
++ if (tp->udev->speed == USB_SPEED_HIGH) {
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_L1_CTRL);
++ ocp_data &= ~0xf;
++ if (is_flow_control(speed))
++ ocp_data |= 0xf;
++ else
++ ocp_data |= 0x1;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_L1_CTRL, ocp_data);
++ }
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK);
++ ocp_data &= ~FC_PATCH_TASK;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
++ usleep_range(1000, 2000);
++ ocp_data |= FC_PATCH_TASK;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
++
++ return rtl_enable(tp);
++}
++
+ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
+ u32 advertising)
+ {
+@@ -4891,58 +5505,73 @@ static int rtl8152_set_speed(struct r815
+
+ tp->mii.force_media = 1;
+ } else {
+- u16 anar, tmp1;
++ u16 orig, new1;
+ u32 support;
+
+ support = RTL_ADVERTISED_10_HALF | RTL_ADVERTISED_10_FULL |
+ RTL_ADVERTISED_100_HALF | RTL_ADVERTISED_100_FULL;
+
+- if (tp->mii.supports_gmii)
++ if (tp->mii.supports_gmii) {
+ support |= RTL_ADVERTISED_1000_FULL;
+
++ if (tp->support_2500full)
++ support |= RTL_ADVERTISED_2500_FULL;
++ }
++
+ if (!(advertising & support))
+ return -EINVAL;
+
+- anar = r8152_mdio_read(tp, MII_ADVERTISE);
+- tmp1 = anar & ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
++ orig = r8152_mdio_read(tp, MII_ADVERTISE);
++ new1 = orig & ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
+ ADVERTISE_100HALF | ADVERTISE_100FULL);
+ if (advertising & RTL_ADVERTISED_10_HALF) {
+- tmp1 |= ADVERTISE_10HALF;
++ new1 |= ADVERTISE_10HALF;
+ tp->ups_info.speed_duplex = NWAY_10M_HALF;
+ }
+ if (advertising & RTL_ADVERTISED_10_FULL) {
+- tmp1 |= ADVERTISE_10FULL;
++ new1 |= ADVERTISE_10FULL;
+ tp->ups_info.speed_duplex = NWAY_10M_FULL;
+ }
+
+ if (advertising & RTL_ADVERTISED_100_HALF) {
+- tmp1 |= ADVERTISE_100HALF;
++ new1 |= ADVERTISE_100HALF;
+ tp->ups_info.speed_duplex = NWAY_100M_HALF;
+ }
+ if (advertising & RTL_ADVERTISED_100_FULL) {
+- tmp1 |= ADVERTISE_100FULL;
++ new1 |= ADVERTISE_100FULL;
+ tp->ups_info.speed_duplex = NWAY_100M_FULL;
+ }
+
+- if (anar != tmp1) {
+- r8152_mdio_write(tp, MII_ADVERTISE, tmp1);
+- tp->mii.advertising = tmp1;
++ if (orig != new1) {
++ r8152_mdio_write(tp, MII_ADVERTISE, new1);
++ tp->mii.advertising = new1;
+ }
+
+ if (tp->mii.supports_gmii) {
+- u16 gbcr;
+-
+- gbcr = r8152_mdio_read(tp, MII_CTRL1000);
+- tmp1 = gbcr & ~(ADVERTISE_1000FULL |
++ orig = r8152_mdio_read(tp, MII_CTRL1000);
++ new1 = orig & ~(ADVERTISE_1000FULL |
+ ADVERTISE_1000HALF);
+
+ if (advertising & RTL_ADVERTISED_1000_FULL) {
+- tmp1 |= ADVERTISE_1000FULL;
++ new1 |= ADVERTISE_1000FULL;
+ tp->ups_info.speed_duplex = NWAY_1000M_FULL;
+ }
+
+- if (gbcr != tmp1)
+- r8152_mdio_write(tp, MII_CTRL1000, tmp1);
++ if (orig != new1)
++ r8152_mdio_write(tp, MII_CTRL1000, new1);
++ }
++
++ if (tp->support_2500full) {
++ orig = ocp_reg_read(tp, OCP_10GBT_CTRL);
++ new1 = orig & ~MDIO_AN_10GBT_CTRL_ADV2_5G;
++
++ if (advertising & RTL_ADVERTISED_2500_FULL) {
++ new1 |= MDIO_AN_10GBT_CTRL_ADV2_5G;
++ tp->ups_info.speed_duplex = NWAY_2500M_FULL;
++ }
++
++ if (orig != new1)
++ ocp_reg_write(tp, OCP_10GBT_CTRL, new1);
+ }
+
+ bmcr = BMCR_ANENABLE | BMCR_ANRESTART;
+@@ -5098,6 +5727,253 @@ static void rtl8153b_down(struct r8152 *
+ r8153_aldps_en(tp, true);
+ }
+
++static void rtl8153c_change_mtu(struct r8152 *tp)
++{
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, mtu_to_size(tp->netdev->mtu));
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, 10 * 1024 / 64);
++
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, 512 / 64);
++
++ /* Adjust the tx fifo free credit full threshold, otherwise
++ * the fifo would be too small to send a jumbo frame packet.
++ */
++ if (tp->netdev->mtu < 8000)
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_FULL, 2048 / 8);
++ else
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_FULL, 900 / 8);
++}
++
++static void rtl8153c_up(struct r8152 *tp)
++{
++ u32 ocp_data;
++
++ if (test_bit(RTL8152_UNPLUG, &tp->flags))
++ return;
++
++ r8153b_u1u2en(tp, false);
++ r8153_u2p3en(tp, false);
++ r8153_aldps_en(tp, false);
++
++ rxdy_gated_en(tp, true);
++ r8153_teredo_off(tp);
++
++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
++ ocp_data &= ~RCR_ACPT_ALL;
++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
++
++ rtl8152_nic_reset(tp);
++ rtl_reset_bmu(tp);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
++ ocp_data &= ~NOW_IS_OOB;
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
++ ocp_data &= ~MCU_BORW_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
++
++ wait_oob_link_list_ready(tp);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
++ ocp_data |= RE_INIT_LL;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
++
++ wait_oob_link_list_ready(tp);
++
++ rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX);
++
++ rtl8153c_change_mtu(tp);
++
++ rtl8152_nic_reset(tp);
++
++ /* rx share fifo credit full threshold */
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, 0x02);
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_RXFIFO_FULL, 0x08);
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_NORMAL);
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_NORMAL);
++
++ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_B);
++
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34);
++ ocp_data |= BIT(8);
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data);
++
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
++ ocp_data &= ~PLA_MCU_SPDWN_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data);
++
++ r8153_aldps_en(tp, true);
++ r8153b_u1u2en(tp, true);
++}
++
++static inline u32 fc_pause_on_auto(struct r8152 *tp)
++{
++ return (ALIGN(mtu_to_size(tp->netdev->mtu), 1024) + 6 * 1024);
++}
++
++static inline u32 fc_pause_off_auto(struct r8152 *tp)
++{
++ return (ALIGN(mtu_to_size(tp->netdev->mtu), 1024) + 14 * 1024);
++}
++
++static void r8156_fc_parameter(struct r8152 *tp)
++{
++ u32 pause_on = tp->fc_pause_on ? tp->fc_pause_on : fc_pause_on_auto(tp);
++ u32 pause_off = tp->fc_pause_off ? tp->fc_pause_off : fc_pause_off_auto(tp);
++
++ switch (tp->version) {
++ case RTL_VER_10:
++ case RTL_VER_11:
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_FULL, pause_on / 8);
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_EMPTY, pause_off / 8);
++ break;
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_15:
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_FULL, pause_on / 16);
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_EMPTY, pause_off / 16);
++ break;
++ default:
++ break;
++ }
++}
++
++static void rtl8156_change_mtu(struct r8152 *tp)
++{
++ u32 rx_max_size = mtu_to_size(tp->netdev->mtu);
++
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, rx_max_size);
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO);
++ r8156_fc_parameter(tp);
++
++ /* TX share fifo free credit full threshold */
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, 512 / 64);
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_FULL,
++ ALIGN(rx_max_size + sizeof(struct tx_desc), 1024) / 16);
++}
++
++static void rtl8156_up(struct r8152 *tp)
++{
++ u32 ocp_data;
++
++ if (test_bit(RTL8152_UNPLUG, &tp->flags))
++ return;
++
++ r8153b_u1u2en(tp, false);
++ r8153_u2p3en(tp, false);
++ r8153_aldps_en(tp, false);
++
++ rxdy_gated_en(tp, true);
++ r8153_teredo_off(tp);
++
++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
++ ocp_data &= ~RCR_ACPT_ALL;
++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
++
++ rtl8152_nic_reset(tp);
++ rtl_reset_bmu(tp);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
++ ocp_data &= ~NOW_IS_OOB;
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
++ ocp_data &= ~MCU_BORW_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
++
++ rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX);
++
++ rtl8156_change_mtu(tp);
++
++ switch (tp->version) {
++ case RTL_TEST_01:
++ case RTL_VER_10:
++ case RTL_VER_11:
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_BMU_CONFIG);
++ ocp_data |= ACT_ODMA;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ocp_data);
++ break;
++ default:
++ break;
++ }
++
++ /* share FIFO settings */
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_FULL);
++ ocp_data &= ~RXFIFO_FULL_MASK;
++ ocp_data |= 0x08;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_FULL, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
++ ocp_data &= ~PLA_MCU_SPDWN_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION);
++ ocp_data &= ~(RG_PWRDN_EN | ALL_SPEED_OFF);
++ ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, ocp_data);
++
++ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, 0x00600400);
++
++ if (tp->saved_wolopts != __rtl_get_wol(tp)) {
++ netif_warn(tp, ifup, tp->netdev, "wol setting is changed\n");
++ __rtl_set_wol(tp, tp->saved_wolopts);
++ }
++
++ r8153_aldps_en(tp, true);
++ r8153_u2p3en(tp, true);
++
++ if (tp->udev->speed >= USB_SPEED_SUPER)
++ r8153b_u1u2en(tp, true);
++}
++
++static void rtl8156_down(struct r8152 *tp)
++{
++ u32 ocp_data;
++
++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++ rtl_drop_queued_tx(tp);
++ return;
++ }
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
++ ocp_data |= PLA_MCU_SPDWN_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data);
++
++ r8153b_u1u2en(tp, false);
++ r8153_u2p3en(tp, false);
++ r8153b_power_cut_en(tp, false);
++ r8153_aldps_en(tp, false);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
++ ocp_data &= ~NOW_IS_OOB;
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
++
++ rtl_disable(tp);
++ rtl_reset_bmu(tp);
++
++ /* Clear teredo wake event. bit[15:8] is the teredo wakeup
++ * type. Set it to zero. bits[7:0] are the W1C bits about
++ * the events. Set them to all 1 to clear them.
++ */
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_WAKE_BASE, 0x00ff);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
++ ocp_data |= NOW_IS_OOB;
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
++
++ rtl_rx_vlan_en(tp, true);
++ rxdy_gated_en(tp, false);
++
++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
++ ocp_data |= RCR_APM | RCR_AM | RCR_AB;
++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
++
++ r8153_aldps_en(tp, true);
++}
++
+ static bool rtl8152_in_nway(struct r8152 *tp)
+ {
+ u16 nway_state;
+@@ -5128,7 +6004,7 @@ static void set_carrier(struct r8152 *tp
+ {
+ struct net_device *netdev = tp->netdev;
+ struct napi_struct *napi = &tp->napi;
+- u8 speed;
++ u16 speed;
+
+ speed = rtl8152_get_speed(tp);
+
+@@ -5141,7 +6017,7 @@ static void set_carrier(struct r8152 *tp
+ rtl_start_rx(tp);
+ clear_bit(RTL8152_SET_RX_MODE, &tp->flags);
+ _rtl8152_set_rx_mode(netdev);
+- napi_enable(&tp->napi);
++ napi_enable(napi);
+ netif_wake_queue(netdev);
+ netif_info(tp, link, netdev, "carrier on\n");
+ } else if (netif_queue_stopped(netdev) &&
+@@ -5521,14 +6397,9 @@ static void r8153_init(struct r8152 *tp)
+
+ ocp_write_word(tp, MCU_TYPE_USB, USB_CONNECT_TIMER, 0x0001);
+
+- /* MAC clock speed down */
+- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, 0);
+- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, 0);
+- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, 0);
+- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, 0);
+-
+ r8153_power_cut_en(tp, false);
+ rtl_runtime_suspend_enable(tp, false);
++ r8153_mac_clk_speed_down(tp, false);
+ r8153_u1u2en(tp, true);
+ usb_enable_lpm(tp->udev);
+
+@@ -5621,9 +6492,7 @@ static void r8153b_init(struct r8152 *tp
+ usb_enable_lpm(tp->udev);
+
+ /* MAC clock speed down */
+- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2);
+- ocp_data |= MAC_CLK_SPDWN_EN;
+- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data);
++ r8153_mac_clk_speed_down(tp, true);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
+ ocp_data &= ~PLA_MCU_SPDWN_EN;
+@@ -5652,6 +6521,1069 @@ static void r8153b_init(struct r8152 *tp
+ r8152_led_configuration(tp);
+ }
+
++static void r8153c_init(struct r8152 *tp)
++{
++ u32 ocp_data;
++ u16 data;
++ int i;
++
++ if (test_bit(RTL8152_UNPLUG, &tp->flags))
++ return;
++
++ r8153b_u1u2en(tp, false);
++
++ /* Disable spi_en */
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5);
++ ocp_data &= ~BIT(3);
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG5, ocp_data);
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, 0xcbf0);
++ ocp_data |= BIT(1);
++ ocp_write_word(tp, MCU_TYPE_USB, 0xcbf0, ocp_data);
++
++ for (i = 0; i < 500; i++) {
++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
++ AUTOLOAD_DONE)
++ break;
++
++ msleep(20);
++ if (test_bit(RTL8152_UNPLUG, &tp->flags))
++ return;
++ }
++
++ data = r8153_phy_status(tp, 0);
++
++ data = r8152_mdio_read(tp, MII_BMCR);
++ if (data & BMCR_PDOWN) {
++ data &= ~BMCR_PDOWN;
++ r8152_mdio_write(tp, MII_BMCR, data);
++ }
++
++ data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
++
++ r8153_u2p3en(tp, false);
++
++ /* MSC timer = 0xfff * 8ms = 32760 ms */
++ ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
++
++ r8153b_power_cut_en(tp, false);
++ r8153c_ups_en(tp, false);
++ r8153_queue_wake(tp, false);
++ rtl_runtime_suspend_enable(tp, false);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS);
++ if (rtl8152_get_speed(tp) & LINK_STATUS)
++ ocp_data |= CUR_LINK_OK;
++ else
++ ocp_data &= ~CUR_LINK_OK;
++
++ ocp_data |= POLL_LINK_CHG;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data);
++
++ r8153b_u1u2en(tp, true);
++
++ usb_enable_lpm(tp->udev);
++
++ /* MAC clock speed down */
++ r8153_mac_clk_speed_down(tp, true);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
++ ocp_data &= ~BIT(7);
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
++
++ set_bit(GREEN_ETHERNET, &tp->flags);
++
++ /* rx aggregation */
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
++ ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
++ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
++
++ rtl_tally_reset(tp);
++
++ tp->coalesce = 15000; /* 15 us */
++}
++
++static void r8156_hw_phy_cfg(struct r8152 *tp)
++{
++ u32 ocp_data;
++ u16 data;
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
++ if (ocp_data & PCUT_STATUS) {
++ ocp_data &= ~PCUT_STATUS;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
++ }
++
++ data = r8153_phy_status(tp, 0);
++ switch (data) {
++ case PHY_STAT_EXT_INIT:
++ rtl8152_apply_firmware(tp, true);
++
++ data = ocp_reg_read(tp, 0xa468);
++ data &= ~(BIT(3) | BIT(1));
++ ocp_reg_write(tp, 0xa468, data);
++ break;
++ case PHY_STAT_LAN_ON:
++ case PHY_STAT_PWRDN:
++ default:
++ rtl8152_apply_firmware(tp, false);
++ break;
++ }
++
++ /* disable ALDPS before updating the PHY parameters */
++ r8153_aldps_en(tp, false);
++
++ /* disable EEE before updating the PHY parameters */
++ rtl_eee_enable(tp, false);
++
++ data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
++ WARN_ON_ONCE(data != PHY_STAT_LAN_ON);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR);
++ ocp_data |= PFM_PWM_SWITCH;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data);
++
++ switch (tp->version) {
++ case RTL_VER_10:
++ data = ocp_reg_read(tp, 0xad40);
++ data &= ~0x3ff;
++ data |= BIT(7) | BIT(2);
++ ocp_reg_write(tp, 0xad40, data);
++
++ data = ocp_reg_read(tp, 0xad4e);
++ data |= BIT(4);
++ ocp_reg_write(tp, 0xad4e, data);
++ data = ocp_reg_read(tp, 0xad16);
++ data &= ~0x3ff;
++ data |= 0x6;
++ ocp_reg_write(tp, 0xad16, data);
++ data = ocp_reg_read(tp, 0xad32);
++ data &= ~0x3f;
++ data |= 6;
++ ocp_reg_write(tp, 0xad32, data);
++ data = ocp_reg_read(tp, 0xac08);
++ data &= ~(BIT(12) | BIT(8));
++ ocp_reg_write(tp, 0xac08, data);
++ data = ocp_reg_read(tp, 0xac8a);
++ data |= BIT(12) | BIT(13) | BIT(14);
++ data &= ~BIT(15);
++ ocp_reg_write(tp, 0xac8a, data);
++ data = ocp_reg_read(tp, 0xad18);
++ data |= BIT(10);
++ ocp_reg_write(tp, 0xad18, data);
++ data = ocp_reg_read(tp, 0xad1a);
++ data |= 0x3ff;
++ ocp_reg_write(tp, 0xad1a, data);
++ data = ocp_reg_read(tp, 0xad1c);
++ data |= 0x3ff;
++ ocp_reg_write(tp, 0xad1c, data);
++
++ data = sram_read(tp, 0x80ea);
++ data &= ~0xff00;
++ data |= 0xc400;
++ sram_write(tp, 0x80ea, data);
++ data = sram_read(tp, 0x80eb);
++ data &= ~0x0700;
++ data |= 0x0300;
++ sram_write(tp, 0x80eb, data);
++ data = sram_read(tp, 0x80f8);
++ data &= ~0xff00;
++ data |= 0x1c00;
++ sram_write(tp, 0x80f8, data);
++ data = sram_read(tp, 0x80f1);
++ data &= ~0xff00;
++ data |= 0x3000;
++ sram_write(tp, 0x80f1, data);
++
++ data = sram_read(tp, 0x80fe);
++ data &= ~0xff00;
++ data |= 0xa500;
++ sram_write(tp, 0x80fe, data);
++ data = sram_read(tp, 0x8102);
++ data &= ~0xff00;
++ data |= 0x5000;
++ sram_write(tp, 0x8102, data);
++ data = sram_read(tp, 0x8015);
++ data &= ~0xff00;
++ data |= 0x3300;
++ sram_write(tp, 0x8015, data);
++ data = sram_read(tp, 0x8100);
++ data &= ~0xff00;
++ data |= 0x7000;
++ sram_write(tp, 0x8100, data);
++ data = sram_read(tp, 0x8014);
++ data &= ~0xff00;
++ data |= 0xf000;
++ sram_write(tp, 0x8014, data);
++ data = sram_read(tp, 0x8016);
++ data &= ~0xff00;
++ data |= 0x6500;
++ sram_write(tp, 0x8016, data);
++ data = sram_read(tp, 0x80dc);
++ data &= ~0xff00;
++ data |= 0xed00;
++ sram_write(tp, 0x80dc, data);
++ data = sram_read(tp, 0x80df);
++ data |= BIT(8);
++ sram_write(tp, 0x80df, data);
++ data = sram_read(tp, 0x80e1);
++ data &= ~BIT(8);
++ sram_write(tp, 0x80e1, data);
++
++ data = ocp_reg_read(tp, 0xbf06);
++ data &= ~0x003f;
++ data |= 0x0038;
++ ocp_reg_write(tp, 0xbf06, data);
++
++ sram_write(tp, 0x819f, 0xddb6);
++
++ ocp_reg_write(tp, 0xbc34, 0x5555);
++ data = ocp_reg_read(tp, 0xbf0a);
++ data &= ~0x0e00;
++ data |= 0x0a00;
++ ocp_reg_write(tp, 0xbf0a, data);
++
++ data = ocp_reg_read(tp, 0xbd2c);
++ data &= ~BIT(13);
++ ocp_reg_write(tp, 0xbd2c, data);
++ break;
++ case RTL_VER_11:
++ data = ocp_reg_read(tp, 0xad16);
++ data |= 0x3ff;
++ ocp_reg_write(tp, 0xad16, data);
++ data = ocp_reg_read(tp, 0xad32);
++ data &= ~0x3f;
++ data |= 6;
++ ocp_reg_write(tp, 0xad32, data);
++ data = ocp_reg_read(tp, 0xac08);
++ data &= ~(BIT(12) | BIT(8));
++ ocp_reg_write(tp, 0xac08, data);
++ data = ocp_reg_read(tp, 0xacc0);
++ data &= ~0x3;
++ data |= BIT(1);
++ ocp_reg_write(tp, 0xacc0, data);
++ data = ocp_reg_read(tp, 0xad40);
++ data &= ~0xe7;
++ data |= BIT(6) | BIT(2);
++ ocp_reg_write(tp, 0xad40, data);
++ data = ocp_reg_read(tp, 0xac14);
++ data &= ~BIT(7);
++ ocp_reg_write(tp, 0xac14, data);
++ data = ocp_reg_read(tp, 0xac80);
++ data &= ~(BIT(8) | BIT(9));
++ ocp_reg_write(tp, 0xac80, data);
++ data = ocp_reg_read(tp, 0xac5e);
++ data &= ~0x7;
++ data |= BIT(1);
++ ocp_reg_write(tp, 0xac5e, data);
++ ocp_reg_write(tp, 0xad4c, 0x00a8);
++ ocp_reg_write(tp, 0xac5c, 0x01ff);
++ data = ocp_reg_read(tp, 0xac8a);
++ data &= ~0xf0;
++ data |= BIT(4) | BIT(5);
++ ocp_reg_write(tp, 0xac8a, data);
++ ocp_reg_write(tp, 0xb87c, 0x8157);
++ data = ocp_reg_read(tp, 0xb87e);
++ data &= ~0xff00;
++ data |= 0x0500;
++ ocp_reg_write(tp, 0xb87e, data);
++ ocp_reg_write(tp, 0xb87c, 0x8159);
++ data = ocp_reg_read(tp, 0xb87e);
++ data &= ~0xff00;
++ data |= 0x0700;
++ ocp_reg_write(tp, 0xb87e, data);
++
++ /* AAGC */
++ ocp_reg_write(tp, 0xb87c, 0x80a2);
++ ocp_reg_write(tp, 0xb87e, 0x0153);
++ ocp_reg_write(tp, 0xb87c, 0x809c);
++ ocp_reg_write(tp, 0xb87e, 0x0153);
++
++ /* EEE parameter */
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS_2P5G, 0x0056);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_USB_CFG);
++ ocp_data |= EN_XG_LIP | EN_G_LIP;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_USB_CFG, ocp_data);
++
++ sram_write(tp, 0x8257, 0x020f); /* XG PLL */
++ sram_write(tp, 0x80ea, 0x7843); /* GIGA Master */
++
++ if (rtl_phy_patch_request(tp, true, true))
++ return;
++
++ /* Advance EEE */
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
++ ocp_data |= EEE_SPDWN_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
++
++ data = ocp_reg_read(tp, OCP_DOWN_SPEED);
++ data &= ~(EN_EEE_100 | EN_EEE_1000);
++ data |= EN_10M_CLKDIV;
++ ocp_reg_write(tp, OCP_DOWN_SPEED, data);
++ tp->ups_info._10m_ckdiv = true;
++ tp->ups_info.eee_plloff_100 = false;
++ tp->ups_info.eee_plloff_giga = false;
++
++ data = ocp_reg_read(tp, OCP_POWER_CFG);
++ data &= ~EEE_CLKDIV_EN;
++ ocp_reg_write(tp, OCP_POWER_CFG, data);
++ tp->ups_info.eee_ckdiv = false;
++
++ ocp_reg_write(tp, OCP_SYSCLK_CFG, 0);
++ ocp_reg_write(tp, OCP_SYSCLK_CFG, sysclk_div_expo(5));
++ tp->ups_info._250m_ckdiv = false;
++
++ rtl_phy_patch_request(tp, false, true);
++
++ /* enable ADC Ibias Cal */
++ data = ocp_reg_read(tp, 0xd068);
++ data |= BIT(13);
++ ocp_reg_write(tp, 0xd068, data);
++
++ /* enable Thermal Sensor */
++ data = sram_read(tp, 0x81a2);
++ data &= ~BIT(8);
++ sram_write(tp, 0x81a2, data);
++ data = ocp_reg_read(tp, 0xb54c);
++ data &= ~0xff00;
++ data |= 0xdb00;
++ ocp_reg_write(tp, 0xb54c, data);
++
++ /* Nway 2.5G Lite */
++ data = ocp_reg_read(tp, 0xa454);
++ data &= ~BIT(0);
++ ocp_reg_write(tp, 0xa454, data);
++
++ /* CS DSP solution */
++ data = ocp_reg_read(tp, OCP_10GBT_CTRL);
++ data |= RTL_ADV2_5G_F_R;
++ ocp_reg_write(tp, OCP_10GBT_CTRL, data);
++ data = ocp_reg_read(tp, 0xad4e);
++ data &= ~BIT(4);
++ ocp_reg_write(tp, 0xad4e, data);
++ data = ocp_reg_read(tp, 0xa86a);
++ data &= ~BIT(0);
++ ocp_reg_write(tp, 0xa86a, data);
++
++ /* MDI SWAP */
++ if ((ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CFG) & MID_REVERSE) &&
++ (ocp_reg_read(tp, 0xd068) & BIT(1))) {
++ u16 swap_a, swap_b;
++
++ data = ocp_reg_read(tp, 0xd068);
++ data &= ~0x1f;
++ data |= 0x1; /* p0 */
++ ocp_reg_write(tp, 0xd068, data);
++ swap_a = ocp_reg_read(tp, 0xd06a);
++ data &= ~0x18;
++ data |= 0x18; /* p3 */
++ ocp_reg_write(tp, 0xd068, data);
++ swap_b = ocp_reg_read(tp, 0xd06a);
++ data &= ~0x18; /* p0 */
++ ocp_reg_write(tp, 0xd068, data);
++ ocp_reg_write(tp, 0xd06a,
++ (swap_a & ~0x7ff) | (swap_b & 0x7ff));
++ data |= 0x18; /* p3 */
++ ocp_reg_write(tp, 0xd068, data);
++ ocp_reg_write(tp, 0xd06a,
++ (swap_b & ~0x7ff) | (swap_a & 0x7ff));
++ data &= ~0x18;
++ data |= 0x08; /* p1 */
++ ocp_reg_write(tp, 0xd068, data);
++ swap_a = ocp_reg_read(tp, 0xd06a);
++ data &= ~0x18;
++ data |= 0x10; /* p2 */
++ ocp_reg_write(tp, 0xd068, data);
++ swap_b = ocp_reg_read(tp, 0xd06a);
++ data &= ~0x18;
++ data |= 0x08; /* p1 */
++ ocp_reg_write(tp, 0xd068, data);
++ ocp_reg_write(tp, 0xd06a,
++ (swap_a & ~0x7ff) | (swap_b & 0x7ff));
++ data &= ~0x18;
++ data |= 0x10; /* p2 */
++ ocp_reg_write(tp, 0xd068, data);
++ ocp_reg_write(tp, 0xd06a,
++ (swap_b & ~0x7ff) | (swap_a & 0x7ff));
++ swap_a = ocp_reg_read(tp, 0xbd5a);
++ swap_b = ocp_reg_read(tp, 0xbd5c);
++ ocp_reg_write(tp, 0xbd5a, (swap_a & ~0x1f1f) |
++ ((swap_b & 0x1f) << 8) |
++ ((swap_b >> 8) & 0x1f));
++ ocp_reg_write(tp, 0xbd5c, (swap_b & ~0x1f1f) |
++ ((swap_a & 0x1f) << 8) |
++ ((swap_a >> 8) & 0x1f));
++ swap_a = ocp_reg_read(tp, 0xbc18);
++ swap_b = ocp_reg_read(tp, 0xbc1a);
++ ocp_reg_write(tp, 0xbc18, (swap_a & ~0x1f1f) |
++ ((swap_b & 0x1f) << 8) |
++ ((swap_b >> 8) & 0x1f));
++ ocp_reg_write(tp, 0xbc1a, (swap_b & ~0x1f1f) |
++ ((swap_a & 0x1f) << 8) |
++ ((swap_a >> 8) & 0x1f));
++ }
++ break;
++ default:
++ break;
++ }
++
++ rtl_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
++
++ data = ocp_reg_read(tp, 0xa428);
++ data &= ~BIT(9);
++ ocp_reg_write(tp, 0xa428, data);
++ data = ocp_reg_read(tp, 0xa5ea);
++ data &= ~BIT(0);
++ ocp_reg_write(tp, 0xa5ea, data);
++ tp->ups_info.lite_mode = 0;
++
++ if (tp->eee_en)
++ rtl_eee_enable(tp, true);
++
++ r8153_aldps_en(tp, true);
++ r8152b_enable_fc(tp);
++ r8153_u2p3en(tp, true);
++
++ set_bit(PHY_RESET, &tp->flags);
++}
++
++static void r8156b_hw_phy_cfg(struct r8152 *tp)
++{
++ u32 ocp_data;
++ u16 data;
++
++ switch (tp->version) {
++ case RTL_VER_12:
++ ocp_reg_write(tp, 0xbf86, 0x9000);
++ data = ocp_reg_read(tp, 0xc402);
++ data |= BIT(10);
++ ocp_reg_write(tp, 0xc402, data);
++ data &= ~BIT(10);
++ ocp_reg_write(tp, 0xc402, data);
++ ocp_reg_write(tp, 0xbd86, 0x1010);
++ ocp_reg_write(tp, 0xbd88, 0x1010);
++ data = ocp_reg_read(tp, 0xbd4e);
++ data &= ~(BIT(10) | BIT(11));
++ data |= BIT(11);
++ ocp_reg_write(tp, 0xbd4e, data);
++ data = ocp_reg_read(tp, 0xbf46);
++ data &= ~0xf00;
++ data |= 0x700;
++ ocp_reg_write(tp, 0xbf46, data);
++ break;
++ case RTL_VER_13:
++ case RTL_VER_15:
++ r8156b_wait_loading_flash(tp);
++ break;
++ default:
++ break;
++ }
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
++ if (ocp_data & PCUT_STATUS) {
++ ocp_data &= ~PCUT_STATUS;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
++ }
++
++ data = r8153_phy_status(tp, 0);
++ switch (data) {
++ case PHY_STAT_EXT_INIT:
++ rtl8152_apply_firmware(tp, true);
++
++ data = ocp_reg_read(tp, 0xa466);
++ data &= ~BIT(0);
++ ocp_reg_write(tp, 0xa466, data);
++
++ data = ocp_reg_read(tp, 0xa468);
++ data &= ~(BIT(3) | BIT(1));
++ ocp_reg_write(tp, 0xa468, data);
++ break;
++ case PHY_STAT_LAN_ON:
++ case PHY_STAT_PWRDN:
++ default:
++ rtl8152_apply_firmware(tp, false);
++ break;
++ }
++
++ data = r8152_mdio_read(tp, MII_BMCR);
++ if (data & BMCR_PDOWN) {
++ data &= ~BMCR_PDOWN;
++ r8152_mdio_write(tp, MII_BMCR, data);
++ }
++
++ /* disable ALDPS before updating the PHY parameters */
++ r8153_aldps_en(tp, false);
++
++ /* disable EEE before updating the PHY parameters */
++ rtl_eee_enable(tp, false);
++
++ data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
++ WARN_ON_ONCE(data != PHY_STAT_LAN_ON);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR);
++ ocp_data |= PFM_PWM_SWITCH;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data);
++
++ switch (tp->version) {
++ case RTL_VER_12:
++ data = ocp_reg_read(tp, 0xbc08);
++ data |= BIT(3) | BIT(2);
++ ocp_reg_write(tp, 0xbc08, data);
++
++ data = sram_read(tp, 0x8fff);
++ data &= ~0xff00;
++ data |= 0x0400;
++ sram_write(tp, 0x8fff, data);
++
++ data = ocp_reg_read(tp, 0xacda);
++ data |= 0xff00;
++ ocp_reg_write(tp, 0xacda, data);
++ data = ocp_reg_read(tp, 0xacde);
++ data |= 0xf000;
++ ocp_reg_write(tp, 0xacde, data);
++ ocp_reg_write(tp, 0xac8c, 0x0ffc);
++ ocp_reg_write(tp, 0xac46, 0xb7b4);
++ ocp_reg_write(tp, 0xac50, 0x0fbc);
++ ocp_reg_write(tp, 0xac3c, 0x9240);
++ ocp_reg_write(tp, 0xac4e, 0x0db4);
++ ocp_reg_write(tp, 0xacc6, 0x0707);
++ ocp_reg_write(tp, 0xacc8, 0xa0d3);
++ ocp_reg_write(tp, 0xad08, 0x0007);
++
++ ocp_reg_write(tp, 0xb87c, 0x8560);
++ ocp_reg_write(tp, 0xb87e, 0x19cc);
++ ocp_reg_write(tp, 0xb87c, 0x8562);
++ ocp_reg_write(tp, 0xb87e, 0x19cc);
++ ocp_reg_write(tp, 0xb87c, 0x8564);
++ ocp_reg_write(tp, 0xb87e, 0x19cc);
++ ocp_reg_write(tp, 0xb87c, 0x8566);
++ ocp_reg_write(tp, 0xb87e, 0x147d);
++ ocp_reg_write(tp, 0xb87c, 0x8568);
++ ocp_reg_write(tp, 0xb87e, 0x147d);
++ ocp_reg_write(tp, 0xb87c, 0x856a);
++ ocp_reg_write(tp, 0xb87e, 0x147d);
++ ocp_reg_write(tp, 0xb87c, 0x8ffe);
++ ocp_reg_write(tp, 0xb87e, 0x0907);
++ ocp_reg_write(tp, 0xb87c, 0x80d6);
++ ocp_reg_write(tp, 0xb87e, 0x2801);
++ ocp_reg_write(tp, 0xb87c, 0x80f2);
++ ocp_reg_write(tp, 0xb87e, 0x2801);
++ ocp_reg_write(tp, 0xb87c, 0x80f4);
++ ocp_reg_write(tp, 0xb87e, 0x6077);
++ ocp_reg_write(tp, 0xb506, 0x01e7);
++
++ ocp_reg_write(tp, 0xb87c, 0x8013);
++ ocp_reg_write(tp, 0xb87e, 0x0700);
++ ocp_reg_write(tp, 0xb87c, 0x8fb9);
++ ocp_reg_write(tp, 0xb87e, 0x2801);
++ ocp_reg_write(tp, 0xb87c, 0x8fba);
++ ocp_reg_write(tp, 0xb87e, 0x0100);
++ ocp_reg_write(tp, 0xb87c, 0x8fbc);
++ ocp_reg_write(tp, 0xb87e, 0x1900);
++ ocp_reg_write(tp, 0xb87c, 0x8fbe);
++ ocp_reg_write(tp, 0xb87e, 0xe100);
++ ocp_reg_write(tp, 0xb87c, 0x8fc0);
++ ocp_reg_write(tp, 0xb87e, 0x0800);
++ ocp_reg_write(tp, 0xb87c, 0x8fc2);
++ ocp_reg_write(tp, 0xb87e, 0xe500);
++ ocp_reg_write(tp, 0xb87c, 0x8fc4);
++ ocp_reg_write(tp, 0xb87e, 0x0f00);
++ ocp_reg_write(tp, 0xb87c, 0x8fc6);
++ ocp_reg_write(tp, 0xb87e, 0xf100);
++ ocp_reg_write(tp, 0xb87c, 0x8fc8);
++ ocp_reg_write(tp, 0xb87e, 0x0400);
++ ocp_reg_write(tp, 0xb87c, 0x8fca);
++ ocp_reg_write(tp, 0xb87e, 0xf300);
++ ocp_reg_write(tp, 0xb87c, 0x8fcc);
++ ocp_reg_write(tp, 0xb87e, 0xfd00);
++ ocp_reg_write(tp, 0xb87c, 0x8fce);
++ ocp_reg_write(tp, 0xb87e, 0xff00);
++ ocp_reg_write(tp, 0xb87c, 0x8fd0);
++ ocp_reg_write(tp, 0xb87e, 0xfb00);
++ ocp_reg_write(tp, 0xb87c, 0x8fd2);
++ ocp_reg_write(tp, 0xb87e, 0x0100);
++ ocp_reg_write(tp, 0xb87c, 0x8fd4);
++ ocp_reg_write(tp, 0xb87e, 0xf400);
++ ocp_reg_write(tp, 0xb87c, 0x8fd6);
++ ocp_reg_write(tp, 0xb87e, 0xff00);
++ ocp_reg_write(tp, 0xb87c, 0x8fd8);
++ ocp_reg_write(tp, 0xb87e, 0xf600);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_USB_CFG);
++ ocp_data |= EN_XG_LIP | EN_G_LIP;
++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_USB_CFG, ocp_data);
++ ocp_reg_write(tp, 0xb87c, 0x813d);
++ ocp_reg_write(tp, 0xb87e, 0x390e);
++ ocp_reg_write(tp, 0xb87c, 0x814f);
++ ocp_reg_write(tp, 0xb87e, 0x790e);
++ ocp_reg_write(tp, 0xb87c, 0x80b0);
++ ocp_reg_write(tp, 0xb87e, 0x0f31);
++ data = ocp_reg_read(tp, 0xbf4c);
++ data |= BIT(1);
++ ocp_reg_write(tp, 0xbf4c, data);
++ data = ocp_reg_read(tp, 0xbcca);
++ data |= BIT(9) | BIT(8);
++ ocp_reg_write(tp, 0xbcca, data);
++ ocp_reg_write(tp, 0xb87c, 0x8141);
++ ocp_reg_write(tp, 0xb87e, 0x320e);
++ ocp_reg_write(tp, 0xb87c, 0x8153);
++ ocp_reg_write(tp, 0xb87e, 0x720e);
++ ocp_reg_write(tp, 0xb87c, 0x8529);
++ ocp_reg_write(tp, 0xb87e, 0x050e);
++ data = ocp_reg_read(tp, OCP_EEE_CFG);
++ data &= ~CTAP_SHORT_EN;
++ ocp_reg_write(tp, OCP_EEE_CFG, data);
++
++ sram_write(tp, 0x816c, 0xc4a0);
++ sram_write(tp, 0x8170, 0xc4a0);
++ sram_write(tp, 0x8174, 0x04a0);
++ sram_write(tp, 0x8178, 0x04a0);
++ sram_write(tp, 0x817c, 0x0719);
++ sram_write(tp, 0x8ff4, 0x0400);
++ sram_write(tp, 0x8ff1, 0x0404);
++
++ ocp_reg_write(tp, 0xbf4a, 0x001b);
++ ocp_reg_write(tp, 0xb87c, 0x8033);
++ ocp_reg_write(tp, 0xb87e, 0x7c13);
++ ocp_reg_write(tp, 0xb87c, 0x8037);
++ ocp_reg_write(tp, 0xb87e, 0x7c13);
++ ocp_reg_write(tp, 0xb87c, 0x803b);
++ ocp_reg_write(tp, 0xb87e, 0xfc32);
++ ocp_reg_write(tp, 0xb87c, 0x803f);
++ ocp_reg_write(tp, 0xb87e, 0x7c13);
++ ocp_reg_write(tp, 0xb87c, 0x8043);
++ ocp_reg_write(tp, 0xb87e, 0x7c13);
++ ocp_reg_write(tp, 0xb87c, 0x8047);
++ ocp_reg_write(tp, 0xb87e, 0x7c13);
++
++ ocp_reg_write(tp, 0xb87c, 0x8145);
++ ocp_reg_write(tp, 0xb87e, 0x370e);
++ ocp_reg_write(tp, 0xb87c, 0x8157);
++ ocp_reg_write(tp, 0xb87e, 0x770e);
++ ocp_reg_write(tp, 0xb87c, 0x8169);
++ ocp_reg_write(tp, 0xb87e, 0x0d0a);
++ ocp_reg_write(tp, 0xb87c, 0x817b);
++ ocp_reg_write(tp, 0xb87e, 0x1d0a);
++
++ data = sram_read(tp, 0x8217);
++ data &= ~0xff00;
++ data |= 0x5000;
++ sram_write(tp, 0x8217, data);
++ data = sram_read(tp, 0x821a);
++ data &= ~0xff00;
++ data |= 0x5000;
++ sram_write(tp, 0x821a, data);
++ sram_write(tp, 0x80da, 0x0403);
++ data = sram_read(tp, 0x80dc);
++ data &= ~0xff00;
++ data |= 0x1000;
++ sram_write(tp, 0x80dc, data);
++ sram_write(tp, 0x80b3, 0x0384);
++ sram_write(tp, 0x80b7, 0x2007);
++ data = sram_read(tp, 0x80ba);
++ data &= ~0xff00;
++ data |= 0x6c00;
++ sram_write(tp, 0x80ba, data);
++ sram_write(tp, 0x80b5, 0xf009);
++ data = sram_read(tp, 0x80bd);
++ data &= ~0xff00;
++ data |= 0x9f00;
++ sram_write(tp, 0x80bd, data);
++ sram_write(tp, 0x80c7, 0xf083);
++ sram_write(tp, 0x80dd, 0x03f0);
++ data = sram_read(tp, 0x80df);
++ data &= ~0xff00;
++ data |= 0x1000;
++ sram_write(tp, 0x80df, data);
++ sram_write(tp, 0x80cb, 0x2007);
++ data = sram_read(tp, 0x80ce);
++ data &= ~0xff00;
++ data |= 0x6c00;
++ sram_write(tp, 0x80ce, data);
++ sram_write(tp, 0x80c9, 0x8009);
++ data = sram_read(tp, 0x80d1);
++ data &= ~0xff00;
++ data |= 0x8000;
++ sram_write(tp, 0x80d1, data);
++ sram_write(tp, 0x80a3, 0x200a);
++ sram_write(tp, 0x80a5, 0xf0ad);
++ sram_write(tp, 0x809f, 0x6073);
++ sram_write(tp, 0x80a1, 0x000b);
++ data = sram_read(tp, 0x80a9);
++ data &= ~0xff00;
++ data |= 0xc000;
++ sram_write(tp, 0x80a9, data);
++
++ if (rtl_phy_patch_request(tp, true, true))
++ return;
++
++ data = ocp_reg_read(tp, 0xb896);
++ data &= ~BIT(0);
++ ocp_reg_write(tp, 0xb896, data);
++ data = ocp_reg_read(tp, 0xb892);
++ data &= ~0xff00;
++ ocp_reg_write(tp, 0xb892, data);
++ ocp_reg_write(tp, 0xb88e, 0xc23e);
++ ocp_reg_write(tp, 0xb890, 0x0000);
++ ocp_reg_write(tp, 0xb88e, 0xc240);
++ ocp_reg_write(tp, 0xb890, 0x0103);
++ ocp_reg_write(tp, 0xb88e, 0xc242);
++ ocp_reg_write(tp, 0xb890, 0x0507);
++ ocp_reg_write(tp, 0xb88e, 0xc244);
++ ocp_reg_write(tp, 0xb890, 0x090b);
++ ocp_reg_write(tp, 0xb88e, 0xc246);
++ ocp_reg_write(tp, 0xb890, 0x0c0e);
++ ocp_reg_write(tp, 0xb88e, 0xc248);
++ ocp_reg_write(tp, 0xb890, 0x1012);
++ ocp_reg_write(tp, 0xb88e, 0xc24a);
++ ocp_reg_write(tp, 0xb890, 0x1416);
++ data = ocp_reg_read(tp, 0xb896);
++ data |= BIT(0);
++ ocp_reg_write(tp, 0xb896, data);
++
++ rtl_phy_patch_request(tp, false, true);
++
++ data = ocp_reg_read(tp, 0xa86a);
++ data |= BIT(0);
++ ocp_reg_write(tp, 0xa86a, data);
++ data = ocp_reg_read(tp, 0xa6f0);
++ data |= BIT(0);
++ ocp_reg_write(tp, 0xa6f0, data);
++
++ ocp_reg_write(tp, 0xbfa0, 0xd70d);
++ ocp_reg_write(tp, 0xbfa2, 0x4100);
++ ocp_reg_write(tp, 0xbfa4, 0xe868);
++ ocp_reg_write(tp, 0xbfa6, 0xdc59);
++ ocp_reg_write(tp, 0xb54c, 0x3c18);
++ data = ocp_reg_read(tp, 0xbfa4);
++ data &= ~BIT(5);
++ ocp_reg_write(tp, 0xbfa4, data);
++ data = sram_read(tp, 0x817d);
++ data |= BIT(12);
++ sram_write(tp, 0x817d, data);
++ break;
++ case RTL_VER_13:
++ /* 2.5G INRX */
++ data = ocp_reg_read(tp, 0xac46);
++ data &= ~0x00f0;
++ data |= 0x0090;
++ ocp_reg_write(tp, 0xac46, data);
++ data = ocp_reg_read(tp, 0xad30);
++ data &= ~0x0003;
++ data |= 0x0001;
++ ocp_reg_write(tp, 0xad30, data);
++ fallthrough;
++ case RTL_VER_15:
++ /* EEE parameter */
++ ocp_reg_write(tp, 0xb87c, 0x80f5);
++ ocp_reg_write(tp, 0xb87e, 0x760e);
++ ocp_reg_write(tp, 0xb87c, 0x8107);
++ ocp_reg_write(tp, 0xb87e, 0x360e);
++ ocp_reg_write(tp, 0xb87c, 0x8551);
++ data = ocp_reg_read(tp, 0xb87e);
++ data &= ~0xff00;
++ data |= 0x0800;
++ ocp_reg_write(tp, 0xb87e, data);
++
++ /* ADC_PGA parameter */
++ data = ocp_reg_read(tp, 0xbf00);
++ data &= ~0xe000;
++ data |= 0xa000;
++ ocp_reg_write(tp, 0xbf00, data);
++ data = ocp_reg_read(tp, 0xbf46);
++ data &= ~0x0f00;
++ data |= 0x0300;
++ ocp_reg_write(tp, 0xbf46, data);
++
++ /* Green Table-PGA, 1G full viterbi */
++ sram_write(tp, 0x8044, 0x2417);
++ sram_write(tp, 0x804a, 0x2417);
++ sram_write(tp, 0x8050, 0x2417);
++ sram_write(tp, 0x8056, 0x2417);
++ sram_write(tp, 0x805c, 0x2417);
++ sram_write(tp, 0x8062, 0x2417);
++ sram_write(tp, 0x8068, 0x2417);
++ sram_write(tp, 0x806e, 0x2417);
++ sram_write(tp, 0x8074, 0x2417);
++ sram_write(tp, 0x807a, 0x2417);
++
++ /* XG PLL */
++ data = ocp_reg_read(tp, 0xbf84);
++ data &= ~0xe000;
++ data |= 0xa000;
++ ocp_reg_write(tp, 0xbf84, data);
++ break;
++ default:
++ break;
++ }
++
++ if (rtl_phy_patch_request(tp, true, true))
++ return;
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
++ ocp_data |= EEE_SPDWN_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
++
++ data = ocp_reg_read(tp, OCP_DOWN_SPEED);
++ data &= ~(EN_EEE_100 | EN_EEE_1000);
++ data |= EN_10M_CLKDIV;
++ ocp_reg_write(tp, OCP_DOWN_SPEED, data);
++ tp->ups_info._10m_ckdiv = true;
++ tp->ups_info.eee_plloff_100 = false;
++ tp->ups_info.eee_plloff_giga = false;
++
++ data = ocp_reg_read(tp, OCP_POWER_CFG);
++ data &= ~EEE_CLKDIV_EN;
++ ocp_reg_write(tp, OCP_POWER_CFG, data);
++ tp->ups_info.eee_ckdiv = false;
++
++ rtl_phy_patch_request(tp, false, true);
++
++ rtl_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
++
++ data = ocp_reg_read(tp, 0xa428);
++ data &= ~BIT(9);
++ ocp_reg_write(tp, 0xa428, data);
++ data = ocp_reg_read(tp, 0xa5ea);
++ data &= ~BIT(0);
++ ocp_reg_write(tp, 0xa5ea, data);
++ tp->ups_info.lite_mode = 0;
++
++ if (tp->eee_en)
++ rtl_eee_enable(tp, true);
++
++ r8153_aldps_en(tp, true);
++ r8152b_enable_fc(tp);
++ r8153_u2p3en(tp, true);
++
++ set_bit(PHY_RESET, &tp->flags);
++}
++
++static void r8156_init(struct r8152 *tp)
++{
++ u32 ocp_data;
++ u16 data;
++ int i;
++
++ if (test_bit(RTL8152_UNPLUG, &tp->flags))
++ return;
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_ECM_OP);
++ ocp_data &= ~EN_ALL_SPEED;
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_ECM_OP, ocp_data);
++
++ ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_ECM_OPTION);
++ ocp_data |= BYPASS_MAC_RESET;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_ECM_OPTION, ocp_data);
++
++ r8153b_u1u2en(tp, false);
++
++ for (i = 0; i < 500; i++) {
++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
++ AUTOLOAD_DONE)
++ break;
++
++ msleep(20);
++ if (test_bit(RTL8152_UNPLUG, &tp->flags))
++ return;
++ }
++
++ data = r8153_phy_status(tp, 0);
++ if (data == PHY_STAT_EXT_INIT) {
++ data = ocp_reg_read(tp, 0xa468);
++ data &= ~(BIT(3) | BIT(1));
++ ocp_reg_write(tp, 0xa468, data);
++ }
++
++ data = r8152_mdio_read(tp, MII_BMCR);
++ if (data & BMCR_PDOWN) {
++ data &= ~BMCR_PDOWN;
++ r8152_mdio_write(tp, MII_BMCR, data);
++ }
++
++ data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
++ WARN_ON_ONCE(data != PHY_STAT_LAN_ON);
++
++ r8153_u2p3en(tp, false);
++
++ /* MSC timer = 0xfff * 8ms = 32760 ms */
++ ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
++
++ /* U1/U2/L1 idle timer. 500 us */
++ ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
++
++ r8153b_power_cut_en(tp, false);
++ r8156_ups_en(tp, false);
++ r8153_queue_wake(tp, false);
++ rtl_runtime_suspend_enable(tp, false);
++
++ if (tp->udev->speed >= USB_SPEED_SUPER)
++ r8153b_u1u2en(tp, true);
++
++ usb_enable_lpm(tp->udev);
++
++ r8156_mac_clk_spd(tp, true);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
++ ocp_data &= ~PLA_MCU_SPDWN_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS);
++ if (rtl8152_get_speed(tp) & LINK_STATUS)
++ ocp_data |= CUR_LINK_OK;
++ else
++ ocp_data &= ~CUR_LINK_OK;
++ ocp_data |= POLL_LINK_CHG;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data);
++
++ set_bit(GREEN_ETHERNET, &tp->flags);
++
++ /* rx aggregation */
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
++ ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
++ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_BMU_CONFIG);
++ ocp_data |= ACT_ODMA;
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ocp_data);
++
++ rtl_tally_reset(tp);
++
++ tp->coalesce = 15000; /* 15 us */
++}
++
++static void r8156b_init(struct r8152 *tp)
++{
++ u32 ocp_data;
++ u16 data;
++ int i;
++
++ if (test_bit(RTL8152_UNPLUG, &tp->flags))
++ return;
++
++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_ECM_OP);
++ ocp_data &= ~EN_ALL_SPEED;
++ ocp_write_byte(tp, MCU_TYPE_USB, USB_ECM_OP, ocp_data);
++
++ ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_ECM_OPTION);
++ ocp_data |= BYPASS_MAC_RESET;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_ECM_OPTION, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL);
++ ocp_data |= RX_DETECT8;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL, ocp_data);
++
++ r8153b_u1u2en(tp, false);
++
++ switch (tp->version) {
++ case RTL_VER_13:
++ case RTL_VER_15:
++ r8156b_wait_loading_flash(tp);
++ break;
++ default:
++ break;
++ }
++
++ for (i = 0; i < 500; i++) {
++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
++ AUTOLOAD_DONE)
++ break;
++
++ msleep(20);
++ if (test_bit(RTL8152_UNPLUG, &tp->flags))
++ return;
++ }
++
++ data = r8153_phy_status(tp, 0);
++ if (data == PHY_STAT_EXT_INIT) {
++ data = ocp_reg_read(tp, 0xa468);
++ data &= ~(BIT(3) | BIT(1));
++ ocp_reg_write(tp, 0xa468, data);
++
++ data = ocp_reg_read(tp, 0xa466);
++ data &= ~BIT(0);
++ ocp_reg_write(tp, 0xa466, data);
++ }
++
++ data = r8152_mdio_read(tp, MII_BMCR);
++ if (data & BMCR_PDOWN) {
++ data &= ~BMCR_PDOWN;
++ r8152_mdio_write(tp, MII_BMCR, data);
++ }
++
++ data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
++
++ r8153_u2p3en(tp, false);
++
++ /* MSC timer = 0xfff * 8ms = 32760 ms */
++ ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
++
++ /* U1/U2/L1 idle timer. 500 us */
++ ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
++
++ r8153b_power_cut_en(tp, false);
++ r8156_ups_en(tp, false);
++ r8153_queue_wake(tp, false);
++ rtl_runtime_suspend_enable(tp, false);
++
++ if (tp->udev->speed >= USB_SPEED_SUPER)
++ r8153b_u1u2en(tp, true);
++
++ usb_enable_lpm(tp->udev);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RCR);
++ ocp_data &= ~SLOT_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR);
++ ocp_data |= FLOW_CTRL_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data);
++
++ /* enable fc timer and set timer to 600 ms. */
++ ocp_write_word(tp, MCU_TYPE_USB, USB_FC_TIMER,
++ CTRL_TIMER_EN | (600 / 8));
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_CTRL);
++ if (!(ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL) & DACK_DET_EN))
++ ocp_data |= FLOW_CTRL_PATCH_2;
++ ocp_data &= ~AUTO_SPEEDUP;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_CTRL, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK);
++ ocp_data |= FC_PATCH_TASK;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
++
++ r8156_mac_clk_spd(tp, true);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
++ ocp_data &= ~PLA_MCU_SPDWN_EN;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data);
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS);
++ if (rtl8152_get_speed(tp) & LINK_STATUS)
++ ocp_data |= CUR_LINK_OK;
++ else
++ ocp_data &= ~CUR_LINK_OK;
++ ocp_data |= POLL_LINK_CHG;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data);
++
++ set_bit(GREEN_ETHERNET, &tp->flags);
++
++ /* rx aggregation */
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
++ ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
++ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
++
++ rtl_tally_reset(tp);
++
++ tp->coalesce = 15000; /* 15 us */
++}
++
+ static int rtl8152_pre_reset(struct usb_interface *intf)
+ {
+ struct r8152 *tp = usb_get_intfdata(intf);
+@@ -6015,6 +7947,22 @@ int rtl8152_get_link_ksettings(struct ne
+
+ mii_ethtool_get_link_ksettings(&tp->mii, cmd);
+
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
++ cmd->link_modes.supported, tp->support_2500full);
++
++ if (tp->support_2500full) {
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
++ cmd->link_modes.advertising,
++ ocp_reg_read(tp, OCP_10GBT_CTRL) & MDIO_AN_10GBT_CTRL_ADV2_5G);
++
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
++ cmd->link_modes.lp_advertising,
++ ocp_reg_read(tp, OCP_10GBT_STAT) & MDIO_AN_10GBT_STAT_LP2_5G);
++
++ if (is_speed_2500(rtl8152_get_speed(tp)))
++ cmd->base.speed = SPEED_2500;
++ }
++
+ mutex_unlock(&tp->control);
+
+ usb_autopm_put_interface(tp->intf);
+@@ -6058,6 +8006,10 @@ static int rtl8152_set_link_ksettings(st
+ cmd->link_modes.advertising))
+ advertising |= RTL_ADVERTISED_1000_FULL;
+
++ if (test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
++ cmd->link_modes.advertising))
++ advertising |= RTL_ADVERTISED_2500_FULL;
++
+ mutex_lock(&tp->control);
+
+ ret = rtl8152_set_speed(tp, cmd->base.autoneg, cmd->base.speed,
+@@ -6647,6 +8599,67 @@ static int rtl_ops_init(struct r8152 *tp
+ tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX;
+ break;
+
++ case RTL_VER_11:
++ tp->eee_en = true;
++ tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX;
++ fallthrough;
++ case RTL_VER_10:
++ ops->init = r8156_init;
++ ops->enable = rtl8156_enable;
++ ops->disable = rtl8153_disable;
++ ops->up = rtl8156_up;
++ ops->down = rtl8156_down;
++ ops->unload = rtl8153_unload;
++ ops->eee_get = r8153_get_eee;
++ ops->eee_set = r8152_set_eee;
++ ops->in_nway = rtl8153_in_nway;
++ ops->hw_phy_cfg = r8156_hw_phy_cfg;
++ ops->autosuspend_en = rtl8156_runtime_enable;
++ ops->change_mtu = rtl8156_change_mtu;
++ tp->rx_buf_sz = 48 * 1024;
++ tp->support_2500full = 1;
++ break;
++
++ case RTL_VER_12:
++ case RTL_VER_13:
++ tp->support_2500full = 1;
++ fallthrough;
++ case RTL_VER_15:
++ tp->eee_en = true;
++ tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX;
++ ops->init = r8156b_init;
++ ops->enable = rtl8156b_enable;
++ ops->disable = rtl8153_disable;
++ ops->up = rtl8156_up;
++ ops->down = rtl8156_down;
++ ops->unload = rtl8153_unload;
++ ops->eee_get = r8153_get_eee;
++ ops->eee_set = r8152_set_eee;
++ ops->in_nway = rtl8153_in_nway;
++ ops->hw_phy_cfg = r8156b_hw_phy_cfg;
++ ops->autosuspend_en = rtl8156_runtime_enable;
++ ops->change_mtu = rtl8156_change_mtu;
++ tp->rx_buf_sz = 48 * 1024;
++ break;
++
++ case RTL_VER_14:
++ ops->init = r8153c_init;
++ ops->enable = rtl8153_enable;
++ ops->disable = rtl8153_disable;
++ ops->up = rtl8153c_up;
++ ops->down = rtl8153b_down;
++ ops->unload = rtl8153_unload;
++ ops->eee_get = r8153_get_eee;
++ ops->eee_set = r8152_set_eee;
++ ops->in_nway = rtl8153_in_nway;
++ ops->hw_phy_cfg = r8153c_hw_phy_cfg;
++ ops->autosuspend_en = rtl8153c_runtime_enable;
++ ops->change_mtu = rtl8153c_change_mtu;
++ tp->rx_buf_sz = 32 * 1024;
++ tp->eee_en = true;
++ tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX;
++ break;
++
+ default:
+ ret = -ENODEV;
+ dev_err(&tp->intf->dev, "Unknown Device\n");
+@@ -6660,11 +8673,13 @@ static int rtl_ops_init(struct r8152 *tp
+ #define FIRMWARE_8153A_3 "rtl_nic/rtl8153a-3.fw"
+ #define FIRMWARE_8153A_4 "rtl_nic/rtl8153a-4.fw"
+ #define FIRMWARE_8153B_2 "rtl_nic/rtl8153b-2.fw"
++#define FIRMWARE_8153C_1 "rtl_nic/rtl8153c-1.fw"
+
+ MODULE_FIRMWARE(FIRMWARE_8153A_2);
+ MODULE_FIRMWARE(FIRMWARE_8153A_3);
+ MODULE_FIRMWARE(FIRMWARE_8153A_4);
+ MODULE_FIRMWARE(FIRMWARE_8153B_2);
++MODULE_FIRMWARE(FIRMWARE_8153C_1);
+
+ static int rtl_fw_init(struct r8152 *tp)
+ {
+@@ -6690,6 +8705,11 @@ static int rtl_fw_init(struct r8152 *tp)
+ rtl_fw->pre_fw = r8153b_pre_firmware_1;
+ rtl_fw->post_fw = r8153b_post_firmware_1;
+ break;
++ case RTL_VER_14:
++ rtl_fw->fw_name = FIRMWARE_8153C_1;
++ rtl_fw->pre_fw = r8153b_pre_firmware_1;
++ rtl_fw->post_fw = r8153c_post_firmware_1;
++ break;
+ default:
+ break;
+ }
+@@ -6745,6 +8765,27 @@ u8 rtl8152_get_version(struct usb_interf
+ case 0x6010:
+ version = RTL_VER_09;
+ break;
++ case 0x7010:
++ version = RTL_TEST_01;
++ break;
++ case 0x7020:
++ version = RTL_VER_10;
++ break;
++ case 0x7030:
++ version = RTL_VER_11;
++ break;
++ case 0x7400:
++ version = RTL_VER_12;
++ break;
++ case 0x7410:
++ version = RTL_VER_13;
++ break;
++ case 0x6400:
++ version = RTL_VER_14;
++ break;
++ case 0x7420:
++ version = RTL_VER_15;
++ break;
+ default:
+ version = RTL_VER_UNKNOWN;
+ dev_info(&intf->dev, "Unknown version 0x%04x\n", ocp_data);
+@@ -6857,12 +8898,29 @@ static int rtl8152_probe(struct usb_inte
+ /* MTU range: 68 - 1500 or 9194 */
+ netdev->min_mtu = ETH_MIN_MTU;
+ switch (tp->version) {
++ case RTL_VER_03:
++ case RTL_VER_04:
++ case RTL_VER_05:
++ case RTL_VER_06:
++ case RTL_VER_08:
++ case RTL_VER_09:
++ case RTL_VER_14:
++ netdev->max_mtu = size_to_mtu(9 * 1024);
++ break;
++ case RTL_VER_10:
++ case RTL_VER_11:
++ netdev->max_mtu = size_to_mtu(15 * 1024);
++ break;
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_15:
++ netdev->max_mtu = size_to_mtu(16 * 1024);
++ break;
+ case RTL_VER_01:
+ case RTL_VER_02:
+- netdev->max_mtu = ETH_DATA_LEN;
+- break;
++ case RTL_VER_07:
+ default:
+- netdev->max_mtu = size_to_mtu(9 * 1024);
++ netdev->max_mtu = ETH_DATA_LEN;
+ break;
+ }
+
+@@ -6878,7 +8936,13 @@ static int rtl8152_probe(struct usb_inte
+ tp->advertising = RTL_ADVERTISED_10_HALF | RTL_ADVERTISED_10_FULL |
+ RTL_ADVERTISED_100_HALF | RTL_ADVERTISED_100_FULL;
+ if (tp->mii.supports_gmii) {
+- tp->speed = SPEED_1000;
++ if (tp->support_2500full &&
++ tp->udev->speed >= USB_SPEED_SUPER) {
++ tp->speed = SPEED_2500;
++ tp->advertising |= RTL_ADVERTISED_2500_FULL;
++ } else {
++ tp->speed = SPEED_1000;
++ }
+ tp->advertising |= RTL_ADVERTISED_1000_FULL;
+ }
+ tp->duplex = DUPLEX_FULL;
+@@ -6902,7 +8966,11 @@ static int rtl8152_probe(struct usb_inte
+ set_ethernet_addr(tp);
+
+ usb_set_intfdata(intf, tp);
+- netif_napi_add(netdev, &tp->napi, r8152_poll, RTL8152_NAPI_WEIGHT);
++
++ if (tp->support_2500full)
++ netif_napi_add(netdev, &tp->napi, r8152_poll, 256);
++ else
++ netif_napi_add(netdev, &tp->napi, r8152_poll, 64);
+
+ ret = register_netdev(netdev);
+ if (ret != 0) {
+@@ -6938,7 +9006,8 @@ static void rtl8152_disconnect(struct us
+ unregister_netdev(tp->netdev);
+ tasklet_kill(&tp->tx_tl);
+ cancel_delayed_work_sync(&tp->hw_phy_work);
+- tp->rtl_ops.unload(tp);
++ if (tp->rtl_ops.unload)
++ tp->rtl_ops.unload(tp);
+ rtl8152_release_firmware(tp);
+ free_netdev(tp->netdev);
+ }
+@@ -6958,13 +9027,28 @@ static void rtl8152_disconnect(struct us
+ .idProduct = (prod), \
+ .bInterfaceClass = USB_CLASS_COMM, \
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \
++ .bInterfaceProtocol = USB_CDC_PROTO_NONE \
++}, \
++{ \
++ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | \
++ USB_DEVICE_ID_MATCH_DEVICE, \
++ .idVendor = (vend), \
++ .idProduct = (prod), \
++ .bInterfaceClass = USB_CLASS_COMM, \
++ .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM, \
+ .bInterfaceProtocol = USB_CDC_PROTO_NONE
+
+ /* table of devices that work with this driver */
+ static const struct usb_device_id rtl8152_table[] = {
++ /* Realtek */
+ {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8050)},
++ {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8053)},
+ {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)},
+ {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)},
++ {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8155)},
++ {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8156)},
++
++ /* Microsoft */
+ {REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab)},
+ {REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07c6)},
+ {REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x0927)},
--- /dev/null
+From ca09589a72a0aa17389754fb75a5cd1a5d46818f Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Fri, 16 Apr 2021 16:04:36 +0800
+Subject: [PATCH] r8152: support PHY firmware for RTL8156 series
+
+commit 4a51b0e8a0143b0e83d51d9c58c6416c3818a9f2 upstream.
+
+Support new firmware type and method for RTL8156 series.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 563 +++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 561 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -975,8 +975,60 @@ enum rtl8152_fw_flags {
+ FW_FLAGS_START,
+ FW_FLAGS_STOP,
+ FW_FLAGS_NC,
++ FW_FLAGS_NC1,
++ FW_FLAGS_NC2,
++ FW_FLAGS_UC2,
++ FW_FLAGS_UC,
++ FW_FLAGS_SPEED_UP,
++ FW_FLAGS_VER,
+ };
+
++enum rtl8152_fw_fixup_cmd {
++ FW_FIXUP_AND = 0,
++ FW_FIXUP_OR,
++ FW_FIXUP_NOT,
++ FW_FIXUP_XOR,
++};
++
++struct fw_phy_set {
++ __le16 addr;
++ __le16 data;
++} __packed;
++
++struct fw_phy_speed_up {
++ struct fw_block blk_hdr;
++ __le16 fw_offset;
++ __le16 version;
++ __le16 fw_reg;
++ __le16 reserved;
++ char info[];
++} __packed;
++
++struct fw_phy_ver {
++ struct fw_block blk_hdr;
++ struct fw_phy_set ver;
++ __le32 reserved;
++} __packed;
++
++struct fw_phy_fixup {
++ struct fw_block blk_hdr;
++ struct fw_phy_set setting;
++ __le16 bit_cmd;
++ __le16 reserved;
++} __packed;
++
++struct fw_phy_union {
++ struct fw_block blk_hdr;
++ __le16 fw_offset;
++ __le16 fw_reg;
++ struct fw_phy_set pre_set[2];
++ struct fw_phy_set bp[8];
++ struct fw_phy_set bp_en;
++ u8 pre_num;
++ u8 bp_num;
++ char info[];
++} __packed;
++
+ /**
+ * struct fw_mac - a firmware block used by RTL_FW_PLA and RTL_FW_USB.
+ * The layout of the firmware block is:
+@@ -1081,6 +1133,15 @@ enum rtl_fw_type {
+ RTL_FW_PHY_START,
+ RTL_FW_PHY_STOP,
+ RTL_FW_PHY_NC,
++ RTL_FW_PHY_FIXUP,
++ RTL_FW_PHY_UNION_NC,
++ RTL_FW_PHY_UNION_NC1,
++ RTL_FW_PHY_UNION_NC2,
++ RTL_FW_PHY_UNION_UC2,
++ RTL_FW_PHY_UNION_UC,
++ RTL_FW_PHY_UNION_MISC,
++ RTL_FW_PHY_SPEED_UP,
++ RTL_FW_PHY_VER,
+ };
+
+ enum rtl_version {
+@@ -4000,6 +4061,162 @@ static int rtl_post_ram_code(struct r815
+ return 0;
+ }
+
++static bool rtl8152_is_fw_phy_speed_up_ok(struct r8152 *tp, struct fw_phy_speed_up *phy)
++{
++ u16 fw_offset;
++ u32 length;
++ bool rc = false;
++
++ switch (tp->version) {
++ case RTL_VER_01:
++ case RTL_VER_02:
++ case RTL_VER_03:
++ case RTL_VER_04:
++ case RTL_VER_05:
++ case RTL_VER_06:
++ case RTL_VER_07:
++ case RTL_VER_08:
++ case RTL_VER_09:
++ case RTL_VER_10:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_14:
++ goto out;
++ case RTL_VER_13:
++ case RTL_VER_15:
++ default:
++ break;
++ }
++
++ fw_offset = __le16_to_cpu(phy->fw_offset);
++ length = __le32_to_cpu(phy->blk_hdr.length);
++ if (fw_offset < sizeof(*phy) || length <= fw_offset) {
++ dev_err(&tp->intf->dev, "invalid fw_offset\n");
++ goto out;
++ }
++
++ length -= fw_offset;
++ if (length & 3) {
++ dev_err(&tp->intf->dev, "invalid block length\n");
++ goto out;
++ }
++
++ if (__le16_to_cpu(phy->fw_reg) != 0x9A00) {
++ dev_err(&tp->intf->dev, "invalid register to load firmware\n");
++ goto out;
++ }
++
++ rc = true;
++out:
++ return rc;
++}
++
++static bool rtl8152_is_fw_phy_ver_ok(struct r8152 *tp, struct fw_phy_ver *ver)
++{
++ bool rc = false;
++
++ switch (tp->version) {
++ case RTL_VER_10:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_15:
++ break;
++ default:
++ goto out;
++ }
++
++ if (__le32_to_cpu(ver->blk_hdr.length) != sizeof(*ver)) {
++ dev_err(&tp->intf->dev, "invalid block length\n");
++ goto out;
++ }
++
++ if (__le16_to_cpu(ver->ver.addr) != SRAM_GPHY_FW_VER) {
++ dev_err(&tp->intf->dev, "invalid phy ver addr\n");
++ goto out;
++ }
++
++ rc = true;
++out:
++ return rc;
++}
++
++static bool rtl8152_is_fw_phy_fixup_ok(struct r8152 *tp, struct fw_phy_fixup *fix)
++{
++ bool rc = false;
++
++ switch (tp->version) {
++ case RTL_VER_10:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_15:
++ break;
++ default:
++ goto out;
++ }
++
++ if (__le32_to_cpu(fix->blk_hdr.length) != sizeof(*fix)) {
++ dev_err(&tp->intf->dev, "invalid block length\n");
++ goto out;
++ }
++
++ if (__le16_to_cpu(fix->setting.addr) != OCP_PHY_PATCH_CMD ||
++ __le16_to_cpu(fix->setting.data) != BIT(7)) {
++ dev_err(&tp->intf->dev, "invalid phy fixup\n");
++ goto out;
++ }
++
++ rc = true;
++out:
++ return rc;
++}
++
++static bool rtl8152_is_fw_phy_union_ok(struct r8152 *tp, struct fw_phy_union *phy)
++{
++ u16 fw_offset;
++ u32 length;
++ bool rc = false;
++
++ switch (tp->version) {
++ case RTL_VER_10:
++ case RTL_VER_11:
++ case RTL_VER_12:
++ case RTL_VER_13:
++ case RTL_VER_15:
++ break;
++ default:
++ goto out;
++ }
++
++ fw_offset = __le16_to_cpu(phy->fw_offset);
++ length = __le32_to_cpu(phy->blk_hdr.length);
++ if (fw_offset < sizeof(*phy) || length <= fw_offset) {
++ dev_err(&tp->intf->dev, "invalid fw_offset\n");
++ goto out;
++ }
++
++ length -= fw_offset;
++ if (length & 1) {
++ dev_err(&tp->intf->dev, "invalid block length\n");
++ goto out;
++ }
++
++ if (phy->pre_num > 2) {
++ dev_err(&tp->intf->dev, "invalid pre_num %d\n", phy->pre_num);
++ goto out;
++ }
++
++ if (phy->bp_num > 8) {
++ dev_err(&tp->intf->dev, "invalid bp_num %d\n", phy->bp_num);
++ goto out;
++ }
++
++ rc = true;
++out:
++ return rc;
++}
++
+ static bool rtl8152_is_fw_phy_nc_ok(struct r8152 *tp, struct fw_phy_nc *phy)
+ {
+ u32 length;
+@@ -4320,6 +4537,10 @@ static long rtl8152_check_firmware(struc
+ case RTL_FW_PHY_START:
+ if (test_bit(FW_FLAGS_START, &fw_flags) ||
+ test_bit(FW_FLAGS_NC, &fw_flags) ||
++ test_bit(FW_FLAGS_NC1, &fw_flags) ||
++ test_bit(FW_FLAGS_NC2, &fw_flags) ||
++ test_bit(FW_FLAGS_UC2, &fw_flags) ||
++ test_bit(FW_FLAGS_UC, &fw_flags) ||
+ test_bit(FW_FLAGS_STOP, &fw_flags)) {
+ dev_err(&tp->intf->dev,
+ "check PHY_START fail\n");
+@@ -4368,7 +4589,153 @@ static long rtl8152_check_firmware(struc
+ goto fail;
+ }
+ __set_bit(FW_FLAGS_NC, &fw_flags);
++ break;
++ case RTL_FW_PHY_UNION_NC:
++ if (!test_bit(FW_FLAGS_START, &fw_flags) ||
++ test_bit(FW_FLAGS_NC1, &fw_flags) ||
++ test_bit(FW_FLAGS_NC2, &fw_flags) ||
++ test_bit(FW_FLAGS_UC2, &fw_flags) ||
++ test_bit(FW_FLAGS_UC, &fw_flags) ||
++ test_bit(FW_FLAGS_STOP, &fw_flags)) {
++ dev_err(&tp->intf->dev, "PHY_UNION_NC out of order\n");
++ goto fail;
++ }
++
++ if (test_bit(FW_FLAGS_NC, &fw_flags)) {
++ dev_err(&tp->intf->dev, "multiple PHY_UNION_NC encountered\n");
++ goto fail;
++ }
+
++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
++ dev_err(&tp->intf->dev, "check PHY_UNION_NC failed\n");
++ goto fail;
++ }
++ __set_bit(FW_FLAGS_NC, &fw_flags);
++ break;
++ case RTL_FW_PHY_UNION_NC1:
++ if (!test_bit(FW_FLAGS_START, &fw_flags) ||
++ test_bit(FW_FLAGS_NC2, &fw_flags) ||
++ test_bit(FW_FLAGS_UC2, &fw_flags) ||
++ test_bit(FW_FLAGS_UC, &fw_flags) ||
++ test_bit(FW_FLAGS_STOP, &fw_flags)) {
++ dev_err(&tp->intf->dev, "PHY_UNION_NC1 out of order\n");
++ goto fail;
++ }
++
++ if (test_bit(FW_FLAGS_NC1, &fw_flags)) {
++ dev_err(&tp->intf->dev, "multiple PHY NC1 encountered\n");
++ goto fail;
++ }
++
++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
++ dev_err(&tp->intf->dev, "check PHY_UNION_NC1 failed\n");
++ goto fail;
++ }
++ __set_bit(FW_FLAGS_NC1, &fw_flags);
++ break;
++ case RTL_FW_PHY_UNION_NC2:
++ if (!test_bit(FW_FLAGS_START, &fw_flags) ||
++ test_bit(FW_FLAGS_UC2, &fw_flags) ||
++ test_bit(FW_FLAGS_UC, &fw_flags) ||
++ test_bit(FW_FLAGS_STOP, &fw_flags)) {
++ dev_err(&tp->intf->dev, "PHY_UNION_NC2 out of order\n");
++ goto fail;
++ }
++
++ if (test_bit(FW_FLAGS_NC2, &fw_flags)) {
++ dev_err(&tp->intf->dev, "multiple PHY NC2 encountered\n");
++ goto fail;
++ }
++
++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
++ dev_err(&tp->intf->dev, "check PHY_UNION_NC2 failed\n");
++ goto fail;
++ }
++ __set_bit(FW_FLAGS_NC2, &fw_flags);
++ break;
++ case RTL_FW_PHY_UNION_UC2:
++ if (!test_bit(FW_FLAGS_START, &fw_flags) ||
++ test_bit(FW_FLAGS_UC, &fw_flags) ||
++ test_bit(FW_FLAGS_STOP, &fw_flags)) {
++ dev_err(&tp->intf->dev, "PHY_UNION_UC2 out of order\n");
++ goto fail;
++ }
++
++ if (test_bit(FW_FLAGS_UC2, &fw_flags)) {
++ dev_err(&tp->intf->dev, "multiple PHY UC2 encountered\n");
++ goto fail;
++ }
++
++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
++ dev_err(&tp->intf->dev, "check PHY_UNION_UC2 failed\n");
++ goto fail;
++ }
++ __set_bit(FW_FLAGS_UC2, &fw_flags);
++ break;
++ case RTL_FW_PHY_UNION_UC:
++ if (!test_bit(FW_FLAGS_START, &fw_flags) ||
++ test_bit(FW_FLAGS_STOP, &fw_flags)) {
++ dev_err(&tp->intf->dev, "PHY_UNION_UC out of order\n");
++ goto fail;
++ }
++
++ if (test_bit(FW_FLAGS_UC, &fw_flags)) {
++ dev_err(&tp->intf->dev, "multiple PHY UC encountered\n");
++ goto fail;
++ }
++
++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
++ dev_err(&tp->intf->dev, "check PHY_UNION_UC failed\n");
++ goto fail;
++ }
++ __set_bit(FW_FLAGS_UC, &fw_flags);
++ break;
++ case RTL_FW_PHY_UNION_MISC:
++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
++ dev_err(&tp->intf->dev, "check RTL_FW_PHY_UNION_MISC failed\n");
++ goto fail;
++ }
++ break;
++ case RTL_FW_PHY_FIXUP:
++ if (!rtl8152_is_fw_phy_fixup_ok(tp, (struct fw_phy_fixup *)block)) {
++ dev_err(&tp->intf->dev, "check PHY fixup failed\n");
++ goto fail;
++ }
++ break;
++ case RTL_FW_PHY_SPEED_UP:
++ if (test_bit(FW_FLAGS_SPEED_UP, &fw_flags)) {
++ dev_err(&tp->intf->dev, "multiple PHY firmware encountered");
++ goto fail;
++ }
++
++ if (!rtl8152_is_fw_phy_speed_up_ok(tp, (struct fw_phy_speed_up *)block)) {
++ dev_err(&tp->intf->dev, "check PHY speed up failed\n");
++ goto fail;
++ }
++ __set_bit(FW_FLAGS_SPEED_UP, &fw_flags);
++ break;
++ case RTL_FW_PHY_VER:
++ if (test_bit(FW_FLAGS_START, &fw_flags) ||
++ test_bit(FW_FLAGS_NC, &fw_flags) ||
++ test_bit(FW_FLAGS_NC1, &fw_flags) ||
++ test_bit(FW_FLAGS_NC2, &fw_flags) ||
++ test_bit(FW_FLAGS_UC2, &fw_flags) ||
++ test_bit(FW_FLAGS_UC, &fw_flags) ||
++ test_bit(FW_FLAGS_STOP, &fw_flags)) {
++ dev_err(&tp->intf->dev, "Invalid order to set PHY version\n");
++ goto fail;
++ }
++
++ if (test_bit(FW_FLAGS_VER, &fw_flags)) {
++ dev_err(&tp->intf->dev, "multiple PHY version encountered");
++ goto fail;
++ }
++
++ if (!rtl8152_is_fw_phy_ver_ok(tp, (struct fw_phy_ver *)block)) {
++ dev_err(&tp->intf->dev, "check PHY version failed\n");
++ goto fail;
++ }
++ __set_bit(FW_FLAGS_VER, &fw_flags);
+ break;
+ default:
+ dev_warn(&tp->intf->dev, "Unknown type %u is found\n",
+@@ -4391,6 +4758,143 @@ fail:
+ return ret;
+ }
+
++static void rtl_ram_code_speed_up(struct r8152 *tp, struct fw_phy_speed_up *phy, bool wait)
++{
++ u32 len;
++ u8 *data;
++
++ if (sram_read(tp, SRAM_GPHY_FW_VER) >= __le16_to_cpu(phy->version)) {
++ dev_dbg(&tp->intf->dev, "PHY firmware has been the newest\n");
++ return;
++ }
++
++ len = __le32_to_cpu(phy->blk_hdr.length);
++ len -= __le16_to_cpu(phy->fw_offset);
++ data = (u8 *)phy + __le16_to_cpu(phy->fw_offset);
++
++ if (rtl_phy_patch_request(tp, true, wait))
++ return;
++
++ while (len) {
++ u32 ocp_data, size;
++ int i;
++
++ if (len < 2048)
++ size = len;
++ else
++ size = 2048;
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL);
++ ocp_data |= GPHY_PATCH_DONE | BACKUP_RESTRORE;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL, ocp_data);
++
++ generic_ocp_write(tp, __le16_to_cpu(phy->fw_reg), 0xff, size, data, MCU_TYPE_USB);
++
++ data += size;
++ len -= size;
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL);
++ ocp_data |= POL_GPHY_PATCH;
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL, ocp_data);
++
++ for (i = 0; i < 1000; i++) {
++ if (!(ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL) & POL_GPHY_PATCH))
++ break;
++ }
++
++ if (i == 1000) {
++ dev_err(&tp->intf->dev, "ram code speedup mode timeout\n");
++ return;
++ }
++ }
++
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, tp->ocp_base);
++ rtl_phy_patch_request(tp, false, wait);
++
++ if (sram_read(tp, SRAM_GPHY_FW_VER) == __le16_to_cpu(phy->version))
++ dev_dbg(&tp->intf->dev, "successfully applied %s\n", phy->info);
++ else
++ dev_err(&tp->intf->dev, "ram code speedup mode fail\n");
++}
++
++static int rtl8152_fw_phy_ver(struct r8152 *tp, struct fw_phy_ver *phy_ver)
++{
++ u16 ver_addr, ver;
++
++ ver_addr = __le16_to_cpu(phy_ver->ver.addr);
++ ver = __le16_to_cpu(phy_ver->ver.data);
++
++ if (sram_read(tp, ver_addr) >= ver) {
++ dev_dbg(&tp->intf->dev, "PHY firmware has been the newest\n");
++ return 0;
++ }
++
++ sram_write(tp, ver_addr, ver);
++
++ dev_dbg(&tp->intf->dev, "PHY firmware version %x\n", ver);
++
++ return ver;
++}
++
++static void rtl8152_fw_phy_fixup(struct r8152 *tp, struct fw_phy_fixup *fix)
++{
++ u16 addr, data;
++
++ addr = __le16_to_cpu(fix->setting.addr);
++ data = ocp_reg_read(tp, addr);
++
++ switch (__le16_to_cpu(fix->bit_cmd)) {
++ case FW_FIXUP_AND:
++ data &= __le16_to_cpu(fix->setting.data);
++ break;
++ case FW_FIXUP_OR:
++ data |= __le16_to_cpu(fix->setting.data);
++ break;
++ case FW_FIXUP_NOT:
++ data &= ~__le16_to_cpu(fix->setting.data);
++ break;
++ case FW_FIXUP_XOR:
++ data ^= __le16_to_cpu(fix->setting.data);
++ break;
++ default:
++ return;
++ }
++
++ ocp_reg_write(tp, addr, data);
++
++ dev_dbg(&tp->intf->dev, "applied ocp %x %x\n", addr, data);
++}
++
++static void rtl8152_fw_phy_union_apply(struct r8152 *tp, struct fw_phy_union *phy)
++{
++ __le16 *data;
++ u32 length;
++ int i, num;
++
++ num = phy->pre_num;
++ for (i = 0; i < num; i++)
++ sram_write(tp, __le16_to_cpu(phy->pre_set[i].addr),
++ __le16_to_cpu(phy->pre_set[i].data));
++
++ length = __le32_to_cpu(phy->blk_hdr.length);
++ length -= __le16_to_cpu(phy->fw_offset);
++ num = length / 2;
++ data = (__le16 *)((u8 *)phy + __le16_to_cpu(phy->fw_offset));
++
++ ocp_reg_write(tp, OCP_SRAM_ADDR, __le16_to_cpu(phy->fw_reg));
++ for (i = 0; i < num; i++)
++ ocp_reg_write(tp, OCP_SRAM_DATA, __le16_to_cpu(data[i]));
++
++ num = phy->bp_num;
++ for (i = 0; i < num; i++)
++ sram_write(tp, __le16_to_cpu(phy->bp[i].addr), __le16_to_cpu(phy->bp[i].data));
++
++ if (phy->bp_num && phy->bp_en.addr)
++ sram_write(tp, __le16_to_cpu(phy->bp_en.addr), __le16_to_cpu(phy->bp_en.data));
++
++ dev_dbg(&tp->intf->dev, "successfully applied %s\n", phy->info);
++}
++
+ static void rtl8152_fw_phy_nc_apply(struct r8152 *tp, struct fw_phy_nc *phy)
+ {
+ u16 mode_reg, bp_index;
+@@ -4444,6 +4948,12 @@ static void rtl8152_fw_mac_apply(struct
+ return;
+ }
+
++ fw_ver_reg = __le16_to_cpu(mac->fw_ver_reg);
++ if (fw_ver_reg && ocp_read_byte(tp, MCU_TYPE_USB, fw_ver_reg) >= mac->fw_ver_data) {
++ dev_dbg(&tp->intf->dev, "%s firmware has been the newest\n", type ? "PLA" : "USB");
++ return;
++ }
++
+ rtl_clear_bp(tp, type);
+
+ /* Enable backup/restore of MACDBG. This is required after clearing PLA
+@@ -4479,7 +4989,6 @@ static void rtl8152_fw_mac_apply(struct
+ ocp_write_word(tp, type, bp_en_addr,
+ __le16_to_cpu(mac->bp_en_value));
+
+- fw_ver_reg = __le16_to_cpu(mac->fw_ver_reg);
+ if (fw_ver_reg)
+ ocp_write_byte(tp, MCU_TYPE_USB, fw_ver_reg,
+ mac->fw_ver_data);
+@@ -4494,7 +5003,7 @@ static void rtl8152_apply_firmware(struc
+ struct fw_header *fw_hdr;
+ struct fw_phy_patch_key *key;
+ u16 key_addr = 0;
+- int i;
++ int i, patch_phy = 1;
+
+ if (IS_ERR_OR_NULL(rtl_fw->fw))
+ return;
+@@ -4516,17 +5025,40 @@ static void rtl8152_apply_firmware(struc
+ rtl8152_fw_mac_apply(tp, (struct fw_mac *)block);
+ break;
+ case RTL_FW_PHY_START:
++ if (!patch_phy)
++ break;
+ key = (struct fw_phy_patch_key *)block;
+ key_addr = __le16_to_cpu(key->key_reg);
+ rtl_pre_ram_code(tp, key_addr, __le16_to_cpu(key->key_data), !power_cut);
+ break;
+ case RTL_FW_PHY_STOP:
++ if (!patch_phy)
++ break;
+ WARN_ON(!key_addr);
+ rtl_post_ram_code(tp, key_addr, !power_cut);
+ break;
+ case RTL_FW_PHY_NC:
+ rtl8152_fw_phy_nc_apply(tp, (struct fw_phy_nc *)block);
+ break;
++ case RTL_FW_PHY_VER:
++ patch_phy = rtl8152_fw_phy_ver(tp, (struct fw_phy_ver *)block);
++ break;
++ case RTL_FW_PHY_UNION_NC:
++ case RTL_FW_PHY_UNION_NC1:
++ case RTL_FW_PHY_UNION_NC2:
++ case RTL_FW_PHY_UNION_UC2:
++ case RTL_FW_PHY_UNION_UC:
++ case RTL_FW_PHY_UNION_MISC:
++ if (patch_phy)
++ rtl8152_fw_phy_union_apply(tp, (struct fw_phy_union *)block);
++ break;
++ case RTL_FW_PHY_FIXUP:
++ if (patch_phy)
++ rtl8152_fw_phy_fixup(tp, (struct fw_phy_fixup *)block);
++ break;
++ case RTL_FW_PHY_SPEED_UP:
++ rtl_ram_code_speed_up(tp, (struct fw_phy_speed_up *)block, !power_cut);
++ break;
+ default:
+ break;
+ }
+@@ -5034,6 +5566,21 @@ static int r8153c_post_firmware_1(struct
+ return 0;
+ }
+
++static int r8156a_post_firmware_1(struct r8152 *tp)
++{
++ u32 ocp_data;
++
++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1);
++ ocp_data |= FW_IP_RESET_EN;
++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data);
++
++ /* Modify U3PHY parameter for compatibility issue */
++ ocp_write_dword(tp, MCU_TYPE_USB, USB_UPHY3_MDCMDIO, 0x4026840e);
++ ocp_write_dword(tp, MCU_TYPE_USB, USB_UPHY3_MDCMDIO, 0x4001acc9);
++
++ return 0;
++}
++
+ static void r8153_aldps_en(struct r8152 *tp, bool enable)
+ {
+ u16 data;
+@@ -8674,12 +9221,16 @@ static int rtl_ops_init(struct r8152 *tp
+ #define FIRMWARE_8153A_4 "rtl_nic/rtl8153a-4.fw"
+ #define FIRMWARE_8153B_2 "rtl_nic/rtl8153b-2.fw"
+ #define FIRMWARE_8153C_1 "rtl_nic/rtl8153c-1.fw"
++#define FIRMWARE_8156A_2 "rtl_nic/rtl8156a-2.fw"
++#define FIRMWARE_8156B_2 "rtl_nic/rtl8156b-2.fw"
+
+ MODULE_FIRMWARE(FIRMWARE_8153A_2);
+ MODULE_FIRMWARE(FIRMWARE_8153A_3);
+ MODULE_FIRMWARE(FIRMWARE_8153A_4);
+ MODULE_FIRMWARE(FIRMWARE_8153B_2);
+ MODULE_FIRMWARE(FIRMWARE_8153C_1);
++MODULE_FIRMWARE(FIRMWARE_8156A_2);
++MODULE_FIRMWARE(FIRMWARE_8156B_2);
+
+ static int rtl_fw_init(struct r8152 *tp)
+ {
+@@ -8705,6 +9256,14 @@ static int rtl_fw_init(struct r8152 *tp)
+ rtl_fw->pre_fw = r8153b_pre_firmware_1;
+ rtl_fw->post_fw = r8153b_post_firmware_1;
+ break;
++ case RTL_VER_11:
++ rtl_fw->fw_name = FIRMWARE_8156A_2;
++ rtl_fw->post_fw = r8156a_post_firmware_1;
++ break;
++ case RTL_VER_13:
++ case RTL_VER_15:
++ rtl_fw->fw_name = FIRMWARE_8156B_2;
++ break;
+ case RTL_VER_14:
+ rtl_fw->fw_name = FIRMWARE_8153C_1;
+ rtl_fw->pre_fw = r8153b_pre_firmware_1;
--- /dev/null
+From 579f58dd2819910354753bc5489fc1588fe9cfe2 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Fri, 16 Apr 2021 16:04:37 +0800
+Subject: [PATCH] r8152: search the configuration of vendor mode
+
+commit c2198943e33b100ed21dfb636c8fa6baef841e9d upstream.
+
+The vendor mode is not always at config #1, so it is necessary to
+set the correct configuration number.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 39 +++++++++++++++++++++++++++++++++++----
+ 1 file changed, 35 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -30,7 +30,7 @@
+ #include <linux/usb/r8152.h>
+
+ /* Information for net-next */
+-#define NETNEXT_VERSION "11"
++#define NETNEXT_VERSION "12"
+
+ /* Information for net */
+ #define NET_VERSION "11"
+@@ -8131,6 +8131,39 @@ static void r8156b_init(struct r8152 *tp
+ tp->coalesce = 15000; /* 15 us */
+ }
+
++static bool rtl_vendor_mode(struct usb_interface *intf)
++{
++ struct usb_host_interface *alt = intf->cur_altsetting;
++ struct usb_device *udev;
++ struct usb_host_config *c;
++ int i, num_configs;
++
++ if (alt->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC)
++ return true;
++
++ /* The vendor mode is not always config #1, so to find it out. */
++ udev = interface_to_usbdev(intf);
++ c = udev->config;
++ num_configs = udev->descriptor.bNumConfigurations;
++ for (i = 0; i < num_configs; (i++, c++)) {
++ struct usb_interface_descriptor *desc = NULL;
++
++ if (c->desc.bNumInterfaces > 0)
++ desc = &c->intf_cache[0]->altsetting->desc;
++ else
++ continue;
++
++ if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
++ usb_driver_set_configuration(udev, c->desc.bConfigurationValue);
++ break;
++ }
++ }
++
++ WARN_ON_ONCE(i == num_configs);
++
++ return false;
++}
++
+ static int rtl8152_pre_reset(struct usb_interface *intf)
+ {
+ struct r8152 *tp = usb_get_intfdata(intf);
+@@ -9369,10 +9402,8 @@ static int rtl8152_probe(struct usb_inte
+ if (version == RTL_VER_UNKNOWN)
+ return -ENODEV;
+
+- if (udev->actconfig->desc.bConfigurationValue != 1) {
+- usb_driver_set_configuration(udev, 1);
++ if (!rtl_vendor_mode(intf))
+ return -ENODEV;
+- }
+
+ if (intf->cur_altsetting->desc.bNumEndpoints < 3)
+ return -ENODEV;
--- /dev/null
+From fb009cbdd0693bd633f11e99526617b3d392cfad Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 8 Mar 2021 10:03:16 +0100
+Subject: [PATCH] firmware: bcm47xx_nvram: rename finding function and its
+ variables
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+1. Use "bcm47xx_" function name prefix for consistency
+2. It takes flash start as argument so s/iobase/flash_start/
+3. "off" was used for finding flash end so just call it "flash_size"
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ drivers/firmware/broadcom/bcm47xx_nvram.c | 24 ++++++++++++-----------
+ 1 file changed, 13 insertions(+), 11 deletions(-)
+
+--- a/drivers/firmware/broadcom/bcm47xx_nvram.c
++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
+@@ -48,11 +48,13 @@ static u32 find_nvram_size(void __iomem
+ return 0;
+ }
+
+-/* Probe for NVRAM header */
+-static int nvram_find_and_copy(void __iomem *iobase, u32 lim)
++/**
++ * bcm47xx_nvram_find_and_copy - find NVRAM on flash mapping & copy it
++ */
++static int bcm47xx_nvram_find_and_copy(void __iomem *flash_start, size_t res_size)
+ {
+ struct nvram_header __iomem *header;
+- u32 off;
++ size_t flash_size;
+ u32 size;
+
+ if (nvram_len) {
+@@ -61,25 +63,25 @@ static int nvram_find_and_copy(void __io
+ }
+
+ /* TODO: when nvram is on nand flash check for bad blocks first. */
+- off = FLASH_MIN;
+- while (off <= lim) {
++ flash_size = FLASH_MIN;
++ while (flash_size <= res_size) {
+ /* Windowed flash access */
+- size = find_nvram_size(iobase + off);
++ size = find_nvram_size(flash_start + flash_size);
+ if (size) {
+- header = (struct nvram_header *)(iobase + off - size);
++ header = (struct nvram_header *)(flash_start + flash_size - size);
+ goto found;
+ }
+- off <<= 1;
++ flash_size <<= 1;
+ }
+
+ /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
+- header = (struct nvram_header *)(iobase + 4096);
++ header = (struct nvram_header *)(flash_start + 4096);
+ if (header->magic == NVRAM_MAGIC) {
+ size = NVRAM_SPACE;
+ goto found;
+ }
+
+- header = (struct nvram_header *)(iobase + 1024);
++ header = (struct nvram_header *)(flash_start + 1024);
+ if (header->magic == NVRAM_MAGIC) {
+ size = NVRAM_SPACE;
+ goto found;
+@@ -124,7 +126,7 @@ int bcm47xx_nvram_init_from_mem(u32 base
+ if (!iobase)
+ return -ENOMEM;
+
+- err = nvram_find_and_copy(iobase, lim);
++ err = bcm47xx_nvram_find_and_copy(iobase, lim);
+
+ iounmap(iobase);
+
--- /dev/null
+From 0a24b51a3264a3f942a75025ea5ff6133c8989b0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 8 Mar 2021 10:03:17 +0100
+Subject: [PATCH] firmware: bcm47xx_nvram: add helper checking for NVRAM
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This avoids duplicating code doing casting and checking for NVRAM magic.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ drivers/firmware/broadcom/bcm47xx_nvram.c | 30 ++++++++++++++---------
+ 1 file changed, 18 insertions(+), 12 deletions(-)
+
+--- a/drivers/firmware/broadcom/bcm47xx_nvram.c
++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
+@@ -34,14 +34,20 @@ static char nvram_buf[NVRAM_SPACE];
+ static size_t nvram_len;
+ static const u32 nvram_sizes[] = {0x6000, 0x8000, 0xF000, 0x10000};
+
++/**
++ * bcm47xx_nvram_is_valid - check for a valid NVRAM at specified memory
++ */
++static bool bcm47xx_nvram_is_valid(void __iomem *nvram)
++{
++ return ((struct nvram_header *)nvram)->magic == NVRAM_MAGIC;
++}
++
+ static u32 find_nvram_size(void __iomem *end)
+ {
+- struct nvram_header __iomem *header;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) {
+- header = (struct nvram_header *)(end - nvram_sizes[i]);
+- if (header->magic == NVRAM_MAGIC)
++ if (bcm47xx_nvram_is_valid(end - nvram_sizes[i]))
+ return nvram_sizes[i];
+ }
+
+@@ -55,6 +61,7 @@ static int bcm47xx_nvram_find_and_copy(v
+ {
+ struct nvram_header __iomem *header;
+ size_t flash_size;
++ size_t offset;
+ u32 size;
+
+ if (nvram_len) {
+@@ -68,31 +75,30 @@ static int bcm47xx_nvram_find_and_copy(v
+ /* Windowed flash access */
+ size = find_nvram_size(flash_start + flash_size);
+ if (size) {
+- header = (struct nvram_header *)(flash_start + flash_size - size);
++ offset = flash_size - size;
+ goto found;
+ }
+ flash_size <<= 1;
+ }
+
+ /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
+- header = (struct nvram_header *)(flash_start + 4096);
+- if (header->magic == NVRAM_MAGIC) {
+- size = NVRAM_SPACE;
++
++ offset = 4096;
++ if (bcm47xx_nvram_is_valid(flash_start + offset))
+ goto found;
+- }
+
+- header = (struct nvram_header *)(flash_start + 1024);
+- if (header->magic == NVRAM_MAGIC) {
+- size = NVRAM_SPACE;
++ offset = 1024;
++ if (bcm47xx_nvram_is_valid(flash_start + offset))
+ goto found;
+- }
+
+ pr_err("no nvram found\n");
+ return -ENXIO;
+
+ found:
++ header = (struct nvram_header *)(flash_start + offset);
+ __ioread32_copy(nvram_buf, header, sizeof(*header) / 4);
+ nvram_len = ((struct nvram_header *)(nvram_buf))->len;
++ size = res_size - offset;
+ if (nvram_len > size) {
+ pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n");
+ nvram_len = size;
--- /dev/null
+From 298923cf999cecd2ef06df126f85a3d68da8c4d8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 8 Mar 2021 10:03:18 +0100
+Subject: [PATCH] firmware: bcm47xx_nvram: extract code copying NVRAM
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This simplifies function finding NVRAM. It doesn't directly deal with
+NVRAM structure anymore and is a bit smaller.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ drivers/firmware/broadcom/bcm47xx_nvram.c | 43 +++++++++++++----------
+ 1 file changed, 25 insertions(+), 18 deletions(-)
+
+--- a/drivers/firmware/broadcom/bcm47xx_nvram.c
++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
+@@ -55,11 +55,34 @@ static u32 find_nvram_size(void __iomem
+ }
+
+ /**
++ * bcm47xx_nvram_copy - copy NVRAM to internal buffer
++ */
++static void bcm47xx_nvram_copy(void __iomem *nvram_start, size_t res_size)
++{
++ struct nvram_header __iomem *header = nvram_start;
++ size_t copy_size;
++
++ copy_size = header->len;
++ if (copy_size > res_size) {
++ pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n");
++ copy_size = res_size;
++ }
++ if (copy_size >= NVRAM_SPACE) {
++ pr_err("nvram on flash (%zu bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n",
++ copy_size, NVRAM_SPACE - 1);
++ copy_size = NVRAM_SPACE - 1;
++ }
++
++ __ioread32_copy(nvram_buf, nvram_start, DIV_ROUND_UP(copy_size, 4));
++ nvram_buf[NVRAM_SPACE - 1] = '\0';
++ nvram_len = copy_size;
++}
++
++/**
+ * bcm47xx_nvram_find_and_copy - find NVRAM on flash mapping & copy it
+ */
+ static int bcm47xx_nvram_find_and_copy(void __iomem *flash_start, size_t res_size)
+ {
+- struct nvram_header __iomem *header;
+ size_t flash_size;
+ size_t offset;
+ u32 size;
+@@ -95,23 +118,7 @@ static int bcm47xx_nvram_find_and_copy(v
+ return -ENXIO;
+
+ found:
+- header = (struct nvram_header *)(flash_start + offset);
+- __ioread32_copy(nvram_buf, header, sizeof(*header) / 4);
+- nvram_len = ((struct nvram_header *)(nvram_buf))->len;
+- size = res_size - offset;
+- if (nvram_len > size) {
+- pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n");
+- nvram_len = size;
+- }
+- if (nvram_len >= NVRAM_SPACE) {
+- pr_err("nvram on flash (%zu bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n",
+- nvram_len, NVRAM_SPACE - 1);
+- nvram_len = NVRAM_SPACE - 1;
+- }
+- /* proceed reading data after header */
+- __ioread32_copy(nvram_buf + sizeof(*header), header + 1,
+- DIV_ROUND_UP(nvram_len, 4));
+- nvram_buf[NVRAM_SPACE - 1] = '\0';
++ bcm47xx_nvram_copy(flash_start + offset, res_size - offset);
+
+ return 0;
+ }
--- /dev/null
+From 98b68324f67236e8c9152976535dc1f27fb67ba8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 8 Mar 2021 10:03:19 +0100
+Subject: [PATCH] firmware: bcm47xx_nvram: look for NVRAM with for instead of
+ while
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This loop requires variable initialization, stop condition and post
+iteration increment. It's pretty much a for loop definition.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ drivers/firmware/broadcom/bcm47xx_nvram.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/firmware/broadcom/bcm47xx_nvram.c
++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
+@@ -93,15 +93,13 @@ static int bcm47xx_nvram_find_and_copy(v
+ }
+
+ /* TODO: when nvram is on nand flash check for bad blocks first. */
+- flash_size = FLASH_MIN;
+- while (flash_size <= res_size) {
++ for (flash_size = FLASH_MIN; flash_size <= res_size; flash_size <<= 1) {
+ /* Windowed flash access */
+ size = find_nvram_size(flash_start + flash_size);
+ if (size) {
+ offset = flash_size - size;
+ goto found;
+ }
+- flash_size <<= 1;
+ }
+
+ /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
--- /dev/null
+From f52da4ccfec9192e17f5c16260dfdd6d3ea76f65 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 8 Mar 2021 10:03:20 +0100
+Subject: [PATCH] firmware: bcm47xx_nvram: inline code checking NVRAM size
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Separated function was not improving code quality much (or at all).
+Moreover it expected possible flash end address as argument and it was
+returning NVRAM size.
+
+The new code always operates on offsets which means less logic and less
+calculations.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ drivers/firmware/broadcom/bcm47xx_nvram.c | 25 +++++++----------------
+ 1 file changed, 7 insertions(+), 18 deletions(-)
+
+--- a/drivers/firmware/broadcom/bcm47xx_nvram.c
++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
+@@ -42,18 +42,6 @@ static bool bcm47xx_nvram_is_valid(void
+ return ((struct nvram_header *)nvram)->magic == NVRAM_MAGIC;
+ }
+
+-static u32 find_nvram_size(void __iomem *end)
+-{
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) {
+- if (bcm47xx_nvram_is_valid(end - nvram_sizes[i]))
+- return nvram_sizes[i];
+- }
+-
+- return 0;
+-}
+-
+ /**
+ * bcm47xx_nvram_copy - copy NVRAM to internal buffer
+ */
+@@ -85,7 +73,7 @@ static int bcm47xx_nvram_find_and_copy(v
+ {
+ size_t flash_size;
+ size_t offset;
+- u32 size;
++ int i;
+
+ if (nvram_len) {
+ pr_warn("nvram already initialized\n");
+@@ -93,12 +81,13 @@ static int bcm47xx_nvram_find_and_copy(v
+ }
+
+ /* TODO: when nvram is on nand flash check for bad blocks first. */
++
++ /* Try every possible flash size and check for NVRAM at its end */
+ for (flash_size = FLASH_MIN; flash_size <= res_size; flash_size <<= 1) {
+- /* Windowed flash access */
+- size = find_nvram_size(flash_start + flash_size);
+- if (size) {
+- offset = flash_size - size;
+- goto found;
++ for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) {
++ offset = flash_size - nvram_sizes[i];
++ if (bcm47xx_nvram_is_valid(flash_start + offset))
++ goto found;
+ }
+ }
+
--- /dev/null
+From 2d5ba37461013253d2ff0a3641b727fd32ea97a9 Mon Sep 17 00:00:00 2001
+From: Florian Fainelli <florian@openwrt.org>
+Date: Tue, 23 Feb 2021 18:44:53 +0100
+Subject: [PATCH 1/3] usb: ehci: add spurious flag to disable overcurrent
+ checking
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch adds an ignore_oc flag which can be set by EHCI controller
+not supporting or wanting to disable overcurrent checking. The EHCI
+platform data in include/linux/usb/ehci_pdriver.h is also augmented to
+take advantage of this new flag.
+
+Signed-off-by: Florian Fainelli <florian@openwrt.org>
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Link: https://lore.kernel.org/r/20210223174455.1378-2-noltari@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/host/ehci-hcd.c | 2 +-
+ drivers/usb/host/ehci-hub.c | 4 ++--
+ drivers/usb/host/ehci-platform.c | 2 ++
+ drivers/usb/host/ehci.h | 1 +
+ include/linux/usb/ehci_pdriver.h | 1 +
+ 5 files changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/usb/host/ehci-hcd.c
++++ b/drivers/usb/host/ehci-hcd.c
+@@ -660,7 +660,7 @@ static int ehci_run (struct usb_hcd *hcd
+ "USB %x.%x started, EHCI %x.%02x%s\n",
+ ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
+ temp >> 8, temp & 0xff,
+- ignore_oc ? ", overcurrent ignored" : "");
++ (ignore_oc || ehci->spurious_oc) ? ", overcurrent ignored" : "");
+
+ ehci_writel(ehci, INTR_MASK,
+ &ehci->regs->intr_enable); /* Turn On Interrupts */
+--- a/drivers/usb/host/ehci-hub.c
++++ b/drivers/usb/host/ehci-hub.c
+@@ -643,7 +643,7 @@ ehci_hub_status_data (struct usb_hcd *hc
+ * always set, seem to clear PORT_OCC and PORT_CSC when writing to
+ * PORT_POWER; that's surprising, but maybe within-spec.
+ */
+- if (!ignore_oc)
++ if (!ignore_oc && !ehci->spurious_oc)
+ mask = PORT_CSC | PORT_PEC | PORT_OCC;
+ else
+ mask = PORT_CSC | PORT_PEC;
+@@ -1013,7 +1013,7 @@ int ehci_hub_control(
+ if (temp & PORT_PEC)
+ status |= USB_PORT_STAT_C_ENABLE << 16;
+
+- if ((temp & PORT_OCC) && !ignore_oc){
++ if ((temp & PORT_OCC) && (!ignore_oc && !ehci->spurious_oc)){
+ status |= USB_PORT_STAT_C_OVERCURRENT << 16;
+
+ /*
+--- a/drivers/usb/host/ehci-platform.c
++++ b/drivers/usb/host/ehci-platform.c
+@@ -333,6 +333,8 @@ static int ehci_platform_probe(struct pl
+ hcd->has_tt = 1;
+ if (pdata->reset_on_resume)
+ priv->reset_on_resume = true;
++ if (pdata->spurious_oc)
++ ehci->spurious_oc = 1;
+
+ #ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
+ if (ehci->big_endian_mmio) {
+--- a/drivers/usb/host/ehci.h
++++ b/drivers/usb/host/ehci.h
+@@ -219,6 +219,7 @@ struct ehci_hcd { /* one per controlle
+ unsigned need_oc_pp_cycle:1; /* MPC834X port power */
+ unsigned imx28_write_fix:1; /* For Freescale i.MX28 */
+ unsigned is_aspeed:1;
++ unsigned spurious_oc:1;
+
+ /* required for usb32 quirk */
+ #define OHCI_CTRL_HCFS (3 << 6)
+--- a/include/linux/usb/ehci_pdriver.h
++++ b/include/linux/usb/ehci_pdriver.h
+@@ -50,6 +50,7 @@ struct usb_ehci_pdata {
+ unsigned no_io_watchdog:1;
+ unsigned reset_on_resume:1;
+ unsigned dma_mask_64:1;
++ unsigned spurious_oc:1;
+
+ /* Turn on all power and clocks */
+ int (*power_on)(struct platform_device *pdev);
--- /dev/null
+From 4da57dbbffdfa7fe4e2b70b047fc5ff95ff25a3d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
+Date: Tue, 23 Feb 2021 18:44:55 +0100
+Subject: [PATCH 3/3] usb: host: ehci-platform: add spurious_oc DT support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Over-current reporting isn't supported on some platforms such as bcm63xx.
+These devices will incorrectly report over-current if this flag isn't properly
+activated.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Link: https://lore.kernel.org/r/20210223174455.1378-4-noltari@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/host/ehci-platform.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/usb/host/ehci-platform.c
++++ b/drivers/usb/host/ehci-platform.c
+@@ -286,6 +286,9 @@ static int ehci_platform_probe(struct pl
+ if (of_property_read_bool(dev->dev.of_node, "big-endian"))
+ ehci->big_endian_mmio = ehci->big_endian_desc = 1;
+
++ if (of_property_read_bool(dev->dev.of_node, "spurious-oc"))
++ ehci->spurious_oc = 1;
++
+ if (of_property_read_bool(dev->dev.of_node,
+ "needs-reset-on-resume"))
+ priv->reset_on_resume = true;
--- /dev/null
+From 9ec37efb87832b578d7972fc80b04d94f5d2bbe3 Mon Sep 17 00:00:00 2001
+From: Marc Zyngier <maz@kernel.org>
+Date: Tue, 30 Mar 2021 16:11:42 +0100
+Subject: PCI/MSI: Make pci_host_common_probe() declare its reliance on MSI
+ domains
+
+The generic PCI host driver relies on MSI domains for MSIs to
+be provided to its end-points. Make this dependency explicit.
+
+This cures the warnings occuring on arm/arm64 VMs when booted
+with PCI virtio devices and no MSI controller (no GICv3 ITS,
+for example).
+
+It is likely that other drivers will need to express the same
+dependency.
+
+Link: https://lore.kernel.org/r/20210330151145.997953-12-maz@kernel.org
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Acked-by: Bjorn Helgaas <bhelgaas@google.com>
+---
+ drivers/pci/controller/pci-host-common.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/pci/controller/pci-host-common.c
++++ b/drivers/pci/controller/pci-host-common.c
+@@ -77,6 +77,7 @@ int pci_host_common_probe(struct platfor
+
+ bridge->sysdata = cfg;
+ bridge->ops = (struct pci_ops *)&ops->pci_ops;
++ bridge->msi_domain = true;
+
+ platform_set_drvdata(pdev, bridge);
+
--- /dev/null
+From 94e89b145371b68fa0ea294855adebcd03e0522e Mon Sep 17 00:00:00 2001
+From: Marc Zyngier <maz@kernel.org>
+Date: Tue, 30 Mar 2021 16:11:41 +0100
+Subject: PCI/MSI: Let PCI host bridges declare their reliance on MSI domains
+
+There is a whole class of host bridges that cannot know whether
+MSIs will be provided or not, as they rely on other blocks
+to provide the MSI functionnality, using MSI domains. This is
+the case for example on systems that use the ARM GIC architecture.
+
+Introduce a new attribute ('msi_domain') indicating that implicit
+dependency, and use this property to set the NO_MSI flag when
+no MSI domain is found at probe time.
+
+Link: https://lore.kernel.org/r/20210330151145.997953-11-maz@kernel.org
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Acked-by: Bjorn Helgaas <bhelgaas@google.com>
+---
+ drivers/pci/probe.c | 2 ++
+ include/linux/pci.h | 1 +
+ 2 files changed, 3 insertions(+)
+
+--- a/drivers/pci/probe.c
++++ b/drivers/pci/probe.c
+@@ -925,6 +925,8 @@ static int pci_register_host_bridge(stru
+ device_enable_async_suspend(bus->bridge);
+ pci_set_bus_of_node(bus);
+ pci_set_bus_msi_domain(bus);
++ if (bridge->msi_domain && !dev_get_msi_domain(&bus->dev))
++ bus->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+
+ if (!parent)
+ set_dev_node(bus->bridge, pcibus_to_node(bus));
+--- a/include/linux/pci.h
++++ b/include/linux/pci.h
+@@ -547,6 +547,7 @@ struct pci_host_bridge {
+ unsigned int native_dpc:1; /* OS may use PCIe DPC */
+ unsigned int preserve_config:1; /* Preserve FW resource setup */
+ unsigned int size_windows:1; /* Enable root bus sizing */
++ unsigned int msi_domain:1; /* Bridge wants MSI domain */
+
+ /* Resource alignment requirements */
+ resource_size_t (*align_resource)(struct pci_dev *dev,
--- /dev/null
+From 645e9c38383d7fcde2784ee537fa18ec9bed54d9 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Tue, 30 Mar 2021 16:11:43 +0100
+Subject: PCI: mediatek: Advertise lack of built-in MSI handling
+
+Some Mediatek host bridges cannot handle MSIs, which is sad.
+This also results in an ugly warning at device probe time,
+as the core PCI code wasn't told that MSIs were not available.
+
+Advertise this fact to the rest of the core PCI code by
+using the 'msi_domain' attribute, which still opens the possibility
+for another block to provide the MSI functionnality.
+
+[maz: commit message, switched over to msi_domain attribute]
+
+Link: https://lore.kernel.org/r/20210330151145.997953-13-maz@kernel.org
+Reported-by: Frank Wunderlich <frank-w@public-files.de>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Acked-by: Bjorn Helgaas <bhelgaas@google.com>
+---
+ drivers/pci/controller/pcie-mediatek.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/pci/controller/pcie-mediatek.c
++++ b/drivers/pci/controller/pcie-mediatek.c
+@@ -143,6 +143,7 @@ struct mtk_pcie_port;
+ * struct mtk_pcie_soc - differentiate between host generations
+ * @need_fix_class_id: whether this host's class ID needed to be fixed or not
+ * @need_fix_device_id: whether this host's device ID needed to be fixed or not
++ * @no_msi: Bridge has no MSI support, and relies on an external block
+ * @device_id: device ID which this host need to be fixed
+ * @ops: pointer to configuration access functions
+ * @startup: pointer to controller setting functions
+@@ -151,6 +152,7 @@ struct mtk_pcie_port;
+ struct mtk_pcie_soc {
+ bool need_fix_class_id;
+ bool need_fix_device_id;
++ bool no_msi;
+ unsigned int device_id;
+ struct pci_ops *ops;
+ int (*startup)(struct mtk_pcie_port *port);
+@@ -1087,6 +1089,7 @@ static int mtk_pcie_probe(struct platfor
+
+ host->ops = pcie->soc->ops;
+ host->sysdata = pcie;
++ host->msi_domain = pcie->soc->no_msi;
+
+ err = pci_host_probe(host);
+ if (err)
+@@ -1176,6 +1179,7 @@ static const struct dev_pm_ops mtk_pcie_
+ };
+
+ static const struct mtk_pcie_soc mtk_pcie_soc_v1 = {
++ .no_msi = true,
+ .ops = &mtk_pcie_ops,
+ .startup = mtk_pcie_startup_port,
+ };
--- /dev/null
+From a5d3d1adc95f4ac5968b7a77ee95a3abbbb96f49 Mon Sep 17 00:00:00 2001
+From: Doug Zobel <dougdev334@gmail.com>
+Date: Mon, 10 May 2021 15:40:00 -0500
+Subject: [PATCH] leds: lp55xx: Initialize enable GPIO direction to output
+
+The "Convert to use GPIO descriptors" commit changed the
+initialization of the enable GPIO from GPIOF_DIR_OUT to
+GPIOD_ASIS. This breaks systems where the GPIO does not
+default to output. Changing the enable initialization
+to GPIOD_OUT_LOW.
+
+Signed-off-by: Doug Zobel <dougdev334@gmail.com>
+Signed-off-by: Pavel Machek <pavel@ucw.cz>
+---
+ drivers/leds/leds-lp55xx-common.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/leds/leds-lp55xx-common.c
++++ b/drivers/leds/leds-lp55xx-common.c
+@@ -694,7 +694,7 @@ struct lp55xx_platform_data *lp55xx_of_p
+ of_property_read_u8(np, "clock-mode", &pdata->clock_mode);
+
+ pdata->enable_gpiod = devm_gpiod_get_optional(dev, "enable",
+- GPIOD_ASIS);
++ GPIOD_OUT_LOW);
+ if (IS_ERR(pdata->enable_gpiod))
+ return ERR_CAST(pdata->enable_gpiod);
+
--- /dev/null
+From 9319230ac147067652b58fe849ffe0ceec098665 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 30 Nov 2021 18:29:03 +0100
+Subject: [PATCH] PCI: pci-bridge-emul: Add description for class_revision
+ field
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The current assignment to the class_revision member
+
+ class_revision |= cpu_to_le32(PCI_CLASS_BRIDGE_PCI << 16);
+
+can make the reader think that class is at high 16 bits of the member and
+revision at low 16 bits.
+
+In reality, class is at high 24 bits, but the class for PCI Bridge Normal
+Decode is PCI_CLASS_BRIDGE_PCI << 8.
+
+Change the assignment and add a comment to make this clearer.
+
+Link: https://lore.kernel.org/r/20211130172913.9727-2-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/pci-bridge-emul.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/pci-bridge-emul.c
++++ b/drivers/pci/pci-bridge-emul.c
+@@ -284,7 +284,11 @@ int pci_bridge_emul_init(struct pci_brid
+ {
+ BUILD_BUG_ON(sizeof(bridge->conf) != PCI_BRIDGE_CONF_END);
+
+- bridge->conf.class_revision |= cpu_to_le32(PCI_CLASS_BRIDGE_PCI << 16);
++ /*
++ * class_revision: Class is high 24 bits and revision is low 8 bit of this member,
++ * while class for PCI Bridge Normal Decode has the 24-bit value: PCI_CLASS_BRIDGE_PCI << 8
++ */
++ bridge->conf.class_revision |= cpu_to_le32((PCI_CLASS_BRIDGE_PCI << 8) << 8);
+ bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE;
+ bridge->conf.cache_line_size = 0x10;
+ bridge->conf.status = cpu_to_le16(PCI_STATUS_CAP_LIST);
--- /dev/null
+From 8ea673a8b30b4a32516b8adabb15e2a68ff02ec8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 30 Nov 2021 18:29:04 +0100
+Subject: [PATCH] PCI: pci-bridge-emul: Add definitions for missing
+ capabilities registers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+pci-bridge-emul driver already allocates buffer for capabilities up to the
+PCI_EXP_SLTSTA2 register, but does not define bit access behavior for these
+registers. Add these missing definitions.
+
+Link: https://lore.kernel.org/r/20211130172913.9727-3-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/pci-bridge-emul.c | 43 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 43 insertions(+)
+
+--- a/drivers/pci/pci-bridge-emul.c
++++ b/drivers/pci/pci-bridge-emul.c
+@@ -270,6 +270,49 @@ struct pci_bridge_reg_behavior pcie_cap_
+ .ro = GENMASK(15, 0) | PCI_EXP_RTSTA_PENDING,
+ .w1c = PCI_EXP_RTSTA_PME,
+ },
++
++ [PCI_EXP_DEVCAP2 / 4] = {
++ /*
++ * Device capabilities 2 register has reserved bits [30:27].
++ * Also bits [26:24] are reserved for non-upstream ports.
++ */
++ .ro = BIT(31) | GENMASK(23, 0),
++ },
++
++ [PCI_EXP_DEVCTL2 / 4] = {
++ /*
++ * Device control 2 register is RW. Bit 11 is reserved for
++ * non-upstream ports.
++ *
++ * Device status 2 register is reserved.
++ */
++ .rw = GENMASK(15, 12) | GENMASK(10, 0),
++ },
++
++ [PCI_EXP_LNKCAP2 / 4] = {
++ /* Link capabilities 2 register has reserved bits [30:25] and 0. */
++ .ro = BIT(31) | GENMASK(24, 1),
++ },
++
++ [PCI_EXP_LNKCTL2 / 4] = {
++ /*
++ * Link control 2 register is RW.
++ *
++ * Link status 2 register has bits 5, 15 W1C;
++ * bits 10, 11 reserved and others are RO.
++ */
++ .rw = GENMASK(15, 0),
++ .w1c = (BIT(15) | BIT(5)) << 16,
++ .ro = (GENMASK(14, 12) | GENMASK(9, 6) | GENMASK(4, 0)) << 16,
++ },
++
++ [PCI_EXP_SLTCAP2 / 4] = {
++ /* Slot capabilities 2 register is reserved. */
++ },
++
++ [PCI_EXP_SLTCTL2 / 4] = {
++ /* Both Slot control 2 and Slot status 2 registers are reserved. */
++ },
+ };
+
+ /*
--- /dev/null
+From 1d3e170344dff2cef8827db6c09909b78cbc11d7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 30 Nov 2021 18:29:05 +0100
+Subject: [PATCH] PCI: aardvark: Add support for DEVCAP2, DEVCTL2, LNKCAP2 and
+ LNKCTL2 registers on emulated bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+PCI aardvark hardware supports access to DEVCAP2, DEVCTL2, LNKCAP2 and
+LNKCTL2 configuration registers of PCIe core via PCIE_CORE_PCIEXP_CAP.
+Export them via emulated software root bridge.
+
+Link: https://lore.kernel.org/r/20211130172913.9727-4-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -881,8 +881,13 @@ advk_pci_bridge_emul_pcie_conf_read(stru
+
+ case PCI_EXP_DEVCAP:
+ case PCI_EXP_DEVCTL:
++ case PCI_EXP_DEVCAP2:
++ case PCI_EXP_DEVCTL2:
++ case PCI_EXP_LNKCAP2:
++ case PCI_EXP_LNKCTL2:
+ *value = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg);
+ return PCI_BRIDGE_EMUL_HANDLED;
++
+ default:
+ return PCI_BRIDGE_EMUL_NOT_HANDLED;
+ }
+@@ -896,10 +901,6 @@ advk_pci_bridge_emul_pcie_conf_write(str
+ struct advk_pcie *pcie = bridge->data;
+
+ switch (reg) {
+- case PCI_EXP_DEVCTL:
+- advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg);
+- break;
+-
+ case PCI_EXP_LNKCTL:
+ advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg);
+ if (new & PCI_EXP_LNKCTL_RL)
+@@ -921,6 +922,12 @@ advk_pci_bridge_emul_pcie_conf_write(str
+ advk_writel(pcie, new, PCIE_ISR0_REG);
+ break;
+
++ case PCI_EXP_DEVCTL:
++ case PCI_EXP_DEVCTL2:
++ case PCI_EXP_LNKCTL2:
++ advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg);
++ break;
++
+ default:
+ break;
+ }
--- /dev/null
+From 7d8dc1f7cd007a7ce94c5b4c20d63a8b8d6d7751 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 30 Nov 2021 18:29:06 +0100
+Subject: [PATCH] PCI: aardvark: Clear all MSIs at setup
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+We already clear all the other interrupts (ISR0, ISR1, HOST_CTRL_INT).
+
+Define a new macro PCIE_MSI_ALL_MASK and do the same clearing for MSIs,
+to ensure that we don't start receiving spurious interrupts.
+
+Use this new mask in advk_pcie_handle_msi();
+
+Link: https://lore.kernel.org/r/20211130172913.9727-5-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -114,6 +114,7 @@
+ #define PCIE_MSI_ADDR_HIGH_REG (CONTROL_BASE_ADDR + 0x54)
+ #define PCIE_MSI_STATUS_REG (CONTROL_BASE_ADDR + 0x58)
+ #define PCIE_MSI_MASK_REG (CONTROL_BASE_ADDR + 0x5C)
++#define PCIE_MSI_ALL_MASK GENMASK(31, 0)
+ #define PCIE_MSI_PAYLOAD_REG (CONTROL_BASE_ADDR + 0x9C)
+ #define PCIE_MSI_DATA_MASK GENMASK(15, 0)
+
+@@ -577,6 +578,7 @@ static void advk_pcie_setup_hw(struct ad
+ advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG);
+
+ /* Clear all interrupts */
++ advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_STATUS_REG);
+ advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG);
+ advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
+ advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
+@@ -589,7 +591,7 @@ static void advk_pcie_setup_hw(struct ad
+ advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
+
+ /* Unmask all MSIs */
+- advk_writel(pcie, 0, PCIE_MSI_MASK_REG);
++ advk_writel(pcie, ~(u32)PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
+
+ /* Enable summary interrupt for GIC SPI source */
+ reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK);
+@@ -1399,7 +1401,7 @@ static void advk_pcie_handle_msi(struct
+
+ msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
+ msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG);
+- msi_status = msi_val & ~msi_mask;
++ msi_status = msi_val & ((~msi_mask) & PCIE_MSI_ALL_MASK);
+
+ for (msi_idx = 0; msi_idx < MSI_IRQ_NUM; msi_idx++) {
+ if (!(BIT(msi_idx) & msi_status))
--- /dev/null
+From a4ca7948e1d47275f8f3e5023243440c40561916 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 30 Nov 2021 18:29:07 +0100
+Subject: [PATCH] PCI: aardvark: Comment actions in driver remove method
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add two more comments into the advk_pcie_remove() method.
+
+Link: https://lore.kernel.org/r/20211130172913.9727-6-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1698,11 +1698,13 @@ static int advk_pcie_remove(struct platf
+ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
+ int i;
+
++ /* Remove PCI bus with all devices */
+ pci_lock_rescan_remove();
+ pci_stop_root_bus(bridge->bus);
+ pci_remove_root_bus(bridge->bus);
+ pci_unlock_rescan_remove();
+
++ /* Remove IRQ domains */
+ advk_pcie_remove_msi_irq_domain(pcie);
+ advk_pcie_remove_irq_domain(pcie);
+
--- /dev/null
+From a46f2f6dd4093438d9615dfbf5c0fea2a9835dba Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 30 Nov 2021 18:29:08 +0100
+Subject: [PATCH] PCI: aardvark: Disable bus mastering when unbinding driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Ensure that after driver unbind PCIe cards are not able to forward
+memory and I/O requests in the upstream direction.
+
+Link: https://lore.kernel.org/r/20211130172913.9727-7-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1696,6 +1696,7 @@ static int advk_pcie_remove(struct platf
+ {
+ struct advk_pcie *pcie = platform_get_drvdata(pdev);
+ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
++ u32 val;
+ int i;
+
+ /* Remove PCI bus with all devices */
+@@ -1704,6 +1705,11 @@ static int advk_pcie_remove(struct platf
+ pci_remove_root_bus(bridge->bus);
+ pci_unlock_rescan_remove();
+
++ /* Disable Root Bridge I/O space, memory space and bus mastering */
++ val = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG);
++ val &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
++ advk_writel(pcie, val, PCIE_CORE_CMD_STATUS_REG);
++
+ /* Remove IRQ domains */
+ advk_pcie_remove_msi_irq_domain(pcie);
+ advk_pcie_remove_irq_domain(pcie);
--- /dev/null
+From 13bcdf07cb2ecff5d45d2c141df2539b15211448 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 30 Nov 2021 18:29:09 +0100
+Subject: [PATCH] PCI: aardvark: Mask all interrupts when unbinding driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Ensure that no interrupt can be triggered after driver unbind.
+
+Link: https://lore.kernel.org/r/20211130172913.9727-8-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1710,6 +1710,27 @@ static int advk_pcie_remove(struct platf
+ val &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ advk_writel(pcie, val, PCIE_CORE_CMD_STATUS_REG);
+
++ /* Disable MSI */
++ val = advk_readl(pcie, PCIE_CORE_CTRL2_REG);
++ val &= ~PCIE_CORE_CTRL2_MSI_ENABLE;
++ advk_writel(pcie, val, PCIE_CORE_CTRL2_REG);
++
++ /* Clear MSI address */
++ advk_writel(pcie, 0, PCIE_MSI_ADDR_LOW_REG);
++ advk_writel(pcie, 0, PCIE_MSI_ADDR_HIGH_REG);
++
++ /* Mask all interrupts */
++ advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
++ advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_MASK_REG);
++ advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
++ advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_MASK_REG);
++
++ /* Clear all interrupts */
++ advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_STATUS_REG);
++ advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG);
++ advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
++ advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
++
+ /* Remove IRQ domains */
+ advk_pcie_remove_msi_irq_domain(pcie);
+ advk_pcie_remove_irq_domain(pcie);
--- /dev/null
+From 2f040a17f5061457ae95035326d3159eddc1e5cc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 30 Nov 2021 18:29:10 +0100
+Subject: [PATCH] PCI: aardvark: Fix memory leak in driver unbind
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Free config space for emulated root bridge when unbinding driver to fix
+memory leak. Do it after disabling and masking all interrupts, since
+aardvark interrupt handler accesses config space of emulated root
+bridge.
+
+Link: https://lore.kernel.org/r/20211130172913.9727-9-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1735,6 +1735,9 @@ static int advk_pcie_remove(struct platf
+ advk_pcie_remove_msi_irq_domain(pcie);
+ advk_pcie_remove_irq_domain(pcie);
+
++ /* Free config space for emulated root bridge */
++ pci_bridge_emul_cleanup(&pcie->bridge);
++
+ /* Disable outbound address windows mapping */
+ for (i = 0; i < OB_WIN_COUNT; i++)
+ advk_pcie_disable_ob_win(pcie, i);
--- /dev/null
+From 1f54391be8ce0c981d312cb93acdc5608def576a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 30 Nov 2021 18:29:11 +0100
+Subject: [PATCH] PCI: aardvark: Assert PERST# when unbinding driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Put the PCIe card into reset by asserting PERST# signal when unbinding
+driver. It doesn't make sense to leave the card working if it can't
+communicate with the host. This should also save some power.
+
+Link: https://lore.kernel.org/r/20211130172913.9727-10-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1738,6 +1738,10 @@ static int advk_pcie_remove(struct platf
+ /* Free config space for emulated root bridge */
+ pci_bridge_emul_cleanup(&pcie->bridge);
+
++ /* Assert PERST# signal which prepares PCIe card for power down */
++ if (pcie->reset_gpio)
++ gpiod_set_value_cansleep(pcie->reset_gpio, 1);
++
+ /* Disable outbound address windows mapping */
+ for (i = 0; i < OB_WIN_COUNT; i++)
+ advk_pcie_disable_ob_win(pcie, i);
--- /dev/null
+From 759dec2e3dfdbd261c41d2279f04f2351c971a49 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 30 Nov 2021 18:29:12 +0100
+Subject: [PATCH] PCI: aardvark: Disable link training when unbinding driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Disable link training circuit in driver unbind sequence. We want to
+leave link training in the same state as it was before the driver was
+probed.
+
+Link: https://lore.kernel.org/r/20211130172913.9727-11-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1742,6 +1742,11 @@ static int advk_pcie_remove(struct platf
+ if (pcie->reset_gpio)
+ gpiod_set_value_cansleep(pcie->reset_gpio, 1);
+
++ /* Disable link training */
++ val = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
++ val &= ~LINK_TRAINING_EN;
++ advk_writel(pcie, val, PCIE_CORE_CTRL0_REG);
++
+ /* Disable outbound address windows mapping */
+ for (i = 0; i < OB_WIN_COUNT; i++)
+ advk_pcie_disable_ob_win(pcie, i);
--- /dev/null
+From fdbbe242c15a8f2cd0e3ad8a56cd0a447b771d0d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 30 Nov 2021 18:29:13 +0100
+Subject: [PATCH] PCI: aardvark: Disable common PHY when unbinding driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Disable the PCIe PHY when unbinding driver. This should save some power.
+
+Link: https://lore.kernel.org/r/20211130172913.9727-12-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1751,6 +1751,9 @@ static int advk_pcie_remove(struct platf
+ for (i = 0; i < OB_WIN_COUNT; i++)
+ advk_pcie_disable_ob_win(pcie, i);
+
++ /* Disable phy */
++ advk_pcie_disable_phy(pcie);
++
+ return 0;
+ }
+
--- /dev/null
+From 40da06da15c1718b02072687bbfb2d08f5eb9399 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 27 Aug 2021 11:27:52 +0200
+Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Rename HS-SGMMI to
+ 2500Base-X
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Comphy phy mode 0x3 is incorrectly named. It is not SGMII but rather
+2500Base-X mode which runs at 3.125 Gbps speed.
+
+Rename macro names and comments to 2500Base-X.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Fixes: 9695375a3f4a ("phy: add A3700 COMPHY support")
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
+@@ -29,7 +29,7 @@
+
+ #define COMPHY_FW_MODE_SATA 0x1
+ #define COMPHY_FW_MODE_SGMII 0x2
+-#define COMPHY_FW_MODE_HS_SGMII 0x3
++#define COMPHY_FW_MODE_2500BASEX 0x3
+ #define COMPHY_FW_MODE_USB3H 0x4
+ #define COMPHY_FW_MODE_USB3D 0x5
+ #define COMPHY_FW_MODE_PCIE 0x6
+@@ -40,7 +40,7 @@
+
+ #define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */
+ #define COMPHY_FW_SPEED_2_5G 1
+-#define COMPHY_FW_SPEED_3_125G 2 /* SGMII 2.5G */
++#define COMPHY_FW_SPEED_3_125G 2 /* 2500BASE-X */
+ #define COMPHY_FW_SPEED_5G 3
+ #define COMPHY_FW_SPEED_5_15625G 4 /* XFI 5G */
+ #define COMPHY_FW_SPEED_6G 5
+@@ -84,14 +84,14 @@ static const struct mvebu_a3700_comphy_c
+ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1,
+ COMPHY_FW_MODE_SGMII),
+ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1,
+- COMPHY_FW_MODE_HS_SGMII),
++ COMPHY_FW_MODE_2500BASEX),
+ /* lane 1 */
+ MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0,
+ COMPHY_FW_MODE_PCIE),
+ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0,
+ COMPHY_FW_MODE_SGMII),
+ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0,
+- COMPHY_FW_MODE_HS_SGMII),
++ COMPHY_FW_MODE_2500BASEX),
+ /* lane 2 */
+ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0,
+ COMPHY_FW_MODE_SATA),
+@@ -205,7 +205,7 @@ static int mvebu_a3700_comphy_power_on(s
+ COMPHY_FW_SPEED_1_25G);
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+- dev_dbg(lane->dev, "set lane %d to HS SGMII mode\n",
++ dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n",
+ lane->id);
+ fw_param = COMPHY_FW_NET(fw_mode, lane->port,
+ COMPHY_FW_SPEED_3_125G);
--- /dev/null
+From e1dbe9ecf621b6f71f3d2df3e50731d583f3d27f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 27 Aug 2021 11:27:53 +0200
+Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Remove unsupported
+ modes
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Armada 3700 does not support RXAUI, XFI and neither SFI. Remove unused
+macros for these unsupported modes.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Fixes: 9695375a3f4a ("phy: add A3700 COMPHY support")
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 6 ------
+ 1 file changed, 6 deletions(-)
+
+--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
+@@ -33,18 +33,12 @@
+ #define COMPHY_FW_MODE_USB3H 0x4
+ #define COMPHY_FW_MODE_USB3D 0x5
+ #define COMPHY_FW_MODE_PCIE 0x6
+-#define COMPHY_FW_MODE_RXAUI 0x7
+-#define COMPHY_FW_MODE_XFI 0x8
+-#define COMPHY_FW_MODE_SFI 0x9
+ #define COMPHY_FW_MODE_USB3 0xa
+
+ #define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */
+ #define COMPHY_FW_SPEED_2_5G 1
+ #define COMPHY_FW_SPEED_3_125G 2 /* 2500BASE-X */
+ #define COMPHY_FW_SPEED_5G 3
+-#define COMPHY_FW_SPEED_5_15625G 4 /* XFI 5G */
+-#define COMPHY_FW_SPEED_6G 5
+-#define COMPHY_FW_SPEED_10_3125G 6 /* XFI 10G */
+ #define COMPHY_FW_SPEED_MAX 0x3F
+
+ #define COMPHY_FW_MODE(mode) ((mode) << 12)
--- /dev/null
+From d3115128bdafb62628ab41861a4f06f6d02ac320 Mon Sep 17 00:00:00 2001
+From: Lech Perczak <lech.perczak@gmail.com>
+Date: Mon, 10 Jan 2022 23:48:44 +0100
+Subject: MIPS: ath79: drop _machine_restart again
+
+Commit 81424d0ad0d4 ("MIPS: ath79: Use the reset controller to restart
+OF machines") removed setup of _machine_restart on OF machines to use
+reset handler in reset controller driver.
+While removing remnants of non-OF machines in commit 3a77e0d75eed
+("MIPS: ath79: drop machfiles"), this was introduced again, making it
+impossible to use additional restart handlers registered through device
+tree. Drop setting _machine_restart altogether, and ath79_restart
+function, which is no longer used after this.
+
+Fixes: 3a77e0d75eed ("MIPS: ath79: drop machfiles")
+Cc: John Crispin <john@phrozen.org>
+Cc: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Lech Perczak <lech.perczak@gmail.com>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ arch/mips/ath79/setup.c | 10 ----------
+ 1 file changed, 10 deletions(-)
+
+--- a/arch/mips/ath79/setup.c
++++ b/arch/mips/ath79/setup.c
+@@ -34,15 +34,6 @@
+
+ static char ath79_sys_type[ATH79_SYS_TYPE_LEN];
+
+-static void ath79_restart(char *command)
+-{
+- local_irq_disable();
+- ath79_device_reset_set(AR71XX_RESET_FULL_CHIP);
+- for (;;)
+- if (cpu_wait)
+- cpu_wait();
+-}
+-
+ static void ath79_halt(void)
+ {
+ while (1)
+@@ -233,7 +224,6 @@ void __init plat_mem_setup(void)
+
+ detect_memory_region(0, ATH79_MEM_SIZE_MIN, ATH79_MEM_SIZE_MAX);
+
+- _machine_restart = ath79_restart;
+ _machine_halt = ath79_halt;
+ pm_power_off = ath79_halt;
+ }
--- /dev/null
+From 31d8f414e1596ba54a4315418e4c0086fda9e428 Mon Sep 17 00:00:00 2001
+From: Christian Lamparter <chunkeey@gmail.com>
+Date: Fri, 18 Feb 2022 10:06:43 +0100
+Subject: hwmon: (lm70) Add ti,tmp125 support
+
+The TMP125 is a 2 degree Celsius accurate Digital
+Temperature Sensor with a SPI interface.
+
+The temperature register is a 16-bit, read-only register.
+The MSB (Bit 15) is a leading zero and never set. Bits 14
+to 5 are the 1+9 temperature data bits in a two's
+complement format. Bits 4 to 0 are useless copies of
+Bit 5 value and therefore ignored.
+
+Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
+Link: https://lore.kernel.org/r/43b19cbd4e7f51e9509e561b02b5d8d0e7079fac.1645175187.git.chunkeey@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+--- a/drivers/hwmon/lm70.c
++++ b/drivers/hwmon/lm70.c
+@@ -34,6 +34,7 @@
+ #define LM70_CHIP_LM71 2 /* NS LM71 */
+ #define LM70_CHIP_LM74 3 /* NS LM74 */
+ #define LM70_CHIP_TMP122 4 /* TI TMP122/TMP124 */
++#define LM70_CHIP_TMP125 5 /* TI TMP125 */
+
+ struct lm70 {
+ struct spi_device *spi;
+@@ -87,6 +88,12 @@ static ssize_t temp1_input_show(struct d
+ * LM71:
+ * 14 bits of 2's complement data, discard LSB 2 bits,
+ * resolution 0.0312 degrees celsius.
++ *
++ * TMP125:
++ * MSB/D15 is a leading zero. D14 is the sign-bit. This is
++ * followed by 9 temperature bits (D13..D5) in 2's complement
++ * data format with a resolution of 0.25 degrees celsius per unit.
++ * LSB 5 bits (D4..D0) share the same value as D5 and get discarded.
+ */
+ switch (p_lm70->chip) {
+ case LM70_CHIP_LM70:
+@@ -102,6 +109,10 @@ static ssize_t temp1_input_show(struct d
+ case LM70_CHIP_LM71:
+ val = ((int)raw / 4) * 3125 / 100;
+ break;
++
++ case LM70_CHIP_TMP125:
++ val = (sign_extend32(raw, 14) / 32) * 250;
++ break;
+ }
+
+ status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */
+@@ -136,6 +147,10 @@ static const struct of_device_id lm70_of
+ .data = (void *) LM70_CHIP_TMP122,
+ },
+ {
++ .compatible = "ti,tmp125",
++ .data = (void *) LM70_CHIP_TMP125,
++ },
++ {
+ .compatible = "ti,lm71",
+ .data = (void *) LM70_CHIP_LM71,
+ },
+@@ -184,6 +199,7 @@ static const struct spi_device_id lm70_i
+ { "lm70", LM70_CHIP_LM70 },
+ { "tmp121", LM70_CHIP_TMP121 },
+ { "tmp122", LM70_CHIP_TMP122 },
++ { "tmp125", LM70_CHIP_TMP125 },
+ { "lm71", LM70_CHIP_LM71 },
+ { "lm74", LM70_CHIP_LM74 },
+ { },
--- /dev/null
+# CONFIG_104_QUAD_8 is not set
+CONFIG_32BIT=y
+CONFIG_64BIT_TIME=y
+# CONFIG_6LOWPAN is not set
+# CONFIG_6LOWPAN_DEBUGFS is not set
+# CONFIG_6PACK is not set
+# CONFIG_8139CP is not set
+# CONFIG_8139TOO is not set
+# CONFIG_9P_FS is not set
+# CONFIG_AB3100_CORE is not set
+# CONFIG_AB8500_CORE is not set
+# CONFIG_ABP060MG is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_ACENIC is not set
+# CONFIG_ACERHDF is not set
+# CONFIG_ACER_WIRELESS is not set
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_ACPI_ALS is not set
+# CONFIG_ACPI_APEI is not set
+# CONFIG_ACPI_BUTTON is not set
+# CONFIG_ACPI_CONFIGFS is not set
+# CONFIG_ACPI_CUSTOM_METHOD is not set
+# CONFIG_ACPI_EXTLOG is not set
+# CONFIG_ACPI_HED is not set
+# CONFIG_ACPI_NFIT is not set
+# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set
+# CONFIG_ACPI_TABLE_UPGRADE is not set
+# CONFIG_ACPI_VIDEO is not set
+# CONFIG_AD2S1200 is not set
+# CONFIG_AD2S1210 is not set
+# CONFIG_AD2S90 is not set
+# CONFIG_AD5064 is not set
+# CONFIG_AD525X_DPOT is not set
+# CONFIG_AD5272 is not set
+# CONFIG_AD5360 is not set
+# CONFIG_AD5380 is not set
+# CONFIG_AD5421 is not set
+# CONFIG_AD5446 is not set
+# CONFIG_AD5449 is not set
+# CONFIG_AD5504 is not set
+# CONFIG_AD5592R is not set
+# CONFIG_AD5593R is not set
+# CONFIG_AD5624R_SPI is not set
+# CONFIG_AD5686 is not set
+# CONFIG_AD5686_SPI is not set
+# CONFIG_AD5696_I2C is not set
+# CONFIG_AD5755 is not set
+# CONFIG_AD5758 is not set
+# CONFIG_AD5761 is not set
+# CONFIG_AD5764 is not set
+# CONFIG_AD5770R is not set
+# CONFIG_AD5791 is not set
+# CONFIG_AD5933 is not set
+# CONFIG_AD7091R5 is not set
+# CONFIG_AD7124 is not set
+# CONFIG_AD7150 is not set
+# CONFIG_AD7152 is not set
+# CONFIG_AD7192 is not set
+# CONFIG_AD7266 is not set
+# CONFIG_AD7280 is not set
+# CONFIG_AD7291 is not set
+# CONFIG_AD7292 is not set
+# CONFIG_AD7298 is not set
+# CONFIG_AD7303 is not set
+# CONFIG_AD7476 is not set
+# CONFIG_AD7606 is not set
+# CONFIG_AD7606_IFACE_PARALLEL is not set
+# CONFIG_AD7606_IFACE_SPI is not set
+# CONFIG_AD7746 is not set
+# CONFIG_AD7766 is not set
+# CONFIG_AD7768_1 is not set
+# CONFIG_AD7780 is not set
+# CONFIG_AD7791 is not set
+# CONFIG_AD7793 is not set
+# CONFIG_AD7816 is not set
+# CONFIG_AD7887 is not set
+# CONFIG_AD7923 is not set
+# CONFIG_AD7949 is not set
+# CONFIG_AD799X is not set
+# CONFIG_AD8366 is not set
+# CONFIG_AD8801 is not set
+# CONFIG_AD9467 is not set
+# CONFIG_AD9523 is not set
+# CONFIG_AD9832 is not set
+# CONFIG_AD9834 is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_ADE7854 is not set
+# CONFIG_ADF4350 is not set
+# CONFIG_ADF4371 is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADIN_PHY is not set
+# CONFIG_ADIS16080 is not set
+# CONFIG_ADIS16130 is not set
+# CONFIG_ADIS16136 is not set
+# CONFIG_ADIS16201 is not set
+# CONFIG_ADIS16203 is not set
+# CONFIG_ADIS16209 is not set
+# CONFIG_ADIS16240 is not set
+# CONFIG_ADIS16260 is not set
+# CONFIG_ADIS16400 is not set
+# CONFIG_ADIS16460 is not set
+# CONFIG_ADIS16475 is not set
+# CONFIG_ADIS16480 is not set
+# CONFIG_ADI_AXI_ADC is not set
+# CONFIG_ADJD_S311 is not set
+# CONFIG_ADM6996_PHY is not set
+# CONFIG_ADM8211 is not set
+# CONFIG_ADT7316 is not set
+# CONFIG_ADUX1020 is not set
+CONFIG_ADVISE_SYSCALLS=y
+# CONFIG_ADXL345_I2C is not set
+# CONFIG_ADXL345_SPI is not set
+# CONFIG_ADXL372_I2C is not set
+# CONFIG_ADXL372_SPI is not set
+# CONFIG_ADXRS290 is not set
+# CONFIG_ADXRS450 is not set
+CONFIG_AEABI=y
+# CONFIG_AFE4403 is not set
+# CONFIG_AFE4404 is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_AFS_DEBUG_CURSOR is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_AF_KCM is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_AF_RXRPC_INJECT_LOSS is not set
+# CONFIG_AF_RXRPC_IPV6 is not set
+# CONFIG_AGP is not set
+# CONFIG_AHCI_CEVA is not set
+# CONFIG_AHCI_IMX is not set
+# CONFIG_AHCI_MVEBU is not set
+# CONFIG_AHCI_QORIQ is not set
+# CONFIG_AHCI_XGENE is not set
+CONFIG_AIO=y
+# CONFIG_AIRO is not set
+# CONFIG_AIRO_CS is not set
+# CONFIG_AIX_PARTITION is not set
+# CONFIG_AK09911 is not set
+# CONFIG_AK8974 is not set
+# CONFIG_AK8975 is not set
+# CONFIG_AL3010 is not set
+# CONFIG_AL3320A is not set
+# CONFIG_ALIM7101_WDT is not set
+CONFIG_ALLOW_DEV_COREDUMP=y
+# CONFIG_ALTERA_MBOX is not set
+# CONFIG_ALTERA_MSGDMA is not set
+# CONFIG_ALTERA_STAPL is not set
+# CONFIG_ALTERA_TSE is not set
+# CONFIG_ALX is not set
+# CONFIG_AL_FIC is not set
+# CONFIG_AM2315 is not set
+# CONFIG_AM335X_PHY_USB is not set
+# CONFIG_AMBA_PL08X is not set
+# CONFIG_AMD8111_ETH is not set
+# CONFIG_AMD_MEM_ENCRYPT is not set
+# CONFIG_AMD_PHY is not set
+# CONFIG_AMD_XGBE is not set
+# CONFIG_AMD_XGBE_HAVE_ECC is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_AMILO_RFKILL is not set
+# CONFIG_ANDROID is not set
+CONFIG_ANON_INODES=y
+# CONFIG_APDS9300 is not set
+# CONFIG_APDS9802ALS is not set
+# CONFIG_APDS9960 is not set
+# CONFIG_APM8018X is not set
+# CONFIG_APM_EMULATION is not set
+# CONFIG_APPLE_GMUX is not set
+# CONFIG_APPLE_MFI_FASTCHARGE is not set
+# CONFIG_APPLE_PROPERTIES is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_AQTION is not set
+# CONFIG_AQUANTIA_PHY is not set
+# CONFIG_AR5523 is not set
+# CONFIG_AR7 is not set
+# CONFIG_AR8216_PHY is not set
+# CONFIG_AR8216_PHY_LEDS is not set
+# CONFIG_ARCH_ACTIONS is not set
+# CONFIG_ARCH_AGILEX is not set
+# CONFIG_ARCH_ALPINE is not set
+# CONFIG_ARCH_ARTPEC is not set
+# CONFIG_ARCH_ASPEED is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_AXXIA is not set
+# CONFIG_ARCH_BCM is not set
+# CONFIG_ARCH_BCM2835 is not set
+# CONFIG_ARCH_BCM_21664 is not set
+# CONFIG_ARCH_BCM_23550 is not set
+# CONFIG_ARCH_BCM_281XX is not set
+# CONFIG_ARCH_BCM_5301X is not set
+# CONFIG_ARCH_BCM_53573 is not set
+# CONFIG_ARCH_BCM_63XX is not set
+# CONFIG_ARCH_BCM_CYGNUS is not set
+# CONFIG_ARCH_BCM_IPROC is not set
+# CONFIG_ARCH_BCM_NSP is not set
+# CONFIG_ARCH_BERLIN is not set
+CONFIG_ARCH_BINFMT_ELF_STATE=y
+# CONFIG_ARCH_BITMAIN is not set
+# CONFIG_ARCH_BRCMSTB is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CNS3XXX is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_DIGICOLOR is not set
+# CONFIG_ARCH_DMA_ADDR_T_64BIT is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_EXYNOS is not set
+CONFIG_ARCH_FLATMEM_ENABLE=y
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_HI3xxx is not set
+# CONFIG_ARCH_HIGHBANK is not set
+# CONFIG_ARCH_HISI is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_K3 is not set
+# CONFIG_ARCH_KEEMBAY is not set
+# CONFIG_ARCH_KEYSTONE is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_LAYERSCAPE is not set
+# CONFIG_ARCH_LG1K is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_MEDIATEK is not set
+# CONFIG_ARCH_MESON is not set
+# CONFIG_ARCH_MILBEAUT is not set
+CONFIG_ARCH_MMAP_RND_BITS=8
+CONFIG_ARCH_MMAP_RND_BITS_MAX=16
+CONFIG_ARCH_MMAP_RND_BITS_MIN=8
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_MSTARV7 is not set
+# CONFIG_ARCH_MULTIPLATFORM is not set
+# CONFIG_ARCH_MULTI_V6 is not set
+# CONFIG_ARCH_MULTI_V7 is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MVEBU is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_MXS is not set
+# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_NOMADIK is not set
+# CONFIG_ARCH_NPCM is not set
+# CONFIG_ARCH_NSPIRE is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_OMAP1 is not set
+# CONFIG_ARCH_OMAP2 is not set
+# CONFIG_ARCH_OMAP2PLUS is not set
+# CONFIG_ARCH_OMAP3 is not set
+# CONFIG_ARCH_OMAP4 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_OXNAS is not set
+# CONFIG_ARCH_PICOXCELL is not set
+# CONFIG_ARCH_PRIMA2 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_QCOM is not set
+# CONFIG_ARCH_RANDOM is not set
+# CONFIG_ARCH_RDA is not set
+# CONFIG_ARCH_REALTEK is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_RENESAS is not set
+# CONFIG_ARCH_ROCKCHIP is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_S32 is not set
+# CONFIG_ARCH_S3C24XX is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_SEATTLE is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_SIRF is not set
+# CONFIG_ARCH_SOCFPGA is not set
+# CONFIG_ARCH_SPARX5 is not set
+# CONFIG_ARCH_SPRD is not set
+# CONFIG_ARCH_STI is not set
+# CONFIG_ARCH_STM32 is not set
+# CONFIG_ARCH_STRATIX10 is not set
+# CONFIG_ARCH_SUNXI is not set
+# CONFIG_ARCH_SYNQUACER is not set
+# CONFIG_ARCH_TANGO is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_THUNDER is not set
+# CONFIG_ARCH_THUNDER2 is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_UNIPHIER is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_VIRT is not set
+# CONFIG_ARCH_VISCONTI is not set
+# CONFIG_ARCH_VT8500 is not set
+# CONFIG_ARCH_VULCAN is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_WANTS_THP_SWAP is not set
+# CONFIG_ARCH_WM8505 is not set
+# CONFIG_ARCH_WM8750 is not set
+# CONFIG_ARCH_WM8850 is not set
+# CONFIG_ARCH_XGENE is not set
+# CONFIG_ARCH_ZX is not set
+# CONFIG_ARCH_ZYNQ is not set
+# CONFIG_ARCH_ZYNQMP is not set
+# CONFIG_ARCNET is not set
+# CONFIG_ARC_EMAC is not set
+# CONFIG_ARC_IRQ_NO_AUTOSAVE is not set
+# CONFIG_ARM64_16K_PAGES is not set
+# CONFIG_ARM64_64K_PAGES is not set
+# CONFIG_ARM64_AMU_EXTN is not set
+# CONFIG_ARM64_BTI is not set
+# CONFIG_ARM64_CRYPTO is not set
+# CONFIG_ARM64_E0PD is not set
+# CONFIG_ARM64_ERRATUM_1024718 is not set
+# CONFIG_ARM64_ERRATUM_1165522 is not set
+# CONFIG_ARM64_ERRATUM_1286807 is not set
+# CONFIG_ARM64_ERRATUM_1319367 is not set
+# CONFIG_ARM64_ERRATUM_1418040 is not set
+# CONFIG_ARM64_ERRATUM_1463225 is not set
+# CONFIG_ARM64_ERRATUM_1508412 is not set
+# CONFIG_ARM64_ERRATUM_1530923 is not set
+# CONFIG_ARM64_ERRATUM_1542419 is not set
+# CONFIG_ARM64_ERRATUM_819472 is not set
+# CONFIG_ARM64_ERRATUM_824069 is not set
+# CONFIG_ARM64_ERRATUM_826319 is not set
+# CONFIG_ARM64_ERRATUM_827319 is not set
+# CONFIG_ARM64_ERRATUM_832075 is not set
+# CONFIG_ARM64_ERRATUM_834220 is not set
+# CONFIG_ARM64_ERRATUM_843419 is not set
+# CONFIG_ARM64_ERRATUM_845719 is not set
+# CONFIG_ARM64_ERRATUM_858921 is not set
+# CONFIG_ARM64_HW_AFDBM is not set
+# CONFIG_ARM64_LSE_ATOMICS is not set
+# CONFIG_ARM64_MODULE_PLTS is not set
+# CONFIG_ARM64_MTE is not set
+# CONFIG_ARM64_PAN is not set
+# CONFIG_ARM64_PMEM is not set
+# CONFIG_ARM64_PSEUDO_NMI is not set
+# CONFIG_ARM64_PTDUMP_DEBUGFS is not set
+# CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET is not set
+# CONFIG_ARM64_RAS_EXTN is not set
+# CONFIG_ARM64_RELOC_TEST is not set
+CONFIG_ARM64_SW_TTBR0_PAN=y
+# CONFIG_ARM64_TLB_RANGE is not set
+# CONFIG_ARM64_UAO is not set
+# CONFIG_ARM64_USE_LSE_ATOMICS is not set
+# CONFIG_ARM64_VA_BITS_48 is not set
+# CONFIG_ARM64_VHE is not set
+# CONFIG_ARM_APPENDED_DTB is not set
+# CONFIG_ARM_ARCH_TIMER is not set
+# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set
+# CONFIG_ARM_BIG_LITTLE_CPUFREQ is not set
+# CONFIG_ARM_CCI is not set
+# CONFIG_ARM_CCI400_PMU is not set
+# CONFIG_ARM_CCI5xx_PMU is not set
+# CONFIG_ARM_CCI_PMU is not set
+# CONFIG_ARM_CCN is not set
+# CONFIG_ARM_CMN is not set
+# CONFIG_ARM_CPUIDLE is not set
+CONFIG_ARM_CPU_TOPOLOGY=y
+# CONFIG_ARM_CRYPTO is not set
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+# CONFIG_ARM_DSU_PMU is not set
+# CONFIG_ARM_ERRATA_326103 is not set
+# CONFIG_ARM_ERRATA_364296 is not set
+# CONFIG_ARM_ERRATA_411920 is not set
+# CONFIG_ARM_ERRATA_430973 is not set
+# CONFIG_ARM_ERRATA_458693 is not set
+# CONFIG_ARM_ERRATA_460075 is not set
+# CONFIG_ARM_ERRATA_643719 is not set
+# CONFIG_ARM_ERRATA_720789 is not set
+# CONFIG_ARM_ERRATA_742230 is not set
+# CONFIG_ARM_ERRATA_742231 is not set
+# CONFIG_ARM_ERRATA_743622 is not set
+# CONFIG_ARM_ERRATA_751472 is not set
+# CONFIG_ARM_ERRATA_754322 is not set
+# CONFIG_ARM_ERRATA_754327 is not set
+# CONFIG_ARM_ERRATA_764369 is not set
+# CONFIG_ARM_ERRATA_773022 is not set
+# CONFIG_ARM_ERRATA_775420 is not set
+# CONFIG_ARM_ERRATA_798181 is not set
+# CONFIG_ARM_ERRATA_814220 is not set
+# CONFIG_ARM_ERRATA_818325_852422 is not set
+# CONFIG_ARM_ERRATA_821420 is not set
+# CONFIG_ARM_ERRATA_825619 is not set
+# CONFIG_ARM_ERRATA_852421 is not set
+# CONFIG_ARM_ERRATA_852423 is not set
+# CONFIG_ARM_ERRATA_857271 is not set
+# CONFIG_ARM_ERRATA_857272 is not set
+CONFIG_ARM_GIC_MAX_NR=1
+# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set
+# CONFIG_ARM_KPROBES_TEST is not set
+# CONFIG_ARM_LPAE is not set
+# CONFIG_ARM_MHU is not set
+# CONFIG_ARM_MODULE_PLTS is not set
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+# CONFIG_ARM_PSCI is not set
+# CONFIG_ARM_PSCI_CHECKER is not set
+# CONFIG_ARM_PSCI_CPUIDLE is not set
+# CONFIG_ARM_PTDUMP_DEBUGFS is not set
+# CONFIG_ARM_SBSA_WATCHDOG is not set
+# CONFIG_ARM_SCMI_PROTOCOL is not set
+# CONFIG_ARM_SCPI_PROTOCOL is not set
+# CONFIG_ARM_SDE_INTERFACE is not set
+# CONFIG_ARM_SMCCC_SOC_ID is not set
+# CONFIG_ARM_SMC_WATCHDOG is not set
+# CONFIG_ARM_SP805_WATCHDOG is not set
+# CONFIG_ARM_SPE_PMU is not set
+# CONFIG_ARM_THUMBEE is not set
+# CONFIG_ARM_TIMER_SP804 is not set
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_ARM_VIRT_EXT is not set
+# CONFIG_AS3935 is not set
+# CONFIG_AS73211 is not set
+# CONFIG_ASM9260_TIMER is not set
+# CONFIG_ASN1 is not set
+# CONFIG_ASUS_LAPTOP is not set
+# CONFIG_ASUS_WIRELESS is not set
+# CONFIG_ASYMMETRIC_KEY_TYPE is not set
+# CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE is not set
+# CONFIG_ASYNC_RAID6_TEST is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_AT76C50X_USB is not set
+# CONFIG_AT803X_PHY is not set
+# CONFIG_AT91_SAMA5D2_ADC is not set
+# CONFIG_ATA is not set
+# CONFIG_ATAGS is not set
+CONFIG_ATAGS_PROC=y
+# CONFIG_ATALK is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_ATA_ACPI is not set
+CONFIG_ATA_BMDMA=y
+# CONFIG_ATA_FORCE is not set
+# CONFIG_ATA_GENERIC is not set
+# CONFIG_ATA_LEDS is not set
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_ATA_PIIX is not set
+CONFIG_ATA_SFF=y
+# CONFIG_ATA_VERBOSE_ERROR is not set
+# CONFIG_ATH10K is not set
+# CONFIG_ATH25 is not set
+# CONFIG_ATH5K is not set
+# CONFIG_ATH6KL is not set
+# CONFIG_ATH79 is not set
+# CONFIG_ATH9K is not set
+# CONFIG_ATH9K_HTC is not set
+# CONFIG_ATH_DEBUG is not set
+# CONFIG_ATL1 is not set
+# CONFIG_ATL1C is not set
+# CONFIG_ATL1E is not set
+# CONFIG_ATL2 is not set
+# CONFIG_ATLAS_EZO_SENSOR is not set
+# CONFIG_ATLAS_PH_SENSOR is not set
+# CONFIG_ATM is not set
+# CONFIG_ATMEL is not set
+# CONFIG_ATMEL_PIT is not set
+# CONFIG_ATMEL_SSC is not set
+# CONFIG_ATM_AMBASSADOR is not set
+# CONFIG_ATM_BR2684 is not set
+CONFIG_ATM_BR2684_IPFILTER=y
+# CONFIG_ATM_CLIP is not set
+CONFIG_ATM_CLIP_NO_ICMP=y
+# CONFIG_ATM_DRIVERS is not set
+# CONFIG_ATM_DUMMY is not set
+# CONFIG_ATM_ENI is not set
+# CONFIG_ATM_FIRESTREAM is not set
+# CONFIG_ATM_FORE200E is not set
+# CONFIG_ATM_HE is not set
+# CONFIG_ATM_HORIZON is not set
+# CONFIG_ATM_IA is not set
+# CONFIG_ATM_IDT77252 is not set
+# CONFIG_ATM_LANAI is not set
+# CONFIG_ATM_LANE is not set
+# CONFIG_ATM_MPOA is not set
+# CONFIG_ATM_NICSTAR is not set
+# CONFIG_ATM_SOLOS is not set
+# CONFIG_ATM_TCP is not set
+# CONFIG_ATM_ZATM is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_ATP is not set
+# CONFIG_AUDIT is not set
+# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set
+# CONFIG_AURORA_NB8800 is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTO_ZRELADDR is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_AX25 is not set
+# CONFIG_AX25_DAMA_SLAVE is not set
+# CONFIG_AX88796 is not set
+# CONFIG_AX88796B_PHY is not set
+# CONFIG_AXP20X_ADC is not set
+# CONFIG_AXP20X_POWER is not set
+# CONFIG_AXP288_ADC is not set
+# CONFIG_AXP288_FUEL_GAUGE is not set
+# CONFIG_B43 is not set
+# CONFIG_B43LEGACY is not set
+# CONFIG_B44 is not set
+# CONFIG_B53 is not set
+# CONFIG_B53_MDIO_DRIVER is not set
+# CONFIG_B53_MMAP_DRIVER is not set
+# CONFIG_B53_SERDES is not set
+# CONFIG_B53_SPI_DRIVER is not set
+# CONFIG_B53_SRAB_DRIVER is not set
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+# CONFIG_BACKLIGHT_APPLE is not set
+# CONFIG_BACKLIGHT_ARCXCNN is not set
+# CONFIG_BACKLIGHT_BD6107 is not set
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
+# CONFIG_BACKLIGHT_GENERIC is not set
+# CONFIG_BACKLIGHT_GPIO is not set
+# CONFIG_BACKLIGHT_KTD253 is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+# CONFIG_BACKLIGHT_LED is not set
+# CONFIG_BACKLIGHT_LM3630A is not set
+# CONFIG_BACKLIGHT_LM3639 is not set
+# CONFIG_BACKLIGHT_LP855X is not set
+# CONFIG_BACKLIGHT_LV5207LP is not set
+# CONFIG_BACKLIGHT_PANDORA is not set
+# CONFIG_BACKLIGHT_PM8941_WLED is not set
+# CONFIG_BACKLIGHT_PWM is not set
+# CONFIG_BACKLIGHT_QCOM_WLED is not set
+# CONFIG_BACKLIGHT_RPI is not set
+# CONFIG_BACKLIGHT_SAHARA is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_BAREUDP is not set
+CONFIG_BASE_FULL=y
+CONFIG_BASE_SMALL=0
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_BATTERY_BQ27XXX is not set
+# CONFIG_BATTERY_BQ27XXX_HDQ is not set
+# CONFIG_BATTERY_CW2015 is not set
+# CONFIG_BATTERY_DS2760 is not set
+# CONFIG_BATTERY_DS2780 is not set
+# CONFIG_BATTERY_DS2781 is not set
+# CONFIG_BATTERY_DS2782 is not set
+# CONFIG_BATTERY_GAUGE_LTC2941 is not set
+# CONFIG_BATTERY_GOLDFISH is not set
+# CONFIG_BATTERY_LEGO_EV3 is not set
+# CONFIG_BATTERY_MAX17040 is not set
+# CONFIG_BATTERY_MAX17042 is not set
+# CONFIG_BATTERY_MAX1721X is not set
+# CONFIG_BATTERY_RT5033 is not set
+# CONFIG_BATTERY_SBS is not set
+# CONFIG_BAYCOM_EPP is not set
+# CONFIG_BAYCOM_PAR is not set
+# CONFIG_BAYCOM_SER_FDX is not set
+# CONFIG_BAYCOM_SER_HDX is not set
+# CONFIG_BCACHE is not set
+# CONFIG_BCM47XX is not set
+# CONFIG_BCM54140_PHY is not set
+# CONFIG_BCM63XX is not set
+# CONFIG_BCM63XX_PHY is not set
+# CONFIG_BCM7038_WDT is not set
+# CONFIG_BCM7XXX_PHY is not set
+# CONFIG_BCM84881_PHY is not set
+# CONFIG_BCM87XX_PHY is not set
+# CONFIG_BCMA is not set
+# CONFIG_BCMA_DRIVER_GPIO is not set
+CONFIG_BCMA_POSSIBLE=y
+# CONFIG_BCMGENET is not set
+# CONFIG_BCM_IPROC_ADC is not set
+# CONFIG_BCM_KONA_USB2_PHY is not set
+# CONFIG_BCM_SBA_RAID is not set
+# CONFIG_BDI_SWITCH is not set
+# CONFIG_BE2ISCSI is not set
+# CONFIG_BE2NET is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_BGMAC is not set
+# CONFIG_BH1750 is not set
+# CONFIG_BH1780 is not set
+# CONFIG_BIG_KEYS is not set
+# CONFIG_BIG_LITTLE is not set
+# CONFIG_BINARY_PRINTF is not set
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_ELF_FDPIC is not set
+# CONFIG_BINFMT_FLAT is not set
+# CONFIG_BINFMT_MISC is not set
+CONFIG_BINFMT_SCRIPT=y
+CONFIG_BITREVERSE=y
+# CONFIG_BLK_CGROUP_IOCOST is not set
+# CONFIG_BLK_CGROUP_IOLATENCY is not set
+# CONFIG_BLK_CMDLINE_PARSER is not set
+# CONFIG_BLK_DEBUG_FS is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_BLK_DEV_4DRIVES is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI14XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_BLK_DEV_ATIIXP is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_CS5520 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_CS5535 is not set
+# CONFIG_BLK_DEV_CS5536 is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_DELKIN is not set
+# CONFIG_BLK_DEV_DRBD is not set
+# CONFIG_BLK_DEV_DTC2278 is not set
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_GENERIC is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_HT6560B is not set
+# CONFIG_BLK_DEV_IDEACPI is not set
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDECS is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_IDEPNP is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDE_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_INTEGRITY is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_BLK_DEV_IT8172 is not set
+# CONFIG_BLK_DEV_IT8213 is not set
+# CONFIG_BLK_DEV_IT821X is not set
+# CONFIG_BLK_DEV_JMICRON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_NULL_BLK is not set
+# CONFIG_BLK_DEV_NVME is not set
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_PLATFORM is not set
+# CONFIG_BLK_DEV_PMEM is not set
+# CONFIG_BLK_DEV_QD65XX is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_RBD is not set
+# CONFIG_BLK_DEV_RSXX is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+# CONFIG_BLK_DEV_SD is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SKD is not set
+# CONFIG_BLK_DEV_SL82C105 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SX8 is not set
+# CONFIG_BLK_DEV_TC86C001 is not set
+# CONFIG_BLK_DEV_THROTTLING is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_UMC8672 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_ZONED is not set
+# CONFIG_BLK_INLINE_ENCRYPTION is not set
+# CONFIG_BLK_SED_OPAL is not set
+# CONFIG_BLK_WBT is not set
+CONFIG_BLOCK=y
+# CONFIG_BMA180 is not set
+# CONFIG_BMA220 is not set
+# CONFIG_BMA400 is not set
+# CONFIG_BMC150_ACCEL is not set
+# CONFIG_BMC150_MAGN is not set
+# CONFIG_BMC150_MAGN_I2C is not set
+# CONFIG_BMC150_MAGN_SPI is not set
+# CONFIG_BME680 is not set
+# CONFIG_BMG160 is not set
+# CONFIG_BMI160_I2C is not set
+# CONFIG_BMI160_SPI is not set
+# CONFIG_BMIPS_GENERIC is not set
+# CONFIG_BMP280 is not set
+# CONFIG_BNA is not set
+# CONFIG_BNX2 is not set
+# CONFIG_BNX2X is not set
+# CONFIG_BNX2X_SRIOV is not set
+# CONFIG_BNXT is not set
+# CONFIG_BONDING is not set
+# CONFIG_BOOKE_WDT is not set
+CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT=3
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+# CONFIG_BOOTTIME_TRACING is not set
+# CONFIG_BOOT_CONFIG is not set
+# CONFIG_BOOT_PRINTK_DELAY is not set
+CONFIG_BOOT_RAW=y
+# CONFIG_BOUNCE is not set
+CONFIG_BPF=y
+# CONFIG_BPFILTER is not set
+CONFIG_BPF_JIT=y
+# CONFIG_BPF_JIT_ALWAYS_ON is not set
+CONFIG_BPF_JIT_DEFAULT_ON=y
+# CONFIG_BPF_PRELOAD is not set
+# CONFIG_BPF_STREAM_PARSER is not set
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_UNPRIV_DEFAULT_OFF=y
+# CONFIG_BPQETHER is not set
+CONFIG_BQL=y
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_BRCMFMAC is not set
+# CONFIG_BRCMSMAC is not set
+# CONFIG_BRCMSTB_GISB_ARB is not set
+CONFIG_BRIDGE=y
+# CONFIG_BRIDGE_EBT_802_3 is not set
+# CONFIG_BRIDGE_EBT_AMONG is not set
+# CONFIG_BRIDGE_EBT_ARP is not set
+# CONFIG_BRIDGE_EBT_ARPREPLY is not set
+# CONFIG_BRIDGE_EBT_BROUTE is not set
+# CONFIG_BRIDGE_EBT_DNAT is not set
+# CONFIG_BRIDGE_EBT_IP is not set
+# CONFIG_BRIDGE_EBT_IP6 is not set
+# CONFIG_BRIDGE_EBT_LIMIT is not set
+# CONFIG_BRIDGE_EBT_LOG is not set
+# CONFIG_BRIDGE_EBT_MARK is not set
+# CONFIG_BRIDGE_EBT_MARK_T is not set
+# CONFIG_BRIDGE_EBT_NFLOG is not set
+# CONFIG_BRIDGE_EBT_PKTTYPE is not set
+# CONFIG_BRIDGE_EBT_REDIRECT is not set
+# CONFIG_BRIDGE_EBT_SNAT is not set
+# CONFIG_BRIDGE_EBT_STP is not set
+# CONFIG_BRIDGE_EBT_T_FILTER is not set
+# CONFIG_BRIDGE_EBT_T_NAT is not set
+# CONFIG_BRIDGE_EBT_VLAN is not set
+CONFIG_BRIDGE_IGMP_SNOOPING=y
+# CONFIG_BRIDGE_MRP is not set
+# CONFIG_BRIDGE_NETFILTER is not set
+# CONFIG_BRIDGE_NF_EBTABLES is not set
+CONFIG_BRIDGE_VLAN_FILTERING=y
+# CONFIG_BROADCOM_PHY is not set
+CONFIG_BROKEN_ON_SMP=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_BT is not set
+# CONFIG_BTRFS_ASSERT is not set
+# CONFIG_BTRFS_DEBUG is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_BTRFS_FS_POSIX_ACL is not set
+# CONFIG_BTRFS_FS_REF_VERIFY is not set
+# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set
+# CONFIG_BT_ATH3K is not set
+# CONFIG_BT_BNEP is not set
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+# CONFIG_BT_BREDR is not set
+# CONFIG_BT_CMTP is not set
+# CONFIG_BT_FEATURE_DEBUG is not set
+# CONFIG_BT_HCIBCM203X is not set
+# CONFIG_BT_HCIBFUSB is not set
+# CONFIG_BT_HCIBLUECARD is not set
+# CONFIG_BT_HCIBPA10X is not set
+# CONFIG_BT_HCIBT3C is not set
+# CONFIG_BT_HCIBTSDIO is not set
+# CONFIG_BT_HCIBTUSB is not set
+# CONFIG_BT_HCIBTUSB_AUTOSUSPEND is not set
+# CONFIG_BT_HCIBTUSB_MTK is not set
+# CONFIG_BT_HCIBTUSB_RTL is not set
+# CONFIG_BT_HCIDTL1 is not set
+# CONFIG_BT_HCIUART is not set
+# CONFIG_BT_HCIUART_3WIRE is not set
+# CONFIG_BT_HCIUART_AG6XX is not set
+# CONFIG_BT_HCIUART_ATH3K is not set
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_H4=y
+# CONFIG_BT_HCIUART_LL is not set
+# CONFIG_BT_HCIUART_MRVL is not set
+# CONFIG_BT_HCIUART_QCA is not set
+# CONFIG_BT_HCIUART_RTL is not set
+# CONFIG_BT_HCIVHCI is not set
+# CONFIG_BT_HIDP is not set
+# CONFIG_BT_HS is not set
+# CONFIG_BT_LE is not set
+# CONFIG_BT_LEDS is not set
+# CONFIG_BT_MRVL is not set
+# CONFIG_BT_MSFTEXT is not set
+# CONFIG_BT_MTKSDIO is not set
+# CONFIG_BT_MTKUART is not set
+# CONFIG_BT_RFCOMM is not set
+CONFIG_BT_RFCOMM_TTY=y
+# CONFIG_BT_SELFTEST is not set
+CONFIG_BUG=y
+# CONFIG_BUG_ON_DATA_CORRUPTION is not set
+CONFIG_BUILDTIME_EXTABLE_SORT=y
+CONFIG_BUILDTIME_TABLE_SORT=y
+# CONFIG_BUILD_BIN2C is not set
+CONFIG_BUILD_SALT=""
+# CONFIG_C2PORT is not set
+CONFIG_CACHE_L2X0_PMU=y
+# CONFIG_CADENCE_WATCHDOG is not set
+# CONFIG_CAIF is not set
+# CONFIG_CAN is not set
+# CONFIG_CAN_BCM is not set
+# CONFIG_CAN_DEBUG_DEVICES is not set
+# CONFIG_CAN_DEV is not set
+# CONFIG_CAN_GS_USB is not set
+# CONFIG_CAN_GW is not set
+# CONFIG_CAN_HI311X is not set
+# CONFIG_CAN_IFI_CANFD is not set
+# CONFIG_CAN_ISOTP is not set
+# CONFIG_CAN_J1939 is not set
+# CONFIG_CAN_KVASER_PCIEFD is not set
+# CONFIG_CAN_MCBA_USB is not set
+# CONFIG_CAN_MCP251XFD is not set
+# CONFIG_CAN_M_CAN is not set
+# CONFIG_CAN_PEAK_PCIEFD is not set
+# CONFIG_CAN_RAW is not set
+# CONFIG_CAN_RCAR is not set
+# CONFIG_CAN_RCAR_CANFD is not set
+# CONFIG_CAN_SLCAN is not set
+# CONFIG_CAN_SUN4I is not set
+# CONFIG_CAN_UCAN is not set
+# CONFIG_CAN_VCAN is not set
+# CONFIG_CAN_VXCAN is not set
+# CONFIG_CAPI_AVM is not set
+# CONFIG_CAPI_EICON is not set
+# CONFIG_CAPI_TRACE is not set
+CONFIG_CARDBUS=y
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_CARL9170 is not set
+# CONFIG_CASSINI is not set
+# CONFIG_CAVIUM_CPT is not set
+# CONFIG_CAVIUM_ERRATUM_22375 is not set
+# CONFIG_CAVIUM_ERRATUM_23144 is not set
+# CONFIG_CAVIUM_ERRATUM_23154 is not set
+# CONFIG_CAVIUM_ERRATUM_27456 is not set
+# CONFIG_CAVIUM_ERRATUM_30115 is not set
+# CONFIG_CAVIUM_OCTEON_SOC is not set
+# CONFIG_CAVIUM_PTP is not set
+# CONFIG_CAVIUM_TX2_ERRATUM_219 is not set
+# CONFIG_CB710_CORE is not set
+# CONFIG_CC10001_ADC is not set
+# CONFIG_CCS811 is not set
+CONFIG_CC_CAN_LINK=y
+CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_CEPH_FS is not set
+# CONFIG_CEPH_LIB is not set
+# CONFIG_CFG80211 is not set
+# CONFIG_CFG80211_CERTIFICATION_ONUS is not set
+# CONFIG_CGROUPS is not set
+# CONFIG_CHARGER_ADP5061 is not set
+# CONFIG_CHARGER_BD99954 is not set
+# CONFIG_CHARGER_BQ2415X is not set
+# CONFIG_CHARGER_BQ24190 is not set
+# CONFIG_CHARGER_BQ24257 is not set
+# CONFIG_CHARGER_BQ24735 is not set
+# CONFIG_CHARGER_BQ2515X is not set
+# CONFIG_CHARGER_BQ25890 is not set
+# CONFIG_CHARGER_BQ25980 is not set
+# CONFIG_CHARGER_DETECTOR_MAX14656 is not set
+# CONFIG_CHARGER_GPIO is not set
+# CONFIG_CHARGER_ISP1704 is not set
+# CONFIG_CHARGER_LP8727 is not set
+# CONFIG_CHARGER_LT3651 is not set
+# CONFIG_CHARGER_LTC3651 is not set
+# CONFIG_CHARGER_MANAGER is not set
+# CONFIG_CHARGER_MAX8903 is not set
+# CONFIG_CHARGER_RT9455 is not set
+# CONFIG_CHARGER_SBS is not set
+# CONFIG_CHARGER_SMB347 is not set
+# CONFIG_CHARGER_TWL4030 is not set
+# CONFIG_CHARGER_UCS1002 is not set
+# CONFIG_CHASH_SELFTEST is not set
+# CONFIG_CHASH_STATS is not set
+# CONFIG_CHECKPOINT_RESTORE is not set
+# CONFIG_CHELSIO_T1 is not set
+# CONFIG_CHELSIO_T3 is not set
+# CONFIG_CHELSIO_T4 is not set
+# CONFIG_CHELSIO_T4VF is not set
+# CONFIG_CHROME_PLATFORMS is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_CHR_DEV_SCH is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_CIFS is not set
+# CONFIG_CIFS_ACL is not set
+CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y
+# CONFIG_CIFS_DEBUG is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_FSCACHE is not set
+# CONFIG_CIFS_NFSD_EXPORT is not set
+CONFIG_CIFS_POSIX=y
+# CONFIG_CIFS_SMB2 is not set
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_STATS2 is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+CONFIG_CIFS_XATTR=y
+# CONFIG_CIO_DAC is not set
+# CONFIG_CLEANCACHE is not set
+# CONFIG_CLKSRC_VERSATILE is not set
+# CONFIG_CLK_HSDK is not set
+# CONFIG_CLK_QORIQ is not set
+# CONFIG_CLOCK_THERMAL is not set
+CONFIG_CLS_U32_MARK=y
+# CONFIG_CLS_U32_PERF is not set
+# CONFIG_CM32181 is not set
+# CONFIG_CM3232 is not set
+# CONFIG_CM3323 is not set
+# CONFIG_CM3605 is not set
+# CONFIG_CM36651 is not set
+# CONFIG_CMA is not set
+CONFIG_CMDLINE=""
+# CONFIG_CMDLINE_BOOL is not set
+# CONFIG_CMDLINE_EXTEND is not set
+# CONFIG_CMDLINE_FORCE is not set
+# CONFIG_CMDLINE_FROM_BOOTLOADER is not set
+# CONFIG_CMDLINE_PARTITION is not set
+# CONFIG_CNIC is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_CODE_PATCHING_SELFTEST is not set
+# CONFIG_COMEDI is not set
+# CONFIG_COMMON_CLK_CDCE706 is not set
+# CONFIG_COMMON_CLK_CDCE925 is not set
+# CONFIG_COMMON_CLK_CS2000_CP is not set
+# CONFIG_COMMON_CLK_FIXED_MMIO is not set
+# CONFIG_COMMON_CLK_IPROC is not set
+# CONFIG_COMMON_CLK_MAX9485 is not set
+# CONFIG_COMMON_CLK_MT6765 is not set
+# CONFIG_COMMON_CLK_MT8167 is not set
+# CONFIG_COMMON_CLK_MT8167_AUDSYS is not set
+# CONFIG_COMMON_CLK_MT8167_IMGSYS is not set
+# CONFIG_COMMON_CLK_MT8167_MFGCFG is not set
+# CONFIG_COMMON_CLK_MT8167_MMSYS is not set
+# CONFIG_COMMON_CLK_MT8167_VDECSYS is not set
+# CONFIG_COMMON_CLK_NXP is not set
+# CONFIG_COMMON_CLK_PIC32 is not set
+# CONFIG_COMMON_CLK_PWM is not set
+# CONFIG_COMMON_CLK_PXA is not set
+# CONFIG_COMMON_CLK_QCOM is not set
+# CONFIG_COMMON_CLK_SI514 is not set
+# CONFIG_COMMON_CLK_SI5341 is not set
+# CONFIG_COMMON_CLK_SI5351 is not set
+# CONFIG_COMMON_CLK_SI544 is not set
+# CONFIG_COMMON_CLK_SI570 is not set
+# CONFIG_COMMON_CLK_VC5 is not set
+# CONFIG_COMMON_CLK_XGENE is not set
+# CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set
+CONFIG_COMPACTION=y
+# CONFIG_COMPAL_LAPTOP is not set
+# CONFIG_COMPAT is not set
+# CONFIG_COMPAT_BRK is not set
+# CONFIG_COMPILE_TEST is not set
+# CONFIG_CONFIGFS_FS is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
+CONFIG_CONSOLE_LOGLEVEL_QUIET=4
+CONFIG_CONSTRUCTORS=y
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_COPS is not set
+# CONFIG_CORDIC is not set
+# CONFIG_COREDUMP is not set
+# CONFIG_CORESIGHT is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_CORTINA_PHY is not set
+# CONFIG_COUNTER is not set
+# CONFIG_CPA_DEBUG is not set
+# CONFIG_CPU_BIG_ENDIAN is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_GOV_SCHEDUTIL is not set
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+# CONFIG_CPU_FREQ_THERMAL is not set
+# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_ICACHE_MISMATCH_WORKAROUND is not set
+# CONFIG_CPU_IDLE is not set
+# CONFIG_CPU_IDLE_GOV_LADDER is not set
+# CONFIG_CPU_IDLE_GOV_MENU is not set
+# CONFIG_CPU_IDLE_GOV_TEO is not set
+# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set
+# CONFIG_CPU_ISOLATION is not set
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_CPU_NO_EFFICIENT_FFS is not set
+CONFIG_CPU_SW_DOMAIN_PAN=y
+# CONFIG_CPU_THERMAL is not set
+# CONFIG_CRAMFS is not set
+CONFIG_CRAMFS_BLOCKDEV=y
+# CONFIG_CRAMFS_MTD is not set
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_CRC16 is not set
+CONFIG_CRC32=y
+# CONFIG_CRC32_BIT is not set
+CONFIG_CRC32_SARWATE=y
+# CONFIG_CRC32_SELFTEST is not set
+# CONFIG_CRC32_SLICEBY4 is not set
+# CONFIG_CRC32_SLICEBY8 is not set
+# CONFIG_CRC4 is not set
+# CONFIG_CRC64 is not set
+# CONFIG_CRC7 is not set
+# CONFIG_CRC8 is not set
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC_ITU_T is not set
+# CONFIG_CRC_T10DIF is not set
+CONFIG_CROSS_COMPILE=""
+# CONFIG_CROSS_MEMORY_ATTACH is not set
+CONFIG_CRYPTO=y
+# CONFIG_CRYPTO_842 is not set
+CONFIG_CRYPTO_ACOMP2=y
+# CONFIG_CRYPTO_ADIANTUM is not set
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+# CONFIG_CRYPTO_AEGIS128 is not set
+# CONFIG_CRYPTO_AEGIS128L is not set
+# CONFIG_CRYPTO_AEGIS128L_AESNI_SSE2 is not set
+# CONFIG_CRYPTO_AEGIS128_AESNI_SSE2 is not set
+# CONFIG_CRYPTO_AEGIS256 is not set
+# CONFIG_CRYPTO_AEGIS256_AESNI_SSE2 is not set
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_AES_586 is not set
+# CONFIG_CRYPTO_AES_ARM is not set
+# CONFIG_CRYPTO_AES_ARM64 is not set
+# CONFIG_CRYPTO_AES_ARM64_BS is not set
+# CONFIG_CRYPTO_AES_ARM64_CE is not set
+# CONFIG_CRYPTO_AES_ARM64_CE_BLK is not set
+# CONFIG_CRYPTO_AES_ARM64_CE_CCM is not set
+# CONFIG_CRYPTO_AES_ARM64_NEON_BLK is not set
+# CONFIG_CRYPTO_AES_ARM_BS is not set
+# CONFIG_CRYPTO_AES_ARM_CE is not set
+# CONFIG_CRYPTO_AES_NI_INTEL is not set
+# CONFIG_CRYPTO_AES_TI is not set
+CONFIG_CRYPTO_AKCIPHER=y
+CONFIG_CRYPTO_AKCIPHER2=y
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_BLAKE2B is not set
+# CONFIG_CRYPTO_BLAKE2S is not set
+# CONFIG_CRYPTO_BLAKE2S_X86 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_CBC is not set
+CONFIG_CRYPTO_CCM=y
+# CONFIG_CRYPTO_CFB is not set
+# CONFIG_CRYPTO_CHACHA20 is not set
+# CONFIG_CRYPTO_CHACHA20POLY1305 is not set
+# CONFIG_CRYPTO_CHACHA20_NEON is not set
+# CONFIG_CRYPTO_CHACHA20_X86_64 is not set
+# CONFIG_CRYPTO_CHACHA_MIPS is not set
+# CONFIG_CRYPTO_CMAC is not set
+# CONFIG_CRYPTO_CRC32 is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_CRC32C_INTEL is not set
+# CONFIG_CRYPTO_CRC32_ARM_CE is not set
+# CONFIG_CRYPTO_CRCT10DIF is not set
+# CONFIG_CRYPTO_CRCT10DIF_ARM64_CE is not set
+# CONFIG_CRYPTO_CRCT10DIF_ARM_CE is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_CTR=y
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_CURVE25519 is not set
+# CONFIG_CRYPTO_CURVE25519_NEON is not set
+# CONFIG_CRYPTO_CURVE25519_X86 is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set
+# CONFIG_CRYPTO_DEV_ATMEL_AES is not set
+# CONFIG_CRYPTO_DEV_ATMEL_AUTHENC is not set
+# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set
+# CONFIG_CRYPTO_DEV_ATMEL_SHA is not set
+# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set
+# CONFIG_CRYPTO_DEV_ATMEL_TDES is not set
+# CONFIG_CRYPTO_DEV_CAVIUM_ZIP is not set
+# CONFIG_CRYPTO_DEV_CCP is not set
+# CONFIG_CRYPTO_DEV_CCP_DEBUGFS is not set
+# CONFIG_CRYPTO_DEV_CCREE is not set
+# CONFIG_CRYPTO_DEV_FSL_CAAM is not set
+# CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC is not set
+# CONFIG_CRYPTO_DEV_HIFN_795X is not set
+# CONFIG_CRYPTO_DEV_HISI_SEC is not set
+# CONFIG_CRYPTO_DEV_HISI_ZIP is not set
+# CONFIG_CRYPTO_DEV_IMGTEC_HASH is not set
+# CONFIG_CRYPTO_DEV_MARVELL_CESA is not set
+# CONFIG_CRYPTO_DEV_MV_CESA is not set
+# CONFIG_CRYPTO_DEV_MXC_SCC is not set
+# CONFIG_CRYPTO_DEV_MXS_DCP is not set
+# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set
+# CONFIG_CRYPTO_DEV_OCTEONTX_CPT is not set
+# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set
+# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set
+# CONFIG_CRYPTO_DEV_QAT_C62X is not set
+# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set
+# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set
+# CONFIG_CRYPTO_DEV_QAT_DH895xCCVF is not set
+# CONFIG_CRYPTO_DEV_QCE is not set
+# CONFIG_CRYPTO_DEV_S5P is not set
+# CONFIG_CRYPTO_DEV_SAFEXCEL is not set
+# CONFIG_CRYPTO_DEV_SAHARA is not set
+# CONFIG_CRYPTO_DEV_SP_PSP is not set
+# CONFIG_CRYPTO_DEV_TALITOS is not set
+# CONFIG_CRYPTO_DEV_VIRTIO is not set
+# CONFIG_CRYPTO_DH is not set
+# CONFIG_CRYPTO_DRBG_CTR is not set
+# CONFIG_CRYPTO_DRBG_HASH is not set
+# CONFIG_CRYPTO_DRBG_MENU is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_ECDH is not set
+# CONFIG_CRYPTO_ECHAINIV is not set
+# CONFIG_CRYPTO_ECRDSA is not set
+# CONFIG_CRYPTO_ESSIV is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_GCM=y
+CONFIG_CRYPTO_GF128MUL=y
+CONFIG_CRYPTO_GHASH=y
+# CONFIG_CRYPTO_GHASH_ARM64_CE is not set
+# CONFIG_CRYPTO_GHASH_ARM_CE is not set
+# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_HW is not set
+# CONFIG_CRYPTO_JITTERENTROPY is not set
+# CONFIG_CRYPTO_KEYWRAP is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+CONFIG_CRYPTO_KPP=y
+CONFIG_CRYPTO_KPP2=y
+CONFIG_CRYPTO_LIB_AES=y
+CONFIG_CRYPTO_LIB_ARC4=y
+# CONFIG_CRYPTO_LIB_BLAKE2S is not set
+# CONFIG_CRYPTO_LIB_CHACHA is not set
+# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set
+# CONFIG_CRYPTO_LIB_CURVE25519 is not set
+# CONFIG_CRYPTO_LIB_POLY1305 is not set
+CONFIG_CRYPTO_LIB_POLY1305_RSIZE=9
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_LZ4 is not set
+# CONFIG_CRYPTO_LZ4HC is not set
+# CONFIG_CRYPTO_LZO is not set
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_MCRYPTD is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_MORUS1280 is not set
+# CONFIG_CRYPTO_MORUS1280_AVX2 is not set
+# CONFIG_CRYPTO_MORUS1280_SSE2 is not set
+# CONFIG_CRYPTO_MORUS640 is not set
+# CONFIG_CRYPTO_MORUS640_SSE2 is not set
+# CONFIG_CRYPTO_NHPOLY1305_NEON is not set
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_NULL2=y
+# CONFIG_CRYPTO_OFB is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_PCOMP is not set
+# CONFIG_CRYPTO_PCOMP2 is not set
+CONFIG_CRYPTO_PCRYPT=y
+# CONFIG_CRYPTO_POLY1305 is not set
+# CONFIG_CRYPTO_POLY1305_ARM is not set
+# CONFIG_CRYPTO_POLY1305_MIPS is not set
+# CONFIG_CRYPTO_POLY1305_NEON is not set
+# CONFIG_CRYPTO_POLY1305_X86_64 is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_RNG is not set
+# CONFIG_CRYPTO_RSA is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SALSA20_586 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SEQIV is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA1_ARM is not set
+# CONFIG_CRYPTO_SHA1_ARM64_CE is not set
+# CONFIG_CRYPTO_SHA1_ARM_CE is not set
+# CONFIG_CRYPTO_SHA1_ARM_NEON is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA256_ARM is not set
+# CONFIG_CRYPTO_SHA256_ARM64 is not set
+# CONFIG_CRYPTO_SHA2_ARM64_CE is not set
+# CONFIG_CRYPTO_SHA2_ARM_CE is not set
+# CONFIG_CRYPTO_SHA3 is not set
+# CONFIG_CRYPTO_SHA3_ARM64 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_SHA512_ARM is not set
+# CONFIG_CRYPTO_SHA512_ARM64 is not set
+# CONFIG_CRYPTO_SHA512_ARM64_CE is not set
+# CONFIG_CRYPTO_SIMD is not set
+CONFIG_CRYPTO_SKCIPHER=y
+CONFIG_CRYPTO_SKCIPHER2=y
+# CONFIG_CRYPTO_SM2 is not set
+# CONFIG_CRYPTO_SM3 is not set
+# CONFIG_CRYPTO_SM3_ARM64_CE is not set
+# CONFIG_CRYPTO_SM4 is not set
+# CONFIG_CRYPTO_SM4_ARM64_CE is not set
+# CONFIG_CRYPTO_SPECK is not set
+# CONFIG_CRYPTO_STATS is not set
+# CONFIG_CRYPTO_STREEBOG is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_TWOFISH_586 is not set
+# CONFIG_CRYPTO_TWOFISH_COMMON is not set
+# CONFIG_CRYPTO_USER is not set
+# CONFIG_CRYPTO_USER_API_AEAD is not set
+# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set
+# CONFIG_CRYPTO_USER_API_HASH is not set
+# CONFIG_CRYPTO_USER_API_RNG is not set
+# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set
+# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
+# CONFIG_CRYPTO_VMAC is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_XXHASH is not set
+# CONFIG_CRYPTO_ZLIB is not set
+# CONFIG_CRYPTO_ZSTD is not set
+# CONFIG_CS5535_MFGPT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_CSD_LOCK_WAIT_DEBUG is not set
+# CONFIG_CUSE is not set
+# CONFIG_CW1200 is not set
+# CONFIG_CXD2880_SPI_DRV is not set
+# CONFIG_CXL_AFU_DRIVER_OPS is not set
+# CONFIG_CXL_BASE is not set
+# CONFIG_CXL_EEH is not set
+# CONFIG_CXL_KERNEL_API is not set
+# CONFIG_CXL_LIB is not set
+# CONFIG_CYPRESS_FIRMWARE is not set
+# CONFIG_DA280 is not set
+# CONFIG_DA311 is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_DAX is not set
+# CONFIG_DCB is not set
+# CONFIG_DDR is not set
+# CONFIG_DEBUG_ALIGN_RODATA is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_EFI is not set
+# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_32B is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_FS_ALLOW_ALL=y
+# CONFIG_DEBUG_FS_ALLOW_NONE is not set
+# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set
+# CONFIG_DEBUG_GPIO is not set
+# CONFIG_DEBUG_HIGHMEM is not set
+# CONFIG_DEBUG_ICEDCC is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_INFO_BTF is not set
+# CONFIG_DEBUG_INFO_COMPRESSED is not set
+# CONFIG_DEBUG_INFO_DWARF4 is not set
+CONFIG_DEBUG_INFO_REDUCED=y
+# CONFIG_DEBUG_INFO_SPLIT is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_KOBJECT_RELEASE is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_LL is not set
+# CONFIG_DEBUG_LL_UART_8250 is not set
+# CONFIG_DEBUG_LL_UART_PL01X is not set
+# CONFIG_DEBUG_LOCKDEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_MISC is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_NX_TEST is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_PAGEALLOC is not set
+# CONFIG_DEBUG_PAGE_REF is not set
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+# CONFIG_DEBUG_PINCTRL is not set
+# CONFIG_DEBUG_PI_LIST is not set
+# CONFIG_DEBUG_PLIST is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RODATA_TEST is not set
+# CONFIG_DEBUG_RSEQ is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_DEBUG_RWSEMS is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+# CONFIG_DEBUG_SEMIHOSTING is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_STACKOVERFLOW is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set
+# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set
+# CONFIG_DEBUG_TIMEKEEPING is not set
+# CONFIG_DEBUG_UART_8250_PALMCHIP is not set
+# CONFIG_DEBUG_UART_8250_WORD is not set
+# CONFIG_DEBUG_UART_BCM63XX is not set
+# CONFIG_DEBUG_UART_FLOW_CONTROL is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_VIRTUAL is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_VM_PGTABLE is not set
+# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set
+# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set
+# CONFIG_DEBUG_WX is not set
+# CONFIG_DEBUG_ZBOOT is not set
+# CONFIG_DECNET is not set
+# CONFIG_DEFAULT_CODEL is not set
+CONFIG_DEFAULT_CUBIC=y
+CONFIG_DEFAULT_DEADLINE=y
+# CONFIG_DEFAULT_FQ is not set
+CONFIG_DEFAULT_FQ_CODEL=y
+CONFIG_DEFAULT_HOSTNAME="(none)"
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
+CONFIG_DEFAULT_INIT=""
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_DEFAULT_NET_SCH="fq_codel"
+# CONFIG_DEFAULT_NOOP is not set
+# CONFIG_DEFAULT_PFIFO_FAST is not set
+# CONFIG_DEFAULT_RENO is not set
+CONFIG_DEFAULT_SECURITY=""
+CONFIG_DEFAULT_SECURITY_DAC=y
+# CONFIG_DEFAULT_SECURITY_SELINUX is not set
+# CONFIG_DEFAULT_SFQ is not set
+CONFIG_DEFAULT_TCP_CONG="cubic"
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set
+# CONFIG_DELL_LAPTOP is not set
+# CONFIG_DELL_RBTN is not set
+# CONFIG_DELL_SMBIOS is not set
+# CONFIG_DELL_SMO8800 is not set
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+# CONFIG_DETECT_HUNG_TASK is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_DEVMEM is not set
+CONFIG_DEVPORT=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_DEVTMPFS is not set
+# CONFIG_DEVTMPFS_MOUNT is not set
+# CONFIG_DEV_DAX is not set
+# CONFIG_DGAP is not set
+# CONFIG_DGNC is not set
+# CONFIG_DHT11 is not set
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_DISPLAY_CONNECTOR_ANALOG_TV is not set
+# CONFIG_DISPLAY_CONNECTOR_DVI is not set
+# CONFIG_DISPLAY_CONNECTOR_HDMI is not set
+# CONFIG_DISPLAY_ENCODER_TFP410 is not set
+# CONFIG_DISPLAY_ENCODER_TPD12S015 is not set
+# CONFIG_DISPLAY_PANEL_DPI is not set
+# CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02 is not set
+# CONFIG_DISPLAY_PANEL_TPO_TD028TTEC1 is not set
+# CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1 is not set
+# CONFIG_DL2K is not set
+# CONFIG_DLHL60D is not set
+# CONFIG_DLM is not set
+# CONFIG_DM9000 is not set
+# CONFIG_DMABUF_HEAPS is not set
+# CONFIG_DMABUF_MOVE_NOTIFY is not set
+# CONFIG_DMABUF_SELFTESTS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_DMADEVICES_DEBUG is not set
+# CONFIG_DMARD06 is not set
+# CONFIG_DMARD09 is not set
+# CONFIG_DMARD10 is not set
+# CONFIG_DMASCC is not set
+# CONFIG_DMATEST is not set
+# CONFIG_DMA_API_DEBUG is not set
+CONFIG_DMA_COHERENT_POOL=y
+CONFIG_DMA_DECLARE_COHERENT=y
+# CONFIG_DMA_ENGINE is not set
+# CONFIG_DMA_FENCE_TRACE is not set
+# CONFIG_DMA_JZ4780 is not set
+CONFIG_DMA_NONCOHERENT_MMAP=y
+# CONFIG_DMA_NOOP_OPS is not set
+# CONFIG_DMA_PERNUMA_CMA is not set
+# CONFIG_DMA_SHARED_BUFFER is not set
+# CONFIG_DMA_VIRT_OPS is not set
+# CONFIG_DM_CACHE is not set
+# CONFIG_DM_CLONE is not set
+# CONFIG_DM_DEBUG is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_DUST is not set
+# CONFIG_DM_EBS is not set
+# CONFIG_DM_ERA is not set
+# CONFIG_DM_FLAKEY is not set
+# CONFIG_DM_INTEGRITY is not set
+# CONFIG_DM_LOG_USERSPACE is not set
+# CONFIG_DM_LOG_WRITES is not set
+# CONFIG_DM_MQ_DEFAULT is not set
+# CONFIG_DM_MULTIPATH is not set
+# CONFIG_DM_RAID is not set
+# CONFIG_DM_SWITCH is not set
+# CONFIG_DM_THIN_PROVISIONING is not set
+# CONFIG_DM_UEVENT is not set
+# CONFIG_DM_UNSTRIPED is not set
+# CONFIG_DM_VERITY is not set
+# CONFIG_DM_WRITECACHE is not set
+# CONFIG_DM_ZERO is not set
+# CONFIG_DNET is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_DNS_RESOLVER is not set
+CONFIG_DOUBLEFAULT=y
+# CONFIG_DP83822_PHY is not set
+# CONFIG_DP83848_PHY is not set
+# CONFIG_DP83867_PHY is not set
+# CONFIG_DP83869_PHY is not set
+# CONFIG_DP83TC811_PHY is not set
+# CONFIG_DPOT_DAC is not set
+# CONFIG_DPS310 is not set
+CONFIG_DQL=y
+# CONFIG_DRAGONRISE_FF is not set
+# CONFIG_DRM is not set
+# CONFIG_DRM_AMDGPU is not set
+# CONFIG_DRM_AMDGPU_CIK is not set
+# CONFIG_DRM_AMDGPU_GART_DEBUGFS is not set
+# CONFIG_DRM_AMDGPU_SI is not set
+# CONFIG_DRM_AMDGPU_USERPTR is not set
+# CONFIG_DRM_AMD_ACP is not set
+# CONFIG_DRM_AMD_DC_DCN2_0 is not set
+# CONFIG_DRM_AMD_DC_DCN3_0 is not set
+# CONFIG_DRM_AMD_DC_HDCP is not set
+# CONFIG_DRM_AMD_DC_SI is not set
+# CONFIG_DRM_ANALOGIX_ANX6345 is not set
+# CONFIG_DRM_ANALOGIX_ANX78XX is not set
+# CONFIG_DRM_ARCPGU is not set
+# CONFIG_DRM_ARMADA is not set
+# CONFIG_DRM_AST is not set
+# CONFIG_DRM_BOCHS is not set
+# CONFIG_DRM_CDNS_DSI is not set
+# CONFIG_DRM_CDNS_MHDP8546 is not set
+# CONFIG_DRM_CHRONTEL_CH7033 is not set
+# CONFIG_DRM_CIRRUS_QEMU is not set
+# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set
+# CONFIG_DRM_DEBUG_MM is not set
+# CONFIG_DRM_DEBUG_SELFTEST is not set
+# CONFIG_DRM_DISPLAY_CONNECTOR is not set
+# CONFIG_DRM_DP_AUX_CHARDEV is not set
+# CONFIG_DRM_DP_CEC is not set
+# CONFIG_DRM_DUMB_VGA_DAC is not set
+# CONFIG_DRM_DW_HDMI_CEC is not set
+# CONFIG_DRM_ETNAVIV is not set
+# CONFIG_DRM_EXYNOS is not set
+# CONFIG_DRM_FBDEV_EMULATION is not set
+# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set
+# CONFIG_DRM_FSL_DCU is not set
+# CONFIG_DRM_GM12U320 is not set
+# CONFIG_DRM_GMA500 is not set
+# CONFIG_DRM_HDLCD is not set
+# CONFIG_DRM_HISI_HIBMC is not set
+# CONFIG_DRM_HISI_KIRIN is not set
+# CONFIG_DRM_I2C_ADV7511 is not set
+# CONFIG_DRM_I2C_CH7006 is not set
+# CONFIG_DRM_I2C_NXP_TDA9950 is not set
+# CONFIG_DRM_I2C_NXP_TDA998X is not set
+# CONFIG_DRM_I2C_SIL164 is not set
+# CONFIG_DRM_I915 is not set
+# CONFIG_DRM_KOMEDA is not set
+# CONFIG_DRM_LEGACY is not set
+# CONFIG_DRM_LIB_RANDOM is not set
+# CONFIG_DRM_LIMA is not set
+# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set
+# CONFIG_DRM_LONTIUM_LT9611 is not set
+# CONFIG_DRM_LVDS_CODEC is not set
+# CONFIG_DRM_LVDS_ENCODER is not set
+# CONFIG_DRM_MALI_DISPLAY is not set
+# CONFIG_DRM_MCDE is not set
+# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set
+# CONFIG_DRM_MGAG200 is not set
+# CONFIG_DRM_MXSFB is not set
+# CONFIG_DRM_NOUVEAU is not set
+# CONFIG_DRM_NWL_MIPI_DSI is not set
+# CONFIG_DRM_NXP_PTN3460 is not set
+# CONFIG_DRM_OMAP is not set
+# CONFIG_DRM_PANEL_ARM_VERSATILE is not set
+# CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 is not set
+# CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set
+# CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set
+# CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set
+# CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set
+# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set
+# CONFIG_DRM_PANEL_ILITEK_IL9322 is not set
+# CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set
+# CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set
+# CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set
+# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829 is not set
+# CONFIG_DRM_PANEL_LG_LB035Q02 is not set
+# CONFIG_DRM_PANEL_LG_LG4573 is not set
+# CONFIG_DRM_PANEL_LVDS is not set
+# CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set
+# CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set
+# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set
+# CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set
+# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set
+# CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set
+# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set
+# CONFIG_DRM_PANEL_ROCKTECH_JH057N00900 is not set
+# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set
+# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set
+# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set
+# CONFIG_DRM_PANEL_SIMPLE is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7703 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set
+# CONFIG_DRM_PANEL_SONY_ACX424AKP is not set
+# CONFIG_DRM_PANEL_SONY_ACX565AKM is not set
+# CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set
+# CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set
+# CONFIG_DRM_PANEL_TPO_TPG110 is not set
+# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set
+# CONFIG_DRM_PANEL_VISIONOX_RM69299 is not set
+# CONFIG_DRM_PANEL_XINPENG_XPP055C272 is not set
+# CONFIG_DRM_PANFROST is not set
+# CONFIG_DRM_PARADE_PS8622 is not set
+# CONFIG_DRM_PARADE_PS8640 is not set
+# CONFIG_DRM_PL111 is not set
+# CONFIG_DRM_QXL is not set
+# CONFIG_DRM_RADEON is not set
+# CONFIG_DRM_RADEON_USERPTR is not set
+# CONFIG_DRM_RCAR_DW_HDMI is not set
+# CONFIG_DRM_RCAR_LVDS is not set
+# CONFIG_DRM_SII902X is not set
+# CONFIG_DRM_SII9234 is not set
+# CONFIG_DRM_SIL_SII8620 is not set
+# CONFIG_DRM_SIMPLE_BRIDGE is not set
+# CONFIG_DRM_STI is not set
+# CONFIG_DRM_STM is not set
+# CONFIG_DRM_SUN4I is not set
+# CONFIG_DRM_THINE_THC63LVD1024 is not set
+# CONFIG_DRM_TIDSS is not set
+# CONFIG_DRM_TILCDC is not set
+# CONFIG_DRM_TINYDRM is not set
+# CONFIG_DRM_TI_SN65DSI86 is not set
+# CONFIG_DRM_TI_TFP410 is not set
+# CONFIG_DRM_TI_TPD12S015 is not set
+# CONFIG_DRM_TOSHIBA_TC358762 is not set
+# CONFIG_DRM_TOSHIBA_TC358764 is not set
+# CONFIG_DRM_TOSHIBA_TC358767 is not set
+# CONFIG_DRM_TOSHIBA_TC358768 is not set
+# CONFIG_DRM_TOSHIBA_TC358775 is not set
+# CONFIG_DRM_TVE200 is not set
+# CONFIG_DRM_UDL is not set
+# CONFIG_DRM_VBOXVIDEO is not set
+# CONFIG_DRM_VC4_HDMI_CEC is not set
+# CONFIG_DRM_VGEM is not set
+# CONFIG_DRM_VIRTIO_GPU is not set
+# CONFIG_DRM_VKMS is not set
+# CONFIG_DRM_VMWGFX is not set
+# CONFIG_DRM_XEN is not set
+# CONFIG_DS1682 is not set
+# CONFIG_DS1803 is not set
+# CONFIG_DS4424 is not set
+# CONFIG_DST_CACHE is not set
+# CONFIG_DTLK is not set
+# CONFIG_DUMMY is not set
+CONFIG_DUMMY_CONSOLE_COLUMNS=80
+CONFIG_DUMMY_CONSOLE_ROWS=25
+# CONFIG_DUMMY_IRQ is not set
+# CONFIG_DVB_A8293 is not set
+# CONFIG_DVB_AF9013 is not set
+# CONFIG_DVB_AF9033 is not set
+# CONFIG_DVB_AS102 is not set
+# CONFIG_DVB_ASCOT2E is not set
+# CONFIG_DVB_ATBM8830 is not set
+# CONFIG_DVB_AU8522_DTV is not set
+# CONFIG_DVB_AU8522_V4L is not set
+# CONFIG_DVB_B2C2_FLEXCOP_USB is not set
+# CONFIG_DVB_BCM3510 is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_DVB_CX22700 is not set
+# CONFIG_DVB_CX22702 is not set
+# CONFIG_DVB_CX24110 is not set
+# CONFIG_DVB_CX24116 is not set
+# CONFIG_DVB_CX24117 is not set
+# CONFIG_DVB_CX24120 is not set
+# CONFIG_DVB_CX24123 is not set
+# CONFIG_DVB_CXD2099 is not set
+# CONFIG_DVB_CXD2820R is not set
+# CONFIG_DVB_CXD2841ER is not set
+# CONFIG_DVB_CXD2880 is not set
+# CONFIG_DVB_DEMUX_SECTION_LOSS_LOG is not set
+# CONFIG_DVB_DIB3000MB is not set
+# CONFIG_DVB_DIB3000MC is not set
+# CONFIG_DVB_DIB7000M is not set
+# CONFIG_DVB_DIB7000P is not set
+# CONFIG_DVB_DIB8000 is not set
+# CONFIG_DVB_DIB9000 is not set
+# CONFIG_DVB_DRX39XYJ is not set
+# CONFIG_DVB_DRXD is not set
+# CONFIG_DVB_DRXK is not set
+# CONFIG_DVB_DS3000 is not set
+# CONFIG_DVB_DUMMY_FE is not set
+# CONFIG_DVB_DYNAMIC_MINORS is not set
+# CONFIG_DVB_EC100 is not set
+# CONFIG_DVB_FIREDTV is not set
+# CONFIG_DVB_HELENE is not set
+# CONFIG_DVB_HORUS3A is not set
+# CONFIG_DVB_ISL6405 is not set
+# CONFIG_DVB_ISL6421 is not set
+# CONFIG_DVB_ISL6423 is not set
+# CONFIG_DVB_IX2505V is not set
+# CONFIG_DVB_L64781 is not set
+# CONFIG_DVB_LG2160 is not set
+# CONFIG_DVB_LGDT3305 is not set
+# CONFIG_DVB_LGDT3306A is not set
+# CONFIG_DVB_LGDT330X is not set
+# CONFIG_DVB_LGS8GL5 is not set
+# CONFIG_DVB_LGS8GXX is not set
+# CONFIG_DVB_LNBH25 is not set
+# CONFIG_DVB_LNBH29 is not set
+# CONFIG_DVB_LNBP21 is not set
+# CONFIG_DVB_LNBP22 is not set
+# CONFIG_DVB_M88DS3103 is not set
+# CONFIG_DVB_M88RS2000 is not set
+CONFIG_DVB_MAX_ADAPTERS=16
+# CONFIG_DVB_MB86A16 is not set
+# CONFIG_DVB_MB86A20S is not set
+# CONFIG_DVB_MMAP is not set
+# CONFIG_DVB_MN88443X is not set
+# CONFIG_DVB_MN88472 is not set
+# CONFIG_DVB_MN88473 is not set
+# CONFIG_DVB_MT312 is not set
+# CONFIG_DVB_MT352 is not set
+# CONFIG_DVB_MXL5XX is not set
+# CONFIG_DVB_NET is not set
+# CONFIG_DVB_NXT200X is not set
+# CONFIG_DVB_NXT6000 is not set
+# CONFIG_DVB_OR51132 is not set
+# CONFIG_DVB_OR51211 is not set
+# CONFIG_DVB_PLATFORM_DRIVERS is not set
+# CONFIG_DVB_PLL is not set
+# CONFIG_DVB_RTL2830 is not set
+# CONFIG_DVB_RTL2832 is not set
+# CONFIG_DVB_RTL2832_SDR is not set
+# CONFIG_DVB_S5H1409 is not set
+# CONFIG_DVB_S5H1411 is not set
+# CONFIG_DVB_S5H1420 is not set
+# CONFIG_DVB_S5H1432 is not set
+# CONFIG_DVB_S921 is not set
+# CONFIG_DVB_SI2165 is not set
+# CONFIG_DVB_SI2168 is not set
+# CONFIG_DVB_SI21XX is not set
+# CONFIG_DVB_SP2 is not set
+# CONFIG_DVB_SP8870 is not set
+# CONFIG_DVB_SP887X is not set
+# CONFIG_DVB_STB0899 is not set
+# CONFIG_DVB_STB6000 is not set
+# CONFIG_DVB_STB6100 is not set
+# CONFIG_DVB_STV0288 is not set
+# CONFIG_DVB_STV0297 is not set
+# CONFIG_DVB_STV0299 is not set
+# CONFIG_DVB_STV0367 is not set
+# CONFIG_DVB_STV0900 is not set
+# CONFIG_DVB_STV090x is not set
+# CONFIG_DVB_STV0910 is not set
+# CONFIG_DVB_STV6110 is not set
+# CONFIG_DVB_STV6110x is not set
+# CONFIG_DVB_STV6111 is not set
+# CONFIG_DVB_TC90522 is not set
+# CONFIG_DVB_TDA10021 is not set
+# CONFIG_DVB_TDA10023 is not set
+# CONFIG_DVB_TDA10048 is not set
+# CONFIG_DVB_TDA1004X is not set
+# CONFIG_DVB_TDA10071 is not set
+# CONFIG_DVB_TDA10086 is not set
+# CONFIG_DVB_TDA18271C2DD is not set
+# CONFIG_DVB_TDA665x is not set
+# CONFIG_DVB_TDA8083 is not set
+# CONFIG_DVB_TDA8261 is not set
+# CONFIG_DVB_TDA826X is not set
+# CONFIG_DVB_TEST_DRIVERS is not set
+# CONFIG_DVB_TS2020 is not set
+# CONFIG_DVB_TTUSB_BUDGET is not set
+# CONFIG_DVB_TTUSB_DEC is not set
+# CONFIG_DVB_TUA6100 is not set
+# CONFIG_DVB_TUNER_CX24113 is not set
+# CONFIG_DVB_TUNER_DIB0070 is not set
+# CONFIG_DVB_TUNER_DIB0090 is not set
+# CONFIG_DVB_TUNER_ITD1000 is not set
+# CONFIG_DVB_ULE_DEBUG is not set
+# CONFIG_DVB_USB_V2 is not set
+# CONFIG_DVB_VES1820 is not set
+# CONFIG_DVB_VES1X93 is not set
+# CONFIG_DVB_ZD1301_DEMOD is not set
+# CONFIG_DVB_ZL10036 is not set
+# CONFIG_DVB_ZL10039 is not set
+# CONFIG_DVB_ZL10353 is not set
+# CONFIG_DWC_XLGMAC is not set
+# CONFIG_DWMAC_DWC_QOS_ETH is not set
+# CONFIG_DWMAC_INTEL_PLAT is not set
+# CONFIG_DWMAC_IPQ806X is not set
+# CONFIG_DWMAC_LPC18XX is not set
+# CONFIG_DWMAC_MESON is not set
+# CONFIG_DWMAC_ROCKCHIP is not set
+# CONFIG_DWMAC_SOCFPGA is not set
+# CONFIG_DWMAC_STI is not set
+# CONFIG_DW_AXI_DMAC is not set
+# CONFIG_DW_DMAC is not set
+# CONFIG_DW_DMAC_PCI is not set
+# CONFIG_DW_EDMA is not set
+# CONFIG_DW_EDMA_PCIE is not set
+# CONFIG_DW_WATCHDOG is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+CONFIG_DYNAMIC_DEBUG_CORE=y
+# CONFIG_E100 is not set
+# CONFIG_E1000 is not set
+# CONFIG_E1000E is not set
+# CONFIG_E1000E_HWTS is not set
+# CONFIG_EARLY_PRINTK_8250 is not set
+# CONFIG_EARLY_PRINTK_USB_XDBC is not set
+# CONFIG_EBC_C384_WDT is not set
+# CONFIG_ECHO is not set
+# CONFIG_ECRYPT_FS is not set
+# CONFIG_EDAC is not set
+# CONFIG_EEEPC_LAPTOP is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_EEPROM_93XX46 is not set
+# CONFIG_EEPROM_AT24 is not set
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EEPROM_DIGSY_MTC_CFG is not set
+# CONFIG_EEPROM_EE1004 is not set
+# CONFIG_EEPROM_IDT_89HPESX is not set
+# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
+# CONFIG_EFI is not set
+CONFIG_EFI_PARTITION=y
+# CONFIG_EFI_VARS_PSTORE is not set
+# CONFIG_EFS_FS is not set
+CONFIG_ELFCORE=y
+# CONFIG_ELF_CORE is not set
+# CONFIG_EMAC_ROCKCHIP is not set
+CONFIG_EMBEDDED=y
+# CONFIG_EM_TIMER_STI is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+# CONFIG_ENA_ETHERNET is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_ENCRYPTED_KEYS is not set
+# CONFIG_ENCX24J600 is not set
+# CONFIG_ENERGY_MODEL is not set
+# CONFIG_ENIC is not set
+# CONFIG_ENVELOPE_DETECTOR is not set
+# CONFIG_EPAPR_PARAVIRT is not set
+# CONFIG_EPIC100 is not set
+CONFIG_EPOLL=y
+# CONFIG_EQUALIZER is not set
+# CONFIG_EROFS_FS is not set
+# CONFIG_ET131X is not set
+CONFIG_ETHERNET=y
+# CONFIG_ETHOC is not set
+CONFIG_ETHTOOL_NETLINK=y
+CONFIG_EVENTFD=y
+# CONFIG_EVM is not set
+# CONFIG_EXFAT_FS is not set
+CONFIG_EXPERT=y
+CONFIG_EXPORTFS=y
+# CONFIG_EXPORTFS_BLOCK_OPS is not set
+# CONFIG_EXT2_FS is not set
+CONFIG_EXT2_FS_XATTR=y
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_DEBUG is not set
+# CONFIG_EXT4_ENCRYPTION is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_EXT4_FS_POSIX_ACL is not set
+# CONFIG_EXT4_FS_SECURITY is not set
+CONFIG_EXT4_USE_FOR_EXT2=y
+# CONFIG_EXTCON is not set
+# CONFIG_EXTCON_ADC_JACK is not set
+# CONFIG_EXTCON_ARIZONA is not set
+# CONFIG_EXTCON_AXP288 is not set
+# CONFIG_EXTCON_FSA9480 is not set
+# CONFIG_EXTCON_GPIO is not set
+# CONFIG_EXTCON_INTEL_INT3496 is not set
+# CONFIG_EXTCON_MAX3355 is not set
+# CONFIG_EXTCON_PTN5150 is not set
+# CONFIG_EXTCON_QCOM_SPMI_MISC is not set
+# CONFIG_EXTCON_RT8973A is not set
+# CONFIG_EXTCON_SM5502 is not set
+# CONFIG_EXTCON_USB_GPIO is not set
+CONFIG_EXTRA_FIRMWARE=""
+CONFIG_EXTRA_TARGETS=""
+# CONFIG_EXYNOS_ADC is not set
+# CONFIG_EXYNOS_VIDEO is not set
+# CONFIG_EZCHIP_NPS_MANAGEMENT_ENET is not set
+# CONFIG_EZX_PCAP is not set
+# CONFIG_F2FS_CHECK_FS is not set
+# CONFIG_F2FS_FAULT_INJECTION is not set
+# CONFIG_F2FS_FS is not set
+# CONFIG_F2FS_FS_COMPRESSION is not set
+# CONFIG_F2FS_FS_ENCRYPTION is not set
+# CONFIG_F2FS_FS_POSIX_ACL is not set
+# CONFIG_F2FS_FS_SECURITY is not set
+CONFIG_F2FS_FS_XATTR=y
+# CONFIG_F2FS_IO_TRACE is not set
+CONFIG_F2FS_STAT_FS=y
+# CONFIG_FAILOVER is not set
+# CONFIG_FAIR_GROUP_SCHED is not set
+# CONFIG_FANOTIFY is not set
+# CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_FAT_DEFAULT_UTF8 is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_FB is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_ARMCLCD is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_AUO_K190X is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_BIG_ENDIAN is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+# CONFIG_FB_BOTH_ENDIAN is not set
+# CONFIG_FB_BROADSHEET is not set
+# CONFIG_FB_CARMINE is not set
+# CONFIG_FB_CFB_COPYAREA is not set
+# CONFIG_FB_CFB_FILLRECT is not set
+# CONFIG_FB_CFB_IMAGEBLIT is not set
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_DA8XX is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_FLEX is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_FSL_DIU is not set
+# CONFIG_FB_GEODE is not set
+# CONFIG_FB_GOLDFISH is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_I740 is not set
+# CONFIG_FB_IBM_GXT4500 is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_IMX is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_LE80578 is not set
+# CONFIG_FB_LITTLE_ENDIAN is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_MXS is not set
+# CONFIG_FB_N411 is not set
+# CONFIG_FB_NEOMAGIC is not set
+CONFIG_FB_NOTIFY=y
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_OF is not set
+# CONFIG_FB_OMAP2 is not set
+# CONFIG_FB_OPENCORES is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_PS3 is not set
+# CONFIG_FB_PXA is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIMPLE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_SM712 is not set
+# CONFIG_FB_SM750 is not set
+# CONFIG_FB_SMSCUFX is not set
+# CONFIG_FB_SSD1307 is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_TFT is not set
+# CONFIG_FB_TFT_AGM1264K_FL is not set
+# CONFIG_FB_TFT_BD663474 is not set
+# CONFIG_FB_TFT_FBTFT_DEVICE is not set
+# CONFIG_FB_TFT_HX8340BN is not set
+# CONFIG_FB_TFT_HX8347D is not set
+# CONFIG_FB_TFT_HX8353D is not set
+# CONFIG_FB_TFT_HX8357D is not set
+# CONFIG_FB_TFT_ILI9163 is not set
+# CONFIG_FB_TFT_ILI9320 is not set
+# CONFIG_FB_TFT_ILI9325 is not set
+# CONFIG_FB_TFT_ILI9340 is not set
+# CONFIG_FB_TFT_ILI9341 is not set
+# CONFIG_FB_TFT_ILI9481 is not set
+# CONFIG_FB_TFT_ILI9486 is not set
+# CONFIG_FB_TFT_PCD8544 is not set
+# CONFIG_FB_TFT_RA8875 is not set
+# CONFIG_FB_TFT_S6D02A1 is not set
+# CONFIG_FB_TFT_S6D1121 is not set
+# CONFIG_FB_TFT_SEPS525 is not set
+# CONFIG_FB_TFT_SH1106 is not set
+# CONFIG_FB_TFT_SSD1289 is not set
+# CONFIG_FB_TFT_SSD1305 is not set
+# CONFIG_FB_TFT_SSD1306 is not set
+# CONFIG_FB_TFT_SSD1325 is not set
+# CONFIG_FB_TFT_SSD1331 is not set
+# CONFIG_FB_TFT_SSD1351 is not set
+# CONFIG_FB_TFT_ST7735R is not set
+# CONFIG_FB_TFT_ST7789V is not set
+# CONFIG_FB_TFT_TINYLCD is not set
+# CONFIG_FB_TFT_TLS8204 is not set
+# CONFIG_FB_TFT_UC1611 is not set
+# CONFIG_FB_TFT_UC1701 is not set
+# CONFIG_FB_TFT_UPD161704 is not set
+# CONFIG_FB_TFT_WATTEROTT is not set
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_TMIO is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_UDL is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_VGA16 is not set
+# CONFIG_FB_VIA is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_XGI is not set
+# CONFIG_FCOE is not set
+# CONFIG_FCOE_FNIC is not set
+# CONFIG_FDDI is not set
+# CONFIG_FEALNX is not set
+# CONFIG_FENCE_TRACE is not set
+# CONFIG_FHANDLE is not set
+CONFIG_FIB_RULES=y
+# CONFIG_FIELDBUS_DEV is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_FIND_BIT_BENCHMARK is not set
+# CONFIG_FIREWIRE is not set
+# CONFIG_FIREWIRE_NOSY is not set
+# CONFIG_FIREWIRE_SERIAL is not set
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+# CONFIG_FIRMWARE_MEMMAP is not set
+# CONFIG_FIT_PARTITION is not set
+# CONFIG_FIXED_PHY is not set
+CONFIG_FLATMEM=y
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_FM10K is not set
+# CONFIG_FMC is not set
+# CONFIG_FONTS is not set
+# CONFIG_FONT_6x8 is not set
+# CONFIG_FONT_TER16x32 is not set
+# CONFIG_FORCEDETH is not set
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_FORTIFY_SOURCE=y
+# CONFIG_FPGA is not set
+# CONFIG_FRAMEBUFFER_CONSOLE is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set
+# CONFIG_FRAME_POINTER is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_FREEZER is not set
+# CONFIG_FRONTSWAP is not set
+# CONFIG_FSCACHE is not set
+# CONFIG_FSI is not set
+# CONFIG_FSL_EDMA is not set
+# CONFIG_FSL_ENETC is not set
+# CONFIG_FSL_ENETC_MDIO is not set
+# CONFIG_FSL_ENETC_VF is not set
+# CONFIG_FSL_ERRATUM_A008585 is not set
+# CONFIG_FSL_MC_BUS is not set
+# CONFIG_FSL_PQ_MDIO is not set
+# CONFIG_FSL_QDMA is not set
+# CONFIG_FSL_RCPM is not set
+# CONFIG_FSL_XGMAC_MDIO is not set
+CONFIG_FSNOTIFY=y
+# CONFIG_FS_DAX is not set
+# CONFIG_FS_ENCRYPTION is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_FS_VERITY is not set
+# CONFIG_FTGMAC100 is not set
+# CONFIG_FTL is not set
+# CONFIG_FTMAC100 is not set
+# CONFIG_FTRACE is not set
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_FTR_FIXUP_SELFTEST is not set
+# CONFIG_FTWDT010_WATCHDOG is not set
+# CONFIG_FUJITSU_ERRATUM_010001 is not set
+# CONFIG_FUJITSU_ES is not set
+# CONFIG_FUJITSU_LAPTOP is not set
+# CONFIG_FUJITSU_TABLET is not set
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_FUSE_FS is not set
+# CONFIG_FUSION is not set
+# CONFIG_FUSION_FC is not set
+# CONFIG_FUSION_SAS is not set
+# CONFIG_FUSION_SPI is not set
+CONFIG_FUTEX=y
+CONFIG_FUTEX_PI=y
+# CONFIG_FW_CFG_SYSFS is not set
+CONFIG_FW_LOADER=y
+# CONFIG_FW_LOADER_COMPRESS is not set
+CONFIG_FW_LOADER_USER_HELPER=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+# CONFIG_FXAS21002C is not set
+# CONFIG_FXOS8700_I2C is not set
+# CONFIG_FXOS8700_SPI is not set
+CONFIG_GACT_PROB=y
+# CONFIG_GADGET_UAC1 is not set
+# CONFIG_GAMEPORT is not set
+# CONFIG_GATEWORKS_GW16083 is not set
+# CONFIG_GCC_PLUGINS is not set
+# CONFIG_GCOV is not set
+# CONFIG_GCOV_KERNEL is not set
+# CONFIG_GDB_SCRIPTS is not set
+# CONFIG_GEMINI_ETHERNET is not set
+# CONFIG_GENERIC_ADC_BATTERY is not set
+# CONFIG_GENERIC_ADC_THERMAL is not set
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+# CONFIG_GENERIC_CPU_DEVICES is not set
+CONFIG_GENERIC_HWEIGHT=y
+# CONFIG_GENERIC_IRQ_DEBUGFS is not set
+CONFIG_GENERIC_IRQ_IPI=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_NET_UTILS=y
+# CONFIG_GENERIC_PHY is not set
+CONFIG_GENERIC_PTDUMP=y
+CONFIG_GENERIC_VDSO_TIME_NS=y
+# CONFIG_GENEVE is not set
+# CONFIG_GENWQE is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_GIGASET_CAPI is not set
+# CONFIG_GIGASET_DEBUG is not set
+# CONFIG_GIGASET_DUMMYLL is not set
+# CONFIG_GLOB_SELFTEST is not set
+# CONFIG_GNSS is not set
+# CONFIG_GOLDFISH is not set
+# CONFIG_GOOGLE_FIRMWARE is not set
+# CONFIG_GP2AP002 is not set
+# CONFIG_GP2AP020A00F is not set
+# CONFIG_GPD_POCKET_FAN is not set
+# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB_FASTPATH_LIMIT=512
+# CONFIG_GPIO_104_DIO_48E is not set
+# CONFIG_GPIO_104_IDIO_16 is not set
+# CONFIG_GPIO_104_IDI_48 is not set
+# CONFIG_GPIO_74X164 is not set
+# CONFIG_GPIO_74XX_MMIO is not set
+# CONFIG_GPIO_ADNP is not set
+# CONFIG_GPIO_ADP5588 is not set
+# CONFIG_GPIO_AGGREGATOR is not set
+# CONFIG_GPIO_ALTERA is not set
+# CONFIG_GPIO_AMD8111 is not set
+# CONFIG_GPIO_AMDPT is not set
+# CONFIG_GPIO_AMD_FCH is not set
+# CONFIG_GPIO_BCM_KONA is not set
+# CONFIG_GPIO_BT8XX is not set
+# CONFIG_GPIO_CADENCE is not set
+# CONFIG_GPIO_CASCADE is not set
+# CONFIG_GPIO_CDEV is not set
+# CONFIG_GPIO_CDEV_V1 is not set
+# CONFIG_GPIO_CS5535 is not set
+# CONFIG_GPIO_DWAPB is not set
+# CONFIG_GPIO_EM is not set
+# CONFIG_GPIO_EXAR is not set
+# CONFIG_GPIO_F7188X is not set
+# CONFIG_GPIO_FTGPIO010 is not set
+# CONFIG_GPIO_GENERIC_PLATFORM is not set
+# CONFIG_GPIO_GPIO_MM is not set
+# CONFIG_GPIO_GRGPIO is not set
+# CONFIG_GPIO_GW_PLD is not set
+# CONFIG_GPIO_HLWD is not set
+# CONFIG_GPIO_ICH is not set
+# CONFIG_GPIO_IT87 is not set
+# CONFIG_GPIO_LOGICVC is not set
+# CONFIG_GPIO_LYNXPOINT is not set
+# CONFIG_GPIO_MAX3191X is not set
+# CONFIG_GPIO_MAX7300 is not set
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_MB86S7X is not set
+# CONFIG_GPIO_MC33880 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_GPIO_ML_IOH is not set
+# CONFIG_GPIO_MOCKUP is not set
+# CONFIG_GPIO_MPC8XXX is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCA953X_IRQ is not set
+# CONFIG_GPIO_PCA9570 is not set
+# CONFIG_GPIO_PCF857X is not set
+# CONFIG_GPIO_PCH is not set
+# CONFIG_GPIO_PCIE_IDIO_24 is not set
+# CONFIG_GPIO_PCI_IDIO_16 is not set
+# CONFIG_GPIO_PISOSR is not set
+# CONFIG_GPIO_PL061 is not set
+# CONFIG_GPIO_RCAR is not set
+# CONFIG_GPIO_RDC321X is not set
+# CONFIG_GPIO_SAMA5D2_PIOBU is not set
+# CONFIG_GPIO_SCH is not set
+# CONFIG_GPIO_SCH311X is not set
+# CONFIG_GPIO_SIFIVE is not set
+# CONFIG_GPIO_SX150X is not set
+# CONFIG_GPIO_SYSCON is not set
+CONFIG_GPIO_SYSFS=y
+# CONFIG_GPIO_TPIC2810 is not set
+# CONFIG_GPIO_TS4900 is not set
+# CONFIG_GPIO_TS5500 is not set
+# CONFIG_GPIO_VX855 is not set
+# CONFIG_GPIO_WATCHDOG is not set
+# CONFIG_GPIO_WINBOND is not set
+# CONFIG_GPIO_WS16C48 is not set
+# CONFIG_GPIO_XGENE is not set
+# CONFIG_GPIO_XILINX is not set
+# CONFIG_GPIO_XRA1403 is not set
+# CONFIG_GPIO_ZEVIO is not set
+# CONFIG_GPIO_ZX is not set
+# CONFIG_GREENASIA_FF is not set
+# CONFIG_GREYBUS is not set
+# CONFIG_GS_FPGABOOT is not set
+# CONFIG_GTP is not set
+# CONFIG_GUP_BENCHMARK is not set
+# CONFIG_GVE is not set
+# CONFIG_HABANA_AI is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_HAPPYMEAL is not set
+CONFIG_HARDENED_USERCOPY=y
+# CONFIG_HARDENED_USERCOPY_FALLBACK is not set
+# CONFIG_HARDENED_USERCOPY_PAGESPAN is not set
+CONFIG_HARDEN_EL2_VECTORS=y
+# CONFIG_HARDLOCKUP_DETECTOR is not set
+# CONFIG_HAVE_ARM_ARCH_TIMER is not set
+# CONFIG_HCALL_STATS is not set
+# CONFIG_HDC100X is not set
+# CONFIG_HDC2010 is not set
+# CONFIG_HDLC is not set
+# CONFIG_HDLC_CISCO is not set
+# CONFIG_HDLC_FR is not set
+# CONFIG_HDLC_PPP is not set
+# CONFIG_HDLC_RAW is not set
+# CONFIG_HDLC_RAW_ETH is not set
+# CONFIG_HDMI_LPE_AUDIO is not set
+# CONFIG_HDQ_MASTER_OMAP is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_HEADERS_INSTALL is not set
+# CONFIG_HEADER_TEST is not set
+# CONFIG_HERMES is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_HFSPLUS_FS_POSIX_ACL is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFS_FS_POSIX_ACL is not set
+# CONFIG_HI8435 is not set
+# CONFIG_HIBERNATION is not set
+# CONFIG_HID is not set
+# CONFIG_HIDRAW is not set
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACCUTOUCH is not set
+# CONFIG_HID_ACRUX is not set
+# CONFIG_HID_ACRUX_FF is not set
+# CONFIG_HID_ALPS is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_APPLEIR is not set
+# CONFIG_HID_ASUS is not set
+# CONFIG_HID_AUREAL is not set
+# CONFIG_HID_BATTERY_STRENGTH is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_BETOP_FF is not set
+# CONFIG_HID_BIGBEN_FF is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CHICONY is not set
+# CONFIG_HID_CMEDIA is not set
+# CONFIG_HID_CORSAIR is not set
+# CONFIG_HID_COUGAR is not set
+# CONFIG_HID_CP2112 is not set
+# CONFIG_HID_CREATIVE_SB0540 is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_ELAN is not set
+# CONFIG_HID_ELECOM is not set
+# CONFIG_HID_ELO is not set
+# CONFIG_HID_EMS_FF is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_GEMBIRD is not set
+# CONFIG_HID_GENERIC is not set
+# CONFIG_HID_GFRM is not set
+# CONFIG_HID_GLORIOUS is not set
+# CONFIG_HID_GOOGLE_HAMMER is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_GT683R is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_HOLTEK is not set
+# CONFIG_HID_ICADE is not set
+# CONFIG_HID_ITE is not set
+# CONFIG_HID_JABRA is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_KEYTOUCH is not set
+# CONFIG_HID_KYE is not set
+# CONFIG_HID_LCPOWER is not set
+# CONFIG_HID_LED is not set
+# CONFIG_HID_LENOVO is not set
+# CONFIG_HID_LOGITECH is not set
+# CONFIG_HID_LOGITECH_DJ is not set
+# CONFIG_HID_LOGITECH_HIDPP is not set
+# CONFIG_HID_MACALLY is not set
+# CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MALTRON is not set
+# CONFIG_HID_MAYFLASH is not set
+# CONFIG_HID_MCP2221 is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_MULTITOUCH is not set
+# CONFIG_HID_NTI is not set
+# CONFIG_HID_NTRIG is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PENMOUNT is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_PID is not set
+# CONFIG_HID_PLANTRONICS is not set
+# CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_PRODIKEYS is not set
+# CONFIG_HID_REDRAGON is not set
+# CONFIG_HID_RETRODE is not set
+# CONFIG_HID_RMI is not set
+# CONFIG_HID_ROCCAT is not set
+# CONFIG_HID_SAITEK is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SENSOR_HUB is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_SONY is not set
+# CONFIG_HID_SPEEDLINK is not set
+# CONFIG_HID_STEAM is not set
+# CONFIG_HID_STEELSERIES is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_THINGM is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_TIVO is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_U2FZERO is not set
+# CONFIG_HID_UCLOGIC is not set
+# CONFIG_HID_UDRAW_PS3 is not set
+# CONFIG_HID_VIEWSONIC is not set
+# CONFIG_HID_VIVALDI is not set
+# CONFIG_HID_WACOM is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_WIIMOTE is not set
+# CONFIG_HID_XINMO is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_HIGH_RES_TIMERS=y
+# CONFIG_HINIC is not set
+# CONFIG_HIP04_ETH is not set
+# CONFIG_HIPPI is not set
+# CONFIG_HISILICON_ERRATUM_161010101 is not set
+# CONFIG_HISILICON_ERRATUM_161600802 is not set
+# CONFIG_HISI_DMA is not set
+# CONFIG_HISI_FEMAC is not set
+# CONFIG_HISI_HIKEY_USB is not set
+# CONFIG_HIX5HD2_GMAC is not set
+# CONFIG_HMC425 is not set
+# CONFIG_HMC6352 is not set
+# CONFIG_HNS is not set
+# CONFIG_HNS3 is not set
+# CONFIG_HNS_DSAF is not set
+# CONFIG_HNS_ENET is not set
+# CONFIG_HOSTAP is not set
+# CONFIG_HOSTAP_CS is not set
+# CONFIG_HOSTAP_PCI is not set
+# CONFIG_HOSTAP_PLX is not set
+# CONFIG_HOTPLUG_CPU is not set
+# CONFIG_HOTPLUG_PCI is not set
+# CONFIG_HP03 is not set
+# CONFIG_HP100 is not set
+# CONFIG_HP206C is not set
+CONFIG_HPET_MMAP_DEFAULT=y
+# CONFIG_HPFS_FS is not set
+# CONFIG_HP_ILO is not set
+# CONFIG_HP_WIRELESS is not set
+# CONFIG_HSA_AMD is not set
+# CONFIG_HSI is not set
+# CONFIG_HSR is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_I2CPLD is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_HTS221 is not set
+# CONFIG_HTU21 is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_HVC_DCC is not set
+# CONFIG_HVC_UDBG is not set
+# CONFIG_HWLAT_TRACER is not set
+# CONFIG_HWMON is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+# CONFIG_HWMON_VID is not set
+# CONFIG_HWSPINLOCK is not set
+# CONFIG_HWSPINLOCK_OMAP is not set
+CONFIG_HW_PERF_EVENTS=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HW_RANDOM_AMD is not set
+# CONFIG_HW_RANDOM_ATMEL is not set
+# CONFIG_HW_RANDOM_BA431 is not set
+# CONFIG_HW_RANDOM_CAVIUM is not set
+# CONFIG_HW_RANDOM_CCTRNG is not set
+# CONFIG_HW_RANDOM_EXYNOS is not set
+# CONFIG_HW_RANDOM_GEODE is not set
+# CONFIG_HW_RANDOM_INTEL is not set
+# CONFIG_HW_RANDOM_IPROC_RNG200 is not set
+# CONFIG_HW_RANDOM_MTK is not set
+# CONFIG_HW_RANDOM_OMAP is not set
+# CONFIG_HW_RANDOM_OMAP3_ROM is not set
+# CONFIG_HW_RANDOM_PPC4XX is not set
+# CONFIG_HW_RANDOM_TIMERIOMEM is not set
+CONFIG_HW_RANDOM_TPM=y
+# CONFIG_HW_RANDOM_VIA is not set
+# CONFIG_HW_RANDOM_VIRTIO is not set
+# CONFIG_HW_RANDOM_XIPHERA is not set
+# CONFIG_HX711 is not set
+# CONFIG_HYPERV is not set
+# CONFIG_HYPERV_TSCPAGE is not set
+# CONFIG_HYSDN is not set
+CONFIG_HZ=100
+CONFIG_HZ_100=y
+# CONFIG_HZ_1000 is not set
+# CONFIG_HZ_1024 is not set
+# CONFIG_HZ_128 is not set
+# CONFIG_HZ_200 is not set
+# CONFIG_HZ_24 is not set
+# CONFIG_HZ_250 is not set
+# CONFIG_HZ_256 is not set
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_48 is not set
+# CONFIG_HZ_500 is not set
+# CONFIG_HZ_PERIODIC is not set
+# CONFIG_I2C is not set
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCA is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD8111 is not set
+# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set
+# CONFIG_I2C_AU1550 is not set
+# CONFIG_I2C_BCM2835 is not set
+# CONFIG_I2C_BCM_IPROC is not set
+# CONFIG_I2C_CADENCE is not set
+# CONFIG_I2C_CBUS_GPIO is not set
+# CONFIG_I2C_CHARDEV is not set
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEMUX_PINCTRL is not set
+# CONFIG_I2C_DESIGNWARE_PCI is not set
+# CONFIG_I2C_DESIGNWARE_PLATFORM is not set
+# CONFIG_I2C_DESIGNWARE_SLAVE is not set
+# CONFIG_I2C_DIOLAN_U2C is not set
+# CONFIG_I2C_EG20T is not set
+# CONFIG_I2C_ELEKTOR is not set
+# CONFIG_I2C_EMEV2 is not set
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+# CONFIG_I2C_HID is not set
+# CONFIG_I2C_I801 is not set
+# CONFIG_I2C_IBM_IIC is not set
+# CONFIG_I2C_IMG is not set
+# CONFIG_I2C_ISCH is not set
+# CONFIG_I2C_ISMT is not set
+# CONFIG_I2C_JZ4780 is not set
+# CONFIG_I2C_MLXCPLD is not set
+# CONFIG_I2C_MPC is not set
+# CONFIG_I2C_MT65XX is not set
+# CONFIG_I2C_MUX is not set
+# CONFIG_I2C_MUX_GPIO is not set
+# CONFIG_I2C_MUX_GPMUX is not set
+# CONFIG_I2C_MUX_LTC4306 is not set
+# CONFIG_I2C_MUX_MLXCPLD is not set
+# CONFIG_I2C_MUX_PCA9541 is not set
+# CONFIG_I2C_MUX_PCA954x is not set
+# CONFIG_I2C_MUX_PINCTRL is not set
+# CONFIG_I2C_MUX_REG is not set
+# CONFIG_I2C_MV64XXX is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_NOMADIK is not set
+# CONFIG_I2C_NVIDIA_GPU is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_OCTEON is not set
+# CONFIG_I2C_PARPORT is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_PCA_ISA is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_PIIX4 is not set
+# CONFIG_I2C_PXA_PCI is not set
+# CONFIG_I2C_PXA_SLAVE is not set
+# CONFIG_I2C_RCAR is not set
+# CONFIG_I2C_RK3X is not set
+# CONFIG_I2C_ROBOTFUZZ_OSIF is not set
+# CONFIG_I2C_S3C2410 is not set
+# CONFIG_I2C_SCMI is not set
+# CONFIG_I2C_SH_MOBILE is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+# CONFIG_I2C_SLAVE is not set
+# CONFIG_I2C_SLAVE_EEPROM is not set
+# CONFIG_I2C_SMBUS is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_THUNDERX is not set
+# CONFIG_I2C_TINY_USB is not set
+# CONFIG_I2C_VERSATILE is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_XILINX is not set
+# CONFIG_I3C is not set
+# CONFIG_I40E is not set
+# CONFIG_I40EVF is not set
+# CONFIG_I6300ESB_WDT is not set
+# CONFIG_I82092 is not set
+# CONFIG_I82365 is not set
+# CONFIG_IAQCORE is not set
+# CONFIG_IBM_ASM is not set
+# CONFIG_IBM_EMAC_DEBUG is not set
+# CONFIG_IBM_EMAC_EMAC4 is not set
+# CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_IBM_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_EMAC_RGMII is not set
+# CONFIG_IBM_EMAC_TAH is not set
+# CONFIG_IBM_EMAC_ZMII is not set
+# CONFIG_ICE is not set
+# CONFIG_ICP10100 is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_IDE is not set
+# CONFIG_IDEAPAD_LAPTOP is not set
+# CONFIG_IDE_GD is not set
+# CONFIG_IDE_PROC_FS is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+# CONFIG_IDLE_PAGE_TRACKING is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_IEEE802154_ADF7242 is not set
+# CONFIG_IEEE802154_ATUSB is not set
+# CONFIG_IEEE802154_CA8210 is not set
+# CONFIG_IEEE802154_HWSIM is not set
+# CONFIG_IEEE802154_MCR20A is not set
+# CONFIG_IFB is not set
+# CONFIG_IGB is not set
+# CONFIG_IGBVF is not set
+# CONFIG_IGC is not set
+# CONFIG_IIO is not set
+# CONFIG_IIO_BUFFER is not set
+# CONFIG_IIO_BUFFER_CB is not set
+# CONFIG_IIO_BUFFER_DMA is not set
+# CONFIG_IIO_BUFFER_DMAENGINE is not set
+# CONFIG_IIO_BUFFER_HDC2010 is not set
+# CONFIG_IIO_BUFFER_HW_CONSUMER is not set
+# CONFIG_IIO_CONFIGFS is not set
+CONFIG_IIO_CONSUMERS_PER_TRIGGER=2
+# CONFIG_IIO_CROS_EC_ACCEL_LEGACY is not set
+# CONFIG_IIO_INTERRUPT_TRIGGER is not set
+# CONFIG_IIO_MUX is not set
+# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set
+# CONFIG_IIO_RESCALE is not set
+# CONFIG_IIO_SIMPLE_DUMMY is not set
+# CONFIG_IIO_SSP_SENSORHUB is not set
+# CONFIG_IIO_ST_ACCEL_3AXIS is not set
+# CONFIG_IIO_ST_GYRO_3AXIS is not set
+# CONFIG_IIO_ST_LSM6DSX is not set
+# CONFIG_IIO_ST_MAGN_3AXIS is not set
+# CONFIG_IIO_ST_PRESS is not set
+# CONFIG_IIO_SW_DEVICE is not set
+# CONFIG_IIO_SW_TRIGGER is not set
+# CONFIG_IIO_SYSFS_TRIGGER is not set
+# CONFIG_IIO_TRIGGER is not set
+# CONFIG_IIO_TRIGGERED_EVENT is not set
+# CONFIG_IKCONFIG is not set
+# CONFIG_IKCONFIG_PROC is not set
+# CONFIG_IKHEADERS is not set
+# CONFIG_IMA is not set
+# CONFIG_IMAGE_CMDLINE_HACK is not set
+# CONFIG_IMGPDC_WDT is not set
+# CONFIG_IMG_MDC_DMA is not set
+# CONFIG_IMX7D_ADC is not set
+# CONFIG_IMX_IPUV3_CORE is not set
+# CONFIG_IMX_THERMAL is not set
+# CONFIG_INA2XX_ADC is not set
+# CONFIG_INDIRECT_PIO is not set
+CONFIG_INET=y
+# CONFIG_INET6_AH is not set
+# CONFIG_INET6_ESP is not set
+# CONFIG_INET6_ESPINTCP is not set
+# CONFIG_INET6_IPCOMP is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_INET6_XFRM_MODE_BEET is not set
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET6_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_ESPINTCP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_TCP_DIAG is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_UDP_DIAG is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INFINIBAND is not set
+# CONFIG_INFTL is not set
+# CONFIG_INGENIC_ADC is not set
+# CONFIG_INGENIC_CGU_JZ4725B is not set
+# CONFIG_INGENIC_CGU_JZ4740 is not set
+# CONFIG_INGENIC_CGU_JZ4770 is not set
+# CONFIG_INGENIC_CGU_JZ4780 is not set
+# CONFIG_INGENIC_CGU_X1000 is not set
+# CONFIG_INGENIC_CGU_X1830 is not set
+# CONFIG_INGENIC_OST is not set
+# CONFIG_INGENIC_SYSOST is not set
+# CONFIG_INGENIC_TCU_CLK is not set
+# CONFIG_INGENIC_TCU_IRQ is not set
+# CONFIG_INGENIC_TIMER is not set
+CONFIG_INIT_ENV_ARG_LIMIT=32
+# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set
+# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set
+CONFIG_INIT_STACK_NONE=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_INPUT is not set
+# CONFIG_INPUT_AD714X is not set
+# CONFIG_INPUT_ADXL34X is not set
+# CONFIG_INPUT_APANEL is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_ATLAS_BTNS is not set
+# CONFIG_INPUT_ATMEL_CAPTOUCH is not set
+# CONFIG_INPUT_AXP20X_PEK is not set
+# CONFIG_INPUT_BMA150 is not set
+# CONFIG_INPUT_CM109 is not set
+# CONFIG_INPUT_CMA3000 is not set
+# CONFIG_INPUT_DRV260X_HAPTICS is not set
+# CONFIG_INPUT_DRV2665_HAPTICS is not set
+# CONFIG_INPUT_DRV2667_HAPTICS is not set
+# CONFIG_INPUT_E3X0_BUTTON is not set
+# CONFIG_INPUT_EVBUG is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_GP2A is not set
+# CONFIG_INPUT_GPIO_BEEPER is not set
+# CONFIG_INPUT_GPIO_DECODER is not set
+# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
+# CONFIG_INPUT_GPIO_TILT_POLLED is not set
+# CONFIG_INPUT_GPIO_VIBRA is not set
+# CONFIG_INPUT_IDEAPAD_SLIDEBAR is not set
+# CONFIG_INPUT_IMS_PCU is not set
+# CONFIG_INPUT_IQS269A is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_KXTJ9 is not set
+# CONFIG_INPUT_LEDS is not set
+# CONFIG_INPUT_MATRIXKMAP is not set
+# CONFIG_INPUT_MAX8997_HAPTIC is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_MMA8450 is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_MPU3050 is not set
+# CONFIG_INPUT_MSM_VIBRATOR is not set
+# CONFIG_INPUT_PALMAS_PWRBUTTON is not set
+# CONFIG_INPUT_PCF8574 is not set
+# CONFIG_INPUT_PCSPKR is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_PWM_BEEPER is not set
+# CONFIG_INPUT_PWM_VIBRA is not set
+# CONFIG_INPUT_REGULATOR_HAPTIC is not set
+# CONFIG_INPUT_SOC_BUTTON_ARRAY is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_TPS65218_PWRBUTTON is not set
+# CONFIG_INPUT_TWL4030_PWRBUTTON is not set
+# CONFIG_INPUT_TWL4030_VIBRA is not set
+# CONFIG_INPUT_TWL6040_VIBRA is not set
+# CONFIG_INPUT_UINPUT is not set
+# CONFIG_INPUT_WISTRON_BTNS is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INT340X_THERMAL is not set
+# CONFIG_INTEGRITY is not set
+# CONFIG_INTEGRITY_AUDIT is not set
+# CONFIG_INTEGRITY_SIGNATURE is not set
+# CONFIG_INTEL_ATOMISP2_LED is not set
+# CONFIG_INTEL_ATOMISP2_PM is not set
+# CONFIG_INTEL_CHT_INT33FE is not set
+# CONFIG_INTEL_HID_EVENT is not set
+# CONFIG_INTEL_IDLE is not set
+# CONFIG_INTEL_IDMA64 is not set
+# CONFIG_INTEL_INT0002_VGPIO is not set
+# CONFIG_INTEL_IOATDMA is not set
+# CONFIG_INTEL_ISH_HID is not set
+# CONFIG_INTEL_MEI is not set
+# CONFIG_INTEL_MEI_ME is not set
+# CONFIG_INTEL_MEI_TXE is not set
+# CONFIG_INTEL_MIC_CARD is not set
+# CONFIG_INTEL_MIC_HOST is not set
+# CONFIG_INTEL_MID_PTI is not set
+# CONFIG_INTEL_OAKTRAIL is not set
+# CONFIG_INTEL_PMC_CORE is not set
+# CONFIG_INTEL_PUNIT_IPC is not set
+# CONFIG_INTEL_RST is not set
+# CONFIG_INTEL_SMARTCONNECT is not set
+# CONFIG_INTEL_SOC_PMIC is not set
+# CONFIG_INTEL_SOC_PMIC_CHTDC_TI is not set
+# CONFIG_INTEL_SOC_PMIC_CHTWC is not set
+# CONFIG_INTEL_TH is not set
+# CONFIG_INTEL_VBTN is not set
+# CONFIG_INTEL_XWAY_PHY is not set
+# CONFIG_INTERCONNECT is not set
+# CONFIG_INTERVAL_TREE_TEST is not set
+# CONFIG_INV_ICM42600_I2C is not set
+# CONFIG_INV_ICM42600_SPI is not set
+# CONFIG_INV_MPU6050_I2C is not set
+# CONFIG_INV_MPU6050_IIO is not set
+# CONFIG_INV_MPU6050_SPI is not set
+# CONFIG_IOMMU_SUPPORT is not set
+# CONFIG_IONIC is not set
+# CONFIG_IOSCHED_BFQ is not set
+CONFIG_IO_STRICT_DEVMEM=y
+# CONFIG_IO_URING is not set
+CONFIG_IO_WQ=y
+# CONFIG_IP17XX_PHY is not set
+# CONFIG_IP6_NF_FILTER is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP6_NF_MANGLE is not set
+# CONFIG_IP6_NF_MATCH_AH is not set
+# CONFIG_IP6_NF_MATCH_EUI64 is not set
+# CONFIG_IP6_NF_MATCH_FRAG is not set
+# CONFIG_IP6_NF_MATCH_HL is not set
+# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set
+# CONFIG_IP6_NF_MATCH_MH is not set
+# CONFIG_IP6_NF_MATCH_OPTS is not set
+# CONFIG_IP6_NF_MATCH_RPFILTER is not set
+# CONFIG_IP6_NF_MATCH_RT is not set
+# CONFIG_IP6_NF_MATCH_SRH is not set
+# CONFIG_IP6_NF_NAT is not set
+# CONFIG_IP6_NF_RAW is not set
+# CONFIG_IP6_NF_SECURITY is not set
+# CONFIG_IP6_NF_TARGET_HL is not set
+# CONFIG_IP6_NF_TARGET_MASQUERADE is not set
+# CONFIG_IP6_NF_TARGET_REJECT is not set
+# CONFIG_IP6_NF_TARGET_SYNPROXY is not set
+# CONFIG_IPACK_BUS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_IPMB_DEVICE_INTERFACE is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_IPV6 is not set
+# CONFIG_IPV6_FOU is not set
+# CONFIG_IPV6_FOU_TUNNEL is not set
+# CONFIG_IPV6_ILA is not set
+# CONFIG_IPV6_MIP6 is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_IPV6_MROUTE_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+CONFIG_IPV6_NDISC_NODETYPE=y
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_ROUTE_INFO is not set
+# CONFIG_IPV6_RPL_LWTUNNEL is not set
+# CONFIG_IPV6_SEG6_HMAC is not set
+# CONFIG_IPV6_SEG6_LWTUNNEL is not set
+# CONFIG_IPV6_SIT is not set
+# CONFIG_IPV6_SIT_6RD is not set
+# CONFIG_IPV6_TUNNEL is not set
+# CONFIG_IPV6_VTI is not set
+# CONFIG_IPVLAN is not set
+# CONFIG_IPVTAP is not set
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2100_DEBUG is not set
+CONFIG_IPW2100_MONITOR=y
+# CONFIG_IPW2200 is not set
+# CONFIG_IPW2200_DEBUG is not set
+CONFIG_IPW2200_MONITOR=y
+# CONFIG_IPW2200_PROMISCUOUS is not set
+# CONFIG_IPW2200_QOS is not set
+# CONFIG_IPW2200_RADIOTAP is not set
+# CONFIG_IPWIRELESS is not set
+# CONFIG_IPX is not set
+CONFIG_IP_ADVANCED_ROUTER=y
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_FIB_TRIE_STATS is not set
+# CONFIG_IP_MROUTE is not set
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_MULTIPLE_TABLES=y
+# CONFIG_IP_NF_ARPFILTER is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+# CONFIG_IP_NF_ARP_MANGLE is not set
+# CONFIG_IP_NF_FILTER is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_MANGLE is not set
+# CONFIG_IP_NF_MATCH_AH is not set
+# CONFIG_IP_NF_MATCH_ECN is not set
+# CONFIG_IP_NF_MATCH_RPFILTER is not set
+# CONFIG_IP_NF_MATCH_TTL is not set
+# CONFIG_IP_NF_RAW is not set
+# CONFIG_IP_NF_SECURITY is not set
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+# CONFIG_IP_NF_TARGET_ECN is not set
+# CONFIG_IP_NF_TARGET_MASQUERADE is not set
+# CONFIG_IP_NF_TARGET_NETMAP is not set
+# CONFIG_IP_NF_TARGET_REDIRECT is not set
+# CONFIG_IP_NF_TARGET_REJECT is not set
+# CONFIG_IP_NF_TARGET_SYNPROXY is not set
+# CONFIG_IP_NF_TARGET_TTL is not set
+# CONFIG_IP_PIMSM_V1 is not set
+# CONFIG_IP_PIMSM_V2 is not set
+# CONFIG_IP_PNP is not set
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+# CONFIG_IP_SCTP is not set
+# CONFIG_IP_SET is not set
+# CONFIG_IP_SET_HASH_IPMAC is not set
+# CONFIG_IP_VS is not set
+# CONFIG_IP_VS_MH is not set
+CONFIG_IP_VS_MH_TAB_INDEX=10
+# CONFIG_IRDA is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_IRQ_ALL_CPUS is not set
+# CONFIG_IRQ_DOMAIN_DEBUG is not set
+# CONFIG_IRQ_POLL is not set
+# CONFIG_IRQ_TIME_ACCOUNTING is not set
+# CONFIG_IR_GPIO_CIR is not set
+# CONFIG_IR_HIX5HD2 is not set
+# CONFIG_IR_IGORPLUGUSB is not set
+# CONFIG_IR_IGUANA is not set
+# CONFIG_IR_IMG is not set
+# CONFIG_IR_IMON is not set
+# CONFIG_IR_IMON_RAW is not set
+# CONFIG_IR_JVC_DECODER is not set
+# CONFIG_IR_LIRC_CODEC is not set
+# CONFIG_IR_MCEUSB is not set
+# CONFIG_IR_NEC_DECODER is not set
+# CONFIG_IR_RC5_DECODER is not set
+# CONFIG_IR_RC6_DECODER is not set
+# CONFIG_IR_REDRAT3 is not set
+# CONFIG_IR_SONY_DECODER is not set
+# CONFIG_IR_STREAMZAP is not set
+# CONFIG_IR_TTUSBIR is not set
+# CONFIG_ISA_BUS is not set
+# CONFIG_ISA_BUS_API is not set
+# CONFIG_ISCSI_BOOT_SYSFS is not set
+# CONFIG_ISCSI_TCP is not set
+CONFIG_ISDN=y
+# CONFIG_ISDN_AUDIO is not set
+# CONFIG_ISDN_CAPI is not set
+# CONFIG_ISDN_CAPI_CAPIDRV is not set
+# CONFIG_ISDN_DIVERSION is not set
+# CONFIG_ISDN_DRV_ACT2000 is not set
+# CONFIG_ISDN_DRV_GIGASET is not set
+# CONFIG_ISDN_DRV_HISAX is not set
+# CONFIG_ISDN_DRV_ICN is not set
+# CONFIG_ISDN_DRV_LOOP is not set
+# CONFIG_ISDN_DRV_PCBIT is not set
+# CONFIG_ISDN_DRV_SC is not set
+# CONFIG_ISDN_I4L is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_ISL29020 is not set
+# CONFIG_ISL29125 is not set
+# CONFIG_ISL29501 is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_ISS4xx is not set
+# CONFIG_ITG3200 is not set
+# CONFIG_IWL3945 is not set
+# CONFIG_IWLWIFI is not set
+# CONFIG_IXGB is not set
+# CONFIG_IXGBE is not set
+# CONFIG_IXGBEVF is not set
+# CONFIG_JAILHOUSE_GUEST is not set
+# CONFIG_JBD2_DEBUG is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS_POSIX_ACL is not set
+# CONFIG_JFFS2_FS_SECURITY is not set
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+CONFIG_JFFS2_FS_XATTR=y
+CONFIG_JFFS2_LZMA=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_ZLIB is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_SECURITY is not set
+# CONFIG_JFS_STATISTICS is not set
+# CONFIG_JME is not set
+CONFIG_JOLIET=y
+# CONFIG_JSA1212 is not set
+# CONFIG_JUMP_LABEL is not set
+# CONFIG_JZ4740_WDT is not set
+# CONFIG_JZ4770_PHY is not set
+# CONFIG_KALLSYMS is not set
+# CONFIG_KALLSYMS_ABSOLUTE_PERCPU is not set
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_KALLSYMS_BASE_RELATIVE=y
+# CONFIG_KALLSYMS_UNCOMPRESSED is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_KASAN is not set
+CONFIG_KASAN_STACK=1
+# CONFIG_KCMP is not set
+# CONFIG_KCOV is not set
+# CONFIG_KCSAN is not set
+# CONFIG_KERNEL_BZIP2 is not set
+# CONFIG_KERNEL_CAT is not set
+# CONFIG_KERNEL_GZIP is not set
+# CONFIG_KERNEL_LZ4 is not set
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_KERNEL_MODE_NEON=y
+CONFIG_KERNEL_XZ=y
+# CONFIG_KERNEL_ZSTD is not set
+CONFIG_KERNFS=y
+# CONFIG_KEXEC is not set
+# CONFIG_KEXEC_FILE is not set
+# CONFIG_KEXEC_SIG is not set
+# CONFIG_KEYBOARD_ADC is not set
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+# CONFIG_KEYBOARD_APPLESPI is not set
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_BCM is not set
+# CONFIG_KEYBOARD_CAP11XX is not set
+# CONFIG_KEYBOARD_DLINK_DIR685 is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_KEYBOARD_GPIO_POLLED is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_LM8323 is not set
+# CONFIG_KEYBOARD_LM8333 is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OMAP4 is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_PXA27x is not set
+# CONFIG_KEYBOARD_QT1050 is not set
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_SAMSUNG is not set
+# CONFIG_KEYBOARD_SH_KEYSC is not set
+# CONFIG_KEYBOARD_SNVS_PWRKEY is not set
+# CONFIG_KEYBOARD_STMPE is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_TCA8418 is not set
+# CONFIG_KEYBOARD_TEGRA is not set
+# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set
+# CONFIG_KEYBOARD_TWL4030 is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYS is not set
+# CONFIG_KEYS_REQUEST_CACHE is not set
+# CONFIG_KEY_DH_OPERATIONS is not set
+# CONFIG_KGDB is not set
+# CONFIG_KMEMCHECK is not set
+# CONFIG_KMX61 is not set
+# CONFIG_KPC2000 is not set
+# CONFIG_KPROBES is not set
+# CONFIG_KPROBES_SANITY_TEST is not set
+# CONFIG_KPROBE_EVENTS_ON_NOTRACE is not set
+# CONFIG_KPROBE_EVENT_GEN_TEST is not set
+# CONFIG_KS7010 is not set
+# CONFIG_KS8842 is not set
+# CONFIG_KS8851 is not set
+# CONFIG_KS8851_MLL is not set
+# CONFIG_KSM is not set
+# CONFIG_KSZ884X_PCI is not set
+# CONFIG_KUNIT is not set
+CONFIG_KUSER_HELPERS=y
+# CONFIG_KVM_AMD is not set
+# CONFIG_KVM_AMD_SEV is not set
+# CONFIG_KVM_GUEST is not set
+# CONFIG_KVM_INTEL is not set
+# CONFIG_KVM_WERROR is not set
+# CONFIG_KXCJK1013 is not set
+# CONFIG_KXSD9 is not set
+# CONFIG_L2TP is not set
+# CONFIG_L2TP_ETH is not set
+# CONFIG_L2TP_IP is not set
+# CONFIG_L2TP_V3 is not set
+# CONFIG_LAN743X is not set
+# CONFIG_LANMEDIA is not set
+# CONFIG_LANTIQ is not set
+# CONFIG_LAPB is not set
+# CONFIG_LASAT is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_LATTICE_ECP3_CONFIG is not set
+CONFIG_LBDAF=y
+# CONFIG_LCD_AMS369FG06 is not set
+# CONFIG_LCD_CLASS_DEVICE is not set
+# CONFIG_LCD_HX8357 is not set
+# CONFIG_LCD_ILI922X is not set
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_L4F00242T03 is not set
+# CONFIG_LCD_LD9040 is not set
+# CONFIG_LCD_LMS283GF05 is not set
+# CONFIG_LCD_LMS501KF03 is not set
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_OTM3225A is not set
+# CONFIG_LCD_S6E63M0 is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+CONFIG_LDISC_AUTOLOAD=y
+# CONFIG_LDM_PARTITION is not set
+CONFIG_LD_DEAD_CODE_DATA_ELIMINATION=y
+# CONFIG_LEDS_AN30259A is not set
+# CONFIG_LEDS_APU is not set
+# CONFIG_LEDS_AW2013 is not set
+# CONFIG_LEDS_BCM6328 is not set
+# CONFIG_LEDS_BCM6358 is not set
+# CONFIG_LEDS_BD2802 is not set
+# CONFIG_LEDS_BLINKM is not set
+CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y
+CONFIG_LEDS_CLASS=y
+# CONFIG_LEDS_CLASS_FLASH is not set
+CONFIG_LEDS_CLASS_MULTICOLOR=y
+# CONFIG_LEDS_CR0014114 is not set
+# CONFIG_LEDS_DAC124S085 is not set
+# CONFIG_LEDS_EL15203000 is not set
+# CONFIG_LEDS_GPIO is not set
+# CONFIG_LEDS_INTEL_SS4200 is not set
+# CONFIG_LEDS_IS31FL319X is not set
+# CONFIG_LEDS_IS31FL32XX is not set
+# CONFIG_LEDS_LM3530 is not set
+# CONFIG_LEDS_LM3532 is not set
+# CONFIG_LEDS_LM355x is not set
+# CONFIG_LEDS_LM3642 is not set
+# CONFIG_LEDS_LM3692X is not set
+# CONFIG_LEDS_LP3944 is not set
+# CONFIG_LEDS_LP3952 is not set
+# CONFIG_LEDS_LP50XX is not set
+# CONFIG_LEDS_LP5521 is not set
+# CONFIG_LEDS_LP5523 is not set
+# CONFIG_LEDS_LP5562 is not set
+# CONFIG_LEDS_LP55XX_COMMON is not set
+# CONFIG_LEDS_LP8501 is not set
+# CONFIG_LEDS_LP8860 is not set
+# CONFIG_LEDS_LT3593 is not set
+# CONFIG_LEDS_MLXCPLD is not set
+# CONFIG_LEDS_MLXREG is not set
+# CONFIG_LEDS_NIC78BX is not set
+# CONFIG_LEDS_NS2 is not set
+# CONFIG_LEDS_OT200 is not set
+# CONFIG_LEDS_PCA9532 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_PCA963X is not set
+# CONFIG_LEDS_PWM is not set
+# CONFIG_LEDS_REGULATOR is not set
+# CONFIG_LEDS_SPI_BYTE is not set
+# CONFIG_LEDS_SYSCON is not set
+# CONFIG_LEDS_TCA6507 is not set
+# CONFIG_LEDS_TI_LMU_COMMON is not set
+# CONFIG_LEDS_TLC591XX is not set
+CONFIG_LEDS_TRIGGERS=y
+# CONFIG_LEDS_TRIGGER_ACTIVITY is not set
+# CONFIG_LEDS_TRIGGER_AUDIO is not set
+# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
+# CONFIG_LEDS_TRIGGER_CAMERA is not set
+# CONFIG_LEDS_TRIGGER_CPU is not set
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+# CONFIG_LEDS_TRIGGER_DISK is not set
+# CONFIG_LEDS_TRIGGER_GPIO is not set
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+# CONFIG_LEDS_TRIGGER_MTD is not set
+CONFIG_LEDS_TRIGGER_NETDEV=y
+# CONFIG_LEDS_TRIGGER_ONESHOT is not set
+# CONFIG_LEDS_TRIGGER_PANIC is not set
+# CONFIG_LEDS_TRIGGER_PATTERN is not set
+CONFIG_LEDS_TRIGGER_TIMER=y
+# CONFIG_LEDS_TRIGGER_TRANSIENT is not set
+# CONFIG_LEDS_TURRIS_OMNIA is not set
+# CONFIG_LEDS_USER is not set
+# CONFIG_LED_TRIGGER_PHY is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_LGUEST is not set
+# CONFIG_LIB80211 is not set
+# CONFIG_LIB80211_CRYPT_CCMP is not set
+# CONFIG_LIB80211_CRYPT_TKIP is not set
+# CONFIG_LIB80211_CRYPT_WEP is not set
+# CONFIG_LIB80211_DEBUG is not set
+# CONFIG_LIBCRC32C is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_LIBERTAS_THINFIRM is not set
+# CONFIG_LIBERTAS_USB is not set
+# CONFIG_LIBFC is not set
+# CONFIG_LIBFCOE is not set
+# CONFIG_LIBIPW_DEBUG is not set
+# CONFIG_LIBNVDIMM is not set
+# CONFIG_LIDAR_LITE_V2 is not set
+CONFIG_LINEAR_RANGES=y
+# CONFIG_LIQUIDIO is not set
+# CONFIG_LIQUIDIO_VF is not set
+# CONFIG_LIS3L02DQ is not set
+# CONFIG_LKDTM is not set
+CONFIG_LLC=y
+# CONFIG_LLC2 is not set
+# CONFIG_LMP91000 is not set
+# CONFIG_LNET is not set
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_LOCKD is not set
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_LOCKD_V4=y
+# CONFIG_LOCKUP_DETECTOR is not set
+# CONFIG_LOCK_EVENT_COUNTS is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_LOCK_TORTURE_TEST is not set
+# CONFIG_LOGFS is not set
+# CONFIG_LOGIG940_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIWHEELS_FF is not set
+# CONFIG_LOGO is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=12
+# CONFIG_LOONGSON_MC146818 is not set
+# CONFIG_LPC_ICH is not set
+# CONFIG_LPC_SCH is not set
+# CONFIG_LP_CONSOLE is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity"
+CONFIG_LSM_MMAP_MIN_ADDR=65536
+# CONFIG_LTC1660 is not set
+# CONFIG_LTC2471 is not set
+# CONFIG_LTC2485 is not set
+# CONFIG_LTC2496 is not set
+# CONFIG_LTC2497 is not set
+# CONFIG_LTC2632 is not set
+# CONFIG_LTC2983 is not set
+# CONFIG_LTE_GDM724X is not set
+# CONFIG_LTPC is not set
+# CONFIG_LTR501 is not set
+# CONFIG_LUSTRE_FS is not set
+# CONFIG_LV0104CS is not set
+# CONFIG_LWTUNNEL is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_LZ4HC_COMPRESS is not set
+# CONFIG_LZ4_COMPRESS is not set
+# CONFIG_LZ4_DECOMPRESS is not set
+CONFIG_LZMA_COMPRESS=y
+CONFIG_LZMA_DECOMPRESS=y
+# CONFIG_LZO_COMPRESS is not set
+# CONFIG_LZO_DECOMPRESS is not set
+# CONFIG_M62332 is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_MAC80211_MESSAGE_TRACING is not set
+CONFIG_MAC80211_STA_HASH_MAX_SIZE=0
+# CONFIG_MACB is not set
+# CONFIG_MACB_USE_HWSTAMP is not set
+# CONFIG_MACH_ASM9260 is not set
+# CONFIG_MACH_DECSTATION is not set
+# CONFIG_MACH_INGENIC is not set
+# CONFIG_MACH_INGENIC_SOC is not set
+# CONFIG_MACH_JAZZ is not set
+# CONFIG_MACH_JZ4740 is not set
+# CONFIG_MACH_LOONGSON2EF is not set
+# CONFIG_MACH_LOONGSON32 is not set
+# CONFIG_MACH_LOONGSON64 is not set
+# CONFIG_MACH_PIC32 is not set
+# CONFIG_MACH_PISTACHIO is not set
+# CONFIG_MACH_TX39XX is not set
+# CONFIG_MACH_TX49XX is not set
+# CONFIG_MACH_VR41XX is not set
+# CONFIG_MACH_XILFPGA is not set
+# CONFIG_MACINTOSH_DRIVERS is not set
+# CONFIG_MACSEC is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_MACVTAP is not set
+# CONFIG_MAC_EMUMOUSEBTN is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MAG3110 is not set
+# CONFIG_MAGIC_SYSRQ is not set
+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
+# CONFIG_MAGIC_SYSRQ_SERIAL is not set
+CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=""
+# CONFIG_MAILBOX is not set
+# CONFIG_MANAGER_SBS is not set
+# CONFIG_MANDATORY_FILE_LOCKING is not set
+# CONFIG_MANGLE_BOOTARGS is not set
+# CONFIG_MARVELL_10G_PHY is not set
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_MAX1027 is not set
+# CONFIG_MAX11100 is not set
+# CONFIG_MAX1118 is not set
+# CONFIG_MAX1241 is not set
+# CONFIG_MAX1363 is not set
+# CONFIG_MAX30100 is not set
+# CONFIG_MAX30102 is not set
+# CONFIG_MAX31856 is not set
+# CONFIG_MAX44000 is not set
+# CONFIG_MAX44009 is not set
+# CONFIG_MAX517 is not set
+# CONFIG_MAX5432 is not set
+# CONFIG_MAX5481 is not set
+# CONFIG_MAX5487 is not set
+# CONFIG_MAX5821 is not set
+# CONFIG_MAX63XX_WATCHDOG is not set
+# CONFIG_MAX9611 is not set
+# CONFIG_MAXIM_THERMOCOUPLE is not set
+CONFIG_MAY_USE_DEVLINK=y
+# CONFIG_MB1232 is not set
+# CONFIG_MC3230 is not set
+# CONFIG_MCB is not set
+# CONFIG_MCP320X is not set
+# CONFIG_MCP3422 is not set
+# CONFIG_MCP3911 is not set
+# CONFIG_MCP4018 is not set
+# CONFIG_MCP41010 is not set
+# CONFIG_MCP4131 is not set
+# CONFIG_MCP4531 is not set
+# CONFIG_MCP4725 is not set
+# CONFIG_MCP4922 is not set
+# CONFIG_MCPM is not set
+# CONFIG_MD is not set
+# CONFIG_MDIO_BCM_UNIMAC is not set
+# CONFIG_MDIO_BITBANG is not set
+# CONFIG_MDIO_BUS_MUX_GPIO is not set
+# CONFIG_MDIO_BUS_MUX_MMIOREG is not set
+# CONFIG_MDIO_BUS_MUX_MULTIPLEXER is not set
+# CONFIG_MDIO_DEVICE is not set
+# CONFIG_MDIO_DEVRES is not set
+# CONFIG_MDIO_HISI_FEMAC is not set
+# CONFIG_MDIO_IPQ4019 is not set
+# CONFIG_MDIO_IPQ8064 is not set
+# CONFIG_MDIO_MSCC_MIIM is not set
+# CONFIG_MDIO_MVUSB is not set
+# CONFIG_MDIO_OCTEON is not set
+# CONFIG_MDIO_THUNDER is not set
+# CONFIG_MDIO_XPCS is not set
+# CONFIG_MD_FAULTY is not set
+# CONFIG_MEDIATEK_GE_PHY is not set
+# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set
+# CONFIG_MEDIA_ATTACH is not set
+# CONFIG_MEDIA_CAMERA_SUPPORT is not set
+# CONFIG_MEDIA_CEC_SUPPORT is not set
+# CONFIG_MEDIA_CONTROLLER is not set
+# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set
+# CONFIG_MEDIA_PCI_SUPPORT is not set
+# CONFIG_MEDIA_PLATFORM_SUPPORT is not set
+# CONFIG_MEDIA_RADIO_SUPPORT is not set
+# CONFIG_MEDIA_RC_SUPPORT is not set
+# CONFIG_MEDIA_SDR_SUPPORT is not set
+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
+# CONFIG_MEDIA_SUPPORT is not set
+# CONFIG_MEDIA_SUPPORT_FILTER is not set
+# CONFIG_MEDIA_TEST_SUPPORT is not set
+# CONFIG_MEDIA_TUNER_E4000 is not set
+# CONFIG_MEDIA_TUNER_FC0011 is not set
+# CONFIG_MEDIA_TUNER_FC0012 is not set
+# CONFIG_MEDIA_TUNER_FC0013 is not set
+# CONFIG_MEDIA_TUNER_FC2580 is not set
+# CONFIG_MEDIA_TUNER_IT913X is not set
+# CONFIG_MEDIA_TUNER_M88RS6000T is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MSI001 is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MXL301RF is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_QM1D1B0004 is not set
+# CONFIG_MEDIA_TUNER_QM1D1C0042 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_R820T is not set
+# CONFIG_MEDIA_TUNER_SI2157 is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18250 is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_TUA9001 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_USB_SUPPORT is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_SAS is not set
+# CONFIG_MELLANOX_PLATFORM is not set
+CONFIG_MEMBARRIER=y
+# CONFIG_MEMORY is not set
+# CONFIG_MEMORY_FAILURE is not set
+# CONFIG_MEMORY_HOTPLUG is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_MEMTEST is not set
+# CONFIG_MEN_A21_WDT is not set
+# CONFIG_MESON_SM is not set
+CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
+# CONFIG_MFD_88PM800 is not set
+# CONFIG_MFD_88PM805 is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_AAT2870_CORE is not set
+# CONFIG_MFD_AC100 is not set
+# CONFIG_MFD_ACT8945A is not set
+# CONFIG_MFD_ARIZONA_I2C is not set
+# CONFIG_MFD_ARIZONA_SPI is not set
+# CONFIG_MFD_AS3711 is not set
+# CONFIG_MFD_AS3722 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_MFD_ATMEL_FLEXCOM is not set
+# CONFIG_MFD_ATMEL_HLCDC is not set
+# CONFIG_MFD_AXP20X is not set
+# CONFIG_MFD_AXP20X_I2C is not set
+# CONFIG_MFD_BCM590XX is not set
+# CONFIG_MFD_BD9571MWV is not set
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_CPCAP is not set
+# CONFIG_MFD_CROS_EC is not set
+# CONFIG_MFD_CS5535 is not set
+# CONFIG_MFD_DA9052_I2C is not set
+# CONFIG_MFD_DA9052_SPI is not set
+# CONFIG_MFD_DA9055 is not set
+# CONFIG_MFD_DA9062 is not set
+# CONFIG_MFD_DA9063 is not set
+# CONFIG_MFD_DA9150 is not set
+# CONFIG_MFD_DLN2 is not set
+# CONFIG_MFD_EXYNOS_LPASS is not set
+# CONFIG_MFD_GATEWORKS_GSC is not set
+# CONFIG_MFD_HI6421_PMIC is not set
+# CONFIG_MFD_INTEL_M10_BMC is not set
+# CONFIG_MFD_INTEL_QUARK_I2C_GPIO is not set
+# CONFIG_MFD_IQS62X is not set
+# CONFIG_MFD_JANZ_CMODIO is not set
+# CONFIG_MFD_KEMPLD is not set
+# CONFIG_MFD_LM3533 is not set
+# CONFIG_MFD_LOCHNAGAR is not set
+# CONFIG_MFD_LP3943 is not set
+# CONFIG_MFD_LP8788 is not set
+# CONFIG_MFD_MADERA is not set
+# CONFIG_MFD_MAX14577 is not set
+# CONFIG_MFD_MAX77620 is not set
+# CONFIG_MFD_MAX77650 is not set
+# CONFIG_MFD_MAX77686 is not set
+# CONFIG_MFD_MAX77693 is not set
+# CONFIG_MFD_MAX77843 is not set
+# CONFIG_MFD_MAX8907 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_MFD_MC13XXX is not set
+# CONFIG_MFD_MC13XXX_I2C is not set
+# CONFIG_MFD_MC13XXX_SPI is not set
+# CONFIG_MFD_MENF21BMC is not set
+# CONFIG_MFD_MP2629 is not set
+# CONFIG_MFD_MT6360 is not set
+# CONFIG_MFD_MT6397 is not set
+# CONFIG_MFD_OMAP_USB_HOST is not set
+# CONFIG_MFD_PALMAS is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_MFD_PM8921_CORE is not set
+# CONFIG_MFD_PM8XXX is not set
+# CONFIG_MFD_RC5T583 is not set
+# CONFIG_MFD_RDC321X is not set
+# CONFIG_MFD_RETU is not set
+# CONFIG_MFD_RK808 is not set
+# CONFIG_MFD_RN5T618 is not set
+# CONFIG_MFD_ROHM_BD70528 is not set
+# CONFIG_MFD_ROHM_BD71828 is not set
+# CONFIG_MFD_ROHM_BD718XX is not set
+# CONFIG_MFD_RT5033 is not set
+# CONFIG_MFD_RTSX_PCI is not set
+# CONFIG_MFD_RTSX_USB is not set
+# CONFIG_MFD_SEC_CORE is not set
+# CONFIG_MFD_SI476X_CORE is not set
+# CONFIG_MFD_SKY81452 is not set
+# CONFIG_MFD_SL28CPLD is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_SMSC is not set
+# CONFIG_MFD_STMFX is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_STPMIC1 is not set
+# CONFIG_MFD_SYSCON is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_MFD_TIMBERDALE is not set
+# CONFIG_MFD_TI_AM335X_TSCADC is not set
+# CONFIG_MFD_TI_LMU is not set
+# CONFIG_MFD_TI_LP873X is not set
+# CONFIG_MFD_TI_LP87565 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_TPS65086 is not set
+# CONFIG_MFD_TPS65090 is not set
+# CONFIG_MFD_TPS65217 is not set
+# CONFIG_MFD_TPS65218 is not set
+# CONFIG_MFD_TPS6586X is not set
+# CONFIG_MFD_TPS65910 is not set
+# CONFIG_MFD_TPS65912 is not set
+# CONFIG_MFD_TPS65912_I2C is not set
+# CONFIG_MFD_TPS65912_SPI is not set
+# CONFIG_MFD_TPS68470 is not set
+# CONFIG_MFD_TPS80031 is not set
+# CONFIG_MFD_TQMX86 is not set
+# CONFIG_MFD_VIPERBOARD is not set
+# CONFIG_MFD_VX855 is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_MFD_WM831X is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM831X_SPI is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_MG_DISK is not set
+# CONFIG_MHI_BUS is not set
+# CONFIG_MICREL_KS8995MA is not set
+# CONFIG_MICREL_PHY is not set
+# CONFIG_MICROCHIP_KSZ is not set
+# CONFIG_MICROCHIP_PHY is not set
+# CONFIG_MICROCHIP_PIT64B is not set
+# CONFIG_MICROCHIP_T1_PHY is not set
+# CONFIG_MICROSEMI_PHY is not set
+# CONFIG_MIGRATION is not set
+CONFIG_HARDEN_BRANCH_HISTORY=y
+CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY=y
+CONFIG_MII=y
+# CONFIG_MIKROTIK is not set
+# CONFIG_MIKROTIK_RB532 is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_MINIX_FS_NATIVE_ENDIAN is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_MIPS_ALCHEMY is not set
+# CONFIG_MIPS_CDMM is not set
+# CONFIG_MIPS_COBALT is not set
+# CONFIG_MIPS_FPU_EMULATOR is not set
+# CONFIG_MIPS_FP_SUPPORT is not set
+# CONFIG_MIPS_GENERIC is not set
+# CONFIG_MIPS_GENERIC_KERNEL is not set
+# CONFIG_MIPS_MALTA is not set
+# CONFIG_MIPS_O32_FP64_SUPPORT is not set
+# CONFIG_MIPS_PARAVIRT is not set
+# CONFIG_MIPS_PLATFORM_DEVICES is not set
+# CONFIG_MIPS_SEAD3 is not set
+# CONFIG_MISC_ALCOR_PCI is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_MISC_RTSX_PCI is not set
+# CONFIG_MISC_RTSX_USB is not set
+# CONFIG_MISDN is not set
+# CONFIG_MISDN_AVMFRITZ is not set
+# CONFIG_MISDN_HFCPCI is not set
+# CONFIG_MISDN_HFCUSB is not set
+# CONFIG_MISDN_INFINEON is not set
+# CONFIG_MISDN_NETJET is not set
+# CONFIG_MISDN_SPEEDFAX is not set
+# CONFIG_MISDN_W6692 is not set
+# CONFIG_MKISS is not set
+# CONFIG_MLX4_CORE is not set
+# CONFIG_MLX4_EN is not set
+# CONFIG_MLX5_CORE is not set
+# CONFIG_MLX90614 is not set
+# CONFIG_MLX90632 is not set
+# CONFIG_MLXFW is not set
+# CONFIG_MLXSW_CORE is not set
+# CONFIG_MLX_CPLD_PLATFORM is not set
+# CONFIG_MLX_PLATFORM is not set
+# CONFIG_MMA7455_I2C is not set
+# CONFIG_MMA7455_SPI is not set
+# CONFIG_MMA7660 is not set
+# CONFIG_MMA8452 is not set
+# CONFIG_MMA9551 is not set
+# CONFIG_MMA9553 is not set
+# CONFIG_MMC is not set
+# CONFIG_MMC35240 is not set
+# CONFIG_MMC_ARMMMCI is not set
+# CONFIG_MMC_AU1X is not set
+# CONFIG_MMC_BLOCK is not set
+CONFIG_MMC_BLOCK_BOUNCE=y
+CONFIG_MMC_BLOCK_MINORS=8
+# CONFIG_MMC_CAVIUM_THUNDERX is not set
+# CONFIG_MMC_CB710 is not set
+# CONFIG_MMC_CQHCI is not set
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_DW is not set
+# CONFIG_MMC_HSQ is not set
+# CONFIG_MMC_JZ4740 is not set
+# CONFIG_MMC_MTK is not set
+# CONFIG_MMC_MVSDIO is not set
+# CONFIG_MMC_S3C is not set
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_SDHCI_ACPI is not set
+# CONFIG_MMC_SDHCI_AM654 is not set
+# CONFIG_MMC_SDHCI_BCM_KONA is not set
+# CONFIG_MMC_SDHCI_CADENCE is not set
+# CONFIG_MMC_SDHCI_F_SDH30 is not set
+# CONFIG_MMC_SDHCI_IPROC is not set
+# CONFIG_MMC_SDHCI_MILBEAUT is not set
+# CONFIG_MMC_SDHCI_MSM is not set
+# CONFIG_MMC_SDHCI_OF_ARASAN is not set
+# CONFIG_MMC_SDHCI_OF_ASPEED is not set
+# CONFIG_MMC_SDHCI_OF_AT91 is not set
+# CONFIG_MMC_SDHCI_OF_DWCMSHC is not set
+# CONFIG_MMC_SDHCI_OF_ESDHC is not set
+# CONFIG_MMC_SDHCI_OF_HLWD is not set
+# CONFIG_MMC_SDHCI_OMAP is not set
+# CONFIG_MMC_SDHCI_PXAV2 is not set
+# CONFIG_MMC_SDHCI_PXAV3 is not set
+# CONFIG_MMC_SDHCI_S3C is not set
+# CONFIG_MMC_SDHCI_XENON is not set
+# CONFIG_MMC_SDRICOH_CS is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MMC_STM32_SDMMC is not set
+# CONFIG_MMC_TEST is not set
+# CONFIG_MMC_TIFM_SD is not set
+# CONFIG_MMC_TOSHIBA_PCI is not set
+# CONFIG_MMC_USDHI6ROL0 is not set
+# CONFIG_MMC_USHC is not set
+# CONFIG_MMC_VIA_SDMMC is not set
+# CONFIG_MMC_VUB300 is not set
+# CONFIG_MMIOTRACE is not set
+CONFIG_MMU=y
+CONFIG_MMU_GATHER_RCU_TABLE_FREE=y
+CONFIG_MMU_GATHER_TABLE_FREE=y
+CONFIG_MODULES=y
+# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set
+# CONFIG_MODULE_COMPRESS is not set
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODULE_SIG is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_MODULE_STRIPPED=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MOST is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_ELAN_I2C is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_MOUSE_INPORT is not set
+# CONFIG_MOUSE_LOGIBM is not set
+# CONFIG_MOUSE_PC110PAD is not set
+# CONFIG_MOUSE_PS2_FOCALTECH is not set
+# CONFIG_MOUSE_PS2_SENTELIC is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_MOUSE_SYNAPTICS_USB is not set
+# CONFIG_MOXTET is not set
+# CONFIG_MPL115 is not set
+# CONFIG_MPL115_I2C is not set
+# CONFIG_MPL115_SPI is not set
+# CONFIG_MPL3115 is not set
+# CONFIG_MPLS is not set
+# CONFIG_MPTCP is not set
+# CONFIG_MPU3050_I2C is not set
+# CONFIG_MQ_IOSCHED_DEADLINE is not set
+# CONFIG_MQ_IOSCHED_KYBER is not set
+# CONFIG_MS5611 is not set
+# CONFIG_MS5637 is not set
+# CONFIG_MSCC_OCELOT_SWITCH is not set
+# CONFIG_MSDOS_FS is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_MSI_BITMAP_SELFTEST is not set
+# CONFIG_MSI_LAPTOP is not set
+# CONFIG_MST_IRQ is not set
+CONFIG_MTD=y
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_MTD_BLOCK2MTD is not set
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_CMDLINE_PARTS is not set
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_DOCG3 is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_GPIO_ADDR is not set
+# CONFIG_MTD_HYPERBUS is not set
+# CONFIG_MTD_IMPA7 is not set
+# CONFIG_MTD_INTEL_VR_NOR is not set
+# CONFIG_MTD_JEDECPROBE is not set
+# CONFIG_MTD_LATCH_ADDR is not set
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_LPDDR2_NVM is not set
+# CONFIG_MTD_M25P80 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MCHP23K256 is not set
+# CONFIG_MTD_MT81xx_NOR is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_MYLOADER_PARTS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_NAND_AMS_DELTA is not set
+# CONFIG_MTD_NAND_AR934X is not set
+# CONFIG_MTD_NAND_AR934X_HW_ECC is not set
+# CONFIG_MTD_NAND_ARASAN is not set
+# CONFIG_MTD_NAND_ATMEL is not set
+# CONFIG_MTD_NAND_AU1550 is not set
+# CONFIG_MTD_NAND_BCH is not set
+# CONFIG_MTD_NAND_BF5XX is not set
+# CONFIG_MTD_NAND_BRCMNAND is not set
+# CONFIG_MTD_NAND_CADENCE is not set
+# CONFIG_MTD_NAND_CAFE is not set
+# CONFIG_MTD_NAND_CM_X270 is not set
+# CONFIG_MTD_NAND_CS553X is not set
+# CONFIG_MTD_NAND_DAVINCI is not set
+# CONFIG_MTD_NAND_DENALI is not set
+# CONFIG_MTD_NAND_DENALI_DT is not set
+# CONFIG_MTD_NAND_DENALI_PCI is not set
+CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR=0xff108018
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_DOCG4 is not set
+# CONFIG_MTD_NAND_ECC is not set
+# CONFIG_MTD_NAND_ECC_BCH is not set
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_ECC_SW_BCH is not set
+# CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC is not set
+# CONFIG_MTD_NAND_FSL_ELBC is not set
+# CONFIG_MTD_NAND_FSL_IFC is not set
+# CONFIG_MTD_NAND_FSL_UPM is not set
+# CONFIG_MTD_NAND_FSMC is not set
+# CONFIG_MTD_NAND_GPIO is not set
+# CONFIG_MTD_NAND_GPMI_NAND is not set
+# CONFIG_MTD_NAND_HISI504 is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_JZ4740 is not set
+# CONFIG_MTD_NAND_MPC5121_NFC is not set
+# CONFIG_MTD_NAND_MTK is not set
+# CONFIG_MTD_NAND_MTK_BMT is not set
+# CONFIG_MTD_NAND_MXC is not set
+# CONFIG_MTD_NAND_MXIC is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_NDFC is not set
+# CONFIG_MTD_NAND_NUC900 is not set
+# CONFIG_MTD_NAND_OMAP2 is not set
+# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set
+# CONFIG_MTD_NAND_ORION is not set
+# CONFIG_MTD_NAND_PASEMI is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_NAND_PXA3xx is not set
+# CONFIG_MTD_NAND_RB4XX is not set
+# CONFIG_MTD_NAND_RB750 is not set
+# CONFIG_MTD_NAND_RB91X is not set
+# CONFIG_MTD_NAND_RICOH is not set
+# CONFIG_MTD_NAND_S3C2410 is not set
+# CONFIG_MTD_NAND_SHARPSL is not set
+# CONFIG_MTD_NAND_SH_FLCTL is not set
+# CONFIG_MTD_NAND_SOCRATES is not set
+# CONFIG_MTD_NAND_TMIO is not set
+# CONFIG_MTD_NAND_TXX9NDFMC is not set
+CONFIG_MTD_OF_PARTS=y
+# CONFIG_MTD_ONENAND is not set
+# CONFIG_MTD_OOPS is not set
+# CONFIG_MTD_OTP is not set
+# CONFIG_MTD_PARSER_TRX is not set
+# CONFIG_MTD_PARTITIONED_MASTER is not set
+# CONFIG_MTD_PCI is not set
+# CONFIG_MTD_PCMCIA is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_PHYSMAP_GEMINI is not set
+# CONFIG_MTD_PHYSMAP_GPIO_ADDR is not set
+# CONFIG_MTD_PHYSMAP_IXP4XX is not set
+CONFIG_MTD_PHYSMAP_OF=y
+# CONFIG_MTD_PHYSMAP_OF_GEMINI is not set
+# CONFIG_MTD_PHYSMAP_OF_VERSATILE is not set
+# CONFIG_MTD_PHYSMAP_VERSATILE is not set
+# CONFIG_MTD_PLATRAM is not set
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_RAW_NAND is not set
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_ROM is not set
+CONFIG_MTD_ROOTFS_ROOT_DEV=y
+# CONFIG_MTD_ROUTERBOOT_PARTS is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_SM_COMMON is not set
+# CONFIG_MTD_SPINAND_MT29F is not set
+# CONFIG_MTD_SPI_NAND is not set
+# CONFIG_MTD_SPI_NOR is not set
+# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
+CONFIG_MTD_SPI_NOR_USE_4K_SECTORS_LIMIT=4096
+# CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE is not set
+CONFIG_MTD_SPLIT=y
+# CONFIG_MTD_SPLIT_BCM63XX_FW is not set
+# CONFIG_MTD_SPLIT_BCM_WFI_FW is not set
+# CONFIG_MTD_SPLIT_BRNIMAGE_FW is not set
+# CONFIG_MTD_SPLIT_ELF_FW is not set
+# CONFIG_MTD_SPLIT_EVA_FW is not set
+# CONFIG_MTD_SPLIT_FIRMWARE is not set
+CONFIG_MTD_SPLIT_FIRMWARE_NAME="firmware"
+# CONFIG_MTD_SPLIT_FIT_FW is not set
+# CONFIG_MTD_SPLIT_JIMAGE_FW is not set
+# CONFIG_MTD_SPLIT_LZMA_FW is not set
+# CONFIG_MTD_SPLIT_MINOR_FW is not set
+# CONFIG_MTD_SPLIT_SEAMA_FW is not set
+CONFIG_MTD_SPLIT_SQUASHFS_ROOT=y
+CONFIG_MTD_SPLIT_SUPPORT=y
+# CONFIG_MTD_SPLIT_TPLINK_FW is not set
+# CONFIG_MTD_SPLIT_TRX_FW is not set
+# CONFIG_MTD_SPLIT_UIMAGE_FW is not set
+# CONFIG_MTD_SPLIT_WRGG_FW is not set
+# CONFIG_MTD_SST25L is not set
+# CONFIG_MTD_SWAP is not set
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_UBI is not set
+# CONFIG_MTD_UBI_FASTMAP is not set
+# CONFIG_MTD_UBI_GLUEBI is not set
+# CONFIG_MTD_UIMAGE_SPLIT is not set
+# CONFIG_MTD_VIRT_CONCAT is not set
+# CONFIG_MTK_MMC is not set
+# CONFIG_MTK_MMSYS is not set
+# CONFIG_MULTIPLEXER is not set
+CONFIG_MULTIUSER=y
+# CONFIG_MUTEX_SPIN_ON_OWNER is not set
+# CONFIG_MUX_ADG792A is not set
+# CONFIG_MUX_ADGS1408 is not set
+# CONFIG_MUX_GPIO is not set
+# CONFIG_MUX_MMIO is not set
+# CONFIG_MV643XX_ETH is not set
+# CONFIG_MVMDIO is not set
+# CONFIG_MVNETA_BM is not set
+# CONFIG_MVSW61XX_PHY is not set
+# CONFIG_MV_XOR_V2 is not set
+# CONFIG_MWAVE is not set
+# CONFIG_MWL8K is not set
+# CONFIG_MXC4005 is not set
+# CONFIG_MXC6255 is not set
+# CONFIG_MYRI10GE is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_NATSEMI is not set
+# CONFIG_NAU7802 is not set
+# CONFIG_NBPFAXI_DMA is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NE2000 is not set
+# CONFIG_NE2K_PCI is not set
+# CONFIG_NEC_MARKEINS is not set
+CONFIG_NET=y
+# CONFIG_NETCONSOLE is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVSIM is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_NETFILTER_ADVANCED is not set
+# CONFIG_NETFILTER_DEBUG is not set
+# CONFIG_NETFILTER_INGRESS is not set
+# CONFIG_NETFILTER_NETLINK is not set
+# CONFIG_NETFILTER_NETLINK_ACCT is not set
+# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set
+# CONFIG_NETFILTER_NETLINK_LOG is not set
+# CONFIG_NETFILTER_NETLINK_OSF is not set
+# CONFIG_NETFILTER_NETLINK_QUEUE is not set
+# CONFIG_NETFILTER_XTABLES is not set
+# CONFIG_NETFILTER_XT_CONNMARK is not set
+# CONFIG_NETFILTER_XT_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_BPF is not set
+# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set
+# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNLIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNTRACK is not set
+# CONFIG_NETFILTER_XT_MATCH_CPU is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ECN is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_HELPER is not set
+# CONFIG_NETFILTER_XT_MATCH_HL is not set
+# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set
+# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set
+# CONFIG_NETFILTER_XT_MATCH_L2TP is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set
+# CONFIG_NETFILTER_XT_MATCH_OSF is not set
+# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
+# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_RECENT is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set
+# CONFIG_NETFILTER_XT_MATCH_STATE is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set
+# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set
+# CONFIG_NETFILTER_XT_TARGET_CT is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_HL is not set
+# CONFIG_NETFILTER_XT_TARGET_HMARK is not set
+# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set
+# CONFIG_NETFILTER_XT_TARGET_LED is not set
+# CONFIG_NETFILTER_XT_TARGET_LOG is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NETMAP is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set
+# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
+# CONFIG_NETFILTER_XT_TARGET_REDIRECT is not set
+# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set
+# CONFIG_NETFILTER_XT_TARGET_TEE is not set
+# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETLABEL is not set
+# CONFIG_NETLINK_DIAG is not set
+# CONFIG_NETLINK_MMAP is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NETROM is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETXEN_NIC is not set
+# CONFIG_NET_9P is not set
+# CONFIG_NET_ACT_BPF is not set
+# CONFIG_NET_ACT_CSUM is not set
+# CONFIG_NET_ACT_CT is not set
+# CONFIG_NET_ACT_GACT is not set
+# CONFIG_NET_ACT_GATE is not set
+# CONFIG_NET_ACT_IFE is not set
+# CONFIG_NET_ACT_IPT is not set
+# CONFIG_NET_ACT_MIRRED is not set
+# CONFIG_NET_ACT_MPLS is not set
+# CONFIG_NET_ACT_NAT is not set
+# CONFIG_NET_ACT_PEDIT is not set
+# CONFIG_NET_ACT_POLICE is not set
+# CONFIG_NET_ACT_SAMPLE is not set
+# CONFIG_NET_ACT_SIMP is not set
+# CONFIG_NET_ACT_SKBEDIT is not set
+# CONFIG_NET_ACT_SKBMOD is not set
+# CONFIG_NET_ACT_TUNNEL_KEY is not set
+# CONFIG_NET_ACT_VLAN is not set
+CONFIG_NET_CADENCE=y
+# CONFIG_NET_CALXEDA_XGMAC is not set
+CONFIG_NET_CLS=y
+# CONFIG_NET_CLS_ACT is not set
+# CONFIG_NET_CLS_BASIC is not set
+# CONFIG_NET_CLS_BPF is not set
+# CONFIG_NET_CLS_FLOW is not set
+# CONFIG_NET_CLS_FLOWER is not set
+# CONFIG_NET_CLS_FW is not set
+CONFIG_NET_CLS_IND=y
+# CONFIG_NET_CLS_MATCHALL is not set
+# CONFIG_NET_CLS_ROUTE4 is not set
+# CONFIG_NET_CLS_RSVP is not set
+# CONFIG_NET_CLS_RSVP6 is not set
+# CONFIG_NET_CLS_TCINDEX is not set
+# CONFIG_NET_CLS_U32 is not set
+CONFIG_NET_CORE=y
+# CONFIG_NET_DEVLINK is not set
+# CONFIG_NET_DROP_MONITOR is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_NET_DSA_AR9331 is not set
+# CONFIG_NET_DSA_BCM_SF2 is not set
+# CONFIG_NET_DSA_LANTIQ_GSWIP is not set
+# CONFIG_NET_DSA_LEGACY is not set
+# CONFIG_NET_DSA_LOOP is not set
+# CONFIG_NET_DSA_MICROCHIP_KSZ8795 is not set
+# CONFIG_NET_DSA_MICROCHIP_KSZ9477 is not set
+# CONFIG_NET_DSA_MSCC_FELIX is not set
+# CONFIG_NET_DSA_MSCC_SEVILLE is not set
+# CONFIG_NET_DSA_MT7530 is not set
+# CONFIG_NET_DSA_MV88E6060 is not set
+# CONFIG_NET_DSA_MV88E6123_61_65 is not set
+# CONFIG_NET_DSA_MV88E6131 is not set
+# CONFIG_NET_DSA_MV88E6171 is not set
+# CONFIG_NET_DSA_MV88E6352 is not set
+# CONFIG_NET_DSA_MV88E6XXX is not set
+# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set
+# CONFIG_NET_DSA_MV88E6XXX_PTP is not set
+# CONFIG_NET_DSA_QCA8K is not set
+# CONFIG_NET_DSA_REALTEK_SMI is not set
+# CONFIG_NET_DSA_SJA1105 is not set
+# CONFIG_NET_DSA_SMSC_LAN9303_I2C is not set
+# CONFIG_NET_DSA_SMSC_LAN9303_MDIO is not set
+# CONFIG_NET_DSA_TAG_8021Q is not set
+# CONFIG_NET_DSA_TAG_AR9331 is not set
+# CONFIG_NET_DSA_TAG_BRCM is not set
+# CONFIG_NET_DSA_TAG_BRCM_LEGACY is not set
+# CONFIG_NET_DSA_TAG_BRCM_PREPEND is not set
+# CONFIG_NET_DSA_TAG_DSA is not set
+# CONFIG_NET_DSA_TAG_EDSA is not set
+# CONFIG_NET_DSA_TAG_GSWIP is not set
+# CONFIG_NET_DSA_TAG_KSZ is not set
+# CONFIG_NET_DSA_TAG_LAN9303 is not set
+# CONFIG_NET_DSA_TAG_MTK is not set
+# CONFIG_NET_DSA_TAG_OCELOT is not set
+# CONFIG_NET_DSA_TAG_QCA is not set
+# CONFIG_NET_DSA_TAG_RTL4_A is not set
+# CONFIG_NET_DSA_TAG_SJA1105 is not set
+# CONFIG_NET_DSA_TAG_TRAILER is not set
+# CONFIG_NET_DSA_VITESSE_VSC73XX is not set
+# CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM is not set
+# CONFIG_NET_DSA_VITESSE_VSC73XX_SPI is not set
+# CONFIG_NET_EMATCH is not set
+# CONFIG_NET_EMATCH_CANID is not set
+# CONFIG_NET_EMATCH_CMP is not set
+# CONFIG_NET_EMATCH_IPT is not set
+# CONFIG_NET_EMATCH_META is not set
+# CONFIG_NET_EMATCH_NBYTE is not set
+CONFIG_NET_EMATCH_STACK=32
+# CONFIG_NET_EMATCH_TEXT is not set
+# CONFIG_NET_EMATCH_U32 is not set
+# CONFIG_NET_FAILOVER is not set
+# CONFIG_NET_FC is not set
+# CONFIG_NET_FOU is not set
+# CONFIG_NET_FOU_IP_TUNNELS is not set
+# CONFIG_NET_IFE is not set
+# CONFIG_NET_IPGRE is not set
+CONFIG_NET_IPGRE_BROADCAST=y
+# CONFIG_NET_IPGRE_DEMUX is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPVTI is not set
+# CONFIG_NET_IP_TUNNEL is not set
+# CONFIG_NET_KEY is not set
+# CONFIG_NET_KEY_MIGRATE is not set
+# CONFIG_NET_L3_MASTER_DEV is not set
+# CONFIG_NET_MEDIATEK_STAR_EMAC is not set
+# CONFIG_NET_MPLS_GSO is not set
+# CONFIG_NET_NCSI is not set
+# CONFIG_NET_NSH is not set
+# CONFIG_NET_PACKET_ENGINE is not set
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_NET_PTP_CLASSIFY is not set
+CONFIG_NET_RX_BUSY_POLL=y
+# CONFIG_NET_SB1000 is not set
+CONFIG_NET_SCHED=y
+# CONFIG_NET_SCH_ATM is not set
+# CONFIG_NET_SCH_CAKE is not set
+# CONFIG_NET_SCH_CBQ is not set
+# CONFIG_NET_SCH_CBS is not set
+# CONFIG_NET_SCH_CHOKE is not set
+# CONFIG_NET_SCH_CODEL is not set
+CONFIG_NET_SCH_DEFAULT=y
+# CONFIG_NET_SCH_DRR is not set
+# CONFIG_NET_SCH_DSMARK is not set
+# CONFIG_NET_SCH_ETF is not set
+# CONFIG_NET_SCH_ETS is not set
+CONFIG_NET_SCH_FIFO=y
+# CONFIG_NET_SCH_FQ is not set
+CONFIG_NET_SCH_FQ_CODEL=y
+# CONFIG_NET_SCH_FQ_PIE is not set
+# CONFIG_NET_SCH_GRED is not set
+# CONFIG_NET_SCH_HFSC is not set
+# CONFIG_NET_SCH_HHF is not set
+# CONFIG_NET_SCH_HTB is not set
+# CONFIG_NET_SCH_INGRESS is not set
+# CONFIG_NET_SCH_MQPRIO is not set
+# CONFIG_NET_SCH_MULTIQ is not set
+# CONFIG_NET_SCH_NETEM is not set
+# CONFIG_NET_SCH_PIE is not set
+# CONFIG_NET_SCH_PLUG is not set
+# CONFIG_NET_SCH_PRIO is not set
+# CONFIG_NET_SCH_QFQ is not set
+# CONFIG_NET_SCH_RED is not set
+# CONFIG_NET_SCH_SFB is not set
+# CONFIG_NET_SCH_SFQ is not set
+# CONFIG_NET_SCH_SKBPRIO is not set
+# CONFIG_NET_SCH_TAPRIO is not set
+# CONFIG_NET_SCH_TBF is not set
+# CONFIG_NET_SCH_TEQL is not set
+# CONFIG_NET_SCTPPROBE is not set
+# CONFIG_NET_SWITCHDEV is not set
+# CONFIG_NET_TCPPROBE is not set
+# CONFIG_NET_TC_SKB_EXT is not set
+# CONFIG_NET_TEAM is not set
+# CONFIG_NET_TULIP is not set
+# CONFIG_NET_UDP_TUNNEL is not set
+CONFIG_NET_VENDOR_3COM=y
+CONFIG_NET_VENDOR_8390=y
+CONFIG_NET_VENDOR_ADAPTEC=y
+CONFIG_NET_VENDOR_AGERE=y
+CONFIG_NET_VENDOR_ALACRITECH=y
+CONFIG_NET_VENDOR_ALTEON=y
+CONFIG_NET_VENDOR_AMAZON=y
+CONFIG_NET_VENDOR_AMD=y
+CONFIG_NET_VENDOR_AQUANTIA=y
+CONFIG_NET_VENDOR_ARC=y
+CONFIG_NET_VENDOR_ATHEROS=y
+CONFIG_NET_VENDOR_AURORA=y
+CONFIG_NET_VENDOR_BROADCOM=y
+CONFIG_NET_VENDOR_BROCADE=y
+CONFIG_NET_VENDOR_CADENCE=y
+CONFIG_NET_VENDOR_CAVIUM=y
+CONFIG_NET_VENDOR_CHELSIO=y
+CONFIG_NET_VENDOR_CIRRUS=y
+CONFIG_NET_VENDOR_CISCO=y
+CONFIG_NET_VENDOR_CORTINA=y
+CONFIG_NET_VENDOR_DEC=y
+CONFIG_NET_VENDOR_DLINK=y
+CONFIG_NET_VENDOR_EMULEX=y
+CONFIG_NET_VENDOR_EXAR=y
+CONFIG_NET_VENDOR_EZCHIP=y
+CONFIG_NET_VENDOR_FARADAY=y
+CONFIG_NET_VENDOR_FREESCALE=y
+CONFIG_NET_VENDOR_FUJITSU=y
+CONFIG_NET_VENDOR_GOOGLE=y
+CONFIG_NET_VENDOR_HISILICON=y
+CONFIG_NET_VENDOR_HP=y
+CONFIG_NET_VENDOR_HUAWEI=y
+CONFIG_NET_VENDOR_I825XX=y
+CONFIG_NET_VENDOR_IBM=y
+CONFIG_NET_VENDOR_INTEL=y
+CONFIG_NET_VENDOR_MARVELL=y
+CONFIG_NET_VENDOR_MELLANOX=y
+CONFIG_NET_VENDOR_MICREL=y
+CONFIG_NET_VENDOR_MICROCHIP=y
+CONFIG_NET_VENDOR_MICROSEMI=y
+CONFIG_NET_VENDOR_MYRI=y
+CONFIG_NET_VENDOR_NATSEMI=y
+CONFIG_NET_VENDOR_NETERION=y
+CONFIG_NET_VENDOR_NETRONOME=y
+CONFIG_NET_VENDOR_NI=y
+CONFIG_NET_VENDOR_NVIDIA=y
+CONFIG_NET_VENDOR_OKI=y
+CONFIG_NET_VENDOR_PACKET_ENGINES=y
+CONFIG_NET_VENDOR_PENSANDO=y
+CONFIG_NET_VENDOR_QLOGIC=y
+CONFIG_NET_VENDOR_QUALCOMM=y
+CONFIG_NET_VENDOR_RDC=y
+CONFIG_NET_VENDOR_REALTEK=y
+CONFIG_NET_VENDOR_RENESAS=y
+CONFIG_NET_VENDOR_ROCKER=y
+CONFIG_NET_VENDOR_SAMSUNG=y
+CONFIG_NET_VENDOR_SEEQ=y
+CONFIG_NET_VENDOR_SILAN=y
+CONFIG_NET_VENDOR_SIS=y
+CONFIG_NET_VENDOR_SMSC=y
+CONFIG_NET_VENDOR_SOCIONEXT=y
+CONFIG_NET_VENDOR_SOLARFLARE=y
+CONFIG_NET_VENDOR_STMICRO=y
+CONFIG_NET_VENDOR_SUN=y
+CONFIG_NET_VENDOR_SYNOPSYS=y
+CONFIG_NET_VENDOR_TEHUTI=y
+CONFIG_NET_VENDOR_TI=y
+CONFIG_NET_VENDOR_TOSHIBA=y
+CONFIG_NET_VENDOR_VIA=y
+CONFIG_NET_VENDOR_WIZNET=y
+CONFIG_NET_VENDOR_XILINX=y
+CONFIG_NET_VENDOR_XIRCOM=y
+# CONFIG_NET_VRF is not set
+# CONFIG_NET_XGENE is not set
+CONFIG_NEW_LEDS=y
+# CONFIG_NFC is not set
+# CONFIG_NFP is not set
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V2_ACL is not set
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+# CONFIG_NFSD_V4 is not set
+# CONFIG_NFS_ACL_SUPPORT is not set
+CONFIG_NFS_COMMON=y
+# CONFIG_NFS_DISABLE_UDP_SUPPORT is not set
+# CONFIG_NFS_FS is not set
+# CONFIG_NFS_FSCACHE is not set
+# CONFIG_NFS_SWAP is not set
+# CONFIG_NFS_V2 is not set
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_V4_1 is not set
+# CONFIG_NFTL is not set
+# CONFIG_NFT_BRIDGE_META is not set
+# CONFIG_NFT_BRIDGE_REJECT is not set
+# CONFIG_NFT_CONNLIMIT is not set
+# CONFIG_NFT_DUP_IPV4 is not set
+# CONFIG_NFT_DUP_IPV6 is not set
+# CONFIG_NFT_FIB_IPV4 is not set
+# CONFIG_NFT_FIB_IPV6 is not set
+# CONFIG_NFT_FIB_NETDEV is not set
+# CONFIG_NFT_FLOW_OFFLOAD is not set
+# CONFIG_NFT_OBJREF is not set
+# CONFIG_NFT_OSF is not set
+# CONFIG_NFT_RT is not set
+# CONFIG_NFT_SET_BITMAP is not set
+# CONFIG_NFT_SOCKET is not set
+# CONFIG_NFT_SYNPROXY is not set
+# CONFIG_NFT_TPROXY is not set
+# CONFIG_NFT_TUNNEL is not set
+# CONFIG_NFT_XFRM is not set
+# CONFIG_NF_CONNTRACK is not set
+# CONFIG_NF_CONNTRACK_AMANDA is not set
+# CONFIG_NF_CONNTRACK_BRIDGE is not set
+# CONFIG_NF_CONNTRACK_EVENTS is not set
+# CONFIG_NF_CONNTRACK_FTP is not set
+# CONFIG_NF_CONNTRACK_H323 is not set
+# CONFIG_NF_CONNTRACK_IRC is not set
+# CONFIG_NF_CONNTRACK_LABELS is not set
+# CONFIG_NF_CONNTRACK_MARK is not set
+# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set
+# CONFIG_NF_CONNTRACK_PPTP is not set
+CONFIG_NF_CONNTRACK_PROCFS=y
+# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set
+# CONFIG_NF_CONNTRACK_SANE is not set
+# CONFIG_NF_CONNTRACK_SECMARK is not set
+# CONFIG_NF_CONNTRACK_SIP is not set
+# CONFIG_NF_CONNTRACK_SNMP is not set
+# CONFIG_NF_CONNTRACK_TFTP is not set
+# CONFIG_NF_CONNTRACK_TIMEOUT is not set
+# CONFIG_NF_CONNTRACK_TIMESTAMP is not set
+# CONFIG_NF_CONNTRACK_ZONES is not set
+# CONFIG_NF_CT_NETLINK is not set
+# CONFIG_NF_CT_NETLINK_HELPER is not set
+# CONFIG_NF_CT_NETLINK_TIMEOUT is not set
+# CONFIG_NF_CT_PROTO_DCCP is not set
+# CONFIG_NF_CT_PROTO_GRE is not set
+# CONFIG_NF_CT_PROTO_SCTP is not set
+# CONFIG_NF_CT_PROTO_UDPLITE is not set
+# CONFIG_NF_DEFRAG_IPV4 is not set
+# CONFIG_NF_DUP_IPV4 is not set
+# CONFIG_NF_DUP_IPV6 is not set
+# CONFIG_NF_FLOW_TABLE is not set
+# CONFIG_NF_LOG_ARP is not set
+# CONFIG_NF_LOG_BRIDGE is not set
+# CONFIG_NF_LOG_IPV4 is not set
+# CONFIG_NF_LOG_NETDEV is not set
+# CONFIG_NF_NAT is not set
+# CONFIG_NF_NAT_AMANDA is not set
+# CONFIG_NF_NAT_FTP is not set
+# CONFIG_NF_NAT_H323 is not set
+# CONFIG_NF_NAT_IPV6 is not set
+# CONFIG_NF_NAT_IRC is not set
+CONFIG_NF_NAT_MASQUERADE_IPV4=y
+CONFIG_NF_NAT_MASQUERADE_IPV6=y
+# CONFIG_NF_NAT_NEEDED is not set
+# CONFIG_NF_NAT_PPTP is not set
+# CONFIG_NF_NAT_PROTO_GRE is not set
+# CONFIG_NF_NAT_SIP is not set
+# CONFIG_NF_NAT_SNMP_BASIC is not set
+# CONFIG_NF_NAT_TFTP is not set
+# CONFIG_NF_REJECT_IPV4 is not set
+# CONFIG_NF_REJECT_IPV6 is not set
+# CONFIG_NF_SOCKET_IPV4 is not set
+# CONFIG_NF_SOCKET_IPV6 is not set
+# CONFIG_NF_TABLES is not set
+CONFIG_NF_TABLES_ARP=y
+CONFIG_NF_TABLES_BRIDGE=y
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_IPV4=y
+CONFIG_NF_TABLES_IPV6=y
+CONFIG_NF_TABLES_NETDEV=y
+# CONFIG_NF_TABLES_SET is not set
+# CONFIG_NF_TPROXY_IPV4 is not set
+# CONFIG_NF_TPROXY_IPV6 is not set
+# CONFIG_NI65 is not set
+# CONFIG_NI903X_WDT is not set
+# CONFIG_NIC7018_WDT is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_NIU is not set
+# CONFIG_NI_XGE_MANAGEMENT_ENET is not set
+CONFIG_NLATTR=y
+# CONFIG_NLMON is not set
+# CONFIG_NLM_XLP_BOARD is not set
+# CONFIG_NLM_XLR_BOARD is not set
+# CONFIG_NLS is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_MAC_CELTIC is not set
+# CONFIG_NLS_MAC_CENTEURO is not set
+# CONFIG_NLS_MAC_CROATIAN is not set
+# CONFIG_NLS_MAC_CYRILLIC is not set
+# CONFIG_NLS_MAC_GAELIC is not set
+# CONFIG_NLS_MAC_GREEK is not set
+# CONFIG_NLS_MAC_ICELAND is not set
+# CONFIG_NLS_MAC_INUIT is not set
+# CONFIG_NLS_MAC_ROMAN is not set
+# CONFIG_NLS_MAC_ROMANIAN is not set
+# CONFIG_NLS_MAC_TURKISH is not set
+# CONFIG_NLS_UTF8 is not set
+CONFIG_NMI_LOG_BUF_SHIFT=13
+# CONFIG_NOA1305 is not set
+# CONFIG_NOP_USB_XCEIV is not set
+# CONFIG_NORTEL_HERMES is not set
+# CONFIG_NOTIFIER_ERROR_INJECTION is not set
+# CONFIG_NOUVEAU_LEGACY_CTX_SUPPORT is not set
+# CONFIG_NOZOMI is not set
+# CONFIG_NO_BOOTMEM is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_NO_HZ_FULL is not set
+# CONFIG_NO_HZ_IDLE is not set
+# CONFIG_NS83820 is not set
+# CONFIG_NTB is not set
+# CONFIG_NTFS_DEBUG is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_NTP_PPS is not set
+# CONFIG_NULL_TTY is not set
+# CONFIG_NUMA is not set
+# CONFIG_NVM is not set
+# CONFIG_NVMEM is not set
+# CONFIG_NVMEM_BCM_OCOTP is not set
+# CONFIG_NVMEM_IMX_OCOTP is not set
+# CONFIG_NVMEM_REBOOT_MODE is not set
+# CONFIG_NVMEM_SYSFS is not set
+# CONFIG_NVME_FC is not set
+# CONFIG_NVME_TARGET is not set
+# CONFIG_NVME_TCP is not set
+# CONFIG_NVRAM is not set
+# CONFIG_NV_TCO is not set
+# CONFIG_NXP_STB220 is not set
+# CONFIG_NXP_STB225 is not set
+# CONFIG_NXP_TJA11XX_PHY is not set
+# CONFIG_N_GSM is not set
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_OBS600 is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_OCTEONTX2_AF is not set
+# CONFIG_OCTEONTX2_PF is not set
+# CONFIG_OF_OVERLAY is not set
+CONFIG_OF_RESERVED_MEM=y
+# CONFIG_OF_UNITTEST is not set
+# CONFIG_OID_REGISTRY is not set
+# CONFIG_OMAP2_DSS_DEBUG is not set
+# CONFIG_OMAP2_DSS_DEBUGFS is not set
+# CONFIG_OMAP2_DSS_SDI is not set
+# CONFIG_OMAP_OCP2SCP is not set
+# CONFIG_OMAP_USB2 is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_OPENVSWITCH is not set
+# CONFIG_OPROFILE is not set
+# CONFIG_OPROFILE_EVENT_MULTIPLEX is not set
+# CONFIG_OPT3001 is not set
+CONFIG_OPTIMIZE_INLINING=y
+# CONFIG_ORANGEFS_FS is not set
+# CONFIG_ORION_WATCHDOG is not set
+# CONFIG_OSF_PARTITION is not set
+CONFIG_OVERLAY_FS=y
+# CONFIG_OVERLAY_FS_INDEX is not set
+# CONFIG_OVERLAY_FS_METACOPY is not set
+CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y
+# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set
+CONFIG_OVERLAY_FS_XINO_AUTO=y
+# CONFIG_OWL_LOADER is not set
+# CONFIG_P54_COMMON is not set
+# CONFIG_PA12203001 is not set
+CONFIG_PACKET=y
+# CONFIG_PACKET_DIAG is not set
+# CONFIG_PACKING is not set
+# CONFIG_PAGE_EXTENSION is not set
+# CONFIG_PAGE_OWNER is not set
+# CONFIG_PAGE_POISONING is not set
+# CONFIG_PAGE_REPORTING is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_32KB is not set
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_64KB is not set
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PALMAS_GPADC is not set
+# CONFIG_PANASONIC_LAPTOP is not set
+# CONFIG_PANEL is not set
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_PANIC_ON_OOPS_VALUE=1
+CONFIG_PANIC_TIMEOUT=1
+# CONFIG_PANTHERLORD_FF is not set
+# CONFIG_PARAVIRT is not set
+# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set
+# CONFIG_PARPORT is not set
+# CONFIG_PARPORT_1284 is not set
+# CONFIG_PARPORT_AX88796 is not set
+# CONFIG_PARPORT_GSC is not set
+# CONFIG_PARPORT_PC is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_PATA_ALI is not set
+# CONFIG_PATA_AMD is not set
+# CONFIG_PATA_ARASAN_CF is not set
+# CONFIG_PATA_ARTOP is not set
+# CONFIG_PATA_ATIIXP is not set
+# CONFIG_PATA_ATP867X is not set
+# CONFIG_PATA_CMD640_PCI is not set
+# CONFIG_PATA_CMD64X is not set
+# CONFIG_PATA_CS5520 is not set
+# CONFIG_PATA_CS5530 is not set
+# CONFIG_PATA_CS5535 is not set
+# CONFIG_PATA_CS5536 is not set
+# CONFIG_PATA_CYPRESS is not set
+# CONFIG_PATA_EFAR is not set
+# CONFIG_PATA_HPT366 is not set
+# CONFIG_PATA_HPT37X is not set
+# CONFIG_PATA_HPT3X2N is not set
+# CONFIG_PATA_HPT3X3 is not set
+# CONFIG_PATA_IMX is not set
+# CONFIG_PATA_ISAPNP is not set
+# CONFIG_PATA_IT8213 is not set
+# CONFIG_PATA_IT821X is not set
+# CONFIG_PATA_JMICRON is not set
+# CONFIG_PATA_LEGACY is not set
+# CONFIG_PATA_MARVELL is not set
+# CONFIG_PATA_MPIIX is not set
+# CONFIG_PATA_NETCELL is not set
+# CONFIG_PATA_NINJA32 is not set
+# CONFIG_PATA_NS87410 is not set
+# CONFIG_PATA_NS87415 is not set
+# CONFIG_PATA_OCTEON_CF is not set
+# CONFIG_PATA_OF_PLATFORM is not set
+# CONFIG_PATA_OLDPIIX is not set
+# CONFIG_PATA_OPTI is not set
+# CONFIG_PATA_OPTIDMA is not set
+# CONFIG_PATA_PCMCIA is not set
+# CONFIG_PATA_PDC2027X is not set
+# CONFIG_PATA_PDC_OLD is not set
+# CONFIG_PATA_PLATFORM is not set
+# CONFIG_PATA_QDI is not set
+# CONFIG_PATA_RADISYS is not set
+# CONFIG_PATA_RDC is not set
+# CONFIG_PATA_RZ1000 is not set
+# CONFIG_PATA_SC1200 is not set
+# CONFIG_PATA_SCH is not set
+# CONFIG_PATA_SERVERWORKS is not set
+# CONFIG_PATA_SIL680 is not set
+# CONFIG_PATA_SIS is not set
+# CONFIG_PATA_TOSHIBA is not set
+# CONFIG_PATA_TRIFLEX is not set
+# CONFIG_PATA_VIA is not set
+# CONFIG_PATA_WINBOND is not set
+# CONFIG_PATA_WINBOND_VLB is not set
+# CONFIG_PC104 is not set
+# CONFIG_PC300TOO is not set
+# CONFIG_PCCARD is not set
+# CONFIG_PCH_DMA is not set
+# CONFIG_PCH_GBE is not set
+# CONFIG_PCH_PHUB is not set
+# CONFIG_PCI is not set
+# CONFIG_PCI200SYN is not set
+# CONFIG_PCIEAER is not set
+# CONFIG_PCIEAER_INJECT is not set
+# CONFIG_PCIEASPM is not set
+# CONFIG_PCIEPORTBUS is not set
+# CONFIG_PCIE_AL is not set
+# CONFIG_PCIE_ALTERA is not set
+# CONFIG_PCIE_ARMADA_8K is not set
+CONFIG_PCIE_BUS_DEFAULT=y
+# CONFIG_PCIE_BUS_PEER2PEER is not set
+# CONFIG_PCIE_BUS_PERFORMANCE is not set
+# CONFIG_PCIE_BUS_SAFE is not set
+# CONFIG_PCIE_BUS_TUNE_OFF is not set
+# CONFIG_PCIE_BW is not set
+# CONFIG_PCIE_CADENCE_HOST is not set
+# CONFIG_PCIE_CADENCE_PLAT_HOST is not set
+# CONFIG_PCIE_DPC is not set
+# CONFIG_PCIE_DW_PLAT is not set
+# CONFIG_PCIE_DW_PLAT_HOST is not set
+# CONFIG_PCIE_ECRC is not set
+# CONFIG_PCIE_IPROC is not set
+# CONFIG_PCIE_KIRIN is not set
+# CONFIG_PCIE_LAYERSCAPE_GEN4 is not set
+# CONFIG_PCIE_PTM is not set
+# CONFIG_PCIE_XILINX is not set
+# CONFIG_PCIPCWATCHDOG is not set
+# CONFIG_PCI_ATMEL is not set
+# CONFIG_PCI_CNB20LE_QUIRK is not set
+# CONFIG_PCI_DEBUG is not set
+# CONFIG_PCI_DISABLE_COMMON_QUIRKS is not set
+# CONFIG_PCI_ENDPOINT is not set
+# CONFIG_PCI_ENDPOINT_TEST is not set
+# CONFIG_PCI_FTPCI100 is not set
+# CONFIG_PCI_HERMES is not set
+# CONFIG_PCI_HISI is not set
+# CONFIG_PCI_HOST_GENERIC is not set
+# CONFIG_PCI_HOST_THUNDER_ECAM is not set
+# CONFIG_PCI_HOST_THUNDER_PEM is not set
+# CONFIG_PCI_IOV is not set
+# CONFIG_PCI_J721E_HOST is not set
+# CONFIG_PCI_LAYERSCAPE is not set
+# CONFIG_PCI_MESON is not set
+# CONFIG_PCI_MSI is not set
+# CONFIG_PCI_PASID is not set
+# CONFIG_PCI_PF_STUB is not set
+# CONFIG_PCI_PRI is not set
+CONFIG_PCI_QUIRKS=y
+# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set
+# CONFIG_PCI_STUB is not set
+# CONFIG_PCI_SW_SWITCHTEC is not set
+CONFIG_PCI_SYSCALL=y
+# CONFIG_PCI_V3_SEMI is not set
+# CONFIG_PCI_XGENE is not set
+# CONFIG_PCMCIA is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_AHA152X is not set
+# CONFIG_PCMCIA_ATMEL is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_PCMCIA_DEBUG is not set
+# CONFIG_PCMCIA_FDOMAIN is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+# CONFIG_PCMCIA_HERMES is not set
+# CONFIG_PCMCIA_LOAD_CIS is not set
+# CONFIG_PCMCIA_NINJA_SCSI is not set
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_PCNET is not set
+# CONFIG_PCMCIA_QLOGIC is not set
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_SPECTRUM is not set
+# CONFIG_PCMCIA_SYM53C500 is not set
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_XIRCOM is not set
+# CONFIG_PCNET32 is not set
+# CONFIG_PCSPKR_PLATFORM is not set
+# CONFIG_PCS_XPCS is not set
+# CONFIG_PD6729 is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_PDC_ADMA is not set
+# CONFIG_PERCPU_STATS is not set
+# CONFIG_PERCPU_TEST is not set
+# CONFIG_PERF_EVENTS is not set
+# CONFIG_PERF_EVENTS_AMD_POWER is not set
+# CONFIG_PERSISTENT_KEYRINGS is not set
+# CONFIG_PHANTOM is not set
+# CONFIG_PHONET is not set
+# CONFIG_PHYLIB is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+# CONFIG_PHY_CADENCE_DP is not set
+# CONFIG_PHY_CADENCE_DPHY is not set
+# CONFIG_PHY_CADENCE_SALVO is not set
+# CONFIG_PHY_CADENCE_SIERRA is not set
+# CONFIG_PHY_CADENCE_TORRENT is not set
+# CONFIG_PHY_CPCAP_USB is not set
+# CONFIG_PHY_EXYNOS_DP_VIDEO is not set
+# CONFIG_PHY_EXYNOS_MIPI_VIDEO is not set
+# CONFIG_PHY_FSL_IMX8MQ_USB is not set
+# CONFIG_PHY_INTEL_KEEMBAY_EMMC is not set
+# CONFIG_PHY_MAPPHONE_MDM6600 is not set
+# CONFIG_PHY_MIXEL_MIPI_DPHY is not set
+# CONFIG_PHY_MTK_HDMI is not set
+# CONFIG_PHY_OCELOT_SERDES is not set
+# CONFIG_PHY_PXA_28NM_HSIC is not set
+# CONFIG_PHY_PXA_28NM_USB2 is not set
+# CONFIG_PHY_QCOM_DWC3 is not set
+# CONFIG_PHY_QCOM_USB_HS is not set
+# CONFIG_PHY_QCOM_USB_HSIC is not set
+# CONFIG_PHY_SAMSUNG_USB2 is not set
+# CONFIG_PHY_TUSB1210 is not set
+# CONFIG_PHY_XGENE is not set
+# CONFIG_PI433 is not set
+# CONFIG_PID_IN_CONTEXTIDR is not set
+# CONFIG_PID_NS is not set
+CONFIG_PINCONF=y
+# CONFIG_PINCTRL is not set
+# CONFIG_PINCTRL_AMD is not set
+# CONFIG_PINCTRL_AXP209 is not set
+# CONFIG_PINCTRL_CEDARFORK is not set
+# CONFIG_PINCTRL_EXYNOS is not set
+# CONFIG_PINCTRL_EXYNOS5440 is not set
+# CONFIG_PINCTRL_ICELAKE is not set
+# CONFIG_PINCTRL_INGENIC is not set
+# CONFIG_PINCTRL_MCP23S08 is not set
+# CONFIG_PINCTRL_MSM8X74 is not set
+# CONFIG_PINCTRL_MT6779 is not set
+# CONFIG_PINCTRL_MT8167 is not set
+# CONFIG_PINCTRL_MT8192 is not set
+# CONFIG_PINCTRL_MTK_V2 is not set
+# CONFIG_PINCTRL_OCELOT is not set
+CONFIG_PINCTRL_SINGLE=y
+# CONFIG_PINCTRL_STMFX is not set
+# CONFIG_PINCTRL_SX150X is not set
+# CONFIG_PING is not set
+CONFIG_PINMUX=y
+# CONFIG_PKCS7_MESSAGE_PARSER is not set
+# CONFIG_PL310_ERRATA_588369 is not set
+# CONFIG_PL310_ERRATA_727915 is not set
+# CONFIG_PL310_ERRATA_753970 is not set
+# CONFIG_PL310_ERRATA_769419 is not set
+# CONFIG_PL320_MBOX is not set
+# CONFIG_PL330_DMA is not set
+# CONFIG_PLATFORM_MHU is not set
+# CONFIG_PLAT_SPEAR is not set
+# CONFIG_PLIP is not set
+# CONFIG_PLX_DMA is not set
+# CONFIG_PLX_HERMES is not set
+# CONFIG_PM is not set
+# CONFIG_PMBUS is not set
+# CONFIG_PMC_MSP is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_PMS7003 is not set
+# CONFIG_PM_AUTOSLEEP is not set
+# CONFIG_PM_DEBUG is not set
+# CONFIG_PM_DEVFREQ is not set
+# CONFIG_PM_WAKELOCKS is not set
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_POSIX_TIMERS=y
+# CONFIG_POWERCAP is not set
+# CONFIG_POWER_AVS is not set
+# CONFIG_POWER_RESET is not set
+# CONFIG_POWER_RESET_BRCMKONA is not set
+# CONFIG_POWER_RESET_BRCMSTB is not set
+# CONFIG_POWER_RESET_GPIO is not set
+# CONFIG_POWER_RESET_GPIO_RESTART is not set
+# CONFIG_POWER_RESET_LINKSTATION is not set
+# CONFIG_POWER_RESET_LTC2952 is not set
+# CONFIG_POWER_RESET_PIIX4_POWEROFF is not set
+# CONFIG_POWER_RESET_QNAP is not set
+# CONFIG_POWER_RESET_RESTART is not set
+# CONFIG_POWER_RESET_SYSCON is not set
+# CONFIG_POWER_RESET_SYSCON_POWEROFF is not set
+# CONFIG_POWER_RESET_VERSATILE is not set
+# CONFIG_POWER_RESET_XGENE is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_POWER_SUPPLY_HWMON is not set
+# CONFIG_PPC4xx_GPIO is not set
+# CONFIG_PPC_16K_PAGES is not set
+# CONFIG_PPC_256K_PAGES is not set
+CONFIG_PPC_4K_PAGES=y
+# CONFIG_PPC_64K_PAGES is not set
+# CONFIG_PPC_DISABLE_WERROR is not set
+# CONFIG_PPC_EMULATED_STATS is not set
+# CONFIG_PPC_EPAPR_HV_BYTECHAN is not set
+# CONFIG_PPC_QUEUED_SPINLOCKS is not set
+# CONFIG_PPP is not set
+# CONFIG_PPPOATM is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_PPP_ASYNC is not set
+# CONFIG_PPP_BSDCOMP is not set
+# CONFIG_PPP_DEFLATE is not set
+CONFIG_PPP_FILTER=y
+# CONFIG_PPP_MPPE is not set
+CONFIG_PPP_MULTILINK=y
+# CONFIG_PPP_SYNC_TTY is not set
+# CONFIG_PPS is not set
+# CONFIG_PPS_CLIENT_GPIO is not set
+# CONFIG_PPS_CLIENT_KTIMER is not set
+# CONFIG_PPS_CLIENT_LDISC is not set
+# CONFIG_PPS_CLIENT_PARPORT is not set
+# CONFIG_PPS_DEBUG is not set
+# CONFIG_PPTP is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
+# CONFIG_PREEMPTIRQ_EVENTS is not set
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PRESTERA is not set
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_PRIME_NUMBERS is not set
+CONFIG_PRINTK=y
+# CONFIG_PRINTK_CALLER is not set
+CONFIG_PRINTK_NMI=y
+CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13
+# CONFIG_PRINTK_TIME is not set
+CONFIG_PRINT_STACK_DEPTH=64
+# CONFIG_PRISM2_USB is not set
+# CONFIG_PRISM54 is not set
+# CONFIG_PROC_CHILDREN is not set
+CONFIG_PROC_FS=y
+# CONFIG_PROC_KCORE is not set
+# CONFIG_PROC_PAGE_MONITOR is not set
+# CONFIG_PROC_STRIPPED is not set
+CONFIG_PROC_SYSCTL=y
+# CONFIG_PROC_VMCORE_DEVICE_DUMP is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILING is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_PROVE_RAW_LOCK_NESTING is not set
+# CONFIG_PROVE_RCU is not set
+# CONFIG_PROVE_RCU_LIST is not set
+# CONFIG_PROVE_RCU_REPEATEDLY is not set
+# CONFIG_PSAMPLE is not set
+# CONFIG_PSB6970_PHY is not set
+# CONFIG_PSI is not set
+# CONFIG_PSTORE is not set
+# CONFIG_PSTORE_842_COMPRESS is not set
+# CONFIG_PSTORE_COMPRESS is not set
+# CONFIG_PSTORE_COMPRESS_DEFAULT="deflate"
+# CONFIG_PSTORE_CONSOLE is not set
+# CONFIG_PSTORE_DEFLATE_COMPRESS is not set
+# CONFIG_PSTORE_DEFLATE_COMPRESS_DEFAULT is not set
+# CONFIG_PSTORE_LZ4HC_COMPRESS is not set
+# CONFIG_PSTORE_LZ4_COMPRESS is not set
+# CONFIG_PSTORE_LZO_COMPRESS is not set
+# CONFIG_PSTORE_PMSG is not set
+# CONFIG_PSTORE_RAM is not set
+# CONFIG_PSTORE_ZSTD_COMPRESS is not set
+# CONFIG_PTDUMP_DEBUGFS is not set
+# CONFIG_PTP_1588_CLOCK is not set
+# CONFIG_PTP_1588_CLOCK_IDT82P33 is not set
+# CONFIG_PTP_1588_CLOCK_IDTCM is not set
+# CONFIG_PTP_1588_CLOCK_IXP46X is not set
+# CONFIG_PTP_1588_CLOCK_KVM is not set
+# CONFIG_PTP_1588_CLOCK_PCH is not set
+# CONFIG_PTP_1588_CLOCK_VMW is not set
+# CONFIG_PUBLIC_KEY_ALGO_RSA is not set
+# CONFIG_PVPANIC is not set
+# CONFIG_PWM is not set
+# CONFIG_PWM_DEBUG is not set
+# CONFIG_PWM_FSL_FTM is not set
+# CONFIG_PWM_JZ4740 is not set
+# CONFIG_PWM_PCA9685 is not set
+CONFIG_PWRSEQ_EMMC=y
+# CONFIG_PWRSEQ_SD8787 is not set
+CONFIG_PWRSEQ_SIMPLE=y
+# CONFIG_QCA7000 is not set
+# CONFIG_QCA7000_SPI is not set
+# CONFIG_QCA7000_UART is not set
+# CONFIG_QCOM_EMAC is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set
+# CONFIG_QCOM_HIDMA is not set
+# CONFIG_QCOM_HIDMA_MGMT is not set
+# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set
+# CONFIG_QCOM_SPMI_ADC5 is not set
+# CONFIG_QCOM_SPMI_IADC is not set
+# CONFIG_QCOM_SPMI_TEMP_ALARM is not set
+# CONFIG_QCOM_SPMI_VADC is not set
+# CONFIG_QED is not set
+# CONFIG_QLA3XXX is not set
+# CONFIG_QLCNIC is not set
+# CONFIG_QLGE is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX6FS_FS is not set
+# CONFIG_QORIQ_CPUFREQ is not set
+# CONFIG_QORIQ_THERMAL is not set
+# CONFIG_QRTR is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_QUEUED_LOCK_STAT is not set
+# CONFIG_QUICC_ENGINE is not set
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+# CONFIG_QUOTA_DEBUG is not set
+# CONFIG_R3964 is not set
+# CONFIG_R6040 is not set
+# CONFIG_R8169 is not set
+# CONFIG_R8188EU is not set
+# CONFIG_R8712U is not set
+# CONFIG_R8723AU is not set
+# CONFIG_RADIO_ADAPTERS is not set
+# CONFIG_RADIO_AZTECH is not set
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_GEMTEK is not set
+# CONFIG_RADIO_MAXIRADIO is not set
+# CONFIG_RADIO_RTRACK is not set
+# CONFIG_RADIO_RTRACK2 is not set
+# CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
+# CONFIG_RADIO_TERRATEC is not set
+# CONFIG_RADIO_TRUST is not set
+# CONFIG_RADIO_TYPHOON is not set
+# CONFIG_RADIO_ZOLTRIX is not set
+# CONFIG_RAID6_PQ_BENCHMARK is not set
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_RALINK is not set
+# CONFIG_RANDOM32_SELFTEST is not set
+# CONFIG_RANDOMIZE_BASE is not set
+# CONFIG_RANDOM_TRUST_BOOTLOADER is not set
+# CONFIG_RANDOM_TRUST_CPU is not set
+# CONFIG_RAPIDIO is not set
+# CONFIG_RAS is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_RBTREE_TEST is not set
+# CONFIG_RCU_BOOST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=21
+# CONFIG_RCU_EQS_DEBUG is not set
+# CONFIG_RCU_EXPEDITE_BOOT is not set
+# CONFIG_RCU_EXPERT is not set
+CONFIG_RCU_KTHREAD_PRIO=0
+CONFIG_RCU_NEED_SEGCBLIST=y
+# CONFIG_RCU_PERF_TEST is not set
+# CONFIG_RCU_REF_SCALE_TEST is not set
+# CONFIG_RCU_SCALE_TEST is not set
+CONFIG_RCU_STALL_COMMON=y
+# CONFIG_RCU_STRICT_GRACE_PERIOD is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+CONFIG_RCU_TORTURE_TEST_SLOW_INIT_DELAY=3
+# CONFIG_RCU_TRACE is not set
+# CONFIG_RC_ATI_REMOTE is not set
+# CONFIG_RC_CORE is not set
+# CONFIG_RC_DECODERS is not set
+# CONFIG_RC_LOOPBACK is not set
+# CONFIG_RC_MAP is not set
+# CONFIG_RDS is not set
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_GZIP is not set
+# CONFIG_RD_LZ4 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_ZSTD is not set
+# CONFIG_READABLE_ASM is not set
+# CONFIG_READ_ONLY_THP_FOR_FS is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_REDWOOD is not set
+# CONFIG_REED_SOLOMON is not set
+# CONFIG_REED_SOLOMON_DEC8 is not set
+# CONFIG_REED_SOLOMON_ENC8 is not set
+# CONFIG_REED_SOLOMON_TEST is not set
+# CONFIG_REGMAP is not set
+# CONFIG_REGMAP_I2C is not set
+# CONFIG_REGMAP_MMIO is not set
+# CONFIG_REGMAP_SPI is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_88PG86X is not set
+# CONFIG_REGULATOR_ACT8865 is not set
+# CONFIG_REGULATOR_AD5398 is not set
+# CONFIG_REGULATOR_ANATOP is not set
+# CONFIG_REGULATOR_DA9210 is not set
+# CONFIG_REGULATOR_DA9211 is not set
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FAN53555 is not set
+# CONFIG_REGULATOR_FAN53880 is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_GPIO is not set
+# CONFIG_REGULATOR_ISL6271A is not set
+# CONFIG_REGULATOR_ISL9305 is not set
+# CONFIG_REGULATOR_LP3971 is not set
+# CONFIG_REGULATOR_LP3972 is not set
+# CONFIG_REGULATOR_LP872X is not set
+# CONFIG_REGULATOR_LP8755 is not set
+# CONFIG_REGULATOR_LTC3589 is not set
+# CONFIG_REGULATOR_LTC3676 is not set
+# CONFIG_REGULATOR_MAX1586 is not set
+# CONFIG_REGULATOR_MAX77620 is not set
+# CONFIG_REGULATOR_MAX77826 is not set
+# CONFIG_REGULATOR_MAX8649 is not set
+# CONFIG_REGULATOR_MAX8660 is not set
+# CONFIG_REGULATOR_MAX8952 is not set
+# CONFIG_REGULATOR_MAX8973 is not set
+# CONFIG_REGULATOR_MCP16502 is not set
+# CONFIG_REGULATOR_MP5416 is not set
+# CONFIG_REGULATOR_MP8859 is not set
+# CONFIG_REGULATOR_MP886X is not set
+# CONFIG_REGULATOR_MPQ7920 is not set
+# CONFIG_REGULATOR_MT6311 is not set
+# CONFIG_REGULATOR_PCA9450 is not set
+# CONFIG_REGULATOR_PFUZE100 is not set
+# CONFIG_REGULATOR_PV88060 is not set
+# CONFIG_REGULATOR_PV88080 is not set
+# CONFIG_REGULATOR_PV88090 is not set
+# CONFIG_REGULATOR_PWM is not set
+# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set
+# CONFIG_REGULATOR_RT4801 is not set
+# CONFIG_REGULATOR_RTMV20 is not set
+# CONFIG_REGULATOR_SLG51000 is not set
+# CONFIG_REGULATOR_SY8106A is not set
+# CONFIG_REGULATOR_SY8824X is not set
+# CONFIG_REGULATOR_SY8827N is not set
+# CONFIG_REGULATOR_TI_ABB is not set
+# CONFIG_REGULATOR_TPS51632 is not set
+# CONFIG_REGULATOR_TPS62360 is not set
+# CONFIG_REGULATOR_TPS65023 is not set
+# CONFIG_REGULATOR_TPS6507X is not set
+# CONFIG_REGULATOR_TPS65132 is not set
+# CONFIG_REGULATOR_TPS6524X is not set
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+# CONFIG_REGULATOR_VCTRL is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_REISERFS_FS_POSIX_ACL is not set
+# CONFIG_REISERFS_FS_SECURITY is not set
+CONFIG_REISERFS_FS_XATTR=y
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_RELAY is not set
+# CONFIG_RELOCATABLE is not set
+# CONFIG_REMOTEPROC is not set
+# CONFIG_RENESAS_PHY is not set
+# CONFIG_RESET_ATH79 is not set
+# CONFIG_RESET_BERLIN is not set
+# CONFIG_RESET_BRCMSTB_RESCAL is not set
+# CONFIG_RESET_CONTROLLER is not set
+# CONFIG_RESET_IMX7 is not set
+# CONFIG_RESET_INTEL_GW is not set
+# CONFIG_RESET_LANTIQ is not set
+# CONFIG_RESET_LPC18XX is not set
+# CONFIG_RESET_MESON is not set
+# CONFIG_RESET_PISTACHIO is not set
+# CONFIG_RESET_SOCFPGA is not set
+# CONFIG_RESET_STM32 is not set
+# CONFIG_RESET_SUNXI is not set
+# CONFIG_RESET_TEGRA_BPMP is not set
+# CONFIG_RESET_TI_SYSCON is not set
+# CONFIG_RESET_ZYNQ is not set
+# CONFIG_RFD77402 is not set
+# CONFIG_RFD_FTL is not set
+CONFIG_RFKILL=y
+# CONFIG_RFKILL_FULL is not set
+# CONFIG_RFKILL_GPIO is not set
+# CONFIG_RFKILL_INPUT is not set
+# CONFIG_RFKILL_LEDS is not set
+# CONFIG_RFKILL_REGULATOR is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_RING_BUFFER_STARTUP_TEST is not set
+# CONFIG_RMI4_CORE is not set
+# CONFIG_RMNET is not set
+# CONFIG_ROCKCHIP_PHY is not set
+# CONFIG_ROCKER is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_ROSE is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPMSG_QCOM_GLINK_RPM is not set
+# CONFIG_RPMSG_VIRTIO is not set
+# CONFIG_RPR0521 is not set
+# CONFIG_RSEQ is not set
+# CONFIG_RT2X00 is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_RTC_DEBUG is not set
+# CONFIG_RTC_DRV_ABB5ZES3 is not set
+# CONFIG_RTC_DRV_ABEOZ9 is not set
+# CONFIG_RTC_DRV_ABX80X is not set
+# CONFIG_RTC_DRV_ARMADA38X is not set
+# CONFIG_RTC_DRV_AU1XXX is not set
+# CONFIG_RTC_DRV_BQ32K is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_CADENCE is not set
+CONFIG_RTC_DRV_CMOS=y
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1302 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1307_CENTURY is not set
+# CONFIG_RTC_DRV_DS1307_HWMON is not set
+# CONFIG_RTC_DRV_DS1343 is not set
+# CONFIG_RTC_DRV_DS1347 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_DS1685_FAMILY is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_DS2404 is not set
+# CONFIG_RTC_DRV_DS3232 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+# CONFIG_RTC_DRV_EM3027 is not set
+# CONFIG_RTC_DRV_EP93XX is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_FTRTC010 is not set
+# CONFIG_RTC_DRV_GENERIC is not set
+# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set
+# CONFIG_RTC_DRV_HYM8563 is not set
+# CONFIG_RTC_DRV_ISL12022 is not set
+# CONFIG_RTC_DRV_ISL12026 is not set
+# CONFIG_RTC_DRV_ISL12057 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_JZ4740 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_M41T93 is not set
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_MAX6916 is not set
+# CONFIG_RTC_DRV_MCP795 is not set
+# CONFIG_RTC_DRV_MOXART is not set
+# CONFIG_RTC_DRV_MPC5121 is not set
+# CONFIG_RTC_DRV_MSM6242 is not set
+# CONFIG_RTC_DRV_MT2712 is not set
+# CONFIG_RTC_DRV_OMAP is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+# CONFIG_RTC_DRV_PCF2127 is not set
+# CONFIG_RTC_DRV_PCF85063 is not set
+# CONFIG_RTC_DRV_PCF8523 is not set
+# CONFIG_RTC_DRV_PCF85363 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_PL030 is not set
+# CONFIG_RTC_DRV_PL031 is not set
+# CONFIG_RTC_DRV_PS3 is not set
+# CONFIG_RTC_DRV_PT7C4338 is not set
+# CONFIG_RTC_DRV_R7301 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RP5C01 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_RTC7301 is not set
+# CONFIG_RTC_DRV_RV3028 is not set
+# CONFIG_RTC_DRV_RV3029C2 is not set
+# CONFIG_RTC_DRV_RV3032 is not set
+# CONFIG_RTC_DRV_RV8803 is not set
+# CONFIG_RTC_DRV_RX4581 is not set
+# CONFIG_RTC_DRV_RX6110 is not set
+# CONFIG_RTC_DRV_RX8010 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_SD3078 is not set
+# CONFIG_RTC_DRV_SNVS is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_SUN6I is not set
+# CONFIG_RTC_DRV_TEST is not set
+# CONFIG_RTC_DRV_V3020 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_XGENE is not set
+# CONFIG_RTC_DRV_ZYNQMP is not set
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_NVMEM is not set
+CONFIG_RTC_SYSTOHC=y
+CONFIG_RTC_SYSTOHC_DEVICE="rtc0"
+# CONFIG_RTL8180 is not set
+# CONFIG_RTL8187 is not set
+# CONFIG_RTL8192E is not set
+# CONFIG_RTL8192U is not set
+# CONFIG_RTL8306_PHY is not set
+# CONFIG_RTL8366RB_PHY is not set
+# CONFIG_RTL8366S_PHY is not set
+# CONFIG_RTL8366_SMI is not set
+# CONFIG_RTL8366_SMI_DEBUG_FS is not set
+# CONFIG_RTL8367B_PHY is not set
+# CONFIG_RTL8367_PHY is not set
+# CONFIG_RTLLIB is not set
+# CONFIG_RTL_CARDS is not set
+# CONFIG_RTS5208 is not set
+CONFIG_RT_MUTEXES=y
+# CONFIG_RUNTIME_DEBUG is not set
+CONFIG_RUNTIME_TESTING_MENU=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_RXKAD=y
+# CONFIG_S2IO is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_SAMSUNG_LAPTOP is not set
+# CONFIG_SATA_ACARD_AHCI is not set
+# CONFIG_SATA_AHCI is not set
+# CONFIG_SATA_AHCI_PLATFORM is not set
+# CONFIG_SATA_DWC is not set
+# CONFIG_SATA_FSL is not set
+# CONFIG_SATA_HIGHBANK is not set
+# CONFIG_SATA_HOST is not set
+# CONFIG_SATA_INIC162X is not set
+CONFIG_SATA_MOBILE_LPM_POLICY=0
+# CONFIG_SATA_MV is not set
+# CONFIG_SATA_NV is not set
+# CONFIG_SATA_PMP is not set
+# CONFIG_SATA_PROMISE is not set
+# CONFIG_SATA_QSTOR is not set
+# CONFIG_SATA_RCAR is not set
+# CONFIG_SATA_SIL is not set
+# CONFIG_SATA_SIL24 is not set
+# CONFIG_SATA_SIS is not set
+# CONFIG_SATA_SVW is not set
+# CONFIG_SATA_SX4 is not set
+# CONFIG_SATA_ULI is not set
+# CONFIG_SATA_VIA is not set
+# CONFIG_SATA_VITESSE is not set
+# CONFIG_SBC_FITPC2_WATCHDOG is not set
+CONFIG_SBITMAP=y
+# CONFIG_SC92031 is not set
+# CONFIG_SCA3000 is not set
+# CONFIG_SCACHE_DEBUGFS is not set
+# CONFIG_SCC is not set
+# CONFIG_SCD30_CORE is not set
+# CONFIG_SCF_TORTURE_TEST is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_SCHED_AUTOGROUP is not set
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHED_HRTICK=y
+# CONFIG_SCHED_MC is not set
+CONFIG_SCHED_OMIT_FRAME_POINTER=y
+# CONFIG_SCHED_SMT is not set
+# CONFIG_SCHED_STACK_END_CHECK is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_SCR24X is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_3W_SAS is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC94XX is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_ARCMSR is not set
+# CONFIG_SCSI_BFA_FC is not set
+# CONFIG_SCSI_BNX2X_FCOE is not set
+# CONFIG_SCSI_BNX2_ISCSI is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_CHELSIO_FCOE is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_CXGB3_ISCSI is not set
+# CONFIG_SCSI_CXGB4_ISCSI is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_ESAS2R is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_FDOMAIN_PCI is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_HISI_SAS is not set
+# CONFIG_SCSI_HPSA is not set
+# CONFIG_SCSI_HPTIOP is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_ISCI is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_LOGGING is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+# CONFIG_SCSI_LPFC is not set
+CONFIG_SCSI_MOD=y
+# CONFIG_SCSI_MPT2SAS is not set
+# CONFIG_SCSI_MPT3SAS is not set
+# CONFIG_SCSI_MQ_DEFAULT is not set
+# CONFIG_SCSI_MVSAS is not set
+# CONFIG_SCSI_MVSAS_DEBUG is not set
+# CONFIG_SCSI_MVUMI is not set
+# CONFIG_SCSI_MYRB is not set
+# CONFIG_SCSI_MYRS is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_OSD_INITIATOR is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PM8001 is not set
+# CONFIG_SCSI_PMCRAID is not set
+CONFIG_SCSI_PROC_FS=y
+# CONFIG_SCSI_QLA_FC is not set
+# CONFIG_SCSI_QLA_ISCSI is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SAS_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+# CONFIG_SCSI_SMARTPQI is not set
+# CONFIG_SCSI_SNIC is not set
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_STEX is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_UFSHCD is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_VIRTIO is not set
+# CONFIG_SCSI_WD719X is not set
+# CONFIG_SCx200_ACB is not set
+# CONFIG_SDIO_UART is not set
+# CONFIG_SDR_MAX2175 is not set
+# CONFIG_SDR_PLATFORM_DRIVERS is not set
+# CONFIG_SD_ADC_MODULATOR is not set
+# CONFIG_SECCOMP is not set
+CONFIG_SECTION_MISMATCH_WARN_ONLY=y
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_APPARMOR is not set
+CONFIG_SECURITY_DMESG_RESTRICT=y
+# CONFIG_SECURITY_LOADPIN is not set
+# CONFIG_SECURITY_LOCKDOWN_LSM is not set
+# CONFIG_SECURITY_NETWORK_XFRM is not set
+# CONFIG_SECURITY_PATH is not set
+# CONFIG_SECURITY_SAFESETID is not set
+# CONFIG_SECURITY_SELINUX_AVC_STATS is not set
+# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set
+CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=0
+# CONFIG_SECURITY_SELINUX_DEVELOP is not set
+# CONFIG_SECURITY_SELINUX_DISABLE is not set
+# CONFIG_SECURITY_SMACK is not set
+# CONFIG_SECURITY_TOMOYO is not set
+# CONFIG_SECURITY_YAMA is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+# CONFIG_SENSIRION_SGP30 is not set
+# CONFIG_SENSORS_ABITUGURU is not set
+# CONFIG_SENSORS_ABITUGURU3 is not set
+# CONFIG_SENSORS_ACPI_POWER is not set
+# CONFIG_SENSORS_AD7314 is not set
+# CONFIG_SENSORS_AD7414 is not set
+# CONFIG_SENSORS_AD7418 is not set
+# CONFIG_SENSORS_ADC128D818 is not set
+# CONFIG_SENSORS_ADCXX is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1029 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM1177 is not set
+# CONFIG_SENSORS_ADM1266 is not set
+# CONFIG_SENSORS_ADM1275 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ADS1015 is not set
+# CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_ADS7871 is not set
+# CONFIG_SENSORS_ADT7310 is not set
+# CONFIG_SENSORS_ADT7410 is not set
+# CONFIG_SENSORS_ADT7411 is not set
+# CONFIG_SENSORS_ADT7462 is not set
+# CONFIG_SENSORS_ADT7470 is not set
+# CONFIG_SENSORS_ADT7475 is not set
+# CONFIG_SENSORS_AMC6821 is not set
+# CONFIG_SENSORS_APDS990X is not set
+# CONFIG_SENSORS_APPLESMC is not set
+# CONFIG_SENSORS_AS370 is not set
+# CONFIG_SENSORS_ASB100 is not set
+# CONFIG_SENSORS_ASC7621 is not set
+# CONFIG_SENSORS_ASPEED is not set
+# CONFIG_SENSORS_ATK0110 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_AXI_FAN_CONTROL is not set
+# CONFIG_SENSORS_BEL_PFE is not set
+# CONFIG_SENSORS_BH1770 is not set
+# CONFIG_SENSORS_BH1780 is not set
+# CONFIG_SENSORS_CORETEMP is not set
+# CONFIG_SENSORS_CORSAIR_CPRO is not set
+# CONFIG_SENSORS_DELL_SMM is not set
+# CONFIG_SENSORS_DME1737 is not set
+# CONFIG_SENSORS_DRIVETEMP is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_DS620 is not set
+# CONFIG_SENSORS_EMC1403 is not set
+# CONFIG_SENSORS_EMC2103 is not set
+# CONFIG_SENSORS_EMC6W201 is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_F75375S is not set
+# CONFIG_SENSORS_FAM15H_POWER is not set
+# CONFIG_SENSORS_FSCHMD is not set
+# CONFIG_SENSORS_FTSTEUTATES is not set
+# CONFIG_SENSORS_G760A is not set
+# CONFIG_SENSORS_G762 is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_GPIO_FAN is not set
+# CONFIG_SENSORS_GSC is not set
+# CONFIG_SENSORS_HDAPS is not set
+# CONFIG_SENSORS_HIH6130 is not set
+# CONFIG_SENSORS_HMC5843 is not set
+# CONFIG_SENSORS_HMC5843_I2C is not set
+# CONFIG_SENSORS_HMC5843_SPI is not set
+# CONFIG_SENSORS_HTU21 is not set
+# CONFIG_SENSORS_I5500 is not set
+# CONFIG_SENSORS_I5K_AMB is not set
+# CONFIG_SENSORS_IBM_CFFPS is not set
+# CONFIG_SENSORS_IIO_HWMON is not set
+# CONFIG_SENSORS_INA209 is not set
+# CONFIG_SENSORS_INA2XX is not set
+# CONFIG_SENSORS_INA3221 is not set
+# CONFIG_SENSORS_INSPUR_IPSPS is not set
+# CONFIG_SENSORS_IR35221 is not set
+# CONFIG_SENSORS_IR38064 is not set
+# CONFIG_SENSORS_IRPS5401 is not set
+# CONFIG_SENSORS_ISL29018 is not set
+# CONFIG_SENSORS_ISL29028 is not set
+# CONFIG_SENSORS_ISL68137 is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_JC42 is not set
+# CONFIG_SENSORS_K10TEMP is not set
+# CONFIG_SENSORS_K8TEMP is not set
+# CONFIG_SENSORS_LINEAGE is not set
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_SENSORS_LIS3_I2C is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
+# CONFIG_SENSORS_LM25066 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 is not set
+# CONFIG_SENSORS_LM73 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_LM93 is not set
+# CONFIG_SENSORS_LM95234 is not set
+# CONFIG_SENSORS_LM95241 is not set
+# CONFIG_SENSORS_LM95245 is not set
+# CONFIG_SENSORS_LTC2945 is not set
+# CONFIG_SENSORS_LTC2947_I2C is not set
+# CONFIG_SENSORS_LTC2947_SPI is not set
+# CONFIG_SENSORS_LTC2978 is not set
+# CONFIG_SENSORS_LTC2990 is not set
+# CONFIG_SENSORS_LTC3815 is not set
+# CONFIG_SENSORS_LTC4151 is not set
+# CONFIG_SENSORS_LTC4215 is not set
+# CONFIG_SENSORS_LTC4222 is not set
+# CONFIG_SENSORS_LTC4245 is not set
+# CONFIG_SENSORS_LTC4260 is not set
+# CONFIG_SENSORS_LTC4261 is not set
+# CONFIG_SENSORS_LTQ_CPUTEMP is not set
+# CONFIG_SENSORS_MAX1111 is not set
+# CONFIG_SENSORS_MAX16064 is not set
+# CONFIG_SENSORS_MAX16065 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_MAX16601 is not set
+# CONFIG_SENSORS_MAX1668 is not set
+# CONFIG_SENSORS_MAX197 is not set
+# CONFIG_SENSORS_MAX20730 is not set
+# CONFIG_SENSORS_MAX20751 is not set
+# CONFIG_SENSORS_MAX31722 is not set
+# CONFIG_SENSORS_MAX31730 is not set
+# CONFIG_SENSORS_MAX31785 is not set
+# CONFIG_SENSORS_MAX31790 is not set
+# CONFIG_SENSORS_MAX34440 is not set
+# CONFIG_SENSORS_MAX6621 is not set
+# CONFIG_SENSORS_MAX6639 is not set
+# CONFIG_SENSORS_MAX6642 is not set
+# CONFIG_SENSORS_MAX6650 is not set
+# CONFIG_SENSORS_MAX6697 is not set
+# CONFIG_SENSORS_MAX8688 is not set
+# CONFIG_SENSORS_MCP3021 is not set
+# CONFIG_SENSORS_MP2975 is not set
+# CONFIG_SENSORS_MR75203 is not set
+# CONFIG_SENSORS_NCT6683 is not set
+# CONFIG_SENSORS_NCT6775 is not set
+# CONFIG_SENSORS_NCT7802 is not set
+# CONFIG_SENSORS_NCT7904 is not set
+# CONFIG_SENSORS_NPCM7XX is not set
+# CONFIG_SENSORS_NSA320 is not set
+# CONFIG_SENSORS_NTC_THERMISTOR is not set
+# CONFIG_SENSORS_OCC_P8_I2C is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_PMBUS is not set
+# CONFIG_SENSORS_POWR1220 is not set
+# CONFIG_SENSORS_PWM_FAN is not set
+# CONFIG_SENSORS_PXE1610 is not set
+# CONFIG_SENSORS_RM3100_I2C is not set
+# CONFIG_SENSORS_RM3100_SPI is not set
+# CONFIG_SENSORS_SCH5627 is not set
+# CONFIG_SENSORS_SCH5636 is not set
+# CONFIG_SENSORS_SCH56XX_COMMON is not set
+# CONFIG_SENSORS_SHT15 is not set
+# CONFIG_SENSORS_SHT21 is not set
+# CONFIG_SENSORS_SHT3x is not set
+# CONFIG_SENSORS_SHTC1 is not set
+# CONFIG_SENSORS_SIS5595 is not set
+# CONFIG_SENSORS_SMM665 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47M192 is not set
+# CONFIG_SENSORS_STTS751 is not set
+# CONFIG_SENSORS_TC654 is not set
+# CONFIG_SENSORS_TC74 is not set
+# CONFIG_SENSORS_THMC50 is not set
+# CONFIG_SENSORS_TMP102 is not set
+# CONFIG_SENSORS_TMP103 is not set
+# CONFIG_SENSORS_TMP108 is not set
+# CONFIG_SENSORS_TMP401 is not set
+# CONFIG_SENSORS_TMP421 is not set
+# CONFIG_SENSORS_TMP513 is not set
+# CONFIG_SENSORS_TPS40422 is not set
+# CONFIG_SENSORS_TPS53679 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_SENSORS_TSL2563 is not set
+# CONFIG_SENSORS_UCD9000 is not set
+# CONFIG_SENSORS_UCD9200 is not set
+# CONFIG_SENSORS_VEXPRESS is not set
+# CONFIG_SENSORS_VIA686A is not set
+# CONFIG_SENSORS_VIA_CPUTEMP is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_VT8231 is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83773G is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83791D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83793 is not set
+# CONFIG_SENSORS_W83795 is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83L786NG is not set
+# CONFIG_SENSORS_XDPE122 is not set
+# CONFIG_SENSORS_XGENE is not set
+# CONFIG_SENSORS_ZL6100 is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_16550A_VARIANTS=y
+# CONFIG_SERIAL_8250_ACCENT is not set
+# CONFIG_SERIAL_8250_ASPEED_VUART is not set
+# CONFIG_SERIAL_8250_BOCA is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_SERIAL_8250_CS is not set
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+CONFIG_SERIAL_8250_DMA=y
+# CONFIG_SERIAL_8250_DW is not set
+# CONFIG_SERIAL_8250_EM is not set
+# CONFIG_SERIAL_8250_EXAR is not set
+# CONFIG_SERIAL_8250_EXAR_ST16C554 is not set
+# CONFIG_SERIAL_8250_EXTENDED is not set
+# CONFIG_SERIAL_8250_FINTEK is not set
+# CONFIG_SERIAL_8250_FOURPORT is not set
+# CONFIG_SERIAL_8250_HUB6 is not set
+# CONFIG_SERIAL_8250_INGENIC is not set
+# CONFIG_SERIAL_8250_LPSS is not set
+# CONFIG_SERIAL_8250_MANY_PORTS is not set
+# CONFIG_SERIAL_8250_MID is not set
+# CONFIG_SERIAL_8250_MOXA is not set
+CONFIG_SERIAL_8250_NR_UARTS=2
+# CONFIG_SERIAL_8250_PCI is not set
+# CONFIG_SERIAL_8250_RSA is not set
+# CONFIG_SERIAL_8250_RT288X is not set
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_AMBA_PL010 is not set
+# CONFIG_SERIAL_AMBA_PL011 is not set
+# CONFIG_SERIAL_ARC is not set
+# CONFIG_SERIAL_BCM63XX is not set
+# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_DEV_BUS is not set
+CONFIG_SERIAL_EARLYCON=y
+# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set
+# CONFIG_SERIAL_FSL_LINFLEXUART is not set
+# CONFIG_SERIAL_FSL_LPUART is not set
+# CONFIG_SERIAL_GRLIB_GAISLER_APBUART is not set
+# CONFIG_SERIAL_IFX6X60 is not set
+# CONFIG_SERIAL_JSM is not set
+# CONFIG_SERIAL_MAX3100 is not set
+# CONFIG_SERIAL_MAX310X is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_SERIAL_OF_PLATFORM is not set
+# CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL is not set
+# CONFIG_SERIAL_PCH_UART is not set
+# CONFIG_SERIAL_RP2 is not set
+# CONFIG_SERIAL_SC16IS7XX is not set
+# CONFIG_SERIAL_SCCNXP is not set
+# CONFIG_SERIAL_SH_SCI is not set
+# CONFIG_SERIAL_SIFIVE is not set
+# CONFIG_SERIAL_SPRD is not set
+# CONFIG_SERIAL_STM32 is not set
+# CONFIG_SERIAL_ST_ASC is not set
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_UARTLITE is not set
+# CONFIG_SERIAL_XILINX_PS_UART is not set
+# CONFIG_SERIO is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_AMBAKMI is not set
+# CONFIG_SERIO_APBPS2 is not set
+# CONFIG_SERIO_ARC_PS2 is not set
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_GPIO_PS2 is not set
+# CONFIG_SERIO_I8042 is not set
+# CONFIG_SERIO_LIBPS2 is not set
+# CONFIG_SERIO_PARKBD is not set
+# CONFIG_SERIO_PCIPS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_SERIO_SUN4I_PS2 is not set
+# CONFIG_SFC is not set
+# CONFIG_SFC_FALCON is not set
+# CONFIG_SFI is not set
+# CONFIG_SFP is not set
+# CONFIG_SF_PDMA is not set
+# CONFIG_SGETMASK_SYSCALL is not set
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_SGI_IP22 is not set
+# CONFIG_SGI_IP27 is not set
+# CONFIG_SGI_IP28 is not set
+# CONFIG_SGI_IP30 is not set
+# CONFIG_SGI_IP32 is not set
+# CONFIG_SGI_MFD_IOC3 is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SG_POOL is not set
+# CONFIG_SG_SPLIT is not set
+CONFIG_SHMEM=y
+# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set
+# CONFIG_SH_ETH is not set
+# CONFIG_SH_TIMER_CMT is not set
+# CONFIG_SH_TIMER_MTU2 is not set
+# CONFIG_SH_TIMER_TMU is not set
+# CONFIG_SI1133 is not set
+# CONFIG_SI1145 is not set
+# CONFIG_SI7005 is not set
+# CONFIG_SI7020 is not set
+# CONFIG_SIBYTE_BIGSUR is not set
+# CONFIG_SIBYTE_CARMEL is not set
+# CONFIG_SIBYTE_CRHINE is not set
+# CONFIG_SIBYTE_CRHONE is not set
+# CONFIG_SIBYTE_LITTLESUR is not set
+# CONFIG_SIBYTE_RHONE is not set
+# CONFIG_SIBYTE_SENTOSA is not set
+# CONFIG_SIBYTE_SWARM is not set
+CONFIG_SIGNALFD=y
+# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set
+# CONFIG_SIMPLE_GPIO is not set
+# CONFIG_SIMPLE_PM_BUS is not set
+# CONFIG_SIOX is not set
+# CONFIG_SIS190 is not set
+# CONFIG_SIS900 is not set
+# CONFIG_SKGE is not set
+# CONFIG_SKY2 is not set
+# CONFIG_SKY2_DEBUG is not set
+# CONFIG_SLAB is not set
+CONFIG_SLABINFO=y
+# CONFIG_SLAB_FREELIST_HARDENED is not set
+# CONFIG_SLAB_FREELIST_RANDOM is not set
+CONFIG_SLAB_MERGE_DEFAULT=y
+# CONFIG_SLHC is not set
+# CONFIG_SLICOSS is not set
+# CONFIG_SLIMBUS is not set
+# CONFIG_SLIP is not set
+# CONFIG_SLOB is not set
+CONFIG_SLUB=y
+CONFIG_SLUB_CPU_PARTIAL=y
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_MEMCG_SYSFS_ON is not set
+# CONFIG_SLUB_STATS is not set
+# CONFIG_SMARTJOYPLUS_FF is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMC9194 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_SMP is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_SMSC9420 is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_SMS_SDIO_DRV is not set
+# CONFIG_SMS_USB_DRV is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_SND is not set
+# CONFIG_SND_AC97_POWER_SAVE is not set
+# CONFIG_SND_AD1816A is not set
+# CONFIG_SND_AD1848 is not set
+# CONFIG_SND_AD1889 is not set
+# CONFIG_SND_ADLIB is not set
+# CONFIG_SND_ALI5451 is not set
+# CONFIG_SND_ALOOP is not set
+# CONFIG_SND_ALS100 is not set
+# CONFIG_SND_ALS300 is not set
+# CONFIG_SND_ALS4000 is not set
+# CONFIG_SND_ARM is not set
+# CONFIG_SND_ASIHPI is not set
+# CONFIG_SND_ATIIXP is not set
+# CONFIG_SND_ATIIXP_MODEM is not set
+# CONFIG_SND_ATMEL_AC97C is not set
+# CONFIG_SND_ATMEL_SOC is not set
+# CONFIG_SND_AU8810 is not set
+# CONFIG_SND_AU8820 is not set
+# CONFIG_SND_AU8830 is not set
+# CONFIG_SND_AUDIO_GRAPH_CARD is not set
+# CONFIG_SND_AUDIO_GRAPH_SCU_CARD is not set
+# CONFIG_SND_AW2 is not set
+# CONFIG_SND_AZT2320 is not set
+# CONFIG_SND_AZT3328 is not set
+# CONFIG_SND_BCD2000 is not set
+# CONFIG_SND_BCM63XX_I2S_WHISTLER is not set
+# CONFIG_SND_BT87X is not set
+# CONFIG_SND_CA0106 is not set
+# CONFIG_SND_CMI8330 is not set
+# CONFIG_SND_CMIPCI is not set
+# CONFIG_SND_CS4231 is not set
+# CONFIG_SND_CS4236 is not set
+# CONFIG_SND_CS4281 is not set
+# CONFIG_SND_CS46XX is not set
+# CONFIG_SND_CS5530 is not set
+# CONFIG_SND_CS5535AUDIO is not set
+# CONFIG_SND_CTXFI is not set
+# CONFIG_SND_DARLA20 is not set
+# CONFIG_SND_DARLA24 is not set
+# CONFIG_SND_DEBUG is not set
+# CONFIG_SND_DESIGNWARE_I2S is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+# CONFIG_SND_ECHO3G is not set
+# CONFIG_SND_EDMA_SOC is not set
+# CONFIG_SND_EMU10K1 is not set
+# CONFIG_SND_EMU10K1X is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
+# CONFIG_SND_ENS1370 is not set
+# CONFIG_SND_ENS1371 is not set
+# CONFIG_SND_ES1688 is not set
+# CONFIG_SND_ES18XX is not set
+# CONFIG_SND_ES1938 is not set
+# CONFIG_SND_ES1968 is not set
+# CONFIG_SND_FIREWIRE is not set
+# CONFIG_SND_FM801 is not set
+# CONFIG_SND_GINA20 is not set
+# CONFIG_SND_GINA24 is not set
+# CONFIG_SND_GUSCLASSIC is not set
+# CONFIG_SND_GUSEXTREME is not set
+# CONFIG_SND_GUSMAX is not set
+# CONFIG_SND_HDA_INTEL is not set
+# CONFIG_SND_HDA_INTEL_DETECT_DMIC is not set
+# CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM is not set
+CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0
+CONFIG_SND_HDA_PREALLOC_SIZE=64
+# CONFIG_SND_HDSP is not set
+# CONFIG_SND_HDSPM is not set
+# CONFIG_SND_HRTIMER is not set
+# CONFIG_SND_HWDEP is not set
+# CONFIG_SND_I2S_HI6210_I2S is not set
+# CONFIG_SND_ICE1712 is not set
+# CONFIG_SND_ICE1724 is not set
+# CONFIG_SND_INDIGO is not set
+# CONFIG_SND_INDIGODJ is not set
+# CONFIG_SND_INDIGODJX is not set
+# CONFIG_SND_INDIGOIO is not set
+# CONFIG_SND_INDIGOIOX is not set
+# CONFIG_SND_INTEL8X0 is not set
+# CONFIG_SND_INTEL8X0M is not set
+# CONFIG_SND_INTERWAVE is not set
+# CONFIG_SND_INTERWAVE_STB is not set
+# CONFIG_SND_ISA is not set
+# CONFIG_SND_JZ4740_SOC_I2S is not set
+# CONFIG_SND_KIRKWOOD_SOC is not set
+# CONFIG_SND_KORG1212 is not set
+# CONFIG_SND_LAYLA20 is not set
+# CONFIG_SND_LAYLA24 is not set
+# CONFIG_SND_LOLA is not set
+# CONFIG_SND_LX6464ES is not set
+# CONFIG_SND_MAESTRO3 is not set
+CONFIG_SND_MAX_CARDS=16
+# CONFIG_SND_MIA is not set
+# CONFIG_SND_MIPS is not set
+# CONFIG_SND_MIRO is not set
+# CONFIG_SND_MIXART is not set
+# CONFIG_SND_MIXER_OSS is not set
+# CONFIG_SND_MONA is not set
+# CONFIG_SND_MPC52xx_SOC_EFIKA is not set
+# CONFIG_SND_MPU401 is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_MTS64 is not set
+# CONFIG_SND_MXS_SOC is not set
+# CONFIG_SND_NM256 is not set
+# CONFIG_SND_OPL3SA2 is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_OPTI92X_AD1848 is not set
+# CONFIG_SND_OPTI92X_CS4231 is not set
+# CONFIG_SND_OPTI93X is not set
+CONFIG_SND_OSSEMUL=y
+# CONFIG_SND_OXYGEN is not set
+CONFIG_SND_PCI=y
+# CONFIG_SND_PCM is not set
+# CONFIG_SND_PCMCIA is not set
+# CONFIG_SND_PCM_OSS is not set
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_PCM_TIMER is not set
+# CONFIG_SND_PCM_XRUN_DEBUG is not set
+# CONFIG_SND_PCXHR is not set
+# CONFIG_SND_PDAUDIOCF is not set
+# CONFIG_SND_PORTMAN2X4 is not set
+# CONFIG_SND_POWERPC_SOC is not set
+# CONFIG_SND_PPC is not set
+CONFIG_SND_PROC_FS=y
+# CONFIG_SND_RAWMIDI is not set
+# CONFIG_SND_RAWMIDI_SEQ is not set
+# CONFIG_SND_RIPTIDE is not set
+# CONFIG_SND_RME32 is not set
+# CONFIG_SND_RME96 is not set
+# CONFIG_SND_RME9652 is not set
+# CONFIG_SND_RTCTIMER is not set
+# CONFIG_SND_SB16 is not set
+# CONFIG_SND_SB8 is not set
+# CONFIG_SND_SBAWE is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_SE6X is not set
+# CONFIG_SND_SEQUENCER is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_SIMPLE_CARD is not set
+# CONFIG_SND_SIMPLE_SCU_CARD is not set
+# CONFIG_SND_SIS7019 is not set
+# CONFIG_SND_SOC is not set
+# CONFIG_SND_SOC_AC97_CODEC is not set
+# CONFIG_SND_SOC_ADAU1701 is not set
+# CONFIG_SND_SOC_ADAU1761_I2C is not set
+# CONFIG_SND_SOC_ADAU1761_SPI is not set
+# CONFIG_SND_SOC_ADAU7002 is not set
+# CONFIG_SND_SOC_ADAU7118_HW is not set
+# CONFIG_SND_SOC_ADAU7118_I2C is not set
+# CONFIG_SND_SOC_AK4104 is not set
+# CONFIG_SND_SOC_AK4118 is not set
+# CONFIG_SND_SOC_AK4458 is not set
+# CONFIG_SND_SOC_AK4554 is not set
+# CONFIG_SND_SOC_AK4613 is not set
+# CONFIG_SND_SOC_AK4642 is not set
+# CONFIG_SND_SOC_AK5386 is not set
+# CONFIG_SND_SOC_AK5558 is not set
+# CONFIG_SND_SOC_ALC5623 is not set
+# CONFIG_SND_SOC_AMD_ACP is not set
+# CONFIG_SND_SOC_AMD_ACP3x is not set
+# CONFIG_SND_SOC_AMD_RENOIR is not set
+# CONFIG_SND_SOC_AU1XAUDIO is not set
+# CONFIG_SND_SOC_AU1XPSC is not set
+# CONFIG_SND_SOC_BD28623 is not set
+# CONFIG_SND_SOC_BT_SCO is not set
+# CONFIG_SND_SOC_CS35L32 is not set
+# CONFIG_SND_SOC_CS35L33 is not set
+# CONFIG_SND_SOC_CS35L34 is not set
+# CONFIG_SND_SOC_CS35L35 is not set
+# CONFIG_SND_SOC_CS35L36 is not set
+# CONFIG_SND_SOC_CS4234 is not set
+# CONFIG_SND_SOC_CS4265 is not set
+# CONFIG_SND_SOC_CS4270 is not set
+# CONFIG_SND_SOC_CS4271 is not set
+# CONFIG_SND_SOC_CS4271_I2C is not set
+# CONFIG_SND_SOC_CS4271_SPI is not set
+# CONFIG_SND_SOC_CS42L42 is not set
+# CONFIG_SND_SOC_CS42L51_I2C is not set
+# CONFIG_SND_SOC_CS42L52 is not set
+# CONFIG_SND_SOC_CS42L56 is not set
+# CONFIG_SND_SOC_CS42L73 is not set
+# CONFIG_SND_SOC_CS42XX8_I2C is not set
+# CONFIG_SND_SOC_CS43130 is not set
+# CONFIG_SND_SOC_CS4341 is not set
+# CONFIG_SND_SOC_CS4349 is not set
+# CONFIG_SND_SOC_CS53L30 is not set
+# CONFIG_SND_SOC_CX2072X is not set
+# CONFIG_SND_SOC_DA7213 is not set
+# CONFIG_SND_SOC_DIO2125 is not set
+# CONFIG_SND_SOC_DMIC is not set
+# CONFIG_SND_SOC_ES7134 is not set
+# CONFIG_SND_SOC_ES7241 is not set
+# CONFIG_SND_SOC_ES8316 is not set
+# CONFIG_SND_SOC_ES8328 is not set
+# CONFIG_SND_SOC_ES8328_I2C is not set
+# CONFIG_SND_SOC_ES8328_SPI is not set
+# CONFIG_SND_SOC_EUKREA_TLV320 is not set
+# CONFIG_SND_SOC_FSL_ASOC_CARD is not set
+# CONFIG_SND_SOC_FSL_ASRC is not set
+# CONFIG_SND_SOC_FSL_AUDMIX is not set
+# CONFIG_SND_SOC_FSL_ESAI is not set
+# CONFIG_SND_SOC_FSL_MICFIL is not set
+# CONFIG_SND_SOC_FSL_SAI is not set
+# CONFIG_SND_SOC_FSL_SPDIF is not set
+# CONFIG_SND_SOC_FSL_SSI is not set
+# CONFIG_SND_SOC_GTM601 is not set
+# CONFIG_SND_SOC_ICS43432 is not set
+# CONFIG_SND_SOC_IMG is not set
+# CONFIG_SND_SOC_IMX_AUDMIX is not set
+# CONFIG_SND_SOC_IMX_AUDMUX is not set
+# CONFIG_SND_SOC_IMX_ES8328 is not set
+# CONFIG_SND_SOC_IMX_SPDIF is not set
+# CONFIG_SND_SOC_IMX_WM8962 is not set
+# CONFIG_SND_SOC_INNO_RK3036 is not set
+# CONFIG_SND_SOC_INTEL_APL is not set
+# CONFIG_SND_SOC_INTEL_BAYTRAIL is not set
+# CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH is not set
+# CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH is not set
+# CONFIG_SND_SOC_INTEL_BXT_RT298_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH is not set
+# CONFIG_SND_SOC_INTEL_CATPT is not set
+# CONFIG_SND_SOC_INTEL_CFL is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH is not set
+# CONFIG_SND_SOC_INTEL_CML_H is not set
+# CONFIG_SND_SOC_INTEL_CML_LP is not set
+# CONFIG_SND_SOC_INTEL_CNL is not set
+# CONFIG_SND_SOC_INTEL_GLK is not set
+# CONFIG_SND_SOC_INTEL_HASWELL is not set
+# CONFIG_SND_SOC_INTEL_KBL is not set
+# CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH is not set
+# CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH is not set
+# CONFIG_SND_SOC_INTEL_KEEMBAY is not set
+# CONFIG_SND_SOC_INTEL_SKL is not set
+# CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH is not set
+# CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH is not set
+# CONFIG_SND_SOC_INTEL_SKL_RT286_MACH is not set
+# CONFIG_SND_SOC_INTEL_SKYLAKE is not set
+# CONFIG_SND_SOC_INTEL_SST is not set
+CONFIG_SND_SOC_INTEL_SST_TOPLEVEL=y
+# CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES is not set
+# CONFIG_SND_SOC_JZ4725B_CODEC is not set
+# CONFIG_SND_SOC_JZ4740_CODEC is not set
+# CONFIG_SND_SOC_JZ4770_CODEC is not set
+# CONFIG_SND_SOC_MA120X0P is not set
+# CONFIG_SND_SOC_MAX9759 is not set
+# CONFIG_SND_SOC_MAX98088 is not set
+# CONFIG_SND_SOC_MAX98357A is not set
+# CONFIG_SND_SOC_MAX98373 is not set
+# CONFIG_SND_SOC_MAX98373_I2C is not set
+# CONFIG_SND_SOC_MAX98390 is not set
+# CONFIG_SND_SOC_MAX98504 is not set
+# CONFIG_SND_SOC_MAX9860 is not set
+# CONFIG_SND_SOC_MAX9867 is not set
+# CONFIG_SND_SOC_MAX98927 is not set
+# CONFIG_SND_SOC_MEDIATEK is not set
+# CONFIG_SND_SOC_MPC5200_AC97 is not set
+# CONFIG_SND_SOC_MPC5200_I2S is not set
+# CONFIG_SND_SOC_MSM8916_WCD_ANALOG is not set
+# CONFIG_SND_SOC_MSM8916_WCD_DIGITAL is not set
+# CONFIG_SND_SOC_MT2701 is not set
+# CONFIG_SND_SOC_MT6351 is not set
+# CONFIG_SND_SOC_MT6358 is not set
+# CONFIG_SND_SOC_MT6660 is not set
+# CONFIG_SND_SOC_MT6797 is not set
+# CONFIG_SND_SOC_MT8173 is not set
+# CONFIG_SND_SOC_MT8183 is not set
+# CONFIG_SND_SOC_MTK_BTCVSD is not set
+# CONFIG_SND_SOC_NAU8540 is not set
+# CONFIG_SND_SOC_NAU8810 is not set
+# CONFIG_SND_SOC_NAU8822 is not set
+# CONFIG_SND_SOC_NAU8824 is not set
+# CONFIG_SND_SOC_PCM1681 is not set
+# CONFIG_SND_SOC_PCM1789_I2C is not set
+# CONFIG_SND_SOC_PCM1792A is not set
+# CONFIG_SND_SOC_PCM179X_I2C is not set
+# CONFIG_SND_SOC_PCM179X_SPI is not set
+# CONFIG_SND_SOC_PCM186X_I2C is not set
+# CONFIG_SND_SOC_PCM186X_SPI is not set
+# CONFIG_SND_SOC_PCM3060_I2C is not set
+# CONFIG_SND_SOC_PCM3060_SPI is not set
+# CONFIG_SND_SOC_PCM3168A_I2C is not set
+# CONFIG_SND_SOC_PCM3168A_SPI is not set
+# CONFIG_SND_SOC_PCM512x_I2C is not set
+# CONFIG_SND_SOC_PCM512x_SPI is not set
+# CONFIG_SND_SOC_QCOM is not set
+# CONFIG_SND_SOC_RK3328 is not set
+# CONFIG_SND_SOC_RT5616 is not set
+# CONFIG_SND_SOC_RT5631 is not set
+# CONFIG_SND_SOC_RT5677_SPI is not set
+# CONFIG_SND_SOC_SGTL5000 is not set
+# CONFIG_SND_SOC_SIMPLE_AMPLIFIER is not set
+# CONFIG_SND_SOC_SIRF_AUDIO_CODEC is not set
+# CONFIG_SND_SOC_SOF_TOPLEVEL is not set
+# CONFIG_SND_SOC_SPDIF is not set
+# CONFIG_SND_SOC_SSM2305 is not set
+# CONFIG_SND_SOC_SSM2602_I2C is not set
+# CONFIG_SND_SOC_SSM2602_SPI is not set
+# CONFIG_SND_SOC_SSM4567 is not set
+# CONFIG_SND_SOC_STA32X is not set
+# CONFIG_SND_SOC_STA350 is not set
+# CONFIG_SND_SOC_STI_SAS is not set
+# CONFIG_SND_SOC_TAS2552 is not set
+# CONFIG_SND_SOC_TAS2562 is not set
+# CONFIG_SND_SOC_TAS2764 is not set
+# CONFIG_SND_SOC_TAS2770 is not set
+# CONFIG_SND_SOC_TAS5086 is not set
+# CONFIG_SND_SOC_TAS571X is not set
+# CONFIG_SND_SOC_TAS5720 is not set
+# CONFIG_SND_SOC_TAS6424 is not set
+# CONFIG_SND_SOC_TDA7419 is not set
+# CONFIG_SND_SOC_TFA9879 is not set
+# CONFIG_SND_SOC_TLV320ADCX140 is not set
+# CONFIG_SND_SOC_TLV320AIC23_I2C is not set
+# CONFIG_SND_SOC_TLV320AIC23_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC31XX is not set
+# CONFIG_SND_SOC_TLV320AIC32X4_I2C is not set
+# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC3X is not set
+# CONFIG_SND_SOC_TPA6130A2 is not set
+# CONFIG_SND_SOC_TS3A227E is not set
+# CONFIG_SND_SOC_TSCS42XX is not set
+# CONFIG_SND_SOC_TSCS454 is not set
+# CONFIG_SND_SOC_UDA1334 is not set
+# CONFIG_SND_SOC_WM8510 is not set
+# CONFIG_SND_SOC_WM8523 is not set
+# CONFIG_SND_SOC_WM8524 is not set
+# CONFIG_SND_SOC_WM8580 is not set
+# CONFIG_SND_SOC_WM8711 is not set
+# CONFIG_SND_SOC_WM8728 is not set
+# CONFIG_SND_SOC_WM8731 is not set
+# CONFIG_SND_SOC_WM8737 is not set
+# CONFIG_SND_SOC_WM8741 is not set
+# CONFIG_SND_SOC_WM8750 is not set
+# CONFIG_SND_SOC_WM8753 is not set
+# CONFIG_SND_SOC_WM8770 is not set
+# CONFIG_SND_SOC_WM8776 is not set
+# CONFIG_SND_SOC_WM8782 is not set
+# CONFIG_SND_SOC_WM8804_I2C is not set
+# CONFIG_SND_SOC_WM8804_SPI is not set
+# CONFIG_SND_SOC_WM8903 is not set
+# CONFIG_SND_SOC_WM8904 is not set
+# CONFIG_SND_SOC_WM8960 is not set
+# CONFIG_SND_SOC_WM8962 is not set
+# CONFIG_SND_SOC_WM8974 is not set
+# CONFIG_SND_SOC_WM8978 is not set
+# CONFIG_SND_SOC_WM8985 is not set
+# CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER is not set
+# CONFIG_SND_SOC_XILINX_I2S is not set
+# CONFIG_SND_SOC_XILINX_SPDIF is not set
+# CONFIG_SND_SOC_XTFPGA_I2S is not set
+# CONFIG_SND_SOC_ZL38060 is not set
+# CONFIG_SND_SOC_ZX_AUD96P22 is not set
+# CONFIG_SND_SONICVIBES is not set
+# CONFIG_SND_SPI is not set
+# CONFIG_SND_SSCAPE is not set
+# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI is not set
+# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set
+# CONFIG_SND_SUN4I_CODEC is not set
+# CONFIG_SND_SUPPORT_OLD_API is not set
+# CONFIG_SND_TIMER is not set
+# CONFIG_SND_TRIDENT is not set
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_6FIRE is not set
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+# CONFIG_SND_USB_HIFACE is not set
+# CONFIG_SND_USB_POD is not set
+# CONFIG_SND_USB_PODHD is not set
+# CONFIG_SND_USB_TONEPORT is not set
+# CONFIG_SND_USB_UA101 is not set
+# CONFIG_SND_USB_US122L is not set
+# CONFIG_SND_USB_USX2Y is not set
+# CONFIG_SND_USB_VARIAX is not set
+# CONFIG_SND_VERBOSE_PRINTK is not set
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VIA82XX is not set
+# CONFIG_SND_VIA82XX_MODEM is not set
+# CONFIG_SND_VIRTUOSO is not set
+# CONFIG_SND_VX222 is not set
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_WAVEFRONT is not set
+CONFIG_SND_X86=y
+# CONFIG_SND_XEN_FRONTEND is not set
+# CONFIG_SND_YMFPCI is not set
+# CONFIG_SNI_RM is not set
+# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set
+# CONFIG_SOCK_CGROUP_DATA is not set
+# CONFIG_SOC_AM33XX is not set
+# CONFIG_SOC_AM43XX is not set
+# CONFIG_SOC_BRCMSTB is not set
+# CONFIG_SOC_CAMERA is not set
+# CONFIG_SOC_DRA7XX is not set
+# CONFIG_SOC_HAS_OMAP2_SDRC is not set
+# CONFIG_SOC_OMAP5 is not set
+# CONFIG_SOC_TI is not set
+# CONFIG_SOFTLOCKUP_DETECTOR is not set
+# CONFIG_SOFT_WATCHDOG is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_SONYPI is not set
+# CONFIG_SONY_LAPTOP is not set
+# CONFIG_SOUND is not set
+# CONFIG_SOUNDWIRE is not set
+# CONFIG_SOUND_OSS_CORE is not set
+# CONFIG_SOUND_OSS_CORE_PRECLAIM is not set
+# CONFIG_SOUND_PRIME is not set
+# CONFIG_SP5100_TCO is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+# CONFIG_SPARSE_IRQ is not set
+# CONFIG_SPARSE_RCU_POINTER is not set
+# CONFIG_SPEAKUP is not set
+# CONFIG_SPI is not set
+# CONFIG_SPINLOCK_TEST is not set
+# CONFIG_SPI_ALTERA is not set
+# CONFIG_SPI_AMD is not set
+# CONFIG_SPI_AU1550 is not set
+# CONFIG_SPI_AXI_SPI_ENGINE is not set
+# CONFIG_SPI_BCM2835 is not set
+# CONFIG_SPI_BCM_QSPI is not set
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_BUTTERFLY is not set
+# CONFIG_SPI_CADENCE is not set
+# CONFIG_SPI_CADENCE_QUADSPI is not set
+# CONFIG_SPI_DEBUG is not set
+# CONFIG_SPI_DESIGNWARE is not set
+# CONFIG_SPI_FSL_DSPI is not set
+# CONFIG_SPI_FSL_ESPI is not set
+# CONFIG_SPI_FSL_SPI is not set
+# CONFIG_SPI_GPIO is not set
+# CONFIG_SPI_GPIO_OLD is not set
+# CONFIG_SPI_IMG_SPFI is not set
+# CONFIG_SPI_LANTIQ_SSC is not set
+# CONFIG_SPI_LM70_LLP is not set
+# CONFIG_SPI_LOOPBACK_TEST is not set
+# CONFIG_SPI_MASTER is not set
+# CONFIG_SPI_MEM is not set
+# CONFIG_SPI_MPC52xx is not set
+# CONFIG_SPI_MPC52xx_PSC is not set
+# CONFIG_SPI_MTK_QUADSPI is not set
+# CONFIG_SPI_MUX is not set
+# CONFIG_SPI_MXIC is not set
+# CONFIG_SPI_NXP_FLEXSPI is not set
+# CONFIG_SPI_OCTEON is not set
+# CONFIG_SPI_OC_TINY is not set
+# CONFIG_SPI_ORION is not set
+# CONFIG_SPI_PL022 is not set
+# CONFIG_SPI_PPC4xx is not set
+# CONFIG_SPI_PXA2XX is not set
+# CONFIG_SPI_PXA2XX_PCI is not set
+# CONFIG_SPI_QCOM_QSPI is not set
+# CONFIG_SPI_ROCKCHIP is not set
+# CONFIG_SPI_S3C64XX is not set
+# CONFIG_SPI_SC18IS602 is not set
+# CONFIG_SPI_SIFIVE is not set
+# CONFIG_SPI_SLAVE is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_THUNDERX is not set
+# CONFIG_SPI_TI_QSPI is not set
+# CONFIG_SPI_TLE62X0 is not set
+# CONFIG_SPI_TOPCLIFF_PCH is not set
+# CONFIG_SPI_XCOMM is not set
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_XWAY is not set
+# CONFIG_SPI_ZYNQMP_GQSPI is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_SPMI is not set
+# CONFIG_SPS30 is not set
+CONFIG_SQUASHFS=y
+# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set
+# CONFIG_SQUASHFS_DECOMP_MULTI is not set
+CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
+# CONFIG_SQUASHFS_DECOMP_SINGLE is not set
+CONFIG_SQUASHFS_EMBEDDED=y
+# CONFIG_SQUASHFS_FILE_CACHE is not set
+CONFIG_SQUASHFS_FILE_DIRECT=y
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+# CONFIG_SQUASHFS_LZ4 is not set
+# CONFIG_SQUASHFS_LZO is not set
+# CONFIG_SQUASHFS_XATTR is not set
+CONFIG_SQUASHFS_XZ=y
+# CONFIG_SQUASHFS_ZLIB is not set
+# CONFIG_SQUASHFS_ZSTD is not set
+# CONFIG_SRAM is not set
+# CONFIG_SRF04 is not set
+# CONFIG_SRF08 is not set
+# CONFIG_SSB is not set
+# CONFIG_SSB_DEBUG is not set
+# CONFIG_SSB_DRIVER_GPIO is not set
+# CONFIG_SSB_HOST_SOC is not set
+# CONFIG_SSB_PCMCIAHOST is not set
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB_SDIOHOST is not set
+# CONFIG_SSB_SILENT is not set
+# CONFIG_SSFDC is not set
+# CONFIG_STACKPROTECTOR is not set
+# CONFIG_STACKPROTECTOR_STRONG is not set
+# CONFIG_STACKTRACE is not set
+CONFIG_STACKTRACE_SUPPORT=y
+# CONFIG_STACK_TRACER is not set
+# CONFIG_STACK_VALIDATION is not set
+CONFIG_STAGING=y
+# CONFIG_STAGING_BOARD is not set
+# CONFIG_STAGING_GASKET_FRAMEWORK is not set
+# CONFIG_STAGING_MEDIA is not set
+CONFIG_STANDALONE=y
+# CONFIG_STATIC_KEYS_SELFTEST is not set
+# CONFIG_STATIC_USERMODEHELPER is not set
+CONFIG_STDBINUTILS=y
+# CONFIG_STE10XP is not set
+# CONFIG_STE_MODEM_RPROC is not set
+# CONFIG_STK3310 is not set
+# CONFIG_STK8312 is not set
+# CONFIG_STK8BA50 is not set
+# CONFIG_STM is not set
+# CONFIG_STMMAC_ETH is not set
+# CONFIG_STMMAC_PCI is not set
+# CONFIG_STMMAC_PLATFORM is not set
+# CONFIG_STM_DUMMY is not set
+# CONFIG_STM_SOURCE_CONSOLE is not set
+CONFIG_STP=y
+# CONFIG_STREAM_PARSER is not set
+# CONFIG_STRICT_DEVMEM is not set
+CONFIG_STRICT_KERNEL_RWX=y
+CONFIG_STRICT_MODULE_RWX=y
+# CONFIG_STRING_SELFTEST is not set
+CONFIG_STRIP_ASM_SYMS=y
+# CONFIG_STX104 is not set
+# CONFIG_ST_UVIS25 is not set
+# CONFIG_SUN4I_GPADC is not set
+# CONFIG_SUN50I_DE2_BUS is not set
+# CONFIG_SUN50I_ERRATUM_UNKNOWN1 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_SUNRPC is not set
+# CONFIG_SUNRPC_DEBUG is not set
+CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES=y
+# CONFIG_SUNRPC_GSS is not set
+# CONFIG_SUNXI_SRAM is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_SURFACE_3_BUTTON is not set
+# CONFIG_SUSPEND is not set
+# CONFIG_SUSPEND_SKIP_SYNC is not set
+CONFIG_SWAP=y
+# CONFIG_SWCONFIG is not set
+# CONFIG_SWCONFIG_B53 is not set
+# CONFIG_SWCONFIG_B53_MDIO_DRIVER is not set
+# CONFIG_SWCONFIG_B53_MMAP_DRIVER is not set
+# CONFIG_SWCONFIG_B53_SPI_DRIVER is not set
+# CONFIG_SWCONFIG_B53_SRAB_DRIVER is not set
+# CONFIG_SWCONFIG_LEDS is not set
+# CONFIG_SW_SYNC is not set
+# CONFIG_SX9310 is not set
+# CONFIG_SX9500 is not set
+# CONFIG_SXGBE_ETH is not set
+CONFIG_SYMBOLIC_ERRNAME=y
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_SYNC_FILE is not set
+# CONFIG_SYNOPSYS_DWC_ETH_QOS is not set
+# CONFIG_SYNTH_EVENTS is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_SYSCON_REBOOT_MODE is not set
+CONFIG_SYSCTL=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_SYSFS=y
+# CONFIG_SYSFS_DEPRECATED is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+# CONFIG_SYSFS_SYSCALL is not set
+# CONFIG_SYSTEMPORT is not set
+# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set
+# CONFIG_SYSTEM_DATA_VERIFICATION is not set
+# CONFIG_SYSTEM_TRUSTED_KEYRING is not set
+CONFIG_SYSTEM_TRUSTED_KEYS=""
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_T5403 is not set
+# CONFIG_TARGET_CORE is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_TASKS_RCU is not set
+# CONFIG_TASK_XACCT is not set
+# CONFIG_TC35815 is not set
+# CONFIG_TCG_ATMEL is not set
+# CONFIG_TCG_CRB is not set
+# CONFIG_TCG_FTPM_TEE is not set
+# CONFIG_TCG_INFINEON is not set
+# CONFIG_TCG_NSC is not set
+# CONFIG_TCG_ST33_I2C is not set
+# CONFIG_TCG_TIS is not set
+# CONFIG_TCG_TIS_I2C_ATMEL is not set
+# CONFIG_TCG_TIS_I2C_INFINEON is not set
+# CONFIG_TCG_TIS_I2C_NUVOTON is not set
+# CONFIG_TCG_TIS_SPI is not set
+# CONFIG_TCG_TIS_ST33ZP24_I2C is not set
+# CONFIG_TCG_TIS_ST33ZP24_SPI is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_TCG_VTPM_PROXY is not set
+# CONFIG_TCG_XEN is not set
+# CONFIG_TCIC is not set
+CONFIG_TCP_CONG_ADVANCED=y
+# CONFIG_TCP_CONG_BBR is not set
+# CONFIG_TCP_CONG_BIC is not set
+# CONFIG_TCP_CONG_CDG is not set
+CONFIG_TCP_CONG_CUBIC=y
+# CONFIG_TCP_CONG_DCTCP is not set
+# CONFIG_TCP_CONG_HSTCP is not set
+# CONFIG_TCP_CONG_HTCP is not set
+# CONFIG_TCP_CONG_HYBLA is not set
+# CONFIG_TCP_CONG_ILLINOIS is not set
+# CONFIG_TCP_CONG_LP is not set
+# CONFIG_TCP_CONG_NV is not set
+# CONFIG_TCP_CONG_SCALABLE is not set
+# CONFIG_TCP_CONG_VEGAS is not set
+# CONFIG_TCP_CONG_VENO is not set
+# CONFIG_TCP_CONG_WESTWOOD is not set
+# CONFIG_TCP_CONG_YEAH is not set
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_TCS3414 is not set
+# CONFIG_TCS3472 is not set
+# CONFIG_TEE is not set
+# CONFIG_TEGRA_AHB is not set
+# CONFIG_TEGRA_HOST1X is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_TERANETICS_PHY is not set
+# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set
+# CONFIG_TEST_BITFIELD is not set
+# CONFIG_TEST_BITMAP is not set
+# CONFIG_TEST_BITOPS is not set
+# CONFIG_TEST_BLACKHOLE_DEV is not set
+# CONFIG_TEST_BPF is not set
+# CONFIG_TEST_FIRMWARE is not set
+# CONFIG_TEST_FREE_PAGES is not set
+# CONFIG_TEST_HASH is not set
+# CONFIG_TEST_HEXDUMP is not set
+# CONFIG_TEST_IDA is not set
+# CONFIG_TEST_KASAN_MODULE is not set
+# CONFIG_TEST_KMOD is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_TEST_LKM is not set
+# CONFIG_TEST_LOCKUP is not set
+# CONFIG_TEST_MEMCAT_P is not set
+# CONFIG_TEST_MEMINIT is not set
+# CONFIG_TEST_MIN_HEAP is not set
+# CONFIG_TEST_OVERFLOW is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_TEST_PRINTF is not set
+# CONFIG_TEST_RHASHTABLE is not set
+# CONFIG_TEST_SORT is not set
+# CONFIG_TEST_STACKINIT is not set
+# CONFIG_TEST_STATIC_KEYS is not set
+# CONFIG_TEST_STRING_HELPERS is not set
+# CONFIG_TEST_STRSCPY is not set
+# CONFIG_TEST_SYSCTL is not set
+# CONFIG_TEST_UBSAN is not set
+# CONFIG_TEST_UDELAY is not set
+# CONFIG_TEST_USER_COPY is not set
+# CONFIG_TEST_UUID is not set
+# CONFIG_TEST_VMALLOC is not set
+# CONFIG_TEST_XARRAY is not set
+CONFIG_TEXTSEARCH=y
+# CONFIG_TEXTSEARCH_BM is not set
+# CONFIG_TEXTSEARCH_FSM is not set
+# CONFIG_TEXTSEARCH_KMP is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
+# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set
+# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_EMULATION is not set
+# CONFIG_THERMAL_GOV_BANG_BANG is not set
+# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
+# CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set
+# CONFIG_THERMAL_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_THERMAL_MMIO is not set
+# CONFIG_THERMAL_NETLINK is not set
+# CONFIG_THERMAL_STATISTICS is not set
+# CONFIG_THERMAL_WRITABLE_TRIPS is not set
+# CONFIG_THINKPAD_ACPI is not set
+CONFIG_THIN_ARCHIVES=y
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_THUMB2_KERNEL is not set
+# CONFIG_THUNDERBOLT is not set
+# CONFIG_THUNDER_NIC_BGX is not set
+# CONFIG_THUNDER_NIC_PF is not set
+# CONFIG_THUNDER_NIC_RGX is not set
+# CONFIG_THUNDER_NIC_VF is not set
+# CONFIG_TICK_CPU_ACCOUNTING is not set
+CONFIG_TICK_ONESHOT=y
+# CONFIG_TIFM_CORE is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_TIMERFD=y
+# CONFIG_TIMER_STATS is not set
+# CONFIG_TIME_NS is not set
+# CONFIG_TINYDRM_HX8357D is not set
+# CONFIG_TINYDRM_ILI9225 is not set
+# CONFIG_TINYDRM_ILI9341 is not set
+# CONFIG_TINYDRM_ILI9486 is not set
+# CONFIG_TINYDRM_MI0283QT is not set
+# CONFIG_TINYDRM_REPAPER is not set
+# CONFIG_TINYDRM_ST7586 is not set
+# CONFIG_TINYDRM_ST7735R is not set
+CONFIG_TINY_RCU=y
+# CONFIG_TIPC is not set
+# CONFIG_TI_ADC081C is not set
+# CONFIG_TI_ADC0832 is not set
+# CONFIG_TI_ADC084S021 is not set
+# CONFIG_TI_ADC108S102 is not set
+# CONFIG_TI_ADC12138 is not set
+# CONFIG_TI_ADC128S052 is not set
+# CONFIG_TI_ADC161S626 is not set
+# CONFIG_TI_ADS1015 is not set
+# CONFIG_TI_ADS124S08 is not set
+# CONFIG_TI_ADS7950 is not set
+# CONFIG_TI_ADS8344 is not set
+# CONFIG_TI_ADS8688 is not set
+# CONFIG_TI_AM335X_ADC is not set
+# CONFIG_TI_CPSW is not set
+# CONFIG_TI_CPSW_ALE is not set
+# CONFIG_TI_CPSW_PHY_SEL is not set
+# CONFIG_TI_CPTS is not set
+# CONFIG_TI_DAC082S085 is not set
+# CONFIG_TI_DAC5571 is not set
+# CONFIG_TI_DAC7311 is not set
+# CONFIG_TI_DAC7512 is not set
+# CONFIG_TI_DAC7612 is not set
+# CONFIG_TI_DAVINCI_CPDMA is not set
+# CONFIG_TI_DAVINCI_MDIO is not set
+# CONFIG_TI_ST is not set
+# CONFIG_TI_SYSCON_RESET is not set
+# CONFIG_TI_TLC4541 is not set
+# CONFIG_TLAN is not set
+# CONFIG_TLS is not set
+# CONFIG_TMD_HERMES is not set
+# CONFIG_TMP006 is not set
+# CONFIG_TMP007 is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_INODE64 is not set
+# CONFIG_TMPFS_POSIX_ACL is not set
+CONFIG_TMPFS_XATTR=y
+# CONFIG_TOPSTAR_LAPTOP is not set
+# CONFIG_TORTURE_TEST is not set
+# CONFIG_TOSHIBA_HAPS is not set
+# CONFIG_TOUCHSCREEN_88PM860X is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_AD7879_I2C is not set
+# CONFIG_TOUCHSCREEN_AD7879_SPI is not set
+# CONFIG_TOUCHSCREEN_ADC is not set
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AR1021_I2C is not set
+# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
+# CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 is not set
+# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_BU21013 is not set
+# CONFIG_TOUCHSCREEN_BU21029 is not set
+# CONFIG_TOUCHSCREEN_CHIPONE_ICN8318 is not set
+# CONFIG_TOUCHSCREEN_CHIPONE_ICN8505 is not set
+# CONFIG_TOUCHSCREEN_COLIBRI_VF50 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMA140 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_I2C is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_SPI is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_SPI is not set
+# CONFIG_TOUCHSCREEN_DA9034 is not set
+# CONFIG_TOUCHSCREEN_DA9052 is not set
+# CONFIG_TOUCHSCREEN_DYNAPRO is not set
+# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
+# CONFIG_TOUCHSCREEN_EGALAX is not set
+# CONFIG_TOUCHSCREEN_EGALAX_SERIAL is not set
+# CONFIG_TOUCHSCREEN_EKTF2127 is not set
+# CONFIG_TOUCHSCREEN_ELAN is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_EXC3000 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GOODIX is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
+# CONFIG_TOUCHSCREEN_HIDEEP is not set
+# CONFIG_TOUCHSCREEN_HP600 is not set
+# CONFIG_TOUCHSCREEN_HP7XX is not set
+# CONFIG_TOUCHSCREEN_HTCPEN is not set
+# CONFIG_TOUCHSCREEN_ILI210X is not set
+# CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_IPAQ_MICRO is not set
+# CONFIG_TOUCHSCREEN_IPROC is not set
+# CONFIG_TOUCHSCREEN_IQS5XX is not set
+# CONFIG_TOUCHSCREEN_LPC32XX is not set
+# CONFIG_TOUCHSCREEN_MAX11801 is not set
+# CONFIG_TOUCHSCREEN_MC13783 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
+# CONFIG_TOUCHSCREEN_MELFAS_MIP4 is not set
+# CONFIG_TOUCHSCREEN_MIGOR is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_MMS114 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MX25 is not set
+# CONFIG_TOUCHSCREEN_MXS_LRADC is not set
+# CONFIG_TOUCHSCREEN_PCAP is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_PROPERTIES is not set
+# CONFIG_TOUCHSCREEN_RASPBERRYPI_FW is not set
+# CONFIG_TOUCHSCREEN_RM_TS is not set
+# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set
+# CONFIG_TOUCHSCREEN_RPI_FT5406 is not set
+# CONFIG_TOUCHSCREEN_S3C2410 is not set
+# CONFIG_TOUCHSCREEN_S6SY761 is not set
+# CONFIG_TOUCHSCREEN_SILEAD is not set
+# CONFIG_TOUCHSCREEN_SIS_I2C is not set
+# CONFIG_TOUCHSCREEN_ST1232 is not set
+# CONFIG_TOUCHSCREEN_STMFTS is not set
+# CONFIG_TOUCHSCREEN_STMPE is not set
+# CONFIG_TOUCHSCREEN_SUN4I is not set
+# CONFIG_TOUCHSCREEN_SUR40 is not set
+# CONFIG_TOUCHSCREEN_SURFACE3_SPI is not set
+# CONFIG_TOUCHSCREEN_SX8654 is not set
+# CONFIG_TOUCHSCREEN_TI_AM335X_TSC is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_TPS6507X is not set
+# CONFIG_TOUCHSCREEN_TS4800 is not set
+# CONFIG_TOUCHSCREEN_TSC2004 is not set
+# CONFIG_TOUCHSCREEN_TSC2005 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_TSC2007_IIO is not set
+# CONFIG_TOUCHSCREEN_TSC200X_CORE is not set
+# CONFIG_TOUCHSCREEN_TSC_SERIO is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_USB_3M is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_USB_DMC_TSC10 is not set
+# CONFIG_TOUCHSCREEN_USB_E2I is not set
+# CONFIG_TOUCHSCREEN_USB_EASYTOUCH is not set
+# CONFIG_TOUCHSCREEN_USB_EGALAX is not set
+# CONFIG_TOUCHSCREEN_USB_ELO is not set
+# CONFIG_TOUCHSCREEN_USB_ETT_TC45USB is not set
+# CONFIG_TOUCHSCREEN_USB_ETURBO is not set
+# CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH is not set
+# CONFIG_TOUCHSCREEN_USB_GOTOP is not set
+# CONFIG_TOUCHSCREEN_USB_GUNZE is not set
+# CONFIG_TOUCHSCREEN_USB_IDEALTEK is not set
+# CONFIG_TOUCHSCREEN_USB_IRTOUCH is not set
+# CONFIG_TOUCHSCREEN_USB_ITM is not set
+# CONFIG_TOUCHSCREEN_USB_JASTEC is not set
+# CONFIG_TOUCHSCREEN_USB_NEXIO is not set
+# CONFIG_TOUCHSCREEN_USB_PANJIT is not set
+# CONFIG_TOUCHSCREEN_USB_ZYTRONIC is not set
+# CONFIG_TOUCHSCREEN_W90X900 is not set
+# CONFIG_TOUCHSCREEN_WACOM_I2C is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set
+# CONFIG_TOUCHSCREEN_WM831X is not set
+# CONFIG_TOUCHSCREEN_WM9705 is not set
+# CONFIG_TOUCHSCREEN_WM9712 is not set
+# CONFIG_TOUCHSCREEN_WM9713 is not set
+# CONFIG_TOUCHSCREEN_WM97XX is not set
+# CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE is not set
+# CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE is not set
+# CONFIG_TOUCHSCREEN_ZET6223 is not set
+# CONFIG_TOUCHSCREEN_ZFORCE is not set
+# CONFIG_TOUCHSCREEN_ZINITIX is not set
+# CONFIG_TPL0102 is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_TRACEPOINT_BENCHMARK is not set
+# CONFIG_TRACER_SNAPSHOT is not set
+# CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP is not set
+# CONFIG_TRACE_BRANCH_PROFILING is not set
+# CONFIG_TRACE_EVAL_MAP_FILE is not set
+# CONFIG_TRACE_EVENT_INJECT is not set
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_TRACE_SINK is not set
+# CONFIG_TRACING_EVENTS_GPIO is not set
+CONFIG_TRACING_SUPPORT=y
+CONFIG_TRAD_SIGNALS=y
+# CONFIG_TRANSPARENT_HUGEPAGE is not set
+# CONFIG_TREE_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_TRIM_UNUSED_KSYMS is not set
+# CONFIG_TRUSTED_FOUNDATIONS is not set
+# CONFIG_TRUSTED_KEYS is not set
+# CONFIG_TSL2583 is not set
+# CONFIG_TSL2772 is not set
+# CONFIG_TSL2x7x is not set
+# CONFIG_TSL4531 is not set
+# CONFIG_TSYS01 is not set
+# CONFIG_TSYS02D is not set
+# CONFIG_TTPCI_EEPROM is not set
+CONFIG_TTY=y
+# CONFIG_TTY_PRINTK is not set
+# CONFIG_TUN is not set
+# CONFIG_TUN_VNET_CROSS_LE is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_TWL4030_MADC is not set
+# CONFIG_TWL6030_GPADC is not set
+# CONFIG_TWL6040_CORE is not set
+# CONFIG_TYPEC is not set
+# CONFIG_TYPEC_TCPM is not set
+# CONFIG_TYPEC_UCSI is not set
+# CONFIG_TYPHOON is not set
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+# CONFIG_UBIFS_ATIME_SUPPORT is not set
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+# CONFIG_UBIFS_FS_AUTHENTICATION is not set
+# CONFIG_UBIFS_FS_ENCRYPTION is not set
+CONFIG_UBIFS_FS_LZO=y
+# CONFIG_UBIFS_FS_SECURITY is not set
+CONFIG_UBIFS_FS_XATTR=y
+CONFIG_UBIFS_FS_ZLIB=y
+CONFIG_UBIFS_FS_ZSTD=y
+# CONFIG_UBSAN is not set
+CONFIG_UBSAN_ALIGNMENT=y
+# CONFIG_UBSAN_MISC is not set
+# CONFIG_UCB1400_CORE is not set
+# CONFIG_UCSI is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDMABUF is not set
+CONFIG_UEVENT_HELPER=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_UFS_FS is not set
+# CONFIG_UHID is not set
+CONFIG_UID16=y
+# CONFIG_UIO is not set
+# CONFIG_ULTRA is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_UNICODE is not set
+# CONFIG_UNISYSSPAR is not set
+# CONFIG_UNISYS_VISORBUS is not set
+CONFIG_UNIX=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_UNIX_DIAG is not set
+CONFIG_UNIX_SCM=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_UNWINDER_FRAME_POINTER is not set
+# CONFIG_UPROBES is not set
+# CONFIG_UPROBE_EVENTS is not set
+# CONFIG_US5182D is not set
+# CONFIG_USB is not set
+# CONFIG_USB4 is not set
+# CONFIG_USBIP_CORE is not set
+CONFIG_USBIP_VHCI_HC_PORTS=8
+CONFIG_USBIP_VHCI_NR_HCS=1
+# CONFIG_USBIP_VUDC is not set
+# CONFIG_USBPCWATCHDOG is not set
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_AIRSPY is not set
+CONFIG_USB_ALI_M5632=y
+# CONFIG_USB_AMD5536UDC is not set
+CONFIG_USB_AN2720=y
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARMLINUX=y
+# CONFIG_USB_ATM is not set
+CONFIG_USB_AUTOSUSPEND_DELAY=2
+# CONFIG_USB_BDC_UDC is not set
+CONFIG_USB_BELKIN=y
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+# CONFIG_USB_CDNS3 is not set
+# CONFIG_USB_CHAOSKEY is not set
+# CONFIG_USB_CHIPIDEA is not set
+# CONFIG_USB_CHIPIDEA_GENERIC is not set
+# CONFIG_USB_CHIPIDEA_IMX is not set
+# CONFIG_USB_CHIPIDEA_MSM is not set
+# CONFIG_USB_CHIPIDEA_PCI is not set
+# CONFIG_USB_CHIPIDEA_TEGRA is not set
+# CONFIG_USB_CONFIGFS is not set
+# CONFIG_USB_CONN_GPIO is not set
+# CONFIG_USB_CXACRU is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+CONFIG_USB_DEFAULT_PERSIST=y
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_DUMMY_HCD is not set
+# CONFIG_USB_DWC2 is not set
+# CONFIG_USB_DWC2_DEBUG is not set
+# CONFIG_USB_DWC2_DUAL_ROLE is not set
+# CONFIG_USB_DWC2_HOST is not set
+# CONFIG_USB_DWC2_PERIPHERAL is not set
+# CONFIG_USB_DWC2_TRACK_MISSED_SOFS is not set
+# CONFIG_USB_DWC3 is not set
+# CONFIG_USB_DWC3_EXYNOS is not set
+# CONFIG_USB_DWC3_HAPS is not set
+# CONFIG_USB_DWC3_KEYSTONE is not set
+# CONFIG_USB_DWC3_OF_SIMPLE is not set
+# CONFIG_USB_DWC3_PCI is not set
+# CONFIG_USB_DWC3_QCOM is not set
+# CONFIG_USB_DWC3_ULPI is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_EG20T is not set
+# CONFIG_USB_EHCI_ATH79 is not set
+# CONFIG_USB_EHCI_FSL is not set
+# CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_EHCI_HCD_AT91 is not set
+# CONFIG_USB_EHCI_HCD_OMAP is not set
+# CONFIG_USB_EHCI_HCD_PPC_OF is not set
+# CONFIG_USB_EHCI_MSM is not set
+# CONFIG_USB_EHCI_MV is not set
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_TT_NEWSCHED=y
+# CONFIG_USB_EHSET_TEST_FIXTURE is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EPSON2888 is not set
+# CONFIG_USB_ETH is not set
+# CONFIG_USB_EZUSB_FX2 is not set
+# CONFIG_USB_FEW_INIT_RETRIES is not set
+# CONFIG_USB_FOTG210_HCD is not set
+# CONFIG_USB_FOTG210_UDC is not set
+# CONFIG_USB_FSL_USB2 is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_FUNCTIONFS is not set
+# CONFIG_USB_FUSB300 is not set
+# CONFIG_USB_GADGET is not set
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+# CONFIG_USB_GADGET_DEBUG_FS is not set
+CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
+CONFIG_USB_GADGET_VBUS_DRAW=2
+# CONFIG_USB_GADGET_XILINX is not set
+# CONFIG_USB_GL860 is not set
+# CONFIG_USB_GOKU is not set
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_USB_GR_UDC is not set
+# CONFIG_USB_GSPCA is not set
+# CONFIG_USB_GSPCA_BENQ is not set
+# CONFIG_USB_GSPCA_CONEX is not set
+# CONFIG_USB_GSPCA_CPIA1 is not set
+# CONFIG_USB_GSPCA_DTCS033 is not set
+# CONFIG_USB_GSPCA_ETOMS is not set
+# CONFIG_USB_GSPCA_FINEPIX is not set
+# CONFIG_USB_GSPCA_JEILINJ is not set
+# CONFIG_USB_GSPCA_JL2005BCD is not set
+# CONFIG_USB_GSPCA_KINECT is not set
+# CONFIG_USB_GSPCA_KONICA is not set
+# CONFIG_USB_GSPCA_MARS is not set
+# CONFIG_USB_GSPCA_MR97310A is not set
+# CONFIG_USB_GSPCA_NW80X is not set
+# CONFIG_USB_GSPCA_OV519 is not set
+# CONFIG_USB_GSPCA_OV534 is not set
+# CONFIG_USB_GSPCA_OV534_9 is not set
+# CONFIG_USB_GSPCA_PAC207 is not set
+# CONFIG_USB_GSPCA_PAC7302 is not set
+# CONFIG_USB_GSPCA_PAC7311 is not set
+# CONFIG_USB_GSPCA_SE401 is not set
+# CONFIG_USB_GSPCA_SN9C2028 is not set
+# CONFIG_USB_GSPCA_SN9C20X is not set
+# CONFIG_USB_GSPCA_SONIXB is not set
+# CONFIG_USB_GSPCA_SONIXJ is not set
+# CONFIG_USB_GSPCA_SPCA1528 is not set
+# CONFIG_USB_GSPCA_SPCA500 is not set
+# CONFIG_USB_GSPCA_SPCA501 is not set
+# CONFIG_USB_GSPCA_SPCA505 is not set
+# CONFIG_USB_GSPCA_SPCA506 is not set
+# CONFIG_USB_GSPCA_SPCA508 is not set
+# CONFIG_USB_GSPCA_SPCA561 is not set
+# CONFIG_USB_GSPCA_SQ905 is not set
+# CONFIG_USB_GSPCA_SQ905C is not set
+# CONFIG_USB_GSPCA_SQ930X is not set
+# CONFIG_USB_GSPCA_STK014 is not set
+# CONFIG_USB_GSPCA_STK1135 is not set
+# CONFIG_USB_GSPCA_STV0680 is not set
+# CONFIG_USB_GSPCA_SUNPLUS is not set
+# CONFIG_USB_GSPCA_T613 is not set
+# CONFIG_USB_GSPCA_TOPRO is not set
+# CONFIG_USB_GSPCA_TOUPTEK is not set
+# CONFIG_USB_GSPCA_TV8532 is not set
+# CONFIG_USB_GSPCA_VC032X is not set
+# CONFIG_USB_GSPCA_VICAM is not set
+# CONFIG_USB_GSPCA_XIRLINK_CIT is not set
+# CONFIG_USB_GSPCA_ZC3XX is not set
+# CONFIG_USB_G_ACM_MS is not set
+# CONFIG_USB_G_DBGP is not set
+# CONFIG_USB_G_HID is not set
+# CONFIG_USB_G_MULTI is not set
+# CONFIG_USB_G_NCM is not set
+# CONFIG_USB_G_NOKIA is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_G_WEBCAM is not set
+# CONFIG_USB_HACKRF is not set
+# CONFIG_USB_HCD_TEST_MODE is not set
+# CONFIG_USB_HID is not set
+# CONFIG_USB_HIDDEV is not set
+# CONFIG_USB_HSIC_USB3503 is not set
+# CONFIG_USB_HSIC_USB4604 is not set
+# CONFIG_USB_HSO is not set
+# CONFIG_USB_HUB_USB251XB is not set
+# CONFIG_USB_HWA_HCD is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_IMX21_HCD is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_IPHETH is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1301 is not set
+# CONFIG_USB_ISP1362_HCD is not set
+# CONFIG_USB_ISP1760 is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_KC2190 is not set
+# CONFIG_USB_LAN78XX is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_LEDS_TRIGGER_USBPORT is not set
+# CONFIG_USB_LED_TRIG is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LGM_PHY is not set
+# CONFIG_USB_LINK_LAYER_TEST is not set
+# CONFIG_USB_M5602 is not set
+# CONFIG_USB_M66592 is not set
+# CONFIG_USB_MASS_STORAGE is not set
+# CONFIG_USB_MAX3420_UDC is not set
+# CONFIG_USB_MAX3421_HCD is not set
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_MOUSE is not set
+# CONFIG_USB_MSI2500 is not set
+# CONFIG_USB_MSM_OTG is not set
+# CONFIG_USB_MTU3 is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_MV_U3D is not set
+# CONFIG_USB_MV_UDC is not set
+# CONFIG_USB_MXS_PHY is not set
+# CONFIG_USB_NET2272 is not set
+# CONFIG_USB_NET2280 is not set
+# CONFIG_USB_NET_AQC111 is not set
+# CONFIG_USB_NET_AX88179_178A is not set
+# CONFIG_USB_NET_AX8817X is not set
+# CONFIG_USB_NET_CDCETHER is not set
+# CONFIG_USB_NET_CDC_EEM is not set
+# CONFIG_USB_NET_CDC_MBIM is not set
+# CONFIG_USB_NET_CDC_NCM is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_CH9200 is not set
+# CONFIG_USB_NET_CX82310_ETH is not set
+# CONFIG_USB_NET_DM9601 is not set
+# CONFIG_USB_NET_DRIVERS is not set
+# CONFIG_USB_NET_GL620A is not set
+# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set
+# CONFIG_USB_NET_INT51X1 is not set
+# CONFIG_USB_NET_KALMIA is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_QMI_WWAN is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_RNDIS_WLAN is not set
+# CONFIG_USB_NET_SMSC75XX is not set
+# CONFIG_USB_NET_SMSC95XX is not set
+# CONFIG_USB_NET_SR9700 is not set
+# CONFIG_USB_NET_SR9800 is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_USB_OHCI_HCD is not set
+# CONFIG_USB_OHCI_HCD_PCI is not set
+# CONFIG_USB_OHCI_HCD_PPC_OF is not set
+# CONFIG_USB_OHCI_HCD_PPC_OF_BE is not set
+# CONFIG_USB_OHCI_HCD_PPC_OF_LE is not set
+# CONFIG_USB_OHCI_HCD_SSB is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+# CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB is not set
+# CONFIG_USB_OTG_FSM is not set
+# CONFIG_USB_OTG_PRODUCTLIST is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_PCI is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_PHY is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_PWC_INPUT_EVDEV is not set
+# CONFIG_USB_PXA27X is not set
+# CONFIG_USB_R8A66597 is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_RAW_GADGET is not set
+# CONFIG_USB_RCAR_PHY is not set
+# CONFIG_USB_RENESAS_USBHS is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_ROLES_INTEL_XHCI is not set
+# CONFIG_USB_ROLE_SWITCH is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_RTL8152 is not set
+# CONFIG_USB_S2255 is not set
+# CONFIG_USB_SERIAL is not set
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+# CONFIG_USB_SERIAL_BELKIN is not set
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_CP210X is not set
+# CONFIG_USB_SERIAL_CYBERJACK is not set
+# CONFIG_USB_SERIAL_CYPRESS_M8 is not set
+# CONFIG_USB_SERIAL_DEBUG is not set
+# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
+# CONFIG_USB_SERIAL_EDGEPORT is not set
+# CONFIG_USB_SERIAL_EDGEPORT_TI is not set
+# CONFIG_USB_SERIAL_EMPEG is not set
+# CONFIG_USB_SERIAL_F81232 is not set
+# CONFIG_USB_SERIAL_F8153X is not set
+# CONFIG_USB_SERIAL_FTDI_SIO is not set
+# CONFIG_USB_SERIAL_GARMIN is not set
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_IPAQ is not set
+# CONFIG_USB_SERIAL_IPW is not set
+# CONFIG_USB_SERIAL_IR is not set
+# CONFIG_USB_SERIAL_IUU is not set
+# CONFIG_USB_SERIAL_KEYSPAN is not set
+CONFIG_USB_SERIAL_KEYSPAN_MPR=y
+# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
+CONFIG_USB_SERIAL_KEYSPAN_USA18X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
+# CONFIG_USB_SERIAL_KLSI is not set
+# CONFIG_USB_SERIAL_KOBIL_SCT is not set
+# CONFIG_USB_SERIAL_MCT_U232 is not set
+# CONFIG_USB_SERIAL_METRO is not set
+# CONFIG_USB_SERIAL_MOS7715_PARPORT is not set
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_MXUPORT is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+# CONFIG_USB_SERIAL_OMNINET is not set
+# CONFIG_USB_SERIAL_OPTICON is not set
+# CONFIG_USB_SERIAL_OPTION is not set
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_PL2303 is not set
+# CONFIG_USB_SERIAL_QCAUX is not set
+# CONFIG_USB_SERIAL_QT2 is not set
+# CONFIG_USB_SERIAL_QUALCOMM is not set
+# CONFIG_USB_SERIAL_SAFE is not set
+CONFIG_USB_SERIAL_SAFE_PADDED=y
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+# CONFIG_USB_SERIAL_SIMPLE is not set
+# CONFIG_USB_SERIAL_SPCP8X5 is not set
+# CONFIG_USB_SERIAL_SSU100 is not set
+# CONFIG_USB_SERIAL_SYMBOL is not set
+# CONFIG_USB_SERIAL_TI is not set
+# CONFIG_USB_SERIAL_UPD78F0730 is not set
+# CONFIG_USB_SERIAL_VISOR is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+# CONFIG_USB_SERIAL_WISHBONE is not set
+# CONFIG_USB_SERIAL_XIRCOM is not set
+# CONFIG_USB_SERIAL_XSENS_MT is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_SIERRA_NET is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_SNP_UDC_PLAT is not set
+# CONFIG_USB_SPEEDTOUCH is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_USB_STORAGE is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_ENE_UB6250 is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_REALTEK is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STV06XX is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_USB_SWITCH_FSA9480 is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_TMC is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_UAS is not set
+# CONFIG_USB_UEAGLEATM is not set
+# CONFIG_USB_ULPI is not set
+# CONFIG_USB_ULPI_BUS is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_USB_USS720 is not set
+# CONFIG_USB_VIDEO_CLASS is not set
+CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
+# CONFIG_USB_VL600 is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_WHCI_HCD is not set
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+# CONFIG_USB_XHCI_DBGCAP is not set
+# CONFIG_USB_XHCI_HCD is not set
+# CONFIG_USB_XHCI_MVEBU is not set
+# CONFIG_USB_XHCI_PCI_RENESAS is not set
+# CONFIG_USB_XUSBATM is not set
+# CONFIG_USB_YUREX is not set
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_USB_ZERO is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USELIB is not set
+# CONFIG_USERFAULTFD is not set
+# CONFIG_USERIO is not set
+# CONFIG_USE_OF is not set
+# CONFIG_UTS_NS is not set
+# CONFIG_UWB is not set
+# CONFIG_U_SERIAL_CONSOLE is not set
+# CONFIG_V4L_MEM2MEM_DRIVERS is not set
+# CONFIG_V4L_PLATFORM_DRIVERS is not set
+# CONFIG_V4L_TEST_DRIVERS is not set
+# CONFIG_VALIDATE_FS_PARSER is not set
+# CONFIG_VBOXGUEST is not set
+# CONFIG_VCNL3020 is not set
+# CONFIG_VCNL4000 is not set
+# CONFIG_VCNL4035 is not set
+# CONFIG_VDPA is not set
+CONFIG_VDSO=y
+# CONFIG_VEML6030 is not set
+# CONFIG_VEML6070 is not set
+# CONFIG_VETH is not set
+# CONFIG_VEXPRESS_CONFIG is not set
+# CONFIG_VF610_ADC is not set
+# CONFIG_VF610_DAC is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_VFIO is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VGA_ARB is not set
+# CONFIG_VGA_SWITCHEROO is not set
+# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set
+CONFIG_VHOST_MENU=y
+# CONFIG_VHOST_NET is not set
+# CONFIG_VHOST_VSOCK is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_VELOCITY is not set
+# CONFIG_VIDEO_AD5820 is not set
+# CONFIG_VIDEO_AD9389B is not set
+# CONFIG_VIDEO_ADP1653 is not set
+# CONFIG_VIDEO_ADV7170 is not set
+# CONFIG_VIDEO_ADV7175 is not set
+# CONFIG_VIDEO_ADV7180 is not set
+# CONFIG_VIDEO_ADV7183 is not set
+# CONFIG_VIDEO_ADV7343 is not set
+# CONFIG_VIDEO_ADV7393 is not set
+# CONFIG_VIDEO_ADV748X is not set
+# CONFIG_VIDEO_ADV7511 is not set
+# CONFIG_VIDEO_ADV7604 is not set
+# CONFIG_VIDEO_ADV7842 is not set
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_AK7375 is not set
+# CONFIG_VIDEO_AK881X is not set
+# CONFIG_VIDEO_AM437X_VPFE is not set
+# CONFIG_VIDEO_ASPEED is not set
+# CONFIG_VIDEO_ATMEL_ISC is not set
+# CONFIG_VIDEO_ATMEL_ISI is not set
+# CONFIG_VIDEO_AU0828 is not set
+# CONFIG_VIDEO_BT819 is not set
+# CONFIG_VIDEO_BT848 is not set
+# CONFIG_VIDEO_BT856 is not set
+# CONFIG_VIDEO_BT866 is not set
+# CONFIG_VIDEO_CADENCE is not set
+# CONFIG_VIDEO_CAFE_CCIC is not set
+# CONFIG_VIDEO_CS3308 is not set
+# CONFIG_VIDEO_CS5345 is not set
+# CONFIG_VIDEO_CS53L32A is not set
+# CONFIG_VIDEO_CX231XX is not set
+# CONFIG_VIDEO_CX2341X is not set
+# CONFIG_VIDEO_CX25840 is not set
+# CONFIG_VIDEO_CX88 is not set
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_DM6446_CCDC is not set
+# CONFIG_VIDEO_DT3155 is not set
+# CONFIG_VIDEO_DW9714 is not set
+# CONFIG_VIDEO_DW9768 is not set
+# CONFIG_VIDEO_DW9807_VCM is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_ET8EK8 is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+# CONFIG_VIDEO_GO7007 is not set
+# CONFIG_VIDEO_GS1662 is not set
+# CONFIG_VIDEO_HDPVR is not set
+# CONFIG_VIDEO_HEXIUM_GEMINI is not set
+# CONFIG_VIDEO_HEXIUM_ORION is not set
+# CONFIG_VIDEO_HI556 is not set
+# CONFIG_VIDEO_I2C is not set
+# CONFIG_VIDEO_IMX214 is not set
+# CONFIG_VIDEO_IMX219 is not set
+# CONFIG_VIDEO_IMX258 is not set
+# CONFIG_VIDEO_IMX274 is not set
+# CONFIG_VIDEO_IMX290 is not set
+# CONFIG_VIDEO_IMX319 is not set
+# CONFIG_VIDEO_IMX355 is not set
+# CONFIG_VIDEO_IMX477 is not set
+# CONFIG_VIDEO_IRS1125 is not set
+# CONFIG_VIDEO_IR_I2C is not set
+# CONFIG_VIDEO_IVTV is not set
+# CONFIG_VIDEO_KS0127 is not set
+# CONFIG_VIDEO_LM3560 is not set
+# CONFIG_VIDEO_LM3646 is not set
+# CONFIG_VIDEO_M52790 is not set
+# CONFIG_VIDEO_M5MOLS is not set
+# CONFIG_VIDEO_MAX9286 is not set
+# CONFIG_VIDEO_ML86V7667 is not set
+# CONFIG_VIDEO_MSP3400 is not set
+# CONFIG_VIDEO_MT9M001 is not set
+# CONFIG_VIDEO_MT9M032 is not set
+# CONFIG_VIDEO_MT9M111 is not set
+# CONFIG_VIDEO_MT9P031 is not set
+# CONFIG_VIDEO_MT9T001 is not set
+# CONFIG_VIDEO_MT9T112 is not set
+# CONFIG_VIDEO_MT9V011 is not set
+# CONFIG_VIDEO_MT9V032 is not set
+# CONFIG_VIDEO_MT9V111 is not set
+# CONFIG_VIDEO_MUX is not set
+# CONFIG_VIDEO_MXB is not set
+# CONFIG_VIDEO_NOON010PC30 is not set
+# CONFIG_VIDEO_OMAP2_VOUT is not set
+# CONFIG_VIDEO_OV13858 is not set
+# CONFIG_VIDEO_OV2640 is not set
+# CONFIG_VIDEO_OV2659 is not set
+# CONFIG_VIDEO_OV2680 is not set
+# CONFIG_VIDEO_OV2685 is not set
+# CONFIG_VIDEO_OV2740 is not set
+# CONFIG_VIDEO_OV5640 is not set
+# CONFIG_VIDEO_OV5645 is not set
+# CONFIG_VIDEO_OV5647 is not set
+# CONFIG_VIDEO_OV5670 is not set
+# CONFIG_VIDEO_OV5675 is not set
+# CONFIG_VIDEO_OV5695 is not set
+# CONFIG_VIDEO_OV6650 is not set
+# CONFIG_VIDEO_OV7251 is not set
+# CONFIG_VIDEO_OV7640 is not set
+# CONFIG_VIDEO_OV7670 is not set
+# CONFIG_VIDEO_OV772X is not set
+# CONFIG_VIDEO_OV7740 is not set
+# CONFIG_VIDEO_OV8856 is not set
+# CONFIG_VIDEO_OV9281 is not set
+# CONFIG_VIDEO_OV9640 is not set
+# CONFIG_VIDEO_OV9650 is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_RDACM20 is not set
+# CONFIG_VIDEO_RJ54N1 is not set
+# CONFIG_VIDEO_S5C73M3 is not set
+# CONFIG_VIDEO_S5K4ECGX is not set
+# CONFIG_VIDEO_S5K5BAF is not set
+# CONFIG_VIDEO_S5K6A3 is not set
+# CONFIG_VIDEO_S5K6AA is not set
+# CONFIG_VIDEO_SAA6588 is not set
+# CONFIG_VIDEO_SAA6752HS is not set
+# CONFIG_VIDEO_SAA7110 is not set
+# CONFIG_VIDEO_SAA711X is not set
+# CONFIG_VIDEO_SAA7127 is not set
+# CONFIG_VIDEO_SAA7134 is not set
+# CONFIG_VIDEO_SAA717X is not set
+# CONFIG_VIDEO_SAA7185 is not set
+# CONFIG_VIDEO_SH_MOBILE_CEU is not set
+# CONFIG_VIDEO_SMIAPP is not set
+# CONFIG_VIDEO_SONY_BTF_MPX is not set
+# CONFIG_VIDEO_SR030PC30 is not set
+# CONFIG_VIDEO_STK1160_COMMON is not set
+# CONFIG_VIDEO_ST_MIPID02 is not set
+# CONFIG_VIDEO_TC358743 is not set
+# CONFIG_VIDEO_TDA1997X is not set
+# CONFIG_VIDEO_TDA7432 is not set
+# CONFIG_VIDEO_TDA9840 is not set
+# CONFIG_VIDEO_TEA6415C is not set
+# CONFIG_VIDEO_TEA6420 is not set
+# CONFIG_VIDEO_THS7303 is not set
+# CONFIG_VIDEO_THS8200 is not set
+# CONFIG_VIDEO_TIMBERDALE is not set
+# CONFIG_VIDEO_TLV320AIC23B is not set
+# CONFIG_VIDEO_TM6000 is not set
+# CONFIG_VIDEO_TVAUDIO is not set
+# CONFIG_VIDEO_TVP514X is not set
+# CONFIG_VIDEO_TVP5150 is not set
+# CONFIG_VIDEO_TVP7002 is not set
+# CONFIG_VIDEO_TW2804 is not set
+# CONFIG_VIDEO_TW9903 is not set
+# CONFIG_VIDEO_TW9906 is not set
+# CONFIG_VIDEO_TW9910 is not set
+# CONFIG_VIDEO_UDA1342 is not set
+# CONFIG_VIDEO_UPD64031A is not set
+# CONFIG_VIDEO_UPD64083 is not set
+# CONFIG_VIDEO_USBTV is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_VIDEO_V4L2 is not set
+# CONFIG_VIDEO_VP27SMPX is not set
+# CONFIG_VIDEO_VPX3220 is not set
+# CONFIG_VIDEO_VS6624 is not set
+# CONFIG_VIDEO_WM8739 is not set
+# CONFIG_VIDEO_WM8775 is not set
+# CONFIG_VIDEO_XILINX is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_VIRTIO_BALLOON is not set
+# CONFIG_VIRTIO_BLK_SCSI is not set
+# CONFIG_VIRTIO_CONSOLE is not set
+# CONFIG_VIRTIO_FS is not set
+# CONFIG_VIRTIO_INPUT is not set
+CONFIG_VIRTIO_MENU=y
+# CONFIG_VIRTIO_MMIO is not set
+# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set
+# CONFIG_VIRTIO_PCI is not set
+# CONFIG_VIRTUALIZATION is not set
+# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set
+# CONFIG_VIRT_DRIVERS is not set
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_VL53L0X_I2C is not set
+# CONFIG_VL6180 is not set
+CONFIG_VLAN_8021Q=y
+# CONFIG_VLAN_8021Q_GVRP is not set
+# CONFIG_VLAN_8021Q_MVRP is not set
+# CONFIG_VME_BUS is not set
+# CONFIG_VMSPLIT_1G is not set
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_2G_OPT is not set
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_3G_OPT is not set
+# CONFIG_VMWARE_PVSCSI is not set
+# CONFIG_VMXNET3 is not set
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_VOP_BUS is not set
+# CONFIG_VORTEX is not set
+# CONFIG_VSOCKETS is not set
+# CONFIG_VSOCKETS_DIAG is not set
+# CONFIG_VT is not set
+# CONFIG_VT6655 is not set
+# CONFIG_VT6656 is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_VXGE is not set
+# CONFIG_VXLAN is not set
+# CONFIG_VZ89X is not set
+# CONFIG_W1 is not set
+# CONFIG_W1_CON is not set
+# CONFIG_W1_MASTER_DS1WM is not set
+# CONFIG_W1_MASTER_DS2482 is not set
+# CONFIG_W1_MASTER_DS2490 is not set
+# CONFIG_W1_MASTER_GPIO is not set
+# CONFIG_W1_MASTER_MATROX is not set
+# CONFIG_W1_MASTER_SGI is not set
+# CONFIG_W1_SLAVE_DS2405 is not set
+# CONFIG_W1_SLAVE_DS2406 is not set
+# CONFIG_W1_SLAVE_DS2408 is not set
+# CONFIG_W1_SLAVE_DS2413 is not set
+# CONFIG_W1_SLAVE_DS2423 is not set
+# CONFIG_W1_SLAVE_DS2430 is not set
+# CONFIG_W1_SLAVE_DS2431 is not set
+# CONFIG_W1_SLAVE_DS2433 is not set
+# CONFIG_W1_SLAVE_DS2438 is not set
+# CONFIG_W1_SLAVE_DS250X is not set
+# CONFIG_W1_SLAVE_DS2780 is not set
+# CONFIG_W1_SLAVE_DS2781 is not set
+# CONFIG_W1_SLAVE_DS2805 is not set
+# CONFIG_W1_SLAVE_DS28E04 is not set
+# CONFIG_W1_SLAVE_DS28E17 is not set
+# CONFIG_W1_SLAVE_SMEM is not set
+# CONFIG_W1_SLAVE_THERM is not set
+# CONFIG_W83627HF_WDT is not set
+# CONFIG_W83877F_WDT is not set
+# CONFIG_W83977F_WDT is not set
+# CONFIG_WAN is not set
+# CONFIG_WANXL is not set
+# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_CORE is not set
+CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+CONFIG_WATCHDOG_OPEN_TIMEOUT=0
+# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set
+# CONFIG_WATCHDOG_SYSFS is not set
+# CONFIG_WATCH_QUEUE is not set
+# CONFIG_WD80x3 is not set
+# CONFIG_WDAT_WDT is not set
+# CONFIG_WDTPCI is not set
+CONFIG_WEXT_CORE=y
+CONFIG_WEXT_PRIV=y
+CONFIG_WEXT_PROC=y
+CONFIG_WEXT_SPY=y
+CONFIG_WILINK_PLATFORM_DATA=y
+# CONFIG_WIMAX is not set
+# CONFIG_WIREGUARD is not set
+CONFIG_WIRELESS=y
+CONFIG_WIRELESS_EXT=y
+# CONFIG_WIRELESS_WDS is not set
+# CONFIG_WIZNET_W5100 is not set
+# CONFIG_WIZNET_W5300 is not set
+# CONFIG_WL1251 is not set
+# CONFIG_WL12XX is not set
+# CONFIG_WL18XX is not set
+CONFIG_WLAN=y
+# CONFIG_WLAN_VENDOR_ADMTEK is not set
+# CONFIG_WLAN_VENDOR_ATH is not set
+# CONFIG_WLAN_VENDOR_ATMEL is not set
+# CONFIG_WLAN_VENDOR_BROADCOM is not set
+# CONFIG_WLAN_VENDOR_CISCO is not set
+# CONFIG_WLAN_VENDOR_INTEL is not set
+# CONFIG_WLAN_VENDOR_INTERSIL is not set
+# CONFIG_WLAN_VENDOR_MARVELL is not set
+# CONFIG_WLAN_VENDOR_MEDIATEK is not set
+# CONFIG_WLAN_VENDOR_MICROCHIP is not set
+# CONFIG_WLAN_VENDOR_QUANTENNA is not set
+# CONFIG_WLAN_VENDOR_RALINK is not set
+# CONFIG_WLAN_VENDOR_REALTEK is not set
+# CONFIG_WLAN_VENDOR_RSI is not set
+# CONFIG_WLAN_VENDOR_ST is not set
+# CONFIG_WLAN_VENDOR_TI is not set
+# CONFIG_WLAN_VENDOR_ZYDAS is not set
+# CONFIG_WLCORE is not set
+CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y
+# CONFIG_WQ_WATCHDOG is not set
+# CONFIG_WW_MUTEX_SELFTEST is not set
+# CONFIG_X25 is not set
+# CONFIG_X509_CERTIFICATE_PARSER is not set
+# CONFIG_X86_PKG_TEMP_THERMAL is not set
+CONFIG_X86_SYSFB=y
+# CONFIG_XDP_SOCKETS is not set
+# CONFIG_XEN is not set
+# CONFIG_XEN_GRANT_DMA_ALLOC is not set
+# CONFIG_XEN_PVCALLS_FRONTEND is not set
+CONFIG_XEN_SCRUB_PAGES_DEFAULT=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_INTERFACE is not set
+# CONFIG_XFRM_IPCOMP is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFS_DEBUG is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_XFS_ONLINE_SCRUB is not set
+# CONFIG_XFS_POSIX_ACL is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_SUPPORT_V4 is not set
+# CONFIG_XFS_WARN is not set
+# CONFIG_XILINX_AXI_EMAC is not set
+# CONFIG_XILINX_DMA is not set
+# CONFIG_XILINX_EMACLITE is not set
+# CONFIG_XILINX_GMII2RGMII is not set
+# CONFIG_XILINX_LL_TEMAC is not set
+# CONFIG_XILINX_SDFEC is not set
+# CONFIG_XILINX_VCU is not set
+# CONFIG_XILINX_WATCHDOG is not set
+# CONFIG_XILINX_XADC is not set
+# CONFIG_XILINX_ZYNQMP_DMA is not set
+# CONFIG_XILINX_ZYNQMP_DPDMA is not set
+# CONFIG_XILLYBUS is not set
+# CONFIG_XIL_AXIS_FIFO is not set
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_XMON is not set
+CONFIG_XZ_DEC=y
+# CONFIG_XZ_DEC_ARM is not set
+# CONFIG_XZ_DEC_ARMTHUMB is not set
+# CONFIG_XZ_DEC_BCJ is not set
+# CONFIG_XZ_DEC_IA64 is not set
+# CONFIG_XZ_DEC_POWERPC is not set
+# CONFIG_XZ_DEC_SPARC is not set
+# CONFIG_XZ_DEC_TEST is not set
+# CONFIG_XZ_DEC_X86 is not set
+# CONFIG_YAM is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_YENTA is not set
+# CONFIG_YENTA_O2 is not set
+# CONFIG_YENTA_RICOH is not set
+# CONFIG_YENTA_TI is not set
+# CONFIG_YENTA_TOSHIBA is not set
+# CONFIG_ZBUD is not set
+# CONFIG_ZD1211RW is not set
+# CONFIG_ZD1211RW_DEBUG is not set
+# CONFIG_ZEROPLUS_FF is not set
+# CONFIG_ZIIRAVE_WATCHDOG is not set
+# CONFIG_ZISOFS is not set
+# CONFIG_ZLIB_DEFLATE is not set
+# CONFIG_ZLIB_INFLATE is not set
+CONFIG_ZONE_DMA=y
+# CONFIG_ZOPT2201 is not set
+# CONFIG_ZPA2326 is not set
+# CONFIG_ZPOOL is not set
+# CONFIG_ZRAM is not set
+# CONFIG_ZRAM_MEMORY_TRACKING is not set
+# CONFIG_ZSMALLOC is not set
+# CONFIG_ZX_TDM is not set
--- /dev/null
+From a779a482fb9b9f8fcdf8b2519c789b4b9bb5dd05 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 16:56:48 +0200
+Subject: build: add a hack for removing non-essential module info
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/module.h | 13 ++++++++-----
+ include/linux/moduleparam.h | 15 ++++++++++++---
+ init/Kconfig | 7 +++++++
+ kernel/module.c | 5 ++++-
+ scripts/mod/modpost.c | 12 ++++++++++++
+ 5 files changed, 43 insertions(+), 9 deletions(-)
+
+--- a/include/linux/module.h
++++ b/include/linux/module.h
+@@ -161,6 +161,7 @@ extern void cleanup_module(void);
+
+ /* Generic info of form tag = "info" */
+ #define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
++#define MODULE_INFO_STRIP(tag, info) __MODULE_INFO_STRIP(tag, tag, info)
+
+ /* For userspace: you can also call me... */
+ #define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)
+@@ -230,12 +231,12 @@ extern void cleanup_module(void);
+ * Author(s), use "Name <email>" or just "Name", for multiple
+ * authors use multiple MODULE_AUTHOR() statements/lines.
+ */
+-#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author)
++#define MODULE_AUTHOR(_author) MODULE_INFO_STRIP(author, _author)
+
+ /* What your module does. */
+-#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
++#define MODULE_DESCRIPTION(_description) MODULE_INFO_STRIP(description, _description)
+
+-#ifdef MODULE
++#if defined(MODULE) && !defined(CONFIG_MODULE_STRIPPED)
+ /* Creates an alias so file2alias.c can find device table. */
+ #define MODULE_DEVICE_TABLE(type, name) \
+ extern typeof(name) __mod_##type##__##name##_device_table \
+@@ -262,7 +263,9 @@ extern typeof(name) __mod_##type##__##na
+ */
+
+ #if defined(MODULE) || !defined(CONFIG_SYSFS)
+-#define MODULE_VERSION(_version) MODULE_INFO(version, _version)
++#define MODULE_VERSION(_version) MODULE_INFO_STRIP(version, _version)
++#elif defined(CONFIG_MODULE_STRIPPED)
++#define MODULE_VERSION(_version) __MODULE_INFO_DISABLED(version)
+ #else
+ #define MODULE_VERSION(_version) \
+ MODULE_INFO(version, _version); \
+@@ -285,7 +288,7 @@ extern typeof(name) __mod_##type##__##na
+ /* Optional firmware file (or files) needed by the module
+ * format is simply firmware file name. Multiple firmware
+ * files require multiple MODULE_FIRMWARE() specifiers */
+-#define MODULE_FIRMWARE(_firmware) MODULE_INFO(firmware, _firmware)
++#define MODULE_FIRMWARE(_firmware) MODULE_INFO_STRIP(firmware, _firmware)
+
+ #define MODULE_IMPORT_NS(ns) MODULE_INFO(import_ns, #ns)
+
+--- a/include/linux/moduleparam.h
++++ b/include/linux/moduleparam.h
+@@ -20,6 +20,16 @@
+ /* Chosen so that structs with an unsigned long line up. */
+ #define MAX_PARAM_PREFIX_LEN (64 - sizeof(unsigned long))
+
++/* This struct is here for syntactic coherency, it is not used */
++#define __MODULE_INFO_DISABLED(name) \
++ struct __UNIQUE_ID(name) {}
++
++#ifdef CONFIG_MODULE_STRIPPED
++#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO_DISABLED(name)
++#else
++#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO(tag, name, info)
++#endif
++
+ #define __MODULE_INFO(tag, name, info) \
+ static const char __UNIQUE_ID(name)[] \
+ __used __section(".modinfo") __attribute__((unused, aligned(1))) \
+@@ -31,7 +41,7 @@ static const char __UNIQUE_ID(name)[]
+ /* One for each parameter, describing how to use it. Some files do
+ multiple of these per line, so can't just use MODULE_INFO. */
+ #define MODULE_PARM_DESC(_parm, desc) \
+- __MODULE_INFO(parm, _parm, #_parm ":" desc)
++ __MODULE_INFO_STRIP(parm, _parm, #_parm ":" desc)
+
+ struct kernel_param;
+
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -2347,6 +2347,13 @@ config UNUSED_KSYMS_WHITELIST
+ one per line. The path can be absolute, or relative to the kernel
+ source tree.
+
++config MODULE_STRIPPED
++ bool "Reduce module size"
++ depends on MODULES
++ help
++ Remove module parameter descriptions, author info, version, aliases,
++ device tables, etc.
++
+ endif # MODULES
+
+ config MODULES_TREE_LOOKUP
+--- a/kernel/module.c
++++ b/kernel/module.c
+@@ -1285,6 +1285,7 @@ static struct module_attribute *modinfo_
+
+ static const char vermagic[] = VERMAGIC_STRING;
+
++#if defined(CONFIG_MODVERSIONS) || !defined(CONFIG_MODULE_STRIPPED)
+ static int try_to_force_load(struct module *mod, const char *reason)
+ {
+ #ifdef CONFIG_MODULE_FORCE_LOAD
+@@ -1296,6 +1297,7 @@ static int try_to_force_load(struct modu
+ return -ENOEXEC;
+ #endif
+ }
++#endif
+
+ #ifdef CONFIG_MODVERSIONS
+
+@@ -3247,9 +3249,11 @@ static int setup_load_info(struct load_i
+
+ static int check_modinfo(struct module *mod, struct load_info *info, int flags)
+ {
+- const char *modmagic = get_modinfo(info, "vermagic");
+ int err;
+
++#ifndef CONFIG_MODULE_STRIPPED
++ const char *modmagic = get_modinfo(info, "vermagic");
++
+ if (flags & MODULE_INIT_IGNORE_VERMAGIC)
+ modmagic = NULL;
+
+@@ -3270,6 +3274,7 @@ static int check_modinfo(struct module *
+ mod->name);
+ add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK);
+ }
++#endif
+
+ check_modinfo_retpoline(mod, info);
+
+--- a/scripts/mod/modpost.c
++++ b/scripts/mod/modpost.c
+@@ -2037,7 +2037,9 @@ static void read_symbols(const char *mod
+ symname = remove_dot(info.strtab + sym->st_name);
+
+ handle_symbol(mod, &info, sym, symname);
++#ifndef CONFIG_MODULE_STRIPPED
+ handle_moddevtable(mod, &info, sym, symname);
++#endif
+ }
+
+ for (sym = info.symtab_start; sym < info.symtab_stop; sym++) {
+@@ -2250,8 +2252,10 @@ static void add_header(struct buffer *b,
+ buf_printf(b, "\n");
+ buf_printf(b, "BUILD_SALT;\n");
+ buf_printf(b, "\n");
++#ifndef CONFIG_MODULE_STRIPPED
+ buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
+ buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n");
++#endif
+ buf_printf(b, "\n");
+ buf_printf(b, "__visible struct module __this_module\n");
+ buf_printf(b, "__section(\".gnu.linkonce.this_module\") = {\n");
+@@ -2268,8 +2272,10 @@ static void add_header(struct buffer *b,
+
+ static void add_intree_flag(struct buffer *b, int is_intree)
+ {
++#ifndef CONFIG_MODULE_STRIPPED
+ if (is_intree)
+ buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
++#endif
+ }
+
+ /* Cannot check for assembler */
+@@ -2282,8 +2288,10 @@ static void add_retpoline(struct buffer
+
+ static void add_staging_flag(struct buffer *b, const char *name)
+ {
++#ifndef CONFIG_MODULE_STRIPPED
+ if (strstarts(name, "drivers/staging"))
+ buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
++#endif
+ }
+
+ /**
+@@ -2367,11 +2375,13 @@ static void add_depends(struct buffer *b
+
+ static void add_srcversion(struct buffer *b, struct module *mod)
+ {
++#ifndef CONFIG_MODULE_STRIPPED
+ if (mod->srcversion[0]) {
+ buf_printf(b, "\n");
+ buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n",
+ mod->srcversion);
+ }
++#endif
+ }
+
+ static void write_buf(struct buffer *b, const char *fname)
+@@ -2630,7 +2640,9 @@ int main(int argc, char **argv)
+ add_staging_flag(&buf, mod->name);
+ err |= add_versions(&buf, mod);
+ add_depends(&buf, mod);
++#ifndef CONFIG_MODULE_STRIPPED
+ add_moddevtable(&buf, mod);
++#endif
+ add_srcversion(&buf, mod);
+
+ sprintf(fname, "%s.mod.c", mod->name);
--- /dev/null
+--- a/scripts/kconfig/conf.c
++++ b/scripts/kconfig/conf.c
+@@ -215,6 +215,8 @@ static int conf_sym(struct menu *menu)
+ break;
+ continue;
+ case 0:
++ if (!sym_has_value(sym) && !tty_stdio && getenv("FAIL_ON_UNCONFIGURED"))
++ exit(1);
+ newval = oldval;
+ break;
+ case '?':
--- /dev/null
+From db7c30dcd9a0391bf13b62c9f91e144d762ef43a Mon Sep 17 00:00:00 2001
+From: Florian Fainelli <f.fainelli@gmail.com>
+Date: Fri, 7 Jul 2017 17:00:49 +0200
+Subject: Add an OSX specific patch to make the kernel be compiled
+
+lede-commit: 3fc2a24f0422b2f55f9ed43f116db3111f700526
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+---
+ scripts/kconfig/Makefile | 3 +
+ scripts/mod/elf.h | 3007 ++++++++++++++++++++++++++++++++++++++++++++
+ scripts/mod/mk_elfconfig.c | 4 +
+ scripts/mod/modpost.h | 4 +
+ 4 files changed, 3018 insertions(+)
+ create mode 100644 scripts/mod/elf.h
+
+--- /dev/null
++++ b/scripts/mod/elf.h
+@@ -0,0 +1,3007 @@
++/* This file defines standard ELF types, structures, and macros.
++ Copyright (C) 1995-2012 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++
++ The GNU C Library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 of the License, or (at your option) any later version.
++
++ The GNU C Library is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with the GNU C Library; if not, see
++ <http://www.gnu.org/licenses/>. */
++
++#ifndef _ELF_H
++#define _ELF_H 1
++
++/* Standard ELF types. */
++
++#include <stdint.h>
++
++/* Type for a 16-bit quantity. */
++typedef uint16_t Elf32_Half;
++typedef uint16_t Elf64_Half;
++
++/* Types for signed and unsigned 32-bit quantities. */
++typedef uint32_t Elf32_Word;
++typedef int32_t Elf32_Sword;
++typedef uint32_t Elf64_Word;
++typedef int32_t Elf64_Sword;
++
++/* Types for signed and unsigned 64-bit quantities. */
++typedef uint64_t Elf32_Xword;
++typedef int64_t Elf32_Sxword;
++typedef uint64_t Elf64_Xword;
++typedef int64_t Elf64_Sxword;
++
++/* Type of addresses. */
++typedef uint32_t Elf32_Addr;
++typedef uint64_t Elf64_Addr;
++
++/* Type of file offsets. */
++typedef uint32_t Elf32_Off;
++typedef uint64_t Elf64_Off;
++
++/* Type for section indices, which are 16-bit quantities. */
++typedef uint16_t Elf32_Section;
++typedef uint16_t Elf64_Section;
++
++/* Type for version symbol information. */
++typedef Elf32_Half Elf32_Versym;
++typedef Elf64_Half Elf64_Versym;
++
++
++/* The ELF file header. This appears at the start of every ELF file. */
++
++#define EI_NIDENT (16)
++
++typedef struct
++{
++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
++ Elf32_Half e_type; /* Object file type */
++ Elf32_Half e_machine; /* Architecture */
++ Elf32_Word e_version; /* Object file version */
++ Elf32_Addr e_entry; /* Entry point virtual address */
++ Elf32_Off e_phoff; /* Program header table file offset */
++ Elf32_Off e_shoff; /* Section header table file offset */
++ Elf32_Word e_flags; /* Processor-specific flags */
++ Elf32_Half e_ehsize; /* ELF header size in bytes */
++ Elf32_Half e_phentsize; /* Program header table entry size */
++ Elf32_Half e_phnum; /* Program header table entry count */
++ Elf32_Half e_shentsize; /* Section header table entry size */
++ Elf32_Half e_shnum; /* Section header table entry count */
++ Elf32_Half e_shstrndx; /* Section header string table index */
++} Elf32_Ehdr;
++
++typedef struct
++{
++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
++ Elf64_Half e_type; /* Object file type */
++ Elf64_Half e_machine; /* Architecture */
++ Elf64_Word e_version; /* Object file version */
++ Elf64_Addr e_entry; /* Entry point virtual address */
++ Elf64_Off e_phoff; /* Program header table file offset */
++ Elf64_Off e_shoff; /* Section header table file offset */
++ Elf64_Word e_flags; /* Processor-specific flags */
++ Elf64_Half e_ehsize; /* ELF header size in bytes */
++ Elf64_Half e_phentsize; /* Program header table entry size */
++ Elf64_Half e_phnum; /* Program header table entry count */
++ Elf64_Half e_shentsize; /* Section header table entry size */
++ Elf64_Half e_shnum; /* Section header table entry count */
++ Elf64_Half e_shstrndx; /* Section header string table index */
++} Elf64_Ehdr;
++
++/* Fields in the e_ident array. The EI_* macros are indices into the
++ array. The macros under each EI_* macro are the values the byte
++ may have. */
++
++#define EI_MAG0 0 /* File identification byte 0 index */
++#define ELFMAG0 0x7f /* Magic number byte 0 */
++
++#define EI_MAG1 1 /* File identification byte 1 index */
++#define ELFMAG1 'E' /* Magic number byte 1 */
++
++#define EI_MAG2 2 /* File identification byte 2 index */
++#define ELFMAG2 'L' /* Magic number byte 2 */
++
++#define EI_MAG3 3 /* File identification byte 3 index */
++#define ELFMAG3 'F' /* Magic number byte 3 */
++
++/* Conglomeration of the identification bytes, for easy testing as a word. */
++#define ELFMAG "\177ELF"
++#define SELFMAG 4
++
++#define EI_CLASS 4 /* File class byte index */
++#define ELFCLASSNONE 0 /* Invalid class */
++#define ELFCLASS32 1 /* 32-bit objects */
++#define ELFCLASS64 2 /* 64-bit objects */
++#define ELFCLASSNUM 3
++
++#define EI_DATA 5 /* Data encoding byte index */
++#define ELFDATANONE 0 /* Invalid data encoding */
++#define ELFDATA2LSB 1 /* 2's complement, little endian */
++#define ELFDATA2MSB 2 /* 2's complement, big endian */
++#define ELFDATANUM 3
++
++#define EI_VERSION 6 /* File version byte index */
++ /* Value must be EV_CURRENT */
++
++#define EI_OSABI 7 /* OS ABI identification */
++#define ELFOSABI_NONE 0 /* UNIX System V ABI */
++#define ELFOSABI_SYSV 0 /* Alias. */
++#define ELFOSABI_HPUX 1 /* HP-UX */
++#define ELFOSABI_NETBSD 2 /* NetBSD. */
++#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */
++#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */
++#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */
++#define ELFOSABI_AIX 7 /* IBM AIX. */
++#define ELFOSABI_IRIX 8 /* SGI Irix. */
++#define ELFOSABI_FREEBSD 9 /* FreeBSD. */
++#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */
++#define ELFOSABI_MODESTO 11 /* Novell Modesto. */
++#define ELFOSABI_OPENBSD 12 /* OpenBSD. */
++#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */
++#define ELFOSABI_ARM 97 /* ARM */
++#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
++
++#define EI_ABIVERSION 8 /* ABI version */
++
++#define EI_PAD 9 /* Byte index of padding bytes */
++
++/* Legal values for e_type (object file type). */
++
++#define ET_NONE 0 /* No file type */
++#define ET_REL 1 /* Relocatable file */
++#define ET_EXEC 2 /* Executable file */
++#define ET_DYN 3 /* Shared object file */
++#define ET_CORE 4 /* Core file */
++#define ET_NUM 5 /* Number of defined types */
++#define ET_LOOS 0xfe00 /* OS-specific range start */
++#define ET_HIOS 0xfeff /* OS-specific range end */
++#define ET_LOPROC 0xff00 /* Processor-specific range start */
++#define ET_HIPROC 0xffff /* Processor-specific range end */
++
++/* Legal values for e_machine (architecture). */
++
++#define EM_NONE 0 /* No machine */
++#define EM_M32 1 /* AT&T WE 32100 */
++#define EM_SPARC 2 /* SUN SPARC */
++#define EM_386 3 /* Intel 80386 */
++#define EM_68K 4 /* Motorola m68k family */
++#define EM_88K 5 /* Motorola m88k family */
++#define EM_860 7 /* Intel 80860 */
++#define EM_MIPS 8 /* MIPS R3000 big-endian */
++#define EM_S370 9 /* IBM System/370 */
++#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */
++
++#define EM_PARISC 15 /* HPPA */
++#define EM_VPP500 17 /* Fujitsu VPP500 */
++#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
++#define EM_960 19 /* Intel 80960 */
++#define EM_PPC 20 /* PowerPC */
++#define EM_PPC64 21 /* PowerPC 64-bit */
++#define EM_S390 22 /* IBM S390 */
++
++#define EM_V800 36 /* NEC V800 series */
++#define EM_FR20 37 /* Fujitsu FR20 */
++#define EM_RH32 38 /* TRW RH-32 */
++#define EM_RCE 39 /* Motorola RCE */
++#define EM_ARM 40 /* ARM */
++#define EM_FAKE_ALPHA 41 /* Digital Alpha */
++#define EM_SH 42 /* Hitachi SH */
++#define EM_SPARCV9 43 /* SPARC v9 64-bit */
++#define EM_TRICORE 44 /* Siemens Tricore */
++#define EM_ARC 45 /* Argonaut RISC Core */
++#define EM_H8_300 46 /* Hitachi H8/300 */
++#define EM_H8_300H 47 /* Hitachi H8/300H */
++#define EM_H8S 48 /* Hitachi H8S */
++#define EM_H8_500 49 /* Hitachi H8/500 */
++#define EM_IA_64 50 /* Intel Merced */
++#define EM_MIPS_X 51 /* Stanford MIPS-X */
++#define EM_COLDFIRE 52 /* Motorola Coldfire */
++#define EM_68HC12 53 /* Motorola M68HC12 */
++#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/
++#define EM_PCP 55 /* Siemens PCP */
++#define EM_NCPU 56 /* Sony nCPU embeeded RISC */
++#define EM_NDR1 57 /* Denso NDR1 microprocessor */
++#define EM_STARCORE 58 /* Motorola Start*Core processor */
++#define EM_ME16 59 /* Toyota ME16 processor */
++#define EM_ST100 60 /* STMicroelectronic ST100 processor */
++#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/
++#define EM_X86_64 62 /* AMD x86-64 architecture */
++#define EM_PDSP 63 /* Sony DSP Processor */
++
++#define EM_FX66 66 /* Siemens FX66 microcontroller */
++#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */
++#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */
++#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */
++#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */
++#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */
++#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */
++#define EM_SVX 73 /* Silicon Graphics SVx */
++#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */
++#define EM_VAX 75 /* Digital VAX */
++#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */
++#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */
++#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */
++#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */
++#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */
++#define EM_HUANY 81 /* Harvard University machine-independent object files */
++#define EM_PRISM 82 /* SiTera Prism */
++#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */
++#define EM_FR30 84 /* Fujitsu FR30 */
++#define EM_D10V 85 /* Mitsubishi D10V */
++#define EM_D30V 86 /* Mitsubishi D30V */
++#define EM_V850 87 /* NEC v850 */
++#define EM_M32R 88 /* Mitsubishi M32R */
++#define EM_MN10300 89 /* Matsushita MN10300 */
++#define EM_MN10200 90 /* Matsushita MN10200 */
++#define EM_PJ 91 /* picoJava */
++#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */
++#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */
++#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */
++#define EM_TILEPRO 188 /* Tilera TILEPro */
++#define EM_TILEGX 191 /* Tilera TILE-Gx */
++#define EM_NUM 192
++
++/* If it is necessary to assign new unofficial EM_* values, please
++ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the
++ chances of collision with official or non-GNU unofficial values. */
++
++#define EM_ALPHA 0x9026
++
++/* Legal values for e_version (version). */
++
++#define EV_NONE 0 /* Invalid ELF version */
++#define EV_CURRENT 1 /* Current version */
++#define EV_NUM 2
++
++/* Section header. */
++
++typedef struct
++{
++ Elf32_Word sh_name; /* Section name (string tbl index) */
++ Elf32_Word sh_type; /* Section type */
++ Elf32_Word sh_flags; /* Section flags */
++ Elf32_Addr sh_addr; /* Section virtual addr at execution */
++ Elf32_Off sh_offset; /* Section file offset */
++ Elf32_Word sh_size; /* Section size in bytes */
++ Elf32_Word sh_link; /* Link to another section */
++ Elf32_Word sh_info; /* Additional section information */
++ Elf32_Word sh_addralign; /* Section alignment */
++ Elf32_Word sh_entsize; /* Entry size if section holds table */
++} Elf32_Shdr;
++
++typedef struct
++{
++ Elf64_Word sh_name; /* Section name (string tbl index) */
++ Elf64_Word sh_type; /* Section type */
++ Elf64_Xword sh_flags; /* Section flags */
++ Elf64_Addr sh_addr; /* Section virtual addr at execution */
++ Elf64_Off sh_offset; /* Section file offset */
++ Elf64_Xword sh_size; /* Section size in bytes */
++ Elf64_Word sh_link; /* Link to another section */
++ Elf64_Word sh_info; /* Additional section information */
++ Elf64_Xword sh_addralign; /* Section alignment */
++ Elf64_Xword sh_entsize; /* Entry size if section holds table */
++} Elf64_Shdr;
++
++/* Special section indices. */
++
++#define SHN_UNDEF 0 /* Undefined section */
++#define SHN_LORESERVE 0xff00 /* Start of reserved indices */
++#define SHN_LOPROC 0xff00 /* Start of processor-specific */
++#define SHN_BEFORE 0xff00 /* Order section before all others
++ (Solaris). */
++#define SHN_AFTER 0xff01 /* Order section after all others
++ (Solaris). */
++#define SHN_HIPROC 0xff1f /* End of processor-specific */
++#define SHN_LOOS 0xff20 /* Start of OS-specific */
++#define SHN_HIOS 0xff3f /* End of OS-specific */
++#define SHN_ABS 0xfff1 /* Associated symbol is absolute */
++#define SHN_COMMON 0xfff2 /* Associated symbol is common */
++#define SHN_XINDEX 0xffff /* Index is in extra table. */
++#define SHN_HIRESERVE 0xffff /* End of reserved indices */
++
++/* Legal values for sh_type (section type). */
++
++#define SHT_NULL 0 /* Section header table entry unused */
++#define SHT_PROGBITS 1 /* Program data */
++#define SHT_SYMTAB 2 /* Symbol table */
++#define SHT_STRTAB 3 /* String table */
++#define SHT_RELA 4 /* Relocation entries with addends */
++#define SHT_HASH 5 /* Symbol hash table */
++#define SHT_DYNAMIC 6 /* Dynamic linking information */
++#define SHT_NOTE 7 /* Notes */
++#define SHT_NOBITS 8 /* Program space with no data (bss) */
++#define SHT_REL 9 /* Relocation entries, no addends */
++#define SHT_SHLIB 10 /* Reserved */
++#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
++#define SHT_INIT_ARRAY 14 /* Array of constructors */
++#define SHT_FINI_ARRAY 15 /* Array of destructors */
++#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
++#define SHT_GROUP 17 /* Section group */
++#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
++#define SHT_NUM 19 /* Number of defined types. */
++#define SHT_LOOS 0x60000000 /* Start OS-specific. */
++#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */
++#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */
++#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */
++#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
++#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
++#define SHT_SUNW_move 0x6ffffffa
++#define SHT_SUNW_COMDAT 0x6ffffffb
++#define SHT_SUNW_syminfo 0x6ffffffc
++#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */
++#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */
++#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */
++#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
++#define SHT_HIOS 0x6fffffff /* End OS-specific type */
++#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
++#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
++#define SHT_LOUSER 0x80000000 /* Start of application-specific */
++#define SHT_HIUSER 0x8fffffff /* End of application-specific */
++
++/* Legal values for sh_flags (section flags). */
++
++#define SHF_WRITE (1 << 0) /* Writable */
++#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
++#define SHF_EXECINSTR (1 << 2) /* Executable */
++#define SHF_MERGE (1 << 4) /* Might be merged */
++#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */
++#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */
++#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */
++#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling
++ required */
++#define SHF_GROUP (1 << 9) /* Section is member of a group. */
++#define SHF_TLS (1 << 10) /* Section hold thread-local data. */
++#define SHF_MASKOS 0x0ff00000 /* OS-specific. */
++#define SHF_MASKPROC 0xf0000000 /* Processor-specific */
++#define SHF_ORDERED (1 << 30) /* Special ordering requirement
++ (Solaris). */
++#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless
++ referenced or allocated (Solaris).*/
++
++/* Section group handling. */
++#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */
++
++/* Symbol table entry. */
++
++typedef struct
++{
++ Elf32_Word st_name; /* Symbol name (string tbl index) */
++ Elf32_Addr st_value; /* Symbol value */
++ Elf32_Word st_size; /* Symbol size */
++ unsigned char st_info; /* Symbol type and binding */
++ unsigned char st_other; /* Symbol visibility */
++ Elf32_Section st_shndx; /* Section index */
++} Elf32_Sym;
++
++typedef struct
++{
++ Elf64_Word st_name; /* Symbol name (string tbl index) */
++ unsigned char st_info; /* Symbol type and binding */
++ unsigned char st_other; /* Symbol visibility */
++ Elf64_Section st_shndx; /* Section index */
++ Elf64_Addr st_value; /* Symbol value */
++ Elf64_Xword st_size; /* Symbol size */
++} Elf64_Sym;
++
++/* The syminfo section if available contains additional information about
++ every dynamic symbol. */
++
++typedef struct
++{
++ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */
++ Elf32_Half si_flags; /* Per symbol flags */
++} Elf32_Syminfo;
++
++typedef struct
++{
++ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */
++ Elf64_Half si_flags; /* Per symbol flags */
++} Elf64_Syminfo;
++
++/* Possible values for si_boundto. */
++#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */
++#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */
++#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */
++
++/* Possible bitmasks for si_flags. */
++#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */
++#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */
++#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */
++#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy
++ loaded */
++/* Syminfo version values. */
++#define SYMINFO_NONE 0
++#define SYMINFO_CURRENT 1
++#define SYMINFO_NUM 2
++
++
++/* How to extract and insert information held in the st_info field. */
++
++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
++#define ELF32_ST_TYPE(val) ((val) & 0xf)
++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
++
++/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */
++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val)
++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val)
++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type))
++
++/* Legal values for ST_BIND subfield of st_info (symbol binding). */
++
++#define STB_LOCAL 0 /* Local symbol */
++#define STB_GLOBAL 1 /* Global symbol */
++#define STB_WEAK 2 /* Weak symbol */
++#define STB_NUM 3 /* Number of defined types. */
++#define STB_LOOS 10 /* Start of OS-specific */
++#define STB_GNU_UNIQUE 10 /* Unique symbol. */
++#define STB_HIOS 12 /* End of OS-specific */
++#define STB_LOPROC 13 /* Start of processor-specific */
++#define STB_HIPROC 15 /* End of processor-specific */
++
++/* Legal values for ST_TYPE subfield of st_info (symbol type). */
++
++#define STT_NOTYPE 0 /* Symbol type is unspecified */
++#define STT_OBJECT 1 /* Symbol is a data object */
++#define STT_FUNC 2 /* Symbol is a code object */
++#define STT_SECTION 3 /* Symbol associated with a section */
++#define STT_FILE 4 /* Symbol's name is file name */
++#define STT_COMMON 5 /* Symbol is a common data object */
++#define STT_TLS 6 /* Symbol is thread-local data object*/
++#define STT_NUM 7 /* Number of defined types. */
++#define STT_LOOS 10 /* Start of OS-specific */
++#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */
++#define STT_HIOS 12 /* End of OS-specific */
++#define STT_LOPROC 13 /* Start of processor-specific */
++#define STT_HIPROC 15 /* End of processor-specific */
++
++
++/* Symbol table indices are found in the hash buckets and chain table
++ of a symbol hash table section. This special index value indicates
++ the end of a chain, meaning no further symbols are found in that bucket. */
++
++#define STN_UNDEF 0 /* End of a chain. */
++
++
++/* How to extract and insert information held in the st_other field. */
++
++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
++
++/* For ELF64 the definitions are the same. */
++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
++
++/* Symbol visibility specification encoded in the st_other field. */
++#define STV_DEFAULT 0 /* Default symbol visibility rules */
++#define STV_INTERNAL 1 /* Processor specific hidden class */
++#define STV_HIDDEN 2 /* Sym unavailable in other modules */
++#define STV_PROTECTED 3 /* Not preemptible, not exported */
++
++
++/* Relocation table entry without addend (in section of type SHT_REL). */
++
++typedef struct
++{
++ Elf32_Addr r_offset; /* Address */
++ Elf32_Word r_info; /* Relocation type and symbol index */
++} Elf32_Rel;
++
++/* I have seen two different definitions of the Elf64_Rel and
++ Elf64_Rela structures, so we'll leave them out until Novell (or
++ whoever) gets their act together. */
++/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */
++
++typedef struct
++{
++ Elf64_Addr r_offset; /* Address */
++ Elf64_Xword r_info; /* Relocation type and symbol index */
++} Elf64_Rel;
++
++/* Relocation table entry with addend (in section of type SHT_RELA). */
++
++typedef struct
++{
++ Elf32_Addr r_offset; /* Address */
++ Elf32_Word r_info; /* Relocation type and symbol index */
++ Elf32_Sword r_addend; /* Addend */
++} Elf32_Rela;
++
++typedef struct
++{
++ Elf64_Addr r_offset; /* Address */
++ Elf64_Xword r_info; /* Relocation type and symbol index */
++ Elf64_Sxword r_addend; /* Addend */
++} Elf64_Rela;
++
++/* How to extract and insert information held in the r_info field. */
++
++#define ELF32_R_SYM(val) ((val) >> 8)
++#define ELF32_R_TYPE(val) ((val) & 0xff)
++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
++
++#define ELF64_R_SYM(i) ((i) >> 32)
++#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type))
++
++/* Program segment header. */
++
++typedef struct
++{
++ Elf32_Word p_type; /* Segment type */
++ Elf32_Off p_offset; /* Segment file offset */
++ Elf32_Addr p_vaddr; /* Segment virtual address */
++ Elf32_Addr p_paddr; /* Segment physical address */
++ Elf32_Word p_filesz; /* Segment size in file */
++ Elf32_Word p_memsz; /* Segment size in memory */
++ Elf32_Word p_flags; /* Segment flags */
++ Elf32_Word p_align; /* Segment alignment */
++} Elf32_Phdr;
++
++typedef struct
++{
++ Elf64_Word p_type; /* Segment type */
++ Elf64_Word p_flags; /* Segment flags */
++ Elf64_Off p_offset; /* Segment file offset */
++ Elf64_Addr p_vaddr; /* Segment virtual address */
++ Elf64_Addr p_paddr; /* Segment physical address */
++ Elf64_Xword p_filesz; /* Segment size in file */
++ Elf64_Xword p_memsz; /* Segment size in memory */
++ Elf64_Xword p_align; /* Segment alignment */
++} Elf64_Phdr;
++
++/* Special value for e_phnum. This indicates that the real number of
++ program headers is too large to fit into e_phnum. Instead the real
++ value is in the field sh_info of section 0. */
++
++#define PN_XNUM 0xffff
++
++/* Legal values for p_type (segment type). */
++
++#define PT_NULL 0 /* Program header table entry unused */
++#define PT_LOAD 1 /* Loadable program segment */
++#define PT_DYNAMIC 2 /* Dynamic linking information */
++#define PT_INTERP 3 /* Program interpreter */
++#define PT_NOTE 4 /* Auxiliary information */
++#define PT_SHLIB 5 /* Reserved */
++#define PT_PHDR 6 /* Entry for header table itself */
++#define PT_TLS 7 /* Thread-local storage segment */
++#define PT_NUM 8 /* Number of defined types */
++#define PT_LOOS 0x60000000 /* Start of OS-specific */
++#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */
++#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */
++#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */
++#define PT_LOSUNW 0x6ffffffa
++#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */
++#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */
++#define PT_HISUNW 0x6fffffff
++#define PT_HIOS 0x6fffffff /* End of OS-specific */
++#define PT_LOPROC 0x70000000 /* Start of processor-specific */
++#define PT_HIPROC 0x7fffffff /* End of processor-specific */
++
++/* Legal values for p_flags (segment flags). */
++
++#define PF_X (1 << 0) /* Segment is executable */
++#define PF_W (1 << 1) /* Segment is writable */
++#define PF_R (1 << 2) /* Segment is readable */
++#define PF_MASKOS 0x0ff00000 /* OS-specific */
++#define PF_MASKPROC 0xf0000000 /* Processor-specific */
++
++/* Legal values for note segment descriptor types for core files. */
++
++#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */
++#define NT_FPREGSET 2 /* Contains copy of fpregset struct */
++#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */
++#define NT_PRXREG 4 /* Contains copy of prxregset struct */
++#define NT_TASKSTRUCT 4 /* Contains copy of task structure */
++#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */
++#define NT_AUXV 6 /* Contains copy of auxv array */
++#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */
++#define NT_ASRS 8 /* Contains copy of asrset struct */
++#define NT_PSTATUS 10 /* Contains copy of pstatus struct */
++#define NT_PSINFO 13 /* Contains copy of psinfo struct */
++#define NT_PRCRED 14 /* Contains copy of prcred struct */
++#define NT_UTSNAME 15 /* Contains copy of utsname struct */
++#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */
++#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */
++#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */
++#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */
++#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
++#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
++#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
++#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
++#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
++#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
++
++/* Legal values for the note segment descriptor types for object files. */
++
++#define NT_VERSION 1 /* Contains a version string. */
++
++
++/* Dynamic section entry. */
++
++typedef struct
++{
++ Elf32_Sword d_tag; /* Dynamic entry type */
++ union
++ {
++ Elf32_Word d_val; /* Integer value */
++ Elf32_Addr d_ptr; /* Address value */
++ } d_un;
++} Elf32_Dyn;
++
++typedef struct
++{
++ Elf64_Sxword d_tag; /* Dynamic entry type */
++ union
++ {
++ Elf64_Xword d_val; /* Integer value */
++ Elf64_Addr d_ptr; /* Address value */
++ } d_un;
++} Elf64_Dyn;
++
++/* Legal values for d_tag (dynamic entry type). */
++
++#define DT_NULL 0 /* Marks end of dynamic section */
++#define DT_NEEDED 1 /* Name of needed library */
++#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */
++#define DT_PLTGOT 3 /* Processor defined value */
++#define DT_HASH 4 /* Address of symbol hash table */
++#define DT_STRTAB 5 /* Address of string table */
++#define DT_SYMTAB 6 /* Address of symbol table */
++#define DT_RELA 7 /* Address of Rela relocs */
++#define DT_RELASZ 8 /* Total size of Rela relocs */
++#define DT_RELAENT 9 /* Size of one Rela reloc */
++#define DT_STRSZ 10 /* Size of string table */
++#define DT_SYMENT 11 /* Size of one symbol table entry */
++#define DT_INIT 12 /* Address of init function */
++#define DT_FINI 13 /* Address of termination function */
++#define DT_SONAME 14 /* Name of shared object */
++#define DT_RPATH 15 /* Library search path (deprecated) */
++#define DT_SYMBOLIC 16 /* Start symbol search here */
++#define DT_REL 17 /* Address of Rel relocs */
++#define DT_RELSZ 18 /* Total size of Rel relocs */
++#define DT_RELENT 19 /* Size of one Rel reloc */
++#define DT_PLTREL 20 /* Type of reloc in PLT */
++#define DT_DEBUG 21 /* For debugging; unspecified */
++#define DT_TEXTREL 22 /* Reloc might modify .text */
++#define DT_JMPREL 23 /* Address of PLT relocs */
++#define DT_BIND_NOW 24 /* Process relocations of object */
++#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */
++#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */
++#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */
++#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */
++#define DT_RUNPATH 29 /* Library search path */
++#define DT_FLAGS 30 /* Flags for the object being loaded */
++#define DT_ENCODING 32 /* Start of encoded range */
++#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/
++#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */
++#define DT_NUM 34 /* Number used */
++#define DT_LOOS 0x6000000d /* Start of OS-specific */
++#define DT_HIOS 0x6ffff000 /* End of OS-specific */
++#define DT_LOPROC 0x70000000 /* Start of processor-specific */
++#define DT_HIPROC 0x7fffffff /* End of processor-specific */
++#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */
++
++/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
++ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's
++ approach. */
++#define DT_VALRNGLO 0x6ffffd00
++#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */
++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */
++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */
++#define DT_CHECKSUM 0x6ffffdf8
++#define DT_PLTPADSZ 0x6ffffdf9
++#define DT_MOVEENT 0x6ffffdfa
++#define DT_MOVESZ 0x6ffffdfb
++#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */
++#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting
++ the following DT_* entry. */
++#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */
++#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */
++#define DT_VALRNGHI 0x6ffffdff
++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */
++#define DT_VALNUM 12
++
++/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
++ Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
++
++ If any adjustment is made to the ELF object after it has been
++ built these entries will need to be adjusted. */
++#define DT_ADDRRNGLO 0x6ffffe00
++#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */
++#define DT_TLSDESC_PLT 0x6ffffef6
++#define DT_TLSDESC_GOT 0x6ffffef7
++#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */
++#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */
++#define DT_CONFIG 0x6ffffefa /* Configuration information. */
++#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */
++#define DT_AUDIT 0x6ffffefc /* Object auditing. */
++#define DT_PLTPAD 0x6ffffefd /* PLT padding. */
++#define DT_MOVETAB 0x6ffffefe /* Move table. */
++#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */
++#define DT_ADDRRNGHI 0x6ffffeff
++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */
++#define DT_ADDRNUM 11
++
++/* The versioning entry types. The next are defined as part of the
++ GNU extension. */
++#define DT_VERSYM 0x6ffffff0
++
++#define DT_RELACOUNT 0x6ffffff9
++#define DT_RELCOUNT 0x6ffffffa
++
++/* These were chosen by Sun. */
++#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */
++#define DT_VERDEF 0x6ffffffc /* Address of version definition
++ table */
++#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */
++#define DT_VERNEED 0x6ffffffe /* Address of table with needed
++ versions */
++#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */
++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */
++#define DT_VERSIONTAGNUM 16
++
++/* Sun added these machine-independent extensions in the "processor-specific"
++ range. Be compatible. */
++#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */
++#define DT_FILTER 0x7fffffff /* Shared object to get values from */
++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
++#define DT_EXTRANUM 3
++
++/* Values of `d_un.d_val' in the DT_FLAGS entry. */
++#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */
++#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */
++#define DF_TEXTREL 0x00000004 /* Object contains text relocations */
++#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */
++#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */
++
++/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1
++ entry in the dynamic section. */
++#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */
++#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */
++#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */
++#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/
++#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/
++#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/
++#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */
++#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */
++#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */
++#define DF_1_TRANS 0x00000200
++#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */
++#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */
++#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */
++#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/
++#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */
++#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */
++#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */
++
++/* Flags for the feature selection in DT_FEATURE_1. */
++#define DTF_1_PARINIT 0x00000001
++#define DTF_1_CONFEXP 0x00000002
++
++/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */
++#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */
++#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not
++ generally available. */
++
++/* Version definition sections. */
++
++typedef struct
++{
++ Elf32_Half vd_version; /* Version revision */
++ Elf32_Half vd_flags; /* Version information */
++ Elf32_Half vd_ndx; /* Version Index */
++ Elf32_Half vd_cnt; /* Number of associated aux entries */
++ Elf32_Word vd_hash; /* Version name hash value */
++ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */
++ Elf32_Word vd_next; /* Offset in bytes to next verdef
++ entry */
++} Elf32_Verdef;
++
++typedef struct
++{
++ Elf64_Half vd_version; /* Version revision */
++ Elf64_Half vd_flags; /* Version information */
++ Elf64_Half vd_ndx; /* Version Index */
++ Elf64_Half vd_cnt; /* Number of associated aux entries */
++ Elf64_Word vd_hash; /* Version name hash value */
++ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */
++ Elf64_Word vd_next; /* Offset in bytes to next verdef
++ entry */
++} Elf64_Verdef;
++
++
++/* Legal values for vd_version (version revision). */
++#define VER_DEF_NONE 0 /* No version */
++#define VER_DEF_CURRENT 1 /* Current version */
++#define VER_DEF_NUM 2 /* Given version number */
++
++/* Legal values for vd_flags (version information flags). */
++#define VER_FLG_BASE 0x1 /* Version definition of file itself */
++#define VER_FLG_WEAK 0x2 /* Weak version identifier */
++
++/* Versym symbol index values. */
++#define VER_NDX_LOCAL 0 /* Symbol is local. */
++#define VER_NDX_GLOBAL 1 /* Symbol is global. */
++#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */
++#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */
++
++/* Auxialiary version information. */
++
++typedef struct
++{
++ Elf32_Word vda_name; /* Version or dependency names */
++ Elf32_Word vda_next; /* Offset in bytes to next verdaux
++ entry */
++} Elf32_Verdaux;
++
++typedef struct
++{
++ Elf64_Word vda_name; /* Version or dependency names */
++ Elf64_Word vda_next; /* Offset in bytes to next verdaux
++ entry */
++} Elf64_Verdaux;
++
++
++/* Version dependency section. */
++
++typedef struct
++{
++ Elf32_Half vn_version; /* Version of structure */
++ Elf32_Half vn_cnt; /* Number of associated aux entries */
++ Elf32_Word vn_file; /* Offset of filename for this
++ dependency */
++ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */
++ Elf32_Word vn_next; /* Offset in bytes to next verneed
++ entry */
++} Elf32_Verneed;
++
++typedef struct
++{
++ Elf64_Half vn_version; /* Version of structure */
++ Elf64_Half vn_cnt; /* Number of associated aux entries */
++ Elf64_Word vn_file; /* Offset of filename for this
++ dependency */
++ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */
++ Elf64_Word vn_next; /* Offset in bytes to next verneed
++ entry */
++} Elf64_Verneed;
++
++
++/* Legal values for vn_version (version revision). */
++#define VER_NEED_NONE 0 /* No version */
++#define VER_NEED_CURRENT 1 /* Current version */
++#define VER_NEED_NUM 2 /* Given version number */
++
++/* Auxiliary needed version information. */
++
++typedef struct
++{
++ Elf32_Word vna_hash; /* Hash value of dependency name */
++ Elf32_Half vna_flags; /* Dependency specific information */
++ Elf32_Half vna_other; /* Unused */
++ Elf32_Word vna_name; /* Dependency name string offset */
++ Elf32_Word vna_next; /* Offset in bytes to next vernaux
++ entry */
++} Elf32_Vernaux;
++
++typedef struct
++{
++ Elf64_Word vna_hash; /* Hash value of dependency name */
++ Elf64_Half vna_flags; /* Dependency specific information */
++ Elf64_Half vna_other; /* Unused */
++ Elf64_Word vna_name; /* Dependency name string offset */
++ Elf64_Word vna_next; /* Offset in bytes to next vernaux
++ entry */
++} Elf64_Vernaux;
++
++
++/* Legal values for vna_flags. */
++#define VER_FLG_WEAK 0x2 /* Weak version identifier */
++
++
++/* Auxiliary vector. */
++
++/* This vector is normally only used by the program interpreter. The
++ usual definition in an ABI supplement uses the name auxv_t. The
++ vector is not usually defined in a standard <elf.h> file, but it
++ can't hurt. We rename it to avoid conflicts. The sizes of these
++ types are an arrangement between the exec server and the program
++ interpreter, so we don't fully specify them here. */
++
++typedef struct
++{
++ uint32_t a_type; /* Entry type */
++ union
++ {
++ uint32_t a_val; /* Integer value */
++ /* We use to have pointer elements added here. We cannot do that,
++ though, since it does not work when using 32-bit definitions
++ on 64-bit platforms and vice versa. */
++ } a_un;
++} Elf32_auxv_t;
++
++typedef struct
++{
++ uint64_t a_type; /* Entry type */
++ union
++ {
++ uint64_t a_val; /* Integer value */
++ /* We use to have pointer elements added here. We cannot do that,
++ though, since it does not work when using 32-bit definitions
++ on 64-bit platforms and vice versa. */
++ } a_un;
++} Elf64_auxv_t;
++
++/* Legal values for a_type (entry type). */
++
++#define AT_NULL 0 /* End of vector */
++#define AT_IGNORE 1 /* Entry should be ignored */
++#define AT_EXECFD 2 /* File descriptor of program */
++#define AT_PHDR 3 /* Program headers for program */
++#define AT_PHENT 4 /* Size of program header entry */
++#define AT_PHNUM 5 /* Number of program headers */
++#define AT_PAGESZ 6 /* System page size */
++#define AT_BASE 7 /* Base address of interpreter */
++#define AT_FLAGS 8 /* Flags */
++#define AT_ENTRY 9 /* Entry point of program */
++#define AT_NOTELF 10 /* Program is not ELF */
++#define AT_UID 11 /* Real uid */
++#define AT_EUID 12 /* Effective uid */
++#define AT_GID 13 /* Real gid */
++#define AT_EGID 14 /* Effective gid */
++#define AT_CLKTCK 17 /* Frequency of times() */
++
++/* Some more special a_type values describing the hardware. */
++#define AT_PLATFORM 15 /* String identifying platform. */
++#define AT_HWCAP 16 /* Machine dependent hints about
++ processor capabilities. */
++
++/* This entry gives some information about the FPU initialization
++ performed by the kernel. */
++#define AT_FPUCW 18 /* Used FPU control word. */
++
++/* Cache block sizes. */
++#define AT_DCACHEBSIZE 19 /* Data cache block size. */
++#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */
++#define AT_UCACHEBSIZE 21 /* Unified cache block size. */
++
++/* A special ignored value for PPC, used by the kernel to control the
++ interpretation of the AUXV. Must be > 16. */
++#define AT_IGNOREPPC 22 /* Entry should be ignored. */
++
++#define AT_SECURE 23 /* Boolean, was exec setuid-like? */
++
++#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/
++
++#define AT_RANDOM 25 /* Address of 16 random bytes. */
++
++#define AT_EXECFN 31 /* Filename of executable. */
++
++/* Pointer to the global system page used for system calls and other
++ nice things. */
++#define AT_SYSINFO 32
++#define AT_SYSINFO_EHDR 33
++
++/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains
++ log2 of line size; mask those to get cache size. */
++#define AT_L1I_CACHESHAPE 34
++#define AT_L1D_CACHESHAPE 35
++#define AT_L2_CACHESHAPE 36
++#define AT_L3_CACHESHAPE 37
++
++/* Note section contents. Each entry in the note section begins with
++ a header of a fixed form. */
++
++typedef struct
++{
++ Elf32_Word n_namesz; /* Length of the note's name. */
++ Elf32_Word n_descsz; /* Length of the note's descriptor. */
++ Elf32_Word n_type; /* Type of the note. */
++} Elf32_Nhdr;
++
++typedef struct
++{
++ Elf64_Word n_namesz; /* Length of the note's name. */
++ Elf64_Word n_descsz; /* Length of the note's descriptor. */
++ Elf64_Word n_type; /* Type of the note. */
++} Elf64_Nhdr;
++
++/* Known names of notes. */
++
++/* Solaris entries in the note section have this name. */
++#define ELF_NOTE_SOLARIS "SUNW Solaris"
++
++/* Note entries for GNU systems have this name. */
++#define ELF_NOTE_GNU "GNU"
++
++
++/* Defined types of notes for Solaris. */
++
++/* Value of descriptor (one word) is desired pagesize for the binary. */
++#define ELF_NOTE_PAGESIZE_HINT 1
++
++
++/* Defined note types for GNU systems. */
++
++/* ABI information. The descriptor consists of words:
++ word 0: OS descriptor
++ word 1: major version of the ABI
++ word 2: minor version of the ABI
++ word 3: subminor version of the ABI
++*/
++#define NT_GNU_ABI_TAG 1
++#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */
++
++/* Known OSes. These values can appear in word 0 of an
++ NT_GNU_ABI_TAG note section entry. */
++#define ELF_NOTE_OS_LINUX 0
++#define ELF_NOTE_OS_GNU 1
++#define ELF_NOTE_OS_SOLARIS2 2
++#define ELF_NOTE_OS_FREEBSD 3
++
++/* Synthetic hwcap information. The descriptor begins with two words:
++ word 0: number of entries
++ word 1: bitmask of enabled entries
++ Then follow variable-length entries, one byte followed by a
++ '\0'-terminated hwcap name string. The byte gives the bit
++ number to test if enabled, (1U << bit) & bitmask. */
++#define NT_GNU_HWCAP 2
++
++/* Build ID bits as generated by ld --build-id.
++ The descriptor consists of any nonzero number of bytes. */
++#define NT_GNU_BUILD_ID 3
++
++/* Version note generated by GNU gold containing a version string. */
++#define NT_GNU_GOLD_VERSION 4
++
++
++/* Move records. */
++typedef struct
++{
++ Elf32_Xword m_value; /* Symbol value. */
++ Elf32_Word m_info; /* Size and index. */
++ Elf32_Word m_poffset; /* Symbol offset. */
++ Elf32_Half m_repeat; /* Repeat count. */
++ Elf32_Half m_stride; /* Stride info. */
++} Elf32_Move;
++
++typedef struct
++{
++ Elf64_Xword m_value; /* Symbol value. */
++ Elf64_Xword m_info; /* Size and index. */
++ Elf64_Xword m_poffset; /* Symbol offset. */
++ Elf64_Half m_repeat; /* Repeat count. */
++ Elf64_Half m_stride; /* Stride info. */
++} Elf64_Move;
++
++/* Macro to construct move records. */
++#define ELF32_M_SYM(info) ((info) >> 8)
++#define ELF32_M_SIZE(info) ((unsigned char) (info))
++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size))
++
++#define ELF64_M_SYM(info) ELF32_M_SYM (info)
++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info)
++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size)
++
++
++/* Motorola 68k specific definitions. */
++
++/* Values for Elf32_Ehdr.e_flags. */
++#define EF_CPU32 0x00810000
++
++/* m68k relocs. */
++
++#define R_68K_NONE 0 /* No reloc */
++#define R_68K_32 1 /* Direct 32 bit */
++#define R_68K_16 2 /* Direct 16 bit */
++#define R_68K_8 3 /* Direct 8 bit */
++#define R_68K_PC32 4 /* PC relative 32 bit */
++#define R_68K_PC16 5 /* PC relative 16 bit */
++#define R_68K_PC8 6 /* PC relative 8 bit */
++#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */
++#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */
++#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */
++#define R_68K_GOT32O 10 /* 32 bit GOT offset */
++#define R_68K_GOT16O 11 /* 16 bit GOT offset */
++#define R_68K_GOT8O 12 /* 8 bit GOT offset */
++#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */
++#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */
++#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */
++#define R_68K_PLT32O 16 /* 32 bit PLT offset */
++#define R_68K_PLT16O 17 /* 16 bit PLT offset */
++#define R_68K_PLT8O 18 /* 8 bit PLT offset */
++#define R_68K_COPY 19 /* Copy symbol at runtime */
++#define R_68K_GLOB_DAT 20 /* Create GOT entry */
++#define R_68K_JMP_SLOT 21 /* Create PLT entry */
++#define R_68K_RELATIVE 22 /* Adjust by program base */
++#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */
++#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */
++#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */
++#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */
++#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */
++#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */
++#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */
++#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */
++#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */
++#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */
++#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */
++#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */
++#define R_68K_TLS_LE32 37 /* 32 bit offset relative to
++ static TLS block */
++#define R_68K_TLS_LE16 38 /* 16 bit offset relative to
++ static TLS block */
++#define R_68K_TLS_LE8 39 /* 8 bit offset relative to
++ static TLS block */
++#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */
++#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */
++#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */
++/* Keep this the last entry. */
++#define R_68K_NUM 43
++
++/* Intel 80386 specific definitions. */
++
++/* i386 relocs. */
++
++#define R_386_NONE 0 /* No reloc */
++#define R_386_32 1 /* Direct 32 bit */
++#define R_386_PC32 2 /* PC relative 32 bit */
++#define R_386_GOT32 3 /* 32 bit GOT entry */
++#define R_386_PLT32 4 /* 32 bit PLT address */
++#define R_386_COPY 5 /* Copy symbol at runtime */
++#define R_386_GLOB_DAT 6 /* Create GOT entry */
++#define R_386_JMP_SLOT 7 /* Create PLT entry */
++#define R_386_RELATIVE 8 /* Adjust by program base */
++#define R_386_GOTOFF 9 /* 32 bit offset to GOT */
++#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */
++#define R_386_32PLT 11
++#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */
++#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS
++ block offset */
++#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block
++ offset */
++#define R_386_TLS_LE 17 /* Offset relative to static TLS
++ block */
++#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of
++ general dynamic thread local data */
++#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of
++ local dynamic thread local data
++ in LE code */
++#define R_386_16 20
++#define R_386_PC16 21
++#define R_386_8 22
++#define R_386_PC8 23
++#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic
++ thread local data */
++#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */
++#define R_386_TLS_GD_CALL 26 /* Relocation for call to
++ __tls_get_addr() */
++#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */
++#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic
++ thread local data in LE code */
++#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */
++#define R_386_TLS_LDM_CALL 30 /* Relocation for call to
++ __tls_get_addr() in LDM code */
++#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */
++#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */
++#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS
++ block offset */
++#define R_386_TLS_LE_32 34 /* Negated offset relative to static
++ TLS block */
++#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */
++#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */
++#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */
++/* 38? */
++#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */
++#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS
++ descriptor for
++ relaxation. */
++#define R_386_TLS_DESC 41 /* TLS descriptor containing
++ pointer to code and to
++ argument, returning the TLS
++ offset for the symbol. */
++#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */
++/* Keep this the last entry. */
++#define R_386_NUM 43
++
++/* SUN SPARC specific definitions. */
++
++/* Legal values for ST_TYPE subfield of st_info (symbol type). */
++
++#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */
++
++/* Values for Elf64_Ehdr.e_flags. */
++
++#define EF_SPARCV9_MM 3
++#define EF_SPARCV9_TSO 0
++#define EF_SPARCV9_PSO 1
++#define EF_SPARCV9_RMO 2
++#define EF_SPARC_LEDATA 0x800000 /* little endian data */
++#define EF_SPARC_EXT_MASK 0xFFFF00
++#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */
++#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */
++#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */
++#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */
++
++/* SPARC relocs. */
++
++#define R_SPARC_NONE 0 /* No reloc */
++#define R_SPARC_8 1 /* Direct 8 bit */
++#define R_SPARC_16 2 /* Direct 16 bit */
++#define R_SPARC_32 3 /* Direct 32 bit */
++#define R_SPARC_DISP8 4 /* PC relative 8 bit */
++#define R_SPARC_DISP16 5 /* PC relative 16 bit */
++#define R_SPARC_DISP32 6 /* PC relative 32 bit */
++#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */
++#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */
++#define R_SPARC_HI22 9 /* High 22 bit */
++#define R_SPARC_22 10 /* Direct 22 bit */
++#define R_SPARC_13 11 /* Direct 13 bit */
++#define R_SPARC_LO10 12 /* Truncated 10 bit */
++#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */
++#define R_SPARC_GOT13 14 /* 13 bit GOT entry */
++#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */
++#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */
++#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */
++#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */
++#define R_SPARC_COPY 19 /* Copy symbol at runtime */
++#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */
++#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */
++#define R_SPARC_RELATIVE 22 /* Adjust by program base */
++#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */
++
++/* Additional Sparc64 relocs. */
++
++#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */
++#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */
++#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */
++#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */
++#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */
++#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */
++#define R_SPARC_10 30 /* Direct 10 bit */
++#define R_SPARC_11 31 /* Direct 11 bit */
++#define R_SPARC_64 32 /* Direct 64 bit */
++#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */
++#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */
++#define R_SPARC_HM10 35 /* High middle 10 bits of ... */
++#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */
++#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */
++#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */
++#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */
++#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */
++#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */
++#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */
++#define R_SPARC_7 43 /* Direct 7 bit */
++#define R_SPARC_5 44 /* Direct 5 bit */
++#define R_SPARC_6 45 /* Direct 6 bit */
++#define R_SPARC_DISP64 46 /* PC relative 64 bit */
++#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */
++#define R_SPARC_HIX22 48 /* High 22 bit complemented */
++#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */
++#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */
++#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */
++#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */
++#define R_SPARC_REGISTER 53 /* Global register usage */
++#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */
++#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */
++#define R_SPARC_TLS_GD_HI22 56
++#define R_SPARC_TLS_GD_LO10 57
++#define R_SPARC_TLS_GD_ADD 58
++#define R_SPARC_TLS_GD_CALL 59
++#define R_SPARC_TLS_LDM_HI22 60
++#define R_SPARC_TLS_LDM_LO10 61
++#define R_SPARC_TLS_LDM_ADD 62
++#define R_SPARC_TLS_LDM_CALL 63
++#define R_SPARC_TLS_LDO_HIX22 64
++#define R_SPARC_TLS_LDO_LOX10 65
++#define R_SPARC_TLS_LDO_ADD 66
++#define R_SPARC_TLS_IE_HI22 67
++#define R_SPARC_TLS_IE_LO10 68
++#define R_SPARC_TLS_IE_LD 69
++#define R_SPARC_TLS_IE_LDX 70
++#define R_SPARC_TLS_IE_ADD 71
++#define R_SPARC_TLS_LE_HIX22 72
++#define R_SPARC_TLS_LE_LOX10 73
++#define R_SPARC_TLS_DTPMOD32 74
++#define R_SPARC_TLS_DTPMOD64 75
++#define R_SPARC_TLS_DTPOFF32 76
++#define R_SPARC_TLS_DTPOFF64 77
++#define R_SPARC_TLS_TPOFF32 78
++#define R_SPARC_TLS_TPOFF64 79
++#define R_SPARC_GOTDATA_HIX22 80
++#define R_SPARC_GOTDATA_LOX10 81
++#define R_SPARC_GOTDATA_OP_HIX22 82
++#define R_SPARC_GOTDATA_OP_LOX10 83
++#define R_SPARC_GOTDATA_OP 84
++#define R_SPARC_H34 85
++#define R_SPARC_SIZE32 86
++#define R_SPARC_SIZE64 87
++#define R_SPARC_WDISP10 88
++#define R_SPARC_JMP_IREL 248
++#define R_SPARC_IRELATIVE 249
++#define R_SPARC_GNU_VTINHERIT 250
++#define R_SPARC_GNU_VTENTRY 251
++#define R_SPARC_REV32 252
++/* Keep this the last entry. */
++#define R_SPARC_NUM 253
++
++/* For Sparc64, legal values for d_tag of Elf64_Dyn. */
++
++#define DT_SPARC_REGISTER 0x70000001
++#define DT_SPARC_NUM 2
++
++/* MIPS R3000 specific definitions. */
++
++/* Legal values for e_flags field of Elf32_Ehdr. */
++
++#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */
++#define EF_MIPS_PIC 2 /* Contains PIC code */
++#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */
++#define EF_MIPS_XGOT 8
++#define EF_MIPS_64BIT_WHIRL 16
++#define EF_MIPS_ABI2 32
++#define EF_MIPS_ABI_ON32 64
++#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */
++
++/* Legal values for MIPS architecture level. */
++
++#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
++#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
++#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
++#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
++#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
++#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */
++#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */
++
++/* The following are non-official names and should not be used. */
++
++#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
++#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
++#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
++#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
++#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
++#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */
++#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */
++
++/* Special section indices. */
++
++#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */
++#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */
++#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */
++#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */
++#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */
++
++/* Legal values for sh_type field of Elf32_Shdr. */
++
++#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */
++#define SHT_MIPS_MSYM 0x70000001
++#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */
++#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */
++#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */
++#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/
++#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */
++#define SHT_MIPS_PACKAGE 0x70000007
++#define SHT_MIPS_PACKSYM 0x70000008
++#define SHT_MIPS_RELD 0x70000009
++#define SHT_MIPS_IFACE 0x7000000b
++#define SHT_MIPS_CONTENT 0x7000000c
++#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */
++#define SHT_MIPS_SHDR 0x70000010
++#define SHT_MIPS_FDESC 0x70000011
++#define SHT_MIPS_EXTSYM 0x70000012
++#define SHT_MIPS_DENSE 0x70000013
++#define SHT_MIPS_PDESC 0x70000014
++#define SHT_MIPS_LOCSYM 0x70000015
++#define SHT_MIPS_AUXSYM 0x70000016
++#define SHT_MIPS_OPTSYM 0x70000017
++#define SHT_MIPS_LOCSTR 0x70000018
++#define SHT_MIPS_LINE 0x70000019
++#define SHT_MIPS_RFDESC 0x7000001a
++#define SHT_MIPS_DELTASYM 0x7000001b
++#define SHT_MIPS_DELTAINST 0x7000001c
++#define SHT_MIPS_DELTACLASS 0x7000001d
++#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */
++#define SHT_MIPS_DELTADECL 0x7000001f
++#define SHT_MIPS_SYMBOL_LIB 0x70000020
++#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */
++#define SHT_MIPS_TRANSLATE 0x70000022
++#define SHT_MIPS_PIXIE 0x70000023
++#define SHT_MIPS_XLATE 0x70000024
++#define SHT_MIPS_XLATE_DEBUG 0x70000025
++#define SHT_MIPS_WHIRL 0x70000026
++#define SHT_MIPS_EH_REGION 0x70000027
++#define SHT_MIPS_XLATE_OLD 0x70000028
++#define SHT_MIPS_PDR_EXCEPTION 0x70000029
++
++/* Legal values for sh_flags field of Elf32_Shdr. */
++
++#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */
++#define SHF_MIPS_MERGE 0x20000000
++#define SHF_MIPS_ADDR 0x40000000
++#define SHF_MIPS_STRINGS 0x80000000
++#define SHF_MIPS_NOSTRIP 0x08000000
++#define SHF_MIPS_LOCAL 0x04000000
++#define SHF_MIPS_NAMES 0x02000000
++#define SHF_MIPS_NODUPE 0x01000000
++
++
++/* Symbol tables. */
++
++/* MIPS specific values for `st_other'. */
++#define STO_MIPS_DEFAULT 0x0
++#define STO_MIPS_INTERNAL 0x1
++#define STO_MIPS_HIDDEN 0x2
++#define STO_MIPS_PROTECTED 0x3
++#define STO_MIPS_PLT 0x8
++#define STO_MIPS_SC_ALIGN_UNUSED 0xff
++
++/* MIPS specific values for `st_info'. */
++#define STB_MIPS_SPLIT_COMMON 13
++
++/* Entries found in sections of type SHT_MIPS_GPTAB. */
++
++typedef union
++{
++ struct
++ {
++ Elf32_Word gt_current_g_value; /* -G value used for compilation */
++ Elf32_Word gt_unused; /* Not used */
++ } gt_header; /* First entry in section */
++ struct
++ {
++ Elf32_Word gt_g_value; /* If this value were used for -G */
++ Elf32_Word gt_bytes; /* This many bytes would be used */
++ } gt_entry; /* Subsequent entries in section */
++} Elf32_gptab;
++
++/* Entry found in sections of type SHT_MIPS_REGINFO. */
++
++typedef struct
++{
++ Elf32_Word ri_gprmask; /* General registers used */
++ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */
++ Elf32_Sword ri_gp_value; /* $gp register value */
++} Elf32_RegInfo;
++
++/* Entries found in sections of type SHT_MIPS_OPTIONS. */
++
++typedef struct
++{
++ unsigned char kind; /* Determines interpretation of the
++ variable part of descriptor. */
++ unsigned char size; /* Size of descriptor, including header. */
++ Elf32_Section section; /* Section header index of section affected,
++ 0 for global options. */
++ Elf32_Word info; /* Kind-specific information. */
++} Elf_Options;
++
++/* Values for `kind' field in Elf_Options. */
++
++#define ODK_NULL 0 /* Undefined. */
++#define ODK_REGINFO 1 /* Register usage information. */
++#define ODK_EXCEPTIONS 2 /* Exception processing options. */
++#define ODK_PAD 3 /* Section padding options. */
++#define ODK_HWPATCH 4 /* Hardware workarounds performed */
++#define ODK_FILL 5 /* record the fill value used by the linker. */
++#define ODK_TAGS 6 /* reserve space for desktop tools to write. */
++#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */
++#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */
++
++/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */
++
++#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */
++#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */
++#define OEX_PAGE0 0x10000 /* page zero must be mapped. */
++#define OEX_SMM 0x20000 /* Force sequential memory mode? */
++#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */
++#define OEX_PRECISEFP OEX_FPDBUG
++#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */
++
++#define OEX_FPU_INVAL 0x10
++#define OEX_FPU_DIV0 0x08
++#define OEX_FPU_OFLO 0x04
++#define OEX_FPU_UFLO 0x02
++#define OEX_FPU_INEX 0x01
++
++/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */
++
++#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */
++#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */
++#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */
++#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */
++
++#define OPAD_PREFIX 0x1
++#define OPAD_POSTFIX 0x2
++#define OPAD_SYMBOL 0x4
++
++/* Entry found in `.options' section. */
++
++typedef struct
++{
++ Elf32_Word hwp_flags1; /* Extra flags. */
++ Elf32_Word hwp_flags2; /* Extra flags. */
++} Elf_Options_Hw;
++
++/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */
++
++#define OHWA0_R4KEOP_CHECKED 0x00000001
++#define OHWA1_R4KEOP_CLEAN 0x00000002
++
++/* MIPS relocs. */
++
++#define R_MIPS_NONE 0 /* No reloc */
++#define R_MIPS_16 1 /* Direct 16 bit */
++#define R_MIPS_32 2 /* Direct 32 bit */
++#define R_MIPS_REL32 3 /* PC relative 32 bit */
++#define R_MIPS_26 4 /* Direct 26 bit shifted */
++#define R_MIPS_HI16 5 /* High 16 bit */
++#define R_MIPS_LO16 6 /* Low 16 bit */
++#define R_MIPS_GPREL16 7 /* GP relative 16 bit */
++#define R_MIPS_LITERAL 8 /* 16 bit literal entry */
++#define R_MIPS_GOT16 9 /* 16 bit GOT entry */
++#define R_MIPS_PC16 10 /* PC relative 16 bit */
++#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */
++#define R_MIPS_GPREL32 12 /* GP relative 32 bit */
++
++#define R_MIPS_SHIFT5 16
++#define R_MIPS_SHIFT6 17
++#define R_MIPS_64 18
++#define R_MIPS_GOT_DISP 19
++#define R_MIPS_GOT_PAGE 20
++#define R_MIPS_GOT_OFST 21
++#define R_MIPS_GOT_HI16 22
++#define R_MIPS_GOT_LO16 23
++#define R_MIPS_SUB 24
++#define R_MIPS_INSERT_A 25
++#define R_MIPS_INSERT_B 26
++#define R_MIPS_DELETE 27
++#define R_MIPS_HIGHER 28
++#define R_MIPS_HIGHEST 29
++#define R_MIPS_CALL_HI16 30
++#define R_MIPS_CALL_LO16 31
++#define R_MIPS_SCN_DISP 32
++#define R_MIPS_REL16 33
++#define R_MIPS_ADD_IMMEDIATE 34
++#define R_MIPS_PJUMP 35
++#define R_MIPS_RELGOT 36
++#define R_MIPS_JALR 37
++#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */
++#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */
++#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */
++#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */
++#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */
++#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */
++#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */
++#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */
++#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */
++#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */
++#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */
++#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */
++#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */
++#define R_MIPS_GLOB_DAT 51
++#define R_MIPS_COPY 126
++#define R_MIPS_JUMP_SLOT 127
++/* Keep this the last entry. */
++#define R_MIPS_NUM 128
++
++/* Legal values for p_type field of Elf32_Phdr. */
++
++#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */
++#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */
++#define PT_MIPS_OPTIONS 0x70000002
++
++/* Special program header types. */
++
++#define PF_MIPS_LOCAL 0x10000000
++
++/* Legal values for d_tag field of Elf32_Dyn. */
++
++#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */
++#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */
++#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */
++#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */
++#define DT_MIPS_FLAGS 0x70000005 /* Flags */
++#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */
++#define DT_MIPS_MSYM 0x70000007
++#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */
++#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */
++#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */
++#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */
++#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */
++#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */
++#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */
++#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */
++#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */
++#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */
++#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */
++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in
++ DT_MIPS_DELTA_CLASS. */
++#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */
++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in
++ DT_MIPS_DELTA_INSTANCE. */
++#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */
++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in
++ DT_MIPS_DELTA_RELOC. */
++#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta
++ relocations refer to. */
++#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in
++ DT_MIPS_DELTA_SYM. */
++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the
++ class declaration. */
++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in
++ DT_MIPS_DELTA_CLASSSYM. */
++#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */
++#define DT_MIPS_PIXIE_INIT 0x70000023
++#define DT_MIPS_SYMBOL_LIB 0x70000024
++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025
++#define DT_MIPS_LOCAL_GOTIDX 0x70000026
++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027
++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028
++#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */
++#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */
++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b
++#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */
++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve
++ function stored in GOT. */
++#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added
++ by rld on dlopen() calls. */
++#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */
++#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */
++#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */
++/* The address of .got.plt in an executable using the new non-PIC ABI. */
++#define DT_MIPS_PLTGOT 0x70000032
++/* The base of the PLT in an executable using the new non-PIC ABI if that
++ PLT is writable. For a non-writable PLT, this is omitted or has a zero
++ value. */
++#define DT_MIPS_RWPLT 0x70000034
++#define DT_MIPS_NUM 0x35
++
++/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */
++
++#define RHF_NONE 0 /* No flags */
++#define RHF_QUICKSTART (1 << 0) /* Use quickstart */
++#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */
++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */
++#define RHF_NO_MOVE (1 << 3)
++#define RHF_SGI_ONLY (1 << 4)
++#define RHF_GUARANTEE_INIT (1 << 5)
++#define RHF_DELTA_C_PLUS_PLUS (1 << 6)
++#define RHF_GUARANTEE_START_INIT (1 << 7)
++#define RHF_PIXIE (1 << 8)
++#define RHF_DEFAULT_DELAY_LOAD (1 << 9)
++#define RHF_REQUICKSTART (1 << 10)
++#define RHF_REQUICKSTARTED (1 << 11)
++#define RHF_CORD (1 << 12)
++#define RHF_NO_UNRES_UNDEF (1 << 13)
++#define RHF_RLD_ORDER_SAFE (1 << 14)
++
++/* Entries found in sections of type SHT_MIPS_LIBLIST. */
++
++typedef struct
++{
++ Elf32_Word l_name; /* Name (string table index) */
++ Elf32_Word l_time_stamp; /* Timestamp */
++ Elf32_Word l_checksum; /* Checksum */
++ Elf32_Word l_version; /* Interface version */
++ Elf32_Word l_flags; /* Flags */
++} Elf32_Lib;
++
++typedef struct
++{
++ Elf64_Word l_name; /* Name (string table index) */
++ Elf64_Word l_time_stamp; /* Timestamp */
++ Elf64_Word l_checksum; /* Checksum */
++ Elf64_Word l_version; /* Interface version */
++ Elf64_Word l_flags; /* Flags */
++} Elf64_Lib;
++
++
++/* Legal values for l_flags. */
++
++#define LL_NONE 0
++#define LL_EXACT_MATCH (1 << 0) /* Require exact match */
++#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */
++#define LL_REQUIRE_MINOR (1 << 2)
++#define LL_EXPORTS (1 << 3)
++#define LL_DELAY_LOAD (1 << 4)
++#define LL_DELTA (1 << 5)
++
++/* Entries found in sections of type SHT_MIPS_CONFLICT. */
++
++typedef Elf32_Addr Elf32_Conflict;
++
++
++/* HPPA specific definitions. */
++
++/* Legal values for e_flags field of Elf32_Ehdr. */
++
++#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */
++#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */
++#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */
++#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */
++#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch
++ prediction. */
++#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */
++#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */
++
++/* Defined values for `e_flags & EF_PARISC_ARCH' are: */
++
++#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */
++#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */
++#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */
++
++/* Additional section indeces. */
++
++#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared
++ symbols in ANSI C. */
++#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */
++
++/* Legal values for sh_type field of Elf32_Shdr. */
++
++#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */
++#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */
++#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */
++
++/* Legal values for sh_flags field of Elf32_Shdr. */
++
++#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */
++#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */
++#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */
++
++/* Legal values for ST_TYPE subfield of st_info (symbol type). */
++
++#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */
++
++#define STT_HP_OPAQUE (STT_LOOS + 0x1)
++#define STT_HP_STUB (STT_LOOS + 0x2)
++
++/* HPPA relocs. */
++
++#define R_PARISC_NONE 0 /* No reloc. */
++#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */
++#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */
++#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */
++#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */
++#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */
++#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */
++#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */
++#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */
++#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */
++#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */
++#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */
++#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */
++#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */
++#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */
++#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */
++#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */
++#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */
++#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */
++#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */
++#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */
++#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */
++#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */
++#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */
++#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */
++#define R_PARISC_FPTR64 64 /* 64 bits function address. */
++#define R_PARISC_PLABEL32 65 /* 32 bits function address. */
++#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */
++#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */
++#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */
++#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */
++#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */
++#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */
++#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */
++#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */
++#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */
++#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */
++#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */
++#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */
++#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */
++#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */
++#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */
++#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */
++#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */
++#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */
++#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */
++#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */
++#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */
++#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */
++#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */
++#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */
++#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */
++#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */
++#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */
++#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */
++#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */
++#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */
++#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */
++#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */
++#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */
++#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */
++#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */
++#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */
++#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */
++#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */
++#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */
++#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */
++#define R_PARISC_LORESERVE 128
++#define R_PARISC_COPY 128 /* Copy relocation. */
++#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */
++#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */
++#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */
++#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */
++#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */
++#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */
++#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/
++#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */
++#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */
++#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */
++#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */
++#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */
++#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */
++#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */
++#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */
++#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/
++#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/
++#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */
++#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */
++#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */
++#define R_PARISC_GNU_VTENTRY 232
++#define R_PARISC_GNU_VTINHERIT 233
++#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */
++#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */
++#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */
++#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */
++#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */
++#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */
++#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */
++#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */
++#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */
++#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */
++#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */
++#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */
++#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L
++#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R
++#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L
++#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R
++#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32
++#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64
++#define R_PARISC_HIRESERVE 255
++
++/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */
++
++#define PT_HP_TLS (PT_LOOS + 0x0)
++#define PT_HP_CORE_NONE (PT_LOOS + 0x1)
++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2)
++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3)
++#define PT_HP_CORE_COMM (PT_LOOS + 0x4)
++#define PT_HP_CORE_PROC (PT_LOOS + 0x5)
++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6)
++#define PT_HP_CORE_STACK (PT_LOOS + 0x7)
++#define PT_HP_CORE_SHM (PT_LOOS + 0x8)
++#define PT_HP_CORE_MMF (PT_LOOS + 0x9)
++#define PT_HP_PARALLEL (PT_LOOS + 0x10)
++#define PT_HP_FASTBIND (PT_LOOS + 0x11)
++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12)
++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13)
++#define PT_HP_STACK (PT_LOOS + 0x14)
++
++#define PT_PARISC_ARCHEXT 0x70000000
++#define PT_PARISC_UNWIND 0x70000001
++
++/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */
++
++#define PF_PARISC_SBP 0x08000000
++
++#define PF_HP_PAGE_SIZE 0x00100000
++#define PF_HP_FAR_SHARED 0x00200000
++#define PF_HP_NEAR_SHARED 0x00400000
++#define PF_HP_CODE 0x01000000
++#define PF_HP_MODIFY 0x02000000
++#define PF_HP_LAZYSWAP 0x04000000
++#define PF_HP_SBP 0x08000000
++
++
++/* Alpha specific definitions. */
++
++/* Legal values for e_flags field of Elf64_Ehdr. */
++
++#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */
++#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */
++
++/* Legal values for sh_type field of Elf64_Shdr. */
++
++/* These two are primerily concerned with ECOFF debugging info. */
++#define SHT_ALPHA_DEBUG 0x70000001
++#define SHT_ALPHA_REGINFO 0x70000002
++
++/* Legal values for sh_flags field of Elf64_Shdr. */
++
++#define SHF_ALPHA_GPREL 0x10000000
++
++/* Legal values for st_other field of Elf64_Sym. */
++#define STO_ALPHA_NOPV 0x80 /* No PV required. */
++#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */
++
++/* Alpha relocs. */
++
++#define R_ALPHA_NONE 0 /* No reloc */
++#define R_ALPHA_REFLONG 1 /* Direct 32 bit */
++#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */
++#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */
++#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */
++#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */
++#define R_ALPHA_GPDISP 6 /* Add displacement to GP */
++#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */
++#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */
++#define R_ALPHA_SREL16 9 /* PC relative 16 bit */
++#define R_ALPHA_SREL32 10 /* PC relative 32 bit */
++#define R_ALPHA_SREL64 11 /* PC relative 64 bit */
++#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */
++#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */
++#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */
++#define R_ALPHA_COPY 24 /* Copy symbol at runtime */
++#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */
++#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */
++#define R_ALPHA_RELATIVE 27 /* Adjust by program base */
++#define R_ALPHA_TLS_GD_HI 28
++#define R_ALPHA_TLSGD 29
++#define R_ALPHA_TLS_LDM 30
++#define R_ALPHA_DTPMOD64 31
++#define R_ALPHA_GOTDTPREL 32
++#define R_ALPHA_DTPREL64 33
++#define R_ALPHA_DTPRELHI 34
++#define R_ALPHA_DTPRELLO 35
++#define R_ALPHA_DTPREL16 36
++#define R_ALPHA_GOTTPREL 37
++#define R_ALPHA_TPREL64 38
++#define R_ALPHA_TPRELHI 39
++#define R_ALPHA_TPRELLO 40
++#define R_ALPHA_TPREL16 41
++/* Keep this the last entry. */
++#define R_ALPHA_NUM 46
++
++/* Magic values of the LITUSE relocation addend. */
++#define LITUSE_ALPHA_ADDR 0
++#define LITUSE_ALPHA_BASE 1
++#define LITUSE_ALPHA_BYTOFF 2
++#define LITUSE_ALPHA_JSR 3
++#define LITUSE_ALPHA_TLS_GD 4
++#define LITUSE_ALPHA_TLS_LDM 5
++
++/* Legal values for d_tag of Elf64_Dyn. */
++#define DT_ALPHA_PLTRO (DT_LOPROC + 0)
++#define DT_ALPHA_NUM 1
++
++/* PowerPC specific declarations */
++
++/* Values for Elf32/64_Ehdr.e_flags. */
++#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */
++
++/* Cygnus local bits below */
++#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/
++#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib
++ flag */
++
++/* PowerPC relocations defined by the ABIs */
++#define R_PPC_NONE 0
++#define R_PPC_ADDR32 1 /* 32bit absolute address */
++#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */
++#define R_PPC_ADDR16 3 /* 16bit absolute address */
++#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */
++#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */
++#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */
++#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */
++#define R_PPC_ADDR14_BRTAKEN 8
++#define R_PPC_ADDR14_BRNTAKEN 9
++#define R_PPC_REL24 10 /* PC relative 26 bit */
++#define R_PPC_REL14 11 /* PC relative 16 bit */
++#define R_PPC_REL14_BRTAKEN 12
++#define R_PPC_REL14_BRNTAKEN 13
++#define R_PPC_GOT16 14
++#define R_PPC_GOT16_LO 15
++#define R_PPC_GOT16_HI 16
++#define R_PPC_GOT16_HA 17
++#define R_PPC_PLTREL24 18
++#define R_PPC_COPY 19
++#define R_PPC_GLOB_DAT 20
++#define R_PPC_JMP_SLOT 21
++#define R_PPC_RELATIVE 22
++#define R_PPC_LOCAL24PC 23
++#define R_PPC_UADDR32 24
++#define R_PPC_UADDR16 25
++#define R_PPC_REL32 26
++#define R_PPC_PLT32 27
++#define R_PPC_PLTREL32 28
++#define R_PPC_PLT16_LO 29
++#define R_PPC_PLT16_HI 30
++#define R_PPC_PLT16_HA 31
++#define R_PPC_SDAREL16 32
++#define R_PPC_SECTOFF 33
++#define R_PPC_SECTOFF_LO 34
++#define R_PPC_SECTOFF_HI 35
++#define R_PPC_SECTOFF_HA 36
++
++/* PowerPC relocations defined for the TLS access ABI. */
++#define R_PPC_TLS 67 /* none (sym+add)@tls */
++#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */
++#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */
++#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */
++#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */
++#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */
++#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */
++#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */
++#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */
++#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */
++#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */
++#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */
++#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */
++#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */
++#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */
++#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */
++#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */
++#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */
++#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */
++#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */
++#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */
++#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */
++#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */
++#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */
++#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */
++#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */
++#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */
++#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */
++
++/* The remaining relocs are from the Embedded ELF ABI, and are not
++ in the SVR4 ELF ABI. */
++#define R_PPC_EMB_NADDR32 101
++#define R_PPC_EMB_NADDR16 102
++#define R_PPC_EMB_NADDR16_LO 103
++#define R_PPC_EMB_NADDR16_HI 104
++#define R_PPC_EMB_NADDR16_HA 105
++#define R_PPC_EMB_SDAI16 106
++#define R_PPC_EMB_SDA2I16 107
++#define R_PPC_EMB_SDA2REL 108
++#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */
++#define R_PPC_EMB_MRKREF 110
++#define R_PPC_EMB_RELSEC16 111
++#define R_PPC_EMB_RELST_LO 112
++#define R_PPC_EMB_RELST_HI 113
++#define R_PPC_EMB_RELST_HA 114
++#define R_PPC_EMB_BIT_FLD 115
++#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */
++
++/* Diab tool relocations. */
++#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */
++#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */
++#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */
++#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */
++#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */
++#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */
++
++/* GNU extension to support local ifunc. */
++#define R_PPC_IRELATIVE 248
++
++/* GNU relocs used in PIC code sequences. */
++#define R_PPC_REL16 249 /* half16 (sym+add-.) */
++#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */
++#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */
++#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */
++
++/* This is a phony reloc to handle any old fashioned TOC16 references
++ that may still be in object files. */
++#define R_PPC_TOC16 255
++
++/* PowerPC specific values for the Dyn d_tag field. */
++#define DT_PPC_GOT (DT_LOPROC + 0)
++#define DT_PPC_NUM 1
++
++/* PowerPC64 relocations defined by the ABIs */
++#define R_PPC64_NONE R_PPC_NONE
++#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */
++#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */
++#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */
++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */
++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */
++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */
++#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */
++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN
++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN
++#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */
++#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */
++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN
++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN
++#define R_PPC64_GOT16 R_PPC_GOT16
++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO
++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI
++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA
++
++#define R_PPC64_COPY R_PPC_COPY
++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT
++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT
++#define R_PPC64_RELATIVE R_PPC_RELATIVE
++
++#define R_PPC64_UADDR32 R_PPC_UADDR32
++#define R_PPC64_UADDR16 R_PPC_UADDR16
++#define R_PPC64_REL32 R_PPC_REL32
++#define R_PPC64_PLT32 R_PPC_PLT32
++#define R_PPC64_PLTREL32 R_PPC_PLTREL32
++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO
++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI
++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA
++
++#define R_PPC64_SECTOFF R_PPC_SECTOFF
++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO
++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI
++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA
++#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */
++#define R_PPC64_ADDR64 38 /* doubleword64 S + A */
++#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */
++#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */
++#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */
++#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */
++#define R_PPC64_UADDR64 43 /* doubleword64 S + A */
++#define R_PPC64_REL64 44 /* doubleword64 S + A - P */
++#define R_PPC64_PLT64 45 /* doubleword64 L + A */
++#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */
++#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */
++#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */
++#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */
++#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */
++#define R_PPC64_TOC 51 /* doubleword64 .TOC */
++#define R_PPC64_PLTGOT16 52 /* half16* M + A */
++#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */
++#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */
++#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */
++
++#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */
++#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */
++#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */
++#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */
++#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */
++#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */
++#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */
++#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */
++#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */
++#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */
++#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */
++
++/* PowerPC64 relocations defined for the TLS access ABI. */
++#define R_PPC64_TLS 67 /* none (sym+add)@tls */
++#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */
++#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */
++#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */
++#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */
++#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */
++#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */
++#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */
++#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */
++#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */
++#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */
++#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */
++#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */
++#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */
++#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */
++#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */
++#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */
++#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */
++#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */
++#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */
++#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */
++#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */
++#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */
++#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */
++#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */
++#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */
++#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */
++#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */
++#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */
++#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */
++#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */
++#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */
++#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */
++#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */
++#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */
++#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */
++#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */
++#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */
++#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */
++#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */
++
++/* GNU extension to support local ifunc. */
++#define R_PPC64_JMP_IREL 247
++#define R_PPC64_IRELATIVE 248
++#define R_PPC64_REL16 249 /* half16 (sym+add-.) */
++#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */
++#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */
++#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */
++
++/* PowerPC64 specific values for the Dyn d_tag field. */
++#define DT_PPC64_GLINK (DT_LOPROC + 0)
++#define DT_PPC64_OPD (DT_LOPROC + 1)
++#define DT_PPC64_OPDSZ (DT_LOPROC + 2)
++#define DT_PPC64_NUM 3
++
++
++/* ARM specific declarations */
++
++/* Processor specific flags for the ELF header e_flags field. */
++#define EF_ARM_RELEXEC 0x01
++#define EF_ARM_HASENTRY 0x02
++#define EF_ARM_INTERWORK 0x04
++#define EF_ARM_APCS_26 0x08
++#define EF_ARM_APCS_FLOAT 0x10
++#define EF_ARM_PIC 0x20
++#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */
++#define EF_ARM_NEW_ABI 0x80
++#define EF_ARM_OLD_ABI 0x100
++#define EF_ARM_SOFT_FLOAT 0x200
++#define EF_ARM_VFP_FLOAT 0x400
++#define EF_ARM_MAVERICK_FLOAT 0x800
++
++
++/* Other constants defined in the ARM ELF spec. version B-01. */
++/* NB. These conflict with values defined above. */
++#define EF_ARM_SYMSARESORTED 0x04
++#define EF_ARM_DYNSYMSUSESEGIDX 0x08
++#define EF_ARM_MAPSYMSFIRST 0x10
++#define EF_ARM_EABIMASK 0XFF000000
++
++/* Constants defined in AAELF. */
++#define EF_ARM_BE8 0x00800000
++#define EF_ARM_LE8 0x00400000
++
++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK)
++#define EF_ARM_EABI_UNKNOWN 0x00000000
++#define EF_ARM_EABI_VER1 0x01000000
++#define EF_ARM_EABI_VER2 0x02000000
++#define EF_ARM_EABI_VER3 0x03000000
++#define EF_ARM_EABI_VER4 0x04000000
++#define EF_ARM_EABI_VER5 0x05000000
++
++/* Additional symbol types for Thumb. */
++#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */
++#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */
++
++/* ARM-specific values for sh_flags */
++#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */
++#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined
++ in the input to a link step. */
++
++/* ARM-specific program header flags */
++#define PF_ARM_SB 0x10000000 /* Segment contains the location
++ addressed by the static base. */
++#define PF_ARM_PI 0x20000000 /* Position-independent segment. */
++#define PF_ARM_ABS 0x40000000 /* Absolute segment. */
++
++/* Processor specific values for the Phdr p_type field. */
++#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */
++
++/* Processor specific values for the Shdr sh_type field. */
++#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */
++#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */
++#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */
++
++
++/* ARM relocs. */
++
++#define R_ARM_NONE 0 /* No reloc */
++#define R_ARM_PC24 1 /* PC relative 26 bit branch */
++#define R_ARM_ABS32 2 /* Direct 32 bit */
++#define R_ARM_REL32 3 /* PC relative 32 bit */
++#define R_ARM_PC13 4
++#define R_ARM_ABS16 5 /* Direct 16 bit */
++#define R_ARM_ABS12 6 /* Direct 12 bit */
++#define R_ARM_THM_ABS5 7
++#define R_ARM_ABS8 8 /* Direct 8 bit */
++#define R_ARM_SBREL32 9
++#define R_ARM_THM_PC22 10
++#define R_ARM_THM_PC8 11
++#define R_ARM_AMP_VCALL9 12
++#define R_ARM_SWI24 13 /* Obsolete static relocation. */
++#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */
++#define R_ARM_THM_SWI8 14
++#define R_ARM_XPC25 15
++#define R_ARM_THM_XPC22 16
++#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */
++#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */
++#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */
++#define R_ARM_COPY 20 /* Copy symbol at runtime */
++#define R_ARM_GLOB_DAT 21 /* Create GOT entry */
++#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */
++#define R_ARM_RELATIVE 23 /* Adjust by program base */
++#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */
++#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */
++#define R_ARM_GOT32 26 /* 32 bit GOT entry */
++#define R_ARM_PLT32 27 /* 32 bit PLT address */
++#define R_ARM_ALU_PCREL_7_0 32
++#define R_ARM_ALU_PCREL_15_8 33
++#define R_ARM_ALU_PCREL_23_15 34
++#define R_ARM_LDR_SBREL_11_0 35
++#define R_ARM_ALU_SBREL_19_12 36
++#define R_ARM_ALU_SBREL_27_20 37
++#define R_ARM_TLS_GOTDESC 90
++#define R_ARM_TLS_CALL 91
++#define R_ARM_TLS_DESCSEQ 92
++#define R_ARM_THM_TLS_CALL 93
++#define R_ARM_GNU_VTENTRY 100
++#define R_ARM_GNU_VTINHERIT 101
++#define R_ARM_THM_PC11 102 /* thumb unconditional branch */
++#define R_ARM_THM_PC9 103 /* thumb conditional branch */
++#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic
++ thread local data */
++#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic
++ thread local data */
++#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS
++ block */
++#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of
++ static TLS block offset */
++#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static
++ TLS block */
++#define R_ARM_THM_TLS_DESCSEQ 129
++#define R_ARM_IRELATIVE 160
++#define R_ARM_RXPC25 249
++#define R_ARM_RSBREL32 250
++#define R_ARM_THM_RPC22 251
++#define R_ARM_RREL32 252
++#define R_ARM_RABS22 253
++#define R_ARM_RPC24 254
++#define R_ARM_RBASE 255
++/* Keep this the last entry. */
++#define R_ARM_NUM 256
++
++/* IA-64 specific declarations. */
++
++/* Processor specific flags for the Ehdr e_flags field. */
++#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */
++#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */
++#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */
++
++/* Processor specific values for the Phdr p_type field. */
++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */
++#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */
++#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12)
++#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13)
++#define PT_IA_64_HP_STACK (PT_LOOS + 0x14)
++
++/* Processor specific flags for the Phdr p_flags field. */
++#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */
++
++/* Processor specific values for the Shdr sh_type field. */
++#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */
++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */
++
++/* Processor specific flags for the Shdr sh_flags field. */
++#define SHF_IA_64_SHORT 0x10000000 /* section near gp */
++#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */
++
++/* Processor specific values for the Dyn d_tag field. */
++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0)
++#define DT_IA_64_NUM 1
++
++/* IA-64 relocations. */
++#define R_IA64_NONE 0x00 /* none */
++#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */
++#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */
++#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */
++#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */
++#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */
++#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */
++#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */
++#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */
++#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */
++#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */
++#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */
++#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */
++#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */
++#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */
++#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */
++#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */
++#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */
++#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */
++#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */
++#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */
++#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */
++#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */
++#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */
++#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */
++#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */
++#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */
++#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */
++#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */
++#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */
++#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */
++#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */
++#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */
++#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */
++#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */
++#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */
++#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */
++#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */
++#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */
++#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */
++#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */
++#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */
++#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */
++#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */
++#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */
++#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */
++#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */
++#define R_IA64_REL32MSB 0x6c /* data 4 + REL */
++#define R_IA64_REL32LSB 0x6d /* data 4 + REL */
++#define R_IA64_REL64MSB 0x6e /* data 8 + REL */
++#define R_IA64_REL64LSB 0x6f /* data 8 + REL */
++#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */
++#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */
++#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */
++#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */
++#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */
++#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */
++#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */
++#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */
++#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */
++#define R_IA64_COPY 0x84 /* copy relocation */
++#define R_IA64_SUB 0x85 /* Addend and symbol difference */
++#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */
++#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */
++#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */
++#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */
++#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */
++#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */
++#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */
++#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */
++#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */
++#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */
++#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */
++#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */
++#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */
++#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */
++#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */
++#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */
++#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */
++#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */
++#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */
++
++/* SH specific declarations */
++
++/* Processor specific flags for the ELF header e_flags field. */
++#define EF_SH_MACH_MASK 0x1f
++#define EF_SH_UNKNOWN 0x0
++#define EF_SH1 0x1
++#define EF_SH2 0x2
++#define EF_SH3 0x3
++#define EF_SH_DSP 0x4
++#define EF_SH3_DSP 0x5
++#define EF_SH4AL_DSP 0x6
++#define EF_SH3E 0x8
++#define EF_SH4 0x9
++#define EF_SH2E 0xb
++#define EF_SH4A 0xc
++#define EF_SH2A 0xd
++#define EF_SH4_NOFPU 0x10
++#define EF_SH4A_NOFPU 0x11
++#define EF_SH4_NOMMU_NOFPU 0x12
++#define EF_SH2A_NOFPU 0x13
++#define EF_SH3_NOMMU 0x14
++#define EF_SH2A_SH4_NOFPU 0x15
++#define EF_SH2A_SH3_NOFPU 0x16
++#define EF_SH2A_SH4 0x17
++#define EF_SH2A_SH3E 0x18
++
++/* SH relocs. */
++#define R_SH_NONE 0
++#define R_SH_DIR32 1
++#define R_SH_REL32 2
++#define R_SH_DIR8WPN 3
++#define R_SH_IND12W 4
++#define R_SH_DIR8WPL 5
++#define R_SH_DIR8WPZ 6
++#define R_SH_DIR8BP 7
++#define R_SH_DIR8W 8
++#define R_SH_DIR8L 9
++#define R_SH_SWITCH16 25
++#define R_SH_SWITCH32 26
++#define R_SH_USES 27
++#define R_SH_COUNT 28
++#define R_SH_ALIGN 29
++#define R_SH_CODE 30
++#define R_SH_DATA 31
++#define R_SH_LABEL 32
++#define R_SH_SWITCH8 33
++#define R_SH_GNU_VTINHERIT 34
++#define R_SH_GNU_VTENTRY 35
++#define R_SH_TLS_GD_32 144
++#define R_SH_TLS_LD_32 145
++#define R_SH_TLS_LDO_32 146
++#define R_SH_TLS_IE_32 147
++#define R_SH_TLS_LE_32 148
++#define R_SH_TLS_DTPMOD32 149
++#define R_SH_TLS_DTPOFF32 150
++#define R_SH_TLS_TPOFF32 151
++#define R_SH_GOT32 160
++#define R_SH_PLT32 161
++#define R_SH_COPY 162
++#define R_SH_GLOB_DAT 163
++#define R_SH_JMP_SLOT 164
++#define R_SH_RELATIVE 165
++#define R_SH_GOTOFF 166
++#define R_SH_GOTPC 167
++/* Keep this the last entry. */
++#define R_SH_NUM 256
++
++/* S/390 specific definitions. */
++
++/* Valid values for the e_flags field. */
++
++#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */
++
++/* Additional s390 relocs */
++
++#define R_390_NONE 0 /* No reloc. */
++#define R_390_8 1 /* Direct 8 bit. */
++#define R_390_12 2 /* Direct 12 bit. */
++#define R_390_16 3 /* Direct 16 bit. */
++#define R_390_32 4 /* Direct 32 bit. */
++#define R_390_PC32 5 /* PC relative 32 bit. */
++#define R_390_GOT12 6 /* 12 bit GOT offset. */
++#define R_390_GOT32 7 /* 32 bit GOT offset. */
++#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */
++#define R_390_COPY 9 /* Copy symbol at runtime. */
++#define R_390_GLOB_DAT 10 /* Create GOT entry. */
++#define R_390_JMP_SLOT 11 /* Create PLT entry. */
++#define R_390_RELATIVE 12 /* Adjust by program base. */
++#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */
++#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */
++#define R_390_GOT16 15 /* 16 bit GOT offset. */
++#define R_390_PC16 16 /* PC relative 16 bit. */
++#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */
++#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */
++#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */
++#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */
++#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */
++#define R_390_64 22 /* Direct 64 bit. */
++#define R_390_PC64 23 /* PC relative 64 bit. */
++#define R_390_GOT64 24 /* 64 bit GOT offset. */
++#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */
++#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */
++#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */
++#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */
++#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */
++#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */
++#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */
++#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */
++#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */
++#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */
++#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */
++#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */
++#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */
++#define R_390_TLS_GDCALL 38 /* Tag for function call in general
++ dynamic TLS code. */
++#define R_390_TLS_LDCALL 39 /* Tag for function call in local
++ dynamic TLS code. */
++#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic
++ thread local data. */
++#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic
++ thread local data. */
++#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS
++ block offset. */
++#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS
++ block offset. */
++#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS
++ block offset. */
++#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic
++ thread local data in LE code. */
++#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic
++ thread local data in LE code. */
++#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for
++ negated static TLS block offset. */
++#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for
++ negated static TLS block offset. */
++#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for
++ negated static TLS block offset. */
++#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to
++ static TLS block. */
++#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to
++ static TLS block. */
++#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS
++ block. */
++#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS
++ block. */
++#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */
++#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */
++#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS
++ block. */
++#define R_390_20 57 /* Direct 20 bit. */
++#define R_390_GOT20 58 /* 20 bit GOT offset. */
++#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */
++#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS
++ block offset. */
++#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */
++/* Keep this the last entry. */
++#define R_390_NUM 62
++
++
++/* CRIS relocations. */
++#define R_CRIS_NONE 0
++#define R_CRIS_8 1
++#define R_CRIS_16 2
++#define R_CRIS_32 3
++#define R_CRIS_8_PCREL 4
++#define R_CRIS_16_PCREL 5
++#define R_CRIS_32_PCREL 6
++#define R_CRIS_GNU_VTINHERIT 7
++#define R_CRIS_GNU_VTENTRY 8
++#define R_CRIS_COPY 9
++#define R_CRIS_GLOB_DAT 10
++#define R_CRIS_JUMP_SLOT 11
++#define R_CRIS_RELATIVE 12
++#define R_CRIS_16_GOT 13
++#define R_CRIS_32_GOT 14
++#define R_CRIS_16_GOTPLT 15
++#define R_CRIS_32_GOTPLT 16
++#define R_CRIS_32_GOTREL 17
++#define R_CRIS_32_PLT_GOTREL 18
++#define R_CRIS_32_PLT_PCREL 19
++
++#define R_CRIS_NUM 20
++
++
++/* AMD x86-64 relocations. */
++#define R_X86_64_NONE 0 /* No reloc */
++#define R_X86_64_64 1 /* Direct 64 bit */
++#define R_X86_64_PC32 2 /* PC relative 32 bit signed */
++#define R_X86_64_GOT32 3 /* 32 bit GOT entry */
++#define R_X86_64_PLT32 4 /* 32 bit PLT address */
++#define R_X86_64_COPY 5 /* Copy symbol at runtime */
++#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */
++#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */
++#define R_X86_64_RELATIVE 8 /* Adjust by program base */
++#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative
++ offset to GOT */
++#define R_X86_64_32 10 /* Direct 32 bit zero extended */
++#define R_X86_64_32S 11 /* Direct 32 bit sign extended */
++#define R_X86_64_16 12 /* Direct 16 bit zero extended */
++#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */
++#define R_X86_64_8 14 /* Direct 8 bit sign extended */
++#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */
++#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */
++#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */
++#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */
++#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset
++ to two GOT entries for GD symbol */
++#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset
++ to two GOT entries for LD symbol */
++#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */
++#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset
++ to GOT entry for IE symbol */
++#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */
++#define R_X86_64_PC64 24 /* PC relative 64 bit */
++#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */
++#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative
++ offset to GOT */
++#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */
++#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset
++ to GOT entry */
++#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */
++#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */
++#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset
++ to PLT entry */
++#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */
++#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */
++#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */
++#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS
++ descriptor. */
++#define R_X86_64_TLSDESC 36 /* TLS descriptor. */
++#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */
++#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */
++
++#define R_X86_64_NUM 39
++
++
++/* AM33 relocations. */
++#define R_MN10300_NONE 0 /* No reloc. */
++#define R_MN10300_32 1 /* Direct 32 bit. */
++#define R_MN10300_16 2 /* Direct 16 bit. */
++#define R_MN10300_8 3 /* Direct 8 bit. */
++#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */
++#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */
++#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */
++#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */
++#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */
++#define R_MN10300_24 9 /* Direct 24 bit. */
++#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */
++#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */
++#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */
++#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */
++#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */
++#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */
++#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */
++#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */
++#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */
++#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */
++#define R_MN10300_COPY 20 /* Copy symbol at runtime. */
++#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */
++#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */
++#define R_MN10300_RELATIVE 23 /* Adjust by program base. */
++
++#define R_MN10300_NUM 24
++
++
++/* M32R relocs. */
++#define R_M32R_NONE 0 /* No reloc. */
++#define R_M32R_16 1 /* Direct 16 bit. */
++#define R_M32R_32 2 /* Direct 32 bit. */
++#define R_M32R_24 3 /* Direct 24 bit. */
++#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */
++#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */
++#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */
++#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */
++#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */
++#define R_M32R_LO16 9 /* Low 16 bit. */
++#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */
++#define R_M32R_GNU_VTINHERIT 11
++#define R_M32R_GNU_VTENTRY 12
++/* M32R relocs use SHT_RELA. */
++#define R_M32R_16_RELA 33 /* Direct 16 bit. */
++#define R_M32R_32_RELA 34 /* Direct 32 bit. */
++#define R_M32R_24_RELA 35 /* Direct 24 bit. */
++#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */
++#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */
++#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */
++#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */
++#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */
++#define R_M32R_LO16_RELA 41 /* Low 16 bit */
++#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */
++#define R_M32R_RELA_GNU_VTINHERIT 43
++#define R_M32R_RELA_GNU_VTENTRY 44
++#define R_M32R_REL32 45 /* PC relative 32 bit. */
++
++#define R_M32R_GOT24 48 /* 24 bit GOT entry */
++#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */
++#define R_M32R_COPY 50 /* Copy symbol at runtime */
++#define R_M32R_GLOB_DAT 51 /* Create GOT entry */
++#define R_M32R_JMP_SLOT 52 /* Create PLT entry */
++#define R_M32R_RELATIVE 53 /* Adjust by program base */
++#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */
++#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */
++#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned
++ low */
++#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed
++ low */
++#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */
++#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to
++ GOT with unsigned low */
++#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to
++ GOT with signed low */
++#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to
++ GOT */
++#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT
++ with unsigned low */
++#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT
++ with signed low */
++#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */
++#define R_M32R_NUM 256 /* Keep this the last entry. */
++
++
++/* TILEPro relocations. */
++#define R_TILEPRO_NONE 0 /* No reloc */
++#define R_TILEPRO_32 1 /* Direct 32 bit */
++#define R_TILEPRO_16 2 /* Direct 16 bit */
++#define R_TILEPRO_8 3 /* Direct 8 bit */
++#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */
++#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */
++#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */
++#define R_TILEPRO_LO16 7 /* Low 16 bit */
++#define R_TILEPRO_HI16 8 /* High 16 bit */
++#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */
++#define R_TILEPRO_COPY 10 /* Copy relocation */
++#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */
++#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */
++#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */
++#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */
++#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */
++#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */
++#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */
++#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */
++#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */
++#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */
++#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */
++#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */
++#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */
++#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */
++#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */
++#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */
++#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */
++#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */
++#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */
++#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */
++#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */
++#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */
++#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */
++#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */
++#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */
++#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */
++#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */
++#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */
++#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */
++#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */
++#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */
++#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */
++#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */
++#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */
++#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */
++#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */
++#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */
++#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */
++/* Relocs 56-59 are currently not defined. */
++#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */
++#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */
++#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */
++#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */
++#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */
++#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */
++#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */
++#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */
++#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */
++#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */
++#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */
++
++#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */
++#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */
++
++#define R_TILEPRO_NUM 130
++
++
++/* TILE-Gx relocations. */
++#define R_TILEGX_NONE 0 /* No reloc */
++#define R_TILEGX_64 1 /* Direct 64 bit */
++#define R_TILEGX_32 2 /* Direct 32 bit */
++#define R_TILEGX_16 3 /* Direct 16 bit */
++#define R_TILEGX_8 4 /* Direct 8 bit */
++#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */
++#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */
++#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */
++#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */
++#define R_TILEGX_HW0 9 /* hword 0 16-bit */
++#define R_TILEGX_HW1 10 /* hword 1 16-bit */
++#define R_TILEGX_HW2 11 /* hword 2 16-bit */
++#define R_TILEGX_HW3 12 /* hword 3 16-bit */
++#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */
++#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */
++#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */
++#define R_TILEGX_COPY 16 /* Copy relocation */
++#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */
++#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */
++#define R_TILEGX_RELATIVE 19 /* Adjust by program base */
++#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */
++#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */
++#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */
++#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */
++#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */
++#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */
++#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */
++#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */
++#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */
++#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */
++#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */
++#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */
++#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */
++#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */
++#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */
++#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */
++#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */
++#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */
++#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */
++#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */
++#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */
++#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */
++#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */
++#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */
++#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */
++#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */
++#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */
++#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */
++#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */
++#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */
++#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */
++#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */
++#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */
++#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */
++#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */
++#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */
++#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */
++#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */
++#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */
++#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */
++#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */
++#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */
++#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */
++#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */
++#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */
++#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */
++/* Relocs 66-71 are currently not defined. */
++#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */
++#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */
++#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */
++#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */
++/* Relocs 76-77 are currently not defined. */
++#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */
++#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */
++#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */
++#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */
++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */
++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */
++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */
++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */
++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */
++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */
++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */
++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */
++/* Relocs 90-91 are currently not defined. */
++#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */
++#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */
++/* Relocs 94-99 are currently not defined. */
++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */
++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */
++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */
++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */
++/* Relocs 104-105 are currently not defined. */
++#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */
++#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */
++#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */
++#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */
++#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */
++#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */
++#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */
++#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */
++#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */
++#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */
++#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */
++#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */
++#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */
++#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */
++#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */
++#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */
++
++#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */
++#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */
++
++#define R_TILEGX_NUM 130
++
++#endif /* elf.h */
+--- a/scripts/mod/mk_elfconfig.c
++++ b/scripts/mod/mk_elfconfig.c
+@@ -2,7 +2,11 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#ifndef __APPLE__
+ #include <elf.h>
++#else
++#include "elf.h"
++#endif
+
+ int
+ main(int argc, char **argv)
+--- a/scripts/mod/modpost.h
++++ b/scripts/mod/modpost.h
+@@ -8,7 +8,11 @@
+ #include <sys/mman.h>
+ #include <fcntl.h>
+ #include <unistd.h>
++#if !(defined(__APPLE__) || defined(__CYGWIN__))
+ #include <elf.h>
++#else
++#include "elf.h"
++#endif
+
+ #include "elfconfig.h"
+
--- /dev/null
+From e44fc2af1ddc452b6659d08c16973d65c73b7d0a Mon Sep 17 00:00:00 2001
+From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+Date: Wed, 5 Feb 2020 18:36:43 +0000
+Subject: [PATCH] file2alias: build on macos
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ scripts/mod/file2alias.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/scripts/mod/file2alias.c
++++ b/scripts/mod/file2alias.c
+@@ -38,6 +38,9 @@ typedef struct {
+ __u8 b[16];
+ } guid_t;
+
++#ifdef __APPLE__
++#define uuid_t compat_uuid_t
++#endif
+ /* backwards compatibility, don't use in new code */
+ typedef struct {
+ __u8 b[16];
--- /dev/null
+From 48232d3d931c95953ce2ddfe7da7bb164aef6a73 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:03:16 +0200
+Subject: fix portability of some includes files in tools/ used on the host
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ tools/include/tools/be_byteshift.h | 4 ++++
+ tools/include/tools/le_byteshift.h | 4 ++++
+ tools/include/tools/linux_types.h | 22 ++++++++++++++++++++++
+ 3 files changed, 30 insertions(+)
+ create mode 100644 tools/include/tools/linux_types.h
+
+--- a/tools/include/tools/be_byteshift.h
++++ b/tools/include/tools/be_byteshift.h
+@@ -2,6 +2,10 @@
+ #ifndef _TOOLS_BE_BYTESHIFT_H
+ #define _TOOLS_BE_BYTESHIFT_H
+
++#ifndef __linux__
++#include "linux_types.h"
++#endif
++
+ #include <stdint.h>
+
+ static inline uint16_t __get_unaligned_be16(const uint8_t *p)
+--- a/tools/include/tools/le_byteshift.h
++++ b/tools/include/tools/le_byteshift.h
+@@ -2,6 +2,10 @@
+ #ifndef _TOOLS_LE_BYTESHIFT_H
+ #define _TOOLS_LE_BYTESHIFT_H
+
++#ifndef __linux__
++#include "linux_types.h"
++#endif
++
+ #include <stdint.h>
+
+ static inline uint16_t __get_unaligned_le16(const uint8_t *p)
+--- /dev/null
++++ b/tools/include/tools/linux_types.h
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_TYPES_H
++#define __LINUX_TYPES_H
++
++#include <stdint.h>
++
++typedef int8_t __s8;
++typedef uint8_t __u8;
++typedef uint8_t __be8;
++typedef uint8_t __le8;
++
++typedef int16_t __s16;
++typedef uint16_t __u16;
++typedef uint16_t __be16;
++typedef uint16_t __le16;
++
++typedef int32_t __s32;
++typedef uint32_t __u32;
++typedef uint32_t __be32;
++typedef uint32_t __le32;
++
++typedef int64_t __s64;
++typedef uint64_t __u64;
++typedef uint64_t __be64;
++typedef uint64_t __le64;
++
++#endif
+--- a/tools/include/linux/types.h
++++ b/tools/include/linux/types.h
+@@ -7,8 +7,12 @@
+ #include <stdint.h>
+
+ #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
++#ifndef __linux__
++#include <tools/linux_types.h>
++#else
+ #include <asm/types.h>
+ #include <asm/posix_types.h>
++#endif
+
+ struct page;
+ struct kmem_cache;
+--- a/tools/perf/pmu-events/jevents.c
++++ b/tools/perf/pmu-events/jevents.c
+@@ -1,4 +1,6 @@
++#ifdef __linux__
+ #define _XOPEN_SOURCE 500 /* needed for nftw() */
++#endif
+ #define _GNU_SOURCE /* needed for asprintf() */
+
+ /* Parse event JSON files */
+@@ -35,6 +37,7 @@
+ #include <stdlib.h>
+ #include <errno.h>
+ #include <string.h>
++#include <strings.h>
+ #include <ctype.h>
+ #include <unistd.h>
+ #include <stdarg.h>
+--- a/tools/perf/pmu-events/json.c
++++ b/tools/perf/pmu-events/json.c
+@@ -38,7 +38,6 @@
+ #include <unistd.h>
+ #include "jsmn.h"
+ #include "json.h"
+-#include <linux/kernel.h>
+
+
+ static char *mapfile(const char *fn, size_t *size)
--- /dev/null
+From be9be95ff10e16a5b4ad36f903978d0cc5747024 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:04:08 +0200
+Subject: kernel: fix linux/spi/spidev.h portability issues with musl
+
+Felix will try to get this define included into musl
+
+lede-commit: 795e7cf60de19e7a076a46874fab7bb88b43bbff
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/uapi/linux/spi/spidev.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/include/uapi/linux/spi/spidev.h
++++ b/include/uapi/linux/spi/spidev.h
+@@ -121,7 +121,7 @@ struct spi_ioc_transfer {
+
+ /* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */
+ #define SPI_MSGSIZE(N) \
+- ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \
++ ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << 13)) \
+ ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0)
+ #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
+
--- /dev/null
+From e3d8676f5722b7622685581e06e8f53e6138e3ab Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 15 Jul 2017 23:42:36 +0200
+Subject: use -ffunction-sections, -fdata-sections and --gc-sections
+
+In combination with kernel symbol export stripping this significantly reduces
+the kernel image size. Used on both ARM and MIPS architectures.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Jonas Gorski <jogo@openwrt.org>
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -114,6 +114,7 @@ config ARM
+ select HAVE_UID16
+ select HAVE_VIRT_CPU_ACCOUNTING_GEN
+ select IRQ_FORCED_THREADING
++ select HAVE_LD_DEAD_CODE_DATA_ELIMINATION
+ select MODULES_USE_ELF_REL
+ select NEED_DMA_MAP_STATE
+ select OF_EARLY_FLATTREE if OF
+--- a/arch/arm/boot/compressed/Makefile
++++ b/arch/arm/boot/compressed/Makefile
+@@ -100,6 +100,7 @@ $(foreach o, $(libfdt_objs) atags_to_fdt
+ ifdef building_out_of_srctree
+ $(shell rm -f $(addprefix $(obj)/, fdt_rw.c fdt_ro.c fdt_wip.c fdt.c))
+ endif
++KBUILD_CFLAGS_KERNEL := $(patsubst -f%-sections,,$(KBUILD_CFLAGS_KERNEL))
+
+ targets := vmlinux vmlinux.lds piggy_data piggy.o \
+ lib1funcs.o ashldi3.o bswapsdi2.o \
+--- a/arch/arm/kernel/vmlinux.lds.S
++++ b/arch/arm/kernel/vmlinux.lds.S
+@@ -75,7 +75,7 @@ SECTIONS
+ . = ALIGN(4);
+ __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {
+ __start___ex_table = .;
+- ARM_MMU_KEEP(*(__ex_table))
++ KEEP(*(__ex_table))
+ __stop___ex_table = .;
+ }
+
+@@ -100,24 +100,24 @@ SECTIONS
+ }
+ .init.arch.info : {
+ __arch_info_begin = .;
+- *(.arch.info.init)
++ KEEP(*(.arch.info.init))
+ __arch_info_end = .;
+ }
+ .init.tagtable : {
+ __tagtable_begin = .;
+- *(.taglist.init)
++ KEEP(*(.taglist.init))
+ __tagtable_end = .;
+ }
+ #ifdef CONFIG_SMP_ON_UP
+ .init.smpalt : {
+ __smpalt_begin = .;
+- *(.alt.smp.init)
++ KEEP(*(.alt.smp.init))
+ __smpalt_end = .;
+ }
+ #endif
+ .init.pv_table : {
+ __pv_table_begin = .;
+- *(.pv_table)
++ KEEP(*(.pv_table))
+ __pv_table_end = .;
+ }
+
+--- a/arch/arm/include/asm/vmlinux.lds.h
++++ b/arch/arm/include/asm/vmlinux.lds.h
+@@ -42,13 +42,13 @@
+ #define PROC_INFO \
+ . = ALIGN(4); \
+ __proc_info_begin = .; \
+- *(.proc.info.init) \
++ KEEP(*(.proc.info.init)) \
+ __proc_info_end = .;
+
+ #define IDMAP_TEXT \
+ ALIGN_FUNCTION(); \
+ __idmap_text_start = .; \
+- *(.idmap.text) \
++ KEEP(*(.idmap.text)) \
+ __idmap_text_end = .; \
+
+ #define ARM_DISCARD \
+@@ -109,12 +109,12 @@
+ . = ALIGN(8); \
+ .ARM.unwind_idx : { \
+ __start_unwind_idx = .; \
+- *(.ARM.exidx*) \
++ KEEP(*(.ARM.exidx*)) \
+ __stop_unwind_idx = .; \
+ } \
+ .ARM.unwind_tab : { \
+ __start_unwind_tab = .; \
+- *(.ARM.extab*) \
++ KEEP(*(.ARM.extab*)) \
+ __stop_unwind_tab = .; \
+ }
+
+@@ -126,7 +126,7 @@
+ __vectors_lma = .; \
+ OVERLAY 0xffff0000 : NOCROSSREFS AT(__vectors_lma) { \
+ .vectors { \
+- *(.vectors) \
++ KEEP(*(.vectors)) \
+ } \
+ .vectors.bhb.loop8 { \
+ *(.vectors.bhb.loop8) \
+@@ -144,7 +144,7 @@
+ \
+ __stubs_lma = .; \
+ .stubs ADDR(.vectors) + 0x1000 : AT(__stubs_lma) { \
+- *(.stubs) \
++ KEEP(*(.stubs)) \
+ } \
+ ARM_LMA(__stubs, .stubs); \
+ . = __stubs_lma + SIZEOF(.stubs); \
--- /dev/null
+From b14784e7883390c20ed3ff904892255404a5914b Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:05:53 +0200
+Subject: add an optional config option for stripping all unnecessary symbol exports from the kernel image
+
+lede-commit: bb5a40c64b7c4f4848509fa0a6625055fc9e66cc
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/asm-generic/vmlinux.lds.h | 18 +++++++++++++++---
+ include/linux/export.h | 9 ++++++++-
+ scripts/Makefile.build | 2 +-
+ 3 files changed, 24 insertions(+), 5 deletions(-)
+
+--- a/include/asm-generic/vmlinux.lds.h
++++ b/include/asm-generic/vmlinux.lds.h
+@@ -81,6 +81,16 @@
+ #define RO_EXCEPTION_TABLE
+ #endif
+
++#ifndef SYMTAB_KEEP
++#define SYMTAB_KEEP KEEP(*(SORT(___ksymtab+*)))
++#define SYMTAB_KEEP_GPL KEEP(*(SORT(___ksymtab_gpl+*)))
++#endif
++
++#ifndef SYMTAB_DISCARD
++#define SYMTAB_DISCARD
++#define SYMTAB_DISCARD_GPL
++#endif
++
+ /* Align . to a 8 byte boundary equals to maximum function alignment. */
+ #define ALIGN_FUNCTION() . = ALIGN(8)
+
+@@ -473,14 +483,14 @@
+ /* Kernel symbol table: Normal symbols */ \
+ __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \
+ __start___ksymtab = .; \
+- KEEP(*(SORT(___ksymtab+*))) \
++ SYMTAB_KEEP \
+ __stop___ksymtab = .; \
+ } \
+ \
+ /* Kernel symbol table: GPL-only symbols */ \
+ __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) { \
+ __start___ksymtab_gpl = .; \
+- KEEP(*(SORT(___ksymtab_gpl+*))) \
++ SYMTAB_KEEP_GPL \
+ __stop___ksymtab_gpl = .; \
+ } \
+ \
+@@ -542,7 +552,7 @@
+ \
+ /* Kernel symbol table: strings */ \
+ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \
+- *(__ksymtab_strings) \
++ *(__ksymtab_strings+*) \
+ } \
+ \
+ /* __*init sections */ \
+@@ -1019,6 +1029,8 @@
+
+ #define COMMON_DISCARDS \
+ SANITIZER_DISCARDS \
++ SYMTAB_DISCARD \
++ SYMTAB_DISCARD_GPL \
+ *(.discard) \
+ *(.discard.*) \
+ *(.modinfo) \
+--- a/include/linux/export.h
++++ b/include/linux/export.h
+@@ -82,6 +82,12 @@ struct kernel_symbol {
+
+ #else
+
++#ifdef MODULE
++#define __EXPORT_SUFFIX(sym)
++#else
++#define __EXPORT_SUFFIX(sym) "+" #sym
++#endif
++
+ /*
+ * For every exported symbol, do the following:
+ *
+@@ -99,7 +105,7 @@ struct kernel_symbol {
+ extern const char __kstrtab_##sym[]; \
+ extern const char __kstrtabns_##sym[]; \
+ __CRC_SYMBOL(sym, sec); \
+- asm(" .section \"__ksymtab_strings\",\"aMS\",%progbits,1 \n" \
++ asm(" .section \"__ksymtab_strings" __EXPORT_SUFFIX(sym) "\",\"aMS\",%progbits,1 \n" \
+ "__kstrtab_" #sym ": \n" \
+ " .asciz \"" #sym "\" \n" \
+ "__kstrtabns_" #sym ": \n" \
+--- a/scripts/Makefile.build
++++ b/scripts/Makefile.build
+@@ -367,7 +367,7 @@ targets += $(lib-y) $(always-y) $(MAKECM
+ # Linker scripts preprocessor (.lds.S -> .lds)
+ # ---------------------------------------------------------------------------
+ quiet_cmd_cpp_lds_S = LDS $@
+- cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -U$(ARCH) \
++ cmd_cpp_lds_S = $(CPP) $(EXTRA_LDSFLAGS) $(cpp_flags) -P -U$(ARCH) \
+ -D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $<
+
+ $(obj)/%.lds: $(src)/%.lds.S FORCE
--- /dev/null
+From b3d00b452467f621317953d9e4c6f9ae8dcfd271 Mon Sep 17 00:00:00 2001
+From: Imre Kaloz <kaloz@openwrt.org>
+Date: Fri, 7 Jul 2017 17:06:55 +0200
+Subject: use the openwrt lzma options for now
+
+lede-commit: 548de949f392049420a6a1feeef118b30ab8ea8c
+Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
+---
+ lib/decompress.c | 1 +
+ scripts/Makefile.lib | 2 +-
+ usr/gen_initramfs_list.sh | 10 +++++-----
+ 3 files changed, 7 insertions(+), 6 deletions(-)
+
+--- a/lib/decompress.c
++++ b/lib/decompress.c
+@@ -53,6 +53,7 @@ static const struct compress_format comp
+ { {0x1f, 0x9e}, "gzip", gunzip },
+ { {0x42, 0x5a}, "bzip2", bunzip2 },
+ { {0x5d, 0x00}, "lzma", unlzma },
++ { {0x6d, 0x00}, "lzma-openwrt", unlzma },
+ { {0xfd, 0x37}, "xz", unxz },
+ { {0x89, 0x4c}, "lzo", unlzo },
+ { {0x02, 0x21}, "lz4", unlz4 },
+--- a/scripts/Makefile.lib
++++ b/scripts/Makefile.lib
+@@ -370,7 +370,7 @@ quiet_cmd_bzip2 = BZIP2 $@
+ # ---------------------------------------------------------------------------
+
+ quiet_cmd_lzma = LZMA $@
+- cmd_lzma = { cat $(real-prereqs) | $(LZMA) -9; $(size_append); } > $@
++ cmd_lzma = { cat $(real-prereqs) | $(LZMA) e -d20 -lc1 -lp2 -pb2 -eos -si -so; $(size_append); } > $@
+
+ quiet_cmd_lzo = LZO $@
+ cmd_lzo = { cat $(real-prereqs) | $(KLZOP) -9; $(size_append); } > $@
--- /dev/null
+--- a/net/ipv4/Kconfig
++++ b/net/ipv4/Kconfig
+@@ -315,7 +315,7 @@ config NET_IPVTI
+ on top.
+
+ config NET_UDP_TUNNEL
+- tristate
++ tristate "IP: UDP tunneling support"
+ select NET_IP_TUNNEL
+ default n
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: hack: net: remove bogus netfilter dependencies
+
+lede-commit: 589d2a377dee27d206fc3725325309cf649e4df6
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/netfilter/Kconfig | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/net/netfilter/Kconfig
++++ b/net/netfilter/Kconfig
+@@ -228,7 +228,6 @@ config NF_CONNTRACK_FTP
+
+ config NF_CONNTRACK_H323
+ tristate "H.323 protocol support"
+- depends on IPV6 || IPV6=n
+ depends on NETFILTER_ADVANCED
+ help
+ H.323 is a VoIP signalling protocol from ITU-T. As one of the most
+@@ -1072,7 +1071,6 @@ config NETFILTER_XT_TARGET_SECMARK
+
+ config NETFILTER_XT_TARGET_TCPMSS
+ tristate '"TCPMSS" target support'
+- depends on IPV6 || IPV6=n
+ default m if NETFILTER_ADVANCED=n
+ help
+ This option adds a `TCPMSS' target, which allows you to alter the
--- /dev/null
+From da3c50704f14132f4adf80d48e9a4cd5d46e54c9 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Fri, 7 Jul 2017 17:09:21 +0200
+Subject: kconfig: owrt specifc dependencies
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ crypto/Kconfig | 10 +++++-----
+ drivers/bcma/Kconfig | 1 +
+ drivers/ssb/Kconfig | 3 ++-
+ lib/Kconfig | 8 ++++----
+ net/netfilter/Kconfig | 2 +-
+ net/wireless/Kconfig | 17 ++++++++++-------
+ sound/core/Kconfig | 4 ++--
+ 7 files changed, 25 insertions(+), 20 deletions(-)
+
+--- a/crypto/Kconfig
++++ b/crypto/Kconfig
+@@ -33,7 +33,7 @@ config CRYPTO_FIPS
+ this is.
+
+ config CRYPTO_ALGAPI
+- tristate
++ tristate "ALGAPI"
+ select CRYPTO_ALGAPI2
+ help
+ This option provides the API for cryptographic algorithms.
+@@ -42,7 +42,7 @@ config CRYPTO_ALGAPI2
+ tristate
+
+ config CRYPTO_AEAD
+- tristate
++ tristate "AEAD"
+ select CRYPTO_AEAD2
+ select CRYPTO_ALGAPI
+
+@@ -53,7 +53,7 @@ config CRYPTO_AEAD2
+ select CRYPTO_RNG2
+
+ config CRYPTO_SKCIPHER
+- tristate
++ tristate "SKCIPHER"
+ select CRYPTO_SKCIPHER2
+ select CRYPTO_ALGAPI
+
+@@ -63,7 +63,7 @@ config CRYPTO_SKCIPHER2
+ select CRYPTO_RNG2
+
+ config CRYPTO_HASH
+- tristate
++ tristate "HASH"
+ select CRYPTO_HASH2
+ select CRYPTO_ALGAPI
+
+@@ -72,7 +72,7 @@ config CRYPTO_HASH2
+ select CRYPTO_ALGAPI2
+
+ config CRYPTO_RNG
+- tristate
++ tristate "RNG"
+ select CRYPTO_RNG2
+ select CRYPTO_ALGAPI
+
+--- a/drivers/bcma/Kconfig
++++ b/drivers/bcma/Kconfig
+@@ -16,6 +16,7 @@ if BCMA
+ # Support for Block-I/O. SELECT this from the driver that needs it.
+ config BCMA_BLOCKIO
+ bool
++ default y
+
+ config BCMA_HOST_PCI_POSSIBLE
+ bool
+--- a/drivers/ssb/Kconfig
++++ b/drivers/ssb/Kconfig
+@@ -29,6 +29,7 @@ config SSB_SPROM
+ config SSB_BLOCKIO
+ bool
+ depends on SSB
++ default y
+
+ config SSB_PCIHOST_POSSIBLE
+ bool
+@@ -49,7 +50,7 @@ config SSB_PCIHOST
+ config SSB_B43_PCI_BRIDGE
+ bool
+ depends on SSB_PCIHOST
+- default n
++ default y
+
+ config SSB_PCMCIAHOST_POSSIBLE
+ bool
+--- a/lib/Kconfig
++++ b/lib/Kconfig
+@@ -418,16 +418,16 @@ config BCH_CONST_T
+ # Textsearch support is select'ed if needed
+ #
+ config TEXTSEARCH
+- bool
++ bool "Textsearch support"
+
+ config TEXTSEARCH_KMP
+- tristate
++ tristate "Textsearch KMP"
+
+ config TEXTSEARCH_BM
+- tristate
++ tristate "Textsearch BM"
+
+ config TEXTSEARCH_FSM
+- tristate
++ tristate "Textsearch FSM"
+
+ config BTREE
+ bool
+--- a/net/netfilter/Kconfig
++++ b/net/netfilter/Kconfig
+@@ -11,7 +11,7 @@ config NETFILTER_INGRESS
+ infrastructure.
+
+ config NETFILTER_NETLINK
+- tristate
++ tristate "Netfilter NFNETLINK interface"
+
+ config NETFILTER_FAMILY_BRIDGE
+ bool
+--- a/net/wireless/Kconfig
++++ b/net/wireless/Kconfig
+@@ -1,6 +1,6 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ config WIRELESS_EXT
+- bool
++ bool "Wireless extensions"
+
+ config WEXT_CORE
+ def_bool y
+@@ -12,10 +12,10 @@ config WEXT_PROC
+ depends on WEXT_CORE
+
+ config WEXT_SPY
+- bool
++ bool "WEXT_SPY"
+
+ config WEXT_PRIV
+- bool
++ bool "WEXT_PRIV"
+
+ config CFG80211
+ tristate "cfg80211 - wireless configuration API"
+@@ -204,7 +204,7 @@ config CFG80211_WEXT_EXPORT
+ endif # CFG80211
+
+ config LIB80211
+- tristate
++ tristate "LIB80211"
+ default n
+ help
+ This options enables a library of common routines used
+@@ -213,17 +213,17 @@ config LIB80211
+ Drivers should select this themselves if needed.
+
+ config LIB80211_CRYPT_WEP
+- tristate
++ tristate "LIB80211_CRYPT_WEP"
+ select CRYPTO_LIB_ARC4
+
+ config LIB80211_CRYPT_CCMP
+- tristate
++ tristate "LIB80211_CRYPT_CCMP"
+ select CRYPTO
+ select CRYPTO_AES
+ select CRYPTO_CCM
+
+ config LIB80211_CRYPT_TKIP
+- tristate
++ tristate "LIB80211_CRYPT_TKIP"
+ select CRYPTO_LIB_ARC4
+
+ config LIB80211_DEBUG
+--- a/sound/core/Kconfig
++++ b/sound/core/Kconfig
+@@ -17,7 +17,7 @@ config SND_DMAENGINE_PCM
+ tristate
+
+ config SND_HWDEP
+- tristate
++ tristate "Sound hardware support"
+
+ config SND_SEQ_DEVICE
+ tristate
+@@ -27,7 +27,7 @@ config SND_RAWMIDI
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
+
+ config SND_COMPRESS_OFFLOAD
+- tristate
++ tristate "Compression offloading support"
+
+ config SND_JACK
+ bool
--- /dev/null
+From 8c817e33be829c7249c2cfd59ff48ad5fac6a31d Mon Sep 17 00:00:00 2001
+From: Sungbo Eo <mans0n@gorani.run>
+Date: Fri, 7 Jul 2017 17:09:21 +0200
+Subject: [PATCH] kconfig: solidify SATA_PMP config
+
+SATA_PMP option in kernel config file disappears for every kernel_oldconfig refresh.
+To prevent this, SATA_HOST is now selected automatically when SATA_PMP is enabled.
+This patch can be dropped if SATA_MV is ever re-added into the config.
+---
+ drivers/ata/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/ata/Kconfig
++++ b/drivers/ata/Kconfig
+@@ -112,7 +112,7 @@ config SATA_ZPODD
+
+ config SATA_PMP
+ bool "SATA Port Multiplier support"
+- depends on SATA_HOST
++ select SATA_HOST
+ default y
+ help
+ This option adds support for SATA Port Multipliers
--- /dev/null
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -2379,7 +2379,7 @@ config PADATA
+ bool
+
+ config ASN1
+- tristate
++ tristate "ASN1"
+ help
+ Build a simple ASN.1 grammar compiler that produces a bytecode output
+ that can be interpreted by the ASN.1 stream decoder and used to
+--- a/lib/Kconfig
++++ b/lib/Kconfig
+@@ -589,7 +589,7 @@ config LIBFDT
+ bool
+
+ config OID_REGISTRY
+- tristate
++ tristate "OID"
+ help
+ Enable fast lookup object identifier registry.
+
--- /dev/null
+From 811d9e2268a62b830cfe93cd8bc929afcb8b198b Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 15 Jul 2017 21:12:38 +0200
+Subject: kernel: move regmap bloat out of the kernel image if it is only being used in modules
+
+lede-commit: 96f39119815028073583e4fca3a9c5fe9141e998
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/base/regmap/Kconfig | 15 ++++++++++-----
+ drivers/base/regmap/Makefile | 12 ++++++++----
+ drivers/base/regmap/regmap.c | 3 +++
+ include/linux/regmap.h | 2 +-
+ 4 files changed, 22 insertions(+), 10 deletions(-)
+
+--- a/drivers/base/regmap/Kconfig
++++ b/drivers/base/regmap/Kconfig
+@@ -4,9 +4,8 @@
+ # subsystems should select the appropriate symbols.
+
+ config REGMAP
+- default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM)
+ select IRQ_DOMAIN if REGMAP_IRQ
+- bool
++ tristate
+
+ config REGCACHE_COMPRESSED
+ select LZO_COMPRESS
+@@ -14,46 +13,59 @@ config REGCACHE_COMPRESSED
+ bool
+
+ config REGMAP_AC97
++ select REGMAP
+ tristate
+
+ config REGMAP_I2C
+ tristate
++ select REGMAP
+ depends on I2C
+
+ config REGMAP_SLIMBUS
+ tristate
++ select REGMAP
+ depends on SLIMBUS
+
+ config REGMAP_SPI
+ tristate
++ select REGMAP
++ depends on SPI_MASTER
+ depends on SPI
+
+ config REGMAP_SPMI
+ tristate
++ select REGMAP
+ depends on SPMI
+
+ config REGMAP_W1
+ tristate
++ select REGMAP
+ depends on W1
+
+ config REGMAP_MMIO
+ tristate
++ select REGMAP
+
+ config REGMAP_IRQ
+ bool
++ select REGMAP
+
+ config REGMAP_SOUNDWIRE
+ tristate
++ select REGMAP
+ depends on SOUNDWIRE
+
+ config REGMAP_SCCB
+ tristate
++ select REGMAP
+ depends on I2C
+
+ config REGMAP_I3C
+ tristate
++ select REGMAP
+ depends on I3C
+
+ config REGMAP_SPI_AVMM
+ tristate
++ select REGMAP
+ depends on SPI
+--- a/drivers/base/regmap/Makefile
++++ b/drivers/base/regmap/Makefile
+@@ -2,10 +2,14 @@
+ # For include/trace/define_trace.h to include trace.h
+ CFLAGS_regmap.o := -I$(src)
+
+-obj-$(CONFIG_REGMAP) += regmap.o regcache.o
+-obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o
+-obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o
+-obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
++regmap-core-objs = regmap.o regcache.o regcache-rbtree.o regcache-flat.o
++ifdef CONFIG_DEBUG_FS
++regmap-core-objs += regmap-debugfs.o
++endif
++ifdef CONFIG_REGCACHE_COMPRESSED
++regmap-core-objs += regcache-lzo.o
++endif
++obj-$(CONFIG_REGMAP) += regmap-core.o
+ obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
+ obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
+ obj-$(CONFIG_REGMAP_SLIMBUS) += regmap-slimbus.o
+--- a/drivers/base/regmap/regmap.c
++++ b/drivers/base/regmap/regmap.c
+@@ -9,6 +9,7 @@
+ #include <linux/device.h>
+ #include <linux/slab.h>
+ #include <linux/export.h>
++#include <linux/module.h>
+ #include <linux/mutex.h>
+ #include <linux/err.h>
+ #include <linux/property.h>
+@@ -3298,3 +3299,5 @@ static int __init regmap_initcall(void)
+ return 0;
+ }
+ postcore_initcall(regmap_initcall);
++
++MODULE_LICENSE("GPL");
+--- a/include/linux/regmap.h
++++ b/include/linux/regmap.h
+@@ -179,7 +179,7 @@ struct reg_sequence {
+ __ret ?: __tmp; \
+ })
+
+-#ifdef CONFIG_REGMAP
++#if IS_REACHABLE(CONFIG_REGMAP)
+
+ enum regmap_endian {
+ /* Unspecified -> 0 -> Backwards compatible default */
--- /dev/null
+From fd1799b0bf5efa46dd3e6dfbbf3955564807e508 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:12:51 +0200
+Subject: kernel: prevent cryptomgr from pulling in useless extra dependencies for tests that are not run
+
+Reduces kernel size after LZMA by about 5k on MIPS
+
+lede-commit: 044c316167e076479a344c59905e5b435b84a77f
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ crypto/Kconfig | 13 ++++++-------
+ crypto/algboss.c | 4 ++++
+ 2 files changed, 10 insertions(+), 7 deletions(-)
+
+--- a/crypto/Kconfig
++++ b/crypto/Kconfig
+@@ -120,13 +120,13 @@ config CRYPTO_MANAGER
+ cbc(aes).
+
+ config CRYPTO_MANAGER2
+- def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y)
+- select CRYPTO_AEAD2
+- select CRYPTO_HASH2
+- select CRYPTO_SKCIPHER2
+- select CRYPTO_AKCIPHER2
+- select CRYPTO_KPP2
+- select CRYPTO_ACOMP2
++ def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y && !CRYPTO_MANAGER_DISABLE_TESTS)
++ select CRYPTO_AEAD2 if !CRYPTO_MANAGER_DISABLE_TESTS
++ select CRYPTO_HASH2 if !CRYPTO_MANAGER_DISABLE_TESTS
++ select CRYPTO_SKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS
++ select CRYPTO_AKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS
++ select CRYPTO_KPP2 if !CRYPTO_MANAGER_DISABLE_TESTS
++ select CRYPTO_ACOMP2 if !CRYPTO_MANAGER_DISABLE_TESTS
+
+ config CRYPTO_USER
+ tristate "Userspace cryptographic algorithm configuration"
+--- a/crypto/algboss.c
++++ b/crypto/algboss.c
+@@ -230,8 +230,12 @@ static int cryptomgr_schedule_test(struc
+ type = alg->cra_flags;
+
+ /* Do not test internal algorithms. */
++#ifdef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS
++ type |= CRYPTO_ALG_TESTED;
++#else
+ if (type & CRYPTO_ALG_INTERNAL)
+ type |= CRYPTO_ALG_TESTED;
++#endif
+
+ param->type = type;
+
--- /dev/null
+This makes it possible to select CONFIG_CRYPTO_LIB_ARC4 directly. We
+need this to be able to compile this into the kernel and make use of it
+from backports.
+
+--- a/lib/crypto/Kconfig
++++ b/lib/crypto/Kconfig
+@@ -6,7 +6,7 @@ config CRYPTO_LIB_AES
+ tristate
+
+ config CRYPTO_LIB_ARC4
+- tristate
++ tristate "ARC4 cipher library"
+
+ config CRYPTO_ARCH_HAVE_LIB_BLAKE2S
+ tristate
--- /dev/null
+From 236c1acdfef5958010ac9814a9872e0a46fd78ee Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Fri, 7 Jul 2017 17:13:44 +0200
+Subject: rfkill: add fake rfkill support
+
+allow building of modules depending on RFKILL even if RFKILL is not enabled.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ include/linux/rfkill.h | 2 +-
+ net/Makefile | 2 +-
+ net/rfkill/Kconfig | 14 +++++++++-----
+ net/rfkill/Makefile | 2 +-
+ 4 files changed, 12 insertions(+), 8 deletions(-)
+
+--- a/include/linux/rfkill.h
++++ b/include/linux/rfkill.h
+@@ -64,7 +64,7 @@ struct rfkill_ops {
+ int (*set_block)(void *data, bool blocked);
+ };
+
+-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
++#if defined(CONFIG_RFKILL_FULL) || defined(CONFIG_RFKILL_FULL_MODULE)
+ /**
+ * rfkill_alloc - Allocate rfkill structure
+ * @name: name of the struct -- the string is not copied internally
+--- a/net/Makefile
++++ b/net/Makefile
+@@ -53,7 +53,7 @@ obj-$(CONFIG_TIPC) += tipc/
+ obj-$(CONFIG_NETLABEL) += netlabel/
+ obj-$(CONFIG_IUCV) += iucv/
+ obj-$(CONFIG_SMC) += smc/
+-obj-$(CONFIG_RFKILL) += rfkill/
++obj-$(CONFIG_RFKILL_FULL) += rfkill/
+ obj-$(CONFIG_NET_9P) += 9p/
+ obj-$(CONFIG_CAIF) += caif/
+ ifneq ($(CONFIG_DCB),)
+--- a/net/rfkill/Kconfig
++++ b/net/rfkill/Kconfig
+@@ -2,7 +2,11 @@
+ #
+ # RF switch subsystem configuration
+ #
+-menuconfig RFKILL
++config RFKILL
++ bool
++ default y
++
++menuconfig RFKILL_FULL
+ tristate "RF switch subsystem support"
+ help
+ Say Y here if you want to have control over RF switches
+@@ -14,19 +18,19 @@ menuconfig RFKILL
+ # LED trigger support
+ config RFKILL_LEDS
+ bool
+- depends on RFKILL
++ depends on RFKILL_FULL
+ depends on LEDS_TRIGGERS = y || RFKILL = LEDS_TRIGGERS
+ default y
+
+ config RFKILL_INPUT
+ bool "RF switch input support" if EXPERT
+- depends on RFKILL
++ depends on RFKILL_FULL
+ depends on INPUT = y || RFKILL = INPUT
+ default y if !EXPERT
+
+ config RFKILL_GPIO
+ tristate "GPIO RFKILL driver"
+- depends on RFKILL
++ depends on RFKILL_FULL
+ depends on GPIOLIB || COMPILE_TEST
+ default n
+ help
+--- a/net/rfkill/Makefile
++++ b/net/rfkill/Makefile
+@@ -5,5 +5,5 @@
+
+ rfkill-y += core.o
+ rfkill-$(CONFIG_RFKILL_INPUT) += input.o
+-obj-$(CONFIG_RFKILL) += rfkill.o
++obj-$(CONFIG_RFKILL_FULL) += rfkill.o
+ obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o
--- /dev/null
+From: Ben Menchaca <ben.menchaca@qca.qualcomm.com>
+Date: Fri, 7 Jun 2013 18:35:22 -0500
+Subject: MIPS: r4k_cache: use more efficient cache blast
+
+Optimize the compiler output for larger cache blast cases that are
+common for DMA-based networking.
+
+Signed-off-by: Ben Menchaca <ben.menchaca@qca.qualcomm.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+--- a/arch/mips/include/asm/r4kcache.h
++++ b/arch/mips/include/asm/r4kcache.h
+@@ -296,14 +296,46 @@ static inline void prot##extra##blast_##
+ unsigned long end) \
+ { \
+ unsigned long lsize = cpu_##desc##_line_size(); \
++ unsigned long lsize_2 = lsize * 2; \
++ unsigned long lsize_3 = lsize * 3; \
++ unsigned long lsize_4 = lsize * 4; \
++ unsigned long lsize_5 = lsize * 5; \
++ unsigned long lsize_6 = lsize * 6; \
++ unsigned long lsize_7 = lsize * 7; \
++ unsigned long lsize_8 = lsize * 8; \
+ unsigned long addr = start & ~(lsize - 1); \
+- unsigned long aend = (end - 1) & ~(lsize - 1); \
++ unsigned long aend = (end + lsize - 1) & ~(lsize - 1); \
++ int lines = (aend - addr) / lsize; \
+ \
+- while (1) { \
++ while (lines >= 8) { \
++ prot##cache_op(hitop, addr); \
++ prot##cache_op(hitop, addr + lsize); \
++ prot##cache_op(hitop, addr + lsize_2); \
++ prot##cache_op(hitop, addr + lsize_3); \
++ prot##cache_op(hitop, addr + lsize_4); \
++ prot##cache_op(hitop, addr + lsize_5); \
++ prot##cache_op(hitop, addr + lsize_6); \
++ prot##cache_op(hitop, addr + lsize_7); \
++ addr += lsize_8; \
++ lines -= 8; \
++ } \
++ \
++ if (lines & 0x4) { \
++ prot##cache_op(hitop, addr); \
++ prot##cache_op(hitop, addr + lsize); \
++ prot##cache_op(hitop, addr + lsize_2); \
++ prot##cache_op(hitop, addr + lsize_3); \
++ addr += lsize_4; \
++ } \
++ \
++ if (lines & 0x2) { \
++ prot##cache_op(hitop, addr); \
++ prot##cache_op(hitop, addr + lsize); \
++ addr += lsize_2; \
++ } \
++ \
++ if (lines & 0x1) { \
+ prot##cache_op(hitop, addr); \
+- if (addr == aend) \
+- break; \
+- addr += lsize; \
+ } \
+ }
+
--- /dev/null
+From: John Crispin <john@phrozen.org>
+Subject: hack: kernel: add generic image_cmdline hack to MIPS targets
+
+lede-commit: d59f5b3a987a48508257a0ddbaeadc7909f9f976
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ arch/mips/Kconfig | 4 ++++
+ arch/mips/kernel/head.S | 6 ++++++
+ 2 files changed, 10 insertions(+)
+
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -1170,6 +1170,10 @@ config MIPS_MSC
+ config SYNC_R4K
+ bool
+
++config IMAGE_CMDLINE_HACK
++ bool "OpenWrt specific image command line hack"
++ default n
++
+ config NO_IOPORT_MAP
+ def_bool n
+
+--- a/arch/mips/kernel/head.S
++++ b/arch/mips/kernel/head.S
+@@ -79,6 +79,12 @@ FEXPORT(__kernel_entry)
+ j kernel_entry
+ #endif /* CONFIG_BOOT_RAW */
+
++#ifdef CONFIG_IMAGE_CMDLINE_HACK
++ .ascii "CMDLINE:"
++EXPORT(__image_cmdline)
++ .fill 0x400
++#endif /* CONFIG_IMAGE_CMDLINE_HACK */
++
+ __REF
+
+ NESTED(kernel_entry, 16, sp) # kernel entry point
--- /dev/null
+From 107c0964cb8db7ca28ac5199426414fdab3c274d Mon Sep 17 00:00:00 2001
+From: "Alexandros C. Couloumbis" <alex@ozo.com>
+Date: Fri, 7 Jul 2017 17:14:51 +0200
+Subject: hack: arch: powerpc: drop register save/restore library from modules
+
+Upstream GCC uses a libgcc function for saving/restoring registers. This
+makes the code bigger, and upstream kernels need to carry that function
+for every single kernel module. Our GCC is patched to avoid those
+references, so we can drop the extra bloat for modules.
+
+lede-commit: e8e1084654f50904e6bf77b70b2de3f137d7b3ec
+Signed-off-by: Alexandros C. Couloumbis <alex@ozo.com>
+---
+ arch/powerpc/Makefile | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/arch/powerpc/Makefile
++++ b/arch/powerpc/Makefile
+@@ -61,19 +61,6 @@ machine-$(CONFIG_PPC64) += 64
+ machine-$(CONFIG_CPU_LITTLE_ENDIAN) += le
+ UTS_MACHINE := $(subst $(space),,$(machine-y))
+
+-# XXX This needs to be before we override LD below
+-ifdef CONFIG_PPC32
+-KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o
+-else
+-ifeq ($(call ld-ifversion, -ge, 225000000, y),y)
+-# Have the linker provide sfpr if possible.
+-# There is a corresponding test in arch/powerpc/lib/Makefile
+-KBUILD_LDFLAGS_MODULE += --save-restore-funcs
+-else
+-KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o
+-endif
+-endif
+-
+ ifdef CONFIG_CPU_LITTLE_ENDIAN
+ KBUILD_CFLAGS += -mlittle-endian
+ KBUILD_LDFLAGS += -EL
--- /dev/null
+From f9760b158f610b1792a222cc924073724c061bfb Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Wed, 7 Apr 2021 22:37:57 +0100
+Subject: [PATCH 1/2] mtd: super: don't reply on mtdblock device minor
+To: linux-mtd@lists.infradead.org
+Cc: Vignesh Raghavendra <vigneshr@ti.com>,
+ Richard Weinberger <richard@nod.at>,
+ Miquel Raynal <miquel.raynal@bootlin.com>,
+ David Woodhouse <dwmw2@infradead.org>
+
+For blktrans devices with partitions (ie. part_bits != 0) the
+assumption that the minor number of the mtdblock device matches
+the mtdnum doesn't hold true.
+Properly resolve mtd device from blktrans layer instead.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/mtdsuper.c | 33 ++++++++++++++++++++++++++-------
+ 1 file changed, 26 insertions(+), 7 deletions(-)
+
+--- a/drivers/mtd/mtdsuper.c
++++ b/drivers/mtd/mtdsuper.c
+@@ -9,6 +9,7 @@
+ */
+
+ #include <linux/mtd/super.h>
++#include <linux/mtd/blktrans.h>
+ #include <linux/namei.h>
+ #include <linux/export.h>
+ #include <linux/ctype.h>
+@@ -121,7 +122,8 @@ int get_tree_mtd(struct fs_context *fc,
+ {
+ #ifdef CONFIG_BLOCK
+ struct block_device *bdev;
+- int ret, major;
++ struct mtd_blktrans_dev *blktrans_dev;
++ int ret, major, part_bits;
+ #endif
+ int mtdnr;
+
+@@ -169,21 +171,38 @@ int get_tree_mtd(struct fs_context *fc,
+ /* try the old way - the hack where we allowed users to mount
+ * /dev/mtdblock$(n) but didn't actually _use_ the blockdev
+ */
+- bdev = lookup_bdev(fc->source);
++ bdev = blkdev_get_by_path(fc->source, FMODE_READ, NULL);
+ if (IS_ERR(bdev)) {
+ ret = PTR_ERR(bdev);
+ errorf(fc, "MTD: Couldn't look up '%s': %d", fc->source, ret);
+ return ret;
+ }
+- pr_debug("MTDSB: lookup_bdev() returned 0\n");
++ pr_debug("MTDSB: blkdev_get_by_path() returned 0\n");
+
+ major = MAJOR(bdev->bd_dev);
+- mtdnr = MINOR(bdev->bd_dev);
+- bdput(bdev);
+
+- if (major == MTD_BLOCK_MAJOR)
+- return mtd_get_sb_by_nr(fc, mtdnr, fill_super);
++ if (major == MTD_BLOCK_MAJOR) {
++ if (!bdev->bd_disk) {
++ blkdev_put(bdev, FMODE_READ);
++ BUG();
++ return -EINVAL;
++ }
++
++ blktrans_dev = (struct mtd_blktrans_dev *)(bdev->bd_disk->private_data);
++ if (!blktrans_dev || !blktrans_dev->tr) {
++ blkdev_put(bdev, FMODE_READ);
++ BUG();
++ return -EINVAL;
++ }
++ mtdnr = blktrans_dev->devnum;
++ part_bits = blktrans_dev->tr->part_bits;
++ blkdev_put(bdev, FMODE_READ);
++ if (MINOR(bdev->bd_dev) != (mtdnr << part_bits))
++ return -EINVAL;
+
++ return mtd_get_sb_by_nr(fc, mtdnr, fill_super);
++ }
++ blkdev_put(bdev, FMODE_READ);
+ #endif /* CONFIG_BLOCK */
+
+ if (!(fc->sb_flags & SB_SILENT))
--- /dev/null
+From 0bccc3722bdd88e8ae995e77ef9f7b77ee4cbdee Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Wed, 7 Apr 2021 22:45:54 +0100
+Subject: [PATCH 2/2] mtd: blktrans: call add disks after mtd device
+To: linux-mtd@lists.infradead.org
+Cc: Vignesh Raghavendra <vigneshr@ti.com>,
+ Richard Weinberger <richard@nod.at>,
+ Miquel Raynal <miquel.raynal@bootlin.com>,
+ David Woodhouse <dwmw2@infradead.org>
+
+Calling device_add_disk while holding mtd_table_mutex leads
+to deadlock in case part_bits!=0 as block partition parsers
+will try to open the newly created disks, trying to acquire
+mutex once again.
+Move device_add_disk to additional function called after
+add partitions of an MTD device have been added and locks
+have been released.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/mtd_blkdevs.c | 33 ++++++++++++++++++++++++++-------
+ drivers/mtd/mtdcore.c | 3 +++
+ include/linux/mtd/blktrans.h | 1 +
+ 3 files changed, 30 insertions(+), 7 deletions(-)
+
+--- a/drivers/mtd/mtd_blkdevs.c
++++ b/drivers/mtd/mtd_blkdevs.c
+@@ -457,13 +457,6 @@ int add_mtd_blktrans_dev(struct mtd_blkt
+ if (new->readonly)
+ set_disk_ro(gd, 1);
+
+- device_add_disk(&new->mtd->dev, gd, NULL);
+-
+- if (new->disk_attributes) {
+- ret = sysfs_create_group(&disk_to_dev(gd)->kobj,
+- new->disk_attributes);
+- WARN_ON(ret);
+- }
+ return 0;
+ error4:
+ kfree(new->tag_set);
+@@ -475,6 +468,27 @@ error1:
+ return ret;
+ }
+
++void register_mtd_blktrans_devs(void)
++{
++ struct mtd_blktrans_ops *tr;
++ struct mtd_blktrans_dev *dev, *next;
++ int ret;
++
++ list_for_each_entry(tr, &blktrans_majors, list) {
++ list_for_each_entry_safe(dev, next, &tr->devs, list) {
++ if (dev->disk->flags & GENHD_FL_UP)
++ continue;
++
++ device_add_disk(&dev->mtd->dev, dev->disk, NULL);
++ if (dev->disk_attributes) {
++ ret = sysfs_create_group(&disk_to_dev(dev->disk)->kobj,
++ dev->disk_attributes);
++ WARN_ON(ret);
++ }
++ }
++ }
++}
++
+ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
+ {
+ unsigned long flags;
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -31,6 +31,7 @@
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/mtd/blktrans.h>
+
+ #include "mtdcore.h"
+
+@@ -851,6 +852,8 @@ int mtd_device_parse_register(struct mtd
+ register_reboot_notifier(&mtd->reboot_notifier);
+ }
+
++ register_mtd_blktrans_devs();
++
+ out:
+ if (ret && device_is_registered(&mtd->dev))
+ del_mtd_device(mtd);
+--- a/include/linux/mtd/blktrans.h
++++ b/include/linux/mtd/blktrans.h
+@@ -76,6 +76,6 @@ extern int deregister_mtd_blktrans(struc
+ extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
+ extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
+ extern int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev);
+-
++extern void register_mtd_blktrans_devs(void);
+
+ #endif /* __MTD_TRANS_H__ */
--- /dev/null
+--- a/block/blk.h
++++ b/block/blk.h
+@@ -361,6 +361,8 @@ char *disk_name(struct gendisk *hd, int
+ #define ADDPART_FLAG_NONE 0
+ #define ADDPART_FLAG_RAID 1
+ #define ADDPART_FLAG_WHOLEDISK 2
++#define ADDPART_FLAG_READONLY 4
++#define ADDPART_FLAG_ROOTDEV 8
+ void delete_partition(struct hd_struct *part);
+ int bdev_add_partition(struct block_device *bdev, int partno,
+ sector_t start, sector_t length);
+--- a/block/partitions/Kconfig
++++ b/block/partitions/Kconfig
+@@ -101,6 +101,13 @@ config ATARI_PARTITION
+ Say Y here if you would like to use hard disks under Linux which
+ were partitioned under the Atari OS.
+
++config FIT_PARTITION
++ bool "Flattened-Image-Tree (FIT) partition support" if PARTITION_ADVANCED
++ default n
++ help
++ Say Y here if your system needs to mount the filesystem part of
++ a Flattened-Image-Tree (FIT) image commonly used with Das U-Boot.
++
+ config IBM_PARTITION
+ bool "IBM disk label and partition support"
+ depends on PARTITION_ADVANCED && S390
+--- a/block/partitions/Makefile
++++ b/block/partitions/Makefile
+@@ -8,6 +8,7 @@ obj-$(CONFIG_ACORN_PARTITION) += acorn.o
+ obj-$(CONFIG_AMIGA_PARTITION) += amiga.o
+ obj-$(CONFIG_ATARI_PARTITION) += atari.o
+ obj-$(CONFIG_AIX_PARTITION) += aix.o
++obj-$(CONFIG_FIT_PARTITION) += fit.o
+ obj-$(CONFIG_CMDLINE_PARTITION) += cmdline.o
+ obj-$(CONFIG_MAC_PARTITION) += mac.o
+ obj-$(CONFIG_LDM_PARTITION) += ldm.o
+--- a/block/partitions/check.h
++++ b/block/partitions/check.h
+@@ -58,6 +58,7 @@ int amiga_partition(struct parsed_partit
+ int atari_partition(struct parsed_partitions *state);
+ int cmdline_partition(struct parsed_partitions *state);
+ int efi_partition(struct parsed_partitions *state);
++int fit_partition(struct parsed_partitions *state);
+ int ibm_partition(struct parsed_partitions *);
+ int karma_partition(struct parsed_partitions *state);
+ int ldm_partition(struct parsed_partitions *state);
+@@ -68,3 +69,5 @@ int sgi_partition(struct parsed_partitio
+ int sun_partition(struct parsed_partitions *state);
+ int sysv68_partition(struct parsed_partitions *state);
+ int ultrix_partition(struct parsed_partitions *state);
++
++int parse_fit_partitions(struct parsed_partitions *state, u64 start_sector, u64 nr_sectors, int *slot, int add_remain);
+--- a/block/partitions/core.c
++++ b/block/partitions/core.c
+@@ -10,6 +10,10 @@
+ #include <linux/vmalloc.h>
+ #include <linux/blktrace_api.h>
+ #include <linux/raid/detect.h>
++#ifdef CONFIG_FIT_PARTITION
++#include <linux/root_dev.h>
++#endif
++
+ #include "check.h"
+
+ static int (*check_part[])(struct parsed_partitions *) = {
+@@ -46,6 +50,9 @@ static int (*check_part[])(struct parsed
+ #ifdef CONFIG_EFI_PARTITION
+ efi_partition, /* this must come before msdos */
+ #endif
++#ifdef CONFIG_FIT_PARTITION
++ fit_partition,
++#endif
+ #ifdef CONFIG_SGI_PARTITION
+ sgi_partition,
+ #endif
+@@ -694,6 +701,14 @@ static bool blk_add_partition(struct gen
+ (state->parts[p].flags & ADDPART_FLAG_RAID))
+ md_autodetect_dev(part_to_dev(part)->devt);
+
++#ifdef CONFIG_FIT_PARTITION
++ if ((state->parts[p].flags & ADDPART_FLAG_ROOTDEV) && ROOT_DEV == 0)
++ ROOT_DEV = part_to_dev(part)->devt;
++
++ if (state->parts[p].flags & ADDPART_FLAG_READONLY)
++ part->policy = true;
++#endif
++
+ return true;
+ }
+
+--- a/drivers/mtd/ubi/block.c
++++ b/drivers/mtd/ubi/block.c
+@@ -396,7 +396,11 @@ int ubiblock_create(struct ubi_volume_in
+ dev->leb_size = vi->usable_leb_size;
+
+ /* Initialize the gendisk of this ubiblock device */
++#ifdef CONFIG_FIT_PARTITION
++ gd = alloc_disk(0);
++#else
+ gd = alloc_disk(1);
++#endif
+ if (!gd) {
+ pr_err("UBI: block: alloc_disk failed\n");
+ ret = -ENODEV;
+@@ -413,6 +417,9 @@ int ubiblock_create(struct ubi_volume_in
+ goto out_put_disk;
+ }
+ gd->private_data = dev;
++#ifdef CONFIG_FIT_PARTITION
++ gd->flags |= GENHD_FL_EXT_DEVT;
++#endif
+ sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id);
+ set_capacity(gd, disk_capacity);
+ dev->gd = gd;
+--- a/block/partitions/efi.c
++++ b/block/partitions/efi.c
+@@ -706,6 +706,9 @@ int efi_partition(struct parsed_partitio
+ gpt_entry *ptes = NULL;
+ u32 i;
+ unsigned ssz = bdev_logical_block_size(state->bdev) / 512;
++#ifdef CONFIG_FIT_PARTITION
++ u32 extra_slot = 64;
++#endif
+
+ if (!find_valid_gpt(state, &gpt, &ptes) || !gpt || !ptes) {
+ kfree(gpt);
+@@ -739,6 +742,11 @@ int efi_partition(struct parsed_partitio
+ ARRAY_SIZE(ptes[i].partition_name));
+ utf16_le_to_7bit(ptes[i].partition_name, label_max, info->volname);
+ state->parts[i + 1].has_info = true;
++#ifdef CONFIG_FIT_PARTITION
++ /* If this is a U-Boot FIT volume it may have subpartitions */
++ if (!efi_guidcmp(ptes[i].partition_type_guid, PARTITION_LINUX_FIT_GUID))
++ (void) parse_fit_partitions(state, start * ssz, size * ssz, &extra_slot, 1);
++#endif
+ }
+ kfree(ptes);
+ kfree(gpt);
+--- a/block/partitions/efi.h
++++ b/block/partitions/efi.h
+@@ -52,6 +52,9 @@
+ #define PARTITION_LINUX_LVM_GUID \
+ EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \
+ 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28)
++#define PARTITION_LINUX_FIT_GUID \
++ EFI_GUID( 0xcae9be83, 0xb15f, 0x49cc, \
++ 0x86, 0x3f, 0x08, 0x1b, 0x74, 0x4a, 0x2d, 0x93)
+
+ typedef struct _gpt_header {
+ __le64 signature;
+--- a/drivers/mtd/mtdblock.c
++++ b/drivers/mtd/mtdblock.c
+@@ -334,7 +334,11 @@ static void mtdblock_remove_dev(struct m
+ static struct mtd_blktrans_ops mtdblock_tr = {
+ .name = "mtdblock",
+ .major = MTD_BLOCK_MAJOR,
++#ifdef CONFIG_FIT_PARTITION
++ .part_bits = 2,
++#else
+ .part_bits = 0,
++#endif
+ .blksize = 512,
+ .open = mtdblock_open,
+ .flush = mtdblock_flush,
+--- a/drivers/mtd/mtd_blkdevs.c
++++ b/drivers/mtd/mtd_blkdevs.c
+@@ -407,18 +407,8 @@ int add_mtd_blktrans_dev(struct mtd_blkt
+ gd->first_minor = (new->devnum) << tr->part_bits;
+ gd->fops = &mtd_block_ops;
+
+- if (tr->part_bits)
+- if (new->devnum < 26)
+- snprintf(gd->disk_name, sizeof(gd->disk_name),
+- "%s%c", tr->name, 'a' + new->devnum);
+- else
+- snprintf(gd->disk_name, sizeof(gd->disk_name),
+- "%s%c%c", tr->name,
+- 'a' - 1 + new->devnum / 26,
+- 'a' + new->devnum % 26);
+- else
+- snprintf(gd->disk_name, sizeof(gd->disk_name),
+- "%s%d", tr->name, new->devnum);
++ snprintf(gd->disk_name, sizeof(gd->disk_name),
++ "%s%d", tr->name, new->devnum);
+
+ set_capacity(gd, ((u64)new->size * tr->blksize) >> 9);
+
+--- a/block/partitions/msdos.c
++++ b/block/partitions/msdos.c
+@@ -563,6 +563,15 @@ static void parse_minix(struct parsed_pa
+ #endif /* CONFIG_MINIX_SUBPARTITION */
+ }
+
++static void parse_fit_mbr(struct parsed_partitions *state,
++ sector_t offset, sector_t size, int origin)
++{
++#ifdef CONFIG_FIT_PARTITION
++ u32 extra_slot = 64;
++ (void) parse_fit_partitions(state, offset, size, &extra_slot, 1);
++#endif /* CONFIG_FIT_PARTITION */
++}
++
+ static struct {
+ unsigned char id;
+ void (*parse)(struct parsed_partitions *, sector_t, sector_t, int);
+@@ -574,6 +583,7 @@ static struct {
+ {UNIXWARE_PARTITION, parse_unixware},
+ {SOLARIS_X86_PARTITION, parse_solaris_x86},
+ {NEW_SOLARIS_X86_PARTITION, parse_solaris_x86},
++ {FIT_PARTITION, parse_fit_mbr},
+ {0, NULL},
+ };
+
+--- a/include/linux/msdos_partition.h
++++ b/include/linux/msdos_partition.h
+@@ -31,6 +31,7 @@ enum msdos_sys_ind {
+ LINUX_LVM_PARTITION = 0x8e,
+ LINUX_RAID_PARTITION = 0xfd, /* autodetect RAID partition */
+
++ FIT_PARTITION = 0x2e, /* U-Boot uImage.FIT */
+ SOLARIS_X86_PARTITION = 0x82, /* also Linux swap partitions */
+ NEW_SOLARIS_X86_PARTITION = 0xbf,
+
--- /dev/null
+From: Gabor Juhos <juhosg@openwrt.org>
+Subject: kernel/3.1[02]: move MTD root device setup code to mtdcore
+
+The current code only allows to automatically set
+root device on MTD partitions. Move the code to MTD
+core to allow to use it with all MTD devices.
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/mtd/mtdcore.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -27,6 +27,7 @@
+ #include <linux/reboot.h>
+ #include <linux/leds.h>
+ #include <linux/debugfs.h>
++#include <linux/root_dev.h>
+ #include <linux/nvmem-provider.h>
+
+ #include <linux/mtd/mtd.h>
+@@ -694,6 +695,19 @@ int add_mtd_device(struct mtd_info *mtd)
+ of this try_ nonsense, and no bitching about it
+ either. :) */
+ __module_get(THIS_MODULE);
++
++ if (!strcmp(mtd->name, "rootfs") &&
++ IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
++ ROOT_DEV == 0) {
++ unsigned int index = mtd->index;
++ pr_notice("mtd: device %d (%s) set to be root filesystem\n",
++ mtd->index, mtd->name);
++#ifdef CONFIG_FIT_PARTITION
++ index <<= 2;
++#endif
++ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, index);
++ }
++
+ return 0;
+
+ fail_nvmem_add:
--- /dev/null
+From 6fa9e3678eb002246df1280322b6a024853950a5 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 11 Oct 2021 00:53:14 +0200
+Subject: [PATCH] drivers: mtd: parsers: add nvmem support to cmdlinepart
+
+Assuming cmdlinepart is only one level deep partition scheme and that
+static partition are also defined in DTS, we can assign an of_node for
+partition declared from bootargs. cmdlinepart have priority than
+fiexed-partition parser so in this specific case the parser doesn't
+assign an of_node. Fix this by searching a defined of_node using a
+similar fixed_partition parser and if a partition is found with the same
+label, check that it has the same offset and size and return the DT
+of_node to correctly use NVMEM cells.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+---
+ drivers/mtd/parsers/cmdlinepart.c | 71 +++++++++++++++++++++++++++++++
+ 1 file changed, 71 insertions(+)
+
+--- a/drivers/mtd/parsers/cmdlinepart.c
++++ b/drivers/mtd/parsers/cmdlinepart.c
+@@ -43,6 +43,7 @@
+ #include <linux/mtd/partitions.h>
+ #include <linux/module.h>
+ #include <linux/err.h>
++#include <linux/of.h>
+
+ /* debug macro */
+ #if 0
+@@ -323,6 +324,68 @@ static int mtdpart_setup_real(char *s)
+ return 0;
+ }
+
++static int search_fixed_partition(struct mtd_info *master,
++ struct mtd_partition *target_part,
++ struct mtd_partition *fixed_part)
++{
++ struct device_node *mtd_node;
++ struct device_node *ofpart_node;
++ struct device_node *pp;
++ struct mtd_partition part;
++ const char *partname;
++
++ mtd_node = mtd_get_of_node(master);
++ if (!mtd_node)
++ return -EINVAL;
++
++ ofpart_node = of_get_child_by_name(mtd_node, "partitions");
++
++ for_each_child_of_node(ofpart_node, pp) {
++ const __be32 *reg;
++ int len;
++ int a_cells, s_cells;
++
++ reg = of_get_property(pp, "reg", &len);
++ if (!reg) {
++ pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n",
++ master->name, pp,
++ mtd_node);
++ continue;
++ }
++
++ a_cells = of_n_addr_cells(pp);
++ s_cells = of_n_size_cells(pp);
++ if (len / 4 != a_cells + s_cells) {
++ pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n",
++ master->name, pp,
++ mtd_node);
++ continue;
++ }
++
++ part.offset = of_read_number(reg, a_cells);
++ part.size = of_read_number(reg + a_cells, s_cells);
++ part.of_node = pp;
++
++ partname = of_get_property(pp, "label", &len);
++ if (!partname)
++ partname = of_get_property(pp, "name", &len);
++ part.name = partname;
++
++ if (!strncmp(target_part->name, part.name, len)) {
++ if (part.offset != target_part->offset)
++ return -EINVAL;
++
++ if (part.size != target_part->size)
++ return -EINVAL;
++
++ memcpy(fixed_part, &part, sizeof(struct mtd_partition));
++ return 0;
++ }
++ }
++
++ return -EINVAL;
++}
++
+ /*
+ * Main function to be called from the MTD mapping driver/device to
+ * obtain the partitioning information. At this point the command line
+@@ -338,6 +401,7 @@ static int parse_cmdline_partitions(stru
+ int i, err;
+ struct cmdline_mtd_partition *part;
+ const char *mtd_id = master->name;
++ struct mtd_partition fixed_part;
+
+ /* parse command line */
+ if (!cmdline_parsed) {
+@@ -382,6 +446,13 @@ static int parse_cmdline_partitions(stru
+ sizeof(*part->parts) * (part->num_parts - i));
+ i--;
+ }
++
++ err = search_fixed_partition(master, &part->parts[i], &fixed_part);
++ if (!err) {
++ part->parts[i].of_node = fixed_part.of_node;
++ pr_info("Found partition defined in DT for %s. Assigning OF node to support nvmem.",
++ part->parts[i].name);
++ }
+ }
+
+ *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts,
--- /dev/null
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -15,6 +15,10 @@ config MTD_NAND_ECC
+ bool
+ depends on MTD_NAND_CORE
+
++config MTD_NAND_MTK_BMT
++ bool "Support MediaTek NAND Bad-block Management Table"
++ default n
++
+ endmenu
+
+ endmenu
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -2,6 +2,7 @@
+
+ nandcore-objs := core.o bbt.o
+ obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
++obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o
+
+ obj-y += onenand/
+ obj-y += raw/
--- /dev/null
+From 3fd297761ac246c54d7723c57fca95c112b99465 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 15 Jul 2017 21:15:44 +0200
+Subject: lzma: de-bloat the lzma library used by jffs2
+
+lede-commit: 3fd1dd08fbcbb78b34efefd32c3032e5c99108d6
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/lzma/LzFind.h | 17 ---
+ include/linux/lzma/LzmaDec.h | 101 ---------------
+ include/linux/lzma/LzmaEnc.h | 20 ---
+ lib/lzma/LzFind.c | 287 ++++---------------------------------------
+ lib/lzma/LzmaDec.c | 86 +------------
+ lib/lzma/LzmaEnc.c | 172 ++------------------------
+ 6 files changed, 42 insertions(+), 641 deletions(-)
+
+--- a/include/linux/lzma/LzFind.h
++++ b/include/linux/lzma/LzFind.h
+@@ -55,11 +55,6 @@ typedef struct _CMatchFinder
+
+ #define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos)
+
+-int MatchFinder_NeedMove(CMatchFinder *p);
+-Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p);
+-void MatchFinder_MoveBlock(CMatchFinder *p);
+-void MatchFinder_ReadIfRequired(CMatchFinder *p);
+-
+ void MatchFinder_Construct(CMatchFinder *p);
+
+ /* Conditions:
+@@ -70,12 +65,6 @@ int MatchFinder_Create(CMatchFinder *p,
+ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
+ ISzAlloc *alloc);
+ void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc);
+-void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems);
+-void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue);
+-
+-UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son,
+- UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue,
+- UInt32 *distances, UInt32 maxLen);
+
+ /*
+ Conditions:
+@@ -102,12 +91,6 @@ typedef struct _IMatchFinder
+
+ void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable);
+
+-void MatchFinder_Init(CMatchFinder *p);
+-UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
+-UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
+-void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
+-void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
+-
+ #ifdef __cplusplus
+ }
+ #endif
+--- a/include/linux/lzma/LzmaDec.h
++++ b/include/linux/lzma/LzmaDec.h
+@@ -31,14 +31,6 @@ typedef struct _CLzmaProps
+ UInt32 dicSize;
+ } CLzmaProps;
+
+-/* LzmaProps_Decode - decodes properties
+-Returns:
+- SZ_OK
+- SZ_ERROR_UNSUPPORTED - Unsupported properties
+-*/
+-
+-SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);
+-
+
+ /* ---------- LZMA Decoder state ---------- */
+
+@@ -70,8 +62,6 @@ typedef struct
+
+ #define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; }
+
+-void LzmaDec_Init(CLzmaDec *p);
+-
+ /* There are two types of LZMA streams:
+ 0) Stream with end mark. That end mark adds about 6 bytes to compressed size.
+ 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */
+@@ -108,97 +98,6 @@ typedef enum
+
+ /* ELzmaStatus is used only as output value for function call */
+
+-
+-/* ---------- Interfaces ---------- */
+-
+-/* There are 3 levels of interfaces:
+- 1) Dictionary Interface
+- 2) Buffer Interface
+- 3) One Call Interface
+- You can select any of these interfaces, but don't mix functions from different
+- groups for same object. */
+-
+-
+-/* There are two variants to allocate state for Dictionary Interface:
+- 1) LzmaDec_Allocate / LzmaDec_Free
+- 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
+- You can use variant 2, if you set dictionary buffer manually.
+- For Buffer Interface you must always use variant 1.
+-
+-LzmaDec_Allocate* can return:
+- SZ_OK
+- SZ_ERROR_MEM - Memory allocation error
+- SZ_ERROR_UNSUPPORTED - Unsupported properties
+-*/
+-
+-SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc);
+-void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc);
+-
+-SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc);
+-void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc);
+-
+-/* ---------- Dictionary Interface ---------- */
+-
+-/* You can use it, if you want to eliminate the overhead for data copying from
+- dictionary to some other external buffer.
+- You must work with CLzmaDec variables directly in this interface.
+-
+- STEPS:
+- LzmaDec_Constr()
+- LzmaDec_Allocate()
+- for (each new stream)
+- {
+- LzmaDec_Init()
+- while (it needs more decompression)
+- {
+- LzmaDec_DecodeToDic()
+- use data from CLzmaDec::dic and update CLzmaDec::dicPos
+- }
+- }
+- LzmaDec_Free()
+-*/
+-
+-/* LzmaDec_DecodeToDic
+-
+- The decoding to internal dictionary buffer (CLzmaDec::dic).
+- You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
+-
+-finishMode:
+- It has meaning only if the decoding reaches output limit (dicLimit).
+- LZMA_FINISH_ANY - Decode just dicLimit bytes.
+- LZMA_FINISH_END - Stream must be finished after dicLimit.
+-
+-Returns:
+- SZ_OK
+- status:
+- LZMA_STATUS_FINISHED_WITH_MARK
+- LZMA_STATUS_NOT_FINISHED
+- LZMA_STATUS_NEEDS_MORE_INPUT
+- LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
+- SZ_ERROR_DATA - Data error
+-*/
+-
+-SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit,
+- const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
+-
+-
+-/* ---------- Buffer Interface ---------- */
+-
+-/* It's zlib-like interface.
+- See LzmaDec_DecodeToDic description for information about STEPS and return results,
+- but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need
+- to work with CLzmaDec variables manually.
+-
+-finishMode:
+- It has meaning only if the decoding reaches output limit (*destLen).
+- LZMA_FINISH_ANY - Decode just destLen bytes.
+- LZMA_FINISH_END - Stream must be finished after (*destLen).
+-*/
+-
+-SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen,
+- const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
+-
+-
+ /* ---------- One Call Interface ---------- */
+
+ /* LzmaDecode
+--- a/include/linux/lzma/LzmaEnc.h
++++ b/include/linux/lzma/LzmaEnc.h
+@@ -31,9 +31,6 @@ typedef struct _CLzmaEncProps
+ } CLzmaEncProps;
+
+ void LzmaEncProps_Init(CLzmaEncProps *p);
+-void LzmaEncProps_Normalize(CLzmaEncProps *p);
+-UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2);
+-
+
+ /* ---------- CLzmaEncHandle Interface ---------- */
+
+@@ -53,26 +50,9 @@ CLzmaEncHandle LzmaEnc_Create(ISzAlloc *
+ void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig);
+ SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props);
+ SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size);
+-SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream,
+- ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
+ SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
+ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
+
+-/* ---------- One Call Interface ---------- */
+-
+-/* LzmaEncode
+-Return code:
+- SZ_OK - OK
+- SZ_ERROR_MEM - Memory allocation error
+- SZ_ERROR_PARAM - Incorrect paramater
+- SZ_ERROR_OUTPUT_EOF - output buffer overflow
+- SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version)
+-*/
+-
+-SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
+- const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
+- ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
+-
+ #ifdef __cplusplus
+ }
+ #endif
+--- a/lib/lzma/LzFind.c
++++ b/lib/lzma/LzFind.c
+@@ -14,9 +14,15 @@
+
+ #define kStartMaxLen 3
+
++#if 0
++#define DIRECT_INPUT p->directInput
++#else
++#define DIRECT_INPUT 1
++#endif
++
+ static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc)
+ {
+- if (!p->directInput)
++ if (!DIRECT_INPUT)
+ {
+ alloc->Free(alloc, p->bufferBase);
+ p->bufferBase = 0;
+@@ -28,7 +34,7 @@ static void LzInWindow_Free(CMatchFinder
+ static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc)
+ {
+ UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv;
+- if (p->directInput)
++ if (DIRECT_INPUT)
+ {
+ p->blockSize = blockSize;
+ return 1;
+@@ -42,12 +48,12 @@ static int LzInWindow_Create(CMatchFinde
+ return (p->bufferBase != 0);
+ }
+
+-Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
+-Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; }
++static Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
++static Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; }
+
+-UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
++static UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
+
+-void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue)
++static void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue)
+ {
+ p->posLimit -= subValue;
+ p->pos -= subValue;
+@@ -58,7 +64,7 @@ static void MatchFinder_ReadBlock(CMatch
+ {
+ if (p->streamEndWasReached || p->result != SZ_OK)
+ return;
+- if (p->directInput)
++ if (DIRECT_INPUT)
+ {
+ UInt32 curSize = 0xFFFFFFFF - p->streamPos;
+ if (curSize > p->directInputRem)
+@@ -89,7 +95,7 @@ static void MatchFinder_ReadBlock(CMatch
+ }
+ }
+
+-void MatchFinder_MoveBlock(CMatchFinder *p)
++static void MatchFinder_MoveBlock(CMatchFinder *p)
+ {
+ memmove(p->bufferBase,
+ p->buffer - p->keepSizeBefore,
+@@ -97,22 +103,14 @@ void MatchFinder_MoveBlock(CMatchFinder
+ p->buffer = p->bufferBase + p->keepSizeBefore;
+ }
+
+-int MatchFinder_NeedMove(CMatchFinder *p)
++static int MatchFinder_NeedMove(CMatchFinder *p)
+ {
+- if (p->directInput)
++ if (DIRECT_INPUT)
+ return 0;
+ /* if (p->streamEndWasReached) return 0; */
+ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter);
+ }
+
+-void MatchFinder_ReadIfRequired(CMatchFinder *p)
+-{
+- if (p->streamEndWasReached)
+- return;
+- if (p->keepSizeAfter >= p->streamPos - p->pos)
+- MatchFinder_ReadBlock(p);
+-}
+-
+ static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p)
+ {
+ if (MatchFinder_NeedMove(p))
+@@ -268,7 +266,7 @@ static void MatchFinder_SetLimits(CMatch
+ p->posLimit = p->pos + limit;
+ }
+
+-void MatchFinder_Init(CMatchFinder *p)
++static void MatchFinder_Init(CMatchFinder *p)
+ {
+ UInt32 i;
+ for (i = 0; i < p->hashSizeSum; i++)
+@@ -287,7 +285,7 @@ static UInt32 MatchFinder_GetSubValue(CM
+ return (p->pos - p->historySize - 1) & kNormalizeMask;
+ }
+
+-void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems)
++static void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems)
+ {
+ UInt32 i;
+ for (i = 0; i < numItems; i++)
+@@ -319,38 +317,7 @@ static void MatchFinder_CheckLimits(CMat
+ MatchFinder_SetLimits(p);
+ }
+
+-static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
+- UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
+- UInt32 *distances, UInt32 maxLen)
+-{
+- son[_cyclicBufferPos] = curMatch;
+- for (;;)
+- {
+- UInt32 delta = pos - curMatch;
+- if (cutValue-- == 0 || delta >= _cyclicBufferSize)
+- return distances;
+- {
+- const Byte *pb = cur - delta;
+- curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
+- if (pb[maxLen] == cur[maxLen] && *pb == *cur)
+- {
+- UInt32 len = 0;
+- while (++len != lenLimit)
+- if (pb[len] != cur[len])
+- break;
+- if (maxLen < len)
+- {
+- *distances++ = maxLen = len;
+- *distances++ = delta - 1;
+- if (len == lenLimit)
+- return distances;
+- }
+- }
+- }
+- }
+-}
+-
+-UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
++static UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
+ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
+ UInt32 *distances, UInt32 maxLen)
+ {
+@@ -460,10 +427,10 @@ static void SkipMatchesSpec(UInt32 lenLi
+ p->buffer++; \
+ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p);
+
+-#define MOVE_POS_RET MOVE_POS return offset;
+-
+ static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; }
+
++#define MOVE_POS_RET MatchFinder_MovePos(p); return offset;
++
+ #define GET_MATCHES_HEADER2(minLen, ret_op) \
+ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \
+ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \
+@@ -479,62 +446,7 @@ static void MatchFinder_MovePos(CMatchFi
+ distances + offset, maxLen) - distances); MOVE_POS_RET;
+
+ #define SKIP_FOOTER \
+- SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS;
+-
+-static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+-{
+- UInt32 offset;
+- GET_MATCHES_HEADER(2)
+- HASH2_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- offset = 0;
+- GET_MATCHES_FOOTER(offset, 1)
+-}
+-
+-UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+-{
+- UInt32 offset;
+- GET_MATCHES_HEADER(3)
+- HASH_ZIP_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- offset = 0;
+- GET_MATCHES_FOOTER(offset, 2)
+-}
+-
+-static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+-{
+- UInt32 hash2Value, delta2, maxLen, offset;
+- GET_MATCHES_HEADER(3)
+-
+- HASH3_CALC;
+-
+- delta2 = p->pos - p->hash[hash2Value];
+- curMatch = p->hash[kFix3HashSize + hashValue];
+-
+- p->hash[hash2Value] =
+- p->hash[kFix3HashSize + hashValue] = p->pos;
+-
+-
+- maxLen = 2;
+- offset = 0;
+- if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
+- {
+- for (; maxLen != lenLimit; maxLen++)
+- if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
+- break;
+- distances[0] = maxLen;
+- distances[1] = delta2 - 1;
+- offset = 2;
+- if (maxLen == lenLimit)
+- {
+- SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
+- MOVE_POS_RET;
+- }
+- }
+- GET_MATCHES_FOOTER(offset, maxLen)
+-}
++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MatchFinder_MovePos(p);
+
+ static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+ {
+@@ -583,108 +495,6 @@ static UInt32 Bt4_MatchFinder_GetMatches
+ GET_MATCHES_FOOTER(offset, maxLen)
+ }
+
+-static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+-{
+- UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset;
+- GET_MATCHES_HEADER(4)
+-
+- HASH4_CALC;
+-
+- delta2 = p->pos - p->hash[ hash2Value];
+- delta3 = p->pos - p->hash[kFix3HashSize + hash3Value];
+- curMatch = p->hash[kFix4HashSize + hashValue];
+-
+- p->hash[ hash2Value] =
+- p->hash[kFix3HashSize + hash3Value] =
+- p->hash[kFix4HashSize + hashValue] = p->pos;
+-
+- maxLen = 1;
+- offset = 0;
+- if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
+- {
+- distances[0] = maxLen = 2;
+- distances[1] = delta2 - 1;
+- offset = 2;
+- }
+- if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur)
+- {
+- maxLen = 3;
+- distances[offset + 1] = delta3 - 1;
+- offset += 2;
+- delta2 = delta3;
+- }
+- if (offset != 0)
+- {
+- for (; maxLen != lenLimit; maxLen++)
+- if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
+- break;
+- distances[offset - 2] = maxLen;
+- if (maxLen == lenLimit)
+- {
+- p->son[p->cyclicBufferPos] = curMatch;
+- MOVE_POS_RET;
+- }
+- }
+- if (maxLen < 3)
+- maxLen = 3;
+- offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
+- distances + offset, maxLen) - (distances));
+- MOVE_POS_RET
+-}
+-
+-UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+-{
+- UInt32 offset;
+- GET_MATCHES_HEADER(3)
+- HASH_ZIP_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
+- distances, 2) - (distances));
+- MOVE_POS_RET
+-}
+-
+-static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+-{
+- do
+- {
+- SKIP_HEADER(2)
+- HASH2_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- SKIP_FOOTER
+- }
+- while (--num != 0);
+-}
+-
+-void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+-{
+- do
+- {
+- SKIP_HEADER(3)
+- HASH_ZIP_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- SKIP_FOOTER
+- }
+- while (--num != 0);
+-}
+-
+-static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+-{
+- do
+- {
+- UInt32 hash2Value;
+- SKIP_HEADER(3)
+- HASH3_CALC;
+- curMatch = p->hash[kFix3HashSize + hashValue];
+- p->hash[hash2Value] =
+- p->hash[kFix3HashSize + hashValue] = p->pos;
+- SKIP_FOOTER
+- }
+- while (--num != 0);
+-}
+-
+ static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+ {
+ do
+@@ -701,61 +511,12 @@ static void Bt4_MatchFinder_Skip(CMatchF
+ while (--num != 0);
+ }
+
+-static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+-{
+- do
+- {
+- UInt32 hash2Value, hash3Value;
+- SKIP_HEADER(4)
+- HASH4_CALC;
+- curMatch = p->hash[kFix4HashSize + hashValue];
+- p->hash[ hash2Value] =
+- p->hash[kFix3HashSize + hash3Value] =
+- p->hash[kFix4HashSize + hashValue] = p->pos;
+- p->son[p->cyclicBufferPos] = curMatch;
+- MOVE_POS
+- }
+- while (--num != 0);
+-}
+-
+-void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+-{
+- do
+- {
+- SKIP_HEADER(3)
+- HASH_ZIP_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- p->son[p->cyclicBufferPos] = curMatch;
+- MOVE_POS
+- }
+- while (--num != 0);
+-}
+-
+ void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable)
+ {
+ vTable->Init = (Mf_Init_Func)MatchFinder_Init;
+ vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte;
+ vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes;
+ vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos;
+- if (!p->btMode)
+- {
+- vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches;
+- vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip;
+- }
+- else if (p->numHashBytes == 2)
+- {
+- vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches;
+- vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip;
+- }
+- else if (p->numHashBytes == 3)
+- {
+- vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches;
+- vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip;
+- }
+- else
+- {
+- vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches;
+- vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip;
+- }
++ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches;
++ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip;
+ }
+--- a/lib/lzma/LzmaDec.c
++++ b/lib/lzma/LzmaDec.c
+@@ -682,7 +682,7 @@ static void LzmaDec_InitRc(CLzmaDec *p,
+ p->needFlush = 0;
+ }
+
+-void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
++static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
+ {
+ p->needFlush = 1;
+ p->remainLen = 0;
+@@ -698,7 +698,7 @@ void LzmaDec_InitDicAndState(CLzmaDec *p
+ p->needInitState = 1;
+ }
+
+-void LzmaDec_Init(CLzmaDec *p)
++static void LzmaDec_Init(CLzmaDec *p)
+ {
+ p->dicPos = 0;
+ LzmaDec_InitDicAndState(p, True, True);
+@@ -716,7 +716,7 @@ static void LzmaDec_InitStateReal(CLzmaD
+ p->needInitState = 0;
+ }
+
+-SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
++static SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
+ ELzmaFinishMode finishMode, ELzmaStatus *status)
+ {
+ SizeT inSize = *srcLen;
+@@ -837,65 +837,13 @@ SRes LzmaDec_DecodeToDic(CLzmaDec *p, Si
+ return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA;
+ }
+
+-SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
+-{
+- SizeT outSize = *destLen;
+- SizeT inSize = *srcLen;
+- *srcLen = *destLen = 0;
+- for (;;)
+- {
+- SizeT inSizeCur = inSize, outSizeCur, dicPos;
+- ELzmaFinishMode curFinishMode;
+- SRes res;
+- if (p->dicPos == p->dicBufSize)
+- p->dicPos = 0;
+- dicPos = p->dicPos;
+- if (outSize > p->dicBufSize - dicPos)
+- {
+- outSizeCur = p->dicBufSize;
+- curFinishMode = LZMA_FINISH_ANY;
+- }
+- else
+- {
+- outSizeCur = dicPos + outSize;
+- curFinishMode = finishMode;
+- }
+-
+- res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status);
+- src += inSizeCur;
+- inSize -= inSizeCur;
+- *srcLen += inSizeCur;
+- outSizeCur = p->dicPos - dicPos;
+- memcpy(dest, p->dic + dicPos, outSizeCur);
+- dest += outSizeCur;
+- outSize -= outSizeCur;
+- *destLen += outSizeCur;
+- if (res != 0)
+- return res;
+- if (outSizeCur == 0 || outSize == 0)
+- return SZ_OK;
+- }
+-}
+-
+-void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
++static void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
+ {
+ alloc->Free(alloc, p->probs);
+ p->probs = 0;
+ }
+
+-static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc)
+-{
+- alloc->Free(alloc, p->dic);
+- p->dic = 0;
+-}
+-
+-void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc)
+-{
+- LzmaDec_FreeProbs(p, alloc);
+- LzmaDec_FreeDict(p, alloc);
+-}
+-
+-SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
++static SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
+ {
+ UInt32 dicSize;
+ Byte d;
+@@ -935,7 +883,7 @@ static SRes LzmaDec_AllocateProbs2(CLzma
+ return SZ_OK;
+ }
+
+-SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
++static SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
+ {
+ CLzmaProps propNew;
+ RINOK(LzmaProps_Decode(&propNew, props, propsSize));
+@@ -943,28 +891,6 @@ SRes LzmaDec_AllocateProbs(CLzmaDec *p,
+ p->prop = propNew;
+ return SZ_OK;
+ }
+-
+-SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
+-{
+- CLzmaProps propNew;
+- SizeT dicBufSize;
+- RINOK(LzmaProps_Decode(&propNew, props, propsSize));
+- RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
+- dicBufSize = propNew.dicSize;
+- if (p->dic == 0 || dicBufSize != p->dicBufSize)
+- {
+- LzmaDec_FreeDict(p, alloc);
+- p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize);
+- if (p->dic == 0)
+- {
+- LzmaDec_FreeProbs(p, alloc);
+- return SZ_ERROR_MEM;
+- }
+- }
+- p->dicBufSize = dicBufSize;
+- p->prop = propNew;
+- return SZ_OK;
+-}
+
+ SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
+ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
+--- a/lib/lzma/LzmaEnc.c
++++ b/lib/lzma/LzmaEnc.c
+@@ -53,7 +53,7 @@ void LzmaEncProps_Init(CLzmaEncProps *p)
+ p->writeEndMark = 0;
+ }
+
+-void LzmaEncProps_Normalize(CLzmaEncProps *p)
++static void LzmaEncProps_Normalize(CLzmaEncProps *p)
+ {
+ int level = p->level;
+ if (level < 0) level = 5;
+@@ -76,7 +76,7 @@ void LzmaEncProps_Normalize(CLzmaEncProp
+ #endif
+ }
+
+-UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2)
++static UInt32 __maybe_unused LzmaEncProps_GetDictSize(const CLzmaEncProps *props2)
+ {
+ CLzmaEncProps props = *props2;
+ LzmaEncProps_Normalize(&props);
+@@ -93,7 +93,7 @@ UInt32 LzmaEncProps_GetDictSize(const CL
+
+ #define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); }
+
+-UInt32 GetPosSlot1(UInt32 pos)
++static UInt32 GetPosSlot1(UInt32 pos)
+ {
+ UInt32 res;
+ BSR2_RET(pos, res);
+@@ -107,7 +107,7 @@ UInt32 GetPosSlot1(UInt32 pos)
+ #define kNumLogBits (9 + (int)sizeof(size_t) / 2)
+ #define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7)
+
+-void LzmaEnc_FastPosInit(Byte *g_FastPos)
++static void LzmaEnc_FastPosInit(Byte *g_FastPos)
+ {
+ int c = 2, slotFast;
+ g_FastPos[0] = 0;
+@@ -339,58 +339,6 @@ typedef struct
+ CSaveState saveState;
+ } CLzmaEnc;
+
+-void LzmaEnc_SaveState(CLzmaEncHandle pp)
+-{
+- CLzmaEnc *p = (CLzmaEnc *)pp;
+- CSaveState *dest = &p->saveState;
+- int i;
+- dest->lenEnc = p->lenEnc;
+- dest->repLenEnc = p->repLenEnc;
+- dest->state = p->state;
+-
+- for (i = 0; i < kNumStates; i++)
+- {
+- memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
+- memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
+- }
+- for (i = 0; i < kNumLenToPosStates; i++)
+- memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
+- memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
+- memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
+- memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
+- memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
+- memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
+- memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
+- memcpy(dest->reps, p->reps, sizeof(p->reps));
+- memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb));
+-}
+-
+-void LzmaEnc_RestoreState(CLzmaEncHandle pp)
+-{
+- CLzmaEnc *dest = (CLzmaEnc *)pp;
+- const CSaveState *p = &dest->saveState;
+- int i;
+- dest->lenEnc = p->lenEnc;
+- dest->repLenEnc = p->repLenEnc;
+- dest->state = p->state;
+-
+- for (i = 0; i < kNumStates; i++)
+- {
+- memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
+- memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
+- }
+- for (i = 0; i < kNumLenToPosStates; i++)
+- memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
+- memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
+- memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
+- memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
+- memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
+- memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
+- memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
+- memcpy(dest->reps, p->reps, sizeof(p->reps));
+- memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb));
+-}
+-
+ SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2)
+ {
+ CLzmaEnc *p = (CLzmaEnc *)pp;
+@@ -600,7 +548,7 @@ static void LitEnc_EncodeMatched(CRangeE
+ while (symbol < 0x10000);
+ }
+
+-void LzmaEnc_InitPriceTables(UInt32 *ProbPrices)
++static void LzmaEnc_InitPriceTables(UInt32 *ProbPrices)
+ {
+ UInt32 i;
+ for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits))
+@@ -1676,7 +1624,7 @@ static void FillDistancesPrices(CLzmaEnc
+ p->matchPriceCount = 0;
+ }
+
+-void LzmaEnc_Construct(CLzmaEnc *p)
++static void LzmaEnc_Construct(CLzmaEnc *p)
+ {
+ RangeEnc_Construct(&p->rc);
+ MatchFinder_Construct(&p->matchFinderBase);
+@@ -1709,7 +1657,7 @@ CLzmaEncHandle LzmaEnc_Create(ISzAlloc *
+ return p;
+ }
+
+-void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc)
++static void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc)
+ {
+ alloc->Free(alloc, p->litProbs);
+ alloc->Free(alloc, p->saveState.litProbs);
+@@ -1717,7 +1665,7 @@ void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAl
+ p->saveState.litProbs = 0;
+ }
+
+-void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig)
++static void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig)
+ {
+ #ifndef _7ZIP_ST
+ MatchFinderMt_Destruct(&p->matchFinderMt, allocBig);
+@@ -1947,7 +1895,7 @@ static SRes LzmaEnc_Alloc(CLzmaEnc *p, U
+ return SZ_OK;
+ }
+
+-void LzmaEnc_Init(CLzmaEnc *p)
++static void LzmaEnc_Init(CLzmaEnc *p)
+ {
+ UInt32 i;
+ p->state = 0;
+@@ -2005,7 +1953,7 @@ void LzmaEnc_Init(CLzmaEnc *p)
+ p->lpMask = (1 << p->lp) - 1;
+ }
+
+-void LzmaEnc_InitPrices(CLzmaEnc *p)
++static void LzmaEnc_InitPrices(CLzmaEnc *p)
+ {
+ if (!p->fastMode)
+ {
+@@ -2037,26 +1985,6 @@ static SRes LzmaEnc_AllocAndInit(CLzmaEn
+ return SZ_OK;
+ }
+
+-static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream,
+- ISzAlloc *alloc, ISzAlloc *allocBig)
+-{
+- CLzmaEnc *p = (CLzmaEnc *)pp;
+- p->matchFinderBase.stream = inStream;
+- p->needInit = 1;
+- p->rc.outStream = outStream;
+- return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig);
+-}
+-
+-SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp,
+- ISeqInStream *inStream, UInt32 keepWindowSize,
+- ISzAlloc *alloc, ISzAlloc *allocBig)
+-{
+- CLzmaEnc *p = (CLzmaEnc *)pp;
+- p->matchFinderBase.stream = inStream;
+- p->needInit = 1;
+- return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
+-}
+-
+ static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen)
+ {
+ p->matchFinderBase.directInput = 1;
+@@ -2064,7 +1992,7 @@ static void LzmaEnc_SetInputBuf(CLzmaEnc
+ p->matchFinderBase.directInputRem = srcLen;
+ }
+
+-SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
++static SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
+ UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
+ {
+ CLzmaEnc *p = (CLzmaEnc *)pp;
+@@ -2074,7 +2002,7 @@ SRes LzmaEnc_MemPrepare(CLzmaEncHandle p
+ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
+ }
+
+-void LzmaEnc_Finish(CLzmaEncHandle pp)
++static void LzmaEnc_Finish(CLzmaEncHandle pp)
+ {
+ #ifndef _7ZIP_ST
+ CLzmaEnc *p = (CLzmaEnc *)pp;
+@@ -2107,53 +2035,6 @@ static size_t MyWrite(void *pp, const vo
+ return size;
+ }
+
+-
+-UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp)
+-{
+- const CLzmaEnc *p = (CLzmaEnc *)pp;
+- return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
+-}
+-
+-const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp)
+-{
+- const CLzmaEnc *p = (CLzmaEnc *)pp;
+- return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset;
+-}
+-
+-SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit,
+- Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize)
+-{
+- CLzmaEnc *p = (CLzmaEnc *)pp;
+- UInt64 nowPos64;
+- SRes res;
+- CSeqOutStreamBuf outStream;
+-
+- outStream.funcTable.Write = MyWrite;
+- outStream.data = dest;
+- outStream.rem = *destLen;
+- outStream.overflow = False;
+-
+- p->writeEndMark = False;
+- p->finished = False;
+- p->result = SZ_OK;
+-
+- if (reInit)
+- LzmaEnc_Init(p);
+- LzmaEnc_InitPrices(p);
+- nowPos64 = p->nowPos64;
+- RangeEnc_Init(&p->rc);
+- p->rc.outStream = &outStream.funcTable;
+-
+- res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize);
+-
+- *unpackSize = (UInt32)(p->nowPos64 - nowPos64);
+- *destLen -= outStream.rem;
+- if (outStream.overflow)
+- return SZ_ERROR_OUTPUT_EOF;
+-
+- return res;
+-}
+-
+ static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress)
+ {
+ SRes res = SZ_OK;
+@@ -2184,13 +2065,6 @@ static SRes LzmaEnc_Encode2(CLzmaEnc *p,
+ return res;
+ }
+
+-SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress,
+- ISzAlloc *alloc, ISzAlloc *allocBig)
+-{
+- RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig));
+- return LzmaEnc_Encode2((CLzmaEnc *)pp, progress);
+-}
+-
+ SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size)
+ {
+ CLzmaEnc *p = (CLzmaEnc *)pp;
+@@ -2247,25 +2121,3 @@ SRes LzmaEnc_MemEncode(CLzmaEncHandle pp
+ return SZ_ERROR_OUTPUT_EOF;
+ return res;
+ }
+-
+-SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
+- const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
+- ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig)
+-{
+- CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc);
+- SRes res;
+- if (p == 0)
+- return SZ_ERROR_MEM;
+-
+- res = LzmaEnc_SetProps(p, props);
+- if (res == SZ_OK)
+- {
+- res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize);
+- if (res == SZ_OK)
+- res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen,
+- writeEndMark, progress, alloc, allocBig);
+- }
+-
+- LzmaEnc_Destroy(p, alloc, allocBig);
+- return res;
+-}
--- /dev/null
+--- a/include/linux/if_bridge.h
++++ b/include/linux/if_bridge.h
+@@ -57,6 +57,7 @@ struct br_ip_list {
+ #define BR_MRP_LOST_CONT BIT(18)
+ #define BR_MRP_LOST_IN_CONT BIT(19)
+ #define BR_BPDU_FILTER BIT(20)
++#define BR_OFFLOAD BIT(21)
+
+ #define BR_DEFAULT_AGEING_TIME (300 * HZ)
+
+--- a/net/bridge/Makefile
++++ b/net/bridge/Makefile
+@@ -5,7 +5,7 @@
+
+ obj-$(CONFIG_BRIDGE) += bridge.o
+
+-bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
++bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o br_offload.o \
+ br_ioctl.o br_stp.o br_stp_bpdu.o \
+ br_stp_if.o br_stp_timer.o br_netlink.o \
+ br_netlink_tunnel.o br_arp_nd_proxy.o
+--- a/net/bridge/br.c
++++ b/net/bridge/br.c
+@@ -18,6 +18,7 @@
+ #include <net/switchdev.h>
+
+ #include "br_private.h"
++#include "br_private_offload.h"
+
+ /*
+ * Handle changes in state of network devices enslaved to a bridge.
+@@ -332,6 +333,10 @@ static int __init br_init(void)
+ if (err)
+ goto err_out;
+
++ err = br_offload_init();
++ if (err)
++ goto err_out0;
++
+ err = register_pernet_subsys(&br_net_ops);
+ if (err)
+ goto err_out1;
+@@ -375,6 +380,8 @@ err_out3:
+ err_out2:
+ unregister_pernet_subsys(&br_net_ops);
+ err_out1:
++ br_offload_fini();
++err_out0:
+ br_fdb_fini();
+ err_out:
+ stp_proto_unregister(&br_stp_proto);
+@@ -396,6 +403,7 @@ static void __exit br_deinit(void)
+ #if IS_ENABLED(CONFIG_ATM_LANE)
+ br_fdb_test_addr_hook = NULL;
+ #endif
++ br_offload_fini();
+ br_fdb_fini();
+ }
+
+--- a/net/bridge/br_device.c
++++ b/net/bridge/br_device.c
+@@ -529,6 +529,8 @@ void br_dev_setup(struct net_device *dev
+ br->bridge_hello_time = br->hello_time = 2 * HZ;
+ br->bridge_forward_delay = br->forward_delay = 15 * HZ;
+ br->bridge_ageing_time = br->ageing_time = BR_DEFAULT_AGEING_TIME;
++ br->offload_cache_size = 128;
++ br->offload_cache_reserved = 8;
+ dev->max_mtu = ETH_MAX_MTU;
+
+ br_netfilter_rtable_init(br);
+--- a/net/bridge/br_fdb.c
++++ b/net/bridge/br_fdb.c
+@@ -23,6 +23,7 @@
+ #include <net/switchdev.h>
+ #include <trace/events/bridge.h>
+ #include "br_private.h"
++#include "br_private_offload.h"
+
+ static const struct rhashtable_params br_fdb_rht_params = {
+ .head_offset = offsetof(struct net_bridge_fdb_entry, rhnode),
+@@ -513,6 +514,8 @@ static struct net_bridge_fdb_entry *fdb_
+ fdb->key.vlan_id = vid;
+ fdb->flags = flags;
+ fdb->updated = fdb->used = jiffies;
++ INIT_HLIST_HEAD(&fdb->offload_in);
++ INIT_HLIST_HEAD(&fdb->offload_out);
+ if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl,
+ &fdb->rhnode,
+ br_fdb_rht_params)) {
+@@ -734,6 +737,8 @@ static void fdb_notify(struct net_bridge
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
++ br_offload_fdb_update(fdb);
++
+ if (swdev_notify)
+ br_switchdev_fdb_notify(br, fdb, type);
+
+--- a/net/bridge/br_forward.c
++++ b/net/bridge/br_forward.c
+@@ -16,6 +16,7 @@
+ #include <linux/if_vlan.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
++#include "br_private_offload.h"
+
+ /* Don't forward packets to originating port or forwarding disabled */
+ static inline int should_deliver(const struct net_bridge_port *p,
+@@ -32,6 +33,8 @@ static inline int should_deliver(const s
+
+ int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
+ {
++ br_offload_output(skb);
++
+ skb_push(skb, ETH_HLEN);
+ if (!is_skb_forwardable(skb->dev, skb))
+ goto drop;
+--- a/net/bridge/br_if.c
++++ b/net/bridge/br_if.c
+@@ -25,6 +25,7 @@
+ #include <net/net_namespace.h>
+
+ #include "br_private.h"
++#include "br_private_offload.h"
+
+ /*
+ * Determine initial path cost based on speed.
+@@ -427,7 +428,7 @@ static struct net_bridge_port *new_nbp(s
+ p->path_cost = port_cost(dev);
+ p->priority = 0x8000 >> BR_PORT_BITS;
+ p->port_no = index;
+- p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
++ p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_OFFLOAD;
+ br_init_port(p);
+ br_set_state(p, BR_STATE_DISABLED);
+ br_stp_port_timer_init(p);
+@@ -777,6 +778,9 @@ void br_port_flags_change(struct net_bri
+
+ if (mask & BR_NEIGH_SUPPRESS)
+ br_recalculate_neigh_suppress_enabled(br);
++
++ if (mask & BR_OFFLOAD)
++ br_offload_port_state(p);
+ }
+
+ bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
+--- a/net/bridge/br_input.c
++++ b/net/bridge/br_input.c
+@@ -22,6 +22,7 @@
+ #include <linux/rculist.h>
+ #include "br_private.h"
+ #include "br_private_tunnel.h"
++#include "br_private_offload.h"
+
+ static int
+ br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb)
+@@ -162,6 +163,7 @@ int br_handle_frame_finish(struct net *n
+ dst->used = now;
+ br_forward(dst->dst, skb, local_rcv, false);
+ } else {
++ br_offload_skb_disable(skb);
+ if (!mcast_hit)
+ br_flood(br, skb, pkt_type, local_rcv, false);
+ else
+@@ -280,6 +282,9 @@ static rx_handler_result_t br_handle_fra
+ memset(skb->cb, 0, sizeof(struct br_input_skb_cb));
+
+ p = br_port_get_rcu(skb->dev);
++ if (br_offload_input(p, skb))
++ return RX_HANDLER_CONSUMED;
++
+ if (p->flags & BR_VLAN_TUNNEL) {
+ if (br_handle_ingress_vlan_tunnel(skb, p,
+ nbp_vlan_group_rcu(p)))
+--- /dev/null
++++ b/net/bridge/br_offload.c
+@@ -0,0 +1,436 @@
++// SPDX-License-Identifier: GPL-2.0-only
++#include <linux/kernel.h>
++#include <linux/workqueue.h>
++#include "br_private.h"
++#include "br_private_offload.h"
++
++static DEFINE_SPINLOCK(offload_lock);
++
++struct bridge_flow_key {
++ u8 dest[ETH_ALEN];
++ u8 src[ETH_ALEN];
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ u16 vlan_tag;
++ bool vlan_present;
++#endif
++};
++
++struct bridge_flow {
++ struct net_bridge_port *port;
++ struct rhash_head node;
++ struct bridge_flow_key key;
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ bool vlan_out_present;
++ u16 vlan_out;
++#endif
++
++ unsigned long used;
++ struct net_bridge_fdb_entry *fdb_in, *fdb_out;
++ struct hlist_node fdb_list_in, fdb_list_out;
++
++ struct rcu_head rcu;
++};
++
++static const struct rhashtable_params flow_params = {
++ .automatic_shrinking = true,
++ .head_offset = offsetof(struct bridge_flow, node),
++ .key_len = sizeof(struct bridge_flow_key),
++ .key_offset = offsetof(struct bridge_flow, key),
++};
++
++static struct kmem_cache *offload_cache __read_mostly;
++
++static void
++flow_rcu_free(struct rcu_head *head)
++{
++ struct bridge_flow *flow;
++
++ flow = container_of(head, struct bridge_flow, rcu);
++ kmem_cache_free(offload_cache, flow);
++}
++
++static void
++__br_offload_flow_free(struct bridge_flow *flow)
++{
++ flow->used = 0;
++ hlist_del(&flow->fdb_list_in);
++ hlist_del(&flow->fdb_list_out);
++
++ call_rcu(&flow->rcu, flow_rcu_free);
++}
++
++static void
++br_offload_flow_free(struct bridge_flow *flow)
++{
++ if (rhashtable_remove_fast(&flow->port->offload.rht, &flow->node,
++ flow_params) != 0)
++ return;
++
++ __br_offload_flow_free(flow);
++}
++
++static bool
++br_offload_flow_fdb_refresh_time(struct bridge_flow *flow,
++ struct net_bridge_fdb_entry *fdb)
++{
++ if (!time_after(flow->used, fdb->updated))
++ return false;
++
++ fdb->updated = flow->used;
++
++ return true;
++}
++
++
++static void
++br_offload_flow_refresh_time(struct bridge_flow *flow)
++{
++ br_offload_flow_fdb_refresh_time(flow, flow->fdb_in);
++ br_offload_flow_fdb_refresh_time(flow, flow->fdb_out);
++}
++
++static void
++br_offload_destroy_cb(void *ptr, void *arg)
++{
++ struct bridge_flow *flow = ptr;
++
++ __br_offload_flow_free(flow);
++}
++
++static bool
++br_offload_need_gc(struct net_bridge_port *p)
++{
++ return (atomic_read(&p->offload.rht.nelems) +
++ p->br->offload_cache_reserved) >= p->br->offload_cache_size;
++}
++
++static void
++br_offload_gc_work(struct work_struct *work)
++{
++ struct rhashtable_iter hti;
++ struct net_bridge_port *p;
++ struct bridge_flow *gc_flow = NULL;
++ struct bridge_flow *flow;
++ unsigned long gc_used;
++
++ p = container_of(work, struct net_bridge_port, offload.gc_work);
++
++ if (!br_offload_need_gc(p))
++ return;
++
++ rhashtable_walk_enter(&p->offload.rht, &hti);
++ rhashtable_walk_start(&hti);
++ while ((flow = rhashtable_walk_next(&hti)) != NULL) {
++ unsigned long used;
++
++ if (IS_ERR(flow))
++ continue;
++
++ used = READ_ONCE(flow->used);
++ if (!used)
++ continue;
++
++ if (gc_flow && !time_before(used, gc_used))
++ continue;
++
++ gc_flow = flow;
++ gc_used = used;
++ }
++ rhashtable_walk_stop(&hti);
++ rhashtable_walk_exit(&hti);
++
++ if (!gc_flow)
++ return;
++
++ spin_lock_bh(&offload_lock);
++ if (br_offload_need_gc(p) && gc_flow &&
++ gc_flow->used == gc_used)
++ br_offload_flow_free(gc_flow);
++ if (p->offload.enabled && br_offload_need_gc(p))
++ queue_work(system_long_wq, work);
++ spin_unlock_bh(&offload_lock);
++
++}
++
++void br_offload_port_state(struct net_bridge_port *p)
++{
++ struct net_bridge_port_offload *o = &p->offload;
++ bool enabled = true;
++ bool flush = false;
++
++ if (p->state != BR_STATE_FORWARDING ||
++ !(p->flags & BR_OFFLOAD))
++ enabled = false;
++
++ spin_lock_bh(&offload_lock);
++ if (o->enabled == enabled)
++ goto out;
++
++ if (enabled) {
++ if (!o->gc_work.func)
++ INIT_WORK(&o->gc_work, br_offload_gc_work);
++ rhashtable_init(&o->rht, &flow_params);
++ } else {
++ flush = true;
++ rhashtable_free_and_destroy(&o->rht, br_offload_destroy_cb, o);
++ }
++
++ o->enabled = enabled;
++
++out:
++ spin_unlock_bh(&offload_lock);
++
++ if (flush)
++ flush_work(&o->gc_work);
++}
++
++void br_offload_fdb_update(const struct net_bridge_fdb_entry *fdb)
++{
++ struct bridge_flow *f;
++ struct hlist_node *tmp;
++
++ spin_lock_bh(&offload_lock);
++
++ hlist_for_each_entry_safe(f, tmp, &fdb->offload_in, fdb_list_in)
++ br_offload_flow_free(f);
++
++ hlist_for_each_entry_safe(f, tmp, &fdb->offload_out, fdb_list_out)
++ br_offload_flow_free(f);
++
++ spin_unlock_bh(&offload_lock);
++}
++
++static void
++br_offload_prepare_key(struct net_bridge_port *p, struct bridge_flow_key *key,
++ struct sk_buff *skb)
++{
++ memset(key, 0, sizeof(*key));
++ memcpy(key, eth_hdr(skb), 2 * ETH_ALEN);
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ if (!br_opt_get(p->br, BROPT_VLAN_ENABLED))
++ return;
++
++ if (!skb_vlan_tag_present(skb) || skb->vlan_proto != p->br->vlan_proto)
++ return;
++
++ key->vlan_present = true;
++ key->vlan_tag = skb_vlan_tag_get_id(skb);
++#endif
++}
++
++void br_offload_output(struct sk_buff *skb)
++{
++ struct net_bridge_port_offload *o;
++ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb;
++ struct net_bridge_port *p, *inp;
++ struct net_device *dev;
++ struct net_bridge_fdb_entry *fdb_in, *fdb_out;
++ struct net_bridge_vlan_group *vg;
++ struct bridge_flow_key key;
++ struct bridge_flow *flow;
++ u16 vlan;
++
++ if (!cb->offload)
++ return;
++
++ rcu_read_lock();
++
++ p = br_port_get_rcu(skb->dev);
++ if (!p)
++ goto out;
++
++ o = &p->offload;
++ if (!o->enabled)
++ goto out;
++
++ if (atomic_read(&p->offload.rht.nelems) >= p->br->offload_cache_size)
++ goto out;
++
++ dev = dev_get_by_index_rcu(dev_net(p->br->dev), cb->input_ifindex);
++ if (!dev)
++ goto out;
++
++ inp = br_port_get_rcu(dev);
++ if (!inp)
++ goto out;
++
++ vg = nbp_vlan_group_rcu(inp);
++ vlan = cb->input_vlan_present ? cb->input_vlan_tag : br_get_pvid(vg);
++ fdb_in = br_fdb_find_rcu(p->br, eth_hdr(skb)->h_source, vlan);
++ if (!fdb_in)
++ goto out;
++
++ vg = nbp_vlan_group_rcu(p);
++ vlan = skb_vlan_tag_present(skb) ? skb_vlan_tag_get_id(skb) : br_get_pvid(vg);
++ fdb_out = br_fdb_find_rcu(p->br, eth_hdr(skb)->h_dest, vlan);
++ if (!fdb_out)
++ goto out;
++
++ br_offload_prepare_key(p, &key, skb);
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ key.vlan_present = cb->input_vlan_present;
++ key.vlan_tag = cb->input_vlan_tag;
++#endif
++
++ flow = kmem_cache_alloc(offload_cache, GFP_ATOMIC);
++ flow->port = fdb_in->dst;
++ memcpy(&flow->key, &key, sizeof(key));
++
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ flow->vlan_out_present = skb_vlan_tag_present(skb);
++ flow->vlan_out = skb_vlan_tag_get(skb);
++#endif
++
++ flow->fdb_in = fdb_in;
++ flow->fdb_out = fdb_out;
++ flow->used = jiffies;
++
++ spin_lock_bh(&offload_lock);
++ if (!o->enabled ||
++ atomic_read(&p->offload.rht.nelems) >= p->br->offload_cache_size ||
++ rhashtable_insert_fast(&flow->port->offload.rht, &flow->node, flow_params)) {
++ kmem_cache_free(offload_cache, flow);
++ goto out_unlock;
++ }
++
++ hlist_add_head(&flow->fdb_list_in, &fdb_in->offload_in);
++ hlist_add_head(&flow->fdb_list_out, &fdb_out->offload_out);
++
++ if (br_offload_need_gc(p))
++ queue_work(system_long_wq, &p->offload.gc_work);
++
++out_unlock:
++ spin_unlock_bh(&offload_lock);
++
++out:
++ rcu_read_unlock();
++}
++
++bool br_offload_input(struct net_bridge_port *p, struct sk_buff *skb)
++{
++ struct net_bridge_port_offload *o = &p->offload;
++ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb;
++ struct bridge_flow_key key;
++ struct net_bridge_port *dst;
++ struct bridge_flow *flow;
++ unsigned long now = jiffies;
++ bool ret = false;
++
++ if (skb->len < sizeof(key))
++ return false;
++
++ if (!o->enabled)
++ return false;
++
++ if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
++ return false;
++
++ br_offload_prepare_key(p, &key, skb);
++
++ rcu_read_lock();
++ flow = rhashtable_lookup(&o->rht, &key, flow_params);
++ if (!flow) {
++ cb->offload = 1;
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ cb->input_vlan_present = key.vlan_present != 0;
++ cb->input_vlan_tag = key.vlan_tag;
++ cb->input_ifindex = p->dev->ifindex;
++#endif
++ goto out;
++ }
++
++ if (flow->fdb_in->dst != p)
++ goto out;
++
++ dst = flow->fdb_out->dst;
++ if (!dst)
++ goto out;
++
++ ret = true;
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ if (!flow->vlan_out_present && key.vlan_present) {
++ __vlan_hwaccel_clear_tag(skb);
++ } else if (flow->vlan_out_present) {
++ if (skb_vlan_tag_present(skb) &&
++ skb->vlan_proto != p->br->vlan_proto) {
++ /* Protocol-mismatch, empty out vlan_tci for new tag */
++ skb_push(skb, ETH_HLEN);
++ skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto,
++ skb_vlan_tag_get(skb));
++ if (unlikely(!skb))
++ goto out;
++
++ skb_pull(skb, ETH_HLEN);
++ skb_reset_mac_len(skb);
++ }
++
++ __vlan_hwaccel_put_tag(skb, p->br->vlan_proto,
++ flow->vlan_out);
++ }
++#endif
++
++ skb->dev = dst->dev;
++ skb_push(skb, ETH_HLEN);
++
++ if (skb_warn_if_lro(skb) || !is_skb_forwardable(skb->dev, skb)) {
++ kfree_skb(skb);
++ goto out;
++ }
++
++ if (now - flow->used >= HZ) {
++ flow->used = now;
++ br_offload_flow_refresh_time(flow);
++ }
++
++ skb_forward_csum(skb);
++ dev_queue_xmit(skb);
++
++out:
++ rcu_read_unlock();
++ return ret;
++}
++
++static void
++br_offload_check_gc(struct net_bridge *br)
++{
++ struct net_bridge_port *p;
++
++ spin_lock_bh(&br->lock);
++ list_for_each_entry(p, &br->port_list, list)
++ if (br_offload_need_gc(p))
++ queue_work(system_long_wq, &p->offload.gc_work);
++ spin_unlock_bh(&br->lock);
++}
++
++
++int br_offload_set_cache_size(struct net_bridge *br, unsigned long val)
++{
++ br->offload_cache_size = val;
++ br_offload_check_gc(br);
++
++ return 0;
++}
++
++int br_offload_set_cache_reserved(struct net_bridge *br, unsigned long val)
++{
++ br->offload_cache_reserved = val;
++ br_offload_check_gc(br);
++
++ return 0;
++}
++
++int __init br_offload_init(void)
++{
++ offload_cache = kmem_cache_create("bridge_offload_cache",
++ sizeof(struct bridge_flow),
++ 0, SLAB_HWCACHE_ALIGN, NULL);
++ if (!offload_cache)
++ return -ENOMEM;
++
++ return 0;
++}
++
++void br_offload_fini(void)
++{
++ kmem_cache_destroy(offload_cache);
++}
+--- a/net/bridge/br_private.h
++++ b/net/bridge/br_private.h
+@@ -207,7 +207,13 @@ struct net_bridge_fdb_entry {
+ unsigned long updated ____cacheline_aligned_in_smp;
+ unsigned long used;
+
+- struct rcu_head rcu;
++ union {
++ struct {
++ struct hlist_head offload_in;
++ struct hlist_head offload_out;
++ };
++ struct rcu_head rcu;
++ };
+ };
+
+ #define MDB_PG_FLAGS_PERMANENT BIT(0)
+@@ -280,6 +286,12 @@ struct net_bridge_mdb_entry {
+ struct rcu_head rcu;
+ };
+
++struct net_bridge_port_offload {
++ struct rhashtable rht;
++ struct work_struct gc_work;
++ bool enabled;
++};
++
+ struct net_bridge_port {
+ struct net_bridge *br;
+ struct net_device *dev;
+@@ -337,6 +349,7 @@ struct net_bridge_port {
+ u16 backup_redirected_cnt;
+
+ struct bridge_stp_xstats stp_xstats;
++ struct net_bridge_port_offload offload;
+ };
+
+ #define kobj_to_brport(obj) container_of(obj, struct net_bridge_port, kobj)
+@@ -475,6 +488,9 @@ struct net_bridge {
+ struct kobject *ifobj;
+ u32 auto_cnt;
+
++ u32 offload_cache_size;
++ u32 offload_cache_reserved;
++
+ #ifdef CONFIG_NET_SWITCHDEV
+ int offload_fwd_mark;
+ #endif
+@@ -501,6 +517,10 @@ struct br_input_skb_cb {
+ #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
+ u8 br_netfilter_broute:1;
+ #endif
++ u8 offload:1;
++ u8 input_vlan_present:1;
++ u16 input_vlan_tag;
++ int input_ifindex;
+
+ #ifdef CONFIG_NET_SWITCHDEV
+ int offload_fwd_mark;
+--- /dev/null
++++ b/net/bridge/br_private_offload.h
+@@ -0,0 +1,21 @@
++#ifndef __BR_OFFLOAD_H
++#define __BR_OFFLOAD_H
++
++bool br_offload_input(struct net_bridge_port *p, struct sk_buff *skb);
++void br_offload_output(struct sk_buff *skb);
++void br_offload_port_state(struct net_bridge_port *p);
++void br_offload_fdb_update(const struct net_bridge_fdb_entry *fdb);
++int br_offload_init(void);
++void br_offload_fini(void);
++int br_offload_set_cache_size(struct net_bridge *br, unsigned long val);
++int br_offload_set_cache_reserved(struct net_bridge *br, unsigned long val);
++
++static inline void br_offload_skb_disable(struct sk_buff *skb)
++{
++ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb;
++
++ if (cb->offload)
++ cb->offload = 0;
++}
++
++#endif
+--- a/net/bridge/br_stp.c
++++ b/net/bridge/br_stp.c
+@@ -12,6 +12,7 @@
+
+ #include "br_private.h"
+ #include "br_private_stp.h"
++#include "br_private_offload.h"
+
+ /* since time values in bpdu are in jiffies and then scaled (1/256)
+ * before sending, make sure that is at least one STP tick.
+@@ -52,6 +53,8 @@ void br_set_state(struct net_bridge_port
+ (unsigned int) p->port_no, p->dev->name,
+ br_port_state_names[p->state]);
+
++ br_offload_port_state(p);
++
+ if (p->br->stp_enabled == BR_KERNEL_STP) {
+ switch (p->state) {
+ case BR_STATE_BLOCKING:
+--- a/net/bridge/br_sysfs_br.c
++++ b/net/bridge/br_sysfs_br.c
+@@ -18,6 +18,7 @@
+ #include <linux/sched/signal.h>
+
+ #include "br_private.h"
++#include "br_private_offload.h"
+
+ #define to_bridge(cd) ((struct net_bridge *)netdev_priv(to_net_dev(cd)))
+
+@@ -842,6 +843,38 @@ static ssize_t vlan_stats_per_port_store
+ static DEVICE_ATTR_RW(vlan_stats_per_port);
+ #endif
+
++static ssize_t offload_cache_size_show(struct device *d,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct net_bridge *br = to_bridge(d);
++ return sprintf(buf, "%u\n", br->offload_cache_size);
++}
++
++static ssize_t offload_cache_size_store(struct device *d,
++ struct device_attribute *attr,
++ const char *buf, size_t len)
++{
++ return store_bridge_parm(d, buf, len, br_offload_set_cache_size);
++}
++static DEVICE_ATTR_RW(offload_cache_size);
++
++static ssize_t offload_cache_reserved_show(struct device *d,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct net_bridge *br = to_bridge(d);
++ return sprintf(buf, "%u\n", br->offload_cache_reserved);
++}
++
++static ssize_t offload_cache_reserved_store(struct device *d,
++ struct device_attribute *attr,
++ const char *buf, size_t len)
++{
++ return store_bridge_parm(d, buf, len, br_offload_set_cache_reserved);
++}
++static DEVICE_ATTR_RW(offload_cache_reserved);
++
+ static struct attribute *bridge_attrs[] = {
+ &dev_attr_forward_delay.attr,
+ &dev_attr_hello_time.attr,
+@@ -896,6 +929,8 @@ static struct attribute *bridge_attrs[]
+ &dev_attr_vlan_stats_enabled.attr,
+ &dev_attr_vlan_stats_per_port.attr,
+ #endif
++ &dev_attr_offload_cache_size.attr,
++ &dev_attr_offload_cache_reserved.attr,
+ NULL
+ };
+
+--- a/net/bridge/br_sysfs_if.c
++++ b/net/bridge/br_sysfs_if.c
+@@ -234,6 +234,7 @@ BRPORT_ATTR_FLAG(broadcast_flood, BR_BCA
+ BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
+ BRPORT_ATTR_FLAG(isolated, BR_ISOLATED);
+ BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER);
++BRPORT_ATTR_FLAG(offload, BR_OFFLOAD);
+
+ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+ static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
+@@ -288,6 +289,7 @@ static const struct brport_attribute *br
+ &brport_attr_isolated,
+ &brport_attr_bpdu_filter,
+ &brport_attr_backup_port,
++ &brport_attr_offload,
+ NULL
+ };
+
+--- a/net/bridge/br_vlan_tunnel.c
++++ b/net/bridge/br_vlan_tunnel.c
+@@ -15,6 +15,7 @@
+
+ #include "br_private.h"
+ #include "br_private_tunnel.h"
++#include "br_private_offload.h"
+
+ static inline int br_vlan_tunid_cmp(struct rhashtable_compare_arg *arg,
+ const void *ptr)
+@@ -180,6 +181,7 @@ int br_handle_ingress_vlan_tunnel(struct
+ skb_dst_drop(skb);
+
+ __vlan_hwaccel_put_tag(skb, p->br->vlan_proto, vlan->vid);
++ br_offload_skb_disable(skb);
+
+ return 0;
+ }
+@@ -203,6 +205,7 @@ int br_handle_egress_vlan_tunnel(struct
+ if (err)
+ return err;
+
++ br_offload_skb_disable(skb);
+ tunnel_dst = rcu_dereference(vlan->tinfo.tunnel_dst);
+ if (tunnel_dst && dst_hold_safe(&tunnel_dst->dst))
+ skb_dst_set(skb, &tunnel_dst->dst);
--- /dev/null
+From eda40b8c8c82e0f2789d6bc8bf63846dce2e8f32 Mon Sep 17 00:00:00 2001
+From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+Date: Sat, 23 Mar 2019 09:29:49 +0000
+Subject: [PATCH] netfilter: connmark: introduce set-dscpmark
+
+set-dscpmark is a method of storing the DSCP of an ip packet into
+conntrack mark. In combination with a suitable tc filter action
+(act_ctinfo) DSCP values are able to be stored in the mark on egress and
+restored on ingress across links that otherwise alter or bleach DSCP.
+
+This is useful for qdiscs such as CAKE which are able to shape according
+to policies based on DSCP.
+
+Ingress classification is traditionally a challenging task since
+iptables rules haven't yet run and tc filter/eBPF programs are pre-NAT
+lookups, hence are unable to see internal IPv4 addresses as used on the
+typical home masquerading gateway.
+
+x_tables CONNMARK set-dscpmark target solves the problem of storing the
+DSCP to the conntrack mark in a way suitable for the new act_ctinfo tc
+action to restore.
+
+The set-dscpmark option accepts 2 parameters, a 32bit 'dscpmask' and a
+32bit 'statemask'. The dscp mask must be 6 contiguous bits and
+represents the area where the DSCP will be stored in the connmark. The
+state mask is a minimum 1 bit length mask that must not overlap with the
+dscpmask. It represents a flag which is set when the DSCP has been
+stored in the conntrack mark. This is useful to implement a 'one shot'
+iptables based classification where the 'complicated' iptables rules are
+only run once to classify the connection on initial (egress) packet and
+subsequent packets are all marked/restored with the same DSCP. A state
+mask of zero disables the setting of a status bit/s.
+
+example syntax with a suitably modified iptables user space application:
+
+iptables -A QOS_MARK_eth0 -t mangle -j CONNMARK --set-dscpmark 0xfc000000/0x01000000
+
+Would store the DSCP in the top 6 bits of the 32bit mark field, and use
+the LSB of the top byte as the 'DSCP has been stored' marker.
+
+|----0xFC----conntrack mark----000000---|
+| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0|
+| DSCP | unused | flag |unused |
+|-----------------------0x01---000000---|
+ ^ ^
+ | |
+ ---| Conditional flag
+ | set this when dscp
+|-ip diffserv-| stored in mark
+| 6 bits |
+|-------------|
+
+an identically configured tc action to restore looks like:
+
+tc filter show dev eth0 ingress
+filter parent ffff: protocol all pref 10 u32 chain 0
+filter parent ffff: protocol all pref 10 u32 chain 0 fh 800: ht divisor 1
+filter parent ffff: protocol all pref 10 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1: not_in_hw
+ match 00000000/00000000 at 0
+ action order 1: ctinfo zone 0 pipe
+ index 2 ref 1 bind 1 dscp 0xfc000000/0x1000000
+
+ action order 2: mirred (Egress Redirect to device ifb4eth0) stolen
+ index 1 ref 1 bind 1
+
+|----0xFC----conntrack mark----000000---|
+| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0|
+| DSCP | unused | flag |unused |
+|-----------------------0x01---000000---|
+ | |
+ | |
+ ---| Conditional flag
+ v only restore if set
+|-ip diffserv-|
+| 6 bits |
+|-------------|
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ include/uapi/linux/netfilter/xt_connmark.h | 10 ++++
+ net/netfilter/xt_connmark.c | 55 ++++++++++++++++++----
+ 2 files changed, 57 insertions(+), 8 deletions(-)
+
+--- a/include/uapi/linux/netfilter/xt_connmark.h
++++ b/include/uapi/linux/netfilter/xt_connmark.h
+@@ -20,6 +20,11 @@ enum {
+ };
+
+ enum {
++ XT_CONNMARK_VALUE = (1 << 0),
++ XT_CONNMARK_DSCP = (1 << 1)
++};
++
++enum {
+ D_SHIFT_LEFT = 0,
+ D_SHIFT_RIGHT,
+ };
+@@ -34,6 +39,11 @@ struct xt_connmark_tginfo2 {
+ __u8 shift_dir, shift_bits, mode;
+ };
+
++struct xt_connmark_tginfo3 {
++ __u32 ctmark, ctmask, nfmask;
++ __u8 shift_dir, shift_bits, mode, func;
++};
++
+ struct xt_connmark_mtinfo1 {
+ __u32 mark, mask;
+ __u8 invert;
+--- a/net/netfilter/xt_connmark.c
++++ b/net/netfilter/xt_connmark.c
+@@ -24,12 +24,13 @@ MODULE_ALIAS("ipt_connmark");
+ MODULE_ALIAS("ip6t_connmark");
+
+ static unsigned int
+-connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info)
++connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo3 *info)
+ {
+ enum ip_conntrack_info ctinfo;
+ u_int32_t new_targetmark;
+ struct nf_conn *ct;
+ u_int32_t newmark;
++ u_int8_t dscp;
+
+ ct = nf_ct_get(skb, &ctinfo);
+ if (ct == NULL)
+@@ -37,12 +38,24 @@ connmark_tg_shift(struct sk_buff *skb, c
+
+ switch (info->mode) {
+ case XT_CONNMARK_SET:
+- newmark = (ct->mark & ~info->ctmask) ^ info->ctmark;
+- if (info->shift_dir == D_SHIFT_RIGHT)
+- newmark >>= info->shift_bits;
+- else
+- newmark <<= info->shift_bits;
++ newmark = ct->mark;
++ if (info->func & XT_CONNMARK_VALUE) {
++ newmark = (newmark & ~info->ctmask) ^ info->ctmark;
++ if (info->shift_dir == D_SHIFT_RIGHT)
++ newmark >>= info->shift_bits;
++ else
++ newmark <<= info->shift_bits;
++ } else if (info->func & XT_CONNMARK_DSCP) {
++ if (skb->protocol == htons(ETH_P_IP))
++ dscp = ipv4_get_dsfield(ip_hdr(skb)) >> 2;
++ else if (skb->protocol == htons(ETH_P_IPV6))
++ dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2;
++ else /* protocol doesn't have diffserv */
++ break;
+
++ newmark = (newmark & ~info->ctmark) |
++ (info->ctmask | (dscp << info->shift_bits));
++ }
+ if (ct->mark != newmark) {
+ ct->mark = newmark;
+ nf_conntrack_event_cache(IPCT_MARK, ct);
+@@ -81,20 +94,36 @@ static unsigned int
+ connmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
+ {
+ const struct xt_connmark_tginfo1 *info = par->targinfo;
+- const struct xt_connmark_tginfo2 info2 = {
++ const struct xt_connmark_tginfo3 info3 = {
+ .ctmark = info->ctmark,
+ .ctmask = info->ctmask,
+ .nfmask = info->nfmask,
+ .mode = info->mode,
++ .func = XT_CONNMARK_VALUE
+ };
+
+- return connmark_tg_shift(skb, &info2);
++ return connmark_tg_shift(skb, &info3);
+ }
+
+ static unsigned int
+ connmark_tg_v2(struct sk_buff *skb, const struct xt_action_param *par)
+ {
+ const struct xt_connmark_tginfo2 *info = par->targinfo;
++ const struct xt_connmark_tginfo3 info3 = {
++ .ctmark = info->ctmark,
++ .ctmask = info->ctmask,
++ .nfmask = info->nfmask,
++ .mode = info->mode,
++ .func = XT_CONNMARK_VALUE
++ };
++
++ return connmark_tg_shift(skb, &info3);
++}
++
++static unsigned int
++connmark_tg_v3(struct sk_buff *skb, const struct xt_action_param *par)
++{
++ const struct xt_connmark_tginfo3 *info = par->targinfo;
+
+ return connmark_tg_shift(skb, info);
+ }
+@@ -165,6 +194,16 @@ static struct xt_target connmark_tg_reg[
+ .targetsize = sizeof(struct xt_connmark_tginfo2),
+ .destroy = connmark_tg_destroy,
+ .me = THIS_MODULE,
++ },
++ {
++ .name = "CONNMARK",
++ .revision = 3,
++ .family = NFPROTO_UNSPEC,
++ .checkentry = connmark_tg_check,
++ .target = connmark_tg_v3,
++ .targetsize = sizeof(struct xt_connmark_tginfo3),
++ .destroy = connmark_tg_destroy,
++ .me = THIS_MODULE,
+ }
+ };
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 20 Feb 2018 15:56:02 +0100
+Subject: [PATCH] netfilter: add xt_FLOWOFFLOAD target
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ create mode 100644 net/netfilter/xt_OFFLOAD.c
+
+--- a/net/ipv4/netfilter/Kconfig
++++ b/net/ipv4/netfilter/Kconfig
+@@ -56,8 +56,6 @@ config NF_TABLES_ARP
+ help
+ This option enables the ARP support for nf_tables.
+
+-endif # NF_TABLES
+-
+ config NF_FLOW_TABLE_IPV4
+ tristate "Netfilter flow table IPv4 module"
+ depends on NF_FLOW_TABLE
+@@ -66,6 +64,8 @@ config NF_FLOW_TABLE_IPV4
+
+ To compile it as a module, choose M here.
+
++endif # NF_TABLES
++
+ config NF_DUP_IPV4
+ tristate "Netfilter IPv4 packet duplication to alternate destination"
+ depends on !NF_CONNTRACK || NF_CONNTRACK
+--- a/net/ipv6/netfilter/Kconfig
++++ b/net/ipv6/netfilter/Kconfig
+@@ -45,7 +45,6 @@ config NFT_FIB_IPV6
+ multicast or blackhole.
+
+ endif # NF_TABLES_IPV6
+-endif # NF_TABLES
+
+ config NF_FLOW_TABLE_IPV6
+ tristate "Netfilter flow table IPv6 module"
+@@ -55,6 +54,8 @@ config NF_FLOW_TABLE_IPV6
+
+ To compile it as a module, choose M here.
+
++endif # NF_TABLES
++
+ config NF_DUP_IPV6
+ tristate "Netfilter IPv6 packet duplication to alternate destination"
+ depends on !NF_CONNTRACK || NF_CONNTRACK
+--- a/net/netfilter/Kconfig
++++ b/net/netfilter/Kconfig
+@@ -683,8 +683,6 @@ config NFT_FIB_NETDEV
+
+ endif # NF_TABLES_NETDEV
+
+-endif # NF_TABLES
+-
+ config NF_FLOW_TABLE_INET
+ tristate "Netfilter flow table mixed IPv4/IPv6 module"
+ depends on NF_FLOW_TABLE
+@@ -693,11 +691,12 @@ config NF_FLOW_TABLE_INET
+
+ To compile it as a module, choose M here.
+
++endif # NF_TABLES
++
+ config NF_FLOW_TABLE
+ tristate "Netfilter flow table module"
+ depends on NETFILTER_INGRESS
+ depends on NF_CONNTRACK
+- depends on NF_TABLES
+ help
+ This option adds the flow table core infrastructure.
+
+@@ -977,6 +976,15 @@ config NETFILTER_XT_TARGET_NOTRACK
+ depends on NETFILTER_ADVANCED
+ select NETFILTER_XT_TARGET_CT
+
++config NETFILTER_XT_TARGET_FLOWOFFLOAD
++ tristate '"FLOWOFFLOAD" target support'
++ depends on NF_FLOW_TABLE
++ depends on NETFILTER_INGRESS
++ help
++ This option adds a `FLOWOFFLOAD' target, which uses the nf_flow_offload
++ module to speed up processing of packets by bypassing the usual
++ netfilter chains
++
+ config NETFILTER_XT_TARGET_RATEEST
+ tristate '"RATEEST" target support'
+ depends on NETFILTER_ADVANCED
+--- a/net/netfilter/Makefile
++++ b/net/netfilter/Makefile
+@@ -145,6 +145,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIF
+ obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
++obj-$(CONFIG_NETFILTER_XT_TARGET_FLOWOFFLOAD) += xt_FLOWOFFLOAD.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
+--- /dev/null
++++ b/net/netfilter/xt_FLOWOFFLOAD.c
+@@ -0,0 +1,657 @@
++/*
++ * Copyright (C) 2018-2021 Felix Fietkau <nbd@nbd.name>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/netfilter.h>
++#include <linux/netfilter/xt_FLOWOFFLOAD.h>
++#include <net/ip.h>
++#include <net/netfilter/nf_conntrack.h>
++#include <net/netfilter/nf_conntrack_extend.h>
++#include <net/netfilter/nf_conntrack_helper.h>
++#include <net/netfilter/nf_flow_table.h>
++
++struct xt_flowoffload_hook {
++ struct hlist_node list;
++ struct nf_hook_ops ops;
++ struct net *net;
++ bool registered;
++ bool used;
++};
++
++struct xt_flowoffload_table {
++ struct nf_flowtable ft;
++ struct hlist_head hooks;
++ struct delayed_work work;
++};
++
++static DEFINE_SPINLOCK(hooks_lock);
++
++struct xt_flowoffload_table flowtable[2];
++
++static unsigned int
++xt_flowoffload_net_hook(void *priv, struct sk_buff *skb,
++ const struct nf_hook_state *state)
++{
++ struct nf_flowtable *ft = priv;
++
++ if (!atomic_read(&ft->rhashtable.nelems))
++ return NF_ACCEPT;
++
++ switch (skb->protocol) {
++ case htons(ETH_P_IP):
++ return nf_flow_offload_ip_hook(priv, skb, state);
++ case htons(ETH_P_IPV6):
++ return nf_flow_offload_ipv6_hook(priv, skb, state);
++ }
++
++ return NF_ACCEPT;
++}
++
++static int
++xt_flowoffload_create_hook(struct xt_flowoffload_table *table,
++ struct net_device *dev)
++{
++ struct xt_flowoffload_hook *hook;
++ struct nf_hook_ops *ops;
++
++ hook = kzalloc(sizeof(*hook), GFP_ATOMIC);
++ if (!hook)
++ return -ENOMEM;
++
++ ops = &hook->ops;
++ ops->pf = NFPROTO_NETDEV;
++ ops->hooknum = NF_NETDEV_INGRESS;
++ ops->priority = 10;
++ ops->priv = &table->ft;
++ ops->hook = xt_flowoffload_net_hook;
++ ops->dev = dev;
++
++ hlist_add_head(&hook->list, &table->hooks);
++ mod_delayed_work(system_power_efficient_wq, &table->work, 0);
++
++ return 0;
++}
++
++static struct xt_flowoffload_hook *
++flow_offload_lookup_hook(struct xt_flowoffload_table *table,
++ struct net_device *dev)
++{
++ struct xt_flowoffload_hook *hook;
++
++ hlist_for_each_entry(hook, &table->hooks, list) {
++ if (hook->ops.dev == dev)
++ return hook;
++ }
++
++ return NULL;
++}
++
++static void
++xt_flowoffload_check_device(struct xt_flowoffload_table *table,
++ struct net_device *dev)
++{
++ struct xt_flowoffload_hook *hook;
++
++ if (!dev)
++ return;
++
++ spin_lock_bh(&hooks_lock);
++ hook = flow_offload_lookup_hook(table, dev);
++ if (hook)
++ hook->used = true;
++ else
++ xt_flowoffload_create_hook(table, dev);
++ spin_unlock_bh(&hooks_lock);
++}
++
++static void
++xt_flowoffload_register_hooks(struct xt_flowoffload_table *table)
++{
++ struct xt_flowoffload_hook *hook;
++
++restart:
++ hlist_for_each_entry(hook, &table->hooks, list) {
++ if (hook->registered)
++ continue;
++
++ hook->registered = true;
++ hook->net = dev_net(hook->ops.dev);
++ spin_unlock_bh(&hooks_lock);
++ nf_register_net_hook(hook->net, &hook->ops);
++ if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD)
++ table->ft.type->setup(&table->ft, hook->ops.dev,
++ FLOW_BLOCK_BIND);
++ spin_lock_bh(&hooks_lock);
++ goto restart;
++ }
++
++}
++
++static bool
++xt_flowoffload_cleanup_hooks(struct xt_flowoffload_table *table)
++{
++ struct xt_flowoffload_hook *hook;
++ bool active = false;
++
++restart:
++ spin_lock_bh(&hooks_lock);
++ hlist_for_each_entry(hook, &table->hooks, list) {
++ if (hook->used || !hook->registered) {
++ active = true;
++ continue;
++ }
++
++ hlist_del(&hook->list);
++ spin_unlock_bh(&hooks_lock);
++ if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD)
++ table->ft.type->setup(&table->ft, hook->ops.dev,
++ FLOW_BLOCK_UNBIND);
++ nf_unregister_net_hook(hook->net, &hook->ops);
++ kfree(hook);
++ goto restart;
++ }
++ spin_unlock_bh(&hooks_lock);
++
++ return active;
++}
++
++static void
++xt_flowoffload_check_hook(struct flow_offload *flow, void *data)
++{
++ struct xt_flowoffload_table *table = data;
++ struct flow_offload_tuple *tuple0 = &flow->tuplehash[0].tuple;
++ struct flow_offload_tuple *tuple1 = &flow->tuplehash[1].tuple;
++ struct xt_flowoffload_hook *hook;
++
++ spin_lock_bh(&hooks_lock);
++ hlist_for_each_entry(hook, &table->hooks, list) {
++ if (hook->ops.dev->ifindex != tuple0->iifidx &&
++ hook->ops.dev->ifindex != tuple1->iifidx)
++ continue;
++
++ hook->used = true;
++ }
++ spin_unlock_bh(&hooks_lock);
++}
++
++static void
++xt_flowoffload_hook_work(struct work_struct *work)
++{
++ struct xt_flowoffload_table *table;
++ struct xt_flowoffload_hook *hook;
++ int err;
++
++ table = container_of(work, struct xt_flowoffload_table, work.work);
++
++ spin_lock_bh(&hooks_lock);
++ xt_flowoffload_register_hooks(table);
++ hlist_for_each_entry(hook, &table->hooks, list)
++ hook->used = false;
++ spin_unlock_bh(&hooks_lock);
++
++ err = nf_flow_table_iterate(&table->ft, xt_flowoffload_check_hook,
++ table);
++ if (err && err != -EAGAIN)
++ goto out;
++
++ if (!xt_flowoffload_cleanup_hooks(table))
++ return;
++
++out:
++ queue_delayed_work(system_power_efficient_wq, &table->work, HZ);
++}
++
++static bool
++xt_flowoffload_skip(struct sk_buff *skb, int family)
++{
++ if (skb_sec_path(skb))
++ return true;
++
++ if (family == NFPROTO_IPV4) {
++ const struct ip_options *opt = &(IPCB(skb)->opt);
++
++ if (unlikely(opt->optlen))
++ return true;
++ }
++
++ return false;
++}
++
++static bool flow_is_valid_ether_device(const struct net_device *dev)
++{
++ if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER ||
++ dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr))
++ return false;
++
++ return true;
++}
++
++static void
++xt_flowoffload_route_check_path(struct nf_flow_route *route,
++ const struct nf_conn *ct,
++ enum ip_conntrack_dir dir,
++ struct net_device **out_dev)
++{
++ const struct dst_entry *dst = route->tuple[dir].dst;
++ const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
++ struct net_device_path_stack stack;
++ enum net_device_path_type prev_type;
++ struct net_device *dev = dst->dev;
++ struct neighbour *n;
++ bool last = false;
++ u8 nud_state;
++ int i;
++
++ route->tuple[!dir].in.ifindex = dev->ifindex;
++ route->tuple[dir].out.ifindex = dev->ifindex;
++
++ if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_XFRM)
++ return;
++
++ if ((dev->flags & IFF_LOOPBACK) ||
++ dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN ||
++ !is_valid_ether_addr(dev->dev_addr))
++ return;
++
++ n = dst_neigh_lookup(dst, daddr);
++ if (!n)
++ return;
++
++ read_lock_bh(&n->lock);
++ nud_state = n->nud_state;
++ memcpy(route->tuple[dir].out.h_dest, n->ha, ETH_ALEN);
++ read_unlock_bh(&n->lock);
++ neigh_release(n);
++
++ if (!(nud_state & NUD_VALID))
++ return;
++
++ if (dev_fill_forward_path(dev, route->tuple[dir].out.h_dest, &stack) ||
++ !stack.num_paths)
++ return;
++
++ prev_type = DEV_PATH_ETHERNET;
++ for (i = 0; i <= stack.num_paths; i++) {
++ const struct net_device_path *path = &stack.path[i];
++ int n_encaps = route->tuple[!dir].in.num_encaps;
++
++ dev = (struct net_device *)path->dev;
++ if (flow_is_valid_ether_device(dev)) {
++ if (route->tuple[dir].xmit_type != FLOW_OFFLOAD_XMIT_DIRECT) {
++ memcpy(route->tuple[dir].out.h_source,
++ dev->dev_addr, ETH_ALEN);
++ route->tuple[dir].out.ifindex = dev->ifindex;
++ }
++ route->tuple[dir].xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
++ }
++
++ switch (path->type) {
++ case DEV_PATH_PPPOE:
++ case DEV_PATH_VLAN:
++ if (n_encaps >= NF_FLOW_TABLE_ENCAP_MAX ||
++ i == stack.num_paths) {
++ last = true;
++ break;
++ }
++
++ route->tuple[!dir].in.num_encaps++;
++ route->tuple[!dir].in.encap[n_encaps].id = path->encap.id;
++ route->tuple[!dir].in.encap[n_encaps].proto = path->encap.proto;
++ if (path->type == DEV_PATH_PPPOE)
++ memcpy(route->tuple[dir].out.h_dest,
++ path->encap.h_dest, ETH_ALEN);
++ break;
++ case DEV_PATH_BRIDGE:
++ switch (path->bridge.vlan_mode) {
++ case DEV_PATH_BR_VLAN_TAG:
++ if (n_encaps >= NF_FLOW_TABLE_ENCAP_MAX ||
++ i == stack.num_paths) {
++ last = true;
++ break;
++ }
++
++ route->tuple[!dir].in.num_encaps++;
++ route->tuple[!dir].in.encap[n_encaps].id =
++ path->bridge.vlan_id;
++ route->tuple[!dir].in.encap[n_encaps].proto =
++ path->bridge.vlan_proto;
++ break;
++ case DEV_PATH_BR_VLAN_UNTAG:
++ route->tuple[!dir].in.num_encaps--;
++ break;
++ case DEV_PATH_BR_VLAN_UNTAG_HW:
++ route->tuple[!dir].in.ingress_vlans |= BIT(n_encaps - 1);
++ break;
++ case DEV_PATH_BR_VLAN_KEEP:
++ break;
++ }
++ break;
++ default:
++ last = true;
++ break;
++ }
++
++ if (last)
++ break;
++ }
++
++ *out_dev = dev;
++ route->tuple[dir].out.hw_ifindex = dev->ifindex;
++ route->tuple[!dir].in.ifindex = dev->ifindex;
++}
++
++static int
++xt_flowoffload_route_dir(struct nf_flow_route *route, const struct nf_conn *ct,
++ enum ip_conntrack_dir dir,
++ const struct xt_action_param *par, int ifindex)
++{
++ struct dst_entry *dst = NULL;
++ struct flowi fl;
++
++ memset(&fl, 0, sizeof(fl));
++ switch (xt_family(par)) {
++ case NFPROTO_IPV4:
++ fl.u.ip4.daddr = ct->tuplehash[!dir].tuple.src.u3.ip;
++ fl.u.ip4.flowi4_oif = ifindex;
++ break;
++ case NFPROTO_IPV6:
++ fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.dst.u3.in6;
++ fl.u.ip6.daddr = ct->tuplehash[!dir].tuple.src.u3.in6;
++ fl.u.ip6.flowi6_oif = ifindex;
++ break;
++ }
++
++ nf_route(xt_net(par), &dst, &fl, false, xt_family(par));
++ if (!dst)
++ return -ENOENT;
++
++ route->tuple[dir].dst = dst;
++ if (dst_xfrm(dst))
++ route->tuple[dir].xmit_type = FLOW_OFFLOAD_XMIT_XFRM;
++ else
++ route->tuple[dir].xmit_type = FLOW_OFFLOAD_XMIT_NEIGH;
++
++ return 0;
++}
++
++static int
++xt_flowoffload_route(struct sk_buff *skb, const struct nf_conn *ct,
++ const struct xt_action_param *par,
++ struct nf_flow_route *route, enum ip_conntrack_dir dir,
++ struct net_device **dev)
++{
++ int ret;
++
++ ret = xt_flowoffload_route_dir(route, ct, dir, par,
++ dev[dir]->ifindex);
++ if (ret)
++ return ret;
++
++ ret = xt_flowoffload_route_dir(route, ct, !dir, par,
++ dev[!dir]->ifindex);
++ if (ret)
++ return ret;
++
++ xt_flowoffload_route_check_path(route, ct, dir, &dev[!dir]);
++ xt_flowoffload_route_check_path(route, ct, !dir, &dev[dir]);
++
++ return 0;
++}
++
++static unsigned int
++flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par)
++{
++ struct xt_flowoffload_table *table;
++ const struct xt_flowoffload_target_info *info = par->targinfo;
++ struct tcphdr _tcph, *tcph = NULL;
++ enum ip_conntrack_info ctinfo;
++ enum ip_conntrack_dir dir;
++ struct nf_flow_route route = {};
++ struct flow_offload *flow = NULL;
++ struct net_device *devs[2] = {};
++ struct nf_conn *ct;
++ struct net *net;
++
++ if (xt_flowoffload_skip(skb, xt_family(par)))
++ return XT_CONTINUE;
++
++ ct = nf_ct_get(skb, &ctinfo);
++ if (ct == NULL)
++ return XT_CONTINUE;
++
++ switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) {
++ case IPPROTO_TCP:
++ if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED)
++ return XT_CONTINUE;
++
++ tcph = skb_header_pointer(skb, par->thoff,
++ sizeof(_tcph), &_tcph);
++ if (unlikely(!tcph || tcph->fin || tcph->rst))
++ return XT_CONTINUE;
++ break;
++ case IPPROTO_UDP:
++ break;
++ default:
++ return XT_CONTINUE;
++ }
++
++ if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) ||
++ ct->status & IPS_SEQ_ADJUST)
++ return XT_CONTINUE;
++
++ if (!nf_ct_is_confirmed(ct))
++ return XT_CONTINUE;
++
++ devs[dir] = xt_out(par);
++ devs[!dir] = xt_in(par);
++
++ if (!devs[dir] || !devs[!dir])
++ return XT_CONTINUE;
++
++ if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status))
++ return XT_CONTINUE;
++
++ dir = CTINFO2DIR(ctinfo);
++
++ if (xt_flowoffload_route(skb, ct, par, &route, dir, devs) < 0)
++ goto err_flow_route;
++
++ flow = flow_offload_alloc(ct);
++ if (!flow)
++ goto err_flow_alloc;
++
++ if (flow_offload_route_init(flow, &route) < 0)
++ goto err_flow_add;
++
++ if (tcph) {
++ ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
++ ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
++ }
++
++ table = &flowtable[!!(info->flags & XT_FLOWOFFLOAD_HW)];
++
++ net = read_pnet(&table->ft.net);
++ if (!net)
++ write_pnet(&table->ft.net, xt_net(par));
++
++ if (flow_offload_add(&table->ft, flow) < 0)
++ goto err_flow_add;
++
++ xt_flowoffload_check_device(table, devs[0]);
++ xt_flowoffload_check_device(table, devs[1]);
++
++ dst_release(route.tuple[dir].dst);
++ dst_release(route.tuple[!dir].dst);
++
++ return XT_CONTINUE;
++
++err_flow_add:
++ flow_offload_free(flow);
++err_flow_alloc:
++ dst_release(route.tuple[dir].dst);
++ dst_release(route.tuple[!dir].dst);
++err_flow_route:
++ clear_bit(IPS_OFFLOAD_BIT, &ct->status);
++
++ return XT_CONTINUE;
++}
++
++static int flowoffload_chk(const struct xt_tgchk_param *par)
++{
++ struct xt_flowoffload_target_info *info = par->targinfo;
++
++ if (info->flags & ~XT_FLOWOFFLOAD_MASK)
++ return -EINVAL;
++
++ return 0;
++}
++
++static struct xt_target offload_tg_reg __read_mostly = {
++ .family = NFPROTO_UNSPEC,
++ .name = "FLOWOFFLOAD",
++ .revision = 0,
++ .targetsize = sizeof(struct xt_flowoffload_target_info),
++ .usersize = sizeof(struct xt_flowoffload_target_info),
++ .checkentry = flowoffload_chk,
++ .target = flowoffload_tg,
++ .me = THIS_MODULE,
++};
++
++static int flow_offload_netdev_event(struct notifier_block *this,
++ unsigned long event, void *ptr)
++{
++ struct xt_flowoffload_hook *hook0, *hook1;
++ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
++
++ if (event != NETDEV_UNREGISTER)
++ return NOTIFY_DONE;
++
++ spin_lock_bh(&hooks_lock);
++ hook0 = flow_offload_lookup_hook(&flowtable[0], dev);
++ if (hook0)
++ hlist_del(&hook0->list);
++
++ hook1 = flow_offload_lookup_hook(&flowtable[1], dev);
++ if (hook1)
++ hlist_del(&hook1->list);
++ spin_unlock_bh(&hooks_lock);
++
++ if (hook0) {
++ nf_unregister_net_hook(hook0->net, &hook0->ops);
++ kfree(hook0);
++ }
++
++ if (hook1) {
++ nf_unregister_net_hook(hook1->net, &hook1->ops);
++ kfree(hook1);
++ }
++
++ nf_flow_table_cleanup(dev);
++
++ return NOTIFY_DONE;
++}
++
++static struct notifier_block flow_offload_netdev_notifier = {
++ .notifier_call = flow_offload_netdev_event,
++};
++
++static unsigned int
++nf_flow_offload_inet_hook(void *priv, struct sk_buff *skb,
++ const struct nf_hook_state *state)
++{
++ switch (skb->protocol) {
++ case htons(ETH_P_IP):
++ return nf_flow_offload_ip_hook(priv, skb, state);
++ case htons(ETH_P_IPV6):
++ return nf_flow_offload_ipv6_hook(priv, skb, state);
++ }
++
++ return NF_ACCEPT;
++}
++
++static int nf_flow_rule_route_inet(struct net *net,
++ const struct flow_offload *flow,
++ enum flow_offload_tuple_dir dir,
++ struct nf_flow_rule *flow_rule)
++{
++ const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
++ int err;
++
++ switch (flow_tuple->l3proto) {
++ case NFPROTO_IPV4:
++ err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule);
++ break;
++ case NFPROTO_IPV6:
++ err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule);
++ break;
++ default:
++ err = -1;
++ break;
++ }
++
++ return err;
++}
++
++static struct nf_flowtable_type flowtable_inet = {
++ .family = NFPROTO_INET,
++ .init = nf_flow_table_init,
++ .setup = nf_flow_table_offload_setup,
++ .action = nf_flow_rule_route_inet,
++ .free = nf_flow_table_free,
++ .hook = nf_flow_offload_inet_hook,
++ .owner = THIS_MODULE,
++};
++
++static int init_flowtable(struct xt_flowoffload_table *tbl)
++{
++ INIT_DELAYED_WORK(&tbl->work, xt_flowoffload_hook_work);
++ tbl->ft.type = &flowtable_inet;
++
++ return nf_flow_table_init(&tbl->ft);
++}
++
++static int __init xt_flowoffload_tg_init(void)
++{
++ int ret;
++
++ register_netdevice_notifier(&flow_offload_netdev_notifier);
++
++ ret = init_flowtable(&flowtable[0]);
++ if (ret)
++ return ret;
++
++ ret = init_flowtable(&flowtable[1]);
++ if (ret)
++ goto cleanup;
++
++ flowtable[1].ft.flags = NF_FLOWTABLE_HW_OFFLOAD;
++
++ ret = xt_register_target(&offload_tg_reg);
++ if (ret)
++ goto cleanup2;
++
++ return 0;
++
++cleanup2:
++ nf_flow_table_free(&flowtable[1].ft);
++cleanup:
++ nf_flow_table_free(&flowtable[0].ft);
++ return ret;
++}
++
++static void __exit xt_flowoffload_tg_exit(void)
++{
++ xt_unregister_target(&offload_tg_reg);
++ unregister_netdevice_notifier(&flow_offload_netdev_notifier);
++ nf_flow_table_free(&flowtable[0].ft);
++ nf_flow_table_free(&flowtable[1].ft);
++}
++
++MODULE_LICENSE("GPL");
++module_init(xt_flowoffload_tg_init);
++module_exit(xt_flowoffload_tg_exit);
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -7,7 +7,6 @@
+ #include <linux/netdevice.h>
+ #include <net/ip.h>
+ #include <net/ip6_route.h>
+-#include <net/netfilter/nf_tables.h>
+ #include <net/netfilter/nf_flow_table.h>
+ #include <net/netfilter/nf_conntrack.h>
+ #include <net/netfilter/nf_conntrack_core.h>
+@@ -395,8 +394,7 @@ flow_offload_lookup(struct nf_flowtable
+ }
+ EXPORT_SYMBOL_GPL(flow_offload_lookup);
+
+-static int
+-nf_flow_table_iterate(struct nf_flowtable *flow_table,
++int nf_flow_table_iterate(struct nf_flowtable *flow_table,
+ void (*iter)(struct flow_offload *flow, void *data),
+ void *data)
+ {
+@@ -428,6 +426,7 @@ nf_flow_table_iterate(struct nf_flowtabl
+
+ return err;
+ }
++EXPORT_SYMBOL_GPL(nf_flow_table_iterate);
+
+ static bool flow_offload_stale_dst(struct flow_offload_tuple *tuple)
+ {
+--- /dev/null
++++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h
+@@ -0,0 +1,17 @@
++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
++#ifndef _XT_FLOWOFFLOAD_H
++#define _XT_FLOWOFFLOAD_H
++
++#include <linux/types.h>
++
++enum {
++ XT_FLOWOFFLOAD_HW = 1 << 0,
++
++ XT_FLOWOFFLOAD_MASK = XT_FLOWOFFLOAD_HW
++};
++
++struct xt_flowoffload_target_info {
++ __u32 flags;
++};
++
++#endif /* _XT_FLOWOFFLOAD_H */
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -270,6 +270,10 @@ void nf_flow_table_free(struct nf_flowta
+
+ void flow_offload_teardown(struct flow_offload *flow);
+
++int nf_flow_table_iterate(struct nf_flowtable *flow_table,
++ void (*iter)(struct flow_offload *flow, void *data),
++ void *data);
++
+ void nf_flow_snat_port(const struct flow_offload *flow,
+ struct sk_buff *skb, unsigned int thoff,
+ u8 protocol, enum flow_offload_tuple_dir dir);
--- /dev/null
+From 6d3bc769657b0ee7c7506dad9911111c4226a7ea Mon Sep 17 00:00:00 2001
+From: Imre Kaloz <kaloz@openwrt.org>
+Date: Fri, 7 Jul 2017 17:21:05 +0200
+Subject: mac80211: increase wireless mesh header size
+
+lede-commit 3d4466cfd8f75f717efdb1f96fdde3c70d865fc1
+Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
+---
+ include/linux/netdevice.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -144,8 +144,8 @@ static inline bool dev_xmit_complete(int
+
+ #if defined(CONFIG_HYPERV_NET)
+ # define LL_MAX_HEADER 128
+-#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25)
+-# if defined(CONFIG_MAC80211_MESH)
++#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) || 1
++# if defined(CONFIG_MAC80211_MESH) || 1
+ # define LL_MAX_HEADER 128
+ # else
+ # define LL_MAX_HEADER 96
--- /dev/null
+From a6ccb238939b25851474a279b20367fd24a0e816 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:21:53 +0200
+Subject: hack: net: fq_codel: tune defaults for small devices
+
+Assume that x86_64 devices always have a big memory and do not need this
+optimization compared to devices with only 32 MB or 64 MB RAM.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/sched/sch_fq_codel.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/net/sched/sch_fq_codel.c
++++ b/net/sched/sch_fq_codel.c
+@@ -469,7 +469,11 @@ static int fq_codel_init(struct Qdisc *s
+
+ sch->limit = 10*1024;
+ q->flows_cnt = 1024;
++#ifdef CONFIG_X86_64
+ q->memory_limit = 32 << 20; /* 32 MBytes */
++#else
++ q->memory_limit = 4 << 20; /* 4 MBytes */
++#endif
+ q->drop_batch_size = 64;
+ q->quantum = psched_mtu(qdisc_dev(sch));
+ INIT_LIST_HEAD(&q->new_flows);
--- /dev/null
+From 36e516290611e613aa92996cb4339561452695b4 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:24:23 +0200
+Subject: net: swconfig: adds openwrt switch layer
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/net/phy/Kconfig | 83 +++++++++++++++++++++++++++++++++++++++++++++++
+ drivers/net/phy/Makefile | 15 +++++++++
+ include/uapi/linux/Kbuild | 1 +
+ 3 files changed, 99 insertions(+)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -61,6 +61,80 @@ config SFP
+ depends on HWMON || HWMON=n
+ select MDIO_I2C
+
++comment "Switch configuration API + drivers"
++
++config SWCONFIG
++ tristate "Switch configuration API"
++ help
++ Switch configuration API using netlink. This allows
++ you to configure the VLAN features of certain switches.
++
++config SWCONFIG_LEDS
++ bool "Switch LED trigger support"
++ depends on (SWCONFIG && LEDS_TRIGGERS)
++
++config ADM6996_PHY
++ tristate "Driver for ADM6996 switches"
++ select SWCONFIG
++ help
++ Currently supports the ADM6996FC and ADM6996M switches.
++ Support for FC is very limited.
++
++config AR8216_PHY
++ tristate "Driver for Atheros AR8216 switches"
++ select SWCONFIG
++ select ETHERNET_PACKET_MANGLE
++
++config AR8216_PHY_LEDS
++ bool "Atheros AR8216 switch LED support"
++ depends on (AR8216_PHY && LEDS_CLASS)
++
++source "drivers/net/phy/b53/Kconfig"
++
++config IP17XX_PHY
++ tristate "Driver for IC+ IP17xx switches"
++ select SWCONFIG
++
++config PSB6970_PHY
++ tristate "Lantiq XWAY Tantos (PSB6970) Ethernet switch"
++ select SWCONFIG
++
++config RTL8306_PHY
++ tristate "Driver for Realtek RTL8306S switches"
++ select SWCONFIG
++
++config RTL8366_SMI
++ tristate "Driver for the RTL8366 SMI interface"
++ depends on GPIOLIB
++ help
++ This module implements the SMI interface protocol which is used
++ by some RTL8366 ethernet switch devices via the generic GPIO API.
++
++if RTL8366_SMI
++
++config RTL8366_SMI_DEBUG_FS
++ bool "RTL8366 SMI interface debugfs support"
++ depends on DEBUG_FS
++ default n
++
++config RTL8366S_PHY
++ tristate "Driver for the Realtek RTL8366S switch"
++ select SWCONFIG
++
++config RTL8366RB_PHY
++ tristate "Driver for the Realtek RTL8366RB switch"
++ select SWCONFIG
++
++config RTL8367_PHY
++ tristate "Driver for the Realtek RTL8367R/M switches"
++ select SWCONFIG
++
++config RTL8367B_PHY
++ tristate "Driver fot the Realtek RTL8367R-VB switch"
++ select SWCONFIG
++
++endif # RTL8366_SMI
++
+ comment "MII PHY device drivers"
+
+ config AMD_PHY
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -24,6 +24,19 @@ libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_
+ obj-$(CONFIG_PHYLINK) += phylink.o
+ obj-$(CONFIG_PHYLIB) += libphy.o
+
++obj-$(CONFIG_SWCONFIG) += swconfig.o
++obj-$(CONFIG_ADM6996_PHY) += adm6996.o
++obj-$(CONFIG_AR8216_PHY) += ar8216.o ar8327.o
++obj-$(CONFIG_SWCONFIG_B53) += b53/
++obj-$(CONFIG_IP17XX_PHY) += ip17xx.o
++obj-$(CONFIG_PSB6970_PHY) += psb6970.o
++obj-$(CONFIG_RTL8306_PHY) += rtl8306.o
++obj-$(CONFIG_RTL8366_SMI) += rtl8366_smi.o
++obj-$(CONFIG_RTL8366S_PHY) += rtl8366s.o
++obj-$(CONFIG_RTL8366RB_PHY) += rtl8366rb.o
++obj-$(CONFIG_RTL8367_PHY) += rtl8367.o
++obj-$(CONFIG_RTL8367B_PHY) += rtl8367b.o
++
+ obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o
+
+ obj-$(CONFIG_SFP) += sfp.o
+--- a/include/linux/platform_data/b53.h
++++ b/include/linux/platform_data/b53.h
+@@ -29,6 +29,9 @@ struct b53_platform_data {
+ u32 chip_id;
+ u16 enabled_ports;
+
++ /* allow to specify an ethX alias */
++ const char *alias;
++
+ /* only used by MMAP'd driver */
+ unsigned big_endian:1;
+ void __iomem *regs;
--- /dev/null
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -2097,6 +2097,7 @@ static int mv88e6xxx_port_fdb_add(struct
+ struct mv88e6xxx_chip *chip = ds->priv;
+ int err;
+
++ vid = vid ? : 1;
+ mv88e6xxx_reg_lock(chip);
+ err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid,
+ MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC);
+@@ -2111,6 +2112,7 @@ static int mv88e6xxx_port_fdb_del(struct
+ struct mv88e6xxx_chip *chip = ds->priv;
+ int err;
+
++ vid = vid ? : 1;
+ mv88e6xxx_reg_lock(chip);
+ err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, 0);
+ mv88e6xxx_reg_unlock(chip);
--- /dev/null
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -2659,6 +2659,9 @@ static int mv88e6xxx_setup_port(struct m
+ if (dsa_is_cpu_port(ds, port))
+ reg = 0;
+
++ /* Disable ATU member violation interrupt */
++ reg |= MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG;
++
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR,
+ reg);
+ if (err)
--- /dev/null
+From: Birger Koblitz <git@birger-koblitz.de>
+Date: Sun, 5 Sep 2021 15:13:10 +0200
+Subject: [PATCH] kernel: Add AQR113C and AQR813 support
+
+This hack adds support for the Aquantia 4th generation, 10GBit
+PHYs AQR113C and AQR813.
+
+Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
+
+--- a/drivers/net/phy/aquantia_main.c
++++ b/drivers/net/phy/aquantia_main.c
+@@ -20,8 +20,10 @@
+ #define PHY_ID_AQR105 0x03a1b4a2
+ #define PHY_ID_AQR106 0x03a1b4d0
+ #define PHY_ID_AQR107 0x03a1b4e0
++#define PHY_ID_AQR113C 0x31c31c12
+ #define PHY_ID_AQCS109 0x03a1b5c2
+ #define PHY_ID_AQR405 0x03a1b4b0
++#define PHY_ID_AQR813 0x31c31cb2
+
+ #define MDIO_PHYXS_VEND_IF_STATUS 0xe812
+ #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3)
+@@ -330,6 +332,49 @@ static int aqr107_read_rate(struct phy_d
+ return 0;
+ }
+
++static int aqr113c_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;
++
++ // On AQR113C, the speed returned by aqr_read_status is wrong
++ aqr107_read_rate(phydev);
++
++ 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_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_SGMII:
++ phydev->interface = PHY_INTERFACE_MODE_SGMII;
++ 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 downshifted rate from vendor register */
++ return aqr107_read_rate(phydev);
++}
++
+ static int aqr107_read_status(struct phy_device *phydev)
+ {
+ int val, ret;
+@@ -460,7 +505,7 @@ static void aqr107_chip_info(struct phy_
+ 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",
++ phydev_info(phydev, "FW %u.%u, Build %u, Provisioning %u\n",
+ fw_major, fw_minor, build_id, prov_id);
+ }
+
+@@ -632,6 +677,24 @@ static struct phy_driver aqr_driver[] =
+ .link_change_notify = aqr107_link_change_notify,
+ },
+ {
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
++ .name = "Aquantia AQR113C",
++ .probe = aqr107_probe,
++ .config_init = aqr107_config_init,
++ .config_aneg = aqr_config_aneg,
++ .config_intr = aqr_config_intr,
++ .ack_interrupt = aqr_ack_interrupt,
++ .read_status = aqr113c_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,
+@@ -657,6 +720,24 @@ static struct phy_driver aqr_driver[] =
+ .ack_interrupt = aqr_ack_interrupt,
+ .read_status = aqr_read_status,
+ },
++{
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR813),
++ .name = "Aquantia AQR813",
++ .probe = aqr107_probe,
++ .config_init = aqr107_config_init,
++ .config_aneg = aqr_config_aneg,
++ .config_intr = aqr_config_intr,
++ .ack_interrupt = aqr_ack_interrupt,
++ .read_status = aqr113c_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);
+@@ -667,8 +748,10 @@ static struct mdio_device_id __maybe_unu
+ { 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_AQR113C) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
+ { }
+ };
+
--- /dev/null
+From ffe387740bbe88dd88bbe04d6375902708003d6e Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:25:00 +0200
+Subject: net: add packet mangeling
+
+ar8216 switches have a hardware bug, which renders normal 802.1q support
+unusable. Packet mangling is required to fix up the vlan for incoming
+packets.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/netdevice.h | 11 +++++++++++
+ include/linux/skbuff.h | 14 ++++----------
+ net/Kconfig | 6 ++++++
+ net/core/dev.c | 20 +++++++++++++++-----
+ net/core/skbuff.c | 17 +++++++++++++++++
+ net/ethernet/eth.c | 6 ++++++
+ 6 files changed, 59 insertions(+), 15 deletions(-)
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -1618,6 +1618,7 @@ enum netdev_priv_flags {
+ IFF_FAILOVER_SLAVE = 1<<28,
+ IFF_L3MDEV_RX_HANDLER = 1<<29,
+ IFF_LIVE_RENAME_OK = 1<<30,
++ IFF_NO_IP_ALIGN = 1<<31,
+ };
+
+ #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN
+@@ -1650,6 +1651,7 @@ enum netdev_priv_flags {
+ #define IFF_FAILOVER_SLAVE IFF_FAILOVER_SLAVE
+ #define IFF_L3MDEV_RX_HANDLER IFF_L3MDEV_RX_HANDLER
+ #define IFF_LIVE_RENAME_OK IFF_LIVE_RENAME_OK
++#define IFF_NO_IP_ALIGN IFF_NO_IP_ALIGN
+
+ /* Specifies the type of the struct net_device::ml_priv pointer */
+ enum netdev_ml_priv_type {
+@@ -1990,6 +1992,11 @@ struct net_device {
+ const struct tlsdev_ops *tlsdev_ops;
+ #endif
+
++#ifdef CONFIG_ETHERNET_PACKET_MANGLE
++ void (*eth_mangle_rx)(struct net_device *dev, struct sk_buff *skb);
++ struct sk_buff *(*eth_mangle_tx)(struct net_device *dev, struct sk_buff *skb);
++#endif
++
+ const struct header_ops *header_ops;
+
+ unsigned int flags;
+@@ -2080,6 +2087,10 @@ struct net_device {
+ struct mpls_dev __rcu *mpls_ptr;
+ #endif
+
++#ifdef CONFIG_ETHERNET_PACKET_MANGLE
++ void *phy_ptr; /* PHY device specific data */
++#endif
++
+ /*
+ * Cache lines mostly used on receive path (including eth_type_trans())
+ */
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -2710,6 +2710,10 @@ static inline int pskb_trim(struct sk_bu
+ return (len < skb->len) ? __pskb_trim(skb, len) : 0;
+ }
+
++extern struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev,
++ unsigned int length, gfp_t gfp);
++
++
+ /**
+ * pskb_trim_unique - remove end from a paged unique (not cloned) buffer
+ * @skb: buffer to alter
+@@ -2841,16 +2845,6 @@ static inline struct sk_buff *dev_alloc_
+ }
+
+
+-static inline struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev,
+- unsigned int length, gfp_t gfp)
+-{
+- struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp);
+-
+- if (NET_IP_ALIGN && skb)
+- skb_reserve(skb, NET_IP_ALIGN);
+- return skb;
+-}
+-
+ static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev,
+ unsigned int length)
+ {
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -26,6 +26,12 @@ menuconfig NET
+
+ if NET
+
++config ETHERNET_PACKET_MANGLE
++ bool
++ help
++ This option can be selected by phy drivers that need to mangle
++ packets going in or out of an ethernet device.
++
+ config WANT_COMPAT_NETLINK_MESSAGES
+ bool
+ help
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -3646,6 +3646,11 @@ static int xmit_one(struct sk_buff *skb,
+ if (dev_nit_active(dev))
+ dev_queue_xmit_nit(skb, dev);
+
++#ifdef CONFIG_ETHERNET_PACKET_MANGLE
++ if (dev->eth_mangle_tx && !(skb = dev->eth_mangle_tx(dev, skb)))
++ return NETDEV_TX_OK;
++#endif
++
+ len = skb->len;
+ PRANDOM_ADD_NOISE(skb, dev, txq, len + jiffies);
+ trace_net_dev_start_xmit(skb, dev);
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -60,6 +60,7 @@
+ #include <linux/prefetch.h>
+ #include <linux/if_vlan.h>
+ #include <linux/mpls.h>
++#include <linux/if.h>
+
+ #include <net/protocol.h>
+ #include <net/dst.h>
+@@ -553,6 +554,22 @@ skb_fail:
+ }
+ EXPORT_SYMBOL(__napi_alloc_skb);
+
++struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev,
++ unsigned int length, gfp_t gfp)
++{
++ struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp);
++
++#ifdef CONFIG_ETHERNET_PACKET_MANGLE
++ if (dev && (dev->priv_flags & IFF_NO_IP_ALIGN))
++ return skb;
++#endif
++
++ if (NET_IP_ALIGN && skb)
++ skb_reserve(skb, NET_IP_ALIGN);
++ return skb;
++}
++EXPORT_SYMBOL(__netdev_alloc_skb_ip_align);
++
+ void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off,
+ int size, unsigned int truesize)
+ {
+--- a/net/ethernet/eth.c
++++ b/net/ethernet/eth.c
+@@ -171,6 +171,12 @@ __be16 eth_type_trans(struct sk_buff *sk
+ const struct ethhdr *eth;
+
+ skb->dev = dev;
++
++#ifdef CONFIG_ETHERNET_PACKET_MANGLE
++ if (dev->eth_mangle_rx)
++ dev->eth_mangle_rx(dev, skb);
++#endif
++
+ skb_reset_mac_header(skb);
+
+ eth = (struct ethhdr *)skb->data;
--- /dev/null
+From 5f62951fba63a9f9cfff564209426bdea5fcc371 Mon Sep 17 00:00:00 2001
+From: Alex Marginean <alexandru.marginean@nxp.com>
+Date: Tue, 27 Aug 2019 15:16:56 +0300
+Subject: [PATCH] drivers: net: phy: aquantia: enable AQR112 and AQR412
+
+Adds support for AQR112 and AQR412 which is mostly based on existing code
+with the addition of code configuring the protocol on system side.
+This allows changing the system side protocol without having to deploy a
+different firmware on the PHY.
+
+Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com>
+---
+ drivers/net/phy/aquantia_main.c | 88 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 88 insertions(+)
+
+--- a/drivers/net/phy/aquantia_main.c
++++ b/drivers/net/phy/aquantia_main.c
+@@ -20,9 +20,11 @@
+ #define PHY_ID_AQR105 0x03a1b4a2
+ #define PHY_ID_AQR106 0x03a1b4d0
+ #define PHY_ID_AQR107 0x03a1b4e0
++#define PHY_ID_AQR112 0x03a1b662
+ #define PHY_ID_AQR113C 0x31c31c12
+ #define PHY_ID_AQCS109 0x03a1b5c2
+ #define PHY_ID_AQR405 0x03a1b4b0
++#define PHY_ID_AQR412 0x03a1b712
+ #define PHY_ID_AQR813 0x31c31cb2
+
+ #define MDIO_PHYXS_VEND_IF_STATUS 0xe812
+@@ -123,6 +125,29 @@
+ #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
+ #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
+
++/* registers in MDIO_MMD_VEND1 region */
++#define AQUANTIA_VND1_GLOBAL_SC 0x000
++#define AQUANTIA_VND1_GLOBAL_SC_LP BIT(0xb)
++
++/* global start rate, the protocol associated with this speed is used by default
++ * on SI.
++ */
++#define AQUANTIA_VND1_GSTART_RATE 0x31a
++#define AQUANTIA_VND1_GSTART_RATE_OFF 0
++#define AQUANTIA_VND1_GSTART_RATE_100M 1
++#define AQUANTIA_VND1_GSTART_RATE_1G 2
++#define AQUANTIA_VND1_GSTART_RATE_10G 3
++#define AQUANTIA_VND1_GSTART_RATE_2_5G 4
++#define AQUANTIA_VND1_GSTART_RATE_5G 5
++
++/* SYSCFG registers for 100M, 1G, 2.5G, 5G, 10G */
++#define AQUANTIA_VND1_GSYSCFG_BASE 0x31b
++#define AQUANTIA_VND1_GSYSCFG_100M 0
++#define AQUANTIA_VND1_GSYSCFG_1G 1
++#define AQUANTIA_VND1_GSYSCFG_2_5G 2
++#define AQUANTIA_VND1_GSYSCFG_5G 3
++#define AQUANTIA_VND1_GSYSCFG_10G 4
++
+ struct aqr107_hw_stat {
+ const char *name;
+ int reg;
+@@ -243,6 +268,51 @@ static int aqr_config_aneg(struct phy_de
+ return genphy_c45_check_and_restart_aneg(phydev, changed);
+ }
+
++static struct {
++ u16 syscfg;
++ int cnt;
++ u16 start_rate;
++} aquantia_syscfg[PHY_INTERFACE_MODE_MAX] = {
++ [PHY_INTERFACE_MODE_SGMII] = {0x04b, AQUANTIA_VND1_GSYSCFG_1G,
++ AQUANTIA_VND1_GSTART_RATE_1G},
++ [PHY_INTERFACE_MODE_2500BASEX] = {0x144, AQUANTIA_VND1_GSYSCFG_2_5G,
++ AQUANTIA_VND1_GSTART_RATE_2_5G},
++ [PHY_INTERFACE_MODE_XGMII] = {0x100, AQUANTIA_VND1_GSYSCFG_10G,
++ AQUANTIA_VND1_GSTART_RATE_10G},
++ [PHY_INTERFACE_MODE_USXGMII] = {0x080, AQUANTIA_VND1_GSYSCFG_10G,
++ AQUANTIA_VND1_GSTART_RATE_10G},
++};
++
++/* Sets up protocol on system side before calling aqr_config_aneg */
++static int aqr_config_aneg_set_prot(struct phy_device *phydev)
++{
++ int if_type = phydev->interface;
++ int i;
++
++ if (!aquantia_syscfg[if_type].cnt)
++ return 0;
++
++ /* set PHY in low power mode so we can configure protocols */
++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC,
++ AQUANTIA_VND1_GLOBAL_SC_LP);
++ mdelay(10);
++
++ /* set the default rate to enable the SI link */
++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE,
++ aquantia_syscfg[if_type].start_rate);
++
++ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++)
++ phy_write_mmd(phydev, MDIO_MMD_VEND1,
++ AQUANTIA_VND1_GSYSCFG_BASE + i,
++ aquantia_syscfg[if_type].syscfg);
++
++ /* wake PHY back up */
++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0);
++ mdelay(10);
++
++ return aqr_config_aneg(phydev);
++}
++
+ static int aqr_config_intr(struct phy_device *phydev)
+ {
+ bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED;
+@@ -738,6 +808,30 @@ static struct phy_driver aqr_driver[] =
+ .get_stats = aqr107_get_stats,
+ .link_change_notify = aqr107_link_change_notify,
+ },
++{
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112),
++ .name = "Aquantia AQR112",
++ .probe = aqr107_probe,
++ .config_aneg = aqr_config_aneg_set_prot,
++ .config_intr = aqr_config_intr,
++ .ack_interrupt = aqr_ack_interrupt,
++ .read_status = aqr107_read_status,
++ .get_sset_count = aqr107_get_sset_count,
++ .get_strings = aqr107_get_strings,
++ .get_stats = aqr107_get_stats,
++},
++{
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
++ .name = "Aquantia AQR412",
++ .probe = aqr107_probe,
++ .config_aneg = aqr_config_aneg_set_prot,
++ .config_intr = aqr_config_intr,
++ .ack_interrupt = aqr_ack_interrupt,
++ .read_status = aqr107_read_status,
++ .get_sset_count = aqr107_get_sset_count,
++ .get_strings = aqr107_get_strings,
++ .get_stats = aqr107_get_stats,
++},
+ };
+
+ module_phy_driver(aqr_driver);
+@@ -748,9 +842,11 @@ static struct mdio_device_id __maybe_unu
+ { 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_AQR112) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
+ { }
+ };
--- /dev/null
+From 5f008cb22f60da4e10375f22266c1a4e20b1252e Mon Sep 17 00:00:00 2001
+From: Alex Marginean <alexandru.marginean@nxp.com>
+Date: Fri, 20 Sep 2019 18:22:52 +0300
+Subject: [PATCH] drivers: net: phy: aquantia: fix system side protocol
+ misconfiguration
+
+Do not set up protocols for speeds that are not supported by FW. Enabling
+these protocols leads to link issues on system side.
+
+Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com>
+---
+ drivers/net/phy/aquantia_main.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/aquantia_main.c
++++ b/drivers/net/phy/aquantia_main.c
+@@ -301,10 +301,16 @@ static int aqr_config_aneg_set_prot(stru
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE,
+ aquantia_syscfg[if_type].start_rate);
+
+- for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++)
++ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) {
++ u16 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++ AQUANTIA_VND1_GSYSCFG_BASE + i);
++ if (!reg)
++ continue;
++
+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
+ AQUANTIA_VND1_GSYSCFG_BASE + i,
+ aquantia_syscfg[if_type].syscfg);
++ }
+
+ /* wake PHY back up */
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0);
--- /dev/null
+From 2e677e4ae8f8330f68013163b060d0fda3a43095 Mon Sep 17 00:00:00 2001
+From: "Langer, Thomas" <tlanger@maxlinear.com>
+Date: Fri, 9 Jul 2021 17:36:46 +0200
+Subject: [PATCH] PONRTSYS-8842: aquantia: Add AQR113 driver support
+
+Add a new entry for AQR113 PHY_ID
+---
+ drivers/net/phy/aquantia_main.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/net/phy/aquantia_main.c
++++ b/drivers/net/phy/aquantia_main.c
+@@ -21,6 +21,7 @@
+ #define PHY_ID_AQR106 0x03a1b4d0
+ #define PHY_ID_AQR107 0x03a1b4e0
+ #define PHY_ID_AQR112 0x03a1b662
++#define PHY_ID_AQR113 0x31c31c40
+ #define PHY_ID_AQR113C 0x31c31c12
+ #define PHY_ID_AQCS109 0x03a1b5c2
+ #define PHY_ID_AQR405 0x03a1b4b0
+@@ -827,6 +828,14 @@ static struct phy_driver aqr_driver[] =
+ .get_stats = aqr107_get_stats,
+ },
+ {
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
++ .name = "Aquantia AQR113",
++ .config_aneg = aqr_config_aneg,
++ .config_intr = aqr_config_intr,
++ .ack_interrupt = aqr_ack_interrupt,
++ .read_status = aqr107_read_status,
++},
++{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
+ .name = "Aquantia AQR412",
+ .probe = aqr107_probe,
+@@ -849,6 +858,7 @@ static struct mdio_device_id __maybe_unu
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
--- /dev/null
+From 3b92ee7b7899b6beffb2b484c58326e36612a873 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 23 Dec 2021 14:52:56 +0000
+Subject: [PATCH] net: phy: aquantia: add PHY_ID for AQR112R
+
+As advised by Ian Chang this PHY is used in Puzzle devices.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/aquantia_main.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/net/phy/aquantia_main.c
++++ b/drivers/net/phy/aquantia_main.c
+@@ -21,6 +21,8 @@
+ #define PHY_ID_AQR106 0x03a1b4d0
+ #define PHY_ID_AQR107 0x03a1b4e0
+ #define PHY_ID_AQR112 0x03a1b662
++#define PHY_ID_AQR112C 0x03a1b790
++#define PHY_ID_AQR112R 0x31c31d12
+ #define PHY_ID_AQR113 0x31c31c40
+ #define PHY_ID_AQR113C 0x31c31c12
+ #define PHY_ID_AQCS109 0x03a1b5c2
+@@ -828,6 +830,30 @@ static struct phy_driver aqr_driver[] =
+ .get_stats = aqr107_get_stats,
+ },
+ {
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112C),
++ .name = "Aquantia AQR112C",
++ .probe = aqr107_probe,
++ .config_aneg = aqr_config_aneg_set_prot,
++ .config_intr = aqr_config_intr,
++ .ack_interrupt = aqr_ack_interrupt,
++ .read_status = aqr107_read_status,
++ .get_sset_count = aqr107_get_sset_count,
++ .get_strings = aqr107_get_strings,
++ .get_stats = aqr107_get_stats,
++},
++{
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112R),
++ .name = "Aquantia AQR112R",
++ .probe = aqr107_probe,
++ .config_aneg = aqr_config_aneg_set_prot,
++ .config_intr = aqr_config_intr,
++ .ack_interrupt = aqr_ack_interrupt,
++ .read_status = aqr107_read_status,
++ .get_sset_count = aqr107_get_sset_count,
++ .get_strings = aqr107_get_strings,
++ .get_stats = aqr107_get_stats,
++},
++{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
+ .name = "Aquantia AQR113",
+ .config_aneg = aqr_config_aneg,
+@@ -858,6 +884,8 @@ static struct mdio_device_id __maybe_unu
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112C) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112R) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
--- /dev/null
+From 82985725e071f2a5735052f18e109a32aeac3a0b Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Sun, 26 Jul 2020 02:38:31 +0200
+Subject: [PATCH] net: usb: r8152: add LED configuration from OF
+
+This adds the ability to configure the LED configuration register using
+OF. This way, the correct value for board specific LED configuration can
+be determined.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/usb/r8152.c | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -11,6 +11,7 @@
+ #include <linux/mii.h>
+ #include <linux/ethtool.h>
+ #include <linux/usb.h>
++#include <linux/of.h>
+ #include <linux/crc32.h>
+ #include <linux/if_vlan.h>
+ #include <linux/uaccess.h>
+@@ -5301,6 +5302,22 @@ static void rtl_tally_reset(struct r8152
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data);
+ }
+
++static int r8152_led_configuration(struct r8152 *tp)
++{
++ u32 led_data;
++ int ret;
++
++ ret = of_property_read_u32(tp->udev->dev.of_node, "realtek,led-data",
++ &led_data);
++
++ if (ret)
++ return ret;
++
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LEDSEL, led_data);
++
++ return 0;
++}
++
+ static void r8152b_init(struct r8152 *tp)
+ {
+ u32 ocp_data;
+@@ -5342,6 +5359,8 @@ static void r8152b_init(struct r8152 *tp
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
+ ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
+ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
++
++ r8152_led_configuration(tp);
+ }
+
+ static void r8153_init(struct r8152 *tp)
+@@ -5487,6 +5506,8 @@ static void r8153_init(struct r8152 *tp)
+ tp->coalesce = COALESCE_SLOW;
+ break;
+ }
++
++ r8152_led_configuration(tp);
+ }
+
+ static void r8153b_init(struct r8152 *tp)
+@@ -5573,6 +5594,8 @@ static void r8153b_init(struct r8152 *tp
+ rtl_tally_reset(tp);
+
+ tp->coalesce = 15000; /* 15 us */
++
++ r8152_led_configuration(tp);
+ }
+
+ static int rtl8152_pre_reset(struct usb_interface *intf)
--- /dev/null
+From 3ee05f4aa64fc86af3be5bc176ba5808de9260a7 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Sun, 26 Jul 2020 15:30:33 +0200
+Subject: [PATCH] dt-bindings: net: add RTL8152 binding documentation
+
+Add binding documentation for the Realtek RTL8152 / RTL8153 USB ethernet
+adapters.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ .../bindings/net/realtek,rtl8152.yaml | 36 +++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/net/realtek,rtl8152.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/realtek,rtl8152.yaml
+@@ -0,0 +1,36 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/net/realtek,rtl8152.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Realtek RTL8152/RTL8153 series USB ethernet
++
++maintainers:
++ - David Bauer <mail@david-bauer.net>
++
++properties:
++ compatible:
++ oneOf:
++ - items:
++ - enum:
++ - realtek,rtl8152
++ - realtek,rtl8153
++
++ reg:
++ description: The device number on the USB bus
++
++ realtek,led-data:
++ description: Value to be written to the LED configuration register.
++
++required:
++ - compatible
++ - reg
++
++examples:
++ - |
++ usb-eth@2 {
++ compatible = "realtek,rtl8153";
++ reg = <2>;
++ realtek,led-data = <0x87>;
++ };
+\ No newline at end of file
--- /dev/null
+From 3cb240533ab787899dc7f17aa7d6c5b4810e2e58 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Fri, 7 Jul 2017 17:26:01 +0200
+Subject: bcm53xx: bgmac: use srab switch driver
+
+use the srab switch driver on these SoCs.
+
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+---
+ drivers/net/ethernet/broadcom/bgmac-bcma.c | 1 +
+ drivers/net/ethernet/broadcom/bgmac.c | 24 ++++++++++++++++++++++++
+ drivers/net/ethernet/broadcom/bgmac.h | 4 ++++
+ 3 files changed, 29 insertions(+)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+@@ -280,6 +280,7 @@ static int bgmac_probe(struct bcma_devic
+ bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST;
+ bgmac->feature_flags |= BGMAC_FEAT_NO_RESET;
+ bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500;
++ bgmac->feature_flags |= BGMAC_FEAT_SRAB;
+ break;
+ default:
+ bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST;
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -12,6 +12,7 @@
+ #include <linux/bcma/bcma.h>
+ #include <linux/etherdevice.h>
+ #include <linux/interrupt.h>
++#include <linux/platform_data/b53.h>
+ #include <linux/bcm47xx_nvram.h>
+ #include <linux/phy.h>
+ #include <linux/phy_fixed.h>
+@@ -1408,6 +1409,17 @@ static const struct ethtool_ops bgmac_et
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
+ };
+
++static struct b53_platform_data bgmac_b53_pdata = {
++};
++
++static struct platform_device bgmac_b53_dev = {
++ .name = "b53-srab-switch",
++ .id = -1,
++ .dev = {
++ .platform_data = &bgmac_b53_pdata,
++ },
++};
++
+ /**************************************************
+ * MII
+ **************************************************/
+@@ -1542,6 +1554,14 @@ int bgmac_enet_probe(struct bgmac *bgmac
+ /* Omit FCS from max MTU size */
+ net_dev->max_mtu = BGMAC_RX_MAX_FRAME_SIZE - ETH_FCS_LEN;
+
++ if ((bgmac->feature_flags & BGMAC_FEAT_SRAB) && !bgmac_b53_pdata.regs) {
++ bgmac_b53_pdata.regs = ioremap(0x18007000, 0x1000);
++
++ err = platform_device_register(&bgmac_b53_dev);
++ if (!err)
++ bgmac->b53_device = &bgmac_b53_dev;
++ }
++
+ err = register_netdev(bgmac->net_dev);
+ if (err) {
+ dev_err(bgmac->dev, "Cannot register net device\n");
+@@ -1564,6 +1584,10 @@ EXPORT_SYMBOL_GPL(bgmac_enet_probe);
+
+ void bgmac_enet_remove(struct bgmac *bgmac)
+ {
++ if (bgmac->b53_device)
++ platform_device_unregister(&bgmac_b53_dev);
++ bgmac->b53_device = NULL;
++
+ unregister_netdev(bgmac->net_dev);
+ phy_disconnect(bgmac->net_dev->phydev);
+ netif_napi_del(&bgmac->napi);
+--- a/drivers/net/ethernet/broadcom/bgmac.h
++++ b/drivers/net/ethernet/broadcom/bgmac.h
+@@ -428,6 +428,7 @@
+ #define BGMAC_FEAT_CC4_IF_SW_TYPE_RGMII BIT(18)
+ #define BGMAC_FEAT_CC7_IF_TYPE_RGMII BIT(19)
+ #define BGMAC_FEAT_IDM_MASK BIT(20)
++#define BGMAC_FEAT_SRAB BIT(21)
+
+ struct bgmac_slot_info {
+ union {
+@@ -533,6 +534,9 @@ struct bgmac {
+ void (*cmn_maskset32)(struct bgmac *bgmac, u16 offset, u32 mask,
+ u32 set);
+ int (*phy_connect)(struct bgmac *bgmac);
++
++ /* platform device for associated switch */
++ struct platform_device *b53_device;
+ };
+
+ struct bgmac *bgmac_alloc(struct device *dev);
--- /dev/null
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -1024,6 +1024,7 @@ static const struct usb_device_id produc
+ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */
+ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)}, /* Quectel EM160R-GL */
+ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */
++ {QMI_MATCH_FF_FF_FF(0x05c6, 0xf601)}, /* MeigLink SLM750 */
+
+ /* 3. Combined interface devices matching on interface number */
+ {QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -243,6 +243,8 @@ static void option_instat_callback(struc
+ #define UBLOX_PRODUCT_R6XX 0x90fa
+ /* These Yuga products use Qualcomm's vendor ID */
+ #define YUGA_PRODUCT_CLM920_NC5 0x9625
++/* These MeigLink products use Qualcomm's vendor ID */
++#define MEIGLINK_PRODUCT_SLM750 0xf601
+
+ #define QUECTEL_VENDOR_ID 0x2c7c
+ /* These Quectel products use Quectel's vendor ID */
+@@ -1127,6 +1129,11 @@ static const struct usb_device_id option
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG95, 0xff, 0, 0) },
+ { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96),
+ .driver_info = RSVD(4) },
++ /* Meiglink products using Qualcomm vendor ID */
++ // Works OK. In case of some issues check macros that are used by Quectel Products
++ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0xff, 0xff),
++ .driver_info = NUMEP2 },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff),
+ .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) },
--- /dev/null
+From cc809a441d8f2924f785eb863dfa6aef47a25b0b Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 12 Aug 2014 20:49:27 +0200
+Subject: [PATCH 30/36] GPIO: add named gpio exports
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+--- a/drivers/gpio/gpiolib-of.c
++++ b/drivers/gpio/gpiolib-of.c
+@@ -19,6 +19,8 @@
+ #include <linux/pinctrl/pinctrl.h>
+ #include <linux/slab.h>
+ #include <linux/gpio/machine.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
+
+ #include "gpiolib.h"
+ #include "gpiolib-of.h"
+@@ -1039,3 +1041,72 @@ void of_gpiochip_remove(struct gpio_chip
+ {
+ of_node_put(chip->of_node);
+ }
++
++#ifdef CONFIG_GPIO_SYSFS
++
++static struct of_device_id gpio_export_ids[] = {
++ { .compatible = "gpio-export" },
++ { /* sentinel */ }
++};
++
++static int of_gpio_export_probe(struct platform_device *pdev)
++{
++ struct device_node *np = pdev->dev.of_node;
++ struct device_node *cnp;
++ u32 val;
++ int nb = 0;
++
++ for_each_child_of_node(np, cnp) {
++ const char *name = NULL;
++ int gpio;
++ bool dmc;
++ int max_gpio = 1;
++ int i;
++
++ of_property_read_string(cnp, "gpio-export,name", &name);
++
++ if (!name)
++ max_gpio = of_gpio_count(cnp);
++
++ for (i = 0; i < max_gpio; i++) {
++ unsigned flags = 0;
++ enum of_gpio_flags of_flags;
++
++ gpio = of_get_gpio_flags(cnp, i, &of_flags);
++ if (!gpio_is_valid(gpio))
++ return gpio;
++
++ if (of_flags == OF_GPIO_ACTIVE_LOW)
++ flags |= GPIOF_ACTIVE_LOW;
++
++ if (!of_property_read_u32(cnp, "gpio-export,output", &val))
++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
++ else
++ flags |= GPIOF_IN;
++
++ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
++ continue;
++
++ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
++ gpio_export_with_name(gpio, dmc, name);
++ nb++;
++ }
++ }
++
++ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);
++
++ return 0;
++}
++
++static struct platform_driver gpio_export_driver = {
++ .driver = {
++ .name = "gpio-export",
++ .owner = THIS_MODULE,
++ .of_match_table = of_match_ptr(gpio_export_ids),
++ },
++ .probe = of_gpio_export_probe,
++};
++
++module_platform_driver(gpio_export_driver);
++
++#endif
+--- a/include/asm-generic/gpio.h
++++ b/include/asm-generic/gpio.h
+@@ -125,6 +125,12 @@ static inline int gpio_export(unsigned g
+ return gpiod_export(gpio_to_desc(gpio), direction_may_change);
+ }
+
++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
++static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name)
++{
++ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name);
++}
++
+ static inline int gpio_export_link(struct device *dev, const char *name,
+ unsigned gpio)
+ {
+--- a/include/linux/gpio/consumer.h
++++ b/include/linux/gpio/consumer.h
+@@ -715,6 +715,7 @@ static inline void devm_acpi_dev_remove_
+
+ #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS)
+
++int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
+ int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
+ int gpiod_export_link(struct device *dev, const char *name,
+ struct gpio_desc *desc);
+@@ -722,6 +723,13 @@ void gpiod_unexport(struct gpio_desc *de
+
+ #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
+
++static inline int _gpiod_export(struct gpio_desc *desc,
++ bool direction_may_change,
++ const char *name)
++{
++ return -ENOSYS;
++}
++
+ static inline int gpiod_export(struct gpio_desc *desc,
+ bool direction_may_change)
+ {
+--- a/drivers/gpio/gpiolib-sysfs.c
++++ b/drivers/gpio/gpiolib-sysfs.c
+@@ -572,7 +572,7 @@ static struct class gpio_class = {
+ *
+ * Returns zero on success, else an error.
+ */
+-int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name)
+ {
+ struct gpio_chip *chip;
+ struct gpio_device *gdev;
+@@ -634,6 +634,8 @@ int gpiod_export(struct gpio_desc *desc,
+ offset = gpio_chip_hwgpio(desc);
+ if (chip->names && chip->names[offset])
+ ioname = chip->names[offset];
++ if (name)
++ ioname = name;
+
+ dev = device_create_with_groups(&gpio_class, &gdev->dev,
+ MKDEV(0, 0), data, gpio_groups,
+@@ -655,6 +657,12 @@ err_unlock:
+ gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+ return status;
+ }
++EXPORT_SYMBOL_GPL(__gpiod_export);
++
++int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
++{
++ return __gpiod_export(desc, direction_may_change, NULL);
++}
+ EXPORT_SYMBOL_GPL(gpiod_export);
+
+ static int match_export(struct device *dev, const void *desc)
--- /dev/null
+From 3b6115d6b57a263bdc8c9b1df273bd4a7955eead Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 8 Jul 2017 08:16:31 +0200
+Subject: debloat: add some debloat patches, strip down procfs and make O_DIRECT support optional, saves ~15K after lzma on MIPS
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/Kconfig | 3 +++
+ net/core/Makefile | 3 ++-
+ net/core/sock.c | 2 ++
+ net/ipv4/Kconfig | 1 +
+ net/netlink/Kconfig | 1 +
+ net/packet/Kconfig | 1 +
+ net/unix/Kconfig | 1 +
+ 7 files changed, 11 insertions(+), 1 deletion(-)
+
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -104,6 +104,9 @@ source "net/mptcp/Kconfig"
+
+ endif # if INET
+
++config SOCK_DIAG
++ bool
++
+ config NETWORK_SECMARK
+ bool "Security Marking"
+ help
+--- a/net/core/Makefile
++++ b/net/core/Makefile
+@@ -10,9 +10,10 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.
+
+ obj-y += dev.o dev_addr_lists.o dst.o netevent.o \
+ neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
+- sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \
++ dev_ioctl.o tso.o sock_reuseport.o \
+ fib_notifier.o xdp.o flow_offload.o
+
++obj-$(CONFIG_SOCK_DIAG) += sock_diag.o
+ obj-y += net-sysfs.o
+ obj-$(CONFIG_PAGE_POOL) += page_pool.o
+ obj-$(CONFIG_PROC_FS) += net-procfs.o
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -114,6 +114,7 @@
+ #include <linux/memcontrol.h>
+ #include <linux/prefetch.h>
+ #include <linux/compat.h>
++#include <linux/cookie.h>
+
+ #include <linux/uaccess.h>
+
+@@ -141,6 +142,7 @@
+
+ static DEFINE_MUTEX(proto_list_mutex);
+ static LIST_HEAD(proto_list);
++DEFINE_COOKIE(sock_cookie);
+
+ static void sock_inuse_add(struct net *net, int val);
+
+@@ -526,6 +528,18 @@ discard_and_relse:
+ }
+ EXPORT_SYMBOL(__sk_receive_skb);
+
++u64 __sock_gen_cookie(struct sock *sk)
++{
++ while (1) {
++ u64 res = atomic64_read(&sk->sk_cookie);
++
++ if (res)
++ return res;
++ res = gen_cookie_next(&sock_cookie);
++ atomic64_cmpxchg(&sk->sk_cookie, 0, res);
++ }
++}
++
+ struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie)
+ {
+ struct dst_entry *dst = __sk_dst_get(sk);
+@@ -1834,9 +1848,11 @@ static void __sk_free(struct sock *sk)
+ if (likely(sk->sk_net_refcnt))
+ sock_inuse_add(sock_net(sk), -1);
+
++#ifdef CONFIG_SOCK_DIAG
+ if (unlikely(sk->sk_net_refcnt && sock_diag_has_destroy_listeners(sk)))
+ sock_diag_broadcast_destroy(sk);
+ else
++#endif
+ sk_destruct(sk);
+ }
+
+--- a/net/core/sock_diag.c
++++ b/net/core/sock_diag.c
+@@ -11,7 +11,6 @@
+ #include <linux/tcp.h>
+ #include <linux/workqueue.h>
+ #include <linux/nospec.h>
+-#include <linux/cookie.h>
+ #include <linux/inet_diag.h>
+ #include <linux/sock_diag.h>
+
+@@ -20,20 +19,6 @@ static int (*inet_rcv_compat)(struct sk_
+ static DEFINE_MUTEX(sock_diag_table_mutex);
+ static struct workqueue_struct *broadcast_wq;
+
+-DEFINE_COOKIE(sock_cookie);
+-
+-u64 __sock_gen_cookie(struct sock *sk)
+-{
+- while (1) {
+- u64 res = atomic64_read(&sk->sk_cookie);
+-
+- if (res)
+- return res;
+- res = gen_cookie_next(&sock_cookie);
+- atomic64_cmpxchg(&sk->sk_cookie, 0, res);
+- }
+-}
+-
+ int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie)
+ {
+ u64 res;
+--- a/net/ipv4/Kconfig
++++ b/net/ipv4/Kconfig
+@@ -414,6 +414,7 @@ config INET_TUNNEL
+
+ config INET_DIAG
+ tristate "INET: socket monitoring interface"
++ select SOCK_DIAG
+ default y
+ help
+ Support for INET (TCP, DCCP, etc) socket monitoring interface used by
+--- a/net/netlink/Kconfig
++++ b/net/netlink/Kconfig
+@@ -5,6 +5,7 @@
+
+ config NETLINK_DIAG
+ tristate "NETLINK: socket monitoring interface"
++ select SOCK_DIAG
+ default n
+ help
+ Support for NETLINK socket monitoring interface used by the ss tool.
+--- a/net/packet/Kconfig
++++ b/net/packet/Kconfig
+@@ -19,6 +19,7 @@ config PACKET
+ config PACKET_DIAG
+ tristate "Packet: sockets monitoring interface"
+ depends on PACKET
++ select SOCK_DIAG
+ default n
+ help
+ Support for PF_PACKET sockets monitoring interface used by the ss tool.
+--- a/net/unix/Kconfig
++++ b/net/unix/Kconfig
+@@ -28,6 +28,7 @@ config UNIX_SCM
+ config UNIX_DIAG
+ tristate "UNIX: socket monitoring interface"
+ depends on UNIX
++ select SOCK_DIAG
+ default n
+ help
+ Support for UNIX socket monitoring interface used by the ss tool.
--- /dev/null
+From 9e3f1d0805b2d919904dd9a4ff0d956314cc3cba Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 8 Jul 2017 08:20:09 +0200
+Subject: debloat: procfs
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ fs/locks.c | 2 ++
+ fs/proc/Kconfig | 5 +++++
+ fs/proc/consoles.c | 3 +++
+ fs/proc/proc_tty.c | 11 ++++++++++-
+ include/net/snmp.h | 18 +++++++++++++++++-
+ ipc/msg.c | 3 +++
+ ipc/sem.c | 2 ++
+ ipc/shm.c | 2 ++
+ ipc/util.c | 3 +++
+ kernel/exec_domain.c | 2 ++
+ kernel/irq/proc.c | 9 +++++++++
+ kernel/time/timer_list.c | 2 ++
+ mm/vmalloc.c | 2 ++
+ mm/vmstat.c | 8 +++++---
+ net/8021q/vlanproc.c | 6 ++++++
+ net/core/net-procfs.c | 18 ++++++++++++------
+ net/core/sock.c | 2 ++
+ net/ipv4/fib_trie.c | 18 ++++++++++++------
+ net/ipv4/proc.c | 3 +++
+ net/ipv4/route.c | 3 +++
+ 20 files changed, 105 insertions(+), 17 deletions(-)
+
+--- a/fs/locks.c
++++ b/fs/locks.c
+@@ -2993,6 +2993,8 @@ static const struct seq_operations locks
+
+ static int __init proc_locks_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
+ proc_create_seq_private("locks", 0, NULL, &locks_seq_operations,
+ sizeof(struct locks_iterator), NULL);
+ return 0;
+--- a/fs/proc/Kconfig
++++ b/fs/proc/Kconfig
+@@ -100,6 +100,11 @@ config PROC_CHILDREN
+ Say Y if you are running any user-space software which takes benefit from
+ this interface. For example, rkt is such a piece of software.
+
++config PROC_STRIPPED
++ default n
++ depends on EXPERT
++ bool "Strip non-essential /proc functionality to reduce code size"
++
+ config PROC_PID_ARCH_STATUS
+ def_bool n
+ depends on PROC_FS
+--- a/fs/proc/consoles.c
++++ b/fs/proc/consoles.c
+@@ -92,6 +92,9 @@ static const struct seq_operations conso
+
+ static int __init proc_consoles_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
++
+ proc_create_seq("consoles", 0, NULL, &consoles_op);
+ return 0;
+ }
+--- a/fs/proc/proc_tty.c
++++ b/fs/proc/proc_tty.c
+@@ -133,7 +133,10 @@ static const struct seq_operations tty_d
+ void proc_tty_register_driver(struct tty_driver *driver)
+ {
+ struct proc_dir_entry *ent;
+-
++
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ if (!driver->driver_name || driver->proc_entry ||
+ !driver->ops->proc_show)
+ return;
+@@ -150,6 +153,9 @@ void proc_tty_unregister_driver(struct t
+ {
+ struct proc_dir_entry *ent;
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ ent = driver->proc_entry;
+ if (!ent)
+ return;
+@@ -164,6 +170,9 @@ void proc_tty_unregister_driver(struct t
+ */
+ void __init proc_tty_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ if (!proc_mkdir("tty", NULL))
+ return;
+ proc_mkdir("tty/ldisc", NULL); /* Preserved: it's userspace visible */
+--- a/include/net/snmp.h
++++ b/include/net/snmp.h
+@@ -124,6 +124,21 @@ struct linux_tls_mib {
+ #define DECLARE_SNMP_STAT(type, name) \
+ extern __typeof__(type) __percpu *name
+
++#ifdef CONFIG_PROC_STRIPPED
++#define __SNMP_STATS_DUMMY(mib) \
++ do { (void) mib->mibs[0]; } while(0)
++
++#define __SNMP_INC_STATS(mib, field) __SNMP_STATS_DUMMY(mib)
++#define SNMP_INC_STATS_ATOMIC_LONG(mib, field) __SNMP_STATS_DUMMY(mib)
++#define SNMP_INC_STATS(mib, field) __SNMP_STATS_DUMMY(mib)
++#define SNMP_DEC_STATS(mib, field) __SNMP_STATS_DUMMY(mib)
++#define __SNMP_ADD_STATS(mib, field, addend) __SNMP_STATS_DUMMY(mib)
++#define SNMP_ADD_STATS(mib, field, addend) __SNMP_STATS_DUMMY(mib)
++#define SNMP_UPD_PO_STATS(mib, basefield, addend) __SNMP_STATS_DUMMY(mib)
++#define __SNMP_UPD_PO_STATS(mib, basefield, addend) __SNMP_STATS_DUMMY(mib)
++
++#else
++
+ #define __SNMP_INC_STATS(mib, field) \
+ __this_cpu_inc(mib->mibs[field])
+
+@@ -154,8 +169,9 @@ struct linux_tls_mib {
+ __this_cpu_add(ptr[basefield##OCTETS], addend); \
+ } while (0)
+
++#endif
+
+-#if BITS_PER_LONG==32
++#if (BITS_PER_LONG==32) && !defined(CONFIG_PROC_STRIPPED)
+
+ #define __SNMP_ADD_STATS64(mib, field, addend) \
+ do { \
+--- a/ipc/msg.c
++++ b/ipc/msg.c
+@@ -1350,6 +1350,9 @@ void __init msg_init(void)
+ {
+ msg_init_ns(&init_ipc_ns);
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ ipc_init_proc_interface("sysvipc/msg",
+ " key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime\n",
+ IPC_MSG_IDS, sysvipc_msg_proc_show);
+--- a/ipc/sem.c
++++ b/ipc/sem.c
+@@ -266,6 +266,8 @@ void sem_exit_ns(struct ipc_namespace *n
+ void __init sem_init(void)
+ {
+ sem_init_ns(&init_ipc_ns);
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
+ ipc_init_proc_interface("sysvipc/sem",
+ " key semid perms nsems uid gid cuid cgid otime ctime\n",
+ IPC_SEM_IDS, sysvipc_sem_proc_show);
+--- a/ipc/shm.c
++++ b/ipc/shm.c
+@@ -154,6 +154,8 @@ pure_initcall(ipc_ns_init);
+
+ void __init shm_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
+ ipc_init_proc_interface("sysvipc/shm",
+ #if BITS_PER_LONG <= 32
+ " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n",
+--- a/ipc/util.c
++++ b/ipc/util.c
+@@ -140,6 +140,9 @@ void __init ipc_init_proc_interface(cons
+ struct proc_dir_entry *pde;
+ struct ipc_proc_iface *iface;
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ iface = kmalloc(sizeof(*iface), GFP_KERNEL);
+ if (!iface)
+ return;
+--- a/kernel/exec_domain.c
++++ b/kernel/exec_domain.c
+@@ -29,6 +29,8 @@ static int execdomains_proc_show(struct
+
+ static int __init proc_execdomains_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
+ proc_create_single("execdomains", 0, NULL, execdomains_proc_show);
+ return 0;
+ }
+--- a/kernel/irq/proc.c
++++ b/kernel/irq/proc.c
+@@ -341,6 +341,9 @@ void register_irq_proc(unsigned int irq,
+ void __maybe_unused *irqp = (void *)(unsigned long) irq;
+ char name [MAX_NAMELEN];
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP))
++ return;
++
+ if (!root_irq_dir || (desc->irq_data.chip == &no_irq_chip))
+ return;
+
+@@ -394,6 +397,9 @@ void unregister_irq_proc(unsigned int ir
+ {
+ char name [MAX_NAMELEN];
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP))
++ return;
++
+ if (!root_irq_dir || !desc->dir)
+ return;
+ #ifdef CONFIG_SMP
+@@ -432,6 +438,9 @@ void init_irq_proc(void)
+ unsigned int irq;
+ struct irq_desc *desc;
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP))
++ return;
++
+ /* create /proc/irq */
+ root_irq_dir = proc_mkdir("irq", NULL);
+ if (!root_irq_dir)
+--- a/kernel/time/timer_list.c
++++ b/kernel/time/timer_list.c
+@@ -370,6 +370,8 @@ static int __init init_timer_list_procfs
+ {
+ struct proc_dir_entry *pe;
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
+ pe = proc_create_seq_private("timer_list", 0400, NULL, &timer_list_sops,
+ sizeof(struct timer_list_iter), NULL);
+ if (!pe)
+--- a/mm/vmalloc.c
++++ b/mm/vmalloc.c
+@@ -3572,6 +3572,8 @@ static const struct seq_operations vmall
+
+ static int __init proc_vmalloc_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
+ if (IS_ENABLED(CONFIG_NUMA))
+ proc_create_seq_private("vmallocinfo", 0400, NULL,
+ &vmalloc_op,
+--- a/mm/vmstat.c
++++ b/mm/vmstat.c
+@@ -2044,10 +2044,12 @@ void __init init_mm_internals(void)
+ start_shepherd_timer();
+ #endif
+ #ifdef CONFIG_PROC_FS
+- proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op);
+- proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) {
++ proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op);
++ proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op);
++ proc_create_seq("zoneinfo", 0444, NULL, &zoneinfo_op);
++ }
+ proc_create_seq("vmstat", 0444, NULL, &vmstat_op);
+- proc_create_seq("zoneinfo", 0444, NULL, &zoneinfo_op);
+ #endif
+ }
+
+--- a/net/8021q/vlanproc.c
++++ b/net/8021q/vlanproc.c
+@@ -93,6 +93,9 @@ void vlan_proc_cleanup(struct net *net)
+ {
+ struct vlan_net *vn = net_generic(net, vlan_net_id);
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ if (vn->proc_vlan_conf)
+ remove_proc_entry(name_conf, vn->proc_vlan_dir);
+
+@@ -112,6 +115,9 @@ int __net_init vlan_proc_init(struct net
+ {
+ struct vlan_net *vn = net_generic(net, vlan_net_id);
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
++
+ vn->proc_vlan_dir = proc_net_mkdir(net, name_root, net->proc_net);
+ if (!vn->proc_vlan_dir)
+ goto err;
+--- a/net/core/net-procfs.c
++++ b/net/core/net-procfs.c
+@@ -320,10 +320,12 @@ static int __net_init dev_proc_net_init(
+ if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops,
+ sizeof(struct seq_net_private)))
+ goto out;
+- if (!proc_create_seq("softnet_stat", 0444, net->proc_net,
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) &&
++ !proc_create_seq("softnet_stat", 0444, net->proc_net,
+ &softnet_seq_ops))
+ goto out_dev;
+- if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) &&
++ !proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
+ sizeof(struct seq_net_private)))
+ goto out_softnet;
+
+@@ -333,9 +335,11 @@ static int __net_init dev_proc_net_init(
+ out:
+ return rc;
+ out_ptype:
+- remove_proc_entry("ptype", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED))
++ remove_proc_entry("ptype", net->proc_net);
+ out_softnet:
+- remove_proc_entry("softnet_stat", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED))
++ remove_proc_entry("softnet_stat", net->proc_net);
+ out_dev:
+ remove_proc_entry("dev", net->proc_net);
+ goto out;
+@@ -345,8 +349,10 @@ static void __net_exit dev_proc_net_exit
+ {
+ wext_proc_exit(net);
+
+- remove_proc_entry("ptype", net->proc_net);
+- remove_proc_entry("softnet_stat", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) {
++ remove_proc_entry("ptype", net->proc_net);
++ remove_proc_entry("softnet_stat", net->proc_net);
++ }
+ remove_proc_entry("dev", net->proc_net);
+ }
+
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -3699,6 +3699,8 @@ static __net_initdata struct pernet_oper
+
+ static int __init proto_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
+ return register_pernet_subsys(&proto_net_ops);
+ }
+
+--- a/net/ipv4/fib_trie.c
++++ b/net/ipv4/fib_trie.c
+@@ -2986,11 +2986,13 @@ static const struct seq_operations fib_r
+
+ int __net_init fib_proc_init(struct net *net)
+ {
+- if (!proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops,
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) &&
++ !proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops,
+ sizeof(struct fib_trie_iter)))
+ goto out1;
+
+- if (!proc_create_net_single("fib_triestat", 0444, net->proc_net,
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) &&
++ !proc_create_net_single("fib_triestat", 0444, net->proc_net,
+ fib_triestat_seq_show, NULL))
+ goto out2;
+
+@@ -3001,17 +3003,21 @@ int __net_init fib_proc_init(struct net
+ return 0;
+
+ out3:
+- remove_proc_entry("fib_triestat", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED))
++ remove_proc_entry("fib_triestat", net->proc_net);
+ out2:
+- remove_proc_entry("fib_trie", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED))
++ remove_proc_entry("fib_trie", net->proc_net);
+ out1:
+ return -ENOMEM;
+ }
+
+ void __net_exit fib_proc_exit(struct net *net)
+ {
+- remove_proc_entry("fib_trie", net->proc_net);
+- remove_proc_entry("fib_triestat", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) {
++ remove_proc_entry("fib_trie", net->proc_net);
++ remove_proc_entry("fib_triestat", net->proc_net);
++ }
+ remove_proc_entry("route", net->proc_net);
+ }
+
+--- a/net/ipv4/proc.c
++++ b/net/ipv4/proc.c
+@@ -528,5 +528,8 @@ static __net_initdata struct pernet_oper
+
+ int __init ip_misc_proc_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
++
+ return register_pernet_subsys(&ip_proc_ops);
+ }
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -410,6 +410,9 @@ static struct pernet_operations ip_rt_pr
+
+ static int __init ip_rt_proc_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
++
+ return register_pernet_subsys(&ip_rt_proc_ops);
+ }
+
--- /dev/null
+From e3692cb2fcd5ba1244512a0f43b8118f65f1c375 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 8 Jul 2017 08:20:43 +0200
+Subject: debloat: dmabuf
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/base/Kconfig | 2 +-
+ drivers/dma-buf/Makefile | 10 +++++++---
+ drivers/dma-buf/dma-buf.c | 4 +++-
+ kernel/sched/core.c | 1 +
+ 4 files changed, 12 insertions(+), 5 deletions(-)
+
+--- a/drivers/base/Kconfig
++++ b/drivers/base/Kconfig
+@@ -184,7 +184,7 @@ config SOC_BUS
+ source "drivers/base/regmap/Kconfig"
+
+ config DMA_SHARED_BUFFER
+- bool
++ tristate
+ default n
+ select IRQ_WORK
+ help
+--- a/drivers/dma-buf/heaps/Makefile
++++ b/drivers/dma-buf/heaps/Makefile
+@@ -1,4 +1,4 @@
+ # SPDX-License-Identifier: GPL-2.0
+-obj-y += heap-helpers.o
+-obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o
+-obj-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o
++dma-buf-objs-y += heap-helpers.o
++dma-buf-objs-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o
++dma-buf-objs-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o
+--- a/drivers/dma-buf/Makefile
++++ b/drivers/dma-buf/Makefile
+@@ -1,15 +1,19 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+-obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
++obj-$(CONFIG_DMA_SHARED_BUFFER) := dma-shared-buffer.o
++
++dma-buf-objs-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
+ dma-resv.o seqno-fence.o
+-obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o
+-obj-$(CONFIG_DMABUF_HEAPS) += heaps/
+-obj-$(CONFIG_SYNC_FILE) += sync_file.o
+-obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
+-obj-$(CONFIG_UDMABUF) += udmabuf.o
++dma-buf-objs-$(CONFIG_DMABUF_HEAPS) += dma-heap.o
++obj-$(CONFIG_DMABUF_HEAPS) += heaps/
++dma-buf-objs-$(CONFIG_SYNC_FILE) += sync_file.o
++dma-buf-objs-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
++dma-buf-objs-$(CONFIG_UDMABUF) += udmabuf.o
+
+ dmabuf_selftests-y := \
+ selftest.o \
+ st-dma-fence.o \
+ st-dma-fence-chain.o
+
+-obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o
++dma-buf-objs-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o
++
++dma-shared-buffer-objs := $(dma-buf-objs-y)
+--- a/drivers/dma-buf/dma-buf.c
++++ b/drivers/dma-buf/dma-buf.c
+@@ -1419,4 +1419,5 @@ static void __exit dma_buf_deinit(void)
+ dma_buf_uninit_debugfs();
+ kern_unmount(dma_buf_mnt);
+ }
+-__exitcall(dma_buf_deinit);
++module_exit(dma_buf_deinit);
++MODULE_LICENSE("GPL");
+--- a/kernel/sched/core.c
++++ b/kernel/sched/core.c
+@@ -3065,6 +3065,7 @@ int wake_up_state(struct task_struct *p,
+ {
+ return try_to_wake_up(p, state, 0);
+ }
++EXPORT_SYMBOL_GPL(wake_up_state);
+
+ /*
+ * Perform scheduler related setup for a newly forked process p.
+--- a/fs/d_path.c
++++ b/fs/d_path.c
+@@ -311,6 +311,7 @@ char *dynamic_dname(struct dentry *dentr
+ buffer += buflen - sz;
+ return memcpy(buffer, temp, sz);
+ }
++EXPORT_SYMBOL_GPL(dynamic_dname);
+
+ char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
+ {
--- /dev/null
+From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 16 Jul 2017 16:56:10 +0200
+Subject: lib: add uevent_next_seqnum()
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/kobject.h | 5 +++++
+ lib/kobject_uevent.c | 37 +++++++++++++++++++++++++++++++++++++
+ 2 files changed, 42 insertions(+)
+
+--- a/lib/kobject_uevent.c
++++ b/lib/kobject_uevent.c
+@@ -179,6 +179,18 @@ out:
+ return r;
+ }
+
++u64 uevent_next_seqnum(void)
++{
++ u64 seq;
++
++ mutex_lock(&uevent_sock_mutex);
++ seq = ++uevent_seqnum;
++ mutex_unlock(&uevent_sock_mutex);
++
++ return seq;
++}
++EXPORT_SYMBOL_GPL(uevent_next_seqnum);
++
+ /**
+ * kobject_synth_uevent - send synthetic uevent with arguments
+ *
--- /dev/null
+From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 16 Jul 2017 16:56:10 +0200
+Subject: lib: add uevent_next_seqnum()
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/kobject.h | 5 +++++
+ lib/kobject_uevent.c | 37 +++++++++++++++++++++++++++++++++++++
+ 2 files changed, 42 insertions(+)
+
+--- a/include/linux/kobject.h
++++ b/include/linux/kobject.h
+@@ -32,6 +32,8 @@
+ #define UEVENT_NUM_ENVP 64 /* number of env pointers */
+ #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */
+
++struct sk_buff;
++
+ #ifdef CONFIG_UEVENT_HELPER
+ /* path to the userspace helper executed on an event */
+ extern char uevent_helper[];
+@@ -244,4 +246,7 @@ int kobject_synth_uevent(struct kobject
+ __printf(2, 3)
+ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...);
+
++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group,
++ gfp_t allocation);
++
+ #endif /* _KOBJECT_H_ */
+--- a/lib/kobject_uevent.c
++++ b/lib/kobject_uevent.c
+@@ -691,6 +691,43 @@ int add_uevent_var(struct kobj_uevent_en
+ EXPORT_SYMBOL_GPL(add_uevent_var);
+
+ #if defined(CONFIG_NET)
++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group,
++ gfp_t allocation)
++{
++ struct uevent_sock *ue_sk;
++ int err = 0;
++
++ /* send netlink message */
++ mutex_lock(&uevent_sock_mutex);
++ list_for_each_entry(ue_sk, &uevent_sock_list, list) {
++ struct sock *uevent_sock = ue_sk->sk;
++ struct sk_buff *skb2;
++
++ skb2 = skb_clone(skb, allocation);
++ if (!skb2)
++ break;
++
++ err = netlink_broadcast(uevent_sock, skb2, pid, group,
++ allocation);
++ if (err)
++ break;
++ }
++ mutex_unlock(&uevent_sock_mutex);
++
++ kfree_skb(skb);
++ return err;
++}
++#else
++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group,
++ gfp_t allocation)
++{
++ kfree_skb(skb);
++ return 0;
++}
++#endif
++EXPORT_SYMBOL_GPL(broadcast_uevent);
++
++#if defined(CONFIG_NET)
+ static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb,
+ struct netlink_ext_ack *extack)
+ {
--- /dev/null
+--- a/drivers/of/fdt.c
++++ b/drivers/of/fdt.c
+@@ -1055,6 +1055,9 @@ int __init early_init_dt_scan_chosen(uns
+ p = of_get_flat_dt_prop(node, "bootargs", &l);
+ if (p != NULL && l > 0)
+ strlcpy(data, p, min(l, COMMAND_LINE_SIZE));
++ p = of_get_flat_dt_prop(node, "bootargs-append", &l);
++ if (p != NULL && l > 0)
++ strlcat(data, p, min_t(int, strlen(data) + (int)l, COMMAND_LINE_SIZE));
+
+ /*
+ * CONFIG_CMDLINE is meant to be a default in case nothing else
--- /dev/null
+From d8d1a9a77863a8c7031ae82a1d461aa78eb72a7b Mon Sep 17 00:00:00 2001
+From: Rob Herring <robh@kernel.org>
+Date: Mon, 11 Oct 2021 14:12:43 -0500
+Subject: [PATCH] checks: Drop interrupt provider '#address-cells' check
+
+'#address-cells' is only needed when parsing 'interrupt-map' properties, so
+remove it from the common interrupt-provider test.
+
+Cc: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
+Signed-off-by: Rob Herring <robh@kernel.org>
+Message-Id: <20211011191245.1009682-3-robh@kernel.org>
+Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
+---
+--- a/scripts/dtc/checks.c
++++ b/scripts/dtc/checks.c
+@@ -1569,11 +1569,6 @@ static void check_interrupt_provider(str
+ if (!prop)
+ FAIL(c, dti, node,
+ "Missing #interrupt-cells in interrupt provider");
+-
+- prop = get_property(node, "#address-cells");
+- if (!prop)
+- FAIL(c, dti, node,
+- "Missing #address-cells in interrupt provider");
+ }
+ WARNING(interrupt_provider, check_interrupt_provider, NULL);
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Oct 2020 22:00:03 +0200
+Subject: [PATCH] compiler.h: only include asm/rwonce.h for kernel code
+
+This header file is not in uapi, which makes any user space code that includes
+linux/compiler.h to fail with the error 'asm/rwonce.h: No such file or directory'
+
+Fixes: e506ea451254 ("compiler.h: Split {READ,WRITE}_ONCE definitions out into rwonce.h")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/linux/compiler.h
++++ b/include/linux/compiler.h
+@@ -213,6 +213,8 @@ void ftrace_likely_update(struct ftrace_
+ __v; \
+ })
+
++#include <asm/rwonce.h>
++
+ #endif /* __KERNEL__ */
+
+ /*
+@@ -245,6 +247,4 @@ static inline void *offset_to_ptr(const
+ */
+ #define prevent_tail_call_optimization() mb()
+
+-#include <asm/rwonce.h>
+-
+ #endif /* __LINUX_COMPILER_H */
--- /dev/null
+--- a/include/uapi/linux/swab.h
++++ b/include/uapi/linux/swab.h
+@@ -3,7 +3,7 @@
+ #define _UAPI_LINUX_SWAB_H
+
+ #include <linux/types.h>
+-#include <linux/compiler.h>
++#include <linux/stddef.h>
+ #include <asm/bitsperlong.h>
+ #include <asm/swab.h>
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 18 Apr 2018 10:50:05 +0200
+Subject: [PATCH] MIPS: only process negative stack offsets on stack traces
+
+Fixes endless back traces in cases where the compiler emits a stack
+pointer increase in a branch delay slot (probably for some form of
+function return).
+
+[ 3.475442] BUG: MAX_STACK_TRACE_ENTRIES too low!
+[ 3.480070] turning off the locking correctness validator.
+[ 3.485521] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.14.34 #0
+[ 3.491475] Stack : 00000000 00000000 00000000 00000000 80e0fce2 00000034 00000000 00000000
+[ 3.499764] 87c3838c 80696377 8061047c 00000000 00000001 00000001 87c2d850 6534689f
+[ 3.508059] 00000000 00000000 80e10000 00000000 00000000 000000cf 0000000f 00000000
+[ 3.516353] 00000000 806a0000 00076891 00000000 00000000 00000000 ffffffff 00000000
+[ 3.524648] 806c0000 00000004 80e10000 806a0000 00000003 80690000 00000000 80700000
+[ 3.532942] ...
+[ 3.535362] Call Trace:
+[ 3.537818] [<80010a48>] show_stack+0x58/0x100
+[ 3.542207] [<804c2f78>] dump_stack+0xe8/0x170
+[ 3.546613] [<80079f90>] save_trace+0xf0/0x110
+[ 3.551010] [<8007b1ec>] mark_lock+0x33c/0x78c
+[ 3.555413] [<8007bf48>] __lock_acquire+0x2ac/0x1a08
+[ 3.560337] [<8007de60>] lock_acquire+0x64/0x8c
+[ 3.564846] [<804e1570>] _raw_spin_lock_irqsave+0x54/0x78
+[ 3.570186] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.574770] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.579257] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.583839] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.588329] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.592911] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.597401] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.601983] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.606473] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.611055] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.615545] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.620125] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.624619] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.629197] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.633691] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.638269] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.642763] [<801b618c>] kernfs_notify+0x94/0xac
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/arch/mips/kernel/process.c
++++ b/arch/mips/kernel/process.c
+@@ -380,6 +380,8 @@ static inline int is_sp_move_ins(union m
+
+ if (ip->i_format.opcode == addiu_op ||
+ ip->i_format.opcode == daddiu_op) {
++ if (ip->i_format.simmediate > 0)
++ return 0;
+ *frame_size = -ip->i_format.simmediate;
+ return 1;
+ }
--- /dev/null
+From: Tobias Wolf <dev-NTEO@vplace.de>
+Subject: mm: Fix alloc_node_mem_map with ARCH_PFN_OFFSET calculation
+
+An rt288x (ralink) based router (Belkin F5D8235 v1) does not boot with any
+kernel beyond version 4.3 resulting in:
+
+BUG: Bad page state in process swapper pfn:086ac
+
+bisect resulted in:
+
+a1c34a3bf00af2cede839879502e12dc68491ad5 is the first bad commit
+commit a1c34a3bf00af2cede839879502e12dc68491ad5
+Author: Laura Abbott <laura@labbott.name>
+Date: Thu Nov 5 18:48:46 2015 -0800
+
+ mm: Don't offset memmap for flatmem
+
+ Srinivas Kandagatla reported bad page messages when trying to remove the
+ bottom 2MB on an ARM based IFC6410 board
+
+ BUG: Bad page state in process swapper pfn:fffa8
+ page:ef7fb500 count:0 mapcount:0 mapping: (null) index:0x0
+ flags: 0x96640253(locked|error|dirty|active|arch_1|reclaim|mlocked)
+ page dumped because: PAGE_FLAGS_CHECK_AT_FREE flag(s) set
+ bad because of flags:
+ flags: 0x200041(locked|active|mlocked)
+ Modules linked in:
+ CPU: 0 PID: 0 Comm: swapper Not tainted 3.19.0-rc3-00007-g412f9ba-dirty
+#816
+ Hardware name: Qualcomm (Flattened Device Tree)
+ unwind_backtrace
+ show_stack
+ dump_stack
+ bad_page
+ free_pages_prepare
+ free_hot_cold_page
+ __free_pages
+ free_highmem_page
+ mem_init
+ start_kernel
+ Disabling lock debugging due to kernel taint
+ [...]
+:040000 040000 2de013c372345fd471cd58f0553c9b38b0ef1cc4
+0a8156f848733dfa21e16c196dfb6c0a76290709 M mm
+
+This fix for ARM does not account ARCH_PFN_OFFSET for mem_map as later used by
+page_to_pfn anymore.
+
+The following output was generated with two hacked in printk statements:
+
+printk("before %p vs. %p or %p\n", mem_map, mem_map - offset, mem_map -
+(pgdat->node_start_pfn - ARCH_PFN_OFFSET));
+ if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
+ mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
+printk("after %p\n", mem_map);
+
+Output:
+
+[ 0.000000] before 8861b280 vs. 8861b280 or 8851b280
+[ 0.000000] after 8851b280
+
+As seen in the first line mem_map with subtraction of offset does not equal the
+mem_map after subtraction of ARCH_PFN_OFFSET.
+
+After adding the offset of ARCH_PFN_OFFSET as well to mem_map as the
+previously calculated offset is zero for the named platform it is able to boot
+4.4 and 4.9-rc7 again.
+
+Signed-off-by: Tobias Wolf <dev-NTEO@vplace.de>
+---
+
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -7055,7 +7055,7 @@ static void __ref alloc_node_mem_map(str
+ if (pgdat == NODE_DATA(0)) {
+ mem_map = NODE_DATA(0)->node_mem_map;
+ if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
+- mem_map -= offset;
++ mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
+ }
+ #endif
+ }
--- /dev/null
+From: Giuseppe Lippolis <giu.lippolis@gmail.com>
+Subject: Add the linux,spidev compatible in spidev Several device in ramips have this binding in the dts
+
+Signed-off-by: Giuseppe Lippolis <giu.lippolis@gmail.com>
+---
+ drivers/spi/spidev.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/spi/spidev.c
++++ b/drivers/spi/spidev.c
+@@ -682,6 +682,7 @@ static const struct of_device_id spidev_
+ { .compatible = "lwn,bk4" },
+ { .compatible = "dh,dhcom-board" },
+ { .compatible = "menlo,m53cpld" },
++ { .compatible = "siliconlabs,si3210" },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, spidev_dt_ids);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: jffs2: use .rename2 and add RENAME_WHITEOUT support
+
+It is required for renames on overlayfs
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/fs/jffs2/dir.c
++++ b/fs/jffs2/dir.c
+@@ -609,7 +609,8 @@ static int jffs2_rmdir (struct inode *di
+ return ret;
+ }
+
+-static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, umode_t mode, dev_t rdev)
++static int __jffs2_mknod (struct inode *dir_i, struct dentry *dentry,
++ umode_t mode, dev_t rdev, bool whiteout)
+ {
+ struct jffs2_inode_info *f, *dir_f;
+ struct jffs2_sb_info *c;
+@@ -748,7 +749,11 @@ static int jffs2_mknod (struct inode *di
+ mutex_unlock(&dir_f->sem);
+ jffs2_complete_reservation(c);
+
+- d_instantiate_new(dentry, inode);
++ if (!whiteout)
++ d_instantiate_new(dentry, inode);
++ else
++ unlock_new_inode(inode);
++
+ return 0;
+
+ fail:
+@@ -756,6 +761,17 @@ static int jffs2_mknod (struct inode *di
+ return ret;
+ }
+
++static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, umode_t mode, dev_t rdev)
++{
++ return __jffs2_mknod(dir_i, dentry, mode, rdev, false);
++}
++
++static int jffs2_whiteout (struct inode *old_dir, struct dentry *old_dentry)
++{
++ return __jffs2_mknod(old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE,
++ WHITEOUT_DEV, true);
++}
++
+ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
+ struct inode *new_dir_i, struct dentry *new_dentry,
+ unsigned int flags)
+@@ -766,7 +782,7 @@ static int jffs2_rename (struct inode *o
+ uint8_t type;
+ uint32_t now;
+
+- if (flags & ~RENAME_NOREPLACE)
++ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT))
+ return -EINVAL;
+
+ /* The VFS will check for us and prevent trying to rename a
+@@ -832,9 +848,14 @@ static int jffs2_rename (struct inode *o
+ if (d_is_dir(old_dentry) && !victim_f)
+ inc_nlink(new_dir_i);
+
+- /* Unlink the original */
+- ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
+- old_dentry->d_name.name, old_dentry->d_name.len, NULL, now);
++ if (flags & RENAME_WHITEOUT)
++ /* Replace with whiteout */
++ ret = jffs2_whiteout(old_dir_i, old_dentry);
++ else
++ /* Unlink the original */
++ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
++ old_dentry->d_name.name,
++ old_dentry->d_name.len, NULL, now);
+
+ /* We don't touch inode->i_nlink */
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: jffs2: add RENAME_EXCHANGE support
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/fs/jffs2/dir.c
++++ b/fs/jffs2/dir.c
+@@ -779,18 +779,31 @@ static int jffs2_rename (struct inode *o
+ int ret;
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);
+ struct jffs2_inode_info *victim_f = NULL;
++ struct inode *fst_inode = d_inode(old_dentry);
++ struct inode *snd_inode = d_inode(new_dentry);
+ uint8_t type;
+ uint32_t now;
+
+- if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT))
++ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT|RENAME_EXCHANGE))
+ return -EINVAL;
+
++ if ((flags & RENAME_EXCHANGE) && (old_dir_i != new_dir_i)) {
++ if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) {
++ inc_nlink(new_dir_i);
++ drop_nlink(old_dir_i);
++ }
++ else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) {
++ drop_nlink(new_dir_i);
++ inc_nlink(old_dir_i);
++ }
++ }
++
+ /* The VFS will check for us and prevent trying to rename a
+ * file over a directory and vice versa, but if it's a directory,
+ * the VFS can't check whether the victim is empty. The filesystem
+ * needs to do that for itself.
+ */
+- if (d_really_is_positive(new_dentry)) {
++ if (d_really_is_positive(new_dentry) && !(flags & RENAME_EXCHANGE)) {
+ victim_f = JFFS2_INODE_INFO(d_inode(new_dentry));
+ if (d_is_dir(new_dentry)) {
+ struct jffs2_full_dirent *fd;
+@@ -825,7 +838,7 @@ static int jffs2_rename (struct inode *o
+ if (ret)
+ return ret;
+
+- if (victim_f) {
++ if (victim_f && !(flags & RENAME_EXCHANGE)) {
+ /* There was a victim. Kill it off nicely */
+ if (d_is_dir(new_dentry))
+ clear_nlink(d_inode(new_dentry));
+@@ -851,6 +864,12 @@ static int jffs2_rename (struct inode *o
+ if (flags & RENAME_WHITEOUT)
+ /* Replace with whiteout */
+ ret = jffs2_whiteout(old_dir_i, old_dentry);
++ else if (flags & RENAME_EXCHANGE)
++ /* Replace the original */
++ ret = jffs2_do_link(c, JFFS2_INODE_INFO(old_dir_i),
++ d_inode(new_dentry)->i_ino, type,
++ old_dentry->d_name.name, old_dentry->d_name.len,
++ now);
+ else
+ /* Unlink the original */
+ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
+@@ -882,7 +901,7 @@ static int jffs2_rename (struct inode *o
+ return ret;
+ }
+
+- if (d_is_dir(old_dentry))
++ if (d_is_dir(old_dentry) && !(flags & RENAME_EXCHANGE))
+ drop_nlink(old_dir_i);
+
+ new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: jffs2: add splice ops
+
+Add splice_read using generic_file_splice_read.
+Add splice_write using iter_file_splice_write
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/fs/jffs2/file.c
++++ b/fs/jffs2/file.c
+@@ -53,6 +53,8 @@ const struct file_operations jffs2_file_
+ .open = generic_file_open,
+ .read_iter = generic_file_read_iter,
+ .write_iter = generic_file_write_iter,
++ .splice_read = generic_file_splice_read,
++ .splice_write = iter_file_splice_write,
+ .unlocked_ioctl=jffs2_ioctl,
+ .mmap = generic_file_readonly_mmap,
+ .fsync = jffs2_fsync,
--- /dev/null
+From: Stephen Hemminger <stephen@networkplumber.org>
+Subject: bridge: allow receiption on disabled port
+
+When an ethernet device is enslaved to a bridge, and the bridge STP
+detects loss of carrier (or operational state down), then normally
+packet receiption is blocked.
+
+This breaks control applications like WPA which maybe expecting to
+receive packets to negotiate to bring link up. The bridge needs to
+block forwarding packets from these disabled ports, but there is no
+hard requirement to not allow local packet delivery.
+
+Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+
+--- a/net/bridge/br_input.c
++++ b/net/bridge/br_input.c
+@@ -195,6 +195,9 @@ static void __br_handle_local_finish(str
+ /* note: already called with rcu_read_lock */
+ static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+ {
++ struct net_bridge_port *p = br_port_get_rcu(skb->dev);
++
++ if (p->state != BR_STATE_DISABLED)
+ __br_handle_local_finish(skb);
+
+ /* return 1 to signal the okfn() was called so it's ok to use the skb */
+@@ -348,6 +351,17 @@ static rx_handler_result_t br_handle_fra
+
+ forward:
+ switch (p->state) {
++ case BR_STATE_DISABLED:
++ if (ether_addr_equal(p->br->dev->dev_addr, dest))
++ skb->pkt_type = PACKET_HOST;
++
++ if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
++ dev_net(skb->dev), NULL, skb, skb->dev, NULL,
++ br_handle_local_finish) == 1) {
++ return RX_HANDLER_PASS;
++ }
++ break;
++
+ case BR_STATE_FORWARDING:
+ case BR_STATE_LEARNING:
+ if (ether_addr_equal(p->br->dev->dev_addr, dest))
--- /dev/null
+From: Daniel González Cabanelas <dgcbueu@gmail.com>
+Subject: [PATCH 1/2] rtc: rs5c372: support alarms up to 1 week
+
+The Ricoh R2221x, R2223x, RS5C372, RV5C387A chips can handle 1 week
+alarms.
+
+Read the "wday" alarm register and convert it to a date to support up 1
+week in our driver.
+
+Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
+---
+ drivers/rtc/rtc-rs5c372.c | 48 ++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 42 insertions(+), 6 deletions(-)
+
+--- a/drivers/rtc/rtc-rs5c372.c
++++ b/drivers/rtc/rtc-rs5c372.c
+@@ -393,7 +393,9 @@ static int rs5c_read_alarm(struct device
+ {
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rs5c372 *rs5c = i2c_get_clientdata(client);
+- int status;
++ int status, wday_offs;
++ struct rtc_time rtc;
++ unsigned long alarm_secs;
+
+ status = rs5c_get_regs(rs5c);
+ if (status < 0)
+@@ -403,6 +405,30 @@ static int rs5c_read_alarm(struct device
+ t->time.tm_sec = 0;
+ t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f);
+ t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]);
++ t->time.tm_wday = ffs(rs5c->regs[RS5C_REG_ALARM_A_WDAY] & 0x7f) - 1;
++
++ /* determine the day, month and year based on alarm wday, taking as a
++ * reference the current time from the rtc
++ */
++ status = rs5c372_rtc_read_time(dev, &rtc);
++ if (status < 0)
++ return status;
++
++ wday_offs = t->time.tm_wday - rtc.tm_wday;
++ alarm_secs = mktime64(rtc.tm_year + 1900,
++ rtc.tm_mon + 1,
++ rtc.tm_mday + wday_offs,
++ t->time.tm_hour,
++ t->time.tm_min,
++ t->time.tm_sec);
++
++ if (wday_offs < 0 || (wday_offs == 0 &&
++ (t->time.tm_hour < rtc.tm_hour ||
++ (t->time.tm_hour == rtc.tm_hour &&
++ t->time.tm_min <= rtc.tm_min))))
++ alarm_secs += 7 * 86400;
++
++ rtc_time64_to_tm(alarm_secs, &t->time);
+
+ /* ... and status */
+ t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE);
+@@ -417,12 +443,20 @@ static int rs5c_set_alarm(struct device
+ struct rs5c372 *rs5c = i2c_get_clientdata(client);
+ int status, addr, i;
+ unsigned char buf[3];
++ struct rtc_time rtc_tm;
++ unsigned long rtc_secs, alarm_secs;
+
+- /* only handle up to 24 hours in the future, like RTC_ALM_SET */
+- if (t->time.tm_mday != -1
+- || t->time.tm_mon != -1
+- || t->time.tm_year != -1)
++ /* chip only can handle alarms up to one week in the future*/
++ status = rs5c372_rtc_read_time(dev, &rtc_tm);
++ if (status)
++ return status;
++ rtc_secs = rtc_tm_to_time64(&rtc_tm);
++ alarm_secs = rtc_tm_to_time64(&t->time);
++ if (alarm_secs >= rtc_secs + 7 * 86400) {
++ dev_err(dev, "%s: alarm maximum is one week in the future (%d)\n",
++ __func__, status);
+ return -EINVAL;
++ }
+
+ /* REVISIT: round up tm_sec */
+
+@@ -443,7 +477,9 @@ static int rs5c_set_alarm(struct device
+ /* set alarm */
+ buf[0] = bin2bcd(t->time.tm_min);
+ buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour);
+- buf[2] = 0x7f; /* any/all days */
++ /* each bit is the day of the week, 0x7f means all days */
++ buf[2] = (t->time.tm_wday >= 0 && t->time.tm_wday < 7) ?
++ BIT(t->time.tm_wday) : 0x7f;
+
+ for (i = 0; i < sizeof(buf); i++) {
+ addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i);
--- /dev/null
+From: Daniel González Cabanelas <dgcbueu@gmail.com>
+Subject: [PATCH 2/2] rtc: rs5c372: let the alarm to be used as wakeup source
+
+Currently there is no use for the interrupts on the rs5c372 RTC and the
+wakealarm isn't enabled. There are some devices like NASes which use this
+RTC to wake up from the power off state when the INTR pin is activated by
+the alarm clock.
+
+Enable the alarm and let to be used as a wakeup source.
+
+Tested on a Buffalo LS421DE NAS.
+
+Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
+---
+ drivers/rtc/rtc-rs5c372.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/rtc/rtc-rs5c372.c
++++ b/drivers/rtc/rtc-rs5c372.c
+@@ -654,6 +654,7 @@ static int rs5c372_probe(struct i2c_clie
+ int err = 0;
+ int smbus_mode = 0;
+ struct rs5c372 *rs5c372;
++ bool rs5c372_can_wakeup_device = false;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+@@ -689,6 +690,12 @@ static int rs5c372_probe(struct i2c_clie
+ else
+ rs5c372->type = id->driver_data;
+
++#ifdef CONFIG_OF
++ if(of_property_read_bool(client->dev.of_node,
++ "wakeup-source"))
++ rs5c372_can_wakeup_device = true;
++#endif
++
+ /* we read registers 0x0f then 0x00-0x0f; skip the first one */
+ rs5c372->regs = &rs5c372->buf[1];
+ rs5c372->smbus = smbus_mode;
+@@ -722,6 +729,8 @@ static int rs5c372_probe(struct i2c_clie
+ goto exit;
+ }
+
++ rs5c372->has_irq = 1;
++
+ /* if the oscillator lost power and no other software (like
+ * the bootloader) set it up, do it here.
+ *
+@@ -748,6 +757,10 @@ static int rs5c372_probe(struct i2c_clie
+ );
+
+ /* REVISIT use client->irq to register alarm irq ... */
++ if (rs5c372_can_wakeup_device) {
++ device_init_wakeup(&client->dev, true);
++ }
++
+ rs5c372->rtc = devm_rtc_device_register(&client->dev,
+ rs5c372_driver.driver.name,
+ &rs5c372_rtc_ops, THIS_MODULE);
+@@ -761,6 +774,9 @@ static int rs5c372_probe(struct i2c_clie
+ if (err)
+ goto exit;
+
++ /* the rs5c372 alarm only supports a minute accuracy */
++ rs5c372->rtc->uie_unsupported = 1;
++
+ return 0;
+
+ exit:
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: Upgrade to Linux 2.6.19
+
+- Includes large parts of the patch from #1021 by dpalffy
+- Includes RB532 NAND driver changes by n0-1
+
+[john@phrozen.org: feix will add this to his upstream queue]
+
+lede-commit: bff468813f78f81e36ebb2a3f4354de7365e640f
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ Makefile | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -735,11 +735,11 @@ KBUILD_CFLAGS += $(call cc-disable-warni
+ KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member)
+
+ ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE
+-KBUILD_CFLAGS += -O2
++KBUILD_CFLAGS += -O2 $(EXTRA_OPTIMIZATION)
+ else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
+-KBUILD_CFLAGS += -O3
++KBUILD_CFLAGS += -O3 $(EXTRA_OPTIMIZATION)
+ else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
+-KBUILD_CFLAGS += -Os
++KBUILD_CFLAGS += -Os -fno-reorder-blocks -fno-tree-ch $(EXTRA_OPTIMIZATION)
+ endif
+
+ # Tell gcc to never replace conditional load with a non-conditional one
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: add a config option for keeping the kallsyms table uncompressed, saving ~9kb kernel size after lzma on ar71xx
+
+[john@phrozen.org: added to my upstream queue 30.12.2016]
+lede-commit: e0e3509b5ce2ccf93d4d67ea907613f5f7ec2eed
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ init/Kconfig | 11 +++++++++++
+ kernel/kallsyms.c | 8 ++++++++
+ scripts/kallsyms.c | 12 ++++++++++++
+ scripts/link-vmlinux.sh | 4 ++++
+ 4 files changed, 35 insertions(+)
+
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -1384,6 +1384,17 @@ config SYSCTL_ARCH_UNALIGN_ALLOW
+ the unaligned access emulation.
+ see arch/parisc/kernel/unaligned.c for reference
+
++config KALLSYMS_UNCOMPRESSED
++ bool "Keep kallsyms uncompressed"
++ depends on KALLSYMS
++ help
++ Normally kallsyms contains compressed symbols (using a token table),
++ reducing the uncompressed kernel image size. Keeping the symbol table
++ uncompressed significantly improves the size of this part in compressed
++ kernel images.
++
++ Say N unless you need compressed kernel images to be small.
++
+ config HAVE_PCSPKR_PLATFORM
+ bool
+
+--- a/kernel/kallsyms.c
++++ b/kernel/kallsyms.c
+@@ -77,6 +77,11 @@ static unsigned int kallsyms_expand_symb
+ * For every byte on the compressed symbol data, copy the table
+ * entry for that byte.
+ */
++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED
++ memcpy(result, data + 1, len - 1);
++ result += len - 1;
++ len = 0;
++#endif
+ while (len) {
+ tptr = &kallsyms_token_table[kallsyms_token_index[*data]];
+ data++;
+@@ -109,6 +114,9 @@ tail:
+ */
+ static char kallsyms_get_symbol_type(unsigned int off)
+ {
++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED
++ return kallsyms_names[off + 1];
++#endif
+ /*
+ * Get just the first code, look it up in the token table,
+ * and return the first char from this token.
+--- a/scripts/kallsyms.c
++++ b/scripts/kallsyms.c
+@@ -58,6 +58,7 @@ static struct addr_range percpu_range =
+ static struct sym_entry **table;
+ static unsigned int table_size, table_cnt;
+ static int all_symbols;
++static int uncompressed;
+ static int absolute_percpu;
+ static int base_relative;
+
+@@ -486,6 +487,9 @@ static void write_src(void)
+
+ free(markers);
+
++ if (uncompressed)
++ return;
++
+ output_label("kallsyms_token_table");
+ off = 0;
+ for (i = 0; i < 256; i++) {
+@@ -537,6 +541,9 @@ static unsigned char *find_token(unsigne
+ {
+ int i;
+
++ if (uncompressed)
++ return NULL;
++
+ for (i = 0; i < len - 1; i++) {
+ if (str[i] == token[0] && str[i+1] == token[1])
+ return &str[i];
+@@ -609,6 +616,9 @@ static void optimize_result(void)
+ {
+ int i, best;
+
++ if (uncompressed)
++ return;
++
+ /* using the '\0' symbol last allows compress_symbols to use standard
+ * fast string functions */
+ for (i = 255; i >= 0; i--) {
+@@ -773,6 +783,8 @@ int main(int argc, char **argv)
+ absolute_percpu = 1;
+ else if (strcmp(argv[i], "--base-relative") == 0)
+ base_relative = 1;
++ else if (strcmp(argv[i], "--uncompressed") == 0)
++ uncompressed = 1;
+ else
+ usage();
+ }
+--- a/scripts/link-vmlinux.sh
++++ b/scripts/link-vmlinux.sh
+@@ -186,6 +186,10 @@ kallsyms()
+ kallsymopt="${kallsymopt} --base-relative"
+ fi
+
++ if [ -n "${CONFIG_KALLSYMS_UNCOMPRESSED}" ]; then
++ kallsymopt="${kallsymopt} --uncompressed"
++ fi
++
+ info KSYMS ${2}
+ ${NM} -n ${1} | scripts/kallsyms ${kallsymopt} > ${2}
+ }
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: when KALLSYMS is disabled, print module address + size for matching backtrace entries
+
+[john@phrozen.org: felix will add this to his upstream queue]
+
+lede-commit 53827cdc824556cda910b23ce5030c363b8f1461
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ lib/vsprintf.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+--- a/lib/vsprintf.c
++++ b/lib/vsprintf.c
+@@ -983,8 +983,10 @@ char *symbol_string(char *buf, char *end
+ struct printf_spec spec, const char *fmt)
+ {
+ unsigned long value;
+-#ifdef CONFIG_KALLSYMS
+ char sym[KSYM_SYMBOL_LEN];
++#ifndef CONFIG_KALLSYMS
++ struct module *mod;
++ int len;
+ #endif
+
+ if (fmt[1] == 'R')
+@@ -1001,8 +1003,14 @@ char *symbol_string(char *buf, char *end
+
+ return string_nocheck(buf, end, sym, spec);
+ #else
+- return special_hex_number(buf, end, value, sizeof(void *));
++ len = snprintf(sym, sizeof(sym), "0x%lx", value);
++ mod = __module_address(value);
++ if (mod)
++ snprintf(sym + len, sizeof(sym) - len, " [%s@%p+0x%x]",
++ mod->name, mod->core_layout.base,
++ mod->core_layout.size);
+ #endif
++ return string(buf, end, sym, spec);
+ }
+
+ static const struct printf_spec default_str_spec = {
--- /dev/null
+From: Gabor Juhos <juhosg@openwrt.org>
+Subject: usr: sanitize deps_initramfs list
+
+If any filename in the intramfs dependency
+list contains a colon, that causes a kernel
+build error like this:
+
+/devel/openwrt/build_dir/linux-ar71xx_generic/linux-3.6.6/usr/Makefile:58: *** multiple target patterns. Stop.
+make[5]: *** [usr] Error 2
+
+Fix it by removing such filenames from the
+deps_initramfs list.
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ usr/Makefile | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/usr/Makefile
++++ b/usr/Makefile
+@@ -61,6 +61,8 @@ hostprogs := gen_init_cpio
+ # The dependency list is generated by gen_initramfs.sh -l
+ -include $(obj)/.initramfs_data.cpio.d
+
++deps_initramfs := $(foreach v,$(deps_initramfs),$(if $(findstring :,$(v)),,$(v)))
++
+ # do not try to update files included in initramfs
+ $(deps_initramfs): ;
+
--- /dev/null
+From: Imre Kaloz <kaloz@openwrt.org>
+Subject: [PATCH] hack: net: wireless: make the wl12xx glue code available with
+ compat-wireless, too
+
+Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
+---
+ drivers/net/wireless/ti/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/wireless/ti/Kconfig
++++ b/drivers/net/wireless/ti/Kconfig
+@@ -20,7 +20,7 @@ source "drivers/net/wireless/ti/wlcore/K
+
+ config WILINK_PLATFORM_DATA
+ bool "TI WiLink platform data"
+- depends on WLCORE_SDIO || WL1251_SDIO
++ depends on WLCORE_SDIO || WL1251_SDIO || ARCH_OMAP2PLUS
+ default y
+ help
+ Small platform data bit needed to pass data to the sdio modules.
--- /dev/null
+From c2deb5ef01a0ef09088832744cbace9e239a6ee0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= <hacks@slashdirt.org>
+Date: Sat, 28 Mar 2020 12:11:50 +0100
+Subject: [PATCH] generic: platform/mikrotik build bits (5.4)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch adds platform/mikrotik kernel build bits
+
+Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
+---
+ drivers/platform/Kconfig | 2 ++
+ drivers/platform/Makefile | 1 +
+ 2 files changed, 3 insertions(+)
+
+--- a/drivers/platform/Kconfig
++++ b/drivers/platform/Kconfig
+@@ -13,3 +13,5 @@ source "drivers/platform/chrome/Kconfig"
+ source "drivers/platform/mellanox/Kconfig"
+
+ source "drivers/platform/olpc/Kconfig"
++
++source "drivers/platform/mikrotik/Kconfig"
+--- a/drivers/platform/Makefile
++++ b/drivers/platform/Makefile
+@@ -9,3 +9,4 @@ obj-$(CONFIG_MIPS) += mips/
+ obj-$(CONFIG_OLPC_EC) += olpc/
+ obj-$(CONFIG_GOLDFISH) += goldfish/
+ obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
++obj-$(CONFIG_MIKROTIK) += mikrotik/
--- /dev/null
+From: Mark Miller <mark@mirell.org>
+Subject: mips: expose CONFIG_BOOT_RAW
+
+This exposes the CONFIG_BOOT_RAW symbol in Kconfig. This is needed on
+certain Broadcom chipsets running CFE in order to load the kernel.
+
+Signed-off-by: Mark Miller <mark@mirell.org>
+Acked-by: Rob Landley <rob@landley.net>
+---
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -1085,9 +1085,6 @@ config FW_ARC
+ config ARCH_MAY_HAVE_PC_FDC
+ bool
+
+-config BOOT_RAW
+- bool
+-
+ config CEVT_BCM1480
+ bool
+
+@@ -3182,6 +3179,18 @@ choice
+ bool "Extend builtin kernel arguments with bootloader arguments"
+ endchoice
+
++config BOOT_RAW
++ bool "Enable the kernel to be executed from the load address"
++ default n
++ help
++ Allow the kernel to be executed from the load address for
++ bootloaders which cannot read the ELF format. This places
++ a jump to start_kernel at the load address.
++
++ If unsure, say N.
++
++
++
+ endmenu
+
+ config LOCKDEP_SUPPORT
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: mips: use -mno-branch-likely for kernel and userspace
+
+saves ~11k kernel size after lzma and ~12k squashfs size in the
+
+lede-commit: 41a039f46450ffae9483d6216422098669da2900
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ arch/mips/Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/mips/Makefile
++++ b/arch/mips/Makefile
+@@ -95,7 +95,7 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin
+ # machines may also. Since BFD is incredibly buggy with respect to
+ # crossformat linking we rely on the elf2ecoff tool for format conversion.
+ #
+-cflags-y += -G 0 -mno-abicalls -fno-pic -pipe
++cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely
+ cflags-y += -msoft-float
+ LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
+ KBUILD_AFLAGS_MODULE += -mlong-calls
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: mips: replace -mlong-calls with -mno-long-calls to make function calls faster in kernel modules to achieve this, try to
+
+lede-commit: 3b3d64743ba2a874df9d70cd19e242205b0a788c
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ arch/mips/Makefile | 5 +
+ arch/mips/include/asm/module.h | 5 +
+ arch/mips/kernel/module.c | 279 ++++++++++++++++++++++++++++++++++++++++-
+ 3 files changed, 284 insertions(+), 5 deletions(-)
+
+--- a/arch/mips/Makefile
++++ b/arch/mips/Makefile
+@@ -98,8 +98,18 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin
+ cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely
+ cflags-y += -msoft-float
+ LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
++ifdef CONFIG_64BIT
+ KBUILD_AFLAGS_MODULE += -mlong-calls
+ KBUILD_CFLAGS_MODULE += -mlong-calls
++else
++ ifdef CONFIG_DYNAMIC_FTRACE
++ KBUILD_AFLAGS_MODULE += -mlong-calls
++ KBUILD_CFLAGS_MODULE += -mlong-calls
++ else
++ KBUILD_AFLAGS_MODULE += -mno-long-calls
++ KBUILD_CFLAGS_MODULE += -mno-long-calls
++ endif
++endif
+
+ ifeq ($(CONFIG_RELOCATABLE),y)
+ LDFLAGS_vmlinux += --emit-relocs
+--- a/arch/mips/include/asm/module.h
++++ b/arch/mips/include/asm/module.h
+@@ -12,6 +12,11 @@ struct mod_arch_specific {
+ const struct exception_table_entry *dbe_start;
+ const struct exception_table_entry *dbe_end;
+ struct mips_hi16 *r_mips_hi16_list;
++
++ void *phys_plt_tbl;
++ void *virt_plt_tbl;
++ unsigned int phys_plt_offset;
++ unsigned int virt_plt_offset;
+ };
+
+ typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */
+--- a/arch/mips/kernel/module.c
++++ b/arch/mips/kernel/module.c
+@@ -31,14 +31,221 @@ struct mips_hi16 {
+ static LIST_HEAD(dbe_list);
+ static DEFINE_SPINLOCK(dbe_lock);
+
+-#ifdef MODULE_START
++/*
++ * Get the potential max trampolines size required of the init and
++ * non-init sections. Only used if we cannot find enough contiguous
++ * physically mapped memory to put the module into.
++ */
++static unsigned int
++get_plt_size(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
++ const char *secstrings, unsigned int symindex, bool is_init)
++{
++ unsigned long ret = 0;
++ unsigned int i, j;
++ Elf_Sym *syms;
++
++ /* Everything marked ALLOC (this includes the exported symbols) */
++ for (i = 1; i < hdr->e_shnum; ++i) {
++ unsigned int info = sechdrs[i].sh_info;
++
++ if (sechdrs[i].sh_type != SHT_REL
++ && sechdrs[i].sh_type != SHT_RELA)
++ continue;
++
++ /* Not a valid relocation section? */
++ if (info >= hdr->e_shnum)
++ continue;
++
++ /* Don't bother with non-allocated sections */
++ if (!(sechdrs[info].sh_flags & SHF_ALLOC))
++ continue;
++
++ /* If it's called *.init*, and we're not init, we're
++ not interested */
++ if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0)
++ != is_init)
++ continue;
++
++ syms = (Elf_Sym *) sechdrs[symindex].sh_addr;
++ if (sechdrs[i].sh_type == SHT_REL) {
++ Elf_Mips_Rel *rel = (void *) sechdrs[i].sh_addr;
++ unsigned int size = sechdrs[i].sh_size / sizeof(*rel);
++
++ for (j = 0; j < size; ++j) {
++ Elf_Sym *sym;
++
++ if (ELF_MIPS_R_TYPE(rel[j]) != R_MIPS_26)
++ continue;
++
++ sym = syms + ELF_MIPS_R_SYM(rel[j]);
++ if (!is_init && sym->st_shndx != SHN_UNDEF)
++ continue;
++
++ ret += 4 * sizeof(int);
++ }
++ } else {
++ Elf_Mips_Rela *rela = (void *) sechdrs[i].sh_addr;
++ unsigned int size = sechdrs[i].sh_size / sizeof(*rela);
++
++ for (j = 0; j < size; ++j) {
++ Elf_Sym *sym;
++
++ if (ELF_MIPS_R_TYPE(rela[j]) != R_MIPS_26)
++ continue;
++
++ sym = syms + ELF_MIPS_R_SYM(rela[j]);
++ if (!is_init && sym->st_shndx != SHN_UNDEF)
++ continue;
++
++ ret += 4 * sizeof(int);
++ }
++ }
++ }
++
++ return ret;
++}
++
++#ifndef MODULE_START
++static void *alloc_phys(unsigned long size)
++{
++ unsigned order;
++ struct page *page;
++ struct page *p;
++
++ size = PAGE_ALIGN(size);
++ order = get_order(size);
++
++ page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN |
++ __GFP_THISNODE, order);
++ if (!page)
++ return NULL;
++
++ split_page(page, order);
++
++ /* mark all pages except for the last one */
++ for (p = page; p + 1 < page + (size >> PAGE_SHIFT); ++p)
++ set_bit(PG_owner_priv_1, &p->flags);
++
++ for (p = page + (size >> PAGE_SHIFT); p < page + (1 << order); ++p)
++ __free_page(p);
++
++ return page_address(page);
++}
++#endif
++
++static void free_phys(void *ptr)
++{
++ struct page *page;
++ bool free;
++
++ page = virt_to_page(ptr);
++ do {
++ free = test_and_clear_bit(PG_owner_priv_1, &page->flags);
++ __free_page(page);
++ page++;
++ } while (free);
++}
++
++
+ void *module_alloc(unsigned long size)
+ {
++#ifdef MODULE_START
+ return __vmalloc_node_range(size, 1, MODULE_START, MODULE_END,
+ GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE,
+ __builtin_return_address(0));
++#else
++ void *ptr;
++
++ if (size == 0)
++ return NULL;
++
++ ptr = alloc_phys(size);
++
++ /* If we failed to allocate physically contiguous memory,
++ * fall back to regular vmalloc. The module loader code will
++ * create jump tables to handle long jumps */
++ if (!ptr)
++ return vmalloc(size);
++
++ return ptr;
++#endif
+ }
++
++static inline bool is_phys_addr(void *ptr)
++{
++#ifdef CONFIG_64BIT
++ return (KSEGX((unsigned long)ptr) == CKSEG0);
++#else
++ return (KSEGX(ptr) == KSEG0);
+ #endif
++}
++
++/* Free memory returned from module_alloc */
++void module_memfree(void *module_region)
++{
++ if (is_phys_addr(module_region))
++ free_phys(module_region);
++ else
++ vfree(module_region);
++}
++
++static void *__module_alloc(int size, bool phys)
++{
++ void *ptr;
++
++ if (phys)
++ ptr = kmalloc(size, GFP_KERNEL);
++ else
++ ptr = vmalloc(size);
++ return ptr;
++}
++
++static void __module_free(void *ptr)
++{
++ if (is_phys_addr(ptr))
++ kfree(ptr);
++ else
++ vfree(ptr);
++}
++
++int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
++ char *secstrings, struct module *mod)
++{
++ unsigned int symindex = 0;
++ unsigned int core_size, init_size;
++ int i;
++
++ mod->arch.phys_plt_offset = 0;
++ mod->arch.virt_plt_offset = 0;
++ mod->arch.phys_plt_tbl = NULL;
++ mod->arch.virt_plt_tbl = NULL;
++
++ if (IS_ENABLED(CONFIG_64BIT))
++ return 0;
++
++ for (i = 1; i < hdr->e_shnum; i++)
++ if (sechdrs[i].sh_type == SHT_SYMTAB)
++ symindex = i;
++
++ core_size = get_plt_size(hdr, sechdrs, secstrings, symindex, false);
++ init_size = get_plt_size(hdr, sechdrs, secstrings, symindex, true);
++
++ if ((core_size + init_size) == 0)
++ return 0;
++
++ mod->arch.phys_plt_tbl = __module_alloc(core_size + init_size, 1);
++ if (!mod->arch.phys_plt_tbl)
++ return -ENOMEM;
++
++ mod->arch.virt_plt_tbl = __module_alloc(core_size + init_size, 0);
++ if (!mod->arch.virt_plt_tbl) {
++ __module_free(mod->arch.phys_plt_tbl);
++ mod->arch.phys_plt_tbl = NULL;
++ return -ENOMEM;
++ }
++
++ return 0;
++}
+
+ static int apply_r_mips_none(struct module *me, u32 *location,
+ u32 base, Elf_Addr v, bool rela)
+@@ -54,9 +261,40 @@ static int apply_r_mips_32(struct module
+ return 0;
+ }
+
++static Elf_Addr add_plt_entry_to(unsigned *plt_offset,
++ void *start, Elf_Addr v)
++{
++ unsigned *tramp = start + *plt_offset;
++ *plt_offset += 4 * sizeof(int);
++
++ /* adjust carry for addiu */
++ if (v & 0x00008000)
++ v += 0x10000;
++
++ tramp[0] = 0x3c190000 | (v >> 16); /* lui t9, hi16 */
++ tramp[1] = 0x27390000 | (v & 0xffff); /* addiu t9, t9, lo16 */
++ tramp[2] = 0x03200008; /* jr t9 */
++ tramp[3] = 0x00000000; /* nop */
++
++ return (Elf_Addr) tramp;
++}
++
++static Elf_Addr add_plt_entry(struct module *me, void *location, Elf_Addr v)
++{
++ if (is_phys_addr(location))
++ return add_plt_entry_to(&me->arch.phys_plt_offset,
++ me->arch.phys_plt_tbl, v);
++ else
++ return add_plt_entry_to(&me->arch.virt_plt_offset,
++ me->arch.virt_plt_tbl, v);
++
++}
++
+ static int apply_r_mips_26(struct module *me, u32 *location,
+ u32 base, Elf_Addr v, bool rela)
+ {
++ u32 ofs = base & 0x03ffffff;
++
+ if (v % 4) {
+ pr_err("module %s: dangerous R_MIPS_26 relocation\n",
+ me->name);
+@@ -64,13 +302,17 @@ static int apply_r_mips_26(struct module
+ }
+
+ if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
+- pr_err("module %s: relocation overflow\n",
+- me->name);
+- return -ENOEXEC;
++ v = add_plt_entry(me, location, v + (ofs << 2));
++ if (!v) {
++ pr_err("module %s: relocation overflow\n",
++ me->name);
++ return -ENOEXEC;
++ }
++ ofs = 0;
+ }
+
+ *location = (*location & ~0x03ffffff) |
+- ((base + (v >> 2)) & 0x03ffffff);
++ ((ofs + (v >> 2)) & 0x03ffffff);
+
+ return 0;
+ }
+@@ -446,9 +688,36 @@ int module_finalize(const Elf_Ehdr *hdr,
+ list_add(&me->arch.dbe_list, &dbe_list);
+ spin_unlock_irq(&dbe_lock);
+ }
++
++ /* Get rid of the fixup trampoline if we're running the module
++ * from physically mapped address space */
++ if (me->arch.phys_plt_offset == 0) {
++ __module_free(me->arch.phys_plt_tbl);
++ me->arch.phys_plt_tbl = NULL;
++ }
++ if (me->arch.virt_plt_offset == 0) {
++ __module_free(me->arch.virt_plt_tbl);
++ me->arch.virt_plt_tbl = NULL;
++ }
++
+ return 0;
+ }
+
++void module_arch_freeing_init(struct module *mod)
++{
++ if (mod->state == MODULE_STATE_LIVE)
++ return;
++
++ if (mod->arch.phys_plt_tbl) {
++ __module_free(mod->arch.phys_plt_tbl);
++ mod->arch.phys_plt_tbl = NULL;
++ }
++ if (mod->arch.virt_plt_tbl) {
++ __module_free(mod->arch.virt_plt_tbl);
++ mod->arch.virt_plt_tbl = NULL;
++ }
++}
++
+ void module_arch_cleanup(struct module *mod)
+ {
+ spin_lock_irq(&dbe_lock);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: adjust mips highmem offset to avoid the need for -mlong-calls on systems with >256M RAM
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ arch/mips/include/asm/mach-generic/spaces.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/mips/include/asm/mach-generic/spaces.h
++++ b/arch/mips/include/asm/mach-generic/spaces.h
+@@ -54,7 +54,7 @@
+ * Memory above this physical address will be considered highmem.
+ */
+ #ifndef HIGHMEM_START
+-#define HIGHMEM_START _AC(0x20000000, UL)
++#define HIGHMEM_START _AC(0x10000000, UL)
+ #endif
+
+ #endif /* CONFIG_32BIT */
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: add -mtune=34kc to MIPS CFLAGS when building for mips32r2
+
+This provides a good tradeoff across at least 24Kc-74Kc, while also
+producing smaller code.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ arch/mips/Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/mips/Makefile
++++ b/arch/mips/Makefile
+@@ -174,7 +174,7 @@ cflags-$(CONFIG_CPU_VR41XX) += -march=r4
+ cflags-$(CONFIG_CPU_R4X00) += -march=r4600 -Wa,--trap
+ cflags-$(CONFIG_CPU_TX49XX) += -march=r4600 -Wa,--trap
+ cflags-$(CONFIG_CPU_MIPS32_R1) += -march=mips32 -Wa,--trap
+-cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -Wa,--trap
++cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -mtune=34kc -Wa,--trap
+ cflags-$(CONFIG_CPU_MIPS32_R5) += -march=mips32r5 -Wa,--trap -modd-spreg
+ cflags-$(CONFIG_CPU_MIPS32_R6) += -march=mips32r6 -Wa,--trap -modd-spreg
+ cflags-$(CONFIG_CPU_MIPS64_R1) += -march=mips64 -Wa,--trap
--- /dev/null
+From 87ec87c2ad615c1a177cd08ef5fa29fc739f6e50 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sun, 23 Dec 2018 18:06:53 +0100
+Subject: [PATCH] MIPS: Add CPU option reporting to /proc/cpuinfo
+
+Many MIPS CPUs have optional CPU features which are not activates for
+all CPU cores. Print the CPU options which are implemented in the core
+in /proc/cpuinfo. This makes it possible to see what features are
+supported and which are not supported. This should cover all standard
+MIPS extensions, before it only printed information about the main MIPS
+ASEs.
+
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+---
+ arch/mips/kernel/proc.c | 116 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 116 insertions(+)
+
+--- a/arch/mips/kernel/proc.c
++++ b/arch/mips/kernel/proc.c
+@@ -138,6 +138,120 @@ static int show_cpuinfo(struct seq_file
+ seq_printf(m, "micromips kernel\t: %s\n",
+ (read_c0_config3() & MIPS_CONF3_ISA_OE) ? "yes" : "no");
+ }
++
++ seq_printf(m, "Options implemented\t:");
++ if (cpu_has_tlb)
++ seq_printf(m, "%s", " tlb");
++ if (cpu_has_ftlb)
++ seq_printf(m, "%s", " ftlb");
++ if (cpu_has_tlbinv)
++ seq_printf(m, "%s", " tlbinv");
++ if (cpu_has_segments)
++ seq_printf(m, "%s", " segments");
++ if (cpu_has_rixiex)
++ seq_printf(m, "%s", " rixiex");
++ if (cpu_has_ldpte)
++ seq_printf(m, "%s", " ldpte");
++ if (cpu_has_maar)
++ seq_printf(m, "%s", " maar");
++ if (cpu_has_rw_llb)
++ seq_printf(m, "%s", " rw_llb");
++ if (cpu_has_4kex)
++ seq_printf(m, "%s", " 4kex");
++ if (cpu_has_3k_cache)
++ seq_printf(m, "%s", " 3k_cache");
++ if (cpu_has_4k_cache)
++ seq_printf(m, "%s", " 4k_cache");
++ if (cpu_has_6k_cache)
++ seq_printf(m, "%s", " 6k_cache");
++ if (cpu_has_8k_cache)
++ seq_printf(m, "%s", " 8k_cache");
++ if (cpu_has_tx39_cache)
++ seq_printf(m, "%s", " tx39_cache");
++ if (cpu_has_octeon_cache)
++ seq_printf(m, "%s", " octeon_cache");
++ if (cpu_has_fpu)
++ seq_printf(m, "%s", " fpu");
++ if (cpu_has_32fpr)
++ seq_printf(m, "%s", " 32fpr");
++ if (cpu_has_cache_cdex_p)
++ seq_printf(m, "%s", " cache_cdex_p");
++ if (cpu_has_cache_cdex_s)
++ seq_printf(m, "%s", " cache_cdex_s");
++ if (cpu_has_prefetch)
++ seq_printf(m, "%s", " prefetch");
++ if (cpu_has_mcheck)
++ seq_printf(m, "%s", " mcheck");
++ if (cpu_has_ejtag)
++ seq_printf(m, "%s", " ejtag");
++ if (cpu_has_llsc)
++ seq_printf(m, "%s", " llsc");
++ if (cpu_has_guestctl0ext)
++ seq_printf(m, "%s", " guestctl0ext");
++ if (cpu_has_guestctl1)
++ seq_printf(m, "%s", " guestctl1");
++ if (cpu_has_guestctl2)
++ seq_printf(m, "%s", " guestctl2");
++ if (cpu_has_guestid)
++ seq_printf(m, "%s", " guestid");
++ if (cpu_has_drg)
++ seq_printf(m, "%s", " drg");
++ if (cpu_has_rixi)
++ seq_printf(m, "%s", " rixi");
++ if (cpu_has_lpa)
++ seq_printf(m, "%s", " lpa");
++ if (cpu_has_mvh)
++ seq_printf(m, "%s", " mvh");
++ if (cpu_has_vtag_icache)
++ seq_printf(m, "%s", " vtag_icache");
++ if (cpu_has_dc_aliases)
++ seq_printf(m, "%s", " dc_aliases");
++ if (cpu_has_ic_fills_f_dc)
++ seq_printf(m, "%s", " ic_fills_f_dc");
++ if (cpu_has_pindexed_dcache)
++ seq_printf(m, "%s", " pindexed_dcache");
++ if (cpu_has_userlocal)
++ seq_printf(m, "%s", " userlocal");
++ if (cpu_has_nofpuex)
++ seq_printf(m, "%s", " nofpuex");
++ if (cpu_has_vint)
++ seq_printf(m, "%s", " vint");
++ if (cpu_has_veic)
++ seq_printf(m, "%s", " veic");
++ if (cpu_has_inclusive_pcaches)
++ seq_printf(m, "%s", " inclusive_pcaches");
++ if (cpu_has_perf_cntr_intr_bit)
++ seq_printf(m, "%s", " perf_cntr_intr_bit");
++ if (cpu_has_ufr)
++ seq_printf(m, "%s", " ufr");
++ if (cpu_has_fre)
++ seq_printf(m, "%s", " fre");
++ if (cpu_has_cdmm)
++ seq_printf(m, "%s", " cdmm");
++ if (cpu_has_small_pages)
++ seq_printf(m, "%s", " small_pages");
++ if (cpu_has_nan_legacy)
++ seq_printf(m, "%s", " nan_legacy");
++ if (cpu_has_nan_2008)
++ seq_printf(m, "%s", " nan_2008");
++ if (cpu_has_ebase_wg)
++ seq_printf(m, "%s", " ebase_wg");
++ if (cpu_has_badinstr)
++ seq_printf(m, "%s", " badinstr");
++ if (cpu_has_badinstrp)
++ seq_printf(m, "%s", " badinstrp");
++ if (cpu_has_contextconfig)
++ seq_printf(m, "%s", " contextconfig");
++ if (cpu_has_perf)
++ seq_printf(m, "%s", " perf");
++ if (cpu_has_shared_ftlb_ram)
++ seq_printf(m, "%s", " shared_ftlb_ram");
++ if (cpu_has_shared_ftlb_entries)
++ seq_printf(m, "%s", " shared_ftlb_entries");
++ if (cpu_has_mipsmt_pertccounters)
++ seq_printf(m, "%s", " mipsmt_pertccounters");
++ seq_printf(m, "\n");
++
+ seq_printf(m, "shadow register sets\t: %d\n",
+ cpu_data[n].srsets);
+ seq_printf(m, "kscratch registers\t: %d\n",
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: fix errors in unresolved weak symbols on arm
+
+lede-commit: 570699d4838a907c3ef9f2819bf19eb72997b32f
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ arch/arm/kernel/module.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/arch/arm/kernel/module.c
++++ b/arch/arm/kernel/module.c
+@@ -105,6 +105,10 @@ apply_relocate(Elf32_Shdr *sechdrs, cons
+ return -ENOEXEC;
+ }
+
++ if ((IS_ERR_VALUE(sym->st_value) || !sym->st_value) &&
++ ELF_ST_BIND(sym->st_info) == STB_WEAK)
++ continue;
++
+ loc = dstsec->sh_addr + rel->r_offset;
+
+ switch (ELF32_R_TYPE(rel->r_info)) {
--- /dev/null
+From: Yousong Zhou <yszhou4tech@gmail.com>
+Subject: MIPS: kexec: Accept command line parameters from userspace.
+
+Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com>
+---
+ arch/mips/kernel/machine_kexec.c | 153 +++++++++++++++++++++++++++++++-----
+ arch/mips/kernel/machine_kexec.h | 20 +++++
+ arch/mips/kernel/relocate_kernel.S | 21 +++--
+ 3 files changed, 167 insertions(+), 27 deletions(-)
+ create mode 100644 arch/mips/kernel/machine_kexec.h
+
+--- a/arch/mips/kernel/machine_kexec.c
++++ b/arch/mips/kernel/machine_kexec.c
+@@ -9,14 +9,11 @@
+ #include <linux/delay.h>
+ #include <linux/libfdt.h>
+
++#include <asm/bootinfo.h>
+ #include <asm/cacheflush.h>
+ #include <asm/page.h>
+-
+-extern const unsigned char relocate_new_kernel[];
+-extern const size_t relocate_new_kernel_size;
+-
+-extern unsigned long kexec_start_address;
+-extern unsigned long kexec_indirection_page;
++#include <linux/uaccess.h>
++#include "machine_kexec.h"
+
+ static unsigned long reboot_code_buffer;
+
+@@ -30,6 +27,101 @@ void (*_crash_smp_send_stop)(void) = NUL
+ void (*_machine_kexec_shutdown)(void) = NULL;
+ void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
+
++static void machine_kexec_print_args(void)
++{
++ unsigned long argc = (int)kexec_args[0];
++ int i;
++
++ pr_info("kexec_args[0] (argc): %lu\n", argc);
++ pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]);
++ pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]);
++ pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
++
++ for (i = 0; i < argc; i++) {
++ pr_info("kexec_argv[%d] = %p, %s\n",
++ i, kexec_argv[i], kexec_argv[i]);
++ }
++}
++
++static void machine_kexec_init_argv(struct kimage *image)
++{
++ void __user *buf = NULL;
++ size_t bufsz;
++ size_t size;
++ int i;
++
++ bufsz = 0;
++ for (i = 0; i < image->nr_segments; i++) {
++ struct kexec_segment *seg;
++
++ seg = &image->segment[i];
++ if (seg->bufsz < 6)
++ continue;
++
++ if (strncmp((char *) seg->buf, "kexec ", 6))
++ continue;
++
++ buf = seg->buf;
++ bufsz = seg->bufsz;
++ break;
++ }
++
++ if (!buf)
++ return;
++
++ size = KEXEC_COMMAND_LINE_SIZE;
++ size = min(size, bufsz);
++ if (size < bufsz)
++ pr_warn("kexec command line truncated to %zd bytes\n", size);
++
++ /* Copy to kernel space */
++ if (copy_from_user(kexec_argv_buf, buf, size))
++ pr_warn("kexec command line copy to kernel space failed\n");
++
++ kexec_argv_buf[size - 1] = 0;
++}
++
++static void machine_kexec_parse_argv(struct kimage *image)
++{
++ char *reboot_code_buffer;
++ int reloc_delta;
++ char *ptr;
++ int argc;
++ int i;
++
++ ptr = kexec_argv_buf;
++ argc = 0;
++
++ /*
++ * convert command line string to array of parameters
++ * (as bootloader does).
++ */
++ while (ptr && *ptr && (KEXEC_MAX_ARGC > argc)) {
++ if (*ptr == ' ') {
++ *ptr++ = '\0';
++ continue;
++ }
++
++ kexec_argv[argc++] = ptr;
++ ptr = strchr(ptr, ' ');
++ }
++
++ if (!argc)
++ return;
++
++ kexec_args[0] = argc;
++ kexec_args[1] = (unsigned long)kexec_argv;
++ kexec_args[2] = 0;
++ kexec_args[3] = 0;
++
++ reboot_code_buffer = page_address(image->control_code_page);
++ reloc_delta = reboot_code_buffer - (char *)kexec_relocate_new_kernel;
++
++ kexec_args[1] += reloc_delta;
++ for (i = 0; i < argc; i++)
++ kexec_argv[i] += reloc_delta;
++}
++
+ static void kexec_image_info(const struct kimage *kimage)
+ {
+ unsigned long i;
+@@ -99,6 +191,18 @@ machine_kexec_prepare(struct kimage *kim
+ #endif
+
+ kexec_image_info(kimage);
++ /*
++ * Whenever arguments passed from kexec-tools, Init the arguments as
++ * the original ones to try avoiding booting failure.
++ */
++
++ kexec_args[0] = fw_arg0;
++ kexec_args[1] = fw_arg1;
++ kexec_args[2] = fw_arg2;
++ kexec_args[3] = fw_arg3;
++
++ machine_kexec_init_argv(kimage);
++ machine_kexec_parse_argv(kimage);
+
+ if (_machine_kexec_prepare)
+ return _machine_kexec_prepare(kimage);
+@@ -161,7 +265,7 @@ machine_crash_shutdown(struct pt_regs *r
+ void kexec_nonboot_cpu_jump(void)
+ {
+ local_flush_icache_range((unsigned long)relocated_kexec_smp_wait,
+- reboot_code_buffer + relocate_new_kernel_size);
++ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE);
+
+ relocated_kexec_smp_wait(NULL);
+ }
+@@ -199,7 +303,7 @@ void kexec_reboot(void)
+ * machine_kexec() CPU.
+ */
+ local_flush_icache_range(reboot_code_buffer,
+- reboot_code_buffer + relocate_new_kernel_size);
++ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE);
+
+ do_kexec = (void *)reboot_code_buffer;
+ do_kexec();
+@@ -212,10 +316,12 @@ machine_kexec(struct kimage *image)
+ unsigned long *ptr;
+
+ reboot_code_buffer =
+- (unsigned long)page_address(image->control_code_page);
++ (unsigned long)page_address(image->control_code_page);
++ pr_info("reboot_code_buffer = %p\n", (void *)reboot_code_buffer);
+
+ kexec_start_address =
+ (unsigned long) phys_to_virt(image->start);
++ pr_info("kexec_start_address = %p\n", (void *)kexec_start_address);
+
+ if (image->type == KEXEC_TYPE_DEFAULT) {
+ kexec_indirection_page =
+@@ -223,9 +329,19 @@ machine_kexec(struct kimage *image)
+ } else {
+ kexec_indirection_page = (unsigned long)&image->head;
+ }
++ pr_info("kexec_indirection_page = %p\n", (void *)kexec_indirection_page);
+
+- memcpy((void*)reboot_code_buffer, relocate_new_kernel,
+- relocate_new_kernel_size);
++ pr_info("Where is memcpy: %p\n", memcpy);
++ pr_info("kexec_relocate_new_kernel = %p, kexec_relocate_new_kernel_end = %p\n",
++ (void *)kexec_relocate_new_kernel, &kexec_relocate_new_kernel_end);
++ pr_info("Copy %lu bytes from %p to %p\n", KEXEC_RELOCATE_NEW_KERNEL_SIZE,
++ (void *)kexec_relocate_new_kernel, (void *)reboot_code_buffer);
++ memcpy((void*)reboot_code_buffer, kexec_relocate_new_kernel,
++ KEXEC_RELOCATE_NEW_KERNEL_SIZE);
++
++ pr_info("Before _print_args().\n");
++ machine_kexec_print_args();
++ pr_info("Before eval loop.\n");
+
+ /*
+ * The generic kexec code builds a page list with physical
+@@ -256,7 +372,7 @@ machine_kexec(struct kimage *image)
+ #ifdef CONFIG_SMP
+ /* All secondary cpus now may jump to kexec_wait cycle */
+ relocated_kexec_smp_wait = reboot_code_buffer +
+- (void *)(kexec_smp_wait - relocate_new_kernel);
++ (void *)(kexec_smp_wait - kexec_relocate_new_kernel);
+ smp_wmb();
+ atomic_set(&kexec_ready_to_reboot, 1);
+ #endif
+--- /dev/null
++++ b/arch/mips/kernel/machine_kexec.h
+@@ -0,0 +1,20 @@
++#ifndef _MACHINE_KEXEC_H
++#define _MACHINE_KEXEC_H
++
++#ifndef __ASSEMBLY__
++extern const unsigned char kexec_relocate_new_kernel[];
++extern unsigned long kexec_relocate_new_kernel_end;
++extern unsigned long kexec_start_address;
++extern unsigned long kexec_indirection_page;
++
++extern char kexec_argv_buf[];
++extern char *kexec_argv[];
++
++#define KEXEC_RELOCATE_NEW_KERNEL_SIZE ((unsigned long)&kexec_relocate_new_kernel_end - (unsigned long)kexec_relocate_new_kernel)
++#endif /* !__ASSEMBLY__ */
++
++#define KEXEC_COMMAND_LINE_SIZE 256
++#define KEXEC_ARGV_SIZE (KEXEC_COMMAND_LINE_SIZE / 16)
++#define KEXEC_MAX_ARGC (KEXEC_ARGV_SIZE / sizeof(long))
++
++#endif
+--- a/arch/mips/kernel/relocate_kernel.S
++++ b/arch/mips/kernel/relocate_kernel.S
+@@ -10,8 +10,9 @@
+ #include <asm/mipsregs.h>
+ #include <asm/stackframe.h>
+ #include <asm/addrspace.h>
++#include "machine_kexec.h"
+
+-LEAF(relocate_new_kernel)
++LEAF(kexec_relocate_new_kernel)
+ PTR_L a0, arg0
+ PTR_L a1, arg1
+ PTR_L a2, arg2
+@@ -96,7 +97,7 @@ done:
+ #endif
+ /* jump to kexec_start_address */
+ j s1
+- END(relocate_new_kernel)
++ END(kexec_relocate_new_kernel)
+
+ #ifdef CONFIG_SMP
+ /*
+@@ -182,9 +183,15 @@ kexec_indirection_page:
+ PTR 0
+ .size kexec_indirection_page, PTRSIZE
+
+-relocate_new_kernel_end:
++kexec_argv_buf:
++ EXPORT(kexec_argv_buf)
++ .skip KEXEC_COMMAND_LINE_SIZE
++ .size kexec_argv_buf, KEXEC_COMMAND_LINE_SIZE
++
++kexec_argv:
++ EXPORT(kexec_argv)
++ .skip KEXEC_ARGV_SIZE
++ .size kexec_argv, KEXEC_ARGV_SIZE
+
+-relocate_new_kernel_size:
+- EXPORT(relocate_new_kernel_size)
+- PTR relocate_new_kernel_end - relocate_new_kernel
+- .size relocate_new_kernel_size, PTRSIZE
++kexec_relocate_new_kernel_end:
++ EXPORT(kexec_relocate_new_kernel_end)
--- /dev/null
+From bb0c3b0175240bf152fd7c644821a0cf9f77c37c Mon Sep 17 00:00:00 2001
+From: Evgeniy Didin <Evgeniy.Didin@synopsys.com>
+Date: Fri, 15 Mar 2019 18:53:38 +0300
+Subject: [PATCH] arc add OWRTDTB section
+
+This change allows OpenWRT to patch resulting kernel binary with
+external .dtb.
+
+That allows us to re-use exactky the same vmlinux on different boards
+given its ARC core configurations match (at least cache line sizes etc).
+
+""patch-dtb" searches for ASCII "OWRTDTB:" strign and copies external
+.dtb right after it, keeping the string in place.
+
+Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
+Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
+Signed-off-by: Evgeniy Didin <Evgeniy.Didin@synopsys.com>
+---
+ arch/arc/kernel/head.S | 10 ++++++++++
+ arch/arc/kernel/setup.c | 4 +++-
+ arch/arc/kernel/vmlinux.lds.S | 13 +++++++++++++
+ 3 files changed, 26 insertions(+), 1 deletion(-)
+
+--- a/arch/arc/kernel/head.S
++++ b/arch/arc/kernel/head.S
+@@ -88,6 +88,16 @@
+ DSP_EARLY_INIT
+ .endm
+
++ ; Here "patch-dtb" will embed external .dtb
++ ; Note "patch-dtb" searches for ASCII "OWRTDTB:" string
++ ; and pastes .dtb right after it, hense the string precedes
++ ; __image_dtb symbol.
++ .section .owrt, "aw",@progbits
++ .ascii "OWRTDTB:"
++ENTRY(__image_dtb)
++ .fill 0x4000
++END(__image_dtb)
++
+ .section .init.text, "ax",@progbits
+
+ ;----------------------------------------------------------------
+--- a/arch/arc/kernel/setup.c
++++ b/arch/arc/kernel/setup.c
+@@ -495,6 +495,8 @@ static inline bool uboot_arg_invalid(uns
+ /* We always pass 0 as magic from U-boot */
+ #define UBOOT_MAGIC_VALUE 0
+
++extern struct boot_param_header __image_dtb;
++
+ void __init handle_uboot_args(void)
+ {
+ bool use_embedded_dtb = true;
+@@ -533,7 +535,7 @@ void __init handle_uboot_args(void)
+ ignore_uboot_args:
+
+ if (use_embedded_dtb) {
+- machine_desc = setup_machine_fdt(__dtb_start);
++ machine_desc = setup_machine_fdt(&__image_dtb);
+ if (!machine_desc)
+ panic("Embedded DT invalid\n");
+ }
+--- a/arch/arc/kernel/vmlinux.lds.S
++++ b/arch/arc/kernel/vmlinux.lds.S
+@@ -27,6 +27,19 @@ SECTIONS
+
+ . = CONFIG_LINUX_LINK_BASE;
+
++ /*
++ * In OpenWRT we want to patch built binary embedding .dtb of choice.
++ * This is implemented with "patch-dtb" utility which searches for
++ * "OWRTDTB:" string in first 16k of image and if it is found
++ * copies .dtb right after mentioned string.
++ *
++ * Note: "OWRTDTB:" won't be overwritten with .dtb, .dtb will follow it.
++ */
++ .owrt : {
++ *(.owrt)
++ . = ALIGN(PAGE_SIZE);
++ }
++
+ _int_vec_base_lds = .;
+ .vector : {
+ *(.vector)
--- /dev/null
+From: Alexey Brodkin <abrodkin@synopsys.com>
+Subject: arc: enable unaligned access in kernel mode
+
+This enables misaligned access handling even in kernel mode.
+Some wireless drivers (ath9k-htc and mt7601u) use misaligned accesses
+here and there and to cope with that without fixing stuff in the drivers
+we're just gracefully handling it on ARC.
+
+Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
+---
+ arch/arc/kernel/unaligned.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arc/kernel/unaligned.c
++++ b/arch/arc/kernel/unaligned.c
+@@ -202,7 +202,7 @@ int misaligned_fixup(unsigned long addre
+ char buf[TASK_COMM_LEN];
+
+ /* handle user mode only and only if enabled by sysadmin */
+- if (!user_mode(regs) || !unaligned_enabled)
++ if (!unaligned_enabled)
+ return 1;
+
+ if (no_unaligned_warning) {
--- /dev/null
+From 66770a004afe10df11d3902e16eaa0c2c39436bb Mon Sep 17 00:00:00 2001
+From: Pawel Dembicki <paweldembicki@gmail.com>
+Date: Fri, 24 May 2019 17:56:19 +0200
+Subject: [PATCH] powerpc: Enable kernel XZ compression option on PPC_85xx
+
+Enable kernel XZ compression option on PPC_85xx. Tested with
+simpleImage on TP-Link TL-WDR4900 (Freescale P1014 processor).
+
+Suggested-by: Christian Lamparter <chunkeey@gmail.com>
+Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
+---
+ arch/powerpc/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/powerpc/Kconfig
++++ b/arch/powerpc/Kconfig
+@@ -214,7 +214,7 @@ config PPC
+ select HAVE_KERNEL_GZIP
+ select HAVE_KERNEL_LZMA if DEFAULT_UIMAGE
+ select HAVE_KERNEL_LZO if DEFAULT_UIMAGE
+- select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x
++ select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x || PPC_85xx
+ select HAVE_KPROBES
+ select HAVE_KPROBES_ON_FTRACE
+ select HAVE_KRETPROBES
--- /dev/null
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -12,6 +12,25 @@ menuconfig MTD
+
+ if MTD
+
++menu "OpenWrt specific MTD options"
++
++config MTD_ROOTFS_ROOT_DEV
++ bool "Automatically set 'rootfs' partition to be root filesystem"
++ default y
++
++config MTD_SPLIT_FIRMWARE
++ bool "Automatically split firmware partition for kernel+rootfs"
++ default y
++
++config MTD_SPLIT_FIRMWARE_NAME
++ string "Firmware partition name"
++ depends on MTD_SPLIT_FIRMWARE
++ default "firmware"
++
++source "drivers/mtd/mtdsplit/Kconfig"
++
++endmenu
++
+ config MTD_TESTS
+ tristate "MTD tests support (DANGEROUS)"
+ depends on m
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -15,10 +15,12 @@
+ #include <linux/kmod.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/magic.h>
+ #include <linux/err.h>
+ #include <linux/of.h>
+
+ #include "mtdcore.h"
++#include "mtdsplit/mtdsplit.h"
+
+ /*
+ * MTD methods which simply translate the effective address and pass through
+@@ -236,6 +238,146 @@ static int mtd_add_partition_attrs(struc
+ return ret;
+ }
+
++static DEFINE_SPINLOCK(part_parser_lock);
++static LIST_HEAD(part_parsers);
++
++static struct mtd_part_parser *mtd_part_parser_get(const char *name)
++{
++ struct mtd_part_parser *p, *ret = NULL;
++
++ spin_lock(&part_parser_lock);
++
++ list_for_each_entry(p, &part_parsers, list)
++ if (!strcmp(p->name, name) && try_module_get(p->owner)) {
++ ret = p;
++ break;
++ }
++
++ spin_unlock(&part_parser_lock);
++
++ return ret;
++}
++
++static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
++{
++ module_put(p->owner);
++}
++
++static struct mtd_part_parser *
++get_partition_parser_by_type(enum mtd_parser_type type,
++ struct mtd_part_parser *start)
++{
++ struct mtd_part_parser *p, *ret = NULL;
++
++ spin_lock(&part_parser_lock);
++
++ p = list_prepare_entry(start, &part_parsers, list);
++ if (start)
++ mtd_part_parser_put(start);
++
++ list_for_each_entry_continue(p, &part_parsers, list) {
++ if (p->type == type && try_module_get(p->owner)) {
++ ret = p;
++ break;
++ }
++ }
++
++ spin_unlock(&part_parser_lock);
++
++ return ret;
++}
++
++static int parse_mtd_partitions_by_type(struct mtd_info *master,
++ enum mtd_parser_type type,
++ const struct mtd_partition **pparts,
++ struct mtd_part_parser_data *data)
++{
++ struct mtd_part_parser *prev = NULL;
++ int ret = 0;
++
++ while (1) {
++ struct mtd_part_parser *parser;
++
++ parser = get_partition_parser_by_type(type, prev);
++ if (!parser)
++ break;
++
++ ret = (*parser->parse_fn)(master, pparts, data);
++
++ if (ret > 0) {
++ mtd_part_parser_put(parser);
++ printk(KERN_NOTICE
++ "%d %s partitions found on MTD device %s\n",
++ ret, parser->name, master->name);
++ break;
++ }
++
++ prev = parser;
++ }
++
++ return ret;
++}
++
++static int
++run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type)
++{
++ struct mtd_partition *parts;
++ int nr_parts;
++ int i;
++
++ nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts,
++ NULL);
++ if (nr_parts <= 0)
++ return nr_parts;
++
++ if (WARN_ON(!parts))
++ return 0;
++
++ for (i = 0; i < nr_parts; i++) {
++ /* adjust partition offsets */
++ parts[i].offset += child->part.offset;
++
++ mtd_add_partition(child->parent,
++ parts[i].name,
++ parts[i].offset,
++ parts[i].size);
++ }
++
++ kfree(parts);
++
++ return nr_parts;
++}
++
++#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME
++#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME
++#else
++#define SPLIT_FIRMWARE_NAME "unused"
++#endif
++
++static void split_firmware(struct mtd_info *master, struct mtd_info *part)
++{
++ run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE);
++}
++
++static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part)
++{
++ static int rootfs_found = 0;
++
++ if (rootfs_found)
++ return;
++
++ if (!strcmp(part->name, "rootfs")) {
++ run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS);
++
++ rootfs_found = 1;
++ }
++
++ if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) &&
++ !strcmp(part->name, SPLIT_FIRMWARE_NAME) &&
++ !of_find_property(mtd_get_of_node(part), "compatible", NULL))
++ split_firmware(master, part);
++}
++
+ int mtd_add_partition(struct mtd_info *parent, const char *name,
+ long long offset, long long length)
+ {
+@@ -274,6 +416,7 @@ int mtd_add_partition(struct mtd_info *p
+ if (ret)
+ goto err_remove_part;
+
++ mtd_partition_split(parent, child);
+ mtd_add_partition_attrs(child);
+
+ return 0;
+@@ -422,6 +565,7 @@ int add_mtd_partitions(struct mtd_info *
+ goto err_del_partitions;
+ }
+
++ mtd_partition_split(master, child);
+ mtd_add_partition_attrs(child);
+
+ /* Look for subpartitions */
+@@ -438,31 +582,6 @@ err_del_partitions:
+ return ret;
+ }
+
+-static DEFINE_SPINLOCK(part_parser_lock);
+-static LIST_HEAD(part_parsers);
+-
+-static struct mtd_part_parser *mtd_part_parser_get(const char *name)
+-{
+- struct mtd_part_parser *p, *ret = NULL;
+-
+- spin_lock(&part_parser_lock);
+-
+- list_for_each_entry(p, &part_parsers, list)
+- if (!strcmp(p->name, name) && try_module_get(p->owner)) {
+- ret = p;
+- break;
+- }
+-
+- spin_unlock(&part_parser_lock);
+-
+- return ret;
+-}
+-
+-static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
+-{
+- module_put(p->owner);
+-}
+-
+ /*
+ * Many partition parsers just expected the core to kfree() all their data in
+ * one chunk. Do that by default.
+--- a/include/linux/mtd/partitions.h
++++ b/include/linux/mtd/partitions.h
+@@ -75,6 +75,12 @@ struct mtd_part_parser_data {
+ * Functions dealing with the various ways of partitioning the space
+ */
+
++enum mtd_parser_type {
++ MTD_PARSER_TYPE_DEVICE = 0,
++ MTD_PARSER_TYPE_ROOTFS,
++ MTD_PARSER_TYPE_FIRMWARE,
++};
++
+ struct mtd_part_parser {
+ struct list_head list;
+ struct module *owner;
+@@ -83,6 +89,7 @@ struct mtd_part_parser {
+ int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
+ struct mtd_part_parser_data *);
+ void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
++ enum mtd_parser_type type;
+ };
+
+ /* Container for passing around a set of parsed partitions */
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconc
+
+ obj-y += parsers/
+
++obj-$(CONFIG_MTD_SPLIT) += mtdsplit/
++
+ # 'Users' - code which presents functionality to userspace.
+ obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
+ obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -608,6 +608,24 @@ static inline void mtd_align_erase_req(s
+ req->len += mtd->erasesize - mod;
+ }
+
++static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd)
++{
++ if (mtd_mod_by_eb(sz, mtd) == 0)
++ return sz;
++
++ /* Round up to next erase block */
++ return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize;
++}
++
++static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd)
++{
++ if (mtd_mod_by_eb(sz, mtd) == 0)
++ return sz;
++
++ /* Round down to the start of the current erase block */
++ return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize;
++}
++
+ static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
+ {
+ if (mtd->writesize_shift)
+@@ -680,6 +698,13 @@ extern void __put_mtd_device(struct mtd_
+ extern struct mtd_info *get_mtd_device_nm(const char *name);
+ extern void put_mtd_device(struct mtd_info *mtd);
+
++static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
++{
++ if (!mtd_is_partition(mtd))
++ return 0;
++
++ return mtd->part.offset;
++}
+
+ struct mtd_notifier {
+ void (*add)(struct mtd_info *mtd);
--- /dev/null
+From patchwork Tue Jun 8 04:07:19 2021
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Submitter: John Thomson <git@johnthomson.fastmail.com.au>
+X-Patchwork-Id: 1489105
+X-Patchwork-Delegate: tudor.ambarus@gmail.com
+Return-Path:
+ <linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org>
+X-Original-To: incoming@patchwork.ozlabs.org
+Delivered-To: patchwork-incoming@bilbo.ozlabs.org
+Authentication-Results: ozlabs.org;
+ spf=none (no SPF record) smtp.mailfrom=lists.infradead.org
+ (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org;
+ envelope-from=linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org;
+ receiver=<UNKNOWN>)
+Authentication-Results: ozlabs.org;
+ dkim=pass (2048-bit key;
+ secure) header.d=lists.infradead.org header.i=@lists.infradead.org
+ header.a=rsa-sha256 header.s=bombadil.20210309 header.b=EMabhVoR;
+ dkim=fail reason="signature verification failed" (2048-bit key;
+ unprotected) header.d=fastmail.com.au header.i=@fastmail.com.au
+ header.a=rsa-sha256 header.s=fm3 header.b=dLzuZ6dB;
+ dkim=fail reason="signature verification failed" (2048-bit key;
+ unprotected) header.d=messagingengine.com header.i=@messagingengine.com
+ header.a=rsa-sha256 header.s=fm3 header.b=nSRGsW+C;
+ dkim-atps=neutral
+Received: from bombadil.infradead.org (bombadil.infradead.org
+ [IPv6:2607:7c80:54:e::133])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest
+ SHA256)
+ (No client certificate requested)
+ by ozlabs.org (Postfix) with ESMTPS id 4FzcFN1j1nz9sW8
+ for <incoming@patchwork.ozlabs.org>; Tue, 8 Jun 2021 14:09:28 +1000 (AEST)
+DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
+ d=lists.infradead.org; s=bombadil.20210309; h=Sender:
+ Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post:
+ List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-Id:Date:Subject:Cc
+ :To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:
+ Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:
+ List-Owner; bh=6mUWQd71FwsINycGYY1qOhKz+ecWJVNtwDkTebG3XkA=; b=EMabhVoRE3ad89
+ o3L2AgyKrs+blSofUC3hoSsQe7gi3m4si8S9HW8Z+8SsS5TufUsvGwDl80qSYGlQOytQF+1yRUWvE
+ 6FJ/+bqv+TwjqZFibgJ6+9OVsQN9dZ/no1R0bBXIpmrf8ORUmv58QK4ZQquaFKbyXKpFeWOC2MSv4
+ H2MAhyhTU8a3gtooH6G8+KvsJEfVgh6C+aDbwxyh2UY3chHKuw1kvL6AktbfUE2xl4zxi3x3kc70B
+ Wi3LiJBFokxVdgnROXxTU5tI0XboWYkQV64gLuQNV4XKClcuhVpzloDK8Iok6NTd7b32a7TdEFlCS
+ lGKsEKmxtUlW2FpfoduA==;
+Received: from localhost ([::1] helo=bombadil.infradead.org)
+ by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux))
+ id 1lqT1r-006OAW-DX; Tue, 08 Jun 2021 04:07:51 +0000
+Received: from new1-smtp.messagingengine.com ([66.111.4.221])
+ by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux))
+ id 1lqT1l-006O9b-Fq
+ for linux-mtd@lists.infradead.org; Tue, 08 Jun 2021 04:07:50 +0000
+Received: from compute2.internal (compute2.nyi.internal [10.202.2.42])
+ by mailnew.nyi.internal (Postfix) with ESMTP id 4456B580622;
+ Tue, 8 Jun 2021 00:07:42 -0400 (EDT)
+Received: from mailfrontend2 ([10.202.2.163])
+ by compute2.internal (MEProxy); Tue, 08 Jun 2021 00:07:42 -0400
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fastmail.com.au;
+ h=from:to:cc:subject:date:message-id:mime-version
+ :content-transfer-encoding; s=fm3; bh=ZXRH+YluM1mHCS1EWUiCY/Sg8O
+ LccfHe1oW5iAay6y8=; b=dLzuZ6dBYf7ZA8tWLOBFZYLi7ERsGe/4vnMXG+ovvb
+ dNBO0+SaFGwoqYSFrfq/TeyHfKyvxrA7+LCdopIuT4abpLHxtRwtRiafQcDYCPat
+ qJIqOZO+wCZC5S9Jc1OP7+t1FviGpgevqIMotci37P+RWc5u3AweMzFljZk90E8C
+ uorV6rXagD+OssJQzllRnAIK88+rOAC9ZyXv2gWxy4d1HSCwSWgzx2vnV9CNp918
+ YC/3tiHas9krbrPIaAsdBROr7Bvoe/ShRRzruKRuvZVgg5NN90vX+/5ZjI8u04GM
+ p2bWCbC62CP6wlcgDaz+c/Sgr5ITd2GPENJsHfqmLRBA==
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
+ messagingengine.com; h=cc:content-transfer-encoding:date:from
+ :message-id:mime-version:subject:to:x-me-proxy:x-me-proxy
+ :x-me-sender:x-me-sender:x-sasl-enc; s=fm3; bh=ZXRH+YluM1mHCS1EW
+ UiCY/Sg8OLccfHe1oW5iAay6y8=; b=nSRGsW+CQ2Zx1RVpIUu8W/VD/k5P+32BW
+ 5k2ltd+UhI3dfldBPzHrYiOP/IJqGkNW+V+rHASacW/vFygnaZoxNjRYKnOsu+26
+ wb2yK3jpl6lsNTg3N1Z4XJrYY2lf9H29DMFbhC67l0PTc050rcZk4XsKTLAlv14Q
+ VA4WREYSaX/4IN4O+ES4TMq0a/3gKZh6nvbbJXbsXfK0WlSHTGZtZmW3fyrqvbXa
+ t+R7L8vvqWvwls0pV+Sn8LeQqb7+A69w0UOnuznjkcA3sCc2YehcHbxcUEnMH+9N
+ bxOjmIDeg9/4X/829tUWUJiLhE5SFmQZ1P6oFtmbWoLrDz0ZJIVBw==
+X-ME-Sender: <xms:C-2-YD2uka4HsA6gcdsV2Ia7vebY4Yjp9E8q7KBMb54jnAzGL7-67Q>
+ <xme:C-2-YCEaxASy5VlcrvNO_jLFpMDGkFCRsuVNuZGEQsiRZygk8jPHWq7unPjeT6uYS
+ 2pUP6PrTQ2rggjEIg>
+X-ME-Received:
+ <xmr:C-2-YD4exeK49N_YZWWf2BWDhVyCbCY3wwvjTyDOFxeugx7Jg08pzMUToo9oJjrBpcVTaA3kbfk>
+X-ME-Proxy-Cause:
+ gggruggvucftvghtrhhoucdtuddrgeduledrfedtkedgjeduucetufdoteggodetrfdotf
+ fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen
+ uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne
+ cujfgurhephffvufffkffoggfgsedtkeertdertddtnecuhfhrohhmpeflohhhnhcuvfhh
+ ohhmshhonhcuoehgihhtsehjohhhnhhthhhomhhsohhnrdhfrghsthhmrghilhdrtghomh
+ drrghuqeenucggtffrrghtthgvrhhnpefffeeihfdukedtuedufeetieeuudfhhefhkefh
+ tefgtdeuffekffelleetveduieenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmh
+ epmhgrihhlfhhrohhmpehgihhtsehjohhhnhhthhhomhhsohhnrdhfrghsthhmrghilhdr
+ tghomhdrrghu
+X-ME-Proxy: <xmx:C-2-YI0AJZGjcB3wIbI9BoC9X8VNl4i9A7cQnBkvwZ25czWJlkKCLw>
+ <xmx:C-2-YGGufw99T-O81-FeiSyEruv6_Pr0IHFhspQdxjv5k1VFTZ0lzQ>
+ <xmx:C-2-YJ8BW7DhSDSCEAPSJWrwh_hHP79qreTZtWh_kOUwSh1c0MMlAg>
+ <xmx:Du2-YJBeX2Fg9oFZVXGwEJ1ZrZnXHiAqNON8tbpzquYgcm2o_LM48g>
+Received: by mail.messagingengine.com (Postfix) with ESMTPA; Tue,
+ 8 Jun 2021 00:07:35 -0400 (EDT)
+From: John Thomson <git@johnthomson.fastmail.com.au>
+To: Miquel Raynal <miquel.raynal@bootlin.com>,
+ Richard Weinberger <richard@nod.at>, Vignesh Raghavendra <vigneshr@ti.com>,
+ Tudor Ambarus <tudor.ambarus@microchip.com>,
+ Michael Walle <michael@walle.cc>, Pratyush Yadav <p.yadav@ti.com>,
+ linux-mtd@lists.infradead.org
+Cc: linux-kernel@vger.kernel.org,
+ John Thomson <git@johnthomson.fastmail.com.au>,
+ kernel test robot <lkp@intel.com>, Dan Carpenter <dan.carpenter@oracle.com>
+Subject: [PATCH] mtd: spi-nor: write support for minor aligned partitions
+Date: Tue, 8 Jun 2021 14:07:19 +1000
+Message-Id: <20210608040719.14431-1-git@johnthomson.fastmail.com.au>
+X-Mailer: git-send-email 2.31.1
+MIME-Version: 1.0
+X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3
+X-CRM114-CacheID: sfid-20210607_210745_712053_67A7D864
+X-CRM114-Status: GOOD ( 26.99 )
+X-Spam-Score: -0.8 (/)
+X-Spam-Report: Spam detection software,
+ running on the system "bombadil.infradead.org",
+ has NOT identified this incoming email as spam. The original
+ message has been attached to this so you can view it or label
+ similar future email. If you have any questions, see
+ the administrator of that system for details.
+ Content preview: Do not prevent writing to mtd partitions where a partition
+ boundary sits on a minor erasesize boundary. This addresses a FIXME that
+ has been present since the start of the linux git history: /* Doesn' [...]
+ Content analysis details: (-0.8 points, 5.0 required)
+ pts rule name description
+ ---- ----------------------
+ --------------------------------------------------
+ -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at https://www.dnswl.org/,
+ low trust [66.111.4.221 listed in list.dnswl.org]
+ -0.0 SPF_PASS SPF: sender matches SPF record
+ -0.0 SPF_HELO_PASS SPF: HELO matches SPF record
+ 0.0 RCVD_IN_MSPIKE_H3 RBL: Good reputation (+3)
+ [66.111.4.221 listed in wl.mailspike.net]
+ -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature
+ 0.1 DKIM_SIGNED Message has a DKIM or DK signature,
+ not necessarily
+ valid
+ -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from
+ envelope-from domain
+ 0.0 RCVD_IN_MSPIKE_WL Mailspike good senders
+X-BeenThere: linux-mtd@lists.infradead.org
+X-Mailman-Version: 2.1.34
+Precedence: list
+List-Id: Linux MTD discussion mailing list <linux-mtd.lists.infradead.org>
+List-Unsubscribe: <http://lists.infradead.org/mailman/options/linux-mtd>,
+ <mailto:linux-mtd-request@lists.infradead.org?subject=unsubscribe>
+List-Archive: <http://lists.infradead.org/pipermail/linux-mtd/>
+List-Post: <mailto:linux-mtd@lists.infradead.org>
+List-Help: <mailto:linux-mtd-request@lists.infradead.org?subject=help>
+List-Subscribe: <http://lists.infradead.org/mailman/listinfo/linux-mtd>,
+ <mailto:linux-mtd-request@lists.infradead.org?subject=subscribe>
+Sender: "linux-mtd" <linux-mtd-bounces@lists.infradead.org>
+Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org
+
+Do not prevent writing to mtd partitions where a partition boundary sits
+on a minor erasesize boundary.
+This addresses a FIXME that has been present since the start of the
+linux git history:
+/* Doesn't start on a boundary of major erase size */
+/* FIXME: Let it be writable if it is on a boundary of
+ * _minor_ erase size though */
+
+Allow a uniform erase region spi-nor device to be configured
+to use the non-uniform erase regions code path for an erase with:
+CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y
+
+On supporting hardware (SECT_4K: majority of current SPI-NOR device)
+provide the facility for an erase to use the least number
+of SPI-NOR operations, as well as access to 4K erase without
+requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+
+Introduce erasesize_minor to the mtd struct,
+the smallest erasesize supported by the device
+
+On existing devices, this is useful where write support is wanted
+for data on a 4K partition, such as some u-boot-env partitions,
+or RouterBoot soft_config, while still netting the performance
+benefits of using 64K sectors
+
+Performance:
+time mtd erase firmware
+OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length
+
+Without this patch
+MTD_SPI_NOR_USE_4K_SECTORS=y |n
+real 2m 11.66s |0m 50.86s
+user 0m 0.00s |0m 0.00s
+sys 1m 56.20s |0m 50.80s
+
+With this patch
+MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y |4K_SECTORS=y
+real 0m 51.68s |0m 50.85s |2m 12.89s
+user 0m 0.00s |0m 0.00s |0m 0.01s
+sys 0m 46.94s |0m 50.38s |2m 12.46s
+
+Signed-off-by: John Thomson <git@johnthomson.fastmail.com.au>
+---
+Have not tested on variable erase regions device.
+
+checkpatch does not like the printk(KERN_WARNING
+these should be changed separately beforehand?
+
+Changes RFC -> v1:
+Fix uninitialized variable smatch warning
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
+---
+ drivers/mtd/mtdpart.c | 52 ++++++++++++++++++++++++++++---------
+ drivers/mtd/spi-nor/Kconfig | 10 +++++++
+ drivers/mtd/spi-nor/core.c | 10 +++++--
+ include/linux/mtd/mtd.h | 2 ++
+ 4 files changed, 60 insertions(+), 14 deletions(-)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -40,10 +40,11 @@ static struct mtd_info *allocate_partiti
+ struct mtd_info *master = mtd_get_master(parent);
+ int wr_alignment = (parent->flags & MTD_NO_ERASE) ?
+ master->writesize : master->erasesize;
++ int wr_alignment_minor = 0;
+ u64 parent_size = mtd_is_partition(parent) ?
+ parent->part.size : parent->size;
+ struct mtd_info *child;
+- u32 remainder;
++ u32 remainder, remainder_minor;
+ char *name;
+ u64 tmp;
+
+@@ -145,6 +146,7 @@ static struct mtd_info *allocate_partiti
+ int i, max = parent->numeraseregions;
+ u64 end = child->part.offset + child->part.size;
+ struct mtd_erase_region_info *regions = parent->eraseregions;
++ uint32_t erasesize_minor = child->erasesize;
+
+ /* Find the first erase regions which is part of this
+ * partition. */
+@@ -155,15 +157,24 @@ static struct mtd_info *allocate_partiti
+ if (i > 0)
+ i--;
+
+- /* Pick biggest erasesize */
+ for (; i < max && regions[i].offset < end; i++) {
++ /* Pick biggest erasesize */
+ if (child->erasesize < regions[i].erasesize)
+ child->erasesize = regions[i].erasesize;
++ /* Pick smallest non-zero erasesize */
++ if ((erasesize_minor > regions[i].erasesize) && (regions[i].erasesize > 0))
++ erasesize_minor = regions[i].erasesize;
+ }
++
++ if (erasesize_minor < child->erasesize)
++ child->erasesize_minor = erasesize_minor;
++
+ BUG_ON(child->erasesize == 0);
+ } else {
+ /* Single erase size */
+ child->erasesize = master->erasesize;
++ if (master->erasesize_minor)
++ child->erasesize_minor = master->erasesize_minor;
+ }
+
+ /*
+@@ -171,26 +182,43 @@ static struct mtd_info *allocate_partiti
+ * exposes several regions with different erasesize. Adjust
+ * wr_alignment accordingly.
+ */
+- if (!(child->flags & MTD_NO_ERASE))
++ if (!(child->flags & MTD_NO_ERASE)) {
+ wr_alignment = child->erasesize;
++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) && child->erasesize_minor)
++ wr_alignment_minor = child->erasesize_minor;
++ }
+
+ tmp = mtd_get_master_ofs(child, 0);
+ remainder = do_div(tmp, wr_alignment);
+ if ((child->flags & MTD_WRITEABLE) && remainder) {
+- /* Doesn't start on a boundary of major erase size */
+- /* FIXME: Let it be writable if it is on a boundary of
+- * _minor_ erase size though */
+- child->flags &= ~MTD_WRITEABLE;
+- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
+- part->name);
++ if (wr_alignment_minor) {
++ tmp = mtd_get_master_ofs(child, 0);
++ remainder_minor = do_div(tmp, wr_alignment_minor);
++ if (remainder_minor == 0)
++ child->erasesize = child->erasesize_minor;
++ }
++
++ if ((!wr_alignment_minor) || (wr_alignment_minor && remainder_minor != 0)) {
++ child->flags &= ~MTD_WRITEABLE;
++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
++ part->name);
++ }
+ }
+
+ tmp = mtd_get_master_ofs(child, 0) + child->part.size;
+ remainder = do_div(tmp, wr_alignment);
+ if ((child->flags & MTD_WRITEABLE) && remainder) {
+- child->flags &= ~MTD_WRITEABLE;
+- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
+- part->name);
++ if (wr_alignment_minor) {
++ tmp = mtd_get_master_ofs(child, 0) + child->part.size;
++ remainder_minor = do_div(tmp, wr_alignment_minor);
++ if (remainder_minor == 0)
++ child->erasesize = child->erasesize_minor;
++ }
++ if ((!wr_alignment_minor) || (wr_alignment_minor && remainder_minor != 0)) {
++ child->flags &= ~MTD_WRITEABLE;
++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
++ part->name);
++ }
+ }
+
+ child->size = child->part.size;
+--- a/drivers/mtd/spi-nor/Kconfig
++++ b/drivers/mtd/spi-nor/Kconfig
+@@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR
+
+ if MTD_SPI_NOR
+
++config MTD_SPI_NOR_USE_VARIABLE_ERASE
++ bool "Disable uniform_erase to allow use of all hardware supported erasesizes"
++ depends on !MTD_SPI_NOR_USE_4K_SECTORS
++ default n
++ help
++ Allow mixed use of all hardware supported erasesizes,
++ by forcing spi_nor to use the multiple eraseregions code path.
++ For example: A 68K erase will use one 64K erase, and one 4K erase
++ on supporting hardware.
++
+ config MTD_SPI_NOR_USE_4K_SECTORS
+ bool "Use small 4096 B erase sectors"
+ default y
+--- a/drivers/mtd/spi-nor/core.c
++++ b/drivers/mtd/spi-nor/core.c
+@@ -1075,6 +1075,8 @@ static u8 spi_nor_convert_3to4_erase(u8
+
+ static bool spi_nor_has_uniform_erase(const struct spi_nor *nor)
+ {
++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE))
++ return false;
+ return !!nor->params->erase_map.uniform_erase_type;
+ }
+
+@@ -2560,6 +2562,7 @@ static int spi_nor_select_erase(struct s
+ {
+ struct spi_nor_erase_map *map = &nor->params->erase_map;
+ const struct spi_nor_erase_type *erase = NULL;
++ const struct spi_nor_erase_type *erase_minor = NULL;
+ struct mtd_info *mtd = &nor->mtd;
+ u32 wanted_size = nor->info->sector_size;
+ int i;
+@@ -2592,8 +2595,9 @@ static int spi_nor_select_erase(struct s
+ */
+ for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
+ if (map->erase_type[i].size) {
+- erase = &map->erase_type[i];
+- break;
++ if (!erase)
++ erase = &map->erase_type[i];
++ erase_minor = &map->erase_type[i];
+ }
+ }
+
+@@ -2601,6 +2605,8 @@ static int spi_nor_select_erase(struct s
+ return -EINVAL;
+
+ mtd->erasesize = erase->size;
++ if (erase_minor && erase_minor->size < erase->size)
++ mtd->erasesize_minor = erase_minor->size;
+ return 0;
+ }
+
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -242,6 +242,8 @@ struct mtd_info {
+ * information below if they desire
+ */
+ uint32_t erasesize;
++ /* "Minor" (smallest) erase size supported by the whole device */
++ uint32_t erasesize_minor;
+ /* Minimal writable flash unit size. In case of NOR flash it is 1 (even
+ * though individual bits can be cleared), in case of NAND flash it is
+ * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
--- /dev/null
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 6 May 2021 12:33:58 +0200
+Subject: [PATCH] mtd: parsers: ofpart: fix parsing subpartitions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+ofpart was recently patched to not scan random partition nodes as
+subpartitions. That change unfortunately broke scanning valid
+subpartitions like:
+
+partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ compatible = "fixed-partitions";
+ label = "bootloader";
+ reg = <0x0 0x100000>;
+
+ partition@0 {
+ label = "config";
+ reg = <0x80000 0x80000>;
+ };
+ };
+};
+
+Fix that regression by adding 1 more code path. We actually need 3
+conditional blocks to support 3 possible cases. This change also makes
+code easier to understand & follow.
+
+Reported-by: David Bauer <mail@david-bauer.net>
+Fixes: 2d751203aacf ("mtd: parsers: ofpart: limit parsing of deprecated DT syntax
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+---
+ drivers/mtd/parsers/ofpart_core.c | 26 ++++++++++++++------------
+ 1 file changed, 14 insertions(+), 12 deletions(-)
+
+--- a/drivers/mtd/parsers/ofpart_core.c
++++ b/drivers/mtd/parsers/ofpart_core.c
+@@ -57,20 +57,22 @@ static int parse_fixed_partitions(struct
+ if (!mtd_node)
+ return 0;
+
+- ofpart_node = of_get_child_by_name(mtd_node, "partitions");
+- if (!ofpart_node && !master->parent) {
+- /*
+- * We might get here even when ofpart isn't used at all (e.g.,
+- * when using another parser), so don't be louder than
+- * KERN_DEBUG
+- */
+- pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n",
+- master->name, mtd_node);
++ if (!master->parent) { /* Master */
++ ofpart_node = of_get_child_by_name(mtd_node, "partitions");
++ if (!ofpart_node) {
++ /*
++ * We might get here even when ofpart isn't used at all (e.g.,
++ * when using another parser), so don't be louder than
++ * KERN_DEBUG
++ */
++ pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n",
++ master->name, mtd_node);
++ ofpart_node = mtd_node;
++ dedicated = false;
++ }
++ } else { /* Partition */
+ ofpart_node = mtd_node;
+- dedicated = false;
+ }
+- if (!ofpart_node)
+- return 0;
+
+ of_id = of_match_node(parse_ofpart_match_table, ofpart_node);
+ if (dedicated && !of_id) {
--- /dev/null
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Subject: [PATCH] mtd: redboot: add of_match_table with DT binding
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This allows parsing RedBoot compatible partitions for properly described
+flash device in DT.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+---
+
+--- a/drivers/mtd/parsers/redboot.c
++++ b/drivers/mtd/parsers/redboot.c
+@@ -305,6 +305,7 @@ static int parse_redboot_partitions(stru
+
+ static const struct of_device_id mtd_parser_redboot_of_match_table[] = {
+ { .compatible = "redboot-fis" },
++ { .compatible = "ecoscentric,redboot-fis-partitions" },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, mtd_parser_redboot_of_match_table);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: add patch for including unpartitioned space in the rootfs partition for redboot devices (if applicable)
+
+[john@phrozen.org: used by ixp and others]
+
+lede-commit: 394918851f84e4d00fa16eb900e7700e95091f00
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/mtd/redboot.c | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+--- a/drivers/mtd/parsers/redboot.c
++++ b/drivers/mtd/parsers/redboot.c
+@@ -279,14 +279,21 @@ static int parse_redboot_partitions(stru
+ #endif
+ names += strlen(names)+1;
+
+-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+ if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
+- i++;
+- parts[i].offset = parts[i-1].size + parts[i-1].offset;
+- parts[i].size = fl->next->img->flash_base - parts[i].offset;
+- parts[i].name = nullname;
+- }
++ if (!strcmp(parts[i].name, "rootfs")) {
++ parts[i].size = fl->next->img->flash_base;
++ parts[i].size &= ~(master->erasesize - 1);
++ parts[i].size -= parts[i].offset;
++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
++ nrparts--;
++ } else {
++ i++;
++ parts[i].offset = parts[i-1].size + parts[i-1].offset;
++ parts[i].size = fl->next->img->flash_base - parts[i].offset;
++ parts[i].name = nullname;
+ #endif
++ }
++ }
+ tmp_fl = fl;
+ fl = fl->next;
+ kfree(tmp_fl);
--- /dev/null
+From: Florian Fainelli <f.fainelli@gmail.com>
+Subject: Add myloader partition table parser
+
+[john@phozen.org: shoud be upstreamable]
+
+lede-commit: d8bf22859b51faa09d22c056fe221a45d2f7a3b8
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+[adjust for kernel 5.4, add myloader.c to patch]
+Signed-off-by: Adrian Schmutzler <freifunk@adrianschmutzler.de>
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -57,6 +57,22 @@ config MTD_CMDLINE_PARTS
+
+ If unsure, say 'N'.
+
++config MTD_MYLOADER_PARTS
++ tristate "MyLoader partition parsing"
++ depends on ADM5120 || ATH25 || ATH79
++ help
++ MyLoader is a bootloader which allows the user to define partitions
++ in flash devices, by putting a table in the second erase block
++ on the device, similar to a partition table. This table gives the
++ offsets and lengths of the user defined partitions.
++
++ If you need code which can detect and parse these tables, and
++ register MTD 'partitions' corresponding to each image detected,
++ enable this option.
++
++ You will still need the parsing functions to be called by the driver
++ for your particular device. It won't happen automatically.
++
+ config MTD_OF_PARTS
+ tristate "OpenFirmware (device tree) partitioning parser"
+ default y
+--- a/drivers/mtd/parsers/Makefile
++++ b/drivers/mtd/parsers/Makefile
+@@ -3,6 +3,7 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.
+ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
+ obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
+ obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
++obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o
+ obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
+ ofpart-y += ofpart_core.o
+ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o
+--- /dev/null
++++ b/drivers/mtd/parsers/myloader.c
+@@ -0,0 +1,181 @@
++/*
++ * Parse MyLoader-style flash partition tables and produce a Linux partition
++ * array to match.
++ *
++ * Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org>
++ *
++ * This file was based on drivers/mtd/redboot.c
++ * Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/vmalloc.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/byteorder/generic.h>
++#include <linux/myloader.h>
++
++#define BLOCK_LEN_MIN 0x10000
++#define PART_NAME_LEN 32
++
++struct part_data {
++ struct mylo_partition_table tab;
++ char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN];
++};
++
++static int myloader_parse_partitions(struct mtd_info *master,
++ const struct mtd_partition **pparts,
++ struct mtd_part_parser_data *data)
++{
++ struct part_data *buf;
++ struct mylo_partition_table *tab;
++ struct mylo_partition *part;
++ struct mtd_partition *mtd_parts;
++ struct mtd_partition *mtd_part;
++ int num_parts;
++ int ret, i;
++ size_t retlen;
++ char *names;
++ unsigned long offset;
++ unsigned long blocklen;
++
++ buf = vmalloc(sizeof(*buf));
++ if (!buf) {
++ return -ENOMEM;
++ goto out;
++ }
++ tab = &buf->tab;
++
++ blocklen = master->erasesize;
++ if (blocklen < BLOCK_LEN_MIN)
++ blocklen = BLOCK_LEN_MIN;
++
++ offset = blocklen;
++
++ /* Find the partition table */
++ for (i = 0; i < 4; i++, offset += blocklen) {
++ printk(KERN_DEBUG "%s: searching for MyLoader partition table"
++ " at offset 0x%lx\n", master->name, offset);
++
++ ret = mtd_read(master, offset, sizeof(*buf), &retlen,
++ (void *)buf);
++ if (ret)
++ goto out_free_buf;
++
++ if (retlen != sizeof(*buf)) {
++ ret = -EIO;
++ goto out_free_buf;
++ }
++
++ /* Check for Partition Table magic number */
++ if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS))
++ break;
++
++ }
++
++ if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) {
++ printk(KERN_DEBUG "%s: no MyLoader partition table found\n",
++ master->name);
++ ret = 0;
++ goto out_free_buf;
++ }
++
++ /* The MyLoader and the Partition Table is always present */
++ num_parts = 2;
++
++ /* Detect number of used partitions */
++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
++ part = &tab->partitions[i];
++
++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
++ continue;
++
++ num_parts++;
++ }
++
++ mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) +
++ num_parts * PART_NAME_LEN), GFP_KERNEL);
++
++ if (!mtd_parts) {
++ ret = -ENOMEM;
++ goto out_free_buf;
++ }
++
++ mtd_part = mtd_parts;
++ names = (char *)&mtd_parts[num_parts];
++
++ strncpy(names, "myloader", PART_NAME_LEN);
++ mtd_part->name = names;
++ mtd_part->offset = 0;
++ mtd_part->size = offset;
++ mtd_part->mask_flags = MTD_WRITEABLE;
++ mtd_part++;
++ names += PART_NAME_LEN;
++
++ strncpy(names, "partition_table", PART_NAME_LEN);
++ mtd_part->name = names;
++ mtd_part->offset = offset;
++ mtd_part->size = blocklen;
++ mtd_part->mask_flags = MTD_WRITEABLE;
++ mtd_part++;
++ names += PART_NAME_LEN;
++
++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
++ part = &tab->partitions[i];
++
++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
++ continue;
++
++ if ((buf->names[i][0]) && (buf->names[i][0] != '\xff'))
++ strncpy(names, buf->names[i], PART_NAME_LEN);
++ else
++ snprintf(names, PART_NAME_LEN, "partition%d", i);
++
++ mtd_part->offset = le32_to_cpu(part->addr);
++ mtd_part->size = le32_to_cpu(part->size);
++ mtd_part->name = names;
++ mtd_part++;
++ names += PART_NAME_LEN;
++ }
++
++ *pparts = mtd_parts;
++ ret = num_parts;
++
++ out_free_buf:
++ vfree(buf);
++ out:
++ return ret;
++}
++
++static struct mtd_part_parser myloader_mtd_parser = {
++ .owner = THIS_MODULE,
++ .parse_fn = myloader_parse_partitions,
++ .name = "MyLoader",
++};
++
++static int __init myloader_mtd_parser_init(void)
++{
++ register_mtd_parser(&myloader_mtd_parser);
++
++ return 0;
++}
++
++static void __exit myloader_mtd_parser_exit(void)
++{
++ deregister_mtd_parser(&myloader_mtd_parser);
++}
++
++module_init(myloader_mtd_parser_init);
++module_exit(myloader_mtd_parser_exit);
++
++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
++MODULE_DESCRIPTION("Parsing code for MyLoader partition tables");
++MODULE_LICENSE("GPL v2");
--- /dev/null
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Subject: [PATCH] mtd: bcm47xxpart: check for bad blocks when calculating offsets
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+---
+
+--- a/drivers/mtd/parsers/parser_trx.c
++++ b/drivers/mtd/parsers/parser_trx.c
+@@ -25,6 +25,33 @@ struct trx_header {
+ uint32_t offset[3];
+ } __packed;
+
++/*
++ * Calculate real end offset (address) for a given amount of data. It checks
++ * all blocks skipping bad ones.
++ */
++static size_t parser_trx_real_offset(struct mtd_info *mtd, size_t bytes)
++{
++ size_t real_offset = 0;
++
++ if (mtd_block_isbad(mtd, real_offset))
++ pr_warn("Base offset shouldn't be at bad block");
++
++ while (bytes >= mtd->erasesize) {
++ bytes -= mtd->erasesize;
++ real_offset += mtd->erasesize;
++ while (mtd_block_isbad(mtd, real_offset)) {
++ real_offset += mtd->erasesize;
++
++ if (real_offset >= mtd->size)
++ return real_offset - mtd->erasesize;
++ }
++ }
++
++ real_offset += bytes;
++
++ return real_offset;
++}
++
+ static const char *parser_trx_data_part_name(struct mtd_info *master,
+ size_t offset)
+ {
+@@ -86,21 +113,21 @@ static int parser_trx_parse(struct mtd_i
+ if (trx.offset[2]) {
+ part = &parts[curr_part++];
+ part->name = "loader";
+- part->offset = trx.offset[i];
++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
+ i++;
+ }
+
+ if (trx.offset[i]) {
+ part = &parts[curr_part++];
+ part->name = "linux";
+- part->offset = trx.offset[i];
++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
+ i++;
+ }
+
+ if (trx.offset[i]) {
+ part = &parts[curr_part++];
+- part->name = parser_trx_data_part_name(mtd, trx.offset[i]);
+- part->offset = trx.offset[i];
++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
++ part->name = parser_trx_data_part_name(mtd, part->offset);
+ i++;
+ }
+
--- /dev/null
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Subject: mtd: bcm47xxpart: detect T_Meter partition
+
+It can be found on many Netgear devices. It consists of many 0x30 blocks
+starting with 4D 54.
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+---
+ drivers/mtd/bcm47xxpart.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/mtd/parsers/bcm47xxpart.c
++++ b/drivers/mtd/parsers/bcm47xxpart.c
+@@ -35,6 +35,7 @@
+ #define NVRAM_HEADER 0x48534C46 /* FLSH */
+ #define POT_MAGIC1 0x54544f50 /* POTT */
+ #define POT_MAGIC2 0x504f /* OP */
++#define T_METER_MAGIC 0x4D540000 /* MT */
+ #define ML_MAGIC1 0x39685a42
+ #define ML_MAGIC2 0x26594131
+ #define TRX_MAGIC 0x30524448
+@@ -178,6 +179,15 @@ static int bcm47xxpart_parse(struct mtd_
+ MTD_WRITEABLE);
+ continue;
+ }
++
++ /* T_Meter */
++ if ((le32_to_cpu(buf[0x000 / 4]) & 0xFFFF0000) == T_METER_MAGIC &&
++ (le32_to_cpu(buf[0x030 / 4]) & 0xFFFF0000) == T_METER_MAGIC &&
++ (le32_to_cpu(buf[0x060 / 4]) & 0xFFFF0000) == T_METER_MAGIC) {
++ bcm47xxpart_add_part(&parts[curr_part++], "T_Meter", offset,
++ MTD_WRITEABLE);
++ continue;
++ }
+
+ /* TRX */
+ if (buf[0x000 / 4] == TRX_MAGIC) {
--- /dev/null
+From 4437e01fb6bca63fccdba5d6c44888b0935885c2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= <hacks@slashdirt.org>
+Date: Tue, 24 Mar 2020 11:45:07 +0100
+Subject: [PATCH] generic: routerboot partition build bits (5.4)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch adds routerbootpart kernel build bits
+
+Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
+---
+ drivers/mtd/parsers/Kconfig | 9 +++++++++
+ drivers/mtd/parsers/Makefile | 1 +
+ 2 files changed, 10 insertions(+)
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -195,3 +195,12 @@ config MTD_REDBOOT_PARTS_READONLY
+ 'FIS directory' images, enable this option.
+
+ endif # MTD_REDBOOT_PARTS
++
++config MTD_ROUTERBOOT_PARTS
++ tristate "RouterBoot flash partition parser"
++ depends on MTD && OF
++ help
++ MikroTik RouterBoot is implemented as a multi segment system on the
++ flash, some of which are fixed and some of which are located at
++ variable offsets. This parser handles both cases via properly
++ formatted DTS.
+--- a/drivers/mtd/parsers/Makefile
++++ b/drivers/mtd/parsers/Makefile
+@@ -13,3 +13,4 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
+ obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
+ obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
+ obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
++obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: disable cfi cmdset 0002 erase suspend
+
+on some platforms, erase suspend leads to data corruption and lockups when write
+ops collide with erase ops. this has been observed on the buffalo wzr-hp-g300nh.
+rather than play whack-a-mole with a hard to reproduce issue on a variety of devices,
+simply disable erase suspend, as it will usually not produce any useful gain on
+the small filesystems used on embedded hardware.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/mtd/chips/cfi_cmdset_0002.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/chips/cfi_cmdset_0002.c
++++ b/drivers/mtd/chips/cfi_cmdset_0002.c
+@@ -913,7 +913,7 @@ static int get_chip(struct map_info *map
+ return 0;
+
+ case FL_ERASING:
+- if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
++ if (1 /* no suspend */ || !cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
+ !(mode == FL_READY || mode == FL_POINT ||
+ (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))))
+ goto sleep;
--- /dev/null
+From: George Kashperko <george@znau.edu.ua>
+Subject: Issue map read after Write Buffer Load command to ensure chip is ready to receive data.
+
+Signed-off-by: George Kashperko <george@znau.edu.ua>
+---
+ drivers/mtd/chips/cfi_cmdset_0002.c | 1 +
+ 1 file changed, 1 insertion(+)
+--- a/drivers/mtd/chips/cfi_cmdset_0002.c
++++ b/drivers/mtd/chips/cfi_cmdset_0002.c
+@@ -2057,6 +2057,7 @@ static int __xipram do_write_buffer(stru
+
+ /* Write Buffer Load */
+ map_write(map, CMD(0x25), cmd_adr);
++ (void) map_read(map, cmd_adr);
+
+ chip->state = FL_WRITING_TO_BUFFER;
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: Disable software protection bits for Macronix flashes.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/macronix.c
++++ b/drivers/mtd/spi-nor/macronix.c
+@@ -93,6 +93,7 @@ static void macronix_default_init(struct
+ {
+ nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
+ nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode;
++ nor->flags |= SNOR_F_HAS_LOCK;
+ }
+
+ static const struct spi_nor_fixups macronix_fixups = {
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 4 Nov 2017 07:40:23 +0100
+Subject: [PATCH] mtd: spi-nor: support limiting 4K sectors support based on
+ flash size
+
+Some devices need 4K sectors to be able to deal with small flash chips.
+For instance, w25x05 is 64 KiB in size, and without 4K sectors, the
+entire chip is just one erase block.
+On bigger flash chip sizes, using 4K sectors can significantly slow down
+many operations, including using a writable filesystem. There are several
+platforms where it makes sense to use a single kernel on both kinds of
+devices.
+
+To support this properly, allow configuring an upper flash chip size
+limit for 4K sectors support.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/mtd/spi-nor/Kconfig
++++ b/drivers/mtd/spi-nor/Kconfig
+@@ -34,6 +34,17 @@ config MTD_SPI_NOR_USE_4K_SECTORS
+ Please note that some tools/drivers/filesystems may not work with
+ 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum).
+
++config MTD_SPI_NOR_USE_4K_SECTORS_LIMIT
++ int "Maximum flash chip size to use 4K sectors on (in KiB)"
++ depends on MTD_SPI_NOR_USE_4K_SECTORS
++ default "4096"
++ help
++ There are many flash chips that support 4K sectors, but are so large
++ that using them significantly slows down writing large amounts of
++ data or using a writable filesystem.
++ Any flash chip larger than the size specified in this option will
++ not use 4K sectors.
++
+ source "drivers/mtd/spi-nor/controllers/Kconfig"
+
+ endif # MTD_SPI_NOR
+--- a/drivers/mtd/spi-nor/core.c
++++ b/drivers/mtd/spi-nor/core.c
+@@ -2792,6 +2792,21 @@ static void spi_nor_info_init_params(str
+ */
+ erase_mask = 0;
+ i = 0;
++#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
++ if ((info->flags & SECT_4K_PMC) && (params->size <=
++ CONFIG_MTD_SPI_NOR_USE_4K_SECTORS_LIMIT * 1024)) {
++ erase_mask |= BIT(i);
++ spi_nor_set_erase_type(&map->erase_type[i], 4096u,
++ SPINOR_OP_BE_4K_PMC);
++ i++;
++ } else if ((info->flags & SECT_4K) && (params->size <=
++ CONFIG_MTD_SPI_NOR_USE_4K_SECTORS_LIMIT * 1024)) {
++ erase_mask |= BIT(i);
++ spi_nor_set_erase_type(&map->erase_type[i], 4096u,
++ SPINOR_OP_BE_4K);
++ i++;
++ }
++#else
+ if (info->flags & SECT_4K_PMC) {
+ erase_mask |= BIT(i);
+ spi_nor_set_erase_type(&map->erase_type[i], 4096u,
+@@ -2803,6 +2818,7 @@ static void spi_nor_info_init_params(str
+ SPINOR_OP_BE_4K);
+ i++;
+ }
++#endif
+ erase_mask |= BIT(i);
+ spi_nor_set_erase_type(&map->erase_type[i], info->sector_size,
+ SPINOR_OP_SE);
--- /dev/null
+From: Piotr Dymacz <pepe2k@gmail.com>
+Subject: kernel/mtd: add support for EON EN25Q128
+
+Signed-off-by: Piotr Dymacz <pepe2k@gmail.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/eon.c
++++ b/drivers/mtd/spi-nor/eon.c
+@@ -15,6 +15,7 @@ static const struct flash_info eon_parts
+ { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
+ { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
+ { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
++ { "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256, SECT_4K) },
+ { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16,
+ SECT_4K | SPI_NOR_DUAL_READ) },
+ { "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32,
--- /dev/null
+From patchwork Thu Feb 6 17:19:41 2020
+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: 1234465
+Date: Thu, 6 Feb 2020 19:19:41 +0200
+From: Daniel Golle <daniel@makrotopia.org>
+To: linux-mtd@lists.infradead.org
+Subject: [PATCH v2] mtd: spi-nor: Add support for xt25f128b chip
+Message-ID: <20200206171941.GA2398@makrotopia.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+List-Subscribe: <http://lists.infradead.org/mailman/listinfo/linux-mtd>,
+ <mailto:linux-mtd-request@lists.infradead.org?subject=subscribe>
+Cc: Eitan Cohen <eitan@neot-semadar.com>, Piotr Dymacz <pepe2k@gmail.com>,
+ Tudor Ambarus <tudor.ambarus@microchip.com>
+Sender: "linux-mtd" <linux-mtd-bounces@lists.infradead.org>
+Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org
+
+Add XT25F128B made by XTX Technology (Shenzhen) Limited.
+This chip supports dual and quad read and uniform 4K-byte erase.
+Verified on Teltonika RUT955 which comes with XT25F128B in recent
+versions of the device.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/mtd/spi-nor/Makefile
++++ b/drivers/mtd/spi-nor/Makefile
+@@ -17,6 +17,7 @@ spi-nor-objs += sst.o
+ spi-nor-objs += winbond.o
+ spi-nor-objs += xilinx.o
+ spi-nor-objs += xmc.o
++spi-nor-objs += xtx.o
+ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
+
+ obj-$(CONFIG_MTD_SPI_NOR) += controllers/
+--- /dev/null
++++ b/drivers/mtd/spi-nor/xtx.c
+@@ -0,0 +1,15 @@
++// SPDX-License-Identifier: GPL-2.0
++#include <linux/mtd/spi-nor.h>
++
++#include "core.h"
++
++static const struct flash_info xtx_parts[] = {
++ /* XTX Technology (Shenzhen) Limited */
++ { "xt25f128b", INFO(0x0B4018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++};
++
++const struct spi_nor_manufacturer spi_nor_xtx = {
++ .name = "xtx",
++ .parts = xtx_parts,
++ .nparts = ARRAY_SIZE(xtx_parts),
++};
+--- a/drivers/mtd/spi-nor/core.c
++++ b/drivers/mtd/spi-nor/core.c
+@@ -2028,6 +2028,7 @@ static const struct spi_nor_manufacturer
+ &spi_nor_winbond,
+ &spi_nor_xilinx,
+ &spi_nor_xmc,
++ &spi_nor_xtx,
+ };
+
+ static const struct flash_info *
+--- a/drivers/mtd/spi-nor/core.h
++++ b/drivers/mtd/spi-nor/core.h
+@@ -398,6 +398,7 @@ extern const struct spi_nor_manufacturer
+ extern const struct spi_nor_manufacturer spi_nor_winbond;
+ extern const struct spi_nor_manufacturer spi_nor_xilinx;
+ extern const struct spi_nor_manufacturer spi_nor_xmc;
++extern const struct spi_nor_manufacturer spi_nor_xtx;
+
+ int spi_nor_write_enable(struct spi_nor *nor);
+ int spi_nor_write_disable(struct spi_nor *nor);
--- /dev/null
+From d68b4aa22e8c625685bfad642dd7337948dc0ad1 Mon Sep 17 00:00:00 2001
+From: Koen Vandeputte <koen.vandeputte@ncentric.com>
+Date: Mon, 6 Jan 2020 13:07:56 +0100
+Subject: [PATCH] mtd: spi-nor: add support for Gigadevice GD25D05
+
+Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/mtd/spi-nor/gigadevice.c
++++ b/drivers/mtd/spi-nor/gigadevice.c
+@@ -24,6 +24,9 @@ static struct spi_nor_fixups gd25q256_fi
+ };
+
+ static const struct flash_info gigadevice_parts[] = {
++ { "gd25q05", INFO(0xc84010, 0, 64 * 1024, 1,
++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) },
+ { "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) },
--- /dev/null
+--- a/drivers/mtd/spi-nor/gigadevice.c
++++ b/drivers/mtd/spi-nor/gigadevice.c
+@@ -53,6 +53,9 @@ static const struct flash_info gigadevic
+ SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK |
+ SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6)
+ .fixups = &gd25q256_fixups },
++ { "gd25q512", INFO(0xc84020, 0, 64 * 1024, 1024,
++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4B_OPCODES) },
+ };
+
+ const struct spi_nor_manufacturer spi_nor_gigadevice = {
--- /dev/null
+From a07e31adf2753cad2fd9790db5bfc047c81e8152 Mon Sep 17 00:00:00 2001
+From: Felix Matouschek <felix@matouschek.org>
+Date: Fri, 2 Jul 2021 20:31:23 +0200
+Subject: [PATCH] mtd: spinand: Add support for XTX XT26G0xA
+
+Add support for XTX Technology XT26G01AXXXXX, XTX26G02AXXXXX and
+XTX26G04AXXXXX SPI NAND.
+
+These are 3V, 1G/2G/4Gbit serial SLC NAND flash devices with on-die ECC
+(8bit strength per 512bytes).
+
+Tested on Teltonika RUTX10 flashed with OpenWrt.
+
+Datasheets available at
+http://www.xtxtech.com/download/?AId=225
+https://datasheet.lcsc.com/szlcsc/2005251034_XTX-XT26G01AWSEGA_C558841.pdf
+
+Signed-off-by: Felix Matouschek <felix@matouschek.org>
+---
+ drivers/mtd/nand/spi/Makefile | 2 +-
+ drivers/mtd/nand/spi/core.c | 1 +
+ drivers/mtd/nand/spi/xtx.c | 122 ++++++++++++++++++++++++++++++++++
+ include/linux/mtd/spinand.h | 1 +
+ 4 files changed, 125 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/mtd/nand/spi/xtx.c
+
+--- a/drivers/mtd/nand/spi/Makefile
++++ b/drivers/mtd/nand/spi/Makefile
+@@ -1,3 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0
+-spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o
++spinand-objs := core.o gigadevice.o macronix.o 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
+@@ -760,6 +760,7 @@ static const struct spinand_manufacturer
+ ¶gon_spinand_manufacturer,
+ &toshiba_spinand_manufacturer,
+ &winbond_spinand_manufacturer,
++ &xtx_spinand_manufacturer,
+ };
+
+ static int spinand_manufacturer_match(struct spinand_device *spinand,
+--- /dev/null
++++ b/drivers/mtd/nand/spi/xtx.c
+@@ -0,0 +1,122 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Author:
++ * Felix Matouschek <felix@matouschek.org>
++ */
++
++#include <linux/device.h>
++#include <linux/kernel.h>
++#include <linux/mtd/spinand.h>
++
++#define SPINAND_MFR_XTX 0x0B
++
++#define XT26G0XA_STATUS_ECC_MASK GENMASK(5, 2)
++#define XT26G0XA_STATUS_ECC_NO_DETECTED (0 << 2)
++#define XT26G0XA_STATUS_ECC_8_CORRECTED (3 << 4)
++#define XT26G0XA_STATUS_ECC_UNCOR_ERROR (2 << 4)
++
++static SPINAND_OP_VARIANTS(read_cache_variants,
++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
++
++static SPINAND_OP_VARIANTS(write_cache_variants,
++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
++ SPINAND_PROG_LOAD(true, 0, NULL, 0));
++
++static SPINAND_OP_VARIANTS(update_cache_variants,
++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
++ SPINAND_PROG_LOAD(false, 0, NULL, 0));
++
++static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section)
++ return -ERANGE;
++
++ region->offset = 8;
++ region->length = 40;
++
++ return 0;
++}
++
++static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section)
++ return -ERANGE;
++
++ region->offset = 1;
++ region->length = 7;
++
++ return 0;
++}
++
++static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = {
++ .ecc = xt26g0xa_ooblayout_ecc,
++ .free = xt26g0xa_ooblayout_free,
++};
++
++static int xt26g0xa_ecc_get_status(struct spinand_device *spinand,
++ u8 status)
++{
++ switch (status & XT26G0XA_STATUS_ECC_MASK) {
++ case XT26G0XA_STATUS_ECC_NO_DETECTED:
++ return 0;
++ case XT26G0XA_STATUS_ECC_8_CORRECTED:
++ return 8;
++ case XT26G0XA_STATUS_ECC_UNCOR_ERROR:
++ return -EBADMSG;
++ default: /* (1 << 2) through (7 << 2) are 1-7 corrected errors */
++ return (status & XT26G0XA_STATUS_ECC_MASK) >> 2;
++ }
++
++ return -EINVAL;
++}
++
++static const struct spinand_info xtx_spinand_table[] = {
++ SPINAND_INFO("XT26G01A",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE1),
++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&xt26g0xa_ooblayout,
++ xt26g0xa_ecc_get_status)),
++ SPINAND_INFO("XT26G02A",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE2),
++ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&xt26g0xa_ooblayout,
++ xt26g0xa_ecc_get_status)),
++ SPINAND_INFO("XT26G04A",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE3),
++ NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&xt26g0xa_ooblayout,
++ xt26g0xa_ecc_get_status)),
++};
++
++static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
++};
++
++const struct spinand_manufacturer xtx_spinand_manufacturer = {
++ .id = SPINAND_MFR_XTX,
++ .name = "XTX",
++ .chips = xtx_spinand_table,
++ .nchips = ARRAY_SIZE(xtx_spinand_table),
++ .ops = &xtx_spinand_manuf_ops,
++};
+--- a/include/linux/mtd/spinand.h
++++ b/include/linux/mtd/spinand.h
+@@ -244,6 +244,7 @@ extern const struct spinand_manufacturer
+ extern const struct spinand_manufacturer paragon_spinand_manufacturer;
+ extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
+ extern const struct spinand_manufacturer winbond_spinand_manufacturer;
++extern const struct spinand_manufacturer xtx_spinand_manufacturer;
+
+ /**
+ * struct spinand_op_variants - SPI NAND operation variants
--- /dev/null
+--- a/drivers/mtd/spi-nor/esmt.c
++++ b/drivers/mtd/spi-nor/esmt.c
+@@ -10,6 +10,8 @@
+
+ static const struct flash_info esmt_parts[] = {
+ /* ESMT */
++ { "f25l16pa-2s", INFO(0x8c2115, 0, 64 * 1024, 32,
++ SECT_4K | SPI_NOR_HAS_LOCK) },
+ { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_HAS_LOCK) },
+ { "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64,
--- /dev/null
+--- a/drivers/mtd/spi-nor/xmc.c
++++ b/drivers/mtd/spi-nor/xmc.c
+@@ -14,6 +14,8 @@ static const struct flash_info xmc_parts
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++ { "XM25QH128C", INFO(0x204018, 0, 64 * 1024, 256,
++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ };
+
+ const struct spi_nor_manufacturer spi_nor_xmc = {
--- /dev/null
+From: Daniel Golle <daniel@makrotopia.org>
+Subject: ubi: auto-attach mtd device named "ubi" or "data" on boot
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/ubi/build.c | 36 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+--- a/drivers/mtd/ubi/build.c
++++ b/drivers/mtd/ubi/build.c
+@@ -1192,6 +1192,73 @@ static struct mtd_info * __init open_mtd
+ return mtd;
+ }
+
++/*
++ * This function tries attaching mtd partitions named either "ubi" or "data"
++ * during boot.
++ */
++static void __init ubi_auto_attach(void)
++{
++ int err;
++ struct mtd_info *mtd;
++ loff_t offset = 0;
++ size_t len;
++ char magic[4];
++
++ /* try attaching mtd device named "ubi" or "data" */
++ mtd = open_mtd_device("ubi");
++ if (IS_ERR(mtd))
++ mtd = open_mtd_device("data");
++
++ if (IS_ERR(mtd))
++ return;
++
++ /* get the first not bad block */
++ if (mtd_can_have_bb(mtd))
++ while (mtd_block_isbad(mtd, offset)) {
++ offset += mtd->erasesize;
++
++ if (offset > mtd->size) {
++ pr_err("UBI error: Failed to find a non-bad "
++ "block on mtd%d\n", mtd->index);
++ goto cleanup;
++ }
++ }
++
++ /* check if the read from flash was successful */
++ err = mtd_read(mtd, offset, 4, &len, (void *) magic);
++ if ((err && !mtd_is_bitflip(err)) || len != 4) {
++ pr_err("UBI error: unable to read from mtd%d\n", mtd->index);
++ goto cleanup;
++ }
++
++ /* check for a valid ubi magic */
++ if (strncmp(magic, "UBI#", 4)) {
++ pr_err("UBI error: no valid UBI magic found inside mtd%d\n", mtd->index);
++ goto cleanup;
++ }
++
++ /* don't auto-add media types where UBI doesn't makes sense */
++ if (mtd->type != MTD_NANDFLASH &&
++ mtd->type != MTD_NORFLASH &&
++ mtd->type != MTD_DATAFLASH &&
++ mtd->type != MTD_MLCNANDFLASH)
++ goto cleanup;
++
++ mutex_lock(&ubi_devices_mutex);
++ pr_notice("UBI: auto-attach mtd%d\n", mtd->index);
++ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0);
++ mutex_unlock(&ubi_devices_mutex);
++ if (err < 0) {
++ pr_err("UBI error: cannot attach mtd%d\n", mtd->index);
++ goto cleanup;
++ }
++
++ return;
++
++cleanup:
++ put_mtd_device(mtd);
++}
++
+ static int __init ubi_init(void)
+ {
+ int err, i, k;
+@@ -1275,6 +1342,12 @@ static int __init ubi_init(void)
+ }
+ }
+
++ /* auto-attach mtd devices only if built-in to the kernel and no ubi.mtd
++ * parameter was given */
++ if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
++ !ubi_is_module() && !mtd_devs)
++ ubi_auto_attach();
++
+ err = ubiblock_init();
+ if (err) {
+ pr_err("UBI error: block: cannot initialize, error %d\n", err);
--- /dev/null
+From: Daniel Golle <daniel@makrotopia.org>
+Subject: ubi: auto-create ubiblock device for rootfs
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/ubi/block.c | 42 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+--- a/drivers/mtd/ubi/block.c
++++ b/drivers/mtd/ubi/block.c
+@@ -652,6 +652,47 @@ static void __init ubiblock_create_from_
+ }
+ }
+
++#define UBIFS_NODE_MAGIC 0x06101831
++static inline int ubi_vol_is_ubifs(struct ubi_volume_desc *desc)
++{
++ int ret;
++ uint32_t magic_of, magic;
++ ret = ubi_read(desc, 0, (char *)&magic_of, 0, 4);
++ if (ret)
++ return 0;
++ magic = le32_to_cpu(magic_of);
++ return magic == UBIFS_NODE_MAGIC;
++}
++
++static void __init ubiblock_create_auto_rootfs(void)
++{
++ int ubi_num, ret, is_ubifs;
++ struct ubi_volume_desc *desc;
++ struct ubi_volume_info vi;
++
++ for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) {
++ desc = ubi_open_volume_nm(ubi_num, "rootfs", UBI_READONLY);
++ if (IS_ERR(desc))
++ desc = ubi_open_volume_nm(ubi_num, "fit", UBI_READONLY);;
++
++ if (IS_ERR(desc))
++ continue;
++
++ ubi_get_volume_info(desc, &vi);
++ is_ubifs = ubi_vol_is_ubifs(desc);
++ ubi_close_volume(desc);
++ if (is_ubifs)
++ break;
++
++ ret = ubiblock_create(&vi);
++ if (ret)
++ pr_err("UBI error: block: can't add '%s' volume, err=%d\n",
++ vi.name, ret);
++ /* always break if we get here */
++ break;
++ }
++}
++
+ static void ubiblock_remove_all(void)
+ {
+ struct ubiblock *next;
+@@ -684,6 +725,10 @@ int __init ubiblock_init(void)
+ */
+ ubiblock_create_from_param();
+
++ /* auto-attach "rootfs" volume if existing and non-ubifs */
++ if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV))
++ ubiblock_create_auto_rootfs();
++
+ /*
+ * Block devices are only created upon user requests, so we ignore
+ * existing volumes.
--- /dev/null
+From: Daniel Golle <daniel@makrotopia.org>
+Subject: try auto-mounting ubi0:rootfs in init/do_mounts.c
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ init/do_mounts.c | 26 +++++++++++++++++++++++++-
+ 1 file changed, 25 insertions(+), 1 deletion(-)
+
+--- a/init/do_mounts.c
++++ b/init/do_mounts.c
+@@ -474,7 +474,30 @@ retry:
+ out:
+ put_page(page);
+ }
+-
++
++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
++static int __init mount_ubi_rootfs(void)
++{
++ int flags = MS_SILENT;
++ int err, tried = 0;
++
++ while (tried < 2) {
++ err = do_mount_root("ubi0:rootfs", "ubifs", flags, \
++ root_mount_data);
++ switch (err) {
++ case -EACCES:
++ flags |= MS_RDONLY;
++ tried++;
++ break;
++ default:
++ return err;
++ }
++ }
++
++ return -EINVAL;
++}
++#endif
++
+ #ifdef CONFIG_ROOT_NFS
+
+ #define NFSROOT_TIMEOUT_MIN 5
+@@ -567,6 +590,10 @@ void __init mount_root(void)
+ return;
+ }
+ #endif
++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
++ if (!mount_ubi_rootfs())
++ return;
++#endif
+ #ifdef CONFIG_BLOCK
+ {
+ int err = create_dev("/dev/root", ROOT_DEV);
--- /dev/null
+From: Daniel Golle <daniel@makrotopia.org>
+Subject: ubi: set ROOT_DEV to ubiblock "rootfs" if unset
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/ubi/block.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/mtd/ubi/block.c
++++ b/drivers/mtd/ubi/block.c
+@@ -42,6 +42,7 @@
+ #include <linux/scatterlist.h>
+ #include <linux/idr.h>
+ #include <asm/div64.h>
++#include <linux/root_dev.h>
+
+ #include "ubi-media.h"
+ #include "ubi.h"
+@@ -458,6 +459,15 @@ int ubiblock_create(struct ubi_volume_in
+ dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)",
+ dev->ubi_num, dev->vol_id, vi->name);
+ mutex_unlock(&devices_mutex);
++
++ if (!strcmp(vi->name, "rootfs") &&
++ IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
++ ROOT_DEV == 0) {
++ pr_notice("ubiblock: device ubiblock%d_%d (%s) set to be root filesystem\n",
++ dev->ubi_num, dev->vol_id, vi->name);
++ ROOT_DEV = MKDEV(gd->major, gd->first_minor);
++ }
++
+ return 0;
+
+ out_free_queue:
--- /dev/null
+From: Gabor Juhos <juhosg@openwrt.org>
+Subject: mtd: add EOF marker support to the UBI layer
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/mtd/ubi/attach.c | 25 ++++++++++++++++++++++---
+ drivers/mtd/ubi/ubi.h | 1 +
+ 2 files changed, 23 insertions(+), 3 deletions(-)
+
+--- a/drivers/mtd/ubi/attach.c
++++ b/drivers/mtd/ubi/attach.c
+@@ -926,6 +926,13 @@ static bool vol_ignored(int vol_id)
+ #endif
+ }
+
++static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech)
++{
++ return ech->padding1[0] == 'E' &&
++ ech->padding1[1] == 'O' &&
++ ech->padding1[2] == 'F';
++}
++
+ /**
+ * scan_peb - scan and process UBI headers of a PEB.
+ * @ubi: UBI device description object
+@@ -958,9 +965,21 @@ static int scan_peb(struct ubi_device *u
+ return 0;
+ }
+
+- err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
+- if (err < 0)
+- return err;
++ if (!ai->eof_found) {
++ err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
++ if (err < 0)
++ return err;
++
++ if (ec_hdr_has_eof(ech)) {
++ pr_notice("UBI: EOF marker found, PEBs from %d will be erased\n",
++ pnum);
++ ai->eof_found = true;
++ }
++ }
++
++ if (ai->eof_found)
++ err = UBI_IO_FF_BITFLIPS;
++
+ switch (err) {
+ case 0:
+ break;
+--- a/drivers/mtd/ubi/ubi.h
++++ b/drivers/mtd/ubi/ubi.h
+@@ -782,6 +782,7 @@ struct ubi_attach_info {
+ int mean_ec;
+ uint64_t ec_sum;
+ int ec_count;
++ bool eof_found;
+ struct kmem_cache *aeb_slab_cache;
+ struct ubi_ec_hdr *ech;
+ struct ubi_vid_io_buf *vidb;
--- /dev/null
+From 1bd1b740f208d1cf4071932cc51860d37266c402 Mon Sep 17 00:00:00 2001
+From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Date: Sat, 1 Sep 2018 00:30:11 +0200
+Subject: [PATCH 495/497] mtd: core: add get_mtd_device_by_node
+
+Add function to retrieve a mtd device by its OF node. Since drivers can
+assign arbitrary names to mtd devices in the absence of a label
+property, there is no other reliable way to retrieve a mtd device for a
+given OF node.
+
+Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/mtd/mtdcore.c | 38 ++++++++++++++++++++++++++++++++++++++
+ include/linux/mtd/mtd.h | 2 ++
+ 2 files changed, 40 insertions(+)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -1046,6 +1046,44 @@ out_unlock:
+ }
+ EXPORT_SYMBOL_GPL(get_mtd_device_nm);
+
++/**
++ * get_mtd_device_by_node - obtain a validated handle for an MTD device
++ * by of_node
++ * @of_node: OF node of MTD device to open
++ *
++ * This function returns MTD device description structure in case of
++ * success and an error code in case of failure.
++ */
++struct mtd_info *get_mtd_device_by_node(const struct device_node *of_node)
++{
++ int err = -ENODEV;
++ struct mtd_info *mtd = NULL, *other;
++
++ mutex_lock(&mtd_table_mutex);
++
++ mtd_for_each_device(other) {
++ if (of_node == other->dev.of_node) {
++ mtd = other;
++ break;
++ }
++ }
++
++ if (!mtd)
++ goto out_unlock;
++
++ err = __get_mtd_device(mtd);
++ if (err)
++ goto out_unlock;
++
++ mutex_unlock(&mtd_table_mutex);
++ return mtd;
++
++out_unlock:
++ mutex_unlock(&mtd_table_mutex);
++ return ERR_PTR(err);
++}
++EXPORT_SYMBOL_GPL(get_mtd_device_by_node);
++
+ void put_mtd_device(struct mtd_info *mtd)
+ {
+ mutex_lock(&mtd_table_mutex);
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -698,6 +698,8 @@ extern struct mtd_info *get_mtd_device(s
+ extern int __get_mtd_device(struct mtd_info *mtd);
+ extern void __put_mtd_device(struct mtd_info *mtd);
+ extern struct mtd_info *get_mtd_device_nm(const char *name);
++extern struct mtd_info *get_mtd_device_by_node(
++ const struct device_node *of_node);
+ extern void put_mtd_device(struct mtd_info *mtd);
+
+ static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
--- /dev/null
+From 5734c6669fba7ddb5ef491ccff7159d15dba0b59 Mon Sep 17 00:00:00 2001
+From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Date: Wed, 5 Sep 2018 01:32:51 +0200
+Subject: [PATCH 496/497] dt-bindings: add bindings for mtd-concat devices
+
+Document virtual mtd-concat device bindings.
+
+Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+---
+ .../devicetree/bindings/mtd/mtd-concat.txt | 36 +++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/mtd-concat.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/mtd-concat.txt
+@@ -0,0 +1,36 @@
++Virtual MTD concat device
++
++Requires properties:
++- devices: list of phandles to mtd nodes that should be concatenated
++
++Example:
++
++&spi {
++ flash0: flash@0 {
++ ...
++ };
++ flash1: flash@1 {
++ ...
++ };
++};
++
++flash {
++ compatible = "mtd-concat";
++
++ devices = <&flash0 &flash1>;
++
++ partitions {
++ compatible = "fixed-partitions";
++
++ partition@0 {
++ label = "boot";
++ reg = <0x0000000 0x0040000>;
++ read-only;
++ };
++
++ partition@40000 {
++ label = "firmware";
++ reg = <0x0040000 0x1fc0000>;
++ };
++ }
++}
--- /dev/null
+From e53f712d8eac71f54399b61038ccf87d2cee99d7 Mon Sep 17 00:00:00 2001
+From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Date: Sat, 25 Aug 2018 12:35:22 +0200
+Subject: [PATCH 497/497] mtd: mtdconcat: add dt driver for concat devices
+
+Some mtd drivers like physmap variants have support for concatenating
+multiple mtd devices, but there is no generic way to define such a
+concat device from within the device tree.
+
+This is useful for some SoC boards that use multiple flash chips as
+memory banks of a single mtd device, with partitions spanning chip
+borders.
+
+This commit adds a driver for creating virtual mtd-concat devices. They
+must have a compatible = "mtd-concat" line, and define a list of devices
+to concat in the 'devices' property, for example:
+
+flash {
+ compatible = "mtd-concat";
+
+ devices = <&flash0 &flash1>;
+
+ partitions {
+ ...
+ };
+};
+
+The driver is added to the very end of the mtd Makefile to increase the
+likelyhood of all child devices already being loaded at the time of
+probing, preventing unnecessary deferred probes.
+
+Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+---
+ drivers/mtd/Kconfig | 2 +
+ drivers/mtd/Makefile | 3 +
+ drivers/mtd/composite/Kconfig | 12 +++
+ drivers/mtd/composite/Makefile | 6 ++
+ drivers/mtd/composite/virt_concat.c | 128 ++++++++++++++++++++++++++++
+ 5 files changed, 151 insertions(+)
+ create mode 100644 drivers/mtd/composite/Kconfig
+ create mode 100644 drivers/mtd/composite/Makefile
+ create mode 100644 drivers/mtd/composite/virt_concat.c
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -238,4 +238,6 @@ source "drivers/mtd/ubi/Kconfig"
+
+ source "drivers/mtd/hyperbus/Kconfig"
+
++source "drivers/mtd/composite/Kconfig"
++
+ endif # MTD
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -33,3 +33,6 @@ obj-y += chips/ lpddr/ maps/ devices/ n
+ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
+ obj-$(CONFIG_MTD_UBI) += ubi/
+ obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/
++
++# Composite drivers must be loaded last
++obj-y += composite/
+--- /dev/null
++++ b/drivers/mtd/composite/Kconfig
+@@ -0,0 +1,12 @@
++menu "Composite MTD device drivers"
++ depends on MTD!=n
++
++config MTD_VIRT_CONCAT
++ tristate "Virtual concat MTD device"
++ help
++ This driver allows creation of a virtual MTD concat device, which
++ concatenates multiple underlying MTD devices to a single device.
++ This is required by some SoC boards where multiple memory banks are
++ used as one device with partitions spanning across device boundaries.
++
++endmenu
+--- /dev/null
++++ b/drivers/mtd/composite/Makefile
+@@ -0,0 +1,6 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# linux/drivers/mtd/composite/Makefile
++#
++
++obj-$(CONFIG_MTD_VIRT_CONCAT) += virt_concat.o
+--- /dev/null
++++ b/drivers/mtd/composite/virt_concat.c
+@@ -0,0 +1,128 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Virtual concat MTD device driver
++ *
++ * Copyright (C) 2018 Bernhard Frauendienst
++ * Author: Bernhard Frauendienst, kernel@nospam.obeliks.de
++ */
++
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/mtd/concat.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
++#include <linux/slab.h>
++
++/*
++ * struct of_virt_concat - platform device driver data.
++ * @cmtd the final mtd_concat device
++ * @num_devices the number of devices in @devices
++ * @devices points to an array of devices already loaded
++ */
++struct of_virt_concat {
++ struct mtd_info *cmtd;
++ int num_devices;
++ struct mtd_info **devices;
++};
++
++static int virt_concat_remove(struct platform_device *pdev)
++{
++ struct of_virt_concat *info;
++ int i;
++
++ info = platform_get_drvdata(pdev);
++ if (!info)
++ return 0;
++
++ // unset data for when this is called after a probe error
++ platform_set_drvdata(pdev, NULL);
++
++ if (info->cmtd) {
++ mtd_device_unregister(info->cmtd);
++ mtd_concat_destroy(info->cmtd);
++ }
++
++ if (info->devices) {
++ for (i = 0; i < info->num_devices; i++)
++ put_mtd_device(info->devices[i]);
++ }
++
++ return 0;
++}
++
++static int virt_concat_probe(struct platform_device *pdev)
++{
++ struct device_node *node = pdev->dev.of_node;
++ struct of_phandle_iterator it;
++ struct of_virt_concat *info;
++ struct mtd_info *mtd;
++ int err = 0, count;
++
++ count = of_count_phandle_with_args(node, "devices", NULL);
++ if (count <= 0)
++ return -EINVAL;
++
++ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return -ENOMEM;
++ info->devices = devm_kcalloc(&pdev->dev, count,
++ sizeof(*(info->devices)), GFP_KERNEL);
++ if (!info->devices) {
++ err = -ENOMEM;
++ goto err_remove;
++ }
++
++ platform_set_drvdata(pdev, info);
++
++ of_for_each_phandle(&it, err, node, "devices", NULL, 0) {
++ mtd = get_mtd_device_by_node(it.node);
++ if (IS_ERR(mtd)) {
++ of_node_put(it.node);
++ err = -EPROBE_DEFER;
++ goto err_remove;
++ }
++
++ info->devices[info->num_devices++] = mtd;
++ }
++
++ info->cmtd = mtd_concat_create(info->devices, info->num_devices,
++ dev_name(&pdev->dev));
++ if (!info->cmtd) {
++ err = -ENXIO;
++ goto err_remove;
++ }
++
++ info->cmtd->dev.parent = &pdev->dev;
++ mtd_set_of_node(info->cmtd, node);
++ mtd_device_register(info->cmtd, NULL, 0);
++
++ return 0;
++
++err_remove:
++ virt_concat_remove(pdev);
++
++ return err;
++}
++
++static const struct of_device_id virt_concat_of_match[] = {
++ { .compatible = "mtd-concat", },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, virt_concat_of_match);
++
++static struct platform_driver virt_concat_driver = {
++ .probe = virt_concat_probe,
++ .remove = virt_concat_remove,
++ .driver = {
++ .name = "virt-mtdconcat",
++ .of_match_table = virt_concat_of_match,
++ },
++};
++
++module_platform_driver(virt_concat_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Bernhard Frauendienst <kernel@nospam.obeliks.de>");
++MODULE_DESCRIPTION("Virtual concat MTD device driver");
--- /dev/null
+From 8bf2ce6ea4ee840b70f55a27f80e1cd308051b13 Mon Sep 17 00:00:00 2001
+From: Nick Hainke <vincent@systemli.org>
+Date: Mon, 27 Dec 2021 00:38:13 +0100
+Subject: [PATCH 1/2] mtd: spi-nor: locking support for MX25L6405D
+
+Macronix MX25L6405D supports locking with four block-protection bits.
+Currently, the driver only sets three bits. If the bootloader does not
+sustain the flash chip in an unlocked state, the flash might be
+non-writeable. Add the corresponding flag to enable locking support with
+four bits in the status register.
+
+Tested on Nanostation M2 XM.
+
+Similar to commit 7ea40b54e83b ("mtd: spi-nor: enable locking support for
+MX25L12805D")
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+Signed-off-by: Nick Hainke <vincent@systemli.org>
+---
+ drivers/mtd/spi-nor/macronix.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/spi-nor/macronix.c
++++ b/drivers/mtd/spi-nor/macronix.c
+@@ -42,7 +42,8 @@ static const struct flash_info macronix_
+ { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) },
+ { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) },
+ { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
+- { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) },
++ { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K |
++ SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP) },
+ { "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4, SECT_4K) },
+ { "mx25u3235f", INFO(0xc22536, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ |
--- /dev/null
+From 245224608b5368c10407da07557e546743d3c489 Mon Sep 17 00:00:00 2001
+From: Nick Hainke <vincent@systemli.org>
+Date: Mon, 27 Dec 2021 09:33:13 +0100
+Subject: [PATCH 2/2] mtd: spi-nor: disable 16-bit-sr for macronix
+
+Macronix flash chips seem to consist of only one status register.
+These chips will not work with the "16-bit Write Status (01h) Command".
+Disable SNOR_F_HAS_16BIT_SR for all Macronix chips.
+
+Tested with MX25L6405D.
+
+Fixes: 39d1e3340c73 ("mtd: spi-nor: Fix clearing of QE bit on
+lock()/unlock()")
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+Signed-off-by: Nick Hainke <vincent@systemli.org>
+---
+ drivers/mtd/spi-nor/macronix.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/macronix.c
++++ b/drivers/mtd/spi-nor/macronix.c
+@@ -94,6 +94,7 @@ static void macronix_default_init(struct
+ {
+ nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
+ nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode;
++ nor->flags &= ~SNOR_F_HAS_16BIT_SR;
+ nor->flags |= SNOR_F_HAS_LOCK;
+ }
+
--- /dev/null
+--- a/fs/hfs/Kconfig
++++ b/fs/hfs/Kconfig
+@@ -2,6 +2,7 @@
+ config HFS_FS
+ tristate "Apple Macintosh file system support"
+ depends on BLOCK
++ select CDROM
+ select NLS
+ help
+ If you say Y here, you will be able to mount Macintosh-formatted
+--- a/fs/hfsplus/Kconfig
++++ b/fs/hfsplus/Kconfig
+@@ -2,6 +2,7 @@
+ config HFSPLUS_FS
+ tristate "Apple Extended HFS file system support"
+ depends on BLOCK
++ select CDROM
+ select NLS
+ select NLS_UTF8
+ help
+--- a/fs/isofs/Kconfig
++++ b/fs/isofs/Kconfig
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ config ISO9660_FS
+ tristate "ISO 9660 CDROM file system support"
++ select CDROM
+ help
+ This is the standard file system used on CD-ROMs. It was previously
+ known as "High Sierra File System" and is called "hsfs" on other
+--- a/fs/udf/Kconfig
++++ b/fs/udf/Kconfig
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ config UDF_FS
+ tristate "UDF file system support"
++ select CDROM
+ select CRC_ITU_T
+ select NLS
+ help
--- /dev/null
+From: Alexandros C. Couloumbis <alex@ozo.com>
+Subject: fs: add jffs2/lzma support (not activated by default yet)
+
+lede-commit: c2c88d315fa0e881f8b19da07b62859b915b11b2
+Signed-off-by: Alexandros C. Couloumbis <alex@ozo.com>
+---
+ fs/jffs2/Kconfig | 9 +
+ fs/jffs2/Makefile | 3 +
+ fs/jffs2/compr.c | 6 +
+ fs/jffs2/compr.h | 10 +-
+ fs/jffs2/compr_lzma.c | 128 +++
+ fs/jffs2/super.c | 33 +-
+ include/linux/lzma.h | 62 ++
+ include/linux/lzma/LzFind.h | 115 +++
+ include/linux/lzma/LzHash.h | 54 +
+ include/linux/lzma/LzmaDec.h | 231 +++++
+ include/linux/lzma/LzmaEnc.h | 80 ++
+ include/linux/lzma/Types.h | 226 +++++
+ include/uapi/linux/jffs2.h | 1 +
+ lib/Kconfig | 6 +
+ lib/Makefile | 12 +
+ lib/lzma/LzFind.c | 761 ++++++++++++++
+ lib/lzma/LzmaDec.c | 999 +++++++++++++++++++
+ lib/lzma/LzmaEnc.c | 2271 ++++++++++++++++++++++++++++++++++++++++++
+ lib/lzma/Makefile | 7 +
+ 19 files changed, 5008 insertions(+), 6 deletions(-)
+ create mode 100644 fs/jffs2/compr_lzma.c
+ create mode 100644 include/linux/lzma.h
+ create mode 100644 include/linux/lzma/LzFind.h
+ create mode 100644 include/linux/lzma/LzHash.h
+ create mode 100644 include/linux/lzma/LzmaDec.h
+ create mode 100644 include/linux/lzma/LzmaEnc.h
+ create mode 100644 include/linux/lzma/Types.h
+ create mode 100644 lib/lzma/LzFind.c
+ create mode 100644 lib/lzma/LzmaDec.c
+ create mode 100644 lib/lzma/LzmaEnc.c
+ create mode 100644 lib/lzma/Makefile
+
+--- a/fs/jffs2/Kconfig
++++ b/fs/jffs2/Kconfig
+@@ -136,6 +136,15 @@ config JFFS2_LZO
+ This feature was added in July, 2007. Say 'N' if you need
+ compatibility with older bootloaders or kernels.
+
++config JFFS2_LZMA
++ bool "JFFS2 LZMA compression support" if JFFS2_COMPRESSION_OPTIONS
++ select LZMA_COMPRESS
++ select LZMA_DECOMPRESS
++ depends on JFFS2_FS
++ default n
++ help
++ JFFS2 wrapper to the LZMA C SDK
++
+ config JFFS2_RTIME
+ bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS
+ depends on JFFS2_FS
+--- a/fs/jffs2/Makefile
++++ b/fs/jffs2/Makefile
+@@ -19,4 +19,7 @@ jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rub
+ jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
+ jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
+ jffs2-$(CONFIG_JFFS2_LZO) += compr_lzo.o
++jffs2-$(CONFIG_JFFS2_LZMA) += compr_lzma.o
+ jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o
++
++CFLAGS_compr_lzma.o += -Iinclude/linux -Ilib/lzma
+--- a/fs/jffs2/compr.c
++++ b/fs/jffs2/compr.c
+@@ -378,6 +378,9 @@ int __init jffs2_compressors_init(void)
+ #ifdef CONFIG_JFFS2_LZO
+ jffs2_lzo_init();
+ #endif
++#ifdef CONFIG_JFFS2_LZMA
++ jffs2_lzma_init();
++#endif
+ /* Setting default compression mode */
+ #ifdef CONFIG_JFFS2_CMODE_NONE
+ jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+@@ -401,6 +404,9 @@ int __init jffs2_compressors_init(void)
+ int jffs2_compressors_exit(void)
+ {
+ /* Unregistering compressors */
++#ifdef CONFIG_JFFS2_LZMA
++ jffs2_lzma_exit();
++#endif
+ #ifdef CONFIG_JFFS2_LZO
+ jffs2_lzo_exit();
+ #endif
+--- a/fs/jffs2/compr.h
++++ b/fs/jffs2/compr.h
+@@ -29,9 +29,9 @@
+ #define JFFS2_DYNRUBIN_PRIORITY 20
+ #define JFFS2_LZARI_PRIORITY 30
+ #define JFFS2_RTIME_PRIORITY 50
+-#define JFFS2_ZLIB_PRIORITY 60
+-#define JFFS2_LZO_PRIORITY 80
+-
++#define JFFS2_LZMA_PRIORITY 70
++#define JFFS2_ZLIB_PRIORITY 80
++#define JFFS2_LZO_PRIORITY 90
+
+ #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
+ #define JFFS2_DYNRUBIN_DISABLED /* for decompression */
+@@ -101,5 +101,9 @@ void jffs2_zlib_exit(void);
+ int jffs2_lzo_init(void);
+ void jffs2_lzo_exit(void);
+ #endif
++#ifdef CONFIG_JFFS2_LZMA
++int jffs2_lzma_init(void);
++void jffs2_lzma_exit(void);
++#endif
+
+ #endif /* __JFFS2_COMPR_H__ */
+--- /dev/null
++++ b/fs/jffs2/compr_lzma.c
+@@ -0,0 +1,128 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * JFFS2 wrapper to the LZMA C SDK
++ *
++ */
++
++#include <linux/lzma.h>
++#include "compr.h"
++
++#ifdef __KERNEL__
++ static DEFINE_MUTEX(deflate_mutex);
++#endif
++
++CLzmaEncHandle *p;
++Byte propsEncoded[LZMA_PROPS_SIZE];
++SizeT propsSize = sizeof(propsEncoded);
++
++STATIC void lzma_free_workspace(void)
++{
++ LzmaEnc_Destroy(p, &lzma_alloc, &lzma_alloc);
++}
++
++STATIC int INIT lzma_alloc_workspace(CLzmaEncProps *props)
++{
++ if ((p = (CLzmaEncHandle *)LzmaEnc_Create(&lzma_alloc)) == NULL)
++ {
++ PRINT_ERROR("Failed to allocate lzma deflate workspace\n");
++ return -ENOMEM;
++ }
++
++ if (LzmaEnc_SetProps(p, props) != SZ_OK)
++ {
++ lzma_free_workspace();
++ return -1;
++ }
++
++ if (LzmaEnc_WriteProperties(p, propsEncoded, &propsSize) != SZ_OK)
++ {
++ lzma_free_workspace();
++ return -1;
++ }
++
++ return 0;
++}
++
++STATIC int jffs2_lzma_compress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t *sourcelen, uint32_t *dstlen)
++{
++ SizeT compress_size = (SizeT)(*dstlen);
++ int ret;
++
++ #ifdef __KERNEL__
++ mutex_lock(&deflate_mutex);
++ #endif
++
++ ret = LzmaEnc_MemEncode(p, cpage_out, &compress_size, data_in, *sourcelen,
++ 0, NULL, &lzma_alloc, &lzma_alloc);
++
++ #ifdef __KERNEL__
++ mutex_unlock(&deflate_mutex);
++ #endif
++
++ if (ret != SZ_OK)
++ return -1;
++
++ *dstlen = (uint32_t)compress_size;
++
++ return 0;
++}
++
++STATIC int jffs2_lzma_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t srclen, uint32_t destlen)
++{
++ int ret;
++ SizeT dl = (SizeT)destlen;
++ SizeT sl = (SizeT)srclen;
++ ELzmaStatus status;
++
++ ret = LzmaDecode(cpage_out, &dl, data_in, &sl, propsEncoded,
++ propsSize, LZMA_FINISH_ANY, &status, &lzma_alloc);
++
++ if (ret != SZ_OK || status == LZMA_STATUS_NOT_FINISHED || dl != (SizeT)destlen)
++ return -1;
++
++ return 0;
++}
++
++static struct jffs2_compressor jffs2_lzma_comp = {
++ .priority = JFFS2_LZMA_PRIORITY,
++ .name = "lzma",
++ .compr = JFFS2_COMPR_LZMA,
++ .compress = &jffs2_lzma_compress,
++ .decompress = &jffs2_lzma_decompress,
++ .disabled = 0,
++};
++
++int INIT jffs2_lzma_init(void)
++{
++ int ret;
++ CLzmaEncProps props;
++ LzmaEncProps_Init(&props);
++
++ props.dictSize = LZMA_BEST_DICT(0x2000);
++ props.level = LZMA_BEST_LEVEL;
++ props.lc = LZMA_BEST_LC;
++ props.lp = LZMA_BEST_LP;
++ props.pb = LZMA_BEST_PB;
++ props.fb = LZMA_BEST_FB;
++
++ ret = lzma_alloc_workspace(&props);
++ if (ret < 0)
++ return ret;
++
++ ret = jffs2_register_compressor(&jffs2_lzma_comp);
++ if (ret)
++ lzma_free_workspace();
++
++ return ret;
++}
++
++void jffs2_lzma_exit(void)
++{
++ jffs2_unregister_compressor(&jffs2_lzma_comp);
++ lzma_free_workspace();
++}
+--- a/fs/jffs2/super.c
++++ b/fs/jffs2/super.c
+@@ -374,14 +374,41 @@ static int __init init_jffs2_fs(void)
+ BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68);
+ BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32);
+
+- pr_info("version 2.2."
++ pr_info("version 2.2"
+ #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+ " (NAND)"
+ #endif
+ #ifdef CONFIG_JFFS2_SUMMARY
+- " (SUMMARY) "
++ " (SUMMARY)"
+ #endif
+- " © 2001-2006 Red Hat, Inc.\n");
++#ifdef CONFIG_JFFS2_ZLIB
++ " (ZLIB)"
++#endif
++#ifdef CONFIG_JFFS2_LZO
++ " (LZO)"
++#endif
++#ifdef CONFIG_JFFS2_LZMA
++ " (LZMA)"
++#endif
++#ifdef CONFIG_JFFS2_RTIME
++ " (RTIME)"
++#endif
++#ifdef CONFIG_JFFS2_RUBIN
++ " (RUBIN)"
++#endif
++#ifdef CONFIG_JFFS2_CMODE_NONE
++ " (CMODE_NONE)"
++#endif
++#ifdef CONFIG_JFFS2_CMODE_PRIORITY
++ " (CMODE_PRIORITY)"
++#endif
++#ifdef CONFIG_JFFS2_CMODE_SIZE
++ " (CMODE_SIZE)"
++#endif
++#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO
++ " (CMODE_FAVOURLZO)"
++#endif
++ " (c) 2001-2006 Red Hat, Inc.\n");
+
+ jffs2_inode_cachep = kmem_cache_create("jffs2_i",
+ sizeof(struct jffs2_inode_info),
+--- /dev/null
++++ b/include/linux/lzma.h
+@@ -0,0 +1,62 @@
++#ifndef __LZMA_H__
++#define __LZMA_H__
++
++#ifdef __KERNEL__
++ #include <linux/kernel.h>
++ #include <linux/sched.h>
++ #include <linux/slab.h>
++ #include <linux/vmalloc.h>
++ #include <linux/init.h>
++ #define LZMA_MALLOC vmalloc
++ #define LZMA_FREE vfree
++ #define PRINT_ERROR(msg) printk(KERN_WARNING #msg)
++ #define INIT __init
++ #define STATIC static
++#else
++ #include <stdint.h>
++ #include <stdlib.h>
++ #include <stdio.h>
++ #include <unistd.h>
++ #include <string.h>
++ #include <asm/types.h>
++ #include <errno.h>
++ #include <linux/jffs2.h>
++ #ifndef PAGE_SIZE
++ extern int page_size;
++ #define PAGE_SIZE page_size
++ #endif
++ #define LZMA_MALLOC malloc
++ #define LZMA_FREE free
++ #define PRINT_ERROR(msg) fprintf(stderr, msg)
++ #define INIT
++ #define STATIC
++#endif
++
++#include "lzma/LzmaDec.h"
++#include "lzma/LzmaEnc.h"
++
++#define LZMA_BEST_LEVEL (9)
++#define LZMA_BEST_LC (0)
++#define LZMA_BEST_LP (0)
++#define LZMA_BEST_PB (0)
++#define LZMA_BEST_FB (273)
++
++#define LZMA_BEST_DICT(n) (((int)((n) / 2)) * 2)
++
++static void *p_lzma_malloc(void *p, size_t size)
++{
++ if (size == 0)
++ return NULL;
++
++ return LZMA_MALLOC(size);
++}
++
++static void p_lzma_free(void *p, void *address)
++{
++ if (address != NULL)
++ LZMA_FREE(address);
++}
++
++static ISzAlloc lzma_alloc = { .Alloc = p_lzma_malloc, .Free = p_lzma_free };
++
++#endif
+--- /dev/null
++++ b/include/linux/lzma/LzFind.h
+@@ -0,0 +1,115 @@
++/* LzFind.h -- Match finder for LZ algorithms
++2009-04-22 : Igor Pavlov : Public domain */
++
++#ifndef __LZ_FIND_H
++#define __LZ_FIND_H
++
++#include "Types.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++typedef UInt32 CLzRef;
++
++typedef struct _CMatchFinder
++{
++ Byte *buffer;
++ UInt32 pos;
++ UInt32 posLimit;
++ UInt32 streamPos;
++ UInt32 lenLimit;
++
++ UInt32 cyclicBufferPos;
++ UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */
++
++ UInt32 matchMaxLen;
++ CLzRef *hash;
++ CLzRef *son;
++ UInt32 hashMask;
++ UInt32 cutValue;
++
++ Byte *bufferBase;
++ ISeqInStream *stream;
++ int streamEndWasReached;
++
++ UInt32 blockSize;
++ UInt32 keepSizeBefore;
++ UInt32 keepSizeAfter;
++
++ UInt32 numHashBytes;
++ int directInput;
++ size_t directInputRem;
++ int btMode;
++ int bigHash;
++ UInt32 historySize;
++ UInt32 fixedHashSize;
++ UInt32 hashSizeSum;
++ UInt32 numSons;
++ SRes result;
++ UInt32 crc[256];
++} CMatchFinder;
++
++#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer)
++#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)])
++
++#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos)
++
++int MatchFinder_NeedMove(CMatchFinder *p);
++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p);
++void MatchFinder_MoveBlock(CMatchFinder *p);
++void MatchFinder_ReadIfRequired(CMatchFinder *p);
++
++void MatchFinder_Construct(CMatchFinder *p);
++
++/* Conditions:
++ historySize <= 3 GB
++ keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB
++*/
++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
++ ISzAlloc *alloc);
++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc);
++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems);
++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue);
++
++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son,
++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue,
++ UInt32 *distances, UInt32 maxLen);
++
++/*
++Conditions:
++ Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func.
++ Mf_GetPointerToCurrentPos_Func's result must be used only before any other function
++*/
++
++typedef void (*Mf_Init_Func)(void *object);
++typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index);
++typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object);
++typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object);
++typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances);
++typedef void (*Mf_Skip_Func)(void *object, UInt32);
++
++typedef struct _IMatchFinder
++{
++ Mf_Init_Func Init;
++ Mf_GetIndexByte_Func GetIndexByte;
++ Mf_GetNumAvailableBytes_Func GetNumAvailableBytes;
++ Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos;
++ Mf_GetMatches_Func GetMatches;
++ Mf_Skip_Func Skip;
++} IMatchFinder;
++
++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable);
++
++void MatchFinder_Init(CMatchFinder *p);
++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+--- /dev/null
++++ b/include/linux/lzma/LzHash.h
+@@ -0,0 +1,54 @@
++/* LzHash.h -- HASH functions for LZ algorithms
++2009-02-07 : Igor Pavlov : Public domain */
++
++#ifndef __LZ_HASH_H
++#define __LZ_HASH_H
++
++#define kHash2Size (1 << 10)
++#define kHash3Size (1 << 16)
++#define kHash4Size (1 << 20)
++
++#define kFix3HashSize (kHash2Size)
++#define kFix4HashSize (kHash2Size + kHash3Size)
++#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size)
++
++#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8);
++
++#define HASH3_CALC { \
++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
++ hash2Value = temp & (kHash2Size - 1); \
++ hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; }
++
++#define HASH4_CALC { \
++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
++ hash2Value = temp & (kHash2Size - 1); \
++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
++ hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; }
++
++#define HASH5_CALC { \
++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
++ hash2Value = temp & (kHash2Size - 1); \
++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \
++ hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \
++ hash4Value &= (kHash4Size - 1); }
++
++/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */
++#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF;
++
++
++#define MT_HASH2_CALC \
++ hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1);
++
++#define MT_HASH3_CALC { \
++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
++ hash2Value = temp & (kHash2Size - 1); \
++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); }
++
++#define MT_HASH4_CALC { \
++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
++ hash2Value = temp & (kHash2Size - 1); \
++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); }
++
++#endif
+--- /dev/null
++++ b/include/linux/lzma/LzmaDec.h
+@@ -0,0 +1,231 @@
++/* LzmaDec.h -- LZMA Decoder
++2009-02-07 : Igor Pavlov : Public domain */
++
++#ifndef __LZMA_DEC_H
++#define __LZMA_DEC_H
++
++#include "Types.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/* #define _LZMA_PROB32 */
++/* _LZMA_PROB32 can increase the speed on some CPUs,
++ but memory usage for CLzmaDec::probs will be doubled in that case */
++
++#ifdef _LZMA_PROB32
++#define CLzmaProb UInt32
++#else
++#define CLzmaProb UInt16
++#endif
++
++
++/* ---------- LZMA Properties ---------- */
++
++#define LZMA_PROPS_SIZE 5
++
++typedef struct _CLzmaProps
++{
++ unsigned lc, lp, pb;
++ UInt32 dicSize;
++} CLzmaProps;
++
++/* LzmaProps_Decode - decodes properties
++Returns:
++ SZ_OK
++ SZ_ERROR_UNSUPPORTED - Unsupported properties
++*/
++
++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);
++
++
++/* ---------- LZMA Decoder state ---------- */
++
++/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case.
++ Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */
++
++#define LZMA_REQUIRED_INPUT_MAX 20
++
++typedef struct
++{
++ CLzmaProps prop;
++ CLzmaProb *probs;
++ Byte *dic;
++ const Byte *buf;
++ UInt32 range, code;
++ SizeT dicPos;
++ SizeT dicBufSize;
++ UInt32 processedPos;
++ UInt32 checkDicSize;
++ unsigned state;
++ UInt32 reps[4];
++ unsigned remainLen;
++ int needFlush;
++ int needInitState;
++ UInt32 numProbs;
++ unsigned tempBufSize;
++ Byte tempBuf[LZMA_REQUIRED_INPUT_MAX];
++} CLzmaDec;
++
++#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; }
++
++void LzmaDec_Init(CLzmaDec *p);
++
++/* There are two types of LZMA streams:
++ 0) Stream with end mark. That end mark adds about 6 bytes to compressed size.
++ 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */
++
++typedef enum
++{
++ LZMA_FINISH_ANY, /* finish at any point */
++ LZMA_FINISH_END /* block must be finished at the end */
++} ELzmaFinishMode;
++
++/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!!
++
++ You must use LZMA_FINISH_END, when you know that current output buffer
++ covers last bytes of block. In other cases you must use LZMA_FINISH_ANY.
++
++ If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK,
++ and output value of destLen will be less than output buffer size limit.
++ You can check status result also.
++
++ You can use multiple checks to test data integrity after full decompression:
++ 1) Check Result and "status" variable.
++ 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
++ 3) Check that output(srcLen) = compressedSize, if you know real compressedSize.
++ You must use correct finish mode in that case. */
++
++typedef enum
++{
++ LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */
++ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */
++ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */
++ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */
++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */
++} ELzmaStatus;
++
++/* ELzmaStatus is used only as output value for function call */
++
++
++/* ---------- Interfaces ---------- */
++
++/* There are 3 levels of interfaces:
++ 1) Dictionary Interface
++ 2) Buffer Interface
++ 3) One Call Interface
++ You can select any of these interfaces, but don't mix functions from different
++ groups for same object. */
++
++
++/* There are two variants to allocate state for Dictionary Interface:
++ 1) LzmaDec_Allocate / LzmaDec_Free
++ 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
++ You can use variant 2, if you set dictionary buffer manually.
++ For Buffer Interface you must always use variant 1.
++
++LzmaDec_Allocate* can return:
++ SZ_OK
++ SZ_ERROR_MEM - Memory allocation error
++ SZ_ERROR_UNSUPPORTED - Unsupported properties
++*/
++
++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc);
++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc);
++
++SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc);
++void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc);
++
++/* ---------- Dictionary Interface ---------- */
++
++/* You can use it, if you want to eliminate the overhead for data copying from
++ dictionary to some other external buffer.
++ You must work with CLzmaDec variables directly in this interface.
++
++ STEPS:
++ LzmaDec_Constr()
++ LzmaDec_Allocate()
++ for (each new stream)
++ {
++ LzmaDec_Init()
++ while (it needs more decompression)
++ {
++ LzmaDec_DecodeToDic()
++ use data from CLzmaDec::dic and update CLzmaDec::dicPos
++ }
++ }
++ LzmaDec_Free()
++*/
++
++/* LzmaDec_DecodeToDic
++
++ The decoding to internal dictionary buffer (CLzmaDec::dic).
++ You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
++
++finishMode:
++ It has meaning only if the decoding reaches output limit (dicLimit).
++ LZMA_FINISH_ANY - Decode just dicLimit bytes.
++ LZMA_FINISH_END - Stream must be finished after dicLimit.
++
++Returns:
++ SZ_OK
++ status:
++ LZMA_STATUS_FINISHED_WITH_MARK
++ LZMA_STATUS_NOT_FINISHED
++ LZMA_STATUS_NEEDS_MORE_INPUT
++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
++ SZ_ERROR_DATA - Data error
++*/
++
++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit,
++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
++
++
++/* ---------- Buffer Interface ---------- */
++
++/* It's zlib-like interface.
++ See LzmaDec_DecodeToDic description for information about STEPS and return results,
++ but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need
++ to work with CLzmaDec variables manually.
++
++finishMode:
++ It has meaning only if the decoding reaches output limit (*destLen).
++ LZMA_FINISH_ANY - Decode just destLen bytes.
++ LZMA_FINISH_END - Stream must be finished after (*destLen).
++*/
++
++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen,
++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
++
++
++/* ---------- One Call Interface ---------- */
++
++/* LzmaDecode
++
++finishMode:
++ It has meaning only if the decoding reaches output limit (*destLen).
++ LZMA_FINISH_ANY - Decode just destLen bytes.
++ LZMA_FINISH_END - Stream must be finished after (*destLen).
++
++Returns:
++ SZ_OK
++ status:
++ LZMA_STATUS_FINISHED_WITH_MARK
++ LZMA_STATUS_NOT_FINISHED
++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
++ SZ_ERROR_DATA - Data error
++ SZ_ERROR_MEM - Memory allocation error
++ SZ_ERROR_UNSUPPORTED - Unsupported properties
++ SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
++*/
++
++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
++ ELzmaStatus *status, ISzAlloc *alloc);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+--- /dev/null
++++ b/include/linux/lzma/LzmaEnc.h
+@@ -0,0 +1,80 @@
++/* LzmaEnc.h -- LZMA Encoder
++2009-02-07 : Igor Pavlov : Public domain */
++
++#ifndef __LZMA_ENC_H
++#define __LZMA_ENC_H
++
++#include "Types.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define LZMA_PROPS_SIZE 5
++
++typedef struct _CLzmaEncProps
++{
++ int level; /* 0 <= level <= 9 */
++ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version
++ (1 << 12) <= dictSize <= (1 << 30) for 64-bit version
++ default = (1 << 24) */
++ int lc; /* 0 <= lc <= 8, default = 3 */
++ int lp; /* 0 <= lp <= 4, default = 0 */
++ int pb; /* 0 <= pb <= 4, default = 2 */
++ int algo; /* 0 - fast, 1 - normal, default = 1 */
++ int fb; /* 5 <= fb <= 273, default = 32 */
++ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */
++ int numHashBytes; /* 2, 3 or 4, default = 4 */
++ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */
++ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */
++ int numThreads; /* 1 or 2, default = 2 */
++} CLzmaEncProps;
++
++void LzmaEncProps_Init(CLzmaEncProps *p);
++void LzmaEncProps_Normalize(CLzmaEncProps *p);
++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2);
++
++
++/* ---------- CLzmaEncHandle Interface ---------- */
++
++/* LzmaEnc_* functions can return the following exit codes:
++Returns:
++ SZ_OK - OK
++ SZ_ERROR_MEM - Memory allocation error
++ SZ_ERROR_PARAM - Incorrect paramater in props
++ SZ_ERROR_WRITE - Write callback error.
++ SZ_ERROR_PROGRESS - some break from progress callback
++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version)
++*/
++
++typedef void * CLzmaEncHandle;
++
++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc);
++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig);
++SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props);
++SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size);
++SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream,
++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
++SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
++
++/* ---------- One Call Interface ---------- */
++
++/* LzmaEncode
++Return code:
++ SZ_OK - OK
++ SZ_ERROR_MEM - Memory allocation error
++ SZ_ERROR_PARAM - Incorrect paramater
++ SZ_ERROR_OUTPUT_EOF - output buffer overflow
++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version)
++*/
++
++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+--- /dev/null
++++ b/include/linux/lzma/Types.h
+@@ -0,0 +1,226 @@
++/* Types.h -- Basic types
++2009-11-23 : Igor Pavlov : Public domain */
++
++#ifndef __7Z_TYPES_H
++#define __7Z_TYPES_H
++
++#include <stddef.h>
++
++#ifdef _WIN32
++#include <windows.h>
++#endif
++
++#ifndef EXTERN_C_BEGIN
++#ifdef __cplusplus
++#define EXTERN_C_BEGIN extern "C" {
++#define EXTERN_C_END }
++#else
++#define EXTERN_C_BEGIN
++#define EXTERN_C_END
++#endif
++#endif
++
++EXTERN_C_BEGIN
++
++#define SZ_OK 0
++
++#define SZ_ERROR_DATA 1
++#define SZ_ERROR_MEM 2
++#define SZ_ERROR_CRC 3
++#define SZ_ERROR_UNSUPPORTED 4
++#define SZ_ERROR_PARAM 5
++#define SZ_ERROR_INPUT_EOF 6
++#define SZ_ERROR_OUTPUT_EOF 7
++#define SZ_ERROR_READ 8
++#define SZ_ERROR_WRITE 9
++#define SZ_ERROR_PROGRESS 10
++#define SZ_ERROR_FAIL 11
++#define SZ_ERROR_THREAD 12
++
++#define SZ_ERROR_ARCHIVE 16
++#define SZ_ERROR_NO_ARCHIVE 17
++
++typedef int SRes;
++
++#ifdef _WIN32
++typedef DWORD WRes;
++#else
++typedef int WRes;
++#endif
++
++#ifndef RINOK
++#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; }
++#endif
++
++typedef unsigned char Byte;
++typedef short Int16;
++typedef unsigned short UInt16;
++
++#ifdef _LZMA_UINT32_IS_ULONG
++typedef long Int32;
++typedef unsigned long UInt32;
++#else
++typedef int Int32;
++typedef unsigned int UInt32;
++#endif
++
++#ifdef _SZ_NO_INT_64
++
++/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.
++ NOTES: Some code will work incorrectly in that case! */
++
++typedef long Int64;
++typedef unsigned long UInt64;
++
++#else
++
++#if defined(_MSC_VER) || defined(__BORLANDC__)
++typedef __int64 Int64;
++typedef unsigned __int64 UInt64;
++#else
++typedef long long int Int64;
++typedef unsigned long long int UInt64;
++#endif
++
++#endif
++
++#ifdef _LZMA_NO_SYSTEM_SIZE_T
++typedef UInt32 SizeT;
++#else
++typedef size_t SizeT;
++#endif
++
++typedef int Bool;
++#define True 1
++#define False 0
++
++
++#ifdef _WIN32
++#define MY_STD_CALL __stdcall
++#else
++#define MY_STD_CALL
++#endif
++
++#ifdef _MSC_VER
++
++#if _MSC_VER >= 1300
++#define MY_NO_INLINE __declspec(noinline)
++#else
++#define MY_NO_INLINE
++#endif
++
++#define MY_CDECL __cdecl
++#define MY_FAST_CALL __fastcall
++
++#else
++
++#define MY_CDECL
++#define MY_FAST_CALL
++
++#endif
++
++
++/* The following interfaces use first parameter as pointer to structure */
++
++typedef struct
++{
++ SRes (*Read)(void *p, void *buf, size_t *size);
++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
++ (output(*size) < input(*size)) is allowed */
++} ISeqInStream;
++
++/* it can return SZ_ERROR_INPUT_EOF */
++SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size);
++SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType);
++SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf);
++
++typedef struct
++{
++ size_t (*Write)(void *p, const void *buf, size_t size);
++ /* Returns: result - the number of actually written bytes.
++ (result < size) means error */
++} ISeqOutStream;
++
++typedef enum
++{
++ SZ_SEEK_SET = 0,
++ SZ_SEEK_CUR = 1,
++ SZ_SEEK_END = 2
++} ESzSeek;
++
++typedef struct
++{
++ SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */
++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin);
++} ISeekInStream;
++
++typedef struct
++{
++ SRes (*Look)(void *p, void **buf, size_t *size);
++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
++ (output(*size) > input(*size)) is not allowed
++ (output(*size) < input(*size)) is allowed */
++ SRes (*Skip)(void *p, size_t offset);
++ /* offset must be <= output(*size) of Look */
++
++ SRes (*Read)(void *p, void *buf, size_t *size);
++ /* reads directly (without buffer). It's same as ISeqInStream::Read */
++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin);
++} ILookInStream;
++
++SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size);
++SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset);
++
++/* reads via ILookInStream::Read */
++SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType);
++SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size);
++
++#define LookToRead_BUF_SIZE (1 << 14)
++
++typedef struct
++{
++ ILookInStream s;
++ ISeekInStream *realStream;
++ size_t pos;
++ size_t size;
++ Byte buf[LookToRead_BUF_SIZE];
++} CLookToRead;
++
++void LookToRead_CreateVTable(CLookToRead *p, int lookahead);
++void LookToRead_Init(CLookToRead *p);
++
++typedef struct
++{
++ ISeqInStream s;
++ ILookInStream *realStream;
++} CSecToLook;
++
++void SecToLook_CreateVTable(CSecToLook *p);
++
++typedef struct
++{
++ ISeqInStream s;
++ ILookInStream *realStream;
++} CSecToRead;
++
++void SecToRead_CreateVTable(CSecToRead *p);
++
++typedef struct
++{
++ SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize);
++ /* Returns: result. (result != SZ_OK) means break.
++ Value (UInt64)(Int64)-1 for size means unknown value. */
++} ICompressProgress;
++
++typedef struct
++{
++ void *(*Alloc)(void *p, size_t size);
++ void (*Free)(void *p, void *address); /* address can be 0 */
++} ISzAlloc;
++
++#define IAlloc_Alloc(p, size) (p)->Alloc((p), size)
++#define IAlloc_Free(p, a) (p)->Free((p), a)
++
++EXTERN_C_END
++
++#endif
+--- a/include/uapi/linux/jffs2.h
++++ b/include/uapi/linux/jffs2.h
+@@ -46,6 +46,7 @@
+ #define JFFS2_COMPR_DYNRUBIN 0x05
+ #define JFFS2_COMPR_ZLIB 0x06
+ #define JFFS2_COMPR_LZO 0x07
++#define JFFS2_COMPR_LZMA 0x08
+ /* Compatibility flags. */
+ #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
+ #define JFFS2_NODE_ACCURATE 0x2000
+--- a/lib/Kconfig
++++ b/lib/Kconfig
+@@ -315,6 +315,12 @@ config ZSTD_DECOMPRESS
+
+ source "lib/xz/Kconfig"
+
++config LZMA_COMPRESS
++ tristate
++
++config LZMA_DECOMPRESS
++ tristate
++
+ #
+ # These all provide a common interface (hence the apparent duplication with
+ # ZLIB_INFLATE; DECOMPRESS_GZIP is just a wrapper.)
+--- a/lib/Makefile
++++ b/lib/Makefile
+@@ -136,6 +136,16 @@ CFLAGS_kobject.o += -DDEBUG
+ CFLAGS_kobject_uevent.o += -DDEBUG
+ endif
+
++ifdef CONFIG_JFFS2_ZLIB
++ CONFIG_ZLIB_INFLATE:=y
++ CONFIG_ZLIB_DEFLATE:=y
++endif
++
++ifdef CONFIG_JFFS2_LZMA
++ CONFIG_LZMA_DECOMPRESS:=y
++ CONFIG_LZMA_COMPRESS:=y
++endif
++
+ obj-$(CONFIG_DEBUG_INFO_REDUCED) += debug_info.o
+ CFLAGS_debug_info.o += $(call cc-option, -femit-struct-debug-detailed=any)
+
+@@ -191,6 +201,8 @@ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/
+ obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/
+ obj-$(CONFIG_XZ_DEC) += xz/
+ obj-$(CONFIG_RAID6_PQ) += raid6/
++obj-$(CONFIG_LZMA_COMPRESS) += lzma/
++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma/
+
+ lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o
+ lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o
+--- /dev/null
++++ b/lib/lzma/LzFind.c
+@@ -0,0 +1,761 @@
++/* LzFind.c -- Match finder for LZ algorithms
++2009-04-22 : Igor Pavlov : Public domain */
++
++#include <string.h>
++
++#include "LzFind.h"
++#include "LzHash.h"
++
++#define kEmptyHashValue 0
++#define kMaxValForNormalize ((UInt32)0xFFFFFFFF)
++#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */
++#define kNormalizeMask (~(kNormalizeStepMin - 1))
++#define kMaxHistorySize ((UInt32)3 << 30)
++
++#define kStartMaxLen 3
++
++static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc)
++{
++ if (!p->directInput)
++ {
++ alloc->Free(alloc, p->bufferBase);
++ p->bufferBase = 0;
++ }
++}
++
++/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */
++
++static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc)
++{
++ UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv;
++ if (p->directInput)
++ {
++ p->blockSize = blockSize;
++ return 1;
++ }
++ if (p->bufferBase == 0 || p->blockSize != blockSize)
++ {
++ LzInWindow_Free(p, alloc);
++ p->blockSize = blockSize;
++ p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize);
++ }
++ return (p->bufferBase != 0);
++}
++
++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
++Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; }
++
++UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
++
++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue)
++{
++ p->posLimit -= subValue;
++ p->pos -= subValue;
++ p->streamPos -= subValue;
++}
++
++static void MatchFinder_ReadBlock(CMatchFinder *p)
++{
++ if (p->streamEndWasReached || p->result != SZ_OK)
++ return;
++ if (p->directInput)
++ {
++ UInt32 curSize = 0xFFFFFFFF - p->streamPos;
++ if (curSize > p->directInputRem)
++ curSize = (UInt32)p->directInputRem;
++ p->directInputRem -= curSize;
++ p->streamPos += curSize;
++ if (p->directInputRem == 0)
++ p->streamEndWasReached = 1;
++ return;
++ }
++ for (;;)
++ {
++ Byte *dest = p->buffer + (p->streamPos - p->pos);
++ size_t size = (p->bufferBase + p->blockSize - dest);
++ if (size == 0)
++ return;
++ p->result = p->stream->Read(p->stream, dest, &size);
++ if (p->result != SZ_OK)
++ return;
++ if (size == 0)
++ {
++ p->streamEndWasReached = 1;
++ return;
++ }
++ p->streamPos += (UInt32)size;
++ if (p->streamPos - p->pos > p->keepSizeAfter)
++ return;
++ }
++}
++
++void MatchFinder_MoveBlock(CMatchFinder *p)
++{
++ memmove(p->bufferBase,
++ p->buffer - p->keepSizeBefore,
++ (size_t)(p->streamPos - p->pos + p->keepSizeBefore));
++ p->buffer = p->bufferBase + p->keepSizeBefore;
++}
++
++int MatchFinder_NeedMove(CMatchFinder *p)
++{
++ if (p->directInput)
++ return 0;
++ /* if (p->streamEndWasReached) return 0; */
++ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter);
++}
++
++void MatchFinder_ReadIfRequired(CMatchFinder *p)
++{
++ if (p->streamEndWasReached)
++ return;
++ if (p->keepSizeAfter >= p->streamPos - p->pos)
++ MatchFinder_ReadBlock(p);
++}
++
++static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p)
++{
++ if (MatchFinder_NeedMove(p))
++ MatchFinder_MoveBlock(p);
++ MatchFinder_ReadBlock(p);
++}
++
++static void MatchFinder_SetDefaultSettings(CMatchFinder *p)
++{
++ p->cutValue = 32;
++ p->btMode = 1;
++ p->numHashBytes = 4;
++ p->bigHash = 0;
++}
++
++#define kCrcPoly 0xEDB88320
++
++void MatchFinder_Construct(CMatchFinder *p)
++{
++ UInt32 i;
++ p->bufferBase = 0;
++ p->directInput = 0;
++ p->hash = 0;
++ MatchFinder_SetDefaultSettings(p);
++
++ for (i = 0; i < 256; i++)
++ {
++ UInt32 r = i;
++ int j;
++ for (j = 0; j < 8; j++)
++ r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
++ p->crc[i] = r;
++ }
++}
++
++static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc)
++{
++ alloc->Free(alloc, p->hash);
++ p->hash = 0;
++}
++
++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc)
++{
++ MatchFinder_FreeThisClassMemory(p, alloc);
++ LzInWindow_Free(p, alloc);
++}
++
++static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc)
++{
++ size_t sizeInBytes = (size_t)num * sizeof(CLzRef);
++ if (sizeInBytes / sizeof(CLzRef) != num)
++ return 0;
++ return (CLzRef *)alloc->Alloc(alloc, sizeInBytes);
++}
++
++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
++ ISzAlloc *alloc)
++{
++ UInt32 sizeReserv;
++ if (historySize > kMaxHistorySize)
++ {
++ MatchFinder_Free(p, alloc);
++ return 0;
++ }
++ sizeReserv = historySize >> 1;
++ if (historySize > ((UInt32)2 << 30))
++ sizeReserv = historySize >> 2;
++ sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19);
++
++ p->keepSizeBefore = historySize + keepAddBufferBefore + 1;
++ p->keepSizeAfter = matchMaxLen + keepAddBufferAfter;
++ /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */
++ if (LzInWindow_Create(p, sizeReserv, alloc))
++ {
++ UInt32 newCyclicBufferSize = historySize + 1;
++ UInt32 hs;
++ p->matchMaxLen = matchMaxLen;
++ {
++ p->fixedHashSize = 0;
++ if (p->numHashBytes == 2)
++ hs = (1 << 16) - 1;
++ else
++ {
++ hs = historySize - 1;
++ hs |= (hs >> 1);
++ hs |= (hs >> 2);
++ hs |= (hs >> 4);
++ hs |= (hs >> 8);
++ hs >>= 1;
++ hs |= 0xFFFF; /* don't change it! It's required for Deflate */
++ if (hs > (1 << 24))
++ {
++ if (p->numHashBytes == 3)
++ hs = (1 << 24) - 1;
++ else
++ hs >>= 1;
++ }
++ }
++ p->hashMask = hs;
++ hs++;
++ if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size;
++ if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size;
++ if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size;
++ hs += p->fixedHashSize;
++ }
++
++ {
++ UInt32 prevSize = p->hashSizeSum + p->numSons;
++ UInt32 newSize;
++ p->historySize = historySize;
++ p->hashSizeSum = hs;
++ p->cyclicBufferSize = newCyclicBufferSize;
++ p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize);
++ newSize = p->hashSizeSum + p->numSons;
++ if (p->hash != 0 && prevSize == newSize)
++ return 1;
++ MatchFinder_FreeThisClassMemory(p, alloc);
++ p->hash = AllocRefs(newSize, alloc);
++ if (p->hash != 0)
++ {
++ p->son = p->hash + p->hashSizeSum;
++ return 1;
++ }
++ }
++ }
++ MatchFinder_Free(p, alloc);
++ return 0;
++}
++
++static void MatchFinder_SetLimits(CMatchFinder *p)
++{
++ UInt32 limit = kMaxValForNormalize - p->pos;
++ UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos;
++ if (limit2 < limit)
++ limit = limit2;
++ limit2 = p->streamPos - p->pos;
++ if (limit2 <= p->keepSizeAfter)
++ {
++ if (limit2 > 0)
++ limit2 = 1;
++ }
++ else
++ limit2 -= p->keepSizeAfter;
++ if (limit2 < limit)
++ limit = limit2;
++ {
++ UInt32 lenLimit = p->streamPos - p->pos;
++ if (lenLimit > p->matchMaxLen)
++ lenLimit = p->matchMaxLen;
++ p->lenLimit = lenLimit;
++ }
++ p->posLimit = p->pos + limit;
++}
++
++void MatchFinder_Init(CMatchFinder *p)
++{
++ UInt32 i;
++ for (i = 0; i < p->hashSizeSum; i++)
++ p->hash[i] = kEmptyHashValue;
++ p->cyclicBufferPos = 0;
++ p->buffer = p->bufferBase;
++ p->pos = p->streamPos = p->cyclicBufferSize;
++ p->result = SZ_OK;
++ p->streamEndWasReached = 0;
++ MatchFinder_ReadBlock(p);
++ MatchFinder_SetLimits(p);
++}
++
++static UInt32 MatchFinder_GetSubValue(CMatchFinder *p)
++{
++ return (p->pos - p->historySize - 1) & kNormalizeMask;
++}
++
++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems)
++{
++ UInt32 i;
++ for (i = 0; i < numItems; i++)
++ {
++ UInt32 value = items[i];
++ if (value <= subValue)
++ value = kEmptyHashValue;
++ else
++ value -= subValue;
++ items[i] = value;
++ }
++}
++
++static void MatchFinder_Normalize(CMatchFinder *p)
++{
++ UInt32 subValue = MatchFinder_GetSubValue(p);
++ MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons);
++ MatchFinder_ReduceOffsets(p, subValue);
++}
++
++static void MatchFinder_CheckLimits(CMatchFinder *p)
++{
++ if (p->pos == kMaxValForNormalize)
++ MatchFinder_Normalize(p);
++ if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos)
++ MatchFinder_CheckAndMoveAndRead(p);
++ if (p->cyclicBufferPos == p->cyclicBufferSize)
++ p->cyclicBufferPos = 0;
++ MatchFinder_SetLimits(p);
++}
++
++static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
++ UInt32 *distances, UInt32 maxLen)
++{
++ son[_cyclicBufferPos] = curMatch;
++ for (;;)
++ {
++ UInt32 delta = pos - curMatch;
++ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
++ return distances;
++ {
++ const Byte *pb = cur - delta;
++ curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
++ if (pb[maxLen] == cur[maxLen] && *pb == *cur)
++ {
++ UInt32 len = 0;
++ while (++len != lenLimit)
++ if (pb[len] != cur[len])
++ break;
++ if (maxLen < len)
++ {
++ *distances++ = maxLen = len;
++ *distances++ = delta - 1;
++ if (len == lenLimit)
++ return distances;
++ }
++ }
++ }
++ }
++}
++
++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
++ UInt32 *distances, UInt32 maxLen)
++{
++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1;
++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1);
++ UInt32 len0 = 0, len1 = 0;
++ for (;;)
++ {
++ UInt32 delta = pos - curMatch;
++ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
++ {
++ *ptr0 = *ptr1 = kEmptyHashValue;
++ return distances;
++ }
++ {
++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
++ const Byte *pb = cur - delta;
++ UInt32 len = (len0 < len1 ? len0 : len1);
++ if (pb[len] == cur[len])
++ {
++ if (++len != lenLimit && pb[len] == cur[len])
++ while (++len != lenLimit)
++ if (pb[len] != cur[len])
++ break;
++ if (maxLen < len)
++ {
++ *distances++ = maxLen = len;
++ *distances++ = delta - 1;
++ if (len == lenLimit)
++ {
++ *ptr1 = pair[0];
++ *ptr0 = pair[1];
++ return distances;
++ }
++ }
++ }
++ if (pb[len] < cur[len])
++ {
++ *ptr1 = curMatch;
++ ptr1 = pair + 1;
++ curMatch = *ptr1;
++ len1 = len;
++ }
++ else
++ {
++ *ptr0 = curMatch;
++ ptr0 = pair;
++ curMatch = *ptr0;
++ len0 = len;
++ }
++ }
++ }
++}
++
++static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue)
++{
++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1;
++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1);
++ UInt32 len0 = 0, len1 = 0;
++ for (;;)
++ {
++ UInt32 delta = pos - curMatch;
++ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
++ {
++ *ptr0 = *ptr1 = kEmptyHashValue;
++ return;
++ }
++ {
++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
++ const Byte *pb = cur - delta;
++ UInt32 len = (len0 < len1 ? len0 : len1);
++ if (pb[len] == cur[len])
++ {
++ while (++len != lenLimit)
++ if (pb[len] != cur[len])
++ break;
++ {
++ if (len == lenLimit)
++ {
++ *ptr1 = pair[0];
++ *ptr0 = pair[1];
++ return;
++ }
++ }
++ }
++ if (pb[len] < cur[len])
++ {
++ *ptr1 = curMatch;
++ ptr1 = pair + 1;
++ curMatch = *ptr1;
++ len1 = len;
++ }
++ else
++ {
++ *ptr0 = curMatch;
++ ptr0 = pair;
++ curMatch = *ptr0;
++ len0 = len;
++ }
++ }
++ }
++}
++
++#define MOVE_POS \
++ ++p->cyclicBufferPos; \
++ p->buffer++; \
++ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p);
++
++#define MOVE_POS_RET MOVE_POS return offset;
++
++static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; }
++
++#define GET_MATCHES_HEADER2(minLen, ret_op) \
++ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \
++ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \
++ cur = p->buffer;
++
++#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0)
++#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue)
++
++#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue
++
++#define GET_MATCHES_FOOTER(offset, maxLen) \
++ offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \
++ distances + offset, maxLen) - distances); MOVE_POS_RET;
++
++#define SKIP_FOOTER \
++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS;
++
++static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
++{
++ UInt32 offset;
++ GET_MATCHES_HEADER(2)
++ HASH2_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ offset = 0;
++ GET_MATCHES_FOOTER(offset, 1)
++}
++
++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
++{
++ UInt32 offset;
++ GET_MATCHES_HEADER(3)
++ HASH_ZIP_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ offset = 0;
++ GET_MATCHES_FOOTER(offset, 2)
++}
++
++static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
++{
++ UInt32 hash2Value, delta2, maxLen, offset;
++ GET_MATCHES_HEADER(3)
++
++ HASH3_CALC;
++
++ delta2 = p->pos - p->hash[hash2Value];
++ curMatch = p->hash[kFix3HashSize + hashValue];
++
++ p->hash[hash2Value] =
++ p->hash[kFix3HashSize + hashValue] = p->pos;
++
++
++ maxLen = 2;
++ offset = 0;
++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
++ {
++ for (; maxLen != lenLimit; maxLen++)
++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
++ break;
++ distances[0] = maxLen;
++ distances[1] = delta2 - 1;
++ offset = 2;
++ if (maxLen == lenLimit)
++ {
++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
++ MOVE_POS_RET;
++ }
++ }
++ GET_MATCHES_FOOTER(offset, maxLen)
++}
++
++static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
++{
++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset;
++ GET_MATCHES_HEADER(4)
++
++ HASH4_CALC;
++
++ delta2 = p->pos - p->hash[ hash2Value];
++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value];
++ curMatch = p->hash[kFix4HashSize + hashValue];
++
++ p->hash[ hash2Value] =
++ p->hash[kFix3HashSize + hash3Value] =
++ p->hash[kFix4HashSize + hashValue] = p->pos;
++
++ maxLen = 1;
++ offset = 0;
++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
++ {
++ distances[0] = maxLen = 2;
++ distances[1] = delta2 - 1;
++ offset = 2;
++ }
++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur)
++ {
++ maxLen = 3;
++ distances[offset + 1] = delta3 - 1;
++ offset += 2;
++ delta2 = delta3;
++ }
++ if (offset != 0)
++ {
++ for (; maxLen != lenLimit; maxLen++)
++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
++ break;
++ distances[offset - 2] = maxLen;
++ if (maxLen == lenLimit)
++ {
++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
++ MOVE_POS_RET;
++ }
++ }
++ if (maxLen < 3)
++ maxLen = 3;
++ GET_MATCHES_FOOTER(offset, maxLen)
++}
++
++static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
++{
++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset;
++ GET_MATCHES_HEADER(4)
++
++ HASH4_CALC;
++
++ delta2 = p->pos - p->hash[ hash2Value];
++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value];
++ curMatch = p->hash[kFix4HashSize + hashValue];
++
++ p->hash[ hash2Value] =
++ p->hash[kFix3HashSize + hash3Value] =
++ p->hash[kFix4HashSize + hashValue] = p->pos;
++
++ maxLen = 1;
++ offset = 0;
++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
++ {
++ distances[0] = maxLen = 2;
++ distances[1] = delta2 - 1;
++ offset = 2;
++ }
++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur)
++ {
++ maxLen = 3;
++ distances[offset + 1] = delta3 - 1;
++ offset += 2;
++ delta2 = delta3;
++ }
++ if (offset != 0)
++ {
++ for (; maxLen != lenLimit; maxLen++)
++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
++ break;
++ distances[offset - 2] = maxLen;
++ if (maxLen == lenLimit)
++ {
++ p->son[p->cyclicBufferPos] = curMatch;
++ MOVE_POS_RET;
++ }
++ }
++ if (maxLen < 3)
++ maxLen = 3;
++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
++ distances + offset, maxLen) - (distances));
++ MOVE_POS_RET
++}
++
++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
++{
++ UInt32 offset;
++ GET_MATCHES_HEADER(3)
++ HASH_ZIP_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
++ distances, 2) - (distances));
++ MOVE_POS_RET
++}
++
++static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ SKIP_HEADER(2)
++ HASH2_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ SKIP_FOOTER
++ }
++ while (--num != 0);
++}
++
++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ SKIP_HEADER(3)
++ HASH_ZIP_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ SKIP_FOOTER
++ }
++ while (--num != 0);
++}
++
++static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ UInt32 hash2Value;
++ SKIP_HEADER(3)
++ HASH3_CALC;
++ curMatch = p->hash[kFix3HashSize + hashValue];
++ p->hash[hash2Value] =
++ p->hash[kFix3HashSize + hashValue] = p->pos;
++ SKIP_FOOTER
++ }
++ while (--num != 0);
++}
++
++static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ UInt32 hash2Value, hash3Value;
++ SKIP_HEADER(4)
++ HASH4_CALC;
++ curMatch = p->hash[kFix4HashSize + hashValue];
++ p->hash[ hash2Value] =
++ p->hash[kFix3HashSize + hash3Value] = p->pos;
++ p->hash[kFix4HashSize + hashValue] = p->pos;
++ SKIP_FOOTER
++ }
++ while (--num != 0);
++}
++
++static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ UInt32 hash2Value, hash3Value;
++ SKIP_HEADER(4)
++ HASH4_CALC;
++ curMatch = p->hash[kFix4HashSize + hashValue];
++ p->hash[ hash2Value] =
++ p->hash[kFix3HashSize + hash3Value] =
++ p->hash[kFix4HashSize + hashValue] = p->pos;
++ p->son[p->cyclicBufferPos] = curMatch;
++ MOVE_POS
++ }
++ while (--num != 0);
++}
++
++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ SKIP_HEADER(3)
++ HASH_ZIP_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ p->son[p->cyclicBufferPos] = curMatch;
++ MOVE_POS
++ }
++ while (--num != 0);
++}
++
++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable)
++{
++ vTable->Init = (Mf_Init_Func)MatchFinder_Init;
++ vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte;
++ vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes;
++ vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos;
++ if (!p->btMode)
++ {
++ vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches;
++ vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip;
++ }
++ else if (p->numHashBytes == 2)
++ {
++ vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches;
++ vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip;
++ }
++ else if (p->numHashBytes == 3)
++ {
++ vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches;
++ vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip;
++ }
++ else
++ {
++ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches;
++ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip;
++ }
++}
+--- /dev/null
++++ b/lib/lzma/LzmaDec.c
+@@ -0,0 +1,999 @@
++/* LzmaDec.c -- LZMA Decoder
++2009-09-20 : Igor Pavlov : Public domain */
++
++#include "LzmaDec.h"
++
++#include <string.h>
++
++#define kNumTopBits 24
++#define kTopValue ((UInt32)1 << kNumTopBits)
++
++#define kNumBitModelTotalBits 11
++#define kBitModelTotal (1 << kNumBitModelTotalBits)
++#define kNumMoveBits 5
++
++#define RC_INIT_SIZE 5
++
++#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); }
++
++#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
++#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits));
++#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits));
++#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \
++ { UPDATE_0(p); i = (i + i); A0; } else \
++ { UPDATE_1(p); i = (i + i) + 1; A1; }
++#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;)
++
++#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); }
++#define TREE_DECODE(probs, limit, i) \
++ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; }
++
++/* #define _LZMA_SIZE_OPT */
++
++#ifdef _LZMA_SIZE_OPT
++#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i)
++#else
++#define TREE_6_DECODE(probs, i) \
++ { i = 1; \
++ TREE_GET_BIT(probs, i); \
++ TREE_GET_BIT(probs, i); \
++ TREE_GET_BIT(probs, i); \
++ TREE_GET_BIT(probs, i); \
++ TREE_GET_BIT(probs, i); \
++ TREE_GET_BIT(probs, i); \
++ i -= 0x40; }
++#endif
++
++#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); }
++
++#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
++#define UPDATE_0_CHECK range = bound;
++#define UPDATE_1_CHECK range -= bound; code -= bound;
++#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \
++ { UPDATE_0_CHECK; i = (i + i); A0; } else \
++ { UPDATE_1_CHECK; i = (i + i) + 1; A1; }
++#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;)
++#define TREE_DECODE_CHECK(probs, limit, i) \
++ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; }
++
++
++#define kNumPosBitsMax 4
++#define kNumPosStatesMax (1 << kNumPosBitsMax)
++
++#define kLenNumLowBits 3
++#define kLenNumLowSymbols (1 << kLenNumLowBits)
++#define kLenNumMidBits 3
++#define kLenNumMidSymbols (1 << kLenNumMidBits)
++#define kLenNumHighBits 8
++#define kLenNumHighSymbols (1 << kLenNumHighBits)
++
++#define LenChoice 0
++#define LenChoice2 (LenChoice + 1)
++#define LenLow (LenChoice2 + 1)
++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
++#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
++
++
++#define kNumStates 12
++#define kNumLitStates 7
++
++#define kStartPosModelIndex 4
++#define kEndPosModelIndex 14
++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
++
++#define kNumPosSlotBits 6
++#define kNumLenToPosStates 4
++
++#define kNumAlignBits 4
++#define kAlignTableSize (1 << kNumAlignBits)
++
++#define kMatchMinLen 2
++#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols)
++
++#define IsMatch 0
++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
++#define IsRepG0 (IsRep + kNumStates)
++#define IsRepG1 (IsRepG0 + kNumStates)
++#define IsRepG2 (IsRepG1 + kNumStates)
++#define IsRep0Long (IsRepG2 + kNumStates)
++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
++#define LenCoder (Align + kAlignTableSize)
++#define RepLenCoder (LenCoder + kNumLenProbs)
++#define Literal (RepLenCoder + kNumLenProbs)
++
++#define LZMA_BASE_SIZE 1846
++#define LZMA_LIT_SIZE 768
++
++#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp)))
++
++#if Literal != LZMA_BASE_SIZE
++StopCompilingDueBUG
++#endif
++
++#define LZMA_DIC_MIN (1 << 12)
++
++/* First LZMA-symbol is always decoded.
++And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization
++Out:
++ Result:
++ SZ_OK - OK
++ SZ_ERROR_DATA - Error
++ p->remainLen:
++ < kMatchSpecLenStart : normal remain
++ = kMatchSpecLenStart : finished
++ = kMatchSpecLenStart + 1 : Flush marker
++ = kMatchSpecLenStart + 2 : State Init Marker
++*/
++
++static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit)
++{
++ CLzmaProb *probs = p->probs;
++
++ unsigned state = p->state;
++ UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3];
++ unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1;
++ unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1;
++ unsigned lc = p->prop.lc;
++
++ Byte *dic = p->dic;
++ SizeT dicBufSize = p->dicBufSize;
++ SizeT dicPos = p->dicPos;
++
++ UInt32 processedPos = p->processedPos;
++ UInt32 checkDicSize = p->checkDicSize;
++ unsigned len = 0;
++
++ const Byte *buf = p->buf;
++ UInt32 range = p->range;
++ UInt32 code = p->code;
++
++ do
++ {
++ CLzmaProb *prob;
++ UInt32 bound;
++ unsigned ttt;
++ unsigned posState = processedPos & pbMask;
++
++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
++ IF_BIT_0(prob)
++ {
++ unsigned symbol;
++ UPDATE_0(prob);
++ prob = probs + Literal;
++ if (checkDicSize != 0 || processedPos != 0)
++ prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) +
++ (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc))));
++
++ if (state < kNumLitStates)
++ {
++ state -= (state < 4) ? state : 3;
++ symbol = 1;
++ do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100);
++ }
++ else
++ {
++ unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
++ unsigned offs = 0x100;
++ state -= (state < 10) ? 3 : 6;
++ symbol = 1;
++ do
++ {
++ unsigned bit;
++ CLzmaProb *probLit;
++ matchByte <<= 1;
++ bit = (matchByte & offs);
++ probLit = prob + offs + bit + symbol;
++ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit)
++ }
++ while (symbol < 0x100);
++ }
++ dic[dicPos++] = (Byte)symbol;
++ processedPos++;
++ continue;
++ }
++ else
++ {
++ UPDATE_1(prob);
++ prob = probs + IsRep + state;
++ IF_BIT_0(prob)
++ {
++ UPDATE_0(prob);
++ state += kNumStates;
++ prob = probs + LenCoder;
++ }
++ else
++ {
++ UPDATE_1(prob);
++ if (checkDicSize == 0 && processedPos == 0)
++ return SZ_ERROR_DATA;
++ prob = probs + IsRepG0 + state;
++ IF_BIT_0(prob)
++ {
++ UPDATE_0(prob);
++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
++ IF_BIT_0(prob)
++ {
++ UPDATE_0(prob);
++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
++ dicPos++;
++ processedPos++;
++ state = state < kNumLitStates ? 9 : 11;
++ continue;
++ }
++ UPDATE_1(prob);
++ }
++ else
++ {
++ UInt32 distance;
++ UPDATE_1(prob);
++ prob = probs + IsRepG1 + state;
++ IF_BIT_0(prob)
++ {
++ UPDATE_0(prob);
++ distance = rep1;
++ }
++ else
++ {
++ UPDATE_1(prob);
++ prob = probs + IsRepG2 + state;
++ IF_BIT_0(prob)
++ {
++ UPDATE_0(prob);
++ distance = rep2;
++ }
++ else
++ {
++ UPDATE_1(prob);
++ distance = rep3;
++ rep3 = rep2;
++ }
++ rep2 = rep1;
++ }
++ rep1 = rep0;
++ rep0 = distance;
++ }
++ state = state < kNumLitStates ? 8 : 11;
++ prob = probs + RepLenCoder;
++ }
++ {
++ unsigned limit, offset;
++ CLzmaProb *probLen = prob + LenChoice;
++ IF_BIT_0(probLen)
++ {
++ UPDATE_0(probLen);
++ probLen = prob + LenLow + (posState << kLenNumLowBits);
++ offset = 0;
++ limit = (1 << kLenNumLowBits);
++ }
++ else
++ {
++ UPDATE_1(probLen);
++ probLen = prob + LenChoice2;
++ IF_BIT_0(probLen)
++ {
++ UPDATE_0(probLen);
++ probLen = prob + LenMid + (posState << kLenNumMidBits);
++ offset = kLenNumLowSymbols;
++ limit = (1 << kLenNumMidBits);
++ }
++ else
++ {
++ UPDATE_1(probLen);
++ probLen = prob + LenHigh;
++ offset = kLenNumLowSymbols + kLenNumMidSymbols;
++ limit = (1 << kLenNumHighBits);
++ }
++ }
++ TREE_DECODE(probLen, limit, len);
++ len += offset;
++ }
++
++ if (state >= kNumStates)
++ {
++ UInt32 distance;
++ prob = probs + PosSlot +
++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits);
++ TREE_6_DECODE(prob, distance);
++ if (distance >= kStartPosModelIndex)
++ {
++ unsigned posSlot = (unsigned)distance;
++ int numDirectBits = (int)(((distance >> 1) - 1));
++ distance = (2 | (distance & 1));
++ if (posSlot < kEndPosModelIndex)
++ {
++ distance <<= numDirectBits;
++ prob = probs + SpecPos + distance - posSlot - 1;
++ {
++ UInt32 mask = 1;
++ unsigned i = 1;
++ do
++ {
++ GET_BIT2(prob + i, i, ; , distance |= mask);
++ mask <<= 1;
++ }
++ while (--numDirectBits != 0);
++ }
++ }
++ else
++ {
++ numDirectBits -= kNumAlignBits;
++ do
++ {
++ NORMALIZE
++ range >>= 1;
++
++ {
++ UInt32 t;
++ code -= range;
++ t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */
++ distance = (distance << 1) + (t + 1);
++ code += range & t;
++ }
++ /*
++ distance <<= 1;
++ if (code >= range)
++ {
++ code -= range;
++ distance |= 1;
++ }
++ */
++ }
++ while (--numDirectBits != 0);
++ prob = probs + Align;
++ distance <<= kNumAlignBits;
++ {
++ unsigned i = 1;
++ GET_BIT2(prob + i, i, ; , distance |= 1);
++ GET_BIT2(prob + i, i, ; , distance |= 2);
++ GET_BIT2(prob + i, i, ; , distance |= 4);
++ GET_BIT2(prob + i, i, ; , distance |= 8);
++ }
++ if (distance == (UInt32)0xFFFFFFFF)
++ {
++ len += kMatchSpecLenStart;
++ state -= kNumStates;
++ break;
++ }
++ }
++ }
++ rep3 = rep2;
++ rep2 = rep1;
++ rep1 = rep0;
++ rep0 = distance + 1;
++ if (checkDicSize == 0)
++ {
++ if (distance >= processedPos)
++ return SZ_ERROR_DATA;
++ }
++ else if (distance >= checkDicSize)
++ return SZ_ERROR_DATA;
++ state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3;
++ }
++
++ len += kMatchMinLen;
++
++ if (limit == dicPos)
++ return SZ_ERROR_DATA;
++ {
++ SizeT rem = limit - dicPos;
++ unsigned curLen = ((rem < len) ? (unsigned)rem : len);
++ SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0);
++
++ processedPos += curLen;
++
++ len -= curLen;
++ if (pos + curLen <= dicBufSize)
++ {
++ Byte *dest = dic + dicPos;
++ ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos;
++ const Byte *lim = dest + curLen;
++ dicPos += curLen;
++ do
++ *(dest) = (Byte)*(dest + src);
++ while (++dest != lim);
++ }
++ else
++ {
++ do
++ {
++ dic[dicPos++] = dic[pos];
++ if (++pos == dicBufSize)
++ pos = 0;
++ }
++ while (--curLen != 0);
++ }
++ }
++ }
++ }
++ while (dicPos < limit && buf < bufLimit);
++ NORMALIZE;
++ p->buf = buf;
++ p->range = range;
++ p->code = code;
++ p->remainLen = len;
++ p->dicPos = dicPos;
++ p->processedPos = processedPos;
++ p->reps[0] = rep0;
++ p->reps[1] = rep1;
++ p->reps[2] = rep2;
++ p->reps[3] = rep3;
++ p->state = state;
++
++ return SZ_OK;
++}
++
++static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit)
++{
++ if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart)
++ {
++ Byte *dic = p->dic;
++ SizeT dicPos = p->dicPos;
++ SizeT dicBufSize = p->dicBufSize;
++ unsigned len = p->remainLen;
++ UInt32 rep0 = p->reps[0];
++ if (limit - dicPos < len)
++ len = (unsigned)(limit - dicPos);
++
++ if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len)
++ p->checkDicSize = p->prop.dicSize;
++
++ p->processedPos += len;
++ p->remainLen -= len;
++ while (len-- != 0)
++ {
++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
++ dicPos++;
++ }
++ p->dicPos = dicPos;
++ }
++}
++
++static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit)
++{
++ do
++ {
++ SizeT limit2 = limit;
++ if (p->checkDicSize == 0)
++ {
++ UInt32 rem = p->prop.dicSize - p->processedPos;
++ if (limit - p->dicPos > rem)
++ limit2 = p->dicPos + rem;
++ }
++ RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit));
++ if (p->processedPos >= p->prop.dicSize)
++ p->checkDicSize = p->prop.dicSize;
++ LzmaDec_WriteRem(p, limit);
++ }
++ while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart);
++
++ if (p->remainLen > kMatchSpecLenStart)
++ {
++ p->remainLen = kMatchSpecLenStart;
++ }
++ return 0;
++}
++
++typedef enum
++{
++ DUMMY_ERROR, /* unexpected end of input stream */
++ DUMMY_LIT,
++ DUMMY_MATCH,
++ DUMMY_REP
++} ELzmaDummy;
++
++static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize)
++{
++ UInt32 range = p->range;
++ UInt32 code = p->code;
++ const Byte *bufLimit = buf + inSize;
++ CLzmaProb *probs = p->probs;
++ unsigned state = p->state;
++ ELzmaDummy res;
++
++ {
++ CLzmaProb *prob;
++ UInt32 bound;
++ unsigned ttt;
++ unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1);
++
++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK
++
++ /* if (bufLimit - buf >= 7) return DUMMY_LIT; */
++
++ prob = probs + Literal;
++ if (p->checkDicSize != 0 || p->processedPos != 0)
++ prob += (LZMA_LIT_SIZE *
++ ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) +
++ (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc))));
++
++ if (state < kNumLitStates)
++ {
++ unsigned symbol = 1;
++ do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100);
++ }
++ else
++ {
++ unsigned matchByte = p->dic[p->dicPos - p->reps[0] +
++ ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)];
++ unsigned offs = 0x100;
++ unsigned symbol = 1;
++ do
++ {
++ unsigned bit;
++ CLzmaProb *probLit;
++ matchByte <<= 1;
++ bit = (matchByte & offs);
++ probLit = prob + offs + bit + symbol;
++ GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit)
++ }
++ while (symbol < 0x100);
++ }
++ res = DUMMY_LIT;
++ }
++ else
++ {
++ unsigned len;
++ UPDATE_1_CHECK;
++
++ prob = probs + IsRep + state;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK;
++ state = 0;
++ prob = probs + LenCoder;
++ res = DUMMY_MATCH;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ res = DUMMY_REP;
++ prob = probs + IsRepG0 + state;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK;
++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK;
++ NORMALIZE_CHECK;
++ return DUMMY_REP;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ }
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ prob = probs + IsRepG1 + state;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ prob = probs + IsRepG2 + state;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ }
++ }
++ }
++ state = kNumStates;
++ prob = probs + RepLenCoder;
++ }
++ {
++ unsigned limit, offset;
++ CLzmaProb *probLen = prob + LenChoice;
++ IF_BIT_0_CHECK(probLen)
++ {
++ UPDATE_0_CHECK;
++ probLen = prob + LenLow + (posState << kLenNumLowBits);
++ offset = 0;
++ limit = 1 << kLenNumLowBits;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ probLen = prob + LenChoice2;
++ IF_BIT_0_CHECK(probLen)
++ {
++ UPDATE_0_CHECK;
++ probLen = prob + LenMid + (posState << kLenNumMidBits);
++ offset = kLenNumLowSymbols;
++ limit = 1 << kLenNumMidBits;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ probLen = prob + LenHigh;
++ offset = kLenNumLowSymbols + kLenNumMidSymbols;
++ limit = 1 << kLenNumHighBits;
++ }
++ }
++ TREE_DECODE_CHECK(probLen, limit, len);
++ len += offset;
++ }
++
++ if (state < 4)
++ {
++ unsigned posSlot;
++ prob = probs + PosSlot +
++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
++ kNumPosSlotBits);
++ TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot);
++ if (posSlot >= kStartPosModelIndex)
++ {
++ int numDirectBits = ((posSlot >> 1) - 1);
++
++ /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */
++
++ if (posSlot < kEndPosModelIndex)
++ {
++ prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1;
++ }
++ else
++ {
++ numDirectBits -= kNumAlignBits;
++ do
++ {
++ NORMALIZE_CHECK
++ range >>= 1;
++ code -= range & (((code - range) >> 31) - 1);
++ /* if (code >= range) code -= range; */
++ }
++ while (--numDirectBits != 0);
++ prob = probs + Align;
++ numDirectBits = kNumAlignBits;
++ }
++ {
++ unsigned i = 1;
++ do
++ {
++ GET_BIT_CHECK(prob + i, i);
++ }
++ while (--numDirectBits != 0);
++ }
++ }
++ }
++ }
++ }
++ NORMALIZE_CHECK;
++ return res;
++}
++
++
++static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data)
++{
++ p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]);
++ p->range = 0xFFFFFFFF;
++ p->needFlush = 0;
++}
++
++void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
++{
++ p->needFlush = 1;
++ p->remainLen = 0;
++ p->tempBufSize = 0;
++
++ if (initDic)
++ {
++ p->processedPos = 0;
++ p->checkDicSize = 0;
++ p->needInitState = 1;
++ }
++ if (initState)
++ p->needInitState = 1;
++}
++
++void LzmaDec_Init(CLzmaDec *p)
++{
++ p->dicPos = 0;
++ LzmaDec_InitDicAndState(p, True, True);
++}
++
++static void LzmaDec_InitStateReal(CLzmaDec *p)
++{
++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp));
++ UInt32 i;
++ CLzmaProb *probs = p->probs;
++ for (i = 0; i < numProbs; i++)
++ probs[i] = kBitModelTotal >> 1;
++ p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1;
++ p->state = 0;
++ p->needInitState = 0;
++}
++
++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
++ ELzmaFinishMode finishMode, ELzmaStatus *status)
++{
++ SizeT inSize = *srcLen;
++ (*srcLen) = 0;
++ LzmaDec_WriteRem(p, dicLimit);
++
++ *status = LZMA_STATUS_NOT_SPECIFIED;
++
++ while (p->remainLen != kMatchSpecLenStart)
++ {
++ int checkEndMarkNow;
++
++ if (p->needFlush != 0)
++ {
++ for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--)
++ p->tempBuf[p->tempBufSize++] = *src++;
++ if (p->tempBufSize < RC_INIT_SIZE)
++ {
++ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
++ return SZ_OK;
++ }
++ if (p->tempBuf[0] != 0)
++ return SZ_ERROR_DATA;
++
++ LzmaDec_InitRc(p, p->tempBuf);
++ p->tempBufSize = 0;
++ }
++
++ checkEndMarkNow = 0;
++ if (p->dicPos >= dicLimit)
++ {
++ if (p->remainLen == 0 && p->code == 0)
++ {
++ *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK;
++ return SZ_OK;
++ }
++ if (finishMode == LZMA_FINISH_ANY)
++ {
++ *status = LZMA_STATUS_NOT_FINISHED;
++ return SZ_OK;
++ }
++ if (p->remainLen != 0)
++ {
++ *status = LZMA_STATUS_NOT_FINISHED;
++ return SZ_ERROR_DATA;
++ }
++ checkEndMarkNow = 1;
++ }
++
++ if (p->needInitState)
++ LzmaDec_InitStateReal(p);
++
++ if (p->tempBufSize == 0)
++ {
++ SizeT processed;
++ const Byte *bufLimit;
++ if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
++ {
++ int dummyRes = LzmaDec_TryDummy(p, src, inSize);
++ if (dummyRes == DUMMY_ERROR)
++ {
++ memcpy(p->tempBuf, src, inSize);
++ p->tempBufSize = (unsigned)inSize;
++ (*srcLen) += inSize;
++ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
++ return SZ_OK;
++ }
++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
++ {
++ *status = LZMA_STATUS_NOT_FINISHED;
++ return SZ_ERROR_DATA;
++ }
++ bufLimit = src;
++ }
++ else
++ bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX;
++ p->buf = src;
++ if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0)
++ return SZ_ERROR_DATA;
++ processed = (SizeT)(p->buf - src);
++ (*srcLen) += processed;
++ src += processed;
++ inSize -= processed;
++ }
++ else
++ {
++ unsigned rem = p->tempBufSize, lookAhead = 0;
++ while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize)
++ p->tempBuf[rem++] = src[lookAhead++];
++ p->tempBufSize = rem;
++ if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
++ {
++ int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem);
++ if (dummyRes == DUMMY_ERROR)
++ {
++ (*srcLen) += lookAhead;
++ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
++ return SZ_OK;
++ }
++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
++ {
++ *status = LZMA_STATUS_NOT_FINISHED;
++ return SZ_ERROR_DATA;
++ }
++ }
++ p->buf = p->tempBuf;
++ if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0)
++ return SZ_ERROR_DATA;
++ lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf));
++ (*srcLen) += lookAhead;
++ src += lookAhead;
++ inSize -= lookAhead;
++ p->tempBufSize = 0;
++ }
++ }
++ if (p->code == 0)
++ *status = LZMA_STATUS_FINISHED_WITH_MARK;
++ return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA;
++}
++
++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
++{
++ SizeT outSize = *destLen;
++ SizeT inSize = *srcLen;
++ *srcLen = *destLen = 0;
++ for (;;)
++ {
++ SizeT inSizeCur = inSize, outSizeCur, dicPos;
++ ELzmaFinishMode curFinishMode;
++ SRes res;
++ if (p->dicPos == p->dicBufSize)
++ p->dicPos = 0;
++ dicPos = p->dicPos;
++ if (outSize > p->dicBufSize - dicPos)
++ {
++ outSizeCur = p->dicBufSize;
++ curFinishMode = LZMA_FINISH_ANY;
++ }
++ else
++ {
++ outSizeCur = dicPos + outSize;
++ curFinishMode = finishMode;
++ }
++
++ res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status);
++ src += inSizeCur;
++ inSize -= inSizeCur;
++ *srcLen += inSizeCur;
++ outSizeCur = p->dicPos - dicPos;
++ memcpy(dest, p->dic + dicPos, outSizeCur);
++ dest += outSizeCur;
++ outSize -= outSizeCur;
++ *destLen += outSizeCur;
++ if (res != 0)
++ return res;
++ if (outSizeCur == 0 || outSize == 0)
++ return SZ_OK;
++ }
++}
++
++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
++{
++ alloc->Free(alloc, p->probs);
++ p->probs = 0;
++}
++
++static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc)
++{
++ alloc->Free(alloc, p->dic);
++ p->dic = 0;
++}
++
++void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc)
++{
++ LzmaDec_FreeProbs(p, alloc);
++ LzmaDec_FreeDict(p, alloc);
++}
++
++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
++{
++ UInt32 dicSize;
++ Byte d;
++
++ if (size < LZMA_PROPS_SIZE)
++ return SZ_ERROR_UNSUPPORTED;
++ else
++ dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24);
++
++ if (dicSize < LZMA_DIC_MIN)
++ dicSize = LZMA_DIC_MIN;
++ p->dicSize = dicSize;
++
++ d = data[0];
++ if (d >= (9 * 5 * 5))
++ return SZ_ERROR_UNSUPPORTED;
++
++ p->lc = d % 9;
++ d /= 9;
++ p->pb = d / 5;
++ p->lp = d % 5;
++
++ return SZ_OK;
++}
++
++static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc)
++{
++ UInt32 numProbs = LzmaProps_GetNumProbs(propNew);
++ if (p->probs == 0 || numProbs != p->numProbs)
++ {
++ LzmaDec_FreeProbs(p, alloc);
++ p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb));
++ p->numProbs = numProbs;
++ if (p->probs == 0)
++ return SZ_ERROR_MEM;
++ }
++ return SZ_OK;
++}
++
++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
++{
++ CLzmaProps propNew;
++ RINOK(LzmaProps_Decode(&propNew, props, propsSize));
++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
++ p->prop = propNew;
++ return SZ_OK;
++}
++
++SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
++{
++ CLzmaProps propNew;
++ SizeT dicBufSize;
++ RINOK(LzmaProps_Decode(&propNew, props, propsSize));
++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
++ dicBufSize = propNew.dicSize;
++ if (p->dic == 0 || dicBufSize != p->dicBufSize)
++ {
++ LzmaDec_FreeDict(p, alloc);
++ p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize);
++ if (p->dic == 0)
++ {
++ LzmaDec_FreeProbs(p, alloc);
++ return SZ_ERROR_MEM;
++ }
++ }
++ p->dicBufSize = dicBufSize;
++ p->prop = propNew;
++ return SZ_OK;
++}
++
++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
++ ELzmaStatus *status, ISzAlloc *alloc)
++{
++ CLzmaDec p;
++ SRes res;
++ SizeT inSize = *srcLen;
++ SizeT outSize = *destLen;
++ *srcLen = *destLen = 0;
++ if (inSize < RC_INIT_SIZE)
++ return SZ_ERROR_INPUT_EOF;
++
++ LzmaDec_Construct(&p);
++ res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc);
++ if (res != 0)
++ return res;
++ p.dic = dest;
++ p.dicBufSize = outSize;
++
++ LzmaDec_Init(&p);
++
++ *srcLen = inSize;
++ res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status);
++
++ if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT)
++ res = SZ_ERROR_INPUT_EOF;
++
++ (*destLen) = p.dicPos;
++ LzmaDec_FreeProbs(&p, alloc);
++ return res;
++}
+--- /dev/null
++++ b/lib/lzma/LzmaEnc.c
+@@ -0,0 +1,2271 @@
++/* LzmaEnc.c -- LZMA Encoder
++2009-11-24 : Igor Pavlov : Public domain */
++
++#include <string.h>
++
++/* #define SHOW_STAT */
++/* #define SHOW_STAT2 */
++
++#if defined(SHOW_STAT) || defined(SHOW_STAT2)
++#include <stdio.h>
++#endif
++
++#include "LzmaEnc.h"
++
++/* disable MT */
++#define _7ZIP_ST
++
++#include "LzFind.h"
++#ifndef _7ZIP_ST
++#include "LzFindMt.h"
++#endif
++
++#ifdef SHOW_STAT
++static int ttt = 0;
++#endif
++
++#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1)
++
++#define kBlockSize (9 << 10)
++#define kUnpackBlockSize (1 << 18)
++#define kMatchArraySize (1 << 21)
++#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX)
++
++#define kNumMaxDirectBits (31)
++
++#define kNumTopBits 24
++#define kTopValue ((UInt32)1 << kNumTopBits)
++
++#define kNumBitModelTotalBits 11
++#define kBitModelTotal (1 << kNumBitModelTotalBits)
++#define kNumMoveBits 5
++#define kProbInitValue (kBitModelTotal >> 1)
++
++#define kNumMoveReducingBits 4
++#define kNumBitPriceShiftBits 4
++#define kBitPrice (1 << kNumBitPriceShiftBits)
++
++void LzmaEncProps_Init(CLzmaEncProps *p)
++{
++ p->level = 5;
++ p->dictSize = p->mc = 0;
++ p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1;
++ p->writeEndMark = 0;
++}
++
++void LzmaEncProps_Normalize(CLzmaEncProps *p)
++{
++ int level = p->level;
++ if (level < 0) level = 5;
++ p->level = level;
++ if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26)));
++ if (p->lc < 0) p->lc = 3;
++ if (p->lp < 0) p->lp = 0;
++ if (p->pb < 0) p->pb = 2;
++ if (p->algo < 0) p->algo = (level < 5 ? 0 : 1);
++ if (p->fb < 0) p->fb = (level < 7 ? 32 : 64);
++ if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1);
++ if (p->numHashBytes < 0) p->numHashBytes = 4;
++ if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1);
++ if (p->numThreads < 0)
++ p->numThreads =
++ #ifndef _7ZIP_ST
++ ((p->btMode && p->algo) ? 2 : 1);
++ #else
++ 1;
++ #endif
++}
++
++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2)
++{
++ CLzmaEncProps props = *props2;
++ LzmaEncProps_Normalize(&props);
++ return props.dictSize;
++}
++
++/* #define LZMA_LOG_BSR */
++/* Define it for Intel's CPU */
++
++
++#ifdef LZMA_LOG_BSR
++
++#define kDicLogSizeMaxCompress 30
++
++#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); }
++
++UInt32 GetPosSlot1(UInt32 pos)
++{
++ UInt32 res;
++ BSR2_RET(pos, res);
++ return res;
++}
++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); }
++#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); }
++
++#else
++
++#define kNumLogBits (9 + (int)sizeof(size_t) / 2)
++#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7)
++
++void LzmaEnc_FastPosInit(Byte *g_FastPos)
++{
++ int c = 2, slotFast;
++ g_FastPos[0] = 0;
++ g_FastPos[1] = 1;
++
++ for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++)
++ {
++ UInt32 k = (1 << ((slotFast >> 1) - 1));
++ UInt32 j;
++ for (j = 0; j < k; j++, c++)
++ g_FastPos[c] = (Byte)slotFast;
++ }
++}
++
++#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \
++ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \
++ res = p->g_FastPos[pos >> i] + (i * 2); }
++/*
++#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \
++ p->g_FastPos[pos >> 6] + 12 : \
++ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; }
++*/
++
++#define GetPosSlot1(pos) p->g_FastPos[pos]
++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); }
++#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); }
++
++#endif
++
++
++#define LZMA_NUM_REPS 4
++
++typedef unsigned CState;
++
++typedef struct
++{
++ UInt32 price;
++
++ CState state;
++ int prev1IsChar;
++ int prev2;
++
++ UInt32 posPrev2;
++ UInt32 backPrev2;
++
++ UInt32 posPrev;
++ UInt32 backPrev;
++ UInt32 backs[LZMA_NUM_REPS];
++} COptimal;
++
++#define kNumOpts (1 << 12)
++
++#define kNumLenToPosStates 4
++#define kNumPosSlotBits 6
++#define kDicLogSizeMin 0
++#define kDicLogSizeMax 32
++#define kDistTableSizeMax (kDicLogSizeMax * 2)
++
++
++#define kNumAlignBits 4
++#define kAlignTableSize (1 << kNumAlignBits)
++#define kAlignMask (kAlignTableSize - 1)
++
++#define kStartPosModelIndex 4
++#define kEndPosModelIndex 14
++#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex)
++
++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
++
++#ifdef _LZMA_PROB32
++#define CLzmaProb UInt32
++#else
++#define CLzmaProb UInt16
++#endif
++
++#define LZMA_PB_MAX 4
++#define LZMA_LC_MAX 8
++#define LZMA_LP_MAX 4
++
++#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX)
++
++
++#define kLenNumLowBits 3
++#define kLenNumLowSymbols (1 << kLenNumLowBits)
++#define kLenNumMidBits 3
++#define kLenNumMidSymbols (1 << kLenNumMidBits)
++#define kLenNumHighBits 8
++#define kLenNumHighSymbols (1 << kLenNumHighBits)
++
++#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols)
++
++#define LZMA_MATCH_LEN_MIN 2
++#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1)
++
++#define kNumStates 12
++
++typedef struct
++{
++ CLzmaProb choice;
++ CLzmaProb choice2;
++ CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits];
++ CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits];
++ CLzmaProb high[kLenNumHighSymbols];
++} CLenEnc;
++
++typedef struct
++{
++ CLenEnc p;
++ UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal];
++ UInt32 tableSize;
++ UInt32 counters[LZMA_NUM_PB_STATES_MAX];
++} CLenPriceEnc;
++
++typedef struct
++{
++ UInt32 range;
++ Byte cache;
++ UInt64 low;
++ UInt64 cacheSize;
++ Byte *buf;
++ Byte *bufLim;
++ Byte *bufBase;
++ ISeqOutStream *outStream;
++ UInt64 processed;
++ SRes res;
++} CRangeEnc;
++
++typedef struct
++{
++ CLzmaProb *litProbs;
++
++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX];
++ CLzmaProb isRep[kNumStates];
++ CLzmaProb isRepG0[kNumStates];
++ CLzmaProb isRepG1[kNumStates];
++ CLzmaProb isRepG2[kNumStates];
++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX];
++
++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits];
++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex];
++ CLzmaProb posAlignEncoder[1 << kNumAlignBits];
++
++ CLenPriceEnc lenEnc;
++ CLenPriceEnc repLenEnc;
++
++ UInt32 reps[LZMA_NUM_REPS];
++ UInt32 state;
++} CSaveState;
++
++typedef struct
++{
++ IMatchFinder matchFinder;
++ void *matchFinderObj;
++
++ #ifndef _7ZIP_ST
++ Bool mtMode;
++ CMatchFinderMt matchFinderMt;
++ #endif
++
++ CMatchFinder matchFinderBase;
++
++ #ifndef _7ZIP_ST
++ Byte pad[128];
++ #endif
++
++ UInt32 optimumEndIndex;
++ UInt32 optimumCurrentIndex;
++
++ UInt32 longestMatchLength;
++ UInt32 numPairs;
++ UInt32 numAvail;
++ COptimal opt[kNumOpts];
++
++ #ifndef LZMA_LOG_BSR
++ Byte g_FastPos[1 << kNumLogBits];
++ #endif
++
++ UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits];
++ UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1];
++ UInt32 numFastBytes;
++ UInt32 additionalOffset;
++ UInt32 reps[LZMA_NUM_REPS];
++ UInt32 state;
++
++ UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax];
++ UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances];
++ UInt32 alignPrices[kAlignTableSize];
++ UInt32 alignPriceCount;
++
++ UInt32 distTableSize;
++
++ unsigned lc, lp, pb;
++ unsigned lpMask, pbMask;
++
++ CLzmaProb *litProbs;
++
++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX];
++ CLzmaProb isRep[kNumStates];
++ CLzmaProb isRepG0[kNumStates];
++ CLzmaProb isRepG1[kNumStates];
++ CLzmaProb isRepG2[kNumStates];
++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX];
++
++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits];
++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex];
++ CLzmaProb posAlignEncoder[1 << kNumAlignBits];
++
++ CLenPriceEnc lenEnc;
++ CLenPriceEnc repLenEnc;
++
++ unsigned lclp;
++
++ Bool fastMode;
++
++ CRangeEnc rc;
++
++ Bool writeEndMark;
++ UInt64 nowPos64;
++ UInt32 matchPriceCount;
++ Bool finished;
++ Bool multiThread;
++
++ SRes result;
++ UInt32 dictSize;
++ UInt32 matchFinderCycles;
++
++ int needInit;
++
++ CSaveState saveState;
++} CLzmaEnc;
++
++void LzmaEnc_SaveState(CLzmaEncHandle pp)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ CSaveState *dest = &p->saveState;
++ int i;
++ dest->lenEnc = p->lenEnc;
++ dest->repLenEnc = p->repLenEnc;
++ dest->state = p->state;
++
++ for (i = 0; i < kNumStates; i++)
++ {
++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
++ }
++ for (i = 0; i < kNumLenToPosStates; i++)
++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
++ memcpy(dest->reps, p->reps, sizeof(p->reps));
++ memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb));
++}
++
++void LzmaEnc_RestoreState(CLzmaEncHandle pp)
++{
++ CLzmaEnc *dest = (CLzmaEnc *)pp;
++ const CSaveState *p = &dest->saveState;
++ int i;
++ dest->lenEnc = p->lenEnc;
++ dest->repLenEnc = p->repLenEnc;
++ dest->state = p->state;
++
++ for (i = 0; i < kNumStates; i++)
++ {
++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
++ }
++ for (i = 0; i < kNumLenToPosStates; i++)
++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
++ memcpy(dest->reps, p->reps, sizeof(p->reps));
++ memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb));
++}
++
++SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ CLzmaEncProps props = *props2;
++ LzmaEncProps_Normalize(&props);
++
++ if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX ||
++ props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30))
++ return SZ_ERROR_PARAM;
++ p->dictSize = props.dictSize;
++ p->matchFinderCycles = props.mc;
++ {
++ unsigned fb = props.fb;
++ if (fb < 5)
++ fb = 5;
++ if (fb > LZMA_MATCH_LEN_MAX)
++ fb = LZMA_MATCH_LEN_MAX;
++ p->numFastBytes = fb;
++ }
++ p->lc = props.lc;
++ p->lp = props.lp;
++ p->pb = props.pb;
++ p->fastMode = (props.algo == 0);
++ p->matchFinderBase.btMode = props.btMode;
++ {
++ UInt32 numHashBytes = 4;
++ if (props.btMode)
++ {
++ if (props.numHashBytes < 2)
++ numHashBytes = 2;
++ else if (props.numHashBytes < 4)
++ numHashBytes = props.numHashBytes;
++ }
++ p->matchFinderBase.numHashBytes = numHashBytes;
++ }
++
++ p->matchFinderBase.cutValue = props.mc;
++
++ p->writeEndMark = props.writeEndMark;
++
++ #ifndef _7ZIP_ST
++ /*
++ if (newMultiThread != _multiThread)
++ {
++ ReleaseMatchFinder();
++ _multiThread = newMultiThread;
++ }
++ */
++ p->multiThread = (props.numThreads > 1);
++ #endif
++
++ return SZ_OK;
++}
++
++static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5};
++static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10};
++static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11};
++static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11};
++
++#define IsCharState(s) ((s) < 7)
++
++#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1)
++
++#define kInfinityPrice (1 << 30)
++
++static void RangeEnc_Construct(CRangeEnc *p)
++{
++ p->outStream = 0;
++ p->bufBase = 0;
++}
++
++#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize)
++
++#define RC_BUF_SIZE (1 << 16)
++static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc)
++{
++ if (p->bufBase == 0)
++ {
++ p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE);
++ if (p->bufBase == 0)
++ return 0;
++ p->bufLim = p->bufBase + RC_BUF_SIZE;
++ }
++ return 1;
++}
++
++static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc)
++{
++ alloc->Free(alloc, p->bufBase);
++ p->bufBase = 0;
++}
++
++static void RangeEnc_Init(CRangeEnc *p)
++{
++ /* Stream.Init(); */
++ p->low = 0;
++ p->range = 0xFFFFFFFF;
++ p->cacheSize = 1;
++ p->cache = 0;
++
++ p->buf = p->bufBase;
++
++ p->processed = 0;
++ p->res = SZ_OK;
++}
++
++static void RangeEnc_FlushStream(CRangeEnc *p)
++{
++ size_t num;
++ if (p->res != SZ_OK)
++ return;
++ num = p->buf - p->bufBase;
++ if (num != p->outStream->Write(p->outStream, p->bufBase, num))
++ p->res = SZ_ERROR_WRITE;
++ p->processed += num;
++ p->buf = p->bufBase;
++}
++
++static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p)
++{
++ if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0)
++ {
++ Byte temp = p->cache;
++ do
++ {
++ Byte *buf = p->buf;
++ *buf++ = (Byte)(temp + (Byte)(p->low >> 32));
++ p->buf = buf;
++ if (buf == p->bufLim)
++ RangeEnc_FlushStream(p);
++ temp = 0xFF;
++ }
++ while (--p->cacheSize != 0);
++ p->cache = (Byte)((UInt32)p->low >> 24);
++ }
++ p->cacheSize++;
++ p->low = (UInt32)p->low << 8;
++}
++
++static void RangeEnc_FlushData(CRangeEnc *p)
++{
++ int i;
++ for (i = 0; i < 5; i++)
++ RangeEnc_ShiftLow(p);
++}
++
++static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits)
++{
++ do
++ {
++ p->range >>= 1;
++ p->low += p->range & (0 - ((value >> --numBits) & 1));
++ if (p->range < kTopValue)
++ {
++ p->range <<= 8;
++ RangeEnc_ShiftLow(p);
++ }
++ }
++ while (numBits != 0);
++}
++
++static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol)
++{
++ UInt32 ttt = *prob;
++ UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt;
++ if (symbol == 0)
++ {
++ p->range = newBound;
++ ttt += (kBitModelTotal - ttt) >> kNumMoveBits;
++ }
++ else
++ {
++ p->low += newBound;
++ p->range -= newBound;
++ ttt -= ttt >> kNumMoveBits;
++ }
++ *prob = (CLzmaProb)ttt;
++ if (p->range < kTopValue)
++ {
++ p->range <<= 8;
++ RangeEnc_ShiftLow(p);
++ }
++}
++
++static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol)
++{
++ symbol |= 0x100;
++ do
++ {
++ RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1);
++ symbol <<= 1;
++ }
++ while (symbol < 0x10000);
++}
++
++static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte)
++{
++ UInt32 offs = 0x100;
++ symbol |= 0x100;
++ do
++ {
++ matchByte <<= 1;
++ RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1);
++ symbol <<= 1;
++ offs &= ~(matchByte ^ symbol);
++ }
++ while (symbol < 0x10000);
++}
++
++void LzmaEnc_InitPriceTables(UInt32 *ProbPrices)
++{
++ UInt32 i;
++ for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits))
++ {
++ const int kCyclesBits = kNumBitPriceShiftBits;
++ UInt32 w = i;
++ UInt32 bitCount = 0;
++ int j;
++ for (j = 0; j < kCyclesBits; j++)
++ {
++ w = w * w;
++ bitCount <<= 1;
++ while (w >= ((UInt32)1 << 16))
++ {
++ w >>= 1;
++ bitCount++;
++ }
++ }
++ ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount);
++ }
++}
++
++
++#define GET_PRICE(prob, symbol) \
++ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits];
++
++#define GET_PRICEa(prob, symbol) \
++ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits];
++
++#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits]
++#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits]
++
++#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits]
++#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits]
++
++static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices)
++{
++ UInt32 price = 0;
++ symbol |= 0x100;
++ do
++ {
++ price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1);
++ symbol <<= 1;
++ }
++ while (symbol < 0x10000);
++ return price;
++}
++
++static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices)
++{
++ UInt32 price = 0;
++ UInt32 offs = 0x100;
++ symbol |= 0x100;
++ do
++ {
++ matchByte <<= 1;
++ price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1);
++ symbol <<= 1;
++ offs &= ~(matchByte ^ symbol);
++ }
++ while (symbol < 0x10000);
++ return price;
++}
++
++
++static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol)
++{
++ UInt32 m = 1;
++ int i;
++ for (i = numBitLevels; i != 0;)
++ {
++ UInt32 bit;
++ i--;
++ bit = (symbol >> i) & 1;
++ RangeEnc_EncodeBit(rc, probs + m, bit);
++ m = (m << 1) | bit;
++ }
++}
++
++static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol)
++{
++ UInt32 m = 1;
++ int i;
++ for (i = 0; i < numBitLevels; i++)
++ {
++ UInt32 bit = symbol & 1;
++ RangeEnc_EncodeBit(rc, probs + m, bit);
++ m = (m << 1) | bit;
++ symbol >>= 1;
++ }
++}
++
++static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices)
++{
++ UInt32 price = 0;
++ symbol |= (1 << numBitLevels);
++ while (symbol != 1)
++ {
++ price += GET_PRICEa(probs[symbol >> 1], symbol & 1);
++ symbol >>= 1;
++ }
++ return price;
++}
++
++static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices)
++{
++ UInt32 price = 0;
++ UInt32 m = 1;
++ int i;
++ for (i = numBitLevels; i != 0; i--)
++ {
++ UInt32 bit = symbol & 1;
++ symbol >>= 1;
++ price += GET_PRICEa(probs[m], bit);
++ m = (m << 1) | bit;
++ }
++ return price;
++}
++
++
++static void LenEnc_Init(CLenEnc *p)
++{
++ unsigned i;
++ p->choice = p->choice2 = kProbInitValue;
++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++)
++ p->low[i] = kProbInitValue;
++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++)
++ p->mid[i] = kProbInitValue;
++ for (i = 0; i < kLenNumHighSymbols; i++)
++ p->high[i] = kProbInitValue;
++}
++
++static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState)
++{
++ if (symbol < kLenNumLowSymbols)
++ {
++ RangeEnc_EncodeBit(rc, &p->choice, 0);
++ RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol);
++ }
++ else
++ {
++ RangeEnc_EncodeBit(rc, &p->choice, 1);
++ if (symbol < kLenNumLowSymbols + kLenNumMidSymbols)
++ {
++ RangeEnc_EncodeBit(rc, &p->choice2, 0);
++ RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols);
++ }
++ else
++ {
++ RangeEnc_EncodeBit(rc, &p->choice2, 1);
++ RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols);
++ }
++ }
++}
++
++static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices)
++{
++ UInt32 a0 = GET_PRICE_0a(p->choice);
++ UInt32 a1 = GET_PRICE_1a(p->choice);
++ UInt32 b0 = a1 + GET_PRICE_0a(p->choice2);
++ UInt32 b1 = a1 + GET_PRICE_1a(p->choice2);
++ UInt32 i = 0;
++ for (i = 0; i < kLenNumLowSymbols; i++)
++ {
++ if (i >= numSymbols)
++ return;
++ prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices);
++ }
++ for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++)
++ {
++ if (i >= numSymbols)
++ return;
++ prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices);
++ }
++ for (; i < numSymbols; i++)
++ prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices);
++}
++
++static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices)
++{
++ LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices);
++ p->counters[posState] = p->tableSize;
++}
++
++static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices)
++{
++ UInt32 posState;
++ for (posState = 0; posState < numPosStates; posState++)
++ LenPriceEnc_UpdateTable(p, posState, ProbPrices);
++}
++
++static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices)
++{
++ LenEnc_Encode(&p->p, rc, symbol, posState);
++ if (updatePrice)
++ if (--p->counters[posState] == 0)
++ LenPriceEnc_UpdateTable(p, posState, ProbPrices);
++}
++
++
++
++
++static void MovePos(CLzmaEnc *p, UInt32 num)
++{
++ #ifdef SHOW_STAT
++ ttt += num;
++ printf("\n MovePos %d", num);
++ #endif
++ if (num != 0)
++ {
++ p->additionalOffset += num;
++ p->matchFinder.Skip(p->matchFinderObj, num);
++ }
++}
++
++static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes)
++{
++ UInt32 lenRes = 0, numPairs;
++ p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
++ numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches);
++ #ifdef SHOW_STAT
++ printf("\n i = %d numPairs = %d ", ttt, numPairs / 2);
++ ttt++;
++ {
++ UInt32 i;
++ for (i = 0; i < numPairs; i += 2)
++ printf("%2d %6d | ", p->matches[i], p->matches[i + 1]);
++ }
++ #endif
++ if (numPairs > 0)
++ {
++ lenRes = p->matches[numPairs - 2];
++ if (lenRes == p->numFastBytes)
++ {
++ const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
++ UInt32 distance = p->matches[numPairs - 1] + 1;
++ UInt32 numAvail = p->numAvail;
++ if (numAvail > LZMA_MATCH_LEN_MAX)
++ numAvail = LZMA_MATCH_LEN_MAX;
++ {
++ const Byte *pby2 = pby - distance;
++ for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++);
++ }
++ }
++ }
++ p->additionalOffset++;
++ *numDistancePairsRes = numPairs;
++ return lenRes;
++}
++
++
++#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False;
++#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False;
++#define IsShortRep(p) ((p)->backPrev == 0)
++
++static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState)
++{
++ return
++ GET_PRICE_0(p->isRepG0[state]) +
++ GET_PRICE_0(p->isRep0Long[state][posState]);
++}
++
++static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState)
++{
++ UInt32 price;
++ if (repIndex == 0)
++ {
++ price = GET_PRICE_0(p->isRepG0[state]);
++ price += GET_PRICE_1(p->isRep0Long[state][posState]);
++ }
++ else
++ {
++ price = GET_PRICE_1(p->isRepG0[state]);
++ if (repIndex == 1)
++ price += GET_PRICE_0(p->isRepG1[state]);
++ else
++ {
++ price += GET_PRICE_1(p->isRepG1[state]);
++ price += GET_PRICE(p->isRepG2[state], repIndex - 2);
++ }
++ }
++ return price;
++}
++
++static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState)
++{
++ return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] +
++ GetPureRepPrice(p, repIndex, state, posState);
++}
++
++static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur)
++{
++ UInt32 posMem = p->opt[cur].posPrev;
++ UInt32 backMem = p->opt[cur].backPrev;
++ p->optimumEndIndex = cur;
++ do
++ {
++ if (p->opt[cur].prev1IsChar)
++ {
++ MakeAsChar(&p->opt[posMem])
++ p->opt[posMem].posPrev = posMem - 1;
++ if (p->opt[cur].prev2)
++ {
++ p->opt[posMem - 1].prev1IsChar = False;
++ p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2;
++ p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2;
++ }
++ }
++ {
++ UInt32 posPrev = posMem;
++ UInt32 backCur = backMem;
++
++ backMem = p->opt[posPrev].backPrev;
++ posMem = p->opt[posPrev].posPrev;
++
++ p->opt[posPrev].backPrev = backCur;
++ p->opt[posPrev].posPrev = cur;
++ cur = posPrev;
++ }
++ }
++ while (cur != 0);
++ *backRes = p->opt[0].backPrev;
++ p->optimumCurrentIndex = p->opt[0].posPrev;
++ return p->optimumCurrentIndex;
++}
++
++#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300)
++
++static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes)
++{
++ UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur;
++ UInt32 matchPrice, repMatchPrice, normalMatchPrice;
++ UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS];
++ UInt32 *matches;
++ const Byte *data;
++ Byte curByte, matchByte;
++ if (p->optimumEndIndex != p->optimumCurrentIndex)
++ {
++ const COptimal *opt = &p->opt[p->optimumCurrentIndex];
++ UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex;
++ *backRes = opt->backPrev;
++ p->optimumCurrentIndex = opt->posPrev;
++ return lenRes;
++ }
++ p->optimumCurrentIndex = p->optimumEndIndex = 0;
++
++ if (p->additionalOffset == 0)
++ mainLen = ReadMatchDistances(p, &numPairs);
++ else
++ {
++ mainLen = p->longestMatchLength;
++ numPairs = p->numPairs;
++ }
++
++ numAvail = p->numAvail;
++ if (numAvail < 2)
++ {
++ *backRes = (UInt32)(-1);
++ return 1;
++ }
++ if (numAvail > LZMA_MATCH_LEN_MAX)
++ numAvail = LZMA_MATCH_LEN_MAX;
++
++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
++ repMaxIndex = 0;
++ for (i = 0; i < LZMA_NUM_REPS; i++)
++ {
++ UInt32 lenTest;
++ const Byte *data2;
++ reps[i] = p->reps[i];
++ data2 = data - (reps[i] + 1);
++ if (data[0] != data2[0] || data[1] != data2[1])
++ {
++ repLens[i] = 0;
++ continue;
++ }
++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++);
++ repLens[i] = lenTest;
++ if (lenTest > repLens[repMaxIndex])
++ repMaxIndex = i;
++ }
++ if (repLens[repMaxIndex] >= p->numFastBytes)
++ {
++ UInt32 lenRes;
++ *backRes = repMaxIndex;
++ lenRes = repLens[repMaxIndex];
++ MovePos(p, lenRes - 1);
++ return lenRes;
++ }
++
++ matches = p->matches;
++ if (mainLen >= p->numFastBytes)
++ {
++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS;
++ MovePos(p, mainLen - 1);
++ return mainLen;
++ }
++ curByte = *data;
++ matchByte = *(data - (reps[0] + 1));
++
++ if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2)
++ {
++ *backRes = (UInt32)-1;
++ return 1;
++ }
++
++ p->opt[0].state = (CState)p->state;
++
++ posState = (position & p->pbMask);
++
++ {
++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1));
++ p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) +
++ (!IsCharState(p->state) ?
++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) :
++ LitEnc_GetPrice(probs, curByte, p->ProbPrices));
++ }
++
++ MakeAsChar(&p->opt[1]);
++
++ matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]);
++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]);
++
++ if (matchByte == curByte)
++ {
++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState);
++ if (shortRepPrice < p->opt[1].price)
++ {
++ p->opt[1].price = shortRepPrice;
++ MakeAsShortRep(&p->opt[1]);
++ }
++ }
++ lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]);
++
++ if (lenEnd < 2)
++ {
++ *backRes = p->opt[1].backPrev;
++ return 1;
++ }
++
++ p->opt[1].posPrev = 0;
++ for (i = 0; i < LZMA_NUM_REPS; i++)
++ p->opt[0].backs[i] = reps[i];
++
++ len = lenEnd;
++ do
++ p->opt[len--].price = kInfinityPrice;
++ while (len >= 2);
++
++ for (i = 0; i < LZMA_NUM_REPS; i++)
++ {
++ UInt32 repLen = repLens[i];
++ UInt32 price;
++ if (repLen < 2)
++ continue;
++ price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState);
++ do
++ {
++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2];
++ COptimal *opt = &p->opt[repLen];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = 0;
++ opt->backPrev = i;
++ opt->prev1IsChar = False;
++ }
++ }
++ while (--repLen >= 2);
++ }
++
++ normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]);
++
++ len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2);
++ if (len <= mainLen)
++ {
++ UInt32 offs = 0;
++ while (len > matches[offs])
++ offs += 2;
++ for (; ; len++)
++ {
++ COptimal *opt;
++ UInt32 distance = matches[offs + 1];
++
++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN];
++ UInt32 lenToPosState = GetLenToPosState(len);
++ if (distance < kNumFullDistances)
++ curAndLenPrice += p->distancesPrices[lenToPosState][distance];
++ else
++ {
++ UInt32 slot;
++ GetPosSlot2(distance, slot);
++ curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot];
++ }
++ opt = &p->opt[len];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = 0;
++ opt->backPrev = distance + LZMA_NUM_REPS;
++ opt->prev1IsChar = False;
++ }
++ if (len == matches[offs])
++ {
++ offs += 2;
++ if (offs == numPairs)
++ break;
++ }
++ }
++ }
++
++ cur = 0;
++
++ #ifdef SHOW_STAT2
++ if (position >= 0)
++ {
++ unsigned i;
++ printf("\n pos = %4X", position);
++ for (i = cur; i <= lenEnd; i++)
++ printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price);
++ }
++ #endif
++
++ for (;;)
++ {
++ UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen;
++ UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice;
++ Bool nextIsChar;
++ Byte curByte, matchByte;
++ const Byte *data;
++ COptimal *curOpt;
++ COptimal *nextOpt;
++
++ cur++;
++ if (cur == lenEnd)
++ return Backward(p, backRes, cur);
++
++ newLen = ReadMatchDistances(p, &numPairs);
++ if (newLen >= p->numFastBytes)
++ {
++ p->numPairs = numPairs;
++ p->longestMatchLength = newLen;
++ return Backward(p, backRes, cur);
++ }
++ position++;
++ curOpt = &p->opt[cur];
++ posPrev = curOpt->posPrev;
++ if (curOpt->prev1IsChar)
++ {
++ posPrev--;
++ if (curOpt->prev2)
++ {
++ state = p->opt[curOpt->posPrev2].state;
++ if (curOpt->backPrev2 < LZMA_NUM_REPS)
++ state = kRepNextStates[state];
++ else
++ state = kMatchNextStates[state];
++ }
++ else
++ state = p->opt[posPrev].state;
++ state = kLiteralNextStates[state];
++ }
++ else
++ state = p->opt[posPrev].state;
++ if (posPrev == cur - 1)
++ {
++ if (IsShortRep(curOpt))
++ state = kShortRepNextStates[state];
++ else
++ state = kLiteralNextStates[state];
++ }
++ else
++ {
++ UInt32 pos;
++ const COptimal *prevOpt;
++ if (curOpt->prev1IsChar && curOpt->prev2)
++ {
++ posPrev = curOpt->posPrev2;
++ pos = curOpt->backPrev2;
++ state = kRepNextStates[state];
++ }
++ else
++ {
++ pos = curOpt->backPrev;
++ if (pos < LZMA_NUM_REPS)
++ state = kRepNextStates[state];
++ else
++ state = kMatchNextStates[state];
++ }
++ prevOpt = &p->opt[posPrev];
++ if (pos < LZMA_NUM_REPS)
++ {
++ UInt32 i;
++ reps[0] = prevOpt->backs[pos];
++ for (i = 1; i <= pos; i++)
++ reps[i] = prevOpt->backs[i - 1];
++ for (; i < LZMA_NUM_REPS; i++)
++ reps[i] = prevOpt->backs[i];
++ }
++ else
++ {
++ UInt32 i;
++ reps[0] = (pos - LZMA_NUM_REPS);
++ for (i = 1; i < LZMA_NUM_REPS; i++)
++ reps[i] = prevOpt->backs[i - 1];
++ }
++ }
++ curOpt->state = (CState)state;
++
++ curOpt->backs[0] = reps[0];
++ curOpt->backs[1] = reps[1];
++ curOpt->backs[2] = reps[2];
++ curOpt->backs[3] = reps[3];
++
++ curPrice = curOpt->price;
++ nextIsChar = False;
++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
++ curByte = *data;
++ matchByte = *(data - (reps[0] + 1));
++
++ posState = (position & p->pbMask);
++
++ curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]);
++ {
++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1));
++ curAnd1Price +=
++ (!IsCharState(state) ?
++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) :
++ LitEnc_GetPrice(probs, curByte, p->ProbPrices));
++ }
++
++ nextOpt = &p->opt[cur + 1];
++
++ if (curAnd1Price < nextOpt->price)
++ {
++ nextOpt->price = curAnd1Price;
++ nextOpt->posPrev = cur;
++ MakeAsChar(nextOpt);
++ nextIsChar = True;
++ }
++
++ matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]);
++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]);
++
++ if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0))
++ {
++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState);
++ if (shortRepPrice <= nextOpt->price)
++ {
++ nextOpt->price = shortRepPrice;
++ nextOpt->posPrev = cur;
++ MakeAsShortRep(nextOpt);
++ nextIsChar = True;
++ }
++ }
++ numAvailFull = p->numAvail;
++ {
++ UInt32 temp = kNumOpts - 1 - cur;
++ if (temp < numAvailFull)
++ numAvailFull = temp;
++ }
++
++ if (numAvailFull < 2)
++ continue;
++ numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes);
++
++ if (!nextIsChar && matchByte != curByte) /* speed optimization */
++ {
++ /* try Literal + rep0 */
++ UInt32 temp;
++ UInt32 lenTest2;
++ const Byte *data2 = data - (reps[0] + 1);
++ UInt32 limit = p->numFastBytes + 1;
++ if (limit > numAvailFull)
++ limit = numAvailFull;
++
++ for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++);
++ lenTest2 = temp - 1;
++ if (lenTest2 >= 2)
++ {
++ UInt32 state2 = kLiteralNextStates[state];
++ UInt32 posStateNext = (position + 1) & p->pbMask;
++ UInt32 nextRepMatchPrice = curAnd1Price +
++ GET_PRICE_1(p->isMatch[state2][posStateNext]) +
++ GET_PRICE_1(p->isRep[state2]);
++ /* for (; lenTest2 >= 2; lenTest2--) */
++ {
++ UInt32 curAndLenPrice;
++ COptimal *opt;
++ UInt32 offset = cur + 1 + lenTest2;
++ while (lenEnd < offset)
++ p->opt[++lenEnd].price = kInfinityPrice;
++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
++ opt = &p->opt[offset];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = cur + 1;
++ opt->backPrev = 0;
++ opt->prev1IsChar = True;
++ opt->prev2 = False;
++ }
++ }
++ }
++ }
++
++ startLen = 2; /* speed optimization */
++ {
++ UInt32 repIndex;
++ for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++)
++ {
++ UInt32 lenTest;
++ UInt32 lenTestTemp;
++ UInt32 price;
++ const Byte *data2 = data - (reps[repIndex] + 1);
++ if (data[0] != data2[0] || data[1] != data2[1])
++ continue;
++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++);
++ while (lenEnd < cur + lenTest)
++ p->opt[++lenEnd].price = kInfinityPrice;
++ lenTestTemp = lenTest;
++ price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState);
++ do
++ {
++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2];
++ COptimal *opt = &p->opt[cur + lenTest];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = cur;
++ opt->backPrev = repIndex;
++ opt->prev1IsChar = False;
++ }
++ }
++ while (--lenTest >= 2);
++ lenTest = lenTestTemp;
++
++ if (repIndex == 0)
++ startLen = lenTest + 1;
++
++ /* if (_maxMode) */
++ {
++ UInt32 lenTest2 = lenTest + 1;
++ UInt32 limit = lenTest2 + p->numFastBytes;
++ UInt32 nextRepMatchPrice;
++ if (limit > numAvailFull)
++ limit = numAvailFull;
++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++);
++ lenTest2 -= lenTest + 1;
++ if (lenTest2 >= 2)
++ {
++ UInt32 state2 = kRepNextStates[state];
++ UInt32 posStateNext = (position + lenTest) & p->pbMask;
++ UInt32 curAndLenCharPrice =
++ price + p->repLenEnc.prices[posState][lenTest - 2] +
++ GET_PRICE_0(p->isMatch[state2][posStateNext]) +
++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]),
++ data[lenTest], data2[lenTest], p->ProbPrices);
++ state2 = kLiteralNextStates[state2];
++ posStateNext = (position + lenTest + 1) & p->pbMask;
++ nextRepMatchPrice = curAndLenCharPrice +
++ GET_PRICE_1(p->isMatch[state2][posStateNext]) +
++ GET_PRICE_1(p->isRep[state2]);
++
++ /* for (; lenTest2 >= 2; lenTest2--) */
++ {
++ UInt32 curAndLenPrice;
++ COptimal *opt;
++ UInt32 offset = cur + lenTest + 1 + lenTest2;
++ while (lenEnd < offset)
++ p->opt[++lenEnd].price = kInfinityPrice;
++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
++ opt = &p->opt[offset];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = cur + lenTest + 1;
++ opt->backPrev = 0;
++ opt->prev1IsChar = True;
++ opt->prev2 = True;
++ opt->posPrev2 = cur;
++ opt->backPrev2 = repIndex;
++ }
++ }
++ }
++ }
++ }
++ }
++ /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */
++ if (newLen > numAvail)
++ {
++ newLen = numAvail;
++ for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2);
++ matches[numPairs] = newLen;
++ numPairs += 2;
++ }
++ if (newLen >= startLen)
++ {
++ UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]);
++ UInt32 offs, curBack, posSlot;
++ UInt32 lenTest;
++ while (lenEnd < cur + newLen)
++ p->opt[++lenEnd].price = kInfinityPrice;
++
++ offs = 0;
++ while (startLen > matches[offs])
++ offs += 2;
++ curBack = matches[offs + 1];
++ GetPosSlot2(curBack, posSlot);
++ for (lenTest = /*2*/ startLen; ; lenTest++)
++ {
++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN];
++ UInt32 lenToPosState = GetLenToPosState(lenTest);
++ COptimal *opt;
++ if (curBack < kNumFullDistances)
++ curAndLenPrice += p->distancesPrices[lenToPosState][curBack];
++ else
++ curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask];
++
++ opt = &p->opt[cur + lenTest];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = cur;
++ opt->backPrev = curBack + LZMA_NUM_REPS;
++ opt->prev1IsChar = False;
++ }
++
++ if (/*_maxMode && */lenTest == matches[offs])
++ {
++ /* Try Match + Literal + Rep0 */
++ const Byte *data2 = data - (curBack + 1);
++ UInt32 lenTest2 = lenTest + 1;
++ UInt32 limit = lenTest2 + p->numFastBytes;
++ UInt32 nextRepMatchPrice;
++ if (limit > numAvailFull)
++ limit = numAvailFull;
++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++);
++ lenTest2 -= lenTest + 1;
++ if (lenTest2 >= 2)
++ {
++ UInt32 state2 = kMatchNextStates[state];
++ UInt32 posStateNext = (position + lenTest) & p->pbMask;
++ UInt32 curAndLenCharPrice = curAndLenPrice +
++ GET_PRICE_0(p->isMatch[state2][posStateNext]) +
++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]),
++ data[lenTest], data2[lenTest], p->ProbPrices);
++ state2 = kLiteralNextStates[state2];
++ posStateNext = (posStateNext + 1) & p->pbMask;
++ nextRepMatchPrice = curAndLenCharPrice +
++ GET_PRICE_1(p->isMatch[state2][posStateNext]) +
++ GET_PRICE_1(p->isRep[state2]);
++
++ /* for (; lenTest2 >= 2; lenTest2--) */
++ {
++ UInt32 offset = cur + lenTest + 1 + lenTest2;
++ UInt32 curAndLenPrice;
++ COptimal *opt;
++ while (lenEnd < offset)
++ p->opt[++lenEnd].price = kInfinityPrice;
++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
++ opt = &p->opt[offset];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = cur + lenTest + 1;
++ opt->backPrev = 0;
++ opt->prev1IsChar = True;
++ opt->prev2 = True;
++ opt->posPrev2 = cur;
++ opt->backPrev2 = curBack + LZMA_NUM_REPS;
++ }
++ }
++ }
++ offs += 2;
++ if (offs == numPairs)
++ break;
++ curBack = matches[offs + 1];
++ if (curBack >= kNumFullDistances)
++ GetPosSlot2(curBack, posSlot);
++ }
++ }
++ }
++ }
++}
++
++#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist))
++
++static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes)
++{
++ UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i;
++ const Byte *data;
++ const UInt32 *matches;
++
++ if (p->additionalOffset == 0)
++ mainLen = ReadMatchDistances(p, &numPairs);
++ else
++ {
++ mainLen = p->longestMatchLength;
++ numPairs = p->numPairs;
++ }
++
++ numAvail = p->numAvail;
++ *backRes = (UInt32)-1;
++ if (numAvail < 2)
++ return 1;
++ if (numAvail > LZMA_MATCH_LEN_MAX)
++ numAvail = LZMA_MATCH_LEN_MAX;
++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
++
++ repLen = repIndex = 0;
++ for (i = 0; i < LZMA_NUM_REPS; i++)
++ {
++ UInt32 len;
++ const Byte *data2 = data - (p->reps[i] + 1);
++ if (data[0] != data2[0] || data[1] != data2[1])
++ continue;
++ for (len = 2; len < numAvail && data[len] == data2[len]; len++);
++ if (len >= p->numFastBytes)
++ {
++ *backRes = i;
++ MovePos(p, len - 1);
++ return len;
++ }
++ if (len > repLen)
++ {
++ repIndex = i;
++ repLen = len;
++ }
++ }
++
++ matches = p->matches;
++ if (mainLen >= p->numFastBytes)
++ {
++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS;
++ MovePos(p, mainLen - 1);
++ return mainLen;
++ }
++
++ mainDist = 0; /* for GCC */
++ if (mainLen >= 2)
++ {
++ mainDist = matches[numPairs - 1];
++ while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1)
++ {
++ if (!ChangePair(matches[numPairs - 3], mainDist))
++ break;
++ numPairs -= 2;
++ mainLen = matches[numPairs - 2];
++ mainDist = matches[numPairs - 1];
++ }
++ if (mainLen == 2 && mainDist >= 0x80)
++ mainLen = 1;
++ }
++
++ if (repLen >= 2 && (
++ (repLen + 1 >= mainLen) ||
++ (repLen + 2 >= mainLen && mainDist >= (1 << 9)) ||
++ (repLen + 3 >= mainLen && mainDist >= (1 << 15))))
++ {
++ *backRes = repIndex;
++ MovePos(p, repLen - 1);
++ return repLen;
++ }
++
++ if (mainLen < 2 || numAvail <= 2)
++ return 1;
++
++ p->longestMatchLength = ReadMatchDistances(p, &p->numPairs);
++ if (p->longestMatchLength >= 2)
++ {
++ UInt32 newDistance = matches[p->numPairs - 1];
++ if ((p->longestMatchLength >= mainLen && newDistance < mainDist) ||
++ (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) ||
++ (p->longestMatchLength > mainLen + 1) ||
++ (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist)))
++ return 1;
++ }
++
++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
++ for (i = 0; i < LZMA_NUM_REPS; i++)
++ {
++ UInt32 len, limit;
++ const Byte *data2 = data - (p->reps[i] + 1);
++ if (data[0] != data2[0] || data[1] != data2[1])
++ continue;
++ limit = mainLen - 1;
++ for (len = 2; len < limit && data[len] == data2[len]; len++);
++ if (len >= limit)
++ return 1;
++ }
++ *backRes = mainDist + LZMA_NUM_REPS;
++ MovePos(p, mainLen - 2);
++ return mainLen;
++}
++
++static void WriteEndMarker(CLzmaEnc *p, UInt32 posState)
++{
++ UInt32 len;
++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1);
++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0);
++ p->state = kMatchNextStates[p->state];
++ len = LZMA_MATCH_LEN_MIN;
++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1);
++ RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits);
++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask);
++}
++
++static SRes CheckErrors(CLzmaEnc *p)
++{
++ if (p->result != SZ_OK)
++ return p->result;
++ if (p->rc.res != SZ_OK)
++ p->result = SZ_ERROR_WRITE;
++ if (p->matchFinderBase.result != SZ_OK)
++ p->result = SZ_ERROR_READ;
++ if (p->result != SZ_OK)
++ p->finished = True;
++ return p->result;
++}
++
++static SRes Flush(CLzmaEnc *p, UInt32 nowPos)
++{
++ /* ReleaseMFStream(); */
++ p->finished = True;
++ if (p->writeEndMark)
++ WriteEndMarker(p, nowPos & p->pbMask);
++ RangeEnc_FlushData(&p->rc);
++ RangeEnc_FlushStream(&p->rc);
++ return CheckErrors(p);
++}
++
++static void FillAlignPrices(CLzmaEnc *p)
++{
++ UInt32 i;
++ for (i = 0; i < kAlignTableSize; i++)
++ p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices);
++ p->alignPriceCount = 0;
++}
++
++static void FillDistancesPrices(CLzmaEnc *p)
++{
++ UInt32 tempPrices[kNumFullDistances];
++ UInt32 i, lenToPosState;
++ for (i = kStartPosModelIndex; i < kNumFullDistances; i++)
++ {
++ UInt32 posSlot = GetPosSlot1(i);
++ UInt32 footerBits = ((posSlot >> 1) - 1);
++ UInt32 base = ((2 | (posSlot & 1)) << footerBits);
++ tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices);
++ }
++
++ for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++)
++ {
++ UInt32 posSlot;
++ const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState];
++ UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState];
++ for (posSlot = 0; posSlot < p->distTableSize; posSlot++)
++ posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices);
++ for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++)
++ posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits);
++
++ {
++ UInt32 *distancesPrices = p->distancesPrices[lenToPosState];
++ UInt32 i;
++ for (i = 0; i < kStartPosModelIndex; i++)
++ distancesPrices[i] = posSlotPrices[i];
++ for (; i < kNumFullDistances; i++)
++ distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i];
++ }
++ }
++ p->matchPriceCount = 0;
++}
++
++void LzmaEnc_Construct(CLzmaEnc *p)
++{
++ RangeEnc_Construct(&p->rc);
++ MatchFinder_Construct(&p->matchFinderBase);
++ #ifndef _7ZIP_ST
++ MatchFinderMt_Construct(&p->matchFinderMt);
++ p->matchFinderMt.MatchFinder = &p->matchFinderBase;
++ #endif
++
++ {
++ CLzmaEncProps props;
++ LzmaEncProps_Init(&props);
++ LzmaEnc_SetProps(p, &props);
++ }
++
++ #ifndef LZMA_LOG_BSR
++ LzmaEnc_FastPosInit(p->g_FastPos);
++ #endif
++
++ LzmaEnc_InitPriceTables(p->ProbPrices);
++ p->litProbs = 0;
++ p->saveState.litProbs = 0;
++}
++
++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc)
++{
++ void *p;
++ p = alloc->Alloc(alloc, sizeof(CLzmaEnc));
++ if (p != 0)
++ LzmaEnc_Construct((CLzmaEnc *)p);
++ return p;
++}
++
++void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc)
++{
++ alloc->Free(alloc, p->litProbs);
++ alloc->Free(alloc, p->saveState.litProbs);
++ p->litProbs = 0;
++ p->saveState.litProbs = 0;
++}
++
++void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ #ifndef _7ZIP_ST
++ MatchFinderMt_Destruct(&p->matchFinderMt, allocBig);
++ #endif
++ MatchFinder_Free(&p->matchFinderBase, allocBig);
++ LzmaEnc_FreeLits(p, alloc);
++ RangeEnc_Free(&p->rc, alloc);
++}
++
++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig);
++ alloc->Free(alloc, p);
++}
++
++static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize)
++{
++ UInt32 nowPos32, startPos32;
++ if (p->needInit)
++ {
++ p->matchFinder.Init(p->matchFinderObj);
++ p->needInit = 0;
++ }
++
++ if (p->finished)
++ return p->result;
++ RINOK(CheckErrors(p));
++
++ nowPos32 = (UInt32)p->nowPos64;
++ startPos32 = nowPos32;
++
++ if (p->nowPos64 == 0)
++ {
++ UInt32 numPairs;
++ Byte curByte;
++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0)
++ return Flush(p, nowPos32);
++ ReadMatchDistances(p, &numPairs);
++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0);
++ p->state = kLiteralNextStates[p->state];
++ curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset);
++ LitEnc_Encode(&p->rc, p->litProbs, curByte);
++ p->additionalOffset--;
++ nowPos32++;
++ }
++
++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0)
++ for (;;)
++ {
++ UInt32 pos, len, posState;
++
++ if (p->fastMode)
++ len = GetOptimumFast(p, &pos);
++ else
++ len = GetOptimum(p, nowPos32, &pos);
++
++ #ifdef SHOW_STAT2
++ printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos);
++ #endif
++
++ posState = nowPos32 & p->pbMask;
++ if (len == 1 && pos == (UInt32)-1)
++ {
++ Byte curByte;
++ CLzmaProb *probs;
++ const Byte *data;
++
++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0);
++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset;
++ curByte = *data;
++ probs = LIT_PROBS(nowPos32, *(data - 1));
++ if (IsCharState(p->state))
++ LitEnc_Encode(&p->rc, probs, curByte);
++ else
++ LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1));
++ p->state = kLiteralNextStates[p->state];
++ }
++ else
++ {
++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1);
++ if (pos < LZMA_NUM_REPS)
++ {
++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1);
++ if (pos == 0)
++ {
++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0);
++ RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1));
++ }
++ else
++ {
++ UInt32 distance = p->reps[pos];
++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1);
++ if (pos == 1)
++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0);
++ else
++ {
++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1);
++ RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2);
++ if (pos == 3)
++ p->reps[3] = p->reps[2];
++ p->reps[2] = p->reps[1];
++ }
++ p->reps[1] = p->reps[0];
++ p->reps[0] = distance;
++ }
++ if (len == 1)
++ p->state = kShortRepNextStates[p->state];
++ else
++ {
++ LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
++ p->state = kRepNextStates[p->state];
++ }
++ }
++ else
++ {
++ UInt32 posSlot;
++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0);
++ p->state = kMatchNextStates[p->state];
++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
++ pos -= LZMA_NUM_REPS;
++ GetPosSlot(pos, posSlot);
++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot);
++
++ if (posSlot >= kStartPosModelIndex)
++ {
++ UInt32 footerBits = ((posSlot >> 1) - 1);
++ UInt32 base = ((2 | (posSlot & 1)) << footerBits);
++ UInt32 posReduced = pos - base;
++
++ if (posSlot < kEndPosModelIndex)
++ RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced);
++ else
++ {
++ RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits);
++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask);
++ p->alignPriceCount++;
++ }
++ }
++ p->reps[3] = p->reps[2];
++ p->reps[2] = p->reps[1];
++ p->reps[1] = p->reps[0];
++ p->reps[0] = pos;
++ p->matchPriceCount++;
++ }
++ }
++ p->additionalOffset -= len;
++ nowPos32 += len;
++ if (p->additionalOffset == 0)
++ {
++ UInt32 processed;
++ if (!p->fastMode)
++ {
++ if (p->matchPriceCount >= (1 << 7))
++ FillDistancesPrices(p);
++ if (p->alignPriceCount >= kAlignTableSize)
++ FillAlignPrices(p);
++ }
++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0)
++ break;
++ processed = nowPos32 - startPos32;
++ if (useLimits)
++ {
++ if (processed + kNumOpts + 300 >= maxUnpackSize ||
++ RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize)
++ break;
++ }
++ else if (processed >= (1 << 15))
++ {
++ p->nowPos64 += nowPos32 - startPos32;
++ return CheckErrors(p);
++ }
++ }
++ }
++ p->nowPos64 += nowPos32 - startPos32;
++ return Flush(p, nowPos32);
++}
++
++#define kBigHashDicLimit ((UInt32)1 << 24)
++
++static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ UInt32 beforeSize = kNumOpts;
++ Bool btMode;
++ if (!RangeEnc_Alloc(&p->rc, alloc))
++ return SZ_ERROR_MEM;
++ btMode = (p->matchFinderBase.btMode != 0);
++ #ifndef _7ZIP_ST
++ p->mtMode = (p->multiThread && !p->fastMode && btMode);
++ #endif
++
++ {
++ unsigned lclp = p->lc + p->lp;
++ if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp)
++ {
++ LzmaEnc_FreeLits(p, alloc);
++ p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb));
++ p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb));
++ if (p->litProbs == 0 || p->saveState.litProbs == 0)
++ {
++ LzmaEnc_FreeLits(p, alloc);
++ return SZ_ERROR_MEM;
++ }
++ p->lclp = lclp;
++ }
++ }
++
++ p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit);
++
++ if (beforeSize + p->dictSize < keepWindowSize)
++ beforeSize = keepWindowSize - p->dictSize;
++
++ #ifndef _7ZIP_ST
++ if (p->mtMode)
++ {
++ RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig));
++ p->matchFinderObj = &p->matchFinderMt;
++ MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder);
++ }
++ else
++ #endif
++ {
++ if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig))
++ return SZ_ERROR_MEM;
++ p->matchFinderObj = &p->matchFinderBase;
++ MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder);
++ }
++ return SZ_OK;
++}
++
++void LzmaEnc_Init(CLzmaEnc *p)
++{
++ UInt32 i;
++ p->state = 0;
++ for (i = 0 ; i < LZMA_NUM_REPS; i++)
++ p->reps[i] = 0;
++
++ RangeEnc_Init(&p->rc);
++
++
++ for (i = 0; i < kNumStates; i++)
++ {
++ UInt32 j;
++ for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++)
++ {
++ p->isMatch[i][j] = kProbInitValue;
++ p->isRep0Long[i][j] = kProbInitValue;
++ }
++ p->isRep[i] = kProbInitValue;
++ p->isRepG0[i] = kProbInitValue;
++ p->isRepG1[i] = kProbInitValue;
++ p->isRepG2[i] = kProbInitValue;
++ }
++
++ {
++ UInt32 num = 0x300 << (p->lp + p->lc);
++ for (i = 0; i < num; i++)
++ p->litProbs[i] = kProbInitValue;
++ }
++
++ {
++ for (i = 0; i < kNumLenToPosStates; i++)
++ {
++ CLzmaProb *probs = p->posSlotEncoder[i];
++ UInt32 j;
++ for (j = 0; j < (1 << kNumPosSlotBits); j++)
++ probs[j] = kProbInitValue;
++ }
++ }
++ {
++ for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++)
++ p->posEncoders[i] = kProbInitValue;
++ }
++
++ LenEnc_Init(&p->lenEnc.p);
++ LenEnc_Init(&p->repLenEnc.p);
++
++ for (i = 0; i < (1 << kNumAlignBits); i++)
++ p->posAlignEncoder[i] = kProbInitValue;
++
++ p->optimumEndIndex = 0;
++ p->optimumCurrentIndex = 0;
++ p->additionalOffset = 0;
++
++ p->pbMask = (1 << p->pb) - 1;
++ p->lpMask = (1 << p->lp) - 1;
++}
++
++void LzmaEnc_InitPrices(CLzmaEnc *p)
++{
++ if (!p->fastMode)
++ {
++ FillDistancesPrices(p);
++ FillAlignPrices(p);
++ }
++
++ p->lenEnc.tableSize =
++ p->repLenEnc.tableSize =
++ p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN;
++ LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices);
++ LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices);
++}
++
++static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ UInt32 i;
++ for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++)
++ if (p->dictSize <= ((UInt32)1 << i))
++ break;
++ p->distTableSize = i * 2;
++
++ p->finished = False;
++ p->result = SZ_OK;
++ RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig));
++ LzmaEnc_Init(p);
++ LzmaEnc_InitPrices(p);
++ p->nowPos64 = 0;
++ return SZ_OK;
++}
++
++static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream,
++ ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ p->matchFinderBase.stream = inStream;
++ p->needInit = 1;
++ p->rc.outStream = outStream;
++ return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig);
++}
++
++SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp,
++ ISeqInStream *inStream, UInt32 keepWindowSize,
++ ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ p->matchFinderBase.stream = inStream;
++ p->needInit = 1;
++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
++}
++
++static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen)
++{
++ p->matchFinderBase.directInput = 1;
++ p->matchFinderBase.bufferBase = (Byte *)src;
++ p->matchFinderBase.directInputRem = srcLen;
++}
++
++SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
++ UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ LzmaEnc_SetInputBuf(p, src, srcLen);
++ p->needInit = 1;
++
++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
++}
++
++void LzmaEnc_Finish(CLzmaEncHandle pp)
++{
++ #ifndef _7ZIP_ST
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ if (p->mtMode)
++ MatchFinderMt_ReleaseStream(&p->matchFinderMt);
++ #else
++ pp = pp;
++ #endif
++}
++
++typedef struct
++{
++ ISeqOutStream funcTable;
++ Byte *data;
++ SizeT rem;
++ Bool overflow;
++} CSeqOutStreamBuf;
++
++static size_t MyWrite(void *pp, const void *data, size_t size)
++{
++ CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp;
++ if (p->rem < size)
++ {
++ size = p->rem;
++ p->overflow = True;
++ }
++ memcpy(p->data, data, size);
++ p->rem -= size;
++ p->data += size;
++ return size;
++}
++
++
++UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp)
++{
++ const CLzmaEnc *p = (CLzmaEnc *)pp;
++ return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
++}
++
++const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp)
++{
++ const CLzmaEnc *p = (CLzmaEnc *)pp;
++ return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset;
++}
++
++SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit,
++ Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ UInt64 nowPos64;
++ SRes res;
++ CSeqOutStreamBuf outStream;
++
++ outStream.funcTable.Write = MyWrite;
++ outStream.data = dest;
++ outStream.rem = *destLen;
++ outStream.overflow = False;
++
++ p->writeEndMark = False;
++ p->finished = False;
++ p->result = SZ_OK;
++
++ if (reInit)
++ LzmaEnc_Init(p);
++ LzmaEnc_InitPrices(p);
++ nowPos64 = p->nowPos64;
++ RangeEnc_Init(&p->rc);
++ p->rc.outStream = &outStream.funcTable;
++
++ res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize);
++
++ *unpackSize = (UInt32)(p->nowPos64 - nowPos64);
++ *destLen -= outStream.rem;
++ if (outStream.overflow)
++ return SZ_ERROR_OUTPUT_EOF;
++
++ return res;
++}
++
++static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress)
++{
++ SRes res = SZ_OK;
++
++ #ifndef _7ZIP_ST
++ Byte allocaDummy[0x300];
++ int i = 0;
++ for (i = 0; i < 16; i++)
++ allocaDummy[i] = (Byte)i;
++ #endif
++
++ for (;;)
++ {
++ res = LzmaEnc_CodeOneBlock(p, False, 0, 0);
++ if (res != SZ_OK || p->finished != 0)
++ break;
++ if (progress != 0)
++ {
++ res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc));
++ if (res != SZ_OK)
++ {
++ res = SZ_ERROR_PROGRESS;
++ break;
++ }
++ }
++ }
++ LzmaEnc_Finish(p);
++ return res;
++}
++
++SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress,
++ ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig));
++ return LzmaEnc_Encode2((CLzmaEnc *)pp, progress);
++}
++
++SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ int i;
++ UInt32 dictSize = p->dictSize;
++ if (*size < LZMA_PROPS_SIZE)
++ return SZ_ERROR_PARAM;
++ *size = LZMA_PROPS_SIZE;
++ props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc);
++
++ for (i = 11; i <= 30; i++)
++ {
++ if (dictSize <= ((UInt32)2 << i))
++ {
++ dictSize = (2 << i);
++ break;
++ }
++ if (dictSize <= ((UInt32)3 << i))
++ {
++ dictSize = (3 << i);
++ break;
++ }
++ }
++
++ for (i = 0; i < 4; i++)
++ props[1 + i] = (Byte)(dictSize >> (8 * i));
++ return SZ_OK;
++}
++
++SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ SRes res;
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++
++ CSeqOutStreamBuf outStream;
++
++ LzmaEnc_SetInputBuf(p, src, srcLen);
++
++ outStream.funcTable.Write = MyWrite;
++ outStream.data = dest;
++ outStream.rem = *destLen;
++ outStream.overflow = False;
++
++ p->writeEndMark = writeEndMark;
++
++ p->rc.outStream = &outStream.funcTable;
++ res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig);
++ if (res == SZ_OK)
++ res = LzmaEnc_Encode2(p, progress);
++
++ *destLen -= outStream.rem;
++ if (outStream.overflow)
++ return SZ_ERROR_OUTPUT_EOF;
++ return res;
++}
++
++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc);
++ SRes res;
++ if (p == 0)
++ return SZ_ERROR_MEM;
++
++ res = LzmaEnc_SetProps(p, props);
++ if (res == SZ_OK)
++ {
++ res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize);
++ if (res == SZ_OK)
++ res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen,
++ writeEndMark, progress, alloc, allocBig);
++ }
++
++ LzmaEnc_Destroy(p, alloc, allocBig);
++ return res;
++}
+--- /dev/null
++++ b/lib/lzma/Makefile
+@@ -0,0 +1,7 @@
++lzma_compress-objs := LzFind.o LzmaEnc.o
++lzma_decompress-objs := LzmaDec.o
++
++obj-$(CONFIG_LZMA_COMPRESS) += lzma_compress.o
++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma_decompress.o
++
++EXTRA_CFLAGS += -Iinclude/linux -Iinclude/linux/lzma -include types.h
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: fs: jffs2: EOF marker
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ fs/jffs2/build.c | 10 ++++++++++
+ fs/jffs2/scan.c | 21 +++++++++++++++++++--
+ 2 files changed, 29 insertions(+), 2 deletions(-)
+
+--- a/fs/jffs2/build.c
++++ b/fs/jffs2/build.c
+@@ -117,6 +117,16 @@ static int jffs2_build_filesystem(struct
+ dbg_fsbuild("scanned flash completely\n");
+ jffs2_dbg_dump_block_lists_nolock(c);
+
++ if (c->flags & (1 << 7)) {
++ printk("%s(): unlocking the mtd device... ", __func__);
++ mtd_unlock(c->mtd, 0, c->mtd->size);
++ printk("done.\n");
++
++ printk("%s(): erasing all blocks after the end marker... ", __func__);
++ jffs2_erase_pending_blocks(c, -1);
++ printk("done.\n");
++ }
++
+ dbg_fsbuild("pass 1 starting\n");
+ c->flags |= JFFS2_SB_FLAG_BUILDING;
+ /* Now scan the directory tree, increasing nlink according to every dirent found. */
+--- a/fs/jffs2/scan.c
++++ b/fs/jffs2/scan.c
+@@ -148,8 +148,14 @@ int jffs2_scan_medium(struct jffs2_sb_in
+ /* reset summary info for next eraseblock scan */
+ jffs2_sum_reset_collected(s);
+
+- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
+- buf_size, s);
++ if (c->flags & (1 << 7)) {
++ if (mtd_block_isbad(c->mtd, jeb->offset))
++ ret = BLK_STATE_BADBLOCK;
++ else
++ ret = BLK_STATE_ALLFF;
++ } else
++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
++ buf_size, s);
+
+ if (ret < 0)
+ goto out;
+@@ -565,6 +571,17 @@ full_scan:
+ return err;
+ }
+
++ if ((buf[0] == 0xde) &&
++ (buf[1] == 0xad) &&
++ (buf[2] == 0xc0) &&
++ (buf[3] == 0xde)) {
++ /* end of filesystem. erase everything after this point */
++ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset);
++ c->flags |= (1 << 7);
++
++ return BLK_STATE_ALLFF;
++ }
++
+ /* We temporarily use 'ofs' as a pointer into the buffer/jeb */
+ ofs = 0;
+ max_ofs = EMPTY_SCAN_SIZE(c->sector_size);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: netfilter: add support for flushing conntrack via /proc
+
+lede-commit 8193bbe59a74d34d6a26d4a8cb857b1952905314
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/netfilter/nf_conntrack_standalone.c | 59 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 58 insertions(+), 1 deletion(-)
+
+--- a/net/netfilter/nf_conntrack_standalone.c
++++ b/net/netfilter/nf_conntrack_standalone.c
+@@ -9,6 +9,7 @@
+ #include <linux/percpu.h>
+ #include <linux/netdevice.h>
+ #include <linux/security.h>
++#include <linux/inet.h>
+ #include <net/net_namespace.h>
+ #ifdef CONFIG_SYSCTL
+ #include <linux/sysctl.h>
+@@ -457,6 +458,56 @@ static int ct_cpu_seq_show(struct seq_fi
+ return 0;
+ }
+
++struct kill_request {
++ u16 family;
++ union nf_inet_addr addr;
++};
++
++static int kill_matching(struct nf_conn *i, void *data)
++{
++ struct kill_request *kr = data;
++ struct nf_conntrack_tuple *t1 = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++ struct nf_conntrack_tuple *t2 = &i->tuplehash[IP_CT_DIR_REPLY].tuple;
++
++ if (!kr->family)
++ return 1;
++
++ if (t1->src.l3num != kr->family)
++ return 0;
++
++ return (nf_inet_addr_cmp(&kr->addr, &t1->src.u3) ||
++ nf_inet_addr_cmp(&kr->addr, &t1->dst.u3) ||
++ nf_inet_addr_cmp(&kr->addr, &t2->src.u3) ||
++ nf_inet_addr_cmp(&kr->addr, &t2->dst.u3));
++}
++
++static int ct_file_write(struct file *file, char *buf, size_t count)
++{
++ struct seq_file *seq = file->private_data;
++ struct net *net = seq_file_net(seq);
++ struct kill_request kr = { };
++
++ if (count == 0)
++ return 0;
++
++ if (count >= INET6_ADDRSTRLEN)
++ count = INET6_ADDRSTRLEN - 1;
++
++ if (strnchr(buf, count, ':')) {
++ kr.family = AF_INET6;
++ if (!in6_pton(buf, count, (void *)&kr.addr, '\n', NULL))
++ return -EINVAL;
++ } else if (strnchr(buf, count, '.')) {
++ kr.family = AF_INET;
++ if (!in4_pton(buf, count, (void *)&kr.addr, '\n', NULL))
++ return -EINVAL;
++ }
++
++ nf_ct_iterate_cleanup_net(net, kill_matching, &kr, 0, 0);
++
++ return 0;
++}
++
+ static const struct seq_operations ct_cpu_seq_ops = {
+ .start = ct_cpu_seq_start,
+ .next = ct_cpu_seq_next,
+@@ -470,8 +521,9 @@ static int nf_conntrack_standalone_init_
+ kuid_t root_uid;
+ kgid_t root_gid;
+
+- pde = proc_create_net("nf_conntrack", 0440, net->proc_net, &ct_seq_ops,
+- sizeof(struct ct_iter_state));
++ pde = proc_create_net_data_write("nf_conntrack", 0440, net->proc_net,
++ &ct_seq_ops, &ct_file_write,
++ sizeof(struct ct_iter_state), NULL);
+ if (!pde)
+ goto out_nf_conntrack;
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: add a new version of my netfilter speedup patches for linux 2.6.39 and 3.0
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/uapi/linux/netfilter_ipv4/ip_tables.h | 1 +
+ net/ipv4/netfilter/ip_tables.c | 37 +++++++++++++++++++++++++++
+ 2 files changed, 38 insertions(+)
+
+--- a/include/uapi/linux/netfilter_ipv4/ip_tables.h
++++ b/include/uapi/linux/netfilter_ipv4/ip_tables.h
+@@ -89,6 +89,7 @@ struct ipt_ip {
+ #define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */
+ #define IPT_F_GOTO 0x02 /* Set if jump is a goto */
+ #define IPT_F_MASK 0x03 /* All possible flag bits mask. */
++#define IPT_F_NO_DEF_MATCH 0x80 /* Internal: no default match rules present */
+
+ /* Values for "inv" field in struct ipt_ip. */
+ #define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -50,6 +50,9 @@ ip_packet_match(const struct iphdr *ip,
+ {
+ unsigned long ret;
+
++ if (ipinfo->flags & IPT_F_NO_DEF_MATCH)
++ return true;
++
+ if (NF_INVF(ipinfo, IPT_INV_SRCIP,
+ (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) ||
+ NF_INVF(ipinfo, IPT_INV_DSTIP,
+@@ -80,6 +83,29 @@ ip_packet_match(const struct iphdr *ip,
+ return true;
+ }
+
++static void
++ip_checkdefault(struct ipt_ip *ip)
++{
++ static const char iface_mask[IFNAMSIZ] = {};
++
++ if (ip->invflags || ip->flags & IPT_F_FRAG)
++ return;
++
++ if (memcmp(ip->iniface_mask, iface_mask, IFNAMSIZ) != 0)
++ return;
++
++ if (memcmp(ip->outiface_mask, iface_mask, IFNAMSIZ) != 0)
++ return;
++
++ if (ip->smsk.s_addr || ip->dmsk.s_addr)
++ return;
++
++ if (ip->proto)
++ return;
++
++ ip->flags |= IPT_F_NO_DEF_MATCH;
++}
++
+ static bool
+ ip_checkentry(const struct ipt_ip *ip)
+ {
+@@ -524,6 +550,8 @@ find_check_entry(struct ipt_entry *e, st
+ struct xt_mtchk_param mtpar;
+ struct xt_entry_match *ematch;
+
++ ip_checkdefault(&e->ip);
++
+ if (!xt_percpu_counter_alloc(alloc_state, &e->counters))
+ return -ENOMEM;
+
+@@ -818,6 +846,7 @@ copy_entries_to_user(unsigned int total_
+ const struct xt_table_info *private = table->private;
+ int ret = 0;
+ const void *loc_cpu_entry;
++ u8 flags;
+
+ counters = alloc_counters(table);
+ if (IS_ERR(counters))
+@@ -845,6 +874,14 @@ copy_entries_to_user(unsigned int total_
+ goto free_counters;
+ }
+
++ flags = e->ip.flags & IPT_F_MASK;
++ if (copy_to_user(userptr + off
++ + offsetof(struct ipt_entry, ip.flags),
++ &flags, sizeof(flags)) != 0) {
++ ret = -EFAULT;
++ goto free_counters;
++ }
++
+ for (i = sizeof(struct ipt_entry);
+ i < e->target_offset;
+ i += m->u.match_size) {
+@@ -1223,12 +1260,15 @@ compat_copy_entry_to_user(struct ipt_ent
+ compat_uint_t origsize;
+ const struct xt_entry_match *ematch;
+ int ret = 0;
++ u8 flags = e->ip.flags & IPT_F_MASK;
+
+ origsize = *size;
+ ce = *dstptr;
+ if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 ||
+ copy_to_user(&ce->counters, &counters[i],
+- sizeof(counters[i])) != 0)
++ sizeof(counters[i])) != 0 ||
++ copy_to_user(&ce->ip.flags, &flags,
++ sizeof(flags)) != 0)
+ return -EFAULT;
+
+ *dstptr += sizeof(struct compat_ipt_entry);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: netfilter: match bypass default table
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/ipv4/netfilter/ip_tables.c | 79 +++++++++++++++++++++++++++++++-----------
+ 1 file changed, 58 insertions(+), 21 deletions(-)
+
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -246,6 +246,33 @@ struct ipt_entry *ipt_next_entry(const s
+ return (void *)entry + entry->next_offset;
+ }
+
++static bool
++ipt_handle_default_rule(struct ipt_entry *e, unsigned int *verdict)
++{
++ struct xt_entry_target *t;
++ struct xt_standard_target *st;
++
++ if (e->target_offset != sizeof(struct ipt_entry))
++ return false;
++
++ if (!(e->ip.flags & IPT_F_NO_DEF_MATCH))
++ return false;
++
++ t = ipt_get_target(e);
++ if (t->u.kernel.target->target)
++ return false;
++
++ st = (struct xt_standard_target *) t;
++ if (st->verdict == XT_RETURN)
++ return false;
++
++ if (st->verdict >= 0)
++ return false;
++
++ *verdict = (unsigned)(-st->verdict) - 1;
++ return true;
++}
++
+ /* Returns one of the generic firewall policies, like NF_ACCEPT. */
+ unsigned int
+ ipt_do_table(struct sk_buff *skb,
+@@ -266,27 +293,28 @@ ipt_do_table(struct sk_buff *skb,
+ unsigned int addend;
+
+ /* Initialization */
++ WARN_ON(!(table->valid_hooks & (1 << hook)));
++ local_bh_disable();
++ private = READ_ONCE(table->private); /* Address dependency. */
++ cpu = smp_processor_id();
++ table_base = private->entries;
++
++ e = get_entry(table_base, private->hook_entry[hook]);
++ if (ipt_handle_default_rule(e, &verdict)) {
++ struct xt_counters *counter;
++
++ counter = xt_get_this_cpu_counter(&e->counters);
++ ADD_COUNTER(*counter, skb->len, 1);
++ local_bh_enable();
++ return verdict;
++ }
++
+ stackidx = 0;
+ ip = ip_hdr(skb);
+ indev = state->in ? state->in->name : nulldevname;
+ outdev = state->out ? state->out->name : nulldevname;
+- /* We handle fragments by dealing with the first fragment as
+- * if it was a normal packet. All other fragments are treated
+- * normally, except that they will NEVER match rules that ask
+- * things we don't know, ie. tcp syn flag or ports). If the
+- * rule is also a fragment-specific rule, non-fragments won't
+- * match it. */
+- acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
+- acpar.thoff = ip_hdrlen(skb);
+- acpar.hotdrop = false;
+- acpar.state = state;
+
+- WARN_ON(!(table->valid_hooks & (1 << hook)));
+- local_bh_disable();
+ addend = xt_write_recseq_begin();
+- private = READ_ONCE(table->private); /* Address dependency. */
+- cpu = smp_processor_id();
+- table_base = private->entries;
+ jumpstack = (struct ipt_entry **)private->jumpstack[cpu];
+
+ /* Switch to alternate jumpstack if we're being invoked via TEE.
+@@ -299,7 +327,16 @@ ipt_do_table(struct sk_buff *skb,
+ if (static_key_false(&xt_tee_enabled))
+ jumpstack += private->stacksize * __this_cpu_read(nf_skb_duplicated);
+
+- e = get_entry(table_base, private->hook_entry[hook]);
++ /* We handle fragments by dealing with the first fragment as
++ * if it was a normal packet. All other fragments are treated
++ * normally, except that they will NEVER match rules that ask
++ * things we don't know, ie. tcp syn flag or ports). If the
++ * rule is also a fragment-specific rule, non-fragments won't
++ * match it. */
++ acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
++ acpar.thoff = ip_hdrlen(skb);
++ acpar.hotdrop = false;
++ acpar.state = state;
+
+ do {
+ const struct xt_entry_target *t;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: netfilter: reduce match memory access
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/ipv4/netfilter/ip_tables.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -53,9 +53,9 @@ ip_packet_match(const struct iphdr *ip,
+ if (ipinfo->flags & IPT_F_NO_DEF_MATCH)
+ return true;
+
+- if (NF_INVF(ipinfo, IPT_INV_SRCIP,
++ if (NF_INVF(ipinfo, IPT_INV_SRCIP, ipinfo->smsk.s_addr &&
+ (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) ||
+- NF_INVF(ipinfo, IPT_INV_DSTIP,
++ NF_INVF(ipinfo, IPT_INV_DSTIP, ipinfo->dmsk.s_addr &&
+ (ip->daddr & ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr))
+ return false;
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: netfilter: optional tcp window check
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/netfilter/nf_conntrack_proto_tcp.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/net/netfilter/nf_conntrack_proto_tcp.c
++++ b/net/netfilter/nf_conntrack_proto_tcp.c
+@@ -31,6 +31,9 @@
+ #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
+ #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
+
++/* Do not check the TCP window for incoming packets */
++static int nf_ct_tcp_no_window_check __read_mostly = 1;
++
+ /* "Be conservative in what you do,
+ be liberal in what you accept from others."
+ If it's non-zero, we mark only out of window RST segments as INVALID. */
+@@ -476,6 +479,9 @@ static bool tcp_in_window(const struct n
+ s32 receiver_offset;
+ bool res, in_recv_win;
+
++ if (nf_ct_tcp_no_window_check)
++ return true;
++
+ /*
+ * Get the required data from the packet.
+ */
+@@ -1130,7 +1136,7 @@ int nf_conntrack_tcp_packet(struct nf_co
+ IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED &&
+ timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK])
+ timeout = timeouts[TCP_CONNTRACK_UNACK];
+- else if (ct->proto.tcp.last_win == 0 &&
++ else if (!nf_ct_tcp_no_window_check && ct->proto.tcp.last_win == 0 &&
+ timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS])
+ timeout = timeouts[TCP_CONNTRACK_RETRANS];
+ else
+--- a/net/netfilter/nf_conntrack_standalone.c
++++ b/net/netfilter/nf_conntrack_standalone.c
+@@ -25,6 +25,9 @@
+ #include <net/netfilter/nf_conntrack_timestamp.h>
+ #include <linux/rculist_nulls.h>
+
++/* Do not check the TCP window for incoming packets */
++static int nf_ct_tcp_no_window_check __read_mostly = 1;
++
+ static bool enable_hooks __read_mostly;
+ MODULE_PARM_DESC(enable_hooks, "Always enable conntrack hooks");
+ module_param(enable_hooks, bool, 0000);
+@@ -660,6 +663,7 @@ enum nf_ct_sysctl_index {
+ NF_SYSCTL_CT_PROTO_TIMEOUT_GRE_STREAM,
+ #endif
+
++ NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK,
+ __NF_SYSCTL_CT_LAST_SYSCTL,
+ };
+
+@@ -1014,6 +1018,13 @@ static struct ctl_table nf_ct_sysctl_tab
+ .proc_handler = proc_dointvec_jiffies,
+ },
+ #endif
++ [NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK] = {
++ .procname = "nf_conntrack_tcp_no_window_check",
++ .data = &nf_ct_tcp_no_window_check,
++ .maxlen = sizeof(unsigned int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
+ {}
+ };
+
--- /dev/null
+From: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
+Date: Mon, 21 Aug 2017 11:14:14 +0300
+Subject: [PATCH] net_sched/codel: do not defer queue length update
+
+When codel wants to drop last packet in ->dequeue() it cannot call
+qdisc_tree_reduce_backlog() right away - it will notify parent qdisc
+about zero qlen and HTB/HFSC will deactivate class. The same class will
+be deactivated second time by caller of ->dequeue(). Currently codel and
+fq_codel defer update. This triggers warning in HFSC when it's qlen != 0
+but there is no active classes.
+
+This patch update parent queue length immediately: just temporary increase
+qlen around qdisc_tree_reduce_backlog() to prevent first class deactivation
+if we have skb to return.
+
+This might open another problem in HFSC - now operation peek could fail and
+deactivate parent class.
+
+Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
+---
+
+--- a/net/sched/sch_codel.c
++++ b/net/sched/sch_codel.c
+@@ -95,11 +95,17 @@ static struct sk_buff *codel_qdisc_deque
+ &q->stats, qdisc_pkt_len, codel_get_enqueue_time,
+ drop_func, dequeue_func);
+
+- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
+- * or HTB crashes. Defer it for next round.
++ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
++ * parent class, dequeue in parent qdisc will do the same if we
++ * return skb. Temporary increment qlen if we have skb.
+ */
+- if (q->stats.drop_count && sch->q.qlen) {
+- qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len);
++ if (q->stats.drop_count) {
++ if (skb)
++ sch->q.qlen++;
++ qdisc_tree_reduce_backlog(sch, q->stats.drop_count,
++ q->stats.drop_len);
++ if (skb)
++ sch->q.qlen--;
+ q->stats.drop_count = 0;
+ q->stats.drop_len = 0;
+ }
+--- a/net/sched/sch_fq_codel.c
++++ b/net/sched/sch_fq_codel.c
+@@ -304,6 +304,21 @@ begin:
+ &flow->cvars, &q->cstats, qdisc_pkt_len,
+ codel_get_enqueue_time, drop_func, dequeue_func);
+
++ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
++ * parent class, dequeue in parent qdisc will do the same if we
++ * return skb. Temporary increment qlen if we have skb.
++ */
++ if (q->cstats.drop_count) {
++ if (skb)
++ sch->q.qlen++;
++ qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
++ q->cstats.drop_len);
++ if (skb)
++ sch->q.qlen--;
++ q->cstats.drop_count = 0;
++ q->cstats.drop_len = 0;
++ }
++
+ if (!skb) {
+ /* force a pass through old_flows to prevent starvation */
+ if ((head == &q->new_flows) && !list_empty(&q->old_flows))
+@@ -314,15 +329,6 @@ begin:
+ }
+ qdisc_bstats_update(sch, skb);
+ flow->deficit -= qdisc_pkt_len(skb);
+- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
+- * or HTB crashes. Defer it for next round.
+- */
+- if (q->cstats.drop_count && sch->q.qlen) {
+- qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
+- q->cstats.drop_len);
+- q->cstats.drop_count = 0;
+- q->cstats.drop_len = 0;
+- }
+ return skb;
+ }
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: net: add an optimization for dealing with raw sockets
+
+lede-commit: 4898039703d7315f0f3431c860123338ec3be0f6
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/uapi/linux/if_packet.h | 3 +++
+ net/packet/af_packet.c | 34 +++++++++++++++++++++++++++-------
+ net/packet/internal.h | 1 +
+ 3 files changed, 31 insertions(+), 7 deletions(-)
+
+--- a/include/uapi/linux/if_packet.h
++++ b/include/uapi/linux/if_packet.h
+@@ -33,6 +33,8 @@ struct sockaddr_ll {
+ #define PACKET_KERNEL 7 /* To kernel space */
+ /* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */
+ #define PACKET_FASTROUTE 6 /* Fastrouted frame */
++#define PACKET_MASK_ANY 0xffffffff /* mask for packet type bits */
++
+
+ /* Packet socket options */
+
+@@ -59,6 +61,7 @@ struct sockaddr_ll {
+ #define PACKET_ROLLOVER_STATS 21
+ #define PACKET_FANOUT_DATA 22
+ #define PACKET_IGNORE_OUTGOING 23
++#define PACKET_RECV_TYPE 24
+
+ #define PACKET_FANOUT_HASH 0
+ #define PACKET_FANOUT_LB 1
+--- a/net/packet/af_packet.c
++++ b/net/packet/af_packet.c
+@@ -1822,6 +1822,7 @@ static int packet_rcv_spkt(struct sk_buf
+ {
+ struct sock *sk;
+ struct sockaddr_pkt *spkt;
++ struct packet_sock *po;
+
+ /*
+ * When we registered the protocol we saved the socket in the data
+@@ -1829,6 +1830,7 @@ static int packet_rcv_spkt(struct sk_buf
+ */
+
+ sk = pt->af_packet_priv;
++ po = pkt_sk(sk);
+
+ /*
+ * Yank back the headers [hope the device set this
+@@ -1841,7 +1843,7 @@ static int packet_rcv_spkt(struct sk_buf
+ * so that this procedure is noop.
+ */
+
+- if (skb->pkt_type == PACKET_LOOPBACK)
++ if (!(po->pkt_type & (1 << skb->pkt_type)))
+ goto out;
+
+ if (!net_eq(dev_net(dev), sock_net(sk)))
+@@ -2079,12 +2081,12 @@ static int packet_rcv(struct sk_buff *sk
+ unsigned int snaplen, res;
+ bool is_drop_n_account = false;
+
+- if (skb->pkt_type == PACKET_LOOPBACK)
+- goto drop;
+-
+ sk = pt->af_packet_priv;
+ po = pkt_sk(sk);
+
++ if (!(po->pkt_type & (1 << skb->pkt_type)))
++ goto drop;
++
+ if (!net_eq(dev_net(dev), sock_net(sk)))
+ goto drop;
+
+@@ -2210,12 +2212,12 @@ static int tpacket_rcv(struct sk_buff *s
+ BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h2)) != 32);
+ BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h3)) != 48);
+
+- if (skb->pkt_type == PACKET_LOOPBACK)
+- goto drop;
+-
+ sk = pt->af_packet_priv;
+ po = pkt_sk(sk);
+
++ if (!(po->pkt_type & (1 << skb->pkt_type)))
++ goto drop;
++
+ if (!net_eq(dev_net(dev), sock_net(sk)))
+ goto drop;
+
+@@ -3325,6 +3327,7 @@ static int packet_create(struct net *net
+ mutex_init(&po->pg_vec_lock);
+ po->rollover = NULL;
+ po->prot_hook.func = packet_rcv;
++ po->pkt_type = PACKET_MASK_ANY & ~(1 << PACKET_LOOPBACK);
+
+ if (sock->type == SOCK_PACKET)
+ po->prot_hook.func = packet_rcv_spkt;
+@@ -3969,6 +3972,16 @@ packet_setsockopt(struct socket *sock, i
+ po->xmit = val ? packet_direct_xmit : dev_queue_xmit;
+ return 0;
+ }
++ case PACKET_RECV_TYPE:
++ {
++ unsigned int val;
++ if (optlen != sizeof(val))
++ return -EINVAL;
++ if (copy_from_sockptr(&val, optval, sizeof(val)))
++ return -EFAULT;
++ po->pkt_type = val & ~BIT(PACKET_LOOPBACK);
++ return 0;
++ }
+ default:
+ return -ENOPROTOOPT;
+ }
+@@ -4025,6 +4038,13 @@ static int packet_getsockopt(struct sock
+ case PACKET_VNET_HDR:
+ val = po->has_vnet_hdr;
+ break;
++ case PACKET_RECV_TYPE:
++ if (len > sizeof(unsigned int))
++ len = sizeof(unsigned int);
++ val = po->pkt_type;
++
++ data = &val;
++ break;
+ case PACKET_VERSION:
+ val = po->tp_version;
+ break;
+--- a/net/packet/internal.h
++++ b/net/packet/internal.h
+@@ -137,6 +137,7 @@ struct packet_sock {
+ int (*xmit)(struct sk_buff *skb);
+ struct packet_type prot_hook ____cacheline_aligned_in_smp;
+ atomic_t tp_drops ____cacheline_aligned_in_smp;
++ unsigned int pkt_type;
+ };
+
+ static struct packet_sock *pkt_sk(struct sock *sk)
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: add a few patches for avoiding unnecessary skb reallocations - significantly improves ethernet<->wireless performance
+
+lede-commit: 6f89cffc9add6939d44a6b54cf9a5e77849aa7fd
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/skbuff.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -2676,7 +2676,7 @@ static inline int pskb_network_may_pull(
+ * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8)
+ */
+ #ifndef NET_SKB_PAD
+-#define NET_SKB_PAD max(32, L1_CACHE_BYTES)
++#define NET_SKB_PAD max(64, L1_CACHE_BYTES)
+ #endif
+
+ int ___pskb_trim(struct sk_buff *skb, unsigned int len);
--- /dev/null
+From: Steven Barth <steven@midlink.org>
+Subject: Add support for MAP-E FMRs (mesh mode)
+
+MAP-E FMRs (draft-ietf-softwire-map-10) are rules for IPv4-communication
+between MAP CEs (mesh mode) without the need to forward such data to a
+border relay. This is similar to how 6rd works but for IPv4 over IPv6.
+
+Signed-off-by: Steven Barth <cyrus@openwrt.org>
+---
+ include/net/ip6_tunnel.h | 13 ++
+ include/uapi/linux/if_tunnel.h | 13 ++
+ net/ipv6/ip6_tunnel.c | 276 +++++++++++++++++++++++++++++++++++++++--
+ 3 files changed, 291 insertions(+), 11 deletions(-)
+
+--- a/include/net/ip6_tunnel.h
++++ b/include/net/ip6_tunnel.h
+@@ -18,6 +18,18 @@
+ /* determine capability on a per-packet basis */
+ #define IP6_TNL_F_CAP_PER_PACKET 0x40000
+
++/* IPv6 tunnel FMR */
++struct __ip6_tnl_fmr {
++ struct __ip6_tnl_fmr *next; /* next fmr in list */
++ struct in6_addr ip6_prefix;
++ struct in_addr ip4_prefix;
++
++ __u8 ip6_prefix_len;
++ __u8 ip4_prefix_len;
++ __u8 ea_len;
++ __u8 offset;
++};
++
+ struct __ip6_tnl_parm {
+ char name[IFNAMSIZ]; /* name of tunnel device */
+ int link; /* ifindex of underlying L2 interface */
+@@ -29,6 +41,7 @@ struct __ip6_tnl_parm {
+ __u32 flags; /* tunnel flags */
+ struct in6_addr laddr; /* local tunnel end-point address */
+ struct in6_addr raddr; /* remote tunnel end-point address */
++ struct __ip6_tnl_fmr *fmrs; /* FMRs */
+
+ __be16 i_flags;
+ __be16 o_flags;
+--- a/include/uapi/linux/if_tunnel.h
++++ b/include/uapi/linux/if_tunnel.h
+@@ -77,10 +77,23 @@ enum {
+ IFLA_IPTUN_ENCAP_DPORT,
+ IFLA_IPTUN_COLLECT_METADATA,
+ IFLA_IPTUN_FWMARK,
++ IFLA_IPTUN_FMRS,
+ __IFLA_IPTUN_MAX,
+ };
+ #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
+
++enum {
++ IFLA_IPTUN_FMR_UNSPEC,
++ IFLA_IPTUN_FMR_IP6_PREFIX,
++ IFLA_IPTUN_FMR_IP4_PREFIX,
++ IFLA_IPTUN_FMR_IP6_PREFIX_LEN,
++ IFLA_IPTUN_FMR_IP4_PREFIX_LEN,
++ IFLA_IPTUN_FMR_EA_LEN,
++ IFLA_IPTUN_FMR_OFFSET,
++ __IFLA_IPTUN_FMR_MAX,
++};
++#define IFLA_IPTUN_FMR_MAX (__IFLA_IPTUN_FMR_MAX - 1)
++
+ enum tunnel_encap_types {
+ TUNNEL_ENCAP_NONE,
+ TUNNEL_ENCAP_FOU,
+--- a/net/ipv6/ip6_tunnel.c
++++ b/net/ipv6/ip6_tunnel.c
+@@ -11,6 +11,9 @@
+ * linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c
+ *
+ * RFC 2473
++ *
++ * Changes:
++ * Steven Barth <cyrus@openwrt.org>: MAP-E FMR support
+ */
+
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+@@ -67,9 +70,9 @@ static bool log_ecn_error = true;
+ module_param(log_ecn_error, bool, 0644);
+ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
+
+-static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2)
++static u32 HASH(const struct in6_addr *addr)
+ {
+- u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2);
++ u32 hash = ipv6_addr_hash(addr);
+
+ return hash_32(hash, IP6_TUNNEL_HASH_SIZE_SHIFT);
+ }
+@@ -144,17 +147,33 @@ static struct ip6_tnl *
+ ip6_tnl_lookup(struct net *net, int link,
+ const struct in6_addr *remote, const struct in6_addr *local)
+ {
+- unsigned int hash = HASH(remote, local);
++ unsigned int hash = HASH(local);
+ struct ip6_tnl *t, *cand = NULL;
+ struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ struct in6_addr any;
+
+ for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+ if (!ipv6_addr_equal(local, &t->parms.laddr) ||
+- !ipv6_addr_equal(remote, &t->parms.raddr) ||
+ !(t->dev->flags & IFF_UP))
+ continue;
+
++ if (!ipv6_addr_equal(remote, &t->parms.raddr)) {
++ struct __ip6_tnl_fmr *fmr;
++ bool found = false;
++
++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) {
++ if (!ipv6_prefix_equal(remote, &fmr->ip6_prefix,
++ fmr->ip6_prefix_len))
++ continue;
++
++ found = true;
++ break;
++ }
++
++ if (!found)
++ continue;
++ }
++
+ if (link == t->parms.link)
+ return t;
+ else
+@@ -162,7 +181,7 @@ ip6_tnl_lookup(struct net *net, int link
+ }
+
+ memset(&any, 0, sizeof(any));
+- hash = HASH(&any, local);
++ hash = HASH(local);
+ for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+ if (!ipv6_addr_equal(local, &t->parms.laddr) ||
+ !ipv6_addr_any(&t->parms.raddr) ||
+@@ -175,7 +194,7 @@ ip6_tnl_lookup(struct net *net, int link
+ cand = t;
+ }
+
+- hash = HASH(remote, &any);
++ hash = HASH(&any);
+ for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+ if (!ipv6_addr_equal(remote, &t->parms.raddr) ||
+ !ipv6_addr_any(&t->parms.laddr) ||
+@@ -223,7 +242,7 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n,
+
+ if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) {
+ prio = 1;
+- h = HASH(remote, local);
++ h = HASH(local);
+ }
+ return &ip6n->tnls[prio][h];
+ }
+@@ -405,6 +424,12 @@ ip6_tnl_dev_uninit(struct net_device *de
+ struct net *net = t->net;
+ struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+
++ while (t->parms.fmrs) {
++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next;
++ kfree(t->parms.fmrs);
++ t->parms.fmrs = next;
++ }
++
+ if (dev == ip6n->fb_tnl_dev)
+ RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL);
+ else
+@@ -821,6 +846,107 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
+ }
+ EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl);
+
++/**
++ * ip4ip6_fmr_calc - calculate target / source IPv6-address based on FMR
++ * @dest: destination IPv6 address buffer
++ * @skb: received socket buffer
++ * @fmr: MAP FMR
++ * @xmit: Calculate for xmit or rcv
++ **/
++static void ip4ip6_fmr_calc(struct in6_addr *dest,
++ const struct iphdr *iph, const uint8_t *end,
++ const struct __ip6_tnl_fmr *fmr, bool xmit)
++{
++ int psidlen = fmr->ea_len - (32 - fmr->ip4_prefix_len);
++ u8 *portp = NULL;
++ bool use_dest_addr;
++ const struct iphdr *dsth = iph;
++
++ if ((u8*)dsth >= end)
++ return;
++
++ /* find significant IP header */
++ if (iph->protocol == IPPROTO_ICMP) {
++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4);
++ if (ih && ((u8*)&ih[1]) <= end && (
++ ih->type == ICMP_DEST_UNREACH ||
++ ih->type == ICMP_SOURCE_QUENCH ||
++ ih->type == ICMP_TIME_EXCEEDED ||
++ ih->type == ICMP_PARAMETERPROB ||
++ ih->type == ICMP_REDIRECT))
++ dsth = (const struct iphdr*)&ih[1];
++ }
++
++ /* in xmit-path use dest port by default and source port only if
++ this is an ICMP reply to something else; vice versa in rcv-path */
++ use_dest_addr = (xmit && dsth == iph) || (!xmit && dsth != iph);
++
++ /* get dst port */
++ if (((u8*)&dsth[1]) <= end && (
++ dsth->protocol == IPPROTO_UDP ||
++ dsth->protocol == IPPROTO_TCP ||
++ dsth->protocol == IPPROTO_SCTP ||
++ dsth->protocol == IPPROTO_DCCP)) {
++ /* for UDP, TCP, SCTP and DCCP source and dest port
++ follow IPv4 header directly */
++ portp = ((u8*)dsth) + dsth->ihl * 4;
++
++ if (use_dest_addr)
++ portp += sizeof(u16);
++ } else if (iph->protocol == IPPROTO_ICMP) {
++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4);
++
++ /* use icmp identifier as port */
++ if (((u8*)&ih) <= end && (
++ (use_dest_addr && (
++ ih->type == ICMP_ECHOREPLY ||
++ ih->type == ICMP_TIMESTAMPREPLY ||
++ ih->type == ICMP_INFO_REPLY ||
++ ih->type == ICMP_ADDRESSREPLY)) ||
++ (!use_dest_addr && (
++ ih->type == ICMP_ECHO ||
++ ih->type == ICMP_TIMESTAMP ||
++ ih->type == ICMP_INFO_REQUEST ||
++ ih->type == ICMP_ADDRESS)
++ )))
++ portp = (u8*)&ih->un.echo.id;
++ }
++
++ if ((portp && &portp[2] <= end) || psidlen == 0) {
++ int frombyte = fmr->ip6_prefix_len / 8;
++ int fromrem = fmr->ip6_prefix_len % 8;
++ int bytes = sizeof(struct in6_addr) - frombyte;
++ const u32 *addr = (use_dest_addr) ? &iph->daddr : &iph->saddr;
++ u64 eabits = ((u64)ntohl(*addr)) << (32 + fmr->ip4_prefix_len);
++ u64 t = 0;
++
++ /* extract PSID from port and add it to eabits */
++ u16 psidbits = 0;
++ if (psidlen > 0) {
++ psidbits = ((u16)portp[0]) << 8 | ((u16)portp[1]);
++ psidbits >>= 16 - psidlen - fmr->offset;
++ psidbits = (u16)(psidbits << (16 - psidlen));
++ eabits |= ((u64)psidbits) << (48 - (fmr->ea_len - psidlen));
++ }
++
++ /* rewrite destination address */
++ *dest = fmr->ip6_prefix;
++ memcpy(&dest->s6_addr[10], addr, sizeof(*addr));
++ dest->s6_addr16[7] = htons(psidbits >> (16 - psidlen));
++
++ if (bytes > sizeof(u64))
++ bytes = sizeof(u64);
++
++ /* insert eabits */
++ memcpy(&t, &dest->s6_addr[frombyte], bytes);
++ t = be64_to_cpu(t) & ~(((((u64)1) << fmr->ea_len) - 1)
++ << (64 - fmr->ea_len - fromrem));
++ t = cpu_to_be64(t | (eabits >> fromrem));
++ memcpy(&dest->s6_addr[frombyte], &t, bytes);
++ }
++}
++
++
+ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
+ const struct tnl_ptk_info *tpi,
+ struct metadata_dst *tun_dst,
+@@ -873,6 +999,27 @@ static int __ip6_tnl_rcv(struct ip6_tnl
+ skb_reset_network_header(skb);
+ memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
+
++ if (tpi->proto == htons(ETH_P_IP) && tunnel->parms.fmrs &&
++ !ipv6_addr_equal(&ipv6h->saddr, &tunnel->parms.raddr)) {
++ /* Packet didn't come from BR, so lookup FMR */
++ struct __ip6_tnl_fmr *fmr;
++ struct in6_addr expected = tunnel->parms.raddr;
++ for (fmr = tunnel->parms.fmrs; fmr; fmr = fmr->next)
++ if (ipv6_prefix_equal(&ipv6h->saddr,
++ &fmr->ip6_prefix, fmr->ip6_prefix_len))
++ break;
++
++ /* Check that IPv6 matches IPv4 source to prevent spoofing */
++ if (fmr)
++ ip4ip6_fmr_calc(&expected, ip_hdr(skb),
++ skb_tail_pointer(skb), fmr, false);
++
++ if (!ipv6_addr_equal(&ipv6h->saddr, &expected)) {
++ rcu_read_unlock();
++ goto drop;
++ }
++ }
++
+ __skb_tunnel_rx(skb, tunnel->dev, tunnel->net);
+
+ err = dscp_ecn_decapsulate(tunnel, ipv6h, skb);
+@@ -1024,6 +1171,7 @@ static void init_tel_txopt(struct ipv6_t
+ opt->ops.opt_nflen = 8;
+ }
+
++
+ /**
+ * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own
+ * @t: the outgoing tunnel device
+@@ -1304,6 +1452,7 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str
+ u8 protocol)
+ {
+ struct ip6_tnl *t = netdev_priv(dev);
++ struct __ip6_tnl_fmr *fmr;
+ struct ipv6hdr *ipv6h;
+ const struct iphdr *iph;
+ int encap_limit = -1;
+@@ -1403,6 +1552,18 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+ dsfield = INET_ECN_encapsulate(dsfield, orig_dsfield);
+
++ /* try to find matching FMR */
++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) {
++ unsigned mshift = 32 - fmr->ip4_prefix_len;
++ if (ntohl(fmr->ip4_prefix.s_addr) >> mshift ==
++ ntohl(ip_hdr(skb)->daddr) >> mshift)
++ break;
++ }
++
++ /* change dstaddr according to FMR */
++ if (fmr)
++ ip4ip6_fmr_calc(&fl6.daddr, ip_hdr(skb), skb_tail_pointer(skb), fmr, true);
++
+ if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6))
+ return -1;
+
+@@ -1555,6 +1716,14 @@ ip6_tnl_change(struct ip6_tnl *t, const
+ t->parms.link = p->link;
+ t->parms.proto = p->proto;
+ t->parms.fwmark = p->fwmark;
++
++ while (t->parms.fmrs) {
++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next;
++ kfree(t->parms.fmrs);
++ t->parms.fmrs = next;
++ }
++ t->parms.fmrs = p->fmrs;
++
+ dst_cache_reset(&t->dst_cache);
+ ip6_tnl_link_config(t);
+ return 0;
+@@ -1593,6 +1762,7 @@ ip6_tnl_parm_from_user(struct __ip6_tnl_
+ p->flowinfo = u->flowinfo;
+ p->link = u->link;
+ p->proto = u->proto;
++ p->fmrs = NULL;
+ memcpy(p->name, u->name, sizeof(u->name));
+ }
+
+@@ -1978,6 +2148,15 @@ static int ip6_tnl_validate(struct nlatt
+ return 0;
+ }
+
++static const struct nla_policy ip6_tnl_fmr_policy[IFLA_IPTUN_FMR_MAX + 1] = {
++ [IFLA_IPTUN_FMR_IP6_PREFIX] = { .len = sizeof(struct in6_addr) },
++ [IFLA_IPTUN_FMR_IP4_PREFIX] = { .len = sizeof(struct in_addr) },
++ [IFLA_IPTUN_FMR_IP6_PREFIX_LEN] = { .type = NLA_U8 },
++ [IFLA_IPTUN_FMR_IP4_PREFIX_LEN] = { .type = NLA_U8 },
++ [IFLA_IPTUN_FMR_EA_LEN] = { .type = NLA_U8 },
++ [IFLA_IPTUN_FMR_OFFSET] = { .type = NLA_U8 }
++};
++
+ static void ip6_tnl_netlink_parms(struct nlattr *data[],
+ struct __ip6_tnl_parm *parms)
+ {
+@@ -2015,6 +2194,46 @@ static void ip6_tnl_netlink_parms(struct
+
+ if (data[IFLA_IPTUN_FWMARK])
+ parms->fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]);
++
++ if (data[IFLA_IPTUN_FMRS]) {
++ unsigned rem;
++ struct nlattr *fmr;
++ nla_for_each_nested(fmr, data[IFLA_IPTUN_FMRS], rem) {
++ struct nlattr *fmrd[IFLA_IPTUN_FMR_MAX + 1], *c;
++ struct __ip6_tnl_fmr *nfmr;
++
++ nla_parse_nested(fmrd, IFLA_IPTUN_FMR_MAX,
++ fmr, ip6_tnl_fmr_policy, NULL);
++
++ if (!(nfmr = kzalloc(sizeof(*nfmr), GFP_KERNEL)))
++ continue;
++
++ nfmr->offset = 6;
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX]))
++ nla_memcpy(&nfmr->ip6_prefix, fmrd[IFLA_IPTUN_FMR_IP6_PREFIX],
++ sizeof(nfmr->ip6_prefix));
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX]))
++ nla_memcpy(&nfmr->ip4_prefix, fmrd[IFLA_IPTUN_FMR_IP4_PREFIX],
++ sizeof(nfmr->ip4_prefix));
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX_LEN]))
++ nfmr->ip6_prefix_len = nla_get_u8(c);
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX_LEN]))
++ nfmr->ip4_prefix_len = nla_get_u8(c);
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_EA_LEN]))
++ nfmr->ea_len = nla_get_u8(c);
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_OFFSET]))
++ nfmr->offset = nla_get_u8(c);
++
++ nfmr->next = parms->fmrs;
++ parms->fmrs = nfmr;
++ }
++ }
+ }
+
+ static bool ip6_tnl_netlink_encap_parms(struct nlattr *data[],
+@@ -2130,6 +2349,12 @@ static void ip6_tnl_dellink(struct net_d
+
+ static size_t ip6_tnl_get_size(const struct net_device *dev)
+ {
++ const struct ip6_tnl *t = netdev_priv(dev);
++ struct __ip6_tnl_fmr *c;
++ int fmrs = 0;
++ for (c = t->parms.fmrs; c; c = c->next)
++ ++fmrs;
++
+ return
+ /* IFLA_IPTUN_LINK */
+ nla_total_size(4) +
+@@ -2159,6 +2384,24 @@ static size_t ip6_tnl_get_size(const str
+ nla_total_size(0) +
+ /* IFLA_IPTUN_FWMARK */
+ nla_total_size(4) +
++ /* IFLA_IPTUN_FMRS */
++ nla_total_size(0) +
++ (
++ /* nest */
++ nla_total_size(0) +
++ /* IFLA_IPTUN_FMR_IP6_PREFIX */
++ nla_total_size(sizeof(struct in6_addr)) +
++ /* IFLA_IPTUN_FMR_IP4_PREFIX */
++ nla_total_size(sizeof(struct in_addr)) +
++ /* IFLA_IPTUN_FMR_EA_LEN */
++ nla_total_size(1) +
++ /* IFLA_IPTUN_FMR_IP6_PREFIX_LEN */
++ nla_total_size(1) +
++ /* IFLA_IPTUN_FMR_IP4_PREFIX_LEN */
++ nla_total_size(1) +
++ /* IFLA_IPTUN_FMR_OFFSET */
++ nla_total_size(1)
++ ) * fmrs +
+ 0;
+ }
+
+@@ -2166,6 +2409,9 @@ static int ip6_tnl_fill_info(struct sk_b
+ {
+ struct ip6_tnl *tunnel = netdev_priv(dev);
+ struct __ip6_tnl_parm *parm = &tunnel->parms;
++ struct __ip6_tnl_fmr *c;
++ int fmrcnt = 0;
++ struct nlattr *fmrs;
+
+ if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
+ nla_put_in6_addr(skb, IFLA_IPTUN_LOCAL, &parm->laddr) ||
+@@ -2175,9 +2421,27 @@ static int ip6_tnl_fill_info(struct sk_b
+ nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) ||
+ nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) ||
+ nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto) ||
+- nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark))
++ nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark) ||
++ !(fmrs = nla_nest_start(skb, IFLA_IPTUN_FMRS)))
+ goto nla_put_failure;
+
++ for (c = parm->fmrs; c; c = c->next) {
++ struct nlattr *fmr = nla_nest_start(skb, ++fmrcnt);
++ if (!fmr ||
++ nla_put(skb, IFLA_IPTUN_FMR_IP6_PREFIX,
++ sizeof(c->ip6_prefix), &c->ip6_prefix) ||
++ nla_put(skb, IFLA_IPTUN_FMR_IP4_PREFIX,
++ sizeof(c->ip4_prefix), &c->ip4_prefix) ||
++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP6_PREFIX_LEN, c->ip6_prefix_len) ||
++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP4_PREFIX_LEN, c->ip4_prefix_len) ||
++ nla_put_u8(skb, IFLA_IPTUN_FMR_EA_LEN, c->ea_len) ||
++ nla_put_u8(skb, IFLA_IPTUN_FMR_OFFSET, c->offset))
++ goto nla_put_failure;
++
++ nla_nest_end(skb, fmr);
++ }
++ nla_nest_end(skb, fmrs);
++
+ if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, tunnel->encap.type) ||
+ nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT, tunnel->encap.sport) ||
+ nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT, tunnel->encap.dport) ||
+@@ -2217,6 +2481,7 @@ static const struct nla_policy ip6_tnl_p
+ [IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 },
+ [IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG },
+ [IFLA_IPTUN_FWMARK] = { .type = NLA_U32 },
++ [IFLA_IPTUN_FMRS] = { .type = NLA_NESTED },
+ };
+
+ static struct rtnl_link_ops ip6_link_ops __read_mostly = {
--- /dev/null
+From: Jonas Gorski <jogo@openwrt.org>
+Subject: ipv6: allow rejecting with "source address failed policy"
+
+RFC6204 L-14 requires rejecting traffic from invalid addresses with
+ICMPv6 Destination Unreachable, Code 5 (Source address failed ingress/
+egress policy) on the LAN side, so add an appropriate rule for that.
+
+Signed-off-by: Jonas Gorski <jogo@openwrt.org>
+---
+ include/net/netns/ipv6.h | 1 +
+ include/uapi/linux/fib_rules.h | 4 +++
+ include/uapi/linux/rtnetlink.h | 1 +
+ net/ipv4/fib_semantics.c | 4 +++
+ net/ipv4/fib_trie.c | 1 +
+ net/ipv4/ipmr.c | 1 +
+ net/ipv6/fib6_rules.c | 4 +++
+ net/ipv6/ip6mr.c | 2 ++
+ net/ipv6/route.c | 58 +++++++++++++++++++++++++++++++++++++++++-
+ 9 files changed, 75 insertions(+), 1 deletion(-)
+
+--- a/include/net/netns/ipv6.h
++++ b/include/net/netns/ipv6.h
+@@ -88,6 +88,7 @@ struct netns_ipv6 {
+ unsigned int fib6_routes_require_src;
+ #endif
+ struct rt6_info *ip6_prohibit_entry;
++ struct rt6_info *ip6_policy_failed_entry;
+ struct rt6_info *ip6_blk_hole_entry;
+ struct fib6_table *fib6_local_tbl;
+ struct fib_rules_ops *fib6_rules_ops;
+--- a/include/uapi/linux/fib_rules.h
++++ b/include/uapi/linux/fib_rules.h
+@@ -82,6 +82,10 @@ enum {
+ FR_ACT_BLACKHOLE, /* Drop without notification */
+ FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */
+ FR_ACT_PROHIBIT, /* Drop with EACCES */
++ FR_ACT_RES9,
++ FR_ACT_RES10,
++ FR_ACT_RES11,
++ FR_ACT_POLICY_FAILED, /* Drop with EACCES */
+ __FR_ACT_MAX,
+ };
+
+--- a/include/uapi/linux/rtnetlink.h
++++ b/include/uapi/linux/rtnetlink.h
+@@ -249,6 +249,7 @@ enum {
+ RTN_THROW, /* Not in this table */
+ RTN_NAT, /* Translate this address */
+ RTN_XRESOLVE, /* Use external resolver */
++ RTN_POLICY_FAILED, /* Failed ingress/egress policy */
+ __RTN_MAX
+ };
+
+--- a/net/ipv4/fib_semantics.c
++++ b/net/ipv4/fib_semantics.c
+@@ -142,6 +142,10 @@ const struct fib_prop fib_props[RTN_MAX
+ .error = -EINVAL,
+ .scope = RT_SCOPE_NOWHERE,
+ },
++ [RTN_POLICY_FAILED] = {
++ .error = -EACCES,
++ .scope = RT_SCOPE_UNIVERSE,
++ },
+ };
+
+ static void rt_fibinfo_free(struct rtable __rcu **rtp)
+--- a/net/ipv4/fib_trie.c
++++ b/net/ipv4/fib_trie.c
+@@ -2734,6 +2734,7 @@ static const char *const rtn_type_names[
+ [RTN_THROW] = "THROW",
+ [RTN_NAT] = "NAT",
+ [RTN_XRESOLVE] = "XRESOLVE",
++ [RTN_POLICY_FAILED] = "POLICY_FAILED",
+ };
+
+ static inline const char *rtn_type(char *buf, size_t len, unsigned int t)
+--- a/net/ipv4/ipmr.c
++++ b/net/ipv4/ipmr.c
+@@ -175,6 +175,7 @@ static int ipmr_rule_action(struct fib_r
+ case FR_ACT_UNREACHABLE:
+ return -ENETUNREACH;
+ case FR_ACT_PROHIBIT:
++ case FR_ACT_POLICY_FAILED:
+ return -EACCES;
+ case FR_ACT_BLACKHOLE:
+ default:
+--- a/net/ipv6/fib6_rules.c
++++ b/net/ipv6/fib6_rules.c
+@@ -220,6 +220,10 @@ static int __fib6_rule_action(struct fib
+ err = -EACCES;
+ rt = net->ipv6.ip6_prohibit_entry;
+ goto discard_pkt;
++ case FR_ACT_POLICY_FAILED:
++ err = -EACCES;
++ rt = net->ipv6.ip6_policy_failed_entry;
++ goto discard_pkt;
+ }
+
+ tb_id = fib_rule_get_table(rule, arg);
+--- a/net/ipv6/ip6mr.c
++++ b/net/ipv6/ip6mr.c
+@@ -163,6 +163,8 @@ static int ip6mr_rule_action(struct fib_
+ return -ENETUNREACH;
+ case FR_ACT_PROHIBIT:
+ return -EACCES;
++ case FR_ACT_POLICY_FAILED:
++ return -EACCES;
+ case FR_ACT_BLACKHOLE:
+ default:
+ return -EINVAL;
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -95,6 +95,8 @@ static int ip6_pkt_discard(struct sk_bu
+ static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
+ static int ip6_pkt_prohibit(struct sk_buff *skb);
+ static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
++static int ip6_pkt_policy_failed(struct sk_buff *skb);
++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb);
+ static void ip6_link_failure(struct sk_buff *skb);
+ static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
+ struct sk_buff *skb, u32 mtu,
+@@ -310,6 +312,18 @@ static const struct rt6_info ip6_prohibi
+ .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
+ };
+
++static const struct rt6_info ip6_policy_failed_entry_template = {
++ .dst = {
++ .__refcnt = ATOMIC_INIT(1),
++ .__use = 1,
++ .obsolete = DST_OBSOLETE_FORCE_CHK,
++ .error = -EACCES,
++ .input = ip6_pkt_policy_failed,
++ .output = ip6_pkt_policy_failed_out,
++ },
++ .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
++};
++
+ static const struct rt6_info ip6_blk_hole_entry_template = {
+ .dst = {
+ .__refcnt = ATOMIC_INIT(1),
+@@ -1031,6 +1045,7 @@ static const int fib6_prop[RTN_MAX + 1]
+ [RTN_BLACKHOLE] = -EINVAL,
+ [RTN_UNREACHABLE] = -EHOSTUNREACH,
+ [RTN_PROHIBIT] = -EACCES,
++ [RTN_POLICY_FAILED] = -EACCES,
+ [RTN_THROW] = -EAGAIN,
+ [RTN_NAT] = -EINVAL,
+ [RTN_XRESOLVE] = -EINVAL,
+@@ -1066,6 +1081,10 @@ static void ip6_rt_init_dst_reject(struc
+ rt->dst.output = ip6_pkt_prohibit_out;
+ rt->dst.input = ip6_pkt_prohibit;
+ break;
++ case RTN_POLICY_FAILED:
++ rt->dst.output = ip6_pkt_policy_failed_out;
++ rt->dst.input = ip6_pkt_policy_failed;
++ break;
+ case RTN_THROW:
+ case RTN_UNREACHABLE:
+ default:
+@@ -4448,6 +4467,17 @@ static int ip6_pkt_prohibit_out(struct n
+ return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
+ }
+
++static int ip6_pkt_policy_failed(struct sk_buff *skb)
++{
++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_INNOROUTES);
++}
++
++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb)
++{
++ skb->dev = skb_dst(skb)->dev;
++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_OUTNOROUTES);
++}
++
+ /*
+ * Allocate a dst for local (unicast / anycast) address.
+ */
+@@ -4928,7 +4958,8 @@ static int rtm_to_fib6_config(struct sk_
+ if (rtm->rtm_type == RTN_UNREACHABLE ||
+ rtm->rtm_type == RTN_BLACKHOLE ||
+ rtm->rtm_type == RTN_PROHIBIT ||
+- rtm->rtm_type == RTN_THROW)
++ rtm->rtm_type == RTN_THROW ||
++ rtm->rtm_type == RTN_POLICY_FAILED)
+ cfg->fc_flags |= RTF_REJECT;
+
+ if (rtm->rtm_type == RTN_LOCAL)
+@@ -6127,6 +6158,8 @@ static int ip6_route_dev_notify(struct n
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ net->ipv6.ip6_prohibit_entry->dst.dev = dev;
+ net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
++ net->ipv6.ip6_policy_failed_entry->dst.dev = dev;
++ net->ipv6.ip6_policy_failed_entry->rt6i_idev = in6_dev_get(dev);
+ net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
+ net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
+ #endif
+@@ -6138,6 +6171,7 @@ static int ip6_route_dev_notify(struct n
+ in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
++ in6_dev_put_clear(&net->ipv6.ip6_policy_failed_entry->rt6i_idev);
+ in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
+ #endif
+ }
+@@ -6329,6 +6363,8 @@ static int __net_init ip6_route_net_init
+
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ net->ipv6.fib6_has_custom_rules = false;
++
++
+ net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
+ sizeof(*net->ipv6.ip6_prohibit_entry),
+ GFP_KERNEL);
+@@ -6339,11 +6375,21 @@ static int __net_init ip6_route_net_init
+ ip6_template_metrics, true);
+ INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached);
+
++ net->ipv6.ip6_policy_failed_entry =
++ kmemdup(&ip6_policy_failed_entry_template,
++ sizeof(*net->ipv6.ip6_policy_failed_entry), GFP_KERNEL);
++ if (!net->ipv6.ip6_policy_failed_entry)
++ goto out_ip6_prohibit_entry;
++ net->ipv6.ip6_policy_failed_entry->dst.ops = &net->ipv6.ip6_dst_ops;
++ dst_init_metrics(&net->ipv6.ip6_policy_failed_entry->dst,
++ ip6_template_metrics, true);
++ INIT_LIST_HEAD(&net->ipv6.ip6_policy_failed_entry->rt6i_uncached);
++
+ net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
+ sizeof(*net->ipv6.ip6_blk_hole_entry),
+ GFP_KERNEL);
+ if (!net->ipv6.ip6_blk_hole_entry)
+- goto out_ip6_prohibit_entry;
++ goto out_ip6_policy_failed_entry;
+ net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
+ dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
+ ip6_template_metrics, true);
+@@ -6370,6 +6416,8 @@ out:
+ return ret;
+
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
++out_ip6_policy_failed_entry:
++ kfree(net->ipv6.ip6_policy_failed_entry);
+ out_ip6_prohibit_entry:
+ kfree(net->ipv6.ip6_prohibit_entry);
+ out_ip6_null_entry:
+@@ -6389,6 +6437,7 @@ static void __net_exit ip6_route_net_exi
+ kfree(net->ipv6.ip6_null_entry);
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ kfree(net->ipv6.ip6_prohibit_entry);
++ kfree(net->ipv6.ip6_policy_failed_entry);
+ kfree(net->ipv6.ip6_blk_hole_entry);
+ #endif
+ dst_entries_destroy(&net->ipv6.ip6_dst_ops);
+@@ -6466,6 +6515,9 @@ void __init ip6_route_init_special_entri
+ init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
+ init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
+ init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
++ init_net.ipv6.ip6_policy_failed_entry->dst.dev = init_net.loopback_dev;
++ init_net.ipv6.ip6_policy_failed_entry->rt6i_idev =
++ in6_dev_get(init_net.loopback_dev);
+ #endif
+ }
+
--- /dev/null
+From: Jonas Gorski <jogo@openwrt.org>
+Subject: net: provide defines for _POLICY_FAILED until all code is updated
+
+Upstream introduced ICMPV6_POLICY_FAIL for code 5 of destination
+unreachable, conflicting with our name.
+
+Add appropriate defines to allow our code to build with the new
+name until we have updated our local patches for older kernels
+and userspace packages.
+
+Signed-off-by: Jonas Gorski <jogo@openwrt.org>
+---
+ include/uapi/linux/fib_rules.h | 2 ++
+ include/uapi/linux/icmpv6.h | 2 ++
+ include/uapi/linux/rtnetlink.h | 2 ++
+ 3 files changed, 6 insertions(+)
+
+--- a/include/uapi/linux/fib_rules.h
++++ b/include/uapi/linux/fib_rules.h
+@@ -89,6 +89,8 @@ enum {
+ __FR_ACT_MAX,
+ };
+
++#define FR_ACT_FAILED_POLICY FR_ACT_POLICY_FAILED
++
+ #define FR_ACT_MAX (__FR_ACT_MAX - 1)
+
+ #endif
+--- a/include/uapi/linux/icmpv6.h
++++ b/include/uapi/linux/icmpv6.h
+@@ -126,6 +126,8 @@ struct icmp6hdr {
+ #define ICMPV6_POLICY_FAIL 5
+ #define ICMPV6_REJECT_ROUTE 6
+
++#define ICMPV6_FAILED_POLICY ICMPV6_POLICY_FAIL
++
+ /*
+ * Codes for Time Exceeded
+ */
+--- a/include/uapi/linux/rtnetlink.h
++++ b/include/uapi/linux/rtnetlink.h
+@@ -253,6 +253,8 @@ enum {
+ __RTN_MAX
+ };
+
++#define RTN_FAILED_POLICY RTN_POLICY_FAILED
++
+ #define RTN_MAX (__RTN_MAX - 1)
+
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: net: replace GRO optimization patch with a new one that supports VLANs/bridges with different MAC addresses
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/netdevice.h | 2 ++
+ include/linux/skbuff.h | 3 ++-
+ net/core/dev.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++
+ net/ethernet/eth.c | 18 +++++++++++++++++-
+ 4 files changed, 69 insertions(+), 2 deletions(-)
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -2036,6 +2036,8 @@ struct net_device {
+ struct netdev_hw_addr_list mc;
+ struct netdev_hw_addr_list dev_addrs;
+
++ unsigned char local_addr_mask[MAX_ADDR_LEN];
++
+ #ifdef CONFIG_SYSFS
+ struct kset *queues_kset;
+ #endif
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -858,6 +858,7 @@ struct sk_buff {
+ #ifdef CONFIG_TLS_DEVICE
+ __u8 decrypted:1;
+ #endif
++ __u8 gro_skip:1;
+
+ #ifdef CONFIG_NET_SCHED
+ __u16 tc_index; /* traffic control index */
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -6062,6 +6062,9 @@ static enum gro_result dev_gro_receive(s
+ int same_flow;
+ int grow;
+
++ if (skb->gro_skip)
++ goto normal;
++
+ if (netif_elide_gro(skb->dev))
+ goto normal;
+
+@@ -8039,6 +8042,48 @@ static void __netdev_adjacent_dev_unlink
+ &upper_dev->adj_list.lower);
+ }
+
++static void __netdev_addr_mask(unsigned char *mask, const unsigned char *addr,
++ struct net_device *dev)
++{
++ int i;
++
++ for (i = 0; i < dev->addr_len; i++)
++ mask[i] |= addr[i] ^ dev->dev_addr[i];
++}
++
++static void __netdev_upper_mask(unsigned char *mask, struct net_device *dev,
++ struct net_device *lower)
++{
++ struct net_device *cur;
++ struct list_head *iter;
++
++ netdev_for_each_upper_dev_rcu(dev, cur, iter) {
++ __netdev_addr_mask(mask, cur->dev_addr, lower);
++ __netdev_upper_mask(mask, cur, lower);
++ }
++}
++
++static void __netdev_update_addr_mask(struct net_device *dev)
++{
++ unsigned char mask[MAX_ADDR_LEN];
++ struct net_device *cur;
++ struct list_head *iter;
++
++ memset(mask, 0, sizeof(mask));
++ __netdev_upper_mask(mask, dev, dev);
++ memcpy(dev->local_addr_mask, mask, dev->addr_len);
++
++ netdev_for_each_lower_dev(dev, cur, iter)
++ __netdev_update_addr_mask(cur);
++}
++
++static void netdev_update_addr_mask(struct net_device *dev)
++{
++ rcu_read_lock();
++ __netdev_update_addr_mask(dev);
++ rcu_read_unlock();
++}
++
+ static int __netdev_upper_dev_link(struct net_device *dev,
+ struct net_device *upper_dev, bool master,
+ void *upper_priv, void *upper_info,
+@@ -8090,6 +8135,7 @@ static int __netdev_upper_dev_link(struc
+ if (ret)
+ return ret;
+
++ netdev_update_addr_mask(dev);
+ ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER,
+ &changeupper_info.info);
+ ret = notifier_to_errno(ret);
+@@ -8186,6 +8232,7 @@ static void __netdev_upper_dev_unlink(st
+
+ __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev);
+
++ netdev_update_addr_mask(dev);
+ call_netdevice_notifiers_info(NETDEV_CHANGEUPPER,
+ &changeupper_info.info);
+
+@@ -8972,6 +9019,7 @@ int dev_set_mac_address(struct net_devic
+ if (err)
+ return err;
+ dev->addr_assign_type = NET_ADDR_SET;
++ netdev_update_addr_mask(dev);
+ call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+ add_device_randomness(dev->dev_addr, dev->addr_len);
+ return 0;
+--- a/net/ethernet/eth.c
++++ b/net/ethernet/eth.c
+@@ -143,6 +143,18 @@ u32 eth_get_headlen(const struct net_dev
+ }
+ EXPORT_SYMBOL(eth_get_headlen);
+
++static inline bool
++eth_check_local_mask(const void *addr1, const void *addr2, const void *mask)
++{
++ const u16 *a1 = addr1;
++ const u16 *a2 = addr2;
++ const u16 *m = mask;
++
++ return (((a1[0] ^ a2[0]) & ~m[0]) |
++ ((a1[1] ^ a2[1]) & ~m[1]) |
++ ((a1[2] ^ a2[2]) & ~m[2]));
++}
++
+ /**
+ * eth_type_trans - determine the packet's protocol ID.
+ * @skb: received socket data
+@@ -174,6 +186,10 @@ __be16 eth_type_trans(struct sk_buff *sk
+ } else {
+ skb->pkt_type = PACKET_OTHERHOST;
+ }
++
++ if (eth_check_local_mask(eth->h_dest, dev->dev_addr,
++ dev->local_addr_mask))
++ skb->gro_skip = 1;
+ }
+
+ /*
--- /dev/null
+From 844c273286f328acf0dab5fbd5d864366b4904dc Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 30 Mar 2021 18:21:14 +0200
+Subject: [PATCH] of_net: add mac-address-increment support
+
+Lots of embedded devices use the mac-address of other interface
+extracted from nvmem cells and increments it by one or two. Add two
+bindings to integrate this and directly use the right mac-address for
+the interface. Some example are some routers that use the gmac
+mac-address stored in the art partition and increments it by one for the
+wifi. mac-address-increment-byte bindings is used to tell what byte of
+the mac-address has to be increased (if not defined the last byte is
+increased) and mac-address-increment tells how much the byte decided
+early has to be increased.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+---
+ drivers/of/of_net.c | 43 +++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 39 insertions(+), 4 deletions(-)
+
+--- a/drivers/of/of_net.c
++++ b/drivers/of/of_net.c
+@@ -115,27 +115,62 @@ static int of_get_mac_addr_nvmem(struct
+ * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists
+ * but is all zeros.
+ *
++ * DT can tell the system to increment the mac-address after is extracted by
++ * using:
++ * - mac-address-increment-byte to decide what byte to increase
++ * (if not defined is increased the last byte)
++ * - mac-address-increment to decide how much to increase. The value WILL
++ * overflow to other bytes if the increment is over 255 or the total
++ * increment will exceed 255 of the current byte.
++ * (example 00:01:02:03:04:ff + 1 == 00:01:02:03:05:00)
++ * (example 00:01:02:03:04:fe + 5 == 00:01:02:03:05:03)
++ *
+ * Return: 0 on success and errno in case of error.
+ */
+ int of_get_mac_address(struct device_node *np, u8 *addr)
+ {
++ u32 inc_idx, mac_inc, mac_val;
+ int ret;
+
++ /* Check first if the increment byte is present and valid.
++ * If not set assume to increment the last byte if found.
++ */
++ if (of_property_read_u32(np, "mac-address-increment-byte", &inc_idx))
++ inc_idx = 5;
++ if (inc_idx < 3 || inc_idx > 5)
++ return -EINVAL;
++
+ if (!np)
+ return -ENODEV;
+
+ ret = of_get_mac_addr(np, "mac-address", addr);
+ if (!ret)
+- return 0;
++ goto found;
+
+ ret = of_get_mac_addr(np, "local-mac-address", addr);
+ if (!ret)
+- return 0;
++ goto found;
+
+ ret = of_get_mac_addr(np, "address", addr);
+ if (!ret)
+- return 0;
++ goto found;
++
++ ret = of_get_mac_addr_nvmem(np, addr);
++ if (ret)
++ return ret;
++
++found:
++ if (!of_property_read_u32(np, "mac-address-increment", &mac_inc)) {
++ /* Convert to a contiguous value */
++ mac_val = (addr[3] << 16) + (addr[4] << 8) + addr[5];
++ mac_val += mac_inc << 8 * (5-inc_idx);
++
++ /* Apply the incremented value handling overflow case */
++ addr[3] = (mac_val >> 16) & 0xff;
++ addr[4] = (mac_val >> 8) & 0xff;
++ addr[5] = (mac_val >> 0) & 0xff;
++ }
+
+- return of_get_mac_addr_nvmem(np, addr);
++ return ret;
+ }
+ EXPORT_SYMBOL(of_get_mac_address);
--- /dev/null
+--- a/drivers/of/of_net.c
++++ b/drivers/of/of_net.c
+@@ -95,6 +95,27 @@ static int of_get_mac_addr_nvmem(struct
+ return 0;
+ }
+
++static int of_add_mac_address(struct device_node *np, u8* addr)
++{
++ struct property *prop;
++
++ prop = kzalloc(sizeof(*prop), GFP_KERNEL);
++ if (!prop)
++ return -ENOMEM;
++
++ prop->name = "mac-address";
++ prop->length = ETH_ALEN;
++ prop->value = kmemdup(addr, ETH_ALEN, GFP_KERNEL);
++ if (!prop->value || of_update_property(np, prop))
++ goto free;
++
++ return 0;
++free:
++ kfree(prop->value);
++ kfree(prop);
++ return -ENOMEM;
++}
++
+ /**
+ * Search the device tree for the best MAC address to use. 'mac-address' is
+ * checked first, because that is supposed to contain to "most recent" MAC
+@@ -171,6 +192,7 @@ found:
+ addr[5] = (mac_val >> 0) & 0xff;
+ }
+
++ of_add_mac_address(np, addr);
+ return ret;
+ }
+ EXPORT_SYMBOL(of_get_mac_address);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 8 Jul 2021 07:08:29 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: avoid creating duplicate offload
+ entries
+
+Sometimes multiple CLS_REPLACE calls are issued for the same connection.
+rhashtable_insert_fast does not check for these duplicates, so multiple
+hardware flow entries can be created.
+Fix this by checking for an existing entry early
+
+Fixes: 502e84e2382d ("net: ethernet: mtk_eth_soc: add flow offloading support")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -187,6 +187,9 @@ mtk_flow_offload_replace(struct mtk_eth
+ int hash;
+ int i;
+
++ if (rhashtable_lookup(ð->flow_table, &f->cookie, mtk_flow_ht_params))
++ return -EEXIST;
++
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) {
+ struct flow_match_meta match;
+
--- /dev/null
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -7,6 +7,7 @@
+ #include <linux/rhashtable.h>
+ #include <linux/if_ether.h>
+ #include <linux/ip.h>
++#include <linux/ipv6.h>
+ #include <net/flow_offload.h>
+ #include <net/pkt_cls.h>
+ #include <net/dsa.h>
+@@ -20,6 +21,11 @@ struct mtk_flow_data {
+ __be32 src_addr;
+ __be32 dst_addr;
+ } v4;
++
++ struct {
++ struct in6_addr src_addr;
++ struct in6_addr dst_addr;
++ } v6;
+ };
+
+ __be16 src_port;
+@@ -64,6 +70,14 @@ mtk_flow_set_ipv4_addr(struct mtk_foe_en
+ data->v4.dst_addr, data->dst_port);
+ }
+
++static int
++mtk_flow_set_ipv6_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data)
++{
++ return mtk_foe_entry_set_ipv6_tuple(foe,
++ data->v6.src_addr.s6_addr32, data->src_port,
++ data->v6.dst_addr.s6_addr32, data->dst_port);
++}
++
+ static void
+ mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
+ {
+@@ -254,6 +268,9 @@ mtk_flow_offload_replace(struct mtk_eth
+ case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
+ offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
+ break;
++ case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
++ offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
++ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+@@ -289,6 +306,17 @@ mtk_flow_offload_replace(struct mtk_eth
+ mtk_flow_set_ipv4_addr(&foe, &data, false);
+ }
+
++ if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
++ struct flow_match_ipv6_addrs addrs;
++
++ flow_rule_match_ipv6_addrs(rule, &addrs);
++
++ data.v6.src_addr = addrs.key->src;
++ data.v6.dst_addr = addrs.key->dst;
++
++ mtk_flow_set_ipv6_addr(&foe, &data);
++ }
++
+ flow_action_for_each(i, act, &rule->action) {
+ if (act->id != FLOW_ACTION_MANGLE)
+ continue;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 21 Mar 2022 20:39:59 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: enable threaded NAPI
+
+This can improve performance under load by ensuring that NAPI processing is
+not pinned on CPU 0.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2171,8 +2171,8 @@ static irqreturn_t mtk_handle_irq_rx(int
+
+ eth->rx_events++;
+ if (likely(napi_schedule_prep(ð->rx_napi))) {
+- __napi_schedule(ð->rx_napi);
+ mtk_rx_irq_disable(eth, MTK_RX_DONE_INT);
++ __napi_schedule(ð->rx_napi);
+ }
+
+ return IRQ_HANDLED;
+@@ -2184,8 +2184,8 @@ static irqreturn_t mtk_handle_irq_tx(int
+
+ eth->tx_events++;
+ if (likely(napi_schedule_prep(ð->tx_napi))) {
+- __napi_schedule(ð->tx_napi);
+ mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
++ __napi_schedule(ð->tx_napi);
+ }
+
+ return IRQ_HANDLED;
+@@ -3229,6 +3229,8 @@ static int mtk_probe(struct platform_dev
+ * for NAPI to work
+ */
+ init_dummy_netdev(ð->dummy_dev);
++ eth->dummy_dev.threaded = 1;
++ strcpy(eth->dummy_dev.name, "mtk_eth");
+ netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx,
+ MTK_NAPI_WEIGHT);
+ netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx,
--- /dev/null
+From: Gabor Juhos <juhosg@openwrt.org>
+Subject: generic: add detach callback to struct phy_driver
+
+lede-commit: fe61fc2d7d0b3fb348b502f68f98243b3ddf5867
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/net/phy/phy_device.c | 3 +++
+ include/linux/phy.h | 6 ++++++
+ 2 files changed, 9 insertions(+)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1651,6 +1651,9 @@ void phy_detach(struct phy_device *phyde
+ struct module *ndev_owner = NULL;
+ struct mii_bus *bus;
+
++ if (phydev->drv && phydev->drv->detach)
++ phydev->drv->detach(phydev);
++
+ if (phydev->sysfs_links) {
+ if (dev)
+ sysfs_remove_link(&dev->dev.kobj, "phydev");
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -765,6 +765,12 @@ struct phy_driver {
+ /** @handle_interrupt: Override default interrupt handling */
+ irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
+
++ /*
++ * Called before an ethernet device is detached
++ * from the PHY.
++ */
++ void (*detach)(struct phy_device *phydev);
++
+ /** @remove: Clears up any memory if needed */
+ void (*remove)(struct phy_device *phydev);
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 27 Aug 2021 12:22:32 +0200
+Subject: [PATCH] bridge: add knob for filtering rx/tx BPDU packets on a port
+
+Some devices (e.g. wireless APs) can't have devices behind them be part of
+a bridge topology with redundant links, due to address limitations.
+Additionally, broadcast traffic on these devices is somewhat expensive, due to
+the low data rate and wakeups of clients in powersave mode.
+This knob can be used to ensure that BPDU packets are never sent or forwarded
+to/from these devices
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/linux/if_bridge.h
++++ b/include/linux/if_bridge.h
+@@ -56,6 +56,7 @@ struct br_ip_list {
+ #define BR_MRP_AWARE BIT(17)
+ #define BR_MRP_LOST_CONT BIT(18)
+ #define BR_MRP_LOST_IN_CONT BIT(19)
++#define BR_BPDU_FILTER BIT(20)
+
+ #define BR_DEFAULT_AGEING_TIME (300 * HZ)
+
+--- a/net/bridge/br_forward.c
++++ b/net/bridge/br_forward.c
+@@ -191,6 +191,7 @@ out:
+ void br_flood(struct net_bridge *br, struct sk_buff *skb,
+ enum br_pkt_type pkt_type, bool local_rcv, bool local_orig)
+ {
++ const unsigned char *dest = eth_hdr(skb)->h_dest;
+ struct net_bridge_port *prev = NULL;
+ struct net_bridge_port *p;
+
+@@ -206,6 +207,10 @@ void br_flood(struct net_bridge *br, str
+ case BR_PKT_MULTICAST:
+ if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev)
+ continue;
++ if ((p->flags & BR_BPDU_FILTER) &&
++ unlikely(is_link_local_ether_addr(dest) &&
++ dest[5] == 0))
++ continue;
+ break;
+ case BR_PKT_BROADCAST:
+ if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev)
+--- a/net/bridge/br_input.c
++++ b/net/bridge/br_input.c
+@@ -305,6 +305,8 @@ static rx_handler_result_t br_handle_fra
+ fwd_mask |= p->group_fwd_mask;
+ switch (dest[5]) {
+ case 0x00: /* Bridge Group Address */
++ if (p->flags & BR_BPDU_FILTER)
++ goto drop;
+ /* If STP is turned off,
+ then must forward to keep loop detection */
+ if (p->br->stp_enabled == BR_NO_STP ||
+--- a/net/bridge/br_sysfs_if.c
++++ b/net/bridge/br_sysfs_if.c
+@@ -233,6 +233,7 @@ BRPORT_ATTR_FLAG(multicast_flood, BR_MCA
+ BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD);
+ BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
+ BRPORT_ATTR_FLAG(isolated, BR_ISOLATED);
++BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER);
+
+ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+ static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
+@@ -285,6 +286,7 @@ static const struct brport_attribute *br
+ &brport_attr_group_fwd_mask,
+ &brport_attr_neigh_suppress,
+ &brport_attr_isolated,
++ &brport_attr_bpdu_filter,
+ &brport_attr_backup_port,
+ NULL
+ };
+--- a/net/bridge/br_stp_bpdu.c
++++ b/net/bridge/br_stp_bpdu.c
+@@ -80,7 +80,8 @@ void br_send_config_bpdu(struct net_brid
+ {
+ unsigned char buf[35];
+
+- if (p->br->stp_enabled != BR_KERNEL_STP)
++ if (p->br->stp_enabled != BR_KERNEL_STP ||
++ (p->flags & BR_BPDU_FILTER))
+ return;
+
+ buf[0] = 0;
+@@ -127,7 +128,8 @@ void br_send_tcn_bpdu(struct net_bridge_
+ {
+ unsigned char buf[4];
+
+- if (p->br->stp_enabled != BR_KERNEL_STP)
++ if (p->br->stp_enabled != BR_KERNEL_STP ||
++ (p->flags & BR_BPDU_FILTER))
+ return;
+
+ buf[0] = 0;
+@@ -172,6 +174,9 @@ void br_stp_rcv(const struct stp_proto *
+ if (!(br->dev->flags & IFF_UP))
+ goto out;
+
++ if (p->flags & BR_BPDU_FILTER)
++ goto out;
++
+ if (p->state == BR_STATE_DISABLED)
+ goto out;
+
+--- a/include/uapi/linux/if_link.h
++++ b/include/uapi/linux/if_link.h
+@@ -524,6 +524,7 @@ enum {
+ IFLA_BRPORT_BACKUP_PORT,
+ IFLA_BRPORT_MRP_RING_OPEN,
+ IFLA_BRPORT_MRP_IN_OPEN,
++ IFLA_BRPORT_BPDU_FILTER,
+ __IFLA_BRPORT_MAX
+ };
+ #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
+--- a/net/bridge/br_netlink.c
++++ b/net/bridge/br_netlink.c
+@@ -137,6 +137,7 @@ static inline size_t br_port_info_size(v
+ + nla_total_size(1) /* IFLA_BRPORT_VLAN_TUNNEL */
+ + nla_total_size(1) /* IFLA_BRPORT_NEIGH_SUPPRESS */
+ + nla_total_size(1) /* IFLA_BRPORT_ISOLATED */
++ + nla_total_size(1) /* IFLA_BRPORT_BPDU_FILTER */
+ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */
+ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */
+ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */
+@@ -220,7 +221,8 @@ static int br_port_fill_attrs(struct sk_
+ BR_MRP_LOST_CONT)) ||
+ nla_put_u8(skb, IFLA_BRPORT_MRP_IN_OPEN,
+ !!(p->flags & BR_MRP_LOST_IN_CONT)) ||
+- nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)))
++ nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)) ||
++ nla_put_u8(skb, IFLA_BRPORT_BPDU_FILTER, !!(p->flags & BR_BPDU_FILTER)))
+ return -EMSGSIZE;
+
+ timerval = br_timer_value(&p->message_age_timer);
+@@ -728,6 +730,7 @@ static const struct nla_policy br_port_p
+ [IFLA_BRPORT_NEIGH_SUPPRESS] = { .type = NLA_U8 },
+ [IFLA_BRPORT_ISOLATED] = { .type = NLA_U8 },
+ [IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 },
++ [IFLA_BRPORT_BPDU_FILTER] = { .type = NLA_U8 },
+ };
+
+ /* Change the state of the port and notify spanning tree */
+@@ -826,6 +829,10 @@ static int br_setport(struct net_bridge_
+ if (err)
+ return err;
+
++ err = br_set_port_flag(p, tb, IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER);
++ if (err)
++ return err;
++
+ br_vlan_tunnel_old = (p->flags & BR_VLAN_TUNNEL) ? true : false;
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL);
+ if (err)
+--- a/net/core/rtnetlink.c
++++ b/net/core/rtnetlink.c
+@@ -55,7 +55,7 @@
+ #include <net/net_namespace.h>
+
+ #define RTNL_MAX_TYPE 50
+-#define RTNL_SLAVE_MAX_TYPE 36
++#define RTNL_SLAVE_MAX_TYPE 37
+
+ struct rtnl_link {
+ rtnl_doit_func doit;
+@@ -4684,7 +4684,9 @@ int ndo_dflt_bridge_getlink(struct sk_bu
+ brport_nla_put_flag(skb, flags, mask,
+ IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD) ||
+ brport_nla_put_flag(skb, flags, mask,
+- IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD)) {
++ IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD) ||
++ brport_nla_put_flag(skb, flags, mask,
++ IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER)) {
+ nla_nest_cancel(skb, protinfo);
+ goto nla_put_failure;
+ }
--- /dev/null
+From 97ca310aa18a93329ef5cd68c20de89761962f45 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Sun, 13 Jun 2021 12:19:36 +0200
+Subject: [PATCH] net: phy: at803x: fix feature detection
+
+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, as this driver
+currently only supports copper operation.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/phy/at803x.c | 30 +++++++++++++++++++++++++++++-
+ 1 file changed, 29 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1032,6 +1032,34 @@ static int at803x_set_tunable(struct phy
+ }
+ }
+
++static int at803x_get_features(struct phy_device *phydev)
++{
++ int err;
++
++ err = genphy_read_abilities(phydev);
++ if (err)
++ return err;
++
++ if (!at803x_match_phy_id(phydev, 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,
++ * as this driver currently only supports copper
++ * operation.
++ */
++ linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
++ phydev->supported);
++ return 0;
++}
++
+ static int at803x_cable_test_result_trans(u16 status)
+ {
+ switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
+@@ -1364,7 +1392,7 @@ static struct phy_driver at803x_driver[]
+ .resume = at803x_resume,
+ .read_page = at803x_read_page,
+ .write_page = at803x_write_page,
+- /* PHY_GBIT_FEATURES */
++ .get_features = at803x_get_features,
+ .read_status = at803x_read_status,
+ .aneg_done = at803x_aneg_done,
+ .ack_interrupt = &at803x_ack_interrupt,
--- /dev/null
+From a1b291f3f6c80a6c5ccad7283fc472d77a2a4763 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Sun, 22 Dec 2019 12:40:11 +0000
+Subject: [PATCH] net: dsa: mv88e6xxx: fix vlan setup
+
+Provide an option that drivers can set to indicate they want to receive
+vlan configuration even when vlan filtering is disabled. This is safe
+for Marvell DSA bridges, which do not look up ingress traffic in the
+VTU if the port is in 8021Q disabled state. Whether this change is
+suitable for all DSA bridges is not known.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+---
+ drivers/net/dsa/mv88e6xxx/chip.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -2869,6 +2869,7 @@ static int mv88e6xxx_setup(struct dsa_sw
+
+ chip->ds = ds;
+ ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip);
++ ds->configure_vlan_while_not_filtering = true;
+
+ mv88e6xxx_reg_lock(chip);
+
--- /dev/null
+From 46fe6cecb296d850c1ee2b333e57093ac4b733f3 Mon Sep 17 00:00:00 2001
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Date: Sat, 16 Jan 2021 02:25:09 +0100
+Subject: [PATCH] net: bridge: switchdev: Refactor br_switchdev_fdb_notify
+
+Instead of having to add more and more arguments to
+br_switchdev_fdb_call_notifiers, get rid of it and build the info
+struct directly in br_switchdev_fdb_notify.
+
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+---
+ net/bridge/br_switchdev.c | 41 +++++++++++----------------------------
+ 1 file changed, 11 insertions(+), 30 deletions(-)
+
+--- a/net/bridge/br_switchdev.c
++++ b/net/bridge/br_switchdev.c
+@@ -102,25 +102,16 @@ int br_switchdev_set_port_flag(struct ne
+ return 0;
+ }
+
+-static void
+-br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac,
+- u16 vid, struct net_device *dev,
+- bool added_by_user, bool offloaded)
+-{
+- struct switchdev_notifier_fdb_info info;
+- unsigned long notifier_type;
+-
+- info.addr = mac;
+- info.vid = vid;
+- info.added_by_user = added_by_user;
+- info.offloaded = offloaded;
+- notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE;
+- call_switchdev_notifiers(notifier_type, dev, &info.info, NULL);
+-}
+-
+ void
+ br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
+ {
++ struct switchdev_notifier_fdb_info info = {
++ .addr = fdb->key.addr.addr,
++ .vid = fdb->key.vlan_id,
++ .added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags),
++ .offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags),
++ };
++
+ if (!fdb->dst)
+ return;
+ if (test_bit(BR_FDB_LOCAL, &fdb->flags))
+@@ -128,22 +119,12 @@ br_switchdev_fdb_notify(const struct net
+
+ switch (type) {
+ case RTM_DELNEIGH:
+- br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr,
+- fdb->key.vlan_id,
+- fdb->dst->dev,
+- test_bit(BR_FDB_ADDED_BY_USER,
+- &fdb->flags),
+- test_bit(BR_FDB_OFFLOADED,
+- &fdb->flags));
++ call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE,
++ fdb->dst->dev, &info.info, NULL);
+ break;
+ case RTM_NEWNEIGH:
+- br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr,
+- fdb->key.vlan_id,
+- fdb->dst->dev,
+- test_bit(BR_FDB_ADDED_BY_USER,
+- &fdb->flags),
+- test_bit(BR_FDB_OFFLOADED,
+- &fdb->flags));
++ call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE,
++ fdb->dst->dev, &info.info, NULL);
+ break;
+ }
+ }
--- /dev/null
+From ec5be4f79026282925ae383caa431a8d41e3456a Mon Sep 17 00:00:00 2001
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Date: Sat, 16 Jan 2021 02:25:10 +0100
+Subject: [PATCH] net: bridge: switchdev: Include local flag in FDB
+ notifications
+
+Some switchdev drivers, notably DSA, ignore all dynamically learned
+address notifications (!added_by_user) as these are autonomously added
+by the switch. Previously, such a notification was indistinguishable
+from a local address notification. Include a local bit in the
+notification so that the two classes can be discriminated.
+
+This allows DSA-like devices to add local addresses to the hardware
+FDB (with the CPU as the destination), thereby avoiding flows towards
+the CPU being flooded by the switch as unknown unicast.
+
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+---
+ include/net/switchdev.h | 1 +
+ net/bridge/br_switchdev.c | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/include/net/switchdev.h
++++ b/include/net/switchdev.h
+@@ -224,6 +224,7 @@ struct switchdev_notifier_fdb_info {
+ const unsigned char *addr;
+ u16 vid;
+ u8 added_by_user:1,
++ local:1,
+ offloaded:1;
+ };
+
+--- a/net/bridge/br_switchdev.c
++++ b/net/bridge/br_switchdev.c
+@@ -109,6 +109,7 @@ br_switchdev_fdb_notify(const struct net
+ .addr = fdb->key.addr.addr,
+ .vid = fdb->key.vlan_id,
+ .added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags),
++ .local = test_bit(BR_FDB_LOCAL, &fdb->flags),
+ .offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags),
+ };
+
--- /dev/null
+From 2e50fd9322047253c327550b4485cf8761035a8c Mon Sep 17 00:00:00 2001
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Date: Sat, 16 Jan 2021 02:25:11 +0100
+Subject: [PATCH] net: bridge: switchdev: Send FDB notifications for host
+ addresses
+
+Treat addresses added to the bridge itself in the same way as regular
+ports and send out a notification so that drivers may sync it down to
+the hardware FDB.
+
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+---
+ net/bridge/br_fdb.c | 4 ++--
+ net/bridge/br_private.h | 7 ++++---
+ net/bridge/br_switchdev.c | 11 +++++------
+ 3 files changed, 11 insertions(+), 11 deletions(-)
+
+--- a/net/bridge/br_fdb.c
++++ b/net/bridge/br_fdb.c
+@@ -602,7 +602,7 @@ void br_fdb_update(struct net_bridge *br
+ /* fastpath: update of existing entry */
+ if (unlikely(source != fdb->dst &&
+ !test_bit(BR_FDB_STICKY, &fdb->flags))) {
+- br_switchdev_fdb_notify(fdb, RTM_DELNEIGH);
++ br_switchdev_fdb_notify(br, fdb, RTM_DELNEIGH);
+ fdb->dst = source;
+ fdb_modified = true;
+ /* Take over HW learned entry */
+@@ -735,7 +735,7 @@ static void fdb_notify(struct net_bridge
+ int err = -ENOBUFS;
+
+ if (swdev_notify)
+- br_switchdev_fdb_notify(fdb, type);
++ br_switchdev_fdb_notify(br, fdb, type);
+
+ skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC);
+ if (skb == NULL)
+--- a/net/bridge/br_private.h
++++ b/net/bridge/br_private.h
+@@ -1525,8 +1525,8 @@ bool nbp_switchdev_allowed_egress(const
+ int br_switchdev_set_port_flag(struct net_bridge_port *p,
+ unsigned long flags,
+ unsigned long mask);
+-void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb,
+- int type);
++void br_switchdev_fdb_notify(struct net_bridge *br,
++ const struct net_bridge_fdb_entry *fdb, int type);
+ int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
+ struct netlink_ext_ack *extack);
+ int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid);
+@@ -1572,7 +1572,8 @@ static inline int br_switchdev_port_vlan
+ }
+
+ static inline void
+-br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
++br_switchdev_fdb_notify(struct net_bridge *br,
++ const struct net_bridge_fdb_entry *fdb, int type)
+ {
+ }
+
+--- a/net/bridge/br_switchdev.c
++++ b/net/bridge/br_switchdev.c
+@@ -103,7 +103,8 @@ int br_switchdev_set_port_flag(struct ne
+ }
+
+ void
+-br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
++br_switchdev_fdb_notify(struct net_bridge *br,
++ const struct net_bridge_fdb_entry *fdb, int type)
+ {
+ struct switchdev_notifier_fdb_info info = {
+ .addr = fdb->key.addr.addr,
+@@ -112,20 +113,19 @@ br_switchdev_fdb_notify(const struct net
+ .local = test_bit(BR_FDB_LOCAL, &fdb->flags),
+ .offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags),
+ };
++ struct net_device *dev = fdb->dst ? fdb->dst->dev : br->dev;
+
+- if (!fdb->dst)
+- return;
+ if (test_bit(BR_FDB_LOCAL, &fdb->flags))
+ return;
+
+ switch (type) {
+ case RTM_DELNEIGH:
+ call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE,
+- fdb->dst->dev, &info.info, NULL);
++ dev, &info.info, NULL);
+ break;
+ case RTM_NEWNEIGH:
+ call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE,
+- fdb->dst->dev, &info.info, NULL);
++ dev, &info.info, NULL);
+ break;
+ }
+ }
--- /dev/null
+From dd082716b43a3684b2f473ae5d1e76d1c076d86d Mon Sep 17 00:00:00 2001
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Date: Sat, 16 Jan 2021 02:25:12 +0100
+Subject: [PATCH] net: dsa: Include local addresses in assisted CPU port
+ learning
+
+Add local addresses (i.e. the ports' MAC addresses) to the hardware
+FDB when assisted CPU port learning is enabled.
+
+NOTE: The bridge's own MAC address is also "local". If that address is
+not shared with any port, the bridge's MAC is not be added by this
+functionality - but the following commit takes care of that case.
+
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+---
+ net/dsa/slave.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -2192,10 +2192,12 @@ static int dsa_slave_switchdev_event(str
+ fdb_info = ptr;
+
+ if (dsa_slave_dev_check(dev)) {
+- if (!fdb_info->added_by_user)
+- return NOTIFY_OK;
+-
+ dp = dsa_slave_to_port(dev);
++
++ if (fdb_info->local && dp->ds->assisted_learning_on_cpu_port)
++ dp = dp->cpu_dp;
++ else if (!fdb_info->added_by_user)
++ return NOTIFY_OK;
+ } else {
+ /* Snoop addresses learnt on foreign interfaces
+ * bridged with us, for switches that don't
--- /dev/null
+From 0663ebde114a6fb2c28c622ba5212b302d4d2581 Mon Sep 17 00:00:00 2001
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Date: Sat, 16 Jan 2021 02:25:13 +0100
+Subject: [PATCH] net: dsa: Include bridge addresses in assisted CPU port
+ learning
+
+Now that notifications are sent out for addresses added to the bridge
+itself, extend DSA to include those addresses in the hardware FDB when
+assisted CPU port learning is enabled.
+
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+---
+ net/dsa/slave.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -2206,7 +2206,11 @@ static int dsa_slave_switchdev_event(str
+ struct net_device *br_dev;
+ struct dsa_slave_priv *p;
+
+- br_dev = netdev_master_upper_dev_get_rcu(dev);
++ if (netif_is_bridge_master(dev))
++ br_dev = dev;
++ else
++ br_dev = netdev_master_upper_dev_get_rcu(dev);
++
+ if (!br_dev)
+ return NOTIFY_DONE;
+
--- /dev/null
+From 81e39fd78db82fb51b05fff309b9c521f1a0bc5a Mon Sep 17 00:00:00 2001
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Date: Sat, 16 Jan 2021 02:25:14 +0100
+Subject: [PATCH] net: dsa: Sync static FDB entries on foreign interfaces to
+ hardware
+
+Reuse the "assisted_learning_on_cpu_port" functionality to always add
+entries for user-configured entries on foreign interfaces, even if
+assisted_learning_on_cpu_port is not enabled. E.g. in this situation:
+
+ br0
+ / \
+swp0 dummy0
+
+$ bridge fdb add 02:00:de:ad:00:01 dev dummy0 vlan 1 master
+
+Results in DSA adding an entry in the hardware FDB, pointing this
+address towards the CPU port.
+
+The same is true for entries added to the bridge itself, e.g:
+
+$ bridge fdb add 02:00:de:ad:00:01 dev br0 vlan 1 self
+
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+---
+ net/dsa/slave.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -2199,9 +2199,12 @@ static int dsa_slave_switchdev_event(str
+ else if (!fdb_info->added_by_user)
+ return NOTIFY_OK;
+ } else {
+- /* Snoop addresses learnt on foreign interfaces
+- * bridged with us, for switches that don't
+- * automatically learn SA from CPU-injected traffic
++ /* Snoop addresses added to foreign interfaces
++ * bridged with us, or the bridge
++ * itself. Dynamically learned addresses can
++ * also be added for switches that don't
++ * automatically learn SA from CPU-injected
++ * traffic.
+ */
+ struct net_device *br_dev;
+ struct dsa_slave_priv *p;
+@@ -2223,7 +2226,8 @@ static int dsa_slave_switchdev_event(str
+
+ dp = p->dp->cpu_dp;
+
+- if (!dp->ds->assisted_learning_on_cpu_port)
++ if (!fdb_info->added_by_user &&
++ !dp->ds->assisted_learning_on_cpu_port)
+ return NOTIFY_DONE;
+ }
+
--- /dev/null
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Subject: [RFC net-next 7/7] net: dsa: mv88e6xxx: Request assisted learning on CPU port
+Date: Sat, 16 Jan 2021 02:25:15 +0100
+Archived-At: <https://lore.kernel.org/netdev/20210116012515.3152-8-tobias@waldekranz.com/>
+
+While the hardware is capable of performing learning on the CPU port,
+it requires alot of additions to the bridge's forwarding path in order
+to handle multi-destination traffic correctly.
+
+Until that is in place, opt for the next best thing and let DSA sync
+the relevant addresses down to the hardware FDB.
+
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+---
+ drivers/net/dsa/mv88e6xxx/chip.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -5436,6 +5436,7 @@ static int mv88e6xxx_register_switch(str
+ ds->ops = &mv88e6xxx_switch_ops;
+ ds->ageing_time_min = chip->info->age_time_coeff;
+ ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
++ ds->assisted_learning_on_cpu_port = true;
+
+ dev_set_drvdata(dev, ds);
+
--- /dev/null
+From patchwork Thu Aug 5 22:23:30 2021
+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: 12422209
+Date: Thu, 5 Aug 2021 23:23:30 +0100
+From: Daniel Golle <daniel@makrotopia.org>
+To: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org,
+ linux-kernel@vger.kernel.org
+Cc: "David S. Miller" <davem@davemloft.net>, Andrew Lunn <andrew@lunn.ch>,
+ Michael Walle <michael@walle.cc>
+Subject: [PATCH] ARM: kirkwood: add missing <linux/if_ether.h> for ETH_ALEN
+Message-ID: <YQxk4jrbm31NM1US@makrotopia.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+X-BeenThere: linux-arm-kernel@lists.infradead.org
+X-Mailman-Version: 2.1.34
+Precedence: list
+List-Id: <linux-arm-kernel.lists.infradead.org>
+List-Archive: <http://lists.infradead.org/pipermail/linux-arm-kernel/>
+Sender: "linux-arm-kernel" <linux-arm-kernel-bounces@lists.infradead.org>
+
+After commit 83216e3988cd1 ("of: net: pass the dst buffer to
+of_get_mac_address()") build fails for kirkwood as ETH_ALEN is not
+defined.
+
+arch/arm/mach-mvebu/kirkwood.c: In function 'kirkwood_dt_eth_fixup':
+arch/arm/mach-mvebu/kirkwood.c:87:13: error: 'ETH_ALEN' undeclared (first use in this function); did you mean 'ESTALE'?
+ u8 tmpmac[ETH_ALEN];
+ ^~~~~~~~
+ ESTALE
+arch/arm/mach-mvebu/kirkwood.c:87:13: note: each undeclared identifier is reported only once for each function it appears in
+arch/arm/mach-mvebu/kirkwood.c:87:6: warning: unused variable 'tmpmac' [-Wunused-variable]
+ u8 tmpmac[ETH_ALEN];
+ ^~~~~~
+make[5]: *** [scripts/Makefile.build:262: arch/arm/mach-mvebu/kirkwood.o] Error 1
+make[5]: *** Waiting for unfinished jobs....
+
+Add missing #include <linux/if_ether.h> to fix this.
+
+Cc: David S. Miller <davem@davemloft.net>
+Cc: Andrew Lunn <andrew@lunn.ch>
+Cc: Michael Walle <michael@walle.cc>
+Reported-by: https://buildbot.openwrt.org/master/images/#/builders/56/builds/220/steps/44/logs/stdio
+Fixes: 83216e3988cd1 ("of: net: pass the dst buffer to of_get_mac_address()")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ arch/arm/mach-mvebu/kirkwood.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/mach-mvebu/kirkwood.c
++++ b/arch/arm/mach-mvebu/kirkwood.c
+@@ -14,6 +14,7 @@
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/mbus.h>
++#include <linux/if_ether.h>
+ #include <linux/of.h>
+ #include <linux/of_address.h>
+ #include <linux/of_net.h>
--- /dev/null
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Subject: [PATCH] bcma: get SoC device struct & copy its DMA params to the
+ subdevices
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+For bus devices to be fully usable it's required to set their DMA
+parameters.
+
+For years it has been missing and remained unnoticed because of
+mips_dma_alloc_coherent() silently handling the empty coherent_dma_mask.
+Kernel 4.19 came with a lot of DMA changes and caused a regression on
+the bcm47xx. Starting with the commit f8c55dc6e828 ("MIPS: use generic
+dma noncoherent ops for simple noncoherent platforms") DMA coherent
+allocations just fail. Example:
+[ 1.114914] bgmac_bcma bcma0:2: Allocation of TX ring 0x200 failed
+[ 1.121215] bgmac_bcma bcma0:2: Unable to alloc memory for DMA
+[ 1.127626] bgmac_bcma: probe of bcma0:2 failed with error -12
+[ 1.133838] bgmac_bcma: Broadcom 47xx GBit MAC driver loaded
+
+This change fixes above regression in addition to the MIPS bcm47xx
+commit 321c46b91550 ("MIPS: BCM47XX: Setup struct device for the SoC").
+
+It also fixes another *old* GPIO regression caused by a parent pointing
+to the NULL:
+[ 0.157054] missing gpiochip .dev parent pointer
+[ 0.157287] bcma: bus0: Error registering GPIO driver: -22
+introduced by the commit 74f4e0cc6108 ("bcma: switch GPIO portions to
+use GPIOLIB_IRQCHIP").
+
+Fixes: f8c55dc6e828 ("MIPS: use generic dma noncoherent ops for simple noncoherent platforms")
+Fixes: 74f4e0cc6108 ("bcma: switch GPIO portions to use GPIOLIB_IRQCHIP")
+Cc: linux-mips@linux-mips.org
+Cc: Christoph Hellwig <hch@lst.de>
+Cc: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+---
+
+--- a/drivers/bcma/host_soc.c
++++ b/drivers/bcma/host_soc.c
+@@ -191,6 +191,8 @@ int __init bcma_host_soc_init(struct bcm
+ struct bcma_bus *bus = &soc->bus;
+ int err;
+
++ bus->dev = soc->dev;
++
+ /* Scan bus and initialize it */
+ err = bcma_bus_early_register(bus);
+ if (err)
+--- a/drivers/bcma/main.c
++++ b/drivers/bcma/main.c
+@@ -241,8 +241,10 @@ void bcma_prepare_core(struct bcma_bus *
+ core->dev.bus = &bcma_bus_type;
+ dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
+ core->dev.parent = bus->dev;
+- if (bus->dev)
++ if (bus->dev) {
+ bcma_of_fill_device(bus->dev, core);
++ dma_coerce_mask_and_coherent(&core->dev, bus->dev->coherent_dma_mask);
++ }
+
+ switch (bus->hosttype) {
+ case BCMA_HOSTTYPE_PCI:
--- /dev/null
+From fc23ea48ba52c24f201fe5ca0132ee1a3de5a70a Mon Sep 17 00:00:00 2001
+From: Mauri Sandberg <maukka@ext.kapsi.fi>
+Date: Thu, 25 Mar 2021 11:48:05 +0200
+Subject: [PATCH 2/2] gpio: gpio-cascade: add generic GPIO cascade
+
+Adds support for building cascades of GPIO lines. That is, it allows
+setups when there is one upstream line and multiple cascaded lines, out
+of which one can be chosen at a time. The status of the upstream line
+can be conveyed to the selected cascaded line or, vice versa, the status
+of the cascaded line can be conveyed to the upstream line.
+
+A multiplexer is being used to select, which cascaded GPIO line is being
+used at any given time.
+
+At the moment only input direction is supported. In future it should be
+possible to add support for output direction, too.
+
+Signed-off-by: Mauri Sandberg <maukka@ext.kapsi.fi>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+---
+v7 -> v8:
+ - rearrange members in struct gpio_cascade
+ - cosmetic changes in file header and in one function declaration
+ - added Reviewed-by tags by Linus and Andy
+v6 -> v7:
+ - In Kconfig add info about module name
+ - adhere to new convention that allows lines longer than 80 chars
+ - use dev_probe_err with upstream gpio line too
+ - refactor for cleaner exit of probe function.
+v5 -> v6:
+ - In Kconfig, remove dependency to OF_GPIO and select only MULTIPLEXER
+ - refactor code preferring one-liners
+ - clean up prints, removing them from success-path.
+ - don't explicitly set gpio_chip.of_node as it's done in the GPIO library
+ - use devm_gpiochip_add_data instead of gpiochip_add
+v4 -> v5:
+ - renamed gpio-mux-input -> gpio-cascade. refactored code accordingly
+ here and there and changed to use new bindings and compatible string
+ - ambigious and vague 'pin' was rename to 'upstream_line'
+ - dropped Tested-by and Reviewed-by due to changes in bindings
+ - dropped Reported-by suggested by an automatic bot as it was not really
+ appropriate to begin with
+ - functionally it's the same as v4
+v3 -> v4:
+ - Changed author email
+ - Included Tested-by and Reviewed-by from Drew
+v2 -> v3:
+ - use managed device resources
+ - update Kconfig description
+v1 -> v2:
+ - removed .owner from platform_driver as per test bot's instruction
+ - added MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE
+ - added gpio_mux_input_get_direction as it's recommended for all chips
+ - removed because this is input only chip: gpio_mux_input_set_value
+ - removed because they are not needed for input/output only chips:
+ gpio_mux_input_direction_input
+ gpio_mux_input_direction_output
+ - fixed typo in an error message
+ - added info message about successful registration
+ - removed can_sleep flag as this does not sleep while getting GPIO value
+ like I2C or SPI do
+ - Updated description in Kconfig
+---
+ drivers/gpio/Kconfig | 15 +++++
+ drivers/gpio/Makefile | 1 +
+ drivers/gpio/gpio-cascade.c | 117 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 133 insertions(+)
+ create mode 100644 drivers/gpio/gpio-cascade.c
+
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -1617,4 +1617,19 @@ config GPIO_MOCKUP
+ tools/testing/selftests/gpio/gpio-mockup.sh. Reference the usage in
+ it.
+
++comment "Other GPIO expanders"
++
++config GPIO_CASCADE
++ tristate "General GPIO cascade"
++ select MULTIPLEXER
++ help
++ Say yes here to enable support for generic GPIO cascade.
++
++ This allows building one-to-many cascades of GPIO lines using
++ different types of multiplexers readily available. At the
++ moment only input lines are supported.
++
++ To build the driver as a module choose 'm' and the resulting module
++ will be called 'gpio-cascade'.
++
+ endif
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -44,6 +44,7 @@ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd
+ obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
+ obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
+ obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o
++obj-$(CONFIG_GPIO_CASCADE) += gpio-cascade.o
+ obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
+ obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o
+ obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o
+--- /dev/null
++++ b/drivers/gpio/gpio-cascade.c
+@@ -0,0 +1,117 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * A generic GPIO cascade driver
++ *
++ * Copyright (C) 2021 Mauri Sandberg <maukka@ext.kapsi.fi>
++ *
++ * This allows building cascades of GPIO lines in a manner illustrated
++ * below:
++ *
++ * /|---- Cascaded GPIO line 0
++ * Upstream | |---- Cascaded GPIO line 1
++ * GPIO line ----+ | .
++ * | | .
++ * \|---- Cascaded GPIO line n
++ *
++ * A multiplexer is being used to select, which cascaded line is being
++ * addressed at any given time.
++ *
++ * At the moment only input mode is supported due to lack of means for
++ * testing output functionality. At least theoretically output should be
++ * possible with open drain constructions.
++ */
++
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/platform_device.h>
++#include <linux/mux/consumer.h>
++
++#include <linux/gpio/consumer.h>
++#include <linux/gpio/driver.h>
++
++struct gpio_cascade {
++ struct gpio_chip gpio_chip;
++ struct device *parent;
++ struct mux_control *mux_control;
++ struct gpio_desc *upstream_line;
++};
++
++static struct gpio_cascade *chip_to_cascade(struct gpio_chip *gc)
++{
++ return container_of(gc, struct gpio_cascade, gpio_chip);
++}
++
++static int gpio_cascade_get_direction(struct gpio_chip *gc, unsigned int offset)
++{
++ return GPIO_LINE_DIRECTION_IN;
++}
++
++static int gpio_cascade_get_value(struct gpio_chip *gc, unsigned int offset)
++{
++ struct gpio_cascade *cas = chip_to_cascade(gc);
++ int ret;
++
++ ret = mux_control_select(cas->mux_control, offset);
++ if (ret)
++ return ret;
++
++ ret = gpiod_get_value(cas->upstream_line);
++ mux_control_deselect(cas->mux_control);
++ return ret;
++}
++
++static int gpio_cascade_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct gpio_cascade *cas;
++ struct mux_control *mc;
++ struct gpio_desc *upstream;
++ struct gpio_chip *gc;
++
++ cas = devm_kzalloc(dev, sizeof(*cas), GFP_KERNEL);
++ if (!cas)
++ return -ENOMEM;
++
++ mc = devm_mux_control_get(dev, NULL);
++ if (IS_ERR(mc))
++ return dev_err_probe(dev, PTR_ERR(mc), "unable to get mux-control\n");
++
++ cas->mux_control = mc;
++ upstream = devm_gpiod_get(dev, "upstream", GPIOD_IN);
++ if (IS_ERR(upstream))
++ return dev_err_probe(dev, PTR_ERR(upstream), "unable to claim upstream GPIO line\n");
++
++ cas->upstream_line = upstream;
++ cas->parent = dev;
++
++ gc = &cas->gpio_chip;
++ gc->get = gpio_cascade_get_value;
++ gc->get_direction = gpio_cascade_get_direction;
++ gc->base = -1;
++ gc->ngpio = mux_control_states(mc);
++ gc->label = dev_name(cas->parent);
++ gc->parent = cas->parent;
++ gc->owner = THIS_MODULE;
++
++ platform_set_drvdata(pdev, cas);
++ return devm_gpiochip_add_data(dev, &cas->gpio_chip, NULL);
++}
++
++static const struct of_device_id gpio_cascade_id[] = {
++ { .compatible = "gpio-cascade" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, gpio_cascade_id);
++
++static struct platform_driver gpio_cascade_driver = {
++ .driver = {
++ .name = "gpio-cascade",
++ .of_match_table = gpio_cascade_id,
++ },
++ .probe = gpio_cascade_probe,
++};
++module_platform_driver(gpio_cascade_driver);
++
++MODULE_AUTHOR("Mauri Sandberg <maukka@ext.kapsi.fi>");
++MODULE_DESCRIPTION("Generic GPIO cascade");
++MODULE_LICENSE("GPL");
--- /dev/null
+From: Gabor Juhos <juhosg@openwrt.org>
+Subject: debloat: add kernel config option to disabling common PCI quirks
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/pci/Kconfig | 6 ++++++
+ drivers/pci/quirks.c | 6 ++++++
+ 2 files changed, 12 insertions(+)
+
+--- a/drivers/pci/Kconfig
++++ b/drivers/pci/Kconfig
+@@ -118,6 +118,13 @@ config XEN_PCIDEV_FRONTEND
+ The PCI device frontend driver allows the kernel to import arbitrary
+ PCI devices from a PCI backend to support PCI driver domains.
+
++config PCI_DISABLE_COMMON_QUIRKS
++ bool "PCI disable common quirks"
++ depends on PCI
++ help
++ If you don't know what to do here, say N.
++
++
+ config PCI_ATS
+ bool
+
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -206,6 +206,7 @@ static void quirk_mmio_always_on(struct
+ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on);
+
++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
+ /*
+ * The Mellanox Tavor device gives false positive parity errors. Mark this
+ * device with a broken_parity_status to allow PCI scanning code to "skip"
+@@ -3323,6 +3324,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata);
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata);
+
++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */
++
+ /*
+ * Ivytown NTB BAR sizes are misreported by the hardware due to an erratum.
+ * To work around this, query the size it should be configured to by the
+@@ -3348,6 +3351,8 @@ static void quirk_intel_ntb(struct pci_d
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb);
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
+
++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
++
+ /*
+ * Some BIOS implementations leave the Intel GPU interrupts enabled, even
+ * though no one is handling them (e.g., if the i915 driver is never
+@@ -3386,6 +3391,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);
+
++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */
++
+ /*
+ * PCI devices which are on Intel chips can skip the 10ms delay
+ * before entering D3 mode.
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Subject: debloat: disable common USB quirks
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/usb/host/pci-quirks.c | 16 ++++++++++++++++
+ drivers/usb/host/pci-quirks.h | 18 +++++++++++++++++-
+ include/linux/usb/hcd.h | 7 +++++++
+ 3 files changed, 40 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/host/pci-quirks.c
++++ b/drivers/usb/host/pci-quirks.c
+@@ -128,6 +128,8 @@ struct amd_chipset_type {
+ u8 rev;
+ };
+
++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
++
+ static struct amd_chipset_info {
+ struct pci_dev *nb_dev;
+ struct pci_dev *smbus_dev;
+@@ -633,6 +635,10 @@ bool usb_amd_pt_check_port(struct device
+ }
+ EXPORT_SYMBOL_GPL(usb_amd_pt_check_port);
+
++#endif /* CONFIG_PCI_DISABLE_COMMON_QUIRKS */
++
++#if IS_ENABLED(CONFIG_USB_UHCI_HCD)
++
+ /*
+ * Make sure the controller is completely inactive, unable to
+ * generate interrupts or do DMA.
+@@ -712,8 +718,17 @@ reset_needed:
+ uhci_reset_hc(pdev, base);
+ return 1;
+ }
++#else
++int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
++{
++ return 0;
++}
++
++#endif
+ EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
+
++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
++
+ static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask)
+ {
+ u16 cmd;
+@@ -1285,3 +1300,4 @@ static void quirk_usb_early_handoff(stru
+ }
+ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff);
++#endif
+--- a/drivers/usb/host/pci-quirks.h
++++ b/drivers/usb/host/pci-quirks.h
+@@ -5,6 +5,9 @@
+ #ifdef CONFIG_USB_PCI
+ void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
+ int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
++#endif /* CONFIG_USB_PCI */
++
++#if defined(CONFIG_USB_PCI) && !defined(CONFIG_PCI_DISABLE_COMMON_QUIRKS)
+ int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev);
+ bool usb_amd_hang_symptom_quirk(void);
+ bool usb_amd_prefetch_quirk(void);
+@@ -19,6 +22,18 @@ void sb800_prefetch(struct device *dev,
+ bool usb_amd_pt_check_port(struct device *device, int port);
+ #else
+ struct pci_dev;
++static inline int usb_amd_quirk_pll_check(void)
++{
++ return 0;
++}
++static inline bool usb_amd_hang_symptom_quirk(void)
++{
++ return false;
++}
++static inline bool usb_amd_prefetch_quirk(void)
++{
++ return false;
++}
+ static inline void usb_amd_quirk_pll_disable(void) {}
+ static inline void usb_amd_quirk_pll_enable(void) {}
+ static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {}
+@@ -29,6 +44,11 @@ static inline bool usb_amd_pt_check_port
+ {
+ return false;
+ }
++static inline void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) {}
++static inline bool usb_xhci_needs_pci_reset(struct pci_dev *pdev)
++{
++ return false;
++}
+ #endif /* CONFIG_USB_PCI */
+
+ #endif /* __LINUX_USB_PCI_QUIRKS_H */
+--- a/include/linux/usb/hcd.h
++++ b/include/linux/usb/hcd.h
+@@ -484,7 +484,14 @@ extern int usb_hcd_pci_probe(struct pci_
+ extern void usb_hcd_pci_remove(struct pci_dev *dev);
+ extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
+
++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
+ extern int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev);
++#else
++static inline int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev)
++{
++ return 0;
++}
++#endif
+
+ #ifdef CONFIG_PM
+ extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
--- /dev/null
+From d9c8bc8c1408f3e8529db6e4e04017b4c579c342 Mon Sep 17 00:00:00 2001
+From: Pawel Dembicki <paweldembicki@gmail.com>
+Date: Sun, 18 Feb 2018 17:08:04 +0100
+Subject: [PATCH] w1: gpio: fix problem with platfom data in w1-gpio
+
+In devices, where fdt is used, is impossible to apply platform data
+without proper fdt node.
+
+This patch allow to use platform data in devices with fdt.
+
+Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
+---
+ drivers/w1/masters/w1-gpio.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+--- a/drivers/w1/masters/w1-gpio.c
++++ b/drivers/w1/masters/w1-gpio.c
+@@ -76,7 +76,7 @@ static int w1_gpio_probe(struct platform
+ enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN;
+ int err;
+
+- if (of_have_populated_dt()) {
++ if (of_have_populated_dt() && !dev_get_platdata(&pdev->dev)) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
--- /dev/null
+From: Daniel Golle <daniel@makrotopia.org>
+Subject: libata: add ledtrig support
+
+This adds a LED trigger for each ATA port indicating disk activity.
+
+As this is needed only on specific platforms (NAS SoCs and such),
+these platforms should define ARCH_WANTS_LIBATA_LEDS if there
+are boards with LED(s) intended to indicate ATA disk activity and
+need the OS to take care of that.
+In that way, if not selected, LED trigger support not will be
+included in libata-core and both, codepaths and structures remain
+untouched.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/ata/Kconfig | 16 ++++++++++++++++
+ drivers/ata/libata-core.c | 41 +++++++++++++++++++++++++++++++++++++++++
+ include/linux/libata.h | 9 +++++++++
+ 3 files changed, 66 insertions(+)
+
+--- a/drivers/ata/Kconfig
++++ b/drivers/ata/Kconfig
+@@ -67,6 +67,22 @@ config ATA_FORCE
+
+ If unsure, say Y.
+
++config ARCH_WANT_LIBATA_LEDS
++ bool
++
++config ATA_LEDS
++ bool "support ATA port LED triggers"
++ depends on ARCH_WANT_LIBATA_LEDS
++ select NEW_LEDS
++ select LEDS_CLASS
++ select LEDS_TRIGGERS
++ default y
++ help
++ This option adds a LED trigger for each registered ATA port.
++ It is used to drive disk activity leds connected via GPIO.
++
++ If unsure, say N.
++
+ config ATA_ACPI
+ bool "ATA ACPI Support"
+ depends on ACPI
+--- a/drivers/ata/libata-core.c
++++ b/drivers/ata/libata-core.c
+@@ -650,6 +650,19 @@ u64 ata_tf_read_block(const struct ata_t
+ return block;
+ }
+
++#ifdef CONFIG_ATA_LEDS
++#define LIBATA_BLINK_DELAY 20 /* ms */
++static inline void ata_led_act(struct ata_port *ap)
++{
++ unsigned long led_delay = LIBATA_BLINK_DELAY;
++
++ if (unlikely(!ap->ledtrig))
++ return;
++
++ led_trigger_blink_oneshot(ap->ledtrig, &led_delay, &led_delay, 0);
++}
++#endif
++
+ /**
+ * ata_build_rw_tf - Build ATA taskfile for given read/write request
+ * @tf: Target ATA taskfile
+@@ -4548,6 +4561,9 @@ struct ata_queued_cmd *ata_qc_new_init(s
+ if (tag < 0)
+ return NULL;
+ }
++#ifdef CONFIG_ATA_LEDS
++ ata_led_act(ap);
++#endif
+
+ qc = __ata_qc_from_tag(ap, tag);
+ qc->tag = qc->hw_tag = tag;
+@@ -5326,6 +5342,9 @@ struct ata_port *ata_port_alloc(struct a
+ ap->stats.unhandled_irq = 1;
+ ap->stats.idle_irq = 1;
+ #endif
++#ifdef CONFIG_ATA_LEDS
++ ap->ledtrig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
++#endif
+ ata_sff_port_init(ap);
+
+ return ap;
+@@ -5361,6 +5380,12 @@ static void ata_host_release(struct kref
+
+ kfree(ap->pmp_link);
+ kfree(ap->slave_link);
++#ifdef CONFIG_ATA_LEDS
++ if (ap->ledtrig) {
++ led_trigger_unregister(ap->ledtrig);
++ kfree(ap->ledtrig);
++ };
++#endif
+ kfree(ap);
+ host->ports[i] = NULL;
+ }
+@@ -5767,7 +5792,23 @@ int ata_host_register(struct ata_host *h
+ host->ports[i]->print_id = atomic_inc_return(&ata_print_id);
+ host->ports[i]->local_port_no = i + 1;
+ }
++#ifdef CONFIG_ATA_LEDS
++ for (i = 0; i < host->n_ports; i++) {
++ if (unlikely(!host->ports[i]->ledtrig))
++ continue;
+
++ snprintf(host->ports[i]->ledtrig_name,
++ sizeof(host->ports[i]->ledtrig_name), "ata%u",
++ host->ports[i]->print_id);
++
++ host->ports[i]->ledtrig->name = host->ports[i]->ledtrig_name;
++
++ if (led_trigger_register(host->ports[i]->ledtrig)) {
++ kfree(host->ports[i]->ledtrig);
++ host->ports[i]->ledtrig = NULL;
++ }
++ }
++#endif
+ /* Create associated sysfs transport objects */
+ for (i = 0; i < host->n_ports; i++) {
+ rc = ata_tport_add(host->dev,host->ports[i]);
+--- a/include/linux/libata.h
++++ b/include/linux/libata.h
+@@ -23,6 +23,9 @@
+ #include <linux/cdrom.h>
+ #include <linux/sched.h>
+ #include <linux/async.h>
++#ifdef CONFIG_ATA_LEDS
++#include <linux/leds.h>
++#endif
+
+ /*
+ * Define if arch has non-standard setup. This is a _PCI_ standard
+@@ -883,6 +886,12 @@ struct ata_port {
+ #ifdef CONFIG_ATA_ACPI
+ struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */
+ #endif
++
++#ifdef CONFIG_ATA_LEDS
++ struct led_trigger *ledtrig;
++ char ledtrig_name[8];
++#endif
++
+ /* owned by EH */
+ u8 sector_buf[ATA_SECT_SIZE] ____cacheline_aligned;
+ };
--- /dev/null
+From d6988cf1d16faac56899918bb2b1be8d85155e3f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
+Date: Sat, 20 Feb 2021 18:36:38 +0100
+Subject: [PATCH] hwrng: bcm2835: set quality to 1000
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This allows devices without a high precission timer to reduce boot from >100s
+to <30s.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+---
+ drivers/char/hw_random/bcm2835-rng.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/char/hw_random/bcm2835-rng.c
++++ b/drivers/char/hw_random/bcm2835-rng.c
+@@ -163,6 +163,7 @@ static int bcm2835_rng_probe(struct plat
+ priv->rng.init = bcm2835_rng_init;
+ priv->rng.read = bcm2835_rng_read;
+ priv->rng.cleanup = bcm2835_rng_cleanup;
++ priv->rng.quality = 1000;
+
+ if (dev_of_node(dev)) {
+ rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node);
--- /dev/null
+From 078c6a1cbd4cd7496048786beec2e312577bebbf Mon Sep 17 00:00:00 2001
+From: Pawel Dembicki <paweldembicki@gmail.com>
+Date: Tue, 11 Jan 2022 23:11:32 +0100
+Subject: [PATCH] net: qmi_wwan: add ZTE MF286D modem 19d2:1485
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Modem from ZTE MF286D is an Qualcomm MDM9250 based 3G/4G modem.
+
+T: Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 3 Spd=5000 MxCh= 0
+D: Ver= 3.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1
+P: Vendor=19d2 ProdID=1485 Rev=52.87
+S: Manufacturer=ZTE,Incorporated
+S: Product=ZTE Technologies MSM
+S: SerialNumber=MF286DZTED000000
+C:* #Ifs= 7 Cfg#= 1 Atr=80 MxPwr=896mA
+A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=06 Prot=00
+I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=ff Driver=rndis_host
+E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms
+I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host
+E: Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E: Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option
+E: Ad=83(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E: Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=option
+E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms
+E: Ad=84(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E: Ad=03(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=option
+E: Ad=87(I) Atr=03(Int.) MxPS= 10 Ivl=32ms
+E: Ad=86(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E: Ad=04(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+I:* If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan
+E: Ad=88(I) Atr=03(Int.) MxPS= 8 Ivl=32ms
+E: Ad=8e(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E: Ad=0f(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+I:* If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=usbfs
+E: Ad=05(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E: Ad=89(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+
+Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
+Acked-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/qmi_wwan.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -1252,6 +1252,7 @@ static const struct usb_device_id produc
+ {QMI_FIXED_INTF(0x19d2, 0x1426, 2)}, /* ZTE MF91 */
+ {QMI_FIXED_INTF(0x19d2, 0x1428, 2)}, /* Telewell TW-LTE 4G v2 */
+ {QMI_FIXED_INTF(0x19d2, 0x1432, 3)}, /* ZTE ME3620 */
++ {QMI_FIXED_INTF(0x19d2, 0x1485, 5)}, /* ZTE MF286D */
+ {QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */
+ {QMI_FIXED_INTF(0x2001, 0x7e16, 3)}, /* D-Link DWM-221 */
+ {QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */
--- /dev/null
+From 43f3f187e6f62ca40802afe39495c8a3e20b4bfa Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 10 Jan 2022 01:50:50 +0100
+Subject: [PATCH] PCI: aardvark: Replace custom PCIE_CORE_INT_* macros with
+ PCI_INTERRUPT_*
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Header file linux/pci.h defines enum pci_interrupt_pin with corresponding
+PCI_INTERRUPT_* values.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 6 +-----
+ 1 file changed, 1 insertion(+), 5 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -37,10 +37,6 @@
+ #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6)
+ #define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK BIT(7)
+ #define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV BIT(8)
+-#define PCIE_CORE_INT_A_ASSERT_ENABLE 1
+-#define PCIE_CORE_INT_B_ASSERT_ENABLE 2
+-#define PCIE_CORE_INT_C_ASSERT_ENABLE 3
+-#define PCIE_CORE_INT_D_ASSERT_ENABLE 4
+ /* PIO registers base address and register offsets */
+ #define PIO_BASE_ADDR 0x4000
+ #define PIO_CTRL (PIO_BASE_ADDR + 0x0)
+@@ -966,7 +962,7 @@ static int advk_sw_pci_bridge_init(struc
+ bridge->conf.pref_mem_limit = cpu_to_le16(PCI_PREF_RANGE_TYPE_64);
+
+ /* Support interrupt A for MSI feature */
+- bridge->conf.intpin = PCIE_CORE_INT_A_ASSERT_ENABLE;
++ bridge->conf.intpin = PCI_INTERRUPT_INTA;
+
+ /* Aardvark HW provides PCIe Capability structure in version 2 */
+ bridge->pcie_conf.cap = cpu_to_le16(2);
--- /dev/null
+From a29a7d01cd778854e08108461cba321a63d98871 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 2 Jul 2021 16:39:47 +0200
+Subject: [PATCH] PCI: aardvark: Fix reading MSI interrupt number
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In advk_pcie_handle_msi() the authors expect that when bit i in the W1C
+register PCIE_MSI_STATUS_REG is cleared, the PCIE_MSI_PAYLOAD_REG is
+updated to contain the MSI number corresponding to index i.
+
+Experiments show that this is not so, and instead PCIE_MSI_PAYLOAD_REG
+always contains the number of the last received MSI, overall.
+
+Do not read PCIE_MSI_PAYLOAD_REG register for determining MSI interrupt
+number. Since Aardvark already forbids more than 32 interrupts and uses
+own allocated hwirq numbers, the msi_idx already corresponds to the
+received MSI number.
+
+Fixes: 8c39d710363c ("PCI: aardvark: Add Aardvark PCI host controller driver")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1393,7 +1393,7 @@ static void advk_pcie_remove_irq_domain(
+ static void advk_pcie_handle_msi(struct advk_pcie *pcie)
+ {
+ u32 msi_val, msi_mask, msi_status, msi_idx;
+- u16 msi_data;
++ int virq;
+
+ msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
+ msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG);
+@@ -1403,13 +1403,12 @@ static void advk_pcie_handle_msi(struct
+ if (!(BIT(msi_idx) & msi_status))
+ continue;
+
+- /*
+- * msi_idx contains bits [4:0] of the msi_data and msi_data
+- * contains 16bit MSI interrupt number
+- */
+ advk_writel(pcie, BIT(msi_idx), PCIE_MSI_STATUS_REG);
+- msi_data = advk_readl(pcie, PCIE_MSI_PAYLOAD_REG) & PCIE_MSI_DATA_MASK;
+- generic_handle_irq(msi_data);
++ virq = irq_find_mapping(pcie->msi_inner_domain, msi_idx);
++ if (virq)
++ generic_handle_irq(virq);
++ else
++ dev_err_ratelimited(&pcie->pdev->dev, "unexpected MSI 0x%02x\n", msi_idx);
+ }
+
+ advk_writel(pcie, PCIE_ISR0_MSI_INT_PENDING,
--- /dev/null
+From bb03b126ea6c9e57177b537dd022246fa5dbef16 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 12 Feb 2021 16:24:07 +0100
+Subject: [PATCH] PCI: aardvark: Fix support for MSI interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Aardvark hardware supports Multi-MSI and MSI_FLAG_MULTI_PCI_MSI is already
+set for the MSI chip. But when allocating MSI interrupt numbers for
+Multi-MSI, the numbers need to be properly aligned, otherwise endpoint
+devices send MSI interrupt with incorrect numbers.
+
+Fix this issue by using function bitmap_find_free_region() instead of
+bitmap_find_next_zero_area().
+
+To ensure that aligned MSI interrupt numbers are used by endpoint devices,
+we cannot use Linux virtual irq numbers (as they are random and not
+properly aligned). Instead we need to use the aligned hwirq numbers.
+
+This change fixes receiving MSI interrupts on Armada 3720 boards and
+allows using NVMe disks which use Multi-MSI feature with 3 interrupts.
+
+Without this NVMe disks freeze booting as linux nvme-core.c is waiting
+60s for an interrupt.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 16 ++++++----------
+ 1 file changed, 6 insertions(+), 10 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1189,7 +1189,7 @@ static void advk_msi_irq_compose_msi_msg
+
+ msg->address_lo = lower_32_bits(msi_msg);
+ msg->address_hi = upper_32_bits(msi_msg);
+- msg->data = data->irq;
++ msg->data = data->hwirq;
+ }
+
+ static int advk_msi_set_affinity(struct irq_data *irq_data,
+@@ -1206,15 +1206,11 @@ static int advk_msi_irq_domain_alloc(str
+ int hwirq, i;
+
+ mutex_lock(&pcie->msi_used_lock);
+- hwirq = bitmap_find_next_zero_area(pcie->msi_used, MSI_IRQ_NUM,
+- 0, nr_irqs, 0);
+- if (hwirq >= MSI_IRQ_NUM) {
+- mutex_unlock(&pcie->msi_used_lock);
+- return -ENOSPC;
+- }
+-
+- bitmap_set(pcie->msi_used, hwirq, nr_irqs);
++ hwirq = bitmap_find_free_region(pcie->msi_used, MSI_IRQ_NUM,
++ order_base_2(nr_irqs));
+ mutex_unlock(&pcie->msi_used_lock);
++ if (hwirq < 0)
++ return -ENOSPC;
+
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_info(domain, virq + i, hwirq + i,
+@@ -1232,7 +1228,7 @@ static void advk_msi_irq_domain_free(str
+ struct advk_pcie *pcie = domain->host_data;
+
+ mutex_lock(&pcie->msi_used_lock);
+- bitmap_clear(pcie->msi_used, d->hwirq, nr_irqs);
++ bitmap_release_region(pcie->msi_used, d->hwirq, order_base_2(nr_irqs));
+ mutex_unlock(&pcie->msi_used_lock);
+ }
+
--- /dev/null
+From 0cd5141d1866afb23286fe90cd846441fe7aeb39 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sat, 27 Mar 2021 14:44:11 +0100
+Subject: [PATCH] PCI: aardvark: Rewrite IRQ code to chained IRQ handler
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Rewrite the code to use irq_set_chained_handler_and_data() handler with
+chained_irq_enter() and chained_irq_exit() processing instead of using
+devm_request_irq().
+
+advk_pcie_irq_handler() reads IRQ status bits and calls other functions
+based on which bits are set. These functions then read its own IRQ status
+bits and calls other aardvark functions based on these bits. Finally
+generic_handle_domain_irq() with translated linux IRQ numbers are called.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 48 +++++++++++++++------------
+ 1 file changed, 26 insertions(+), 22 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -275,6 +275,7 @@ struct advk_pcie {
+ u32 actions;
+ } wins[OB_WIN_COUNT];
+ u8 wins_count;
++ int irq;
+ struct irq_domain *irq_domain;
+ struct irq_chip irq_chip;
+ raw_spinlock_t irq_lock;
+@@ -1442,21 +1443,26 @@ static void advk_pcie_handle_int(struct
+ }
+ }
+
+-static irqreturn_t advk_pcie_irq_handler(int irq, void *arg)
++static void advk_pcie_irq_handler(struct irq_desc *desc)
+ {
+- struct advk_pcie *pcie = arg;
+- u32 status;
++ struct advk_pcie *pcie = irq_desc_get_handler_data(desc);
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ u32 val, mask, status;
+
+- status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG);
+- if (!(status & PCIE_IRQ_CORE_INT))
+- return IRQ_NONE;
++ chained_irq_enter(chip, desc);
+
+- advk_pcie_handle_int(pcie);
++ val = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG);
++ mask = advk_readl(pcie, HOST_CTRL_INT_MASK_REG);
++ status = val & ((~mask) & PCIE_IRQ_ALL_MASK);
+
+- /* Clear interrupt */
+- advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG);
++ if (status & PCIE_IRQ_CORE_INT) {
++ advk_pcie_handle_int(pcie);
+
+- return IRQ_HANDLED;
++ /* Clear interrupt */
++ advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG);
++ }
++
++ chained_irq_exit(chip, desc);
+ }
+
+ static void __maybe_unused advk_pcie_disable_phy(struct advk_pcie *pcie)
+@@ -1523,7 +1529,7 @@ static int advk_pcie_probe(struct platfo
+ struct advk_pcie *pcie;
+ struct pci_host_bridge *bridge;
+ struct resource_entry *entry;
+- int ret, irq;
++ int ret;
+
+ bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct advk_pcie));
+ if (!bridge)
+@@ -1611,17 +1617,9 @@ static int advk_pcie_probe(struct platfo
+ if (IS_ERR(pcie->base))
+ return PTR_ERR(pcie->base);
+
+- irq = platform_get_irq(pdev, 0);
+- if (irq < 0)
+- return irq;
+-
+- ret = devm_request_irq(dev, irq, advk_pcie_irq_handler,
+- IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie",
+- pcie);
+- if (ret) {
+- dev_err(dev, "Failed to register interrupt\n");
+- return ret;
+- }
++ pcie->irq = platform_get_irq(pdev, 0);
++ if (pcie->irq < 0)
++ return pcie->irq;
+
+ pcie->reset_gpio = devm_gpiod_get_from_of_node(dev, dev->of_node,
+ "reset-gpios", 0,
+@@ -1670,11 +1668,14 @@ static int advk_pcie_probe(struct platfo
+ return ret;
+ }
+
++ irq_set_chained_handler_and_data(pcie->irq, advk_pcie_irq_handler, pcie);
++
+ bridge->sysdata = pcie;
+ bridge->ops = &advk_pcie_ops;
+
+ ret = pci_host_probe(bridge);
+ if (ret < 0) {
++ irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
+ advk_pcie_remove_msi_irq_domain(pcie);
+ advk_pcie_remove_irq_domain(pcie);
+ return ret;
+@@ -1722,6 +1723,9 @@ static int advk_pcie_remove(struct platf
+ advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
+ advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
+
++ /* Remove IRQ handler */
++ irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
++
+ /* Remove IRQ domains */
+ advk_pcie_remove_msi_irq_domain(pcie);
+ advk_pcie_remove_irq_domain(pcie);
--- /dev/null
+From 69c1f2c6f45a556361fd8e8d2d4eb20e2c8d3d95 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 18 Mar 2021 17:04:32 +0100
+Subject: [PATCH] PCI: aardvark: Check return value of
+ generic_handle_domain_irq() when processing INTx IRQ
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It is possible that we receive spurious INTx interrupt. Check for the
+return value of generic_handle_domain_irq() when processing INTx IRQ.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1439,7 +1439,9 @@ static void advk_pcie_handle_int(struct
+ PCIE_ISR1_REG);
+
+ virq = irq_find_mapping(pcie->irq_domain, i);
+- generic_handle_irq(virq);
++ if (generic_handle_irq(virq) == -EINVAL)
++ dev_err_ratelimited(&pcie->pdev->dev, "unexpected INT%c IRQ\n",
++ (char)i + 'A');
+ }
+ }
+
--- /dev/null
+From 5eb36a6b9508da442aac80f4df23e3951bbfa7aa Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 10 Jan 2022 00:03:41 +0100
+Subject: [PATCH] PCI: aardvark: Make MSI irq_chip structures static driver
+ structures
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Marc Zyngier says [1] that we should use struct irq_chip as a global
+static struct in the driver. Even though the structure currently
+contains a dynamic member (parent_device), Marc says [2] that he plans
+to kill it and make the structure completely static.
+
+Convert Aardvark's priv->msi_bottom_irq_chip and priv->msi_irq_chip to
+static driver structure.
+
+[1] https://lore.kernel.org/linux-pci/877dbcvngf.wl-maz@kernel.org/
+[2] https://lore.kernel.org/linux-pci/874k6gvkhz.wl-maz@kernel.org/
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 26 ++++++++++++--------------
+ 1 file changed, 12 insertions(+), 14 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -281,8 +281,6 @@ struct advk_pcie {
+ raw_spinlock_t irq_lock;
+ struct irq_domain *msi_domain;
+ struct irq_domain *msi_inner_domain;
+- struct irq_chip msi_bottom_irq_chip;
+- struct irq_chip msi_irq_chip;
+ struct msi_domain_info msi_domain_info;
+ DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
+ struct mutex msi_used_lock;
+@@ -1199,6 +1197,12 @@ static int advk_msi_set_affinity(struct
+ return -EINVAL;
+ }
+
++static struct irq_chip advk_msi_bottom_irq_chip = {
++ .name = "MSI",
++ .irq_compose_msi_msg = advk_msi_irq_compose_msi_msg,
++ .irq_set_affinity = advk_msi_set_affinity,
++};
++
+ static int advk_msi_irq_domain_alloc(struct irq_domain *domain,
+ unsigned int virq,
+ unsigned int nr_irqs, void *args)
+@@ -1215,7 +1219,7 @@ static int advk_msi_irq_domain_alloc(str
+
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_info(domain, virq + i, hwirq + i,
+- &pcie->msi_bottom_irq_chip,
++ &advk_msi_bottom_irq_chip,
+ domain->host_data, handle_simple_irq,
+ NULL, NULL);
+
+@@ -1285,29 +1289,23 @@ static const struct irq_domain_ops advk_
+ .xlate = irq_domain_xlate_onecell,
+ };
+
++static struct irq_chip advk_msi_irq_chip = {
++ .name = "advk-MSI",
++};
++
+ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
+ {
+ struct device *dev = &pcie->pdev->dev;
+ struct device_node *node = dev->of_node;
+- struct irq_chip *bottom_ic, *msi_ic;
+ struct msi_domain_info *msi_di;
+ phys_addr_t msi_msg_phys;
+
+ mutex_init(&pcie->msi_used_lock);
+
+- bottom_ic = &pcie->msi_bottom_irq_chip;
+-
+- bottom_ic->name = "MSI";
+- bottom_ic->irq_compose_msi_msg = advk_msi_irq_compose_msi_msg;
+- bottom_ic->irq_set_affinity = advk_msi_set_affinity;
+-
+- msi_ic = &pcie->msi_irq_chip;
+- msi_ic->name = "advk-MSI";
+-
+ msi_di = &pcie->msi_domain_info;
+ msi_di->flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_MULTI_PCI_MSI;
+- msi_di->chip = msi_ic;
++ msi_di->chip = &advk_msi_irq_chip;
+
+ msi_msg_phys = virt_to_phys(&pcie->msi_msg);
+
--- /dev/null
+From c092ab8994f1f777054c0179a9deb40b87ee606f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 10 Jan 2022 00:10:46 +0100
+Subject: [PATCH] PCI: aardvark: Make msi_domain_info structure a static driver
+ structure
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Make Aardvark's msi_domain_info structure into a private driver structure.
+Domain info is same for every potential instatination of a controller.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -281,7 +281,6 @@ struct advk_pcie {
+ raw_spinlock_t irq_lock;
+ struct irq_domain *msi_domain;
+ struct irq_domain *msi_inner_domain;
+- struct msi_domain_info msi_domain_info;
+ DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
+ struct mutex msi_used_lock;
+ u16 msi_msg;
+@@ -1293,20 +1292,20 @@ static struct irq_chip advk_msi_irq_chip
+ .name = "advk-MSI",
+ };
+
++static struct msi_domain_info advk_msi_domain_info = {
++ .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
++ MSI_FLAG_MULTI_PCI_MSI,
++ .chip = &advk_msi_irq_chip,
++};
++
+ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
+ {
+ struct device *dev = &pcie->pdev->dev;
+ struct device_node *node = dev->of_node;
+- struct msi_domain_info *msi_di;
+ phys_addr_t msi_msg_phys;
+
+ mutex_init(&pcie->msi_used_lock);
+
+- msi_di = &pcie->msi_domain_info;
+- msi_di->flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+- MSI_FLAG_MULTI_PCI_MSI;
+- msi_di->chip = &advk_msi_irq_chip;
+-
+ msi_msg_phys = virt_to_phys(&pcie->msi_msg);
+
+ advk_writel(pcie, lower_32_bits(msi_msg_phys),
+@@ -1322,7 +1321,8 @@ static int advk_pcie_init_msi_irq_domain
+
+ pcie->msi_domain =
+ pci_msi_create_irq_domain(of_node_to_fwnode(node),
+- msi_di, pcie->msi_inner_domain);
++ &advk_msi_domain_info,
++ pcie->msi_inner_domain);
+ if (!pcie->msi_domain) {
+ irq_domain_remove(pcie->msi_inner_domain);
+ return -ENOMEM;
--- /dev/null
+From 59029739d42b439628e2f64f3d8f2db9be97deff Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 10 Jan 2022 00:15:17 +0100
+Subject: [PATCH] PCI: aardvark: Use dev_fwnode() instead of
+ of_node_to_fwnode(dev->of_node)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Use simple
+ dev_fwnode(dev)
+instead of
+ struct device_node *node = dev->of_node;
+ of_node_to_fwnode(node)
+especially since the node variable is not used elsewhere in the function.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1301,7 +1301,6 @@ static struct msi_domain_info advk_msi_d
+ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
+ {
+ struct device *dev = &pcie->pdev->dev;
+- struct device_node *node = dev->of_node;
+ phys_addr_t msi_msg_phys;
+
+ mutex_init(&pcie->msi_used_lock);
+@@ -1320,7 +1319,7 @@ static int advk_pcie_init_msi_irq_domain
+ return -ENOMEM;
+
+ pcie->msi_domain =
+- pci_msi_create_irq_domain(of_node_to_fwnode(node),
++ pci_msi_create_irq_domain(dev_fwnode(dev),
+ &advk_msi_domain_info,
+ pcie->msi_inner_domain);
+ if (!pcie->msi_domain) {
--- /dev/null
+From 98feaf97bc64fc640a6c5b1394cd18fc7cd7dac8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sun, 28 Mar 2021 14:34:49 +0200
+Subject: [PATCH] PCI: aardvark: Refactor unmasking summary MSI interrupt
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Refactor the masking of ISR0/1 Sources and unmasking of summary MSI interrupt
+so that it corresponds to the comments:
+- first mask all ISR0/1
+- then unmask all MSIs
+- then unmask summary MSI interrupt
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -578,15 +578,17 @@ static void advk_pcie_setup_hw(struct ad
+ advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
+
+ /* Disable All ISR0/1 Sources */
+- reg = PCIE_ISR0_ALL_MASK;
+- reg &= ~PCIE_ISR0_MSI_INT_PENDING;
+- advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
+-
++ advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_MASK_REG);
+ advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
+
+ /* Unmask all MSIs */
+ advk_writel(pcie, ~(u32)PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
+
++ /* Unmask summary MSI interrupt */
++ reg = advk_readl(pcie, PCIE_ISR0_MASK_REG);
++ reg &= ~PCIE_ISR0_MSI_INT_PENDING;
++ advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
++
+ /* Enable summary interrupt for GIC SPI source */
+ reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK);
+ advk_writel(pcie, reg, HOST_CTRL_INT_MASK_REG);
--- /dev/null
+From 7f353accca6e4a3222991c65b1a6801503973bd3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 2 Jul 2021 16:44:10 +0200
+Subject: [PATCH] PCI: aardvark: Add support for masking MSI interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+We should not unmask MSIs at setup, but only when kernel asks for them
+to be unmasked.
+
+At setup, mask all MSIs, and implement IRQ chip callbacks for masking
+and unmasking particular MSIs.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 54 ++++++++++++++++++++++++---
+ 1 file changed, 49 insertions(+), 5 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -281,6 +281,7 @@ struct advk_pcie {
+ raw_spinlock_t irq_lock;
+ struct irq_domain *msi_domain;
+ struct irq_domain *msi_inner_domain;
++ raw_spinlock_t msi_irq_lock;
+ DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
+ struct mutex msi_used_lock;
+ u16 msi_msg;
+@@ -577,12 +578,10 @@ static void advk_pcie_setup_hw(struct ad
+ advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
+ advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
+
+- /* Disable All ISR0/1 Sources */
++ /* Disable All ISR0/1 and MSI Sources */
+ advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_MASK_REG);
+ advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
+-
+- /* Unmask all MSIs */
+- advk_writel(pcie, ~(u32)PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
++ advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
+
+ /* Unmask summary MSI interrupt */
+ reg = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+@@ -1198,10 +1197,52 @@ static int advk_msi_set_affinity(struct
+ return -EINVAL;
+ }
+
++static void advk_msi_irq_mask(struct irq_data *d)
++{
++ struct advk_pcie *pcie = d->domain->host_data;
++ irq_hw_number_t hwirq = irqd_to_hwirq(d);
++ unsigned long flags;
++ u32 mask;
++
++ raw_spin_lock_irqsave(&pcie->msi_irq_lock, flags);
++ mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
++ mask |= BIT(hwirq);
++ advk_writel(pcie, mask, PCIE_MSI_MASK_REG);
++ raw_spin_unlock_irqrestore(&pcie->msi_irq_lock, flags);
++}
++
++static void advk_msi_irq_unmask(struct irq_data *d)
++{
++ struct advk_pcie *pcie = d->domain->host_data;
++ irq_hw_number_t hwirq = irqd_to_hwirq(d);
++ unsigned long flags;
++ u32 mask;
++
++ raw_spin_lock_irqsave(&pcie->msi_irq_lock, flags);
++ mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
++ mask &= ~BIT(hwirq);
++ advk_writel(pcie, mask, PCIE_MSI_MASK_REG);
++ raw_spin_unlock_irqrestore(&pcie->msi_irq_lock, flags);
++}
++
++static void advk_msi_top_irq_mask(struct irq_data *d)
++{
++ pci_msi_mask_irq(d);
++ irq_chip_mask_parent(d);
++}
++
++static void advk_msi_top_irq_unmask(struct irq_data *d)
++{
++ pci_msi_unmask_irq(d);
++ irq_chip_unmask_parent(d);
++}
++
+ static struct irq_chip advk_msi_bottom_irq_chip = {
+ .name = "MSI",
+ .irq_compose_msi_msg = advk_msi_irq_compose_msi_msg,
+ .irq_set_affinity = advk_msi_set_affinity,
++ .irq_mask = advk_msi_irq_mask,
++ .irq_unmask = advk_msi_irq_unmask,
+ };
+
+ static int advk_msi_irq_domain_alloc(struct irq_domain *domain,
+@@ -1291,7 +1332,9 @@ static const struct irq_domain_ops advk_
+ };
+
+ static struct irq_chip advk_msi_irq_chip = {
+- .name = "advk-MSI",
++ .name = "advk-MSI",
++ .irq_mask = advk_msi_top_irq_mask,
++ .irq_unmask = advk_msi_top_irq_unmask,
+ };
+
+ static struct msi_domain_info advk_msi_domain_info = {
+@@ -1305,6 +1348,7 @@ static int advk_pcie_init_msi_irq_domain
+ struct device *dev = &pcie->pdev->dev;
+ phys_addr_t msi_msg_phys;
+
++ raw_spin_lock_init(&pcie->msi_irq_lock);
+ mutex_init(&pcie->msi_used_lock);
+
+ msi_msg_phys = virt_to_phys(&pcie->msi_msg);
--- /dev/null
+From fa73c200f181436eab859374657c53a73778d8ad Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 26 Mar 2021 17:35:44 +0100
+Subject: [PATCH] PCI: aardvark: Fix setting MSI address
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+MSI address for receiving MSI interrupts needs to be correctly set before
+enabling processing of MSI interrupts.
+
+Move code for setting PCIE_MSI_ADDR_LOW_REG and PCIE_MSI_ADDR_HIGH_REG
+from advk_pcie_init_msi_irq_domain() to advk_pcie_setup_hw(), before
+enabling PCIE_CORE_CTRL2_MSI_ENABLE.
+
+After this we can remove the now unused member msi_msg, which was used
+only for MSI doorbell address. MSI address can be any address which cannot
+be used to DMA to. So change it to the address of the main struct advk_pcie.
+
+Fixes: 8c39d710363c ("PCI: aardvark: Add Aardvark PCI host controller driver")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Acked-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Cc: stable@vger.kernel.org # f21a8b1b6837 ("PCI: aardvark: Move to MSI handling using generic MSI support")
+---
+ drivers/pci/controller/pci-aardvark.c | 21 +++++++++------------
+ 1 file changed, 9 insertions(+), 12 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -284,7 +284,6 @@ struct advk_pcie {
+ raw_spinlock_t msi_irq_lock;
+ DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
+ struct mutex msi_used_lock;
+- u16 msi_msg;
+ int link_gen;
+ struct pci_bridge_emul bridge;
+ struct gpio_desc *reset_gpio;
+@@ -479,6 +478,7 @@ static void advk_pcie_disable_ob_win(str
+
+ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
+ {
++ phys_addr_t msi_addr;
+ u32 reg;
+ int i;
+
+@@ -567,6 +567,11 @@ static void advk_pcie_setup_hw(struct ad
+ reg |= LANE_COUNT_1;
+ advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
+
++ /* Set MSI address */
++ msi_addr = virt_to_phys(pcie);
++ advk_writel(pcie, lower_32_bits(msi_addr), PCIE_MSI_ADDR_LOW_REG);
++ advk_writel(pcie, upper_32_bits(msi_addr), PCIE_MSI_ADDR_HIGH_REG);
++
+ /* Enable MSI */
+ reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG);
+ reg |= PCIE_CORE_CTRL2_MSI_ENABLE;
+@@ -1184,10 +1189,10 @@ static void advk_msi_irq_compose_msi_msg
+ struct msi_msg *msg)
+ {
+ struct advk_pcie *pcie = irq_data_get_irq_chip_data(data);
+- phys_addr_t msi_msg = virt_to_phys(&pcie->msi_msg);
++ phys_addr_t msi_addr = virt_to_phys(pcie);
+
+- msg->address_lo = lower_32_bits(msi_msg);
+- msg->address_hi = upper_32_bits(msi_msg);
++ msg->address_lo = lower_32_bits(msi_addr);
++ msg->address_hi = upper_32_bits(msi_addr);
+ msg->data = data->hwirq;
+ }
+
+@@ -1346,18 +1351,10 @@ static struct msi_domain_info advk_msi_d
+ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
+ {
+ struct device *dev = &pcie->pdev->dev;
+- phys_addr_t msi_msg_phys;
+
+ raw_spin_lock_init(&pcie->msi_irq_lock);
+ mutex_init(&pcie->msi_used_lock);
+
+- msi_msg_phys = virt_to_phys(&pcie->msi_msg);
+-
+- advk_writel(pcie, lower_32_bits(msi_msg_phys),
+- PCIE_MSI_ADDR_LOW_REG);
+- advk_writel(pcie, upper_32_bits(msi_msg_phys),
+- PCIE_MSI_ADDR_HIGH_REG);
+-
+ pcie->msi_inner_domain =
+ irq_domain_add_linear(NULL, MSI_IRQ_NUM,
+ &advk_msi_domain_ops, pcie);
--- /dev/null
+From 735a4ac9782b96fbe1543c578aa8334364f21abd Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 2 Apr 2021 14:05:24 +0200
+Subject: [PATCH] PCI: aardvark: Enable MSI-X support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+According to PCI 3.0 specification, sending both MSI and MSI-X interrupts
+is done by DWORD memory write operation to doorbell message address. The
+write operation for MSI has zero upper 16 bits and the MSI interrupt number
+in the lower 16 bits, while the write operation for MSI-X contains a 32-bit
+value from MSI-X table.
+
+Since the driver only uses interrupt numbers from range 0..31, the upper
+16 bits of the DWORD memory write operation to doorbell message address
+are zero even for MSI-X interrupts. Thus we can enable MSI-X interrupts.
+
+Testing proves that kernel can correctly receive MSI-X interrupts from PCIe
+cards which supports both MSI and MSI-X interrupts.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1344,7 +1344,7 @@ static struct irq_chip advk_msi_irq_chip
+
+ static struct msi_domain_info advk_msi_domain_info = {
+ .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+- MSI_FLAG_MULTI_PCI_MSI,
++ MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
+ .chip = &advk_msi_irq_chip,
+ };
+
--- /dev/null
+From 7f3e55a3890fa26d15e2e4e90213962d1a7f6df9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 12 Feb 2021 20:32:55 +0100
+Subject: [PATCH] PCI: aardvark: Add support for ERR interrupt on emulated
+ bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+ERR interrupt is triggered when corresponding bit is unmasked in both ISR0
+and PCI_EXP_DEVCTL registers. Unmasking ERR bits in PCI_EXP_DEVCTL register
+is not enough. This means that currently the ERR interrupt is never
+triggered.
+
+Unmask ERR bits in ISR0 register at driver probe time. ERR interrupt is not
+triggered until ERR bits are unmasked also in PCI_EXP_DEVCTL register,
+which is done by AER driver. So it is safe to unconditionally unmask all
+ERR bits in aardvark probe.
+
+Aardvark HW sets PCI_ERR_ROOT_AER_IRQ to zero and when corresponding bits
+in ISR0 and PCI_EXP_DEVCTL are enabled, the HW triggers a generic interrupt
+on GIC. Chain this interrupt to PCIe interrupt 0 with
+generic_handle_domain_irq() to allow processing of ERR interrupts.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 36 ++++++++++++++++++++++++++-
+ 1 file changed, 35 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -97,6 +97,10 @@
+ #define PCIE_MSG_PM_PME_MASK BIT(7)
+ #define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44)
+ #define PCIE_ISR0_MSI_INT_PENDING BIT(24)
++#define PCIE_ISR0_CORR_ERR BIT(11)
++#define PCIE_ISR0_NFAT_ERR BIT(12)
++#define PCIE_ISR0_FAT_ERR BIT(13)
++#define PCIE_ISR0_ERR_MASK GENMASK(13, 11)
+ #define PCIE_ISR0_INTX_ASSERT(val) BIT(16 + (val))
+ #define PCIE_ISR0_INTX_DEASSERT(val) BIT(20 + (val))
+ #define PCIE_ISR0_ALL_MASK GENMASK(31, 0)
+@@ -785,11 +789,15 @@ advk_pci_bridge_emul_base_conf_read(stru
+ case PCI_INTERRUPT_LINE: {
+ /*
+ * From the whole 32bit register we support reading from HW only
+- * one bit: PCI_BRIDGE_CTL_BUS_RESET.
++ * two bits: PCI_BRIDGE_CTL_BUS_RESET and PCI_BRIDGE_CTL_SERR.
+ * Other bits are retrieved only from emulated config buffer.
+ */
+ __le32 *cfgspace = (__le32 *)&bridge->conf;
+ u32 val = le32_to_cpu(cfgspace[PCI_INTERRUPT_LINE / 4]);
++ if (advk_readl(pcie, PCIE_ISR0_MASK_REG) & PCIE_ISR0_ERR_MASK)
++ val &= ~(PCI_BRIDGE_CTL_SERR << 16);
++ else
++ val |= PCI_BRIDGE_CTL_SERR << 16;
+ if (advk_readl(pcie, PCIE_CORE_CTRL1_REG) & HOT_RESET_GEN)
+ val |= PCI_BRIDGE_CTL_BUS_RESET << 16;
+ else
+@@ -815,6 +823,19 @@ advk_pci_bridge_emul_base_conf_write(str
+ break;
+
+ case PCI_INTERRUPT_LINE:
++ /*
++ * According to Figure 6-3: Pseudo Logic Diagram for Error
++ * Message Controls in PCIe base specification, SERR# Enable bit
++ * in Bridge Control register enable receiving of ERR_* messages
++ */
++ if (mask & (PCI_BRIDGE_CTL_SERR << 16)) {
++ u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
++ if (new & (PCI_BRIDGE_CTL_SERR << 16))
++ val &= ~PCIE_ISR0_ERR_MASK;
++ else
++ val |= PCIE_ISR0_ERR_MASK;
++ advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
++ }
+ if (mask & (PCI_BRIDGE_CTL_BUS_RESET << 16)) {
+ u32 val = advk_readl(pcie, PCIE_CORE_CTRL1_REG);
+ if (new & (PCI_BRIDGE_CTL_BUS_RESET << 16))
+@@ -1466,6 +1487,19 @@ static void advk_pcie_handle_int(struct
+ isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG);
+ isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK);
+
++ /* Process ERR interrupt */
++ if (isr0_status & PCIE_ISR0_ERR_MASK) {
++ advk_writel(pcie, PCIE_ISR0_ERR_MASK, PCIE_ISR0_REG);
++
++ /*
++ * Aardvark HW returns zero for PCI_ERR_ROOT_AER_IRQ, so use
++ * PCIe interrupt 0
++ */
++ virq = irq_find_mapping(pcie->irq_domain, 0);
++ if (generic_handle_irq(virq) == -EINVAL)
++ dev_err_ratelimited(&pcie->pdev->dev, "unhandled ERR IRQ\n");
++ }
++
+ /* Process MSI interrupts */
+ if (isr0_status & PCIE_ISR0_MSI_INT_PENDING)
+ advk_pcie_handle_msi(pcie);
--- /dev/null
+From 5f354992eeef9a51c67796dc9f7f578d3584baa2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Wed, 8 Dec 2021 05:57:54 +0100
+Subject: [PATCH] PCI: aardvark: Fix reading PCI_EXP_RTSTA_PME bit on emulated
+ bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The emulated bridge returns incorrect value for PCI_EXP_RTSTA register
+during readout in advk_pci_bridge_emul_pcie_conf_read() function: the
+correct bit is BIT(16), but we are setting BIT(23), because the code
+does
+ *value = (isr0 & PCIE_MSG_PM_PME_MASK) << 16
+where
+ PCIE_MSG_PM_PME_MASK
+is
+ BIT(7).
+
+The code should probably have been something like
+ *value = (!!(isr0 & PCIE_MSG_PM_PME_MASK)) << 16,
+but we are better of using an if() and using the proper macro for this
+bit.
+
+Fixes: 8a3ebd8de328 ("PCI: aardvark: Implement emulated root PCI bridge config space")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -874,7 +874,9 @@ advk_pci_bridge_emul_pcie_conf_read(stru
+ case PCI_EXP_RTSTA: {
+ u32 isr0 = advk_readl(pcie, PCIE_ISR0_REG);
+ u32 msglog = advk_readl(pcie, PCIE_MSG_LOG_REG);
+- *value = (isr0 & PCIE_MSG_PM_PME_MASK) << 16 | (msglog >> 16);
++ *value = msglog >> 16;
++ if (isr0 & PCIE_MSG_PM_PME_MASK)
++ *value |= PCI_EXP_RTSTA_PME;
+ return PCI_BRIDGE_EMUL_HANDLED;
+ }
+
--- /dev/null
+From 3fe0073d116d9902df08761c1cf0d733dd4c38fc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Wed, 8 Dec 2021 06:03:50 +0100
+Subject: [PATCH] PCI: aardvark: Optimize writing PCI_EXP_RTCTL_PMEIE and
+ PCI_EXP_RTSTA_PME on emulated bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+To optimize advk_pci_bridge_emul_pcie_conf_write() code, touch
+PCIE_ISR0_REG and PCIE_ISR0_MASK_REG registers only when it is really
+needed, when processing PCI_EXP_RTCTL_PMEIE and PCI_EXP_RTSTA_PME bits.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 20 +++++++++++---------
+ 1 file changed, 11 insertions(+), 9 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -932,19 +932,21 @@ advk_pci_bridge_emul_pcie_conf_write(str
+ advk_pcie_wait_for_retrain(pcie);
+ break;
+
+- case PCI_EXP_RTCTL: {
++ case PCI_EXP_RTCTL:
+ /* Only mask/unmask PME interrupt */
+- u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG) &
+- ~PCIE_MSG_PM_PME_MASK;
+- if ((new & PCI_EXP_RTCTL_PMEIE) == 0)
+- val |= PCIE_MSG_PM_PME_MASK;
+- advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
++ if (mask & PCI_EXP_RTCTL_PMEIE) {
++ u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
++ if (new & PCI_EXP_RTCTL_PMEIE)
++ val &= ~PCIE_MSG_PM_PME_MASK;
++ else
++ val |= PCIE_MSG_PM_PME_MASK;
++ advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
++ }
+ break;
+- }
+
+ case PCI_EXP_RTSTA:
+- new = (new & PCI_EXP_RTSTA_PME) >> 9;
+- advk_writel(pcie, new, PCIE_ISR0_REG);
++ if (new & PCI_EXP_RTSTA_PME)
++ advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG);
+ break;
+
+ case PCI_EXP_DEVCTL:
--- /dev/null
+From 7acd8ef92e8789e10b5d736d73cea3b625087f26 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Wed, 8 Dec 2021 06:07:44 +0100
+Subject: [PATCH] PCI: aardvark: Add support for PME interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Currently enabling PCI_EXP_RTSTA_PME bit in PCI_EXP_RTCTL register does
+nothing. This is because PCIe PME driver expects to receive PCIe interrupt
+defined in PCI_EXP_FLAGS_IRQ register, but aardvark hardware does not
+trigger PCIe INTx/MSI interrupt for PME event, rather it triggers custom
+aardvark interrupt which this driver is not processing yet.
+
+Fix this issue by handling PME interrupt in advk_pcie_handle_int() and
+chaining it to PCIe interrupt 0 with generic_handle_domain_irq() (since
+aardvark sets PCI_EXP_FLAGS_IRQ to zero). With this change PCIe PME driver
+finally starts receiving PME interrupt.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1491,6 +1491,19 @@ static void advk_pcie_handle_int(struct
+ isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG);
+ isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK);
+
++ /* Process PME interrupt */
++ if (isr0_status & PCIE_MSG_PM_PME_MASK) {
++ /*
++ * Do not clear PME interrupt bit in ISR0, it is cleared by IRQ
++ * receiver by writing to the PCI_EXP_RTSTA register of emulated
++ * root bridge. Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ,
++ * so use PCIe interrupt 0.
++ */
++ virq = irq_find_mapping(pcie->irq_domain, 0);
++ if (generic_handle_irq(virq) == -EINVAL)
++ dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
++ }
++
+ /* Process ERR interrupt */
+ if (isr0_status & PCIE_ISR0_ERR_MASK) {
+ advk_writel(pcie, PCIE_ISR0_ERR_MASK, PCIE_ISR0_REG);
--- /dev/null
+From 68727b545332327b4c2f9c0f8d006be8970e7832 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 19 Feb 2021 14:22:22 +0100
+Subject: [PATCH] PCI: aardvark: Fix support for PME requester on emulated
+ bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Enable aardvark PME interrupt unconditionally by unmasking it and read PME
+requester ID to emulated bridge config space immediately after receiving
+interrupt.
+
+PME requester ID is stored in the PCIE_MSG_LOG_REG register, which contains
+the last inbound message. So when new inbound message is received by HW
+(including non-PM), the content in PCIE_MSG_LOG_REG register is replaced by
+a new value.
+
+PCIe specification mandates that subsequent PMEs are kept pending until the
+PME Status Register bit is cleared by software by writing a 1b.
+
+Support for masking/unmasking PME interrupt on emulated bridge via
+PCI_EXP_RTCTL_PMEIE bit is now implemented only in emulated bridge config
+space, to ensure that we do not miss any aardvark PME interrupt.
+
+Reading of PCI_EXP_RTCAP and PCI_EXP_RTSTA registers is simplified as final
+value is now always stored into emulated bridge config space by the
+interrupt handler, so there is no need to implement support for these
+registers in read_pcie callback.
+
+Clearing of W1C bit PCI_EXP_RTSTA_PME is now also simplified as it is done
+by pci-bridge-emul.c code for emulated bridge config space. So there is no
+need to implement support for clearing this bit in write_pcie callback.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 94 +++++++++++++++------------
+ 1 file changed, 52 insertions(+), 42 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -597,6 +597,11 @@ static void advk_pcie_setup_hw(struct ad
+ reg &= ~PCIE_ISR0_MSI_INT_PENDING;
+ advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
+
++ /* Unmask PME interrupt for processing of PME requester */
++ reg = advk_readl(pcie, PCIE_ISR0_MASK_REG);
++ reg &= ~PCIE_MSG_PM_PME_MASK;
++ advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
++
+ /* Enable summary interrupt for GIC SPI source */
+ reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK);
+ advk_writel(pcie, reg, HOST_CTRL_INT_MASK_REG);
+@@ -863,22 +868,11 @@ advk_pci_bridge_emul_pcie_conf_read(stru
+ *value = PCI_EXP_SLTSTA_PDS << 16;
+ return PCI_BRIDGE_EMUL_HANDLED;
+
+- case PCI_EXP_RTCTL: {
+- u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+- *value = (val & PCIE_MSG_PM_PME_MASK) ? 0 : PCI_EXP_RTCTL_PMEIE;
+- *value |= le16_to_cpu(bridge->pcie_conf.rootctl) & PCI_EXP_RTCTL_CRSSVE;
+- *value |= PCI_EXP_RTCAP_CRSVIS << 16;
+- return PCI_BRIDGE_EMUL_HANDLED;
+- }
+-
+- case PCI_EXP_RTSTA: {
+- u32 isr0 = advk_readl(pcie, PCIE_ISR0_REG);
+- u32 msglog = advk_readl(pcie, PCIE_MSG_LOG_REG);
+- *value = msglog >> 16;
+- if (isr0 & PCIE_MSG_PM_PME_MASK)
+- *value |= PCI_EXP_RTSTA_PME;
+- return PCI_BRIDGE_EMUL_HANDLED;
+- }
++ /*
++ * PCI_EXP_RTCTL and PCI_EXP_RTSTA are also supported, but do not need
++ * to be handled here, because their values are stored in emulated
++ * config space buffer, and we read them from there when needed.
++ */
+
+ case PCI_EXP_LNKCAP: {
+ u32 val = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg);
+@@ -932,22 +926,19 @@ advk_pci_bridge_emul_pcie_conf_write(str
+ advk_pcie_wait_for_retrain(pcie);
+ break;
+
+- case PCI_EXP_RTCTL:
+- /* Only mask/unmask PME interrupt */
+- if (mask & PCI_EXP_RTCTL_PMEIE) {
+- u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+- if (new & PCI_EXP_RTCTL_PMEIE)
+- val &= ~PCIE_MSG_PM_PME_MASK;
+- else
+- val |= PCIE_MSG_PM_PME_MASK;
+- advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
+- }
++ case PCI_EXP_RTCTL: {
++ u16 rootctl = le16_to_cpu(bridge->pcie_conf.rootctl);
++ /* Only emulation of PMEIE and CRSSVE bits is provided */
++ rootctl &= PCI_EXP_RTCTL_PMEIE | PCI_EXP_RTCTL_CRSSVE;
++ bridge->pcie_conf.rootctl = cpu_to_le16(rootctl);
+ break;
++ }
+
+- case PCI_EXP_RTSTA:
+- if (new & PCI_EXP_RTSTA_PME)
+- advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG);
+- break;
++ /*
++ * PCI_EXP_RTSTA is also supported, but does not need to be handled
++ * here, because its value is stored in emulated config space buffer,
++ * and we write it there when needed.
++ */
+
+ case PCI_EXP_DEVCTL:
+ case PCI_EXP_DEVCTL2:
+@@ -1452,6 +1443,34 @@ static void advk_pcie_remove_irq_domain(
+ irq_domain_remove(pcie->irq_domain);
+ }
+
++static void advk_pcie_handle_pme(struct advk_pcie *pcie)
++{
++ u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16;
++ int virq;
++
++ advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG);
++
++ /*
++ * PCIE_MSG_LOG_REG contains the last inbound message, so store
++ * the requester ID only when PME was not asserted yet.
++ * Also do not trigger PME interrupt when PME is still asserted.
++ */
++ if (!(le32_to_cpu(pcie->bridge.pcie_conf.rootsta) & PCI_EXP_RTSTA_PME)) {
++ pcie->bridge.pcie_conf.rootsta = cpu_to_le32(requester | PCI_EXP_RTSTA_PME);
++
++ /*
++ * Trigger PME interrupt only if PMEIE bit in Root Control is set.
++ * Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ, so use PCIe interrupt 0.
++ */
++ if (!(le16_to_cpu(pcie->bridge.pcie_conf.rootctl) & PCI_EXP_RTCTL_PMEIE))
++ return;
++
++ virq = irq_find_mapping(pcie->irq_domain, 0);
++ if (generic_handle_irq(virq) == -EINVAL)
++ dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
++ }
++}
++
+ static void advk_pcie_handle_msi(struct advk_pcie *pcie)
+ {
+ u32 msi_val, msi_mask, msi_status, msi_idx;
+@@ -1491,18 +1510,9 @@ static void advk_pcie_handle_int(struct
+ isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG);
+ isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK);
+
+- /* Process PME interrupt */
+- if (isr0_status & PCIE_MSG_PM_PME_MASK) {
+- /*
+- * Do not clear PME interrupt bit in ISR0, it is cleared by IRQ
+- * receiver by writing to the PCI_EXP_RTSTA register of emulated
+- * root bridge. Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ,
+- * so use PCIe interrupt 0.
+- */
+- virq = irq_find_mapping(pcie->irq_domain, 0);
+- if (generic_handle_irq(virq) == -EINVAL)
+- dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
+- }
++ /* Process PME interrupt as the first one to do not miss PME requester id */
++ if (isr0_status & PCIE_MSG_PM_PME_MASK)
++ advk_pcie_handle_pme(pcie);
+
+ /* Process ERR interrupt */
+ if (isr0_status & PCIE_ISR0_ERR_MASK) {
--- /dev/null
+From db305233136f5aa2444a8287a279384e8458c458 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 1 Apr 2021 20:12:48 +0200
+Subject: [PATCH] PCI: aardvark: Use separate INTA interrupt for emulated root
+ bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Emulated root bridge currently provides only one Legacy INTA interrupt
+which is used for reporting PCIe PME and ERR events and handled by kernel
+PCIe PME and AER drivers.
+
+Aardvark HW reports these PME and ERR events separately, so there is no
+need to mix real INTA interrupt and emulated INTA interrupt for PCIe PME
+and AER drivers.
+
+Register a new advk-RP (as in Root Port) irq chip and a new irq domain
+for emulated root bridge and use this new separate irq domain for
+providing INTA interrupt from emulated root bridge for PME and ERR events.
+
+The real INTA interrupt from real devices is now separate.
+
+A custom map_irq callback function on PCI host bridge structure is used to
+allocate IRQ mapping for emulated root bridge from new irq domain. Original
+callback of_irq_parse_and_map_pci() is used for all other devices as before.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 69 ++++++++++++++++++++++++++-
+ 1 file changed, 67 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -280,6 +280,7 @@ struct advk_pcie {
+ } wins[OB_WIN_COUNT];
+ u8 wins_count;
+ int irq;
++ struct irq_domain *rp_irq_domain;
+ struct irq_domain *irq_domain;
+ struct irq_chip irq_chip;
+ raw_spinlock_t irq_lock;
+@@ -1443,6 +1444,44 @@ static void advk_pcie_remove_irq_domain(
+ irq_domain_remove(pcie->irq_domain);
+ }
+
++static struct irq_chip advk_rp_irq_chip = {
++ .name = "advk-RP",
++};
++
++static int advk_pcie_rp_irq_map(struct irq_domain *h,
++ unsigned int virq, irq_hw_number_t hwirq)
++{
++ struct advk_pcie *pcie = h->host_data;
++
++ irq_set_chip_and_handler(virq, &advk_rp_irq_chip, handle_simple_irq);
++ irq_set_chip_data(virq, pcie);
++
++ return 0;
++}
++
++static const struct irq_domain_ops advk_pcie_rp_irq_domain_ops = {
++ .map = advk_pcie_rp_irq_map,
++ .xlate = irq_domain_xlate_onecell,
++};
++
++static int advk_pcie_init_rp_irq_domain(struct advk_pcie *pcie)
++{
++ pcie->rp_irq_domain = irq_domain_add_linear(NULL, 1,
++ &advk_pcie_rp_irq_domain_ops,
++ pcie);
++ if (!pcie->rp_irq_domain) {
++ dev_err(&pcie->pdev->dev, "Failed to add Root Port IRQ domain\n");
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++static void advk_pcie_remove_rp_irq_domain(struct advk_pcie *pcie)
++{
++ irq_domain_remove(pcie->rp_irq_domain);
++}
++
+ static void advk_pcie_handle_pme(struct advk_pcie *pcie)
+ {
+ u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16;
+@@ -1465,7 +1504,7 @@ static void advk_pcie_handle_pme(struct
+ if (!(le16_to_cpu(pcie->bridge.pcie_conf.rootctl) & PCI_EXP_RTCTL_PMEIE))
+ return;
+
+- virq = irq_find_mapping(pcie->irq_domain, 0);
++ virq = irq_find_mapping(pcie->rp_irq_domain, 0);
+ if (generic_handle_irq(virq) == -EINVAL)
+ dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
+ }
+@@ -1522,7 +1561,7 @@ static void advk_pcie_handle_int(struct
+ * Aardvark HW returns zero for PCI_ERR_ROOT_AER_IRQ, so use
+ * PCIe interrupt 0
+ */
+- virq = irq_find_mapping(pcie->irq_domain, 0);
++ virq = irq_find_mapping(pcie->rp_irq_domain, 0);
+ if (generic_handle_irq(virq) == -EINVAL)
+ dev_err_ratelimited(&pcie->pdev->dev, "unhandled ERR IRQ\n");
+ }
+@@ -1568,6 +1607,21 @@ static void advk_pcie_irq_handler(struct
+ chained_irq_exit(chip, desc);
+ }
+
++static int advk_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
++{
++ struct advk_pcie *pcie = dev->bus->sysdata;
++
++ /*
++ * Emulated root bridge has its own emulated irq chip and irq domain.
++ * Argument pin is the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) and
++ * hwirq for irq_create_mapping() is indexed from zero.
++ */
++ if (pci_is_root_bus(dev->bus))
++ return irq_create_mapping(pcie->rp_irq_domain, pin - 1);
++ else
++ return of_irq_parse_and_map_pci(dev, slot, pin);
++}
++
+ static void __maybe_unused advk_pcie_disable_phy(struct advk_pcie *pcie)
+ {
+ phy_power_off(pcie->phy);
+@@ -1771,14 +1825,24 @@ static int advk_pcie_probe(struct platfo
+ return ret;
+ }
+
++ ret = advk_pcie_init_rp_irq_domain(pcie);
++ if (ret) {
++ dev_err(dev, "Failed to initialize irq\n");
++ advk_pcie_remove_msi_irq_domain(pcie);
++ advk_pcie_remove_irq_domain(pcie);
++ return ret;
++ }
++
+ irq_set_chained_handler_and_data(pcie->irq, advk_pcie_irq_handler, pcie);
+
+ bridge->sysdata = pcie;
+ bridge->ops = &advk_pcie_ops;
++ bridge->map_irq = advk_pcie_map_irq;
+
+ ret = pci_host_probe(bridge);
+ if (ret < 0) {
+ irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
++ advk_pcie_remove_rp_irq_domain(pcie);
+ advk_pcie_remove_msi_irq_domain(pcie);
+ advk_pcie_remove_irq_domain(pcie);
+ return ret;
+@@ -1830,6 +1894,7 @@ static int advk_pcie_remove(struct platf
+ irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
+
+ /* Remove IRQ domains */
++ advk_pcie_remove_rp_irq_domain(pcie);
+ advk_pcie_remove_msi_irq_domain(pcie);
+ advk_pcie_remove_irq_domain(pcie);
+
--- /dev/null
+From 8c9eef96e24f34ff8b62b230700416b822691a37 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 1 Apr 2021 14:24:12 +0200
+Subject: [PATCH] PCI: aardvark: Remove irq_mask_ack callback for INTx
+ interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Callback for irq_mask_ack is the same as for irq_mask. As there is no
+special handling for irq_ack, there is no need to define irq_mask_ack too.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Acked-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1422,7 +1422,6 @@ static int advk_pcie_init_irq_domain(str
+ }
+
+ irq_chip->irq_mask = advk_pcie_irq_mask;
+- irq_chip->irq_mask_ack = advk_pcie_irq_mask;
+ irq_chip->irq_unmask = advk_pcie_irq_unmask;
+
+ pcie->irq_domain =
--- /dev/null
+From dc01fca5a9d9c09ce9a3fb2bc2e7715c37ff3bd9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 1 Apr 2021 14:30:06 +0200
+Subject: [PATCH] PCI: aardvark: Don't mask irq when mapping
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+By default, all Legacy INTx interrupts are masked, so there is no need to
+mask this interrupt during irq_map callback.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1339,7 +1339,6 @@ static int advk_pcie_irq_map(struct irq_
+ {
+ struct advk_pcie *pcie = h->host_data;
+
+- advk_pcie_irq_mask(irq_get_irq_data(virq));
+ irq_set_status_flags(virq, IRQ_LEVEL);
+ irq_set_chip_and_handler(virq, &pcie->irq_chip,
+ handle_level_irq);
--- /dev/null
+From a511c99262ce19ee06908d27212b39ec4c5aeb17 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Wed, 8 Dec 2021 04:40:29 +0100
+Subject: [PATCH] PCI: aardvark: Drop __maybe_unused from
+ advk_pcie_disable_phy()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This function is now always used in driver remove method, drop the
+__maybe_unused attribute.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1620,7 +1620,7 @@ static int advk_pcie_map_irq(const struc
+ return of_irq_parse_and_map_pci(dev, slot, pin);
+ }
+
+-static void __maybe_unused advk_pcie_disable_phy(struct advk_pcie *pcie)
++static void advk_pcie_disable_phy(struct advk_pcie *pcie)
+ {
+ phy_power_off(pcie->phy);
+ phy_exit(pcie->phy);
--- /dev/null
+From bafda858364003a70b9cda84282f9761587f8033 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 10 Jan 2022 00:47:38 +0100
+Subject: [PATCH] PCI: aardvark: Update comment about link going down after
+ link-up
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Update the comment about what happens when link goes down after we have
+checked for link-up. If a PIO request is done while link-down, we have
+a serious problem.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1005,8 +1005,12 @@ static bool advk_pcie_valid_device(struc
+ return false;
+
+ /*
+- * If the link goes down after we check for link-up, nothing bad
+- * happens but the config access times out.
++ * If the link goes down after we check for link-up, we have a problem:
++ * if a PIO request is executed while link-down, the whole controller
++ * gets stuck in a non-functional state, and even after link comes up
++ * again, PIO requests won't work anymore, and a reset of the whole PCIe
++ * controller is needed. Therefore we need to prevent sending PIO
++ * requests while the link is down.
+ */
+ if (!pci_is_root_bus(bus) && !advk_pcie_link_up(pcie))
+ return false;
--- /dev/null
+From 663b9f99bb35dbc0c7b685f71ee3668a60d31320 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 10 Jan 2022 02:02:00 +0100
+Subject: [PATCH] PCI: aardvark: Make main irq_chip structure a static driver
+ structure
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Marc Zyngier says [1] that we should use struct irq_chip as a global
+static struct in the driver. Even though the structure currently
+contains a dynamic member (parent_device), Marc says [2] that he plans
+to kill it and make the structure completely static.
+
+We have already converted others irq_chip structures in this driver in
+this way, but we omitted this one because the .name member is
+dynamically created from device's name, and the name is displayed in
+sysfs, so changing it would break sysfs ABI.
+
+The rationale for changing the name (to "advk-INT") in spite of sysfs
+ABI, and thus allowing to convert to a static structure, is that after
+the other changes we made in this series, the IRQ chip is basically
+something different: it no logner generates ERR and PME interrupts (they
+are generated by emulated bridge's rp_irq_chip).
+
+[1] https://lore.kernel.org/linux-pci/877dbcvngf.wl-maz@kernel.org/
+[2] https://lore.kernel.org/linux-pci/874k6gvkhz.wl-maz@kernel.org/
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 25 +++++++------------------
+ 1 file changed, 7 insertions(+), 18 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -282,7 +282,6 @@ struct advk_pcie {
+ int irq;
+ struct irq_domain *rp_irq_domain;
+ struct irq_domain *irq_domain;
+- struct irq_chip irq_chip;
+ raw_spinlock_t irq_lock;
+ struct irq_domain *msi_domain;
+ struct irq_domain *msi_inner_domain;
+@@ -1338,14 +1337,19 @@ static void advk_pcie_irq_unmask(struct
+ raw_spin_unlock_irqrestore(&pcie->irq_lock, flags);
+ }
+
++static struct irq_chip advk_irq_chip = {
++ .name = "advk-INT",
++ .irq_mask = advk_pcie_irq_mask,
++ .irq_unmask = advk_pcie_irq_unmask,
++};
++
+ static int advk_pcie_irq_map(struct irq_domain *h,
+ unsigned int virq, irq_hw_number_t hwirq)
+ {
+ struct advk_pcie *pcie = h->host_data;
+
+ irq_set_status_flags(virq, IRQ_LEVEL);
+- irq_set_chip_and_handler(virq, &pcie->irq_chip,
+- handle_level_irq);
++ irq_set_chip_and_handler(virq, &advk_irq_chip, handle_level_irq);
+ irq_set_chip_data(virq, pcie);
+
+ return 0;
+@@ -1404,7 +1408,6 @@ static int advk_pcie_init_irq_domain(str
+ struct device *dev = &pcie->pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct device_node *pcie_intc_node;
+- struct irq_chip *irq_chip;
+ int ret = 0;
+
+ raw_spin_lock_init(&pcie->irq_lock);
+@@ -1415,28 +1418,14 @@ static int advk_pcie_init_irq_domain(str
+ return -ENODEV;
+ }
+
+- irq_chip = &pcie->irq_chip;
+-
+- irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq",
+- dev_name(dev));
+- if (!irq_chip->name) {
+- ret = -ENOMEM;
+- goto out_put_node;
+- }
+-
+- irq_chip->irq_mask = advk_pcie_irq_mask;
+- irq_chip->irq_unmask = advk_pcie_irq_unmask;
+-
+ pcie->irq_domain =
+ irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
+ &advk_pcie_irq_domain_ops, pcie);
+ if (!pcie->irq_domain) {
+ dev_err(dev, "Failed to get a INTx IRQ domain\n");
+ ret = -ENOMEM;
+- goto out_put_node;
+ }
+
+-out_put_node:
+ of_node_put(pcie_intc_node);
+ return ret;
+ }
--- /dev/null
+From a719f7ba7fcba05d85801c6f0267f389a21627c1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 24 Sep 2021 13:03:02 +0200
+Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Remove port from driver
+ configuration
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Port number is encoded into argument for SMC call. It is zero for SATA,
+PCIe and also both USB 3.0 PHYs. It is non-zero only for Ethernet PHY
+(incorrectly called SGMII) on lane 0. Ethernet PHY on lane 1 also uses zero
+port number.
+
+So construct "port" bits for SMC call argument can be constructed directly
+from PHY type and lane number.
+
+Change driver code to always pass zero port number for non-ethernet PHYs
+and for ethernet PHYs determinate port number from lane number. This
+simplifies the driver.
+
+As port number from DT PHY configuration is not used anymore, remove whole
+driver code which parses it. This also simplifies the driver.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 62 +++++++++-----------
+ 1 file changed, 29 insertions(+), 33 deletions(-)
+
+--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
+@@ -20,7 +20,6 @@
+ #include <linux/platform_device.h>
+
+ #define MVEBU_A3700_COMPHY_LANES 3
+-#define MVEBU_A3700_COMPHY_PORTS 2
+
+ /* COMPHY Fast SMC function identifiers */
+ #define COMPHY_SIP_POWER_ON 0x82000001
+@@ -45,51 +44,47 @@
+ #define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \
+ ((idx) << 8) | \
+ ((speed) << 2))
+-#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \
++#define COMPHY_FW_PCIE(mode, speed, width) (COMPHY_FW_NET(mode, 0, speed) | \
+ ((width) << 18))
+
+ struct mvebu_a3700_comphy_conf {
+ unsigned int lane;
+ enum phy_mode mode;
+ int submode;
+- unsigned int port;
+ u32 fw_mode;
+ };
+
+-#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \
++#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _fw) \
+ { \
+ .lane = _lane, \
+ .mode = _mode, \
+ .submode = _smode, \
+- .port = _port, \
+ .fw_mode = _fw, \
+ }
+
+-#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \
+- MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw)
++#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _fw) \
++ MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _fw)
+
+-#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \
+- MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw)
++#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _fw) \
++ MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _fw)
+
+ static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
+ /* lane 0 */
+- MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0,
++ MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS,
+ COMPHY_FW_MODE_USB3H),
+- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1,
++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII,
+ COMPHY_FW_MODE_SGMII),
+- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1,
++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX,
+ COMPHY_FW_MODE_2500BASEX),
+ /* lane 1 */
+- MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0,
+- COMPHY_FW_MODE_PCIE),
+- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0,
++ MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE),
++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII,
+ COMPHY_FW_MODE_SGMII),
+- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0,
++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX,
+ COMPHY_FW_MODE_2500BASEX),
+ /* lane 2 */
+- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0,
+- COMPHY_FW_MODE_SATA),
+- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0,
++ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, COMPHY_FW_MODE_SATA),
++ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS,
+ COMPHY_FW_MODE_USB3H),
+ };
+
+@@ -98,7 +93,6 @@ struct mvebu_a3700_comphy_lane {
+ unsigned int id;
+ enum phy_mode mode;
+ int submode;
+- int port;
+ };
+
+ static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
+@@ -120,7 +114,7 @@ static int mvebu_a3700_comphy_smc(unsign
+ }
+ }
+
+-static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
++static int mvebu_a3700_comphy_get_fw_mode(int lane,
+ enum phy_mode mode,
+ int submode)
+ {
+@@ -132,7 +126,6 @@ static int mvebu_a3700_comphy_get_fw_mod
+
+ for (i = 0; i < n; i++) {
+ if (mvebu_a3700_comphy_modes[i].lane == lane &&
+- mvebu_a3700_comphy_modes[i].port == port &&
+ mvebu_a3700_comphy_modes[i].mode == mode &&
+ mvebu_a3700_comphy_modes[i].submode == submode)
+ break;
+@@ -153,7 +146,7 @@ static int mvebu_a3700_comphy_set_mode(s
+ if (submode == PHY_INTERFACE_MODE_1000BASEX)
+ submode = PHY_INTERFACE_MODE_SGMII;
+
+- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode,
++ fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, mode,
+ submode);
+ if (fw_mode < 0) {
+ dev_err(lane->dev, "invalid COMPHY mode\n");
+@@ -172,9 +165,10 @@ static int mvebu_a3700_comphy_power_on(s
+ struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
+ u32 fw_param;
+ int fw_mode;
++ int fw_port;
+ int ret;
+
+- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port,
++ fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id,
+ lane->mode, lane->submode);
+ if (fw_mode < 0) {
+ dev_err(lane->dev, "invalid COMPHY mode\n");
+@@ -191,17 +185,18 @@ static int mvebu_a3700_comphy_power_on(s
+ fw_param = COMPHY_FW_MODE(fw_mode);
+ break;
+ case PHY_MODE_ETHERNET:
++ fw_port = (lane->id == 0) ? 1 : 0;
+ switch (lane->submode) {
+ case PHY_INTERFACE_MODE_SGMII:
+ dev_dbg(lane->dev, "set lane %d to SGMII mode\n",
+ lane->id);
+- fw_param = COMPHY_FW_NET(fw_mode, lane->port,
++ fw_param = COMPHY_FW_NET(fw_mode, fw_port,
+ COMPHY_FW_SPEED_1_25G);
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n",
+ lane->id);
+- fw_param = COMPHY_FW_NET(fw_mode, lane->port,
++ fw_param = COMPHY_FW_NET(fw_mode, fw_port,
+ COMPHY_FW_SPEED_3_125G);
+ break;
+ default:
+@@ -212,8 +207,7 @@ static int mvebu_a3700_comphy_power_on(s
+ break;
+ case PHY_MODE_PCIE:
+ dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id);
+- fw_param = COMPHY_FW_PCIE(fw_mode, lane->port,
+- COMPHY_FW_SPEED_5G,
++ fw_param = COMPHY_FW_PCIE(fw_mode, COMPHY_FW_SPEED_5G,
+ phy->attrs.bus_width);
+ break;
+ default:
+@@ -247,17 +241,20 @@ static struct phy *mvebu_a3700_comphy_xl
+ struct of_phandle_args *args)
+ {
+ struct mvebu_a3700_comphy_lane *lane;
++ unsigned int port;
+ struct phy *phy;
+
+- if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS))
+- return ERR_PTR(-EINVAL);
+-
+ phy = of_phy_simple_xlate(dev, args);
+ if (IS_ERR(phy))
+ return phy;
+
+ lane = phy_get_drvdata(phy);
+- lane->port = args->args[0];
++
++ port = args->args[0];
++ if (port != 0 && (port != 1 || lane->id != 0)) {
++ dev_err(lane->dev, "invalid port number %u\n", port);
++ return ERR_PTR(-EINVAL);
++ }
+
+ return phy;
+ }
+@@ -302,7 +299,6 @@ static int mvebu_a3700_comphy_probe(stru
+ lane->mode = PHY_MODE_INVALID;
+ lane->submode = PHY_INTERFACE_MODE_NA;
+ lane->id = lane_id;
+- lane->port = -1;
+ phy_set_drvdata(phy, lane);
+ }
+
--- /dev/null
+From 9d276da259cce20b2ed7a868b6e6a6a205f7bb04 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 23 Sep 2021 19:20:13 +0200
+Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Add native kernel
+ implementation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Remove old RPC implementation and add a new native kernel implementation.
+
+The old implementation uses ARM SMC API to issue RPC calls to ARM Trusted
+Firmware which provides real implementation of PHY configuration.
+
+But older versions of ARM Trusted Firmware do not provide this PHY
+configuration functionality, simply returning: operation not supported; or
+worse, some versions provide the configuration functionality incorrectly.
+
+For example the firmware shipped in ESPRESSObin board has this older
+version of ARM Trusted Firmware and therefore SATA, USB 3.0 and PCIe
+functionality do not work with newer versions of Linux kernel.
+
+Due to the above reasons, the following commits were introduced into Linux,
+to workaround these issues by ignoring -EOPNOTSUPP error code from
+phy-mvebu-a3700-comphy driver function phy_power_on():
+
+commit 45aefe3d2251 ("ata: ahci: mvebu: Make SATA PHY optional for Armada
+3720")
+commit 3241929b67d2 ("usb: host: xhci: mvebu: make USB 3.0 PHY optional for
+Armada 3720")
+commit b0c6ae0f8948 ("PCI: aardvark: Fix initialization with old Marvell's
+Arm Trusted Firmware")
+
+Replace this RPC implementation with proper native kernel implementation,
+which is independent on the firmware. Never return -EOPNOTSUPP for proper
+arguments.
+
+This should solve multiple issues with real-world boards, where it is not
+possible or really inconvenient to change the firmware. Let's eliminate
+these issues.
+
+This implementation is ported directly from Armada 3720 comphy driver found
+in newest version of ARM Trusted Firmware source code, but with various
+fixes of register names, some added comments, some refactoring due to the
+original code not conforming to kernel standards. Also PCIe mode poweroff
+support was added here, and PHY reset support. These changes are also going
+to be sent to ARM Trusted Firmware.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
+[ Pali did the porting from ATF.
+ I (Marek) then fixed some register names, some various other things,
+ added some comments and refactored the code to kernel standards. Also
+ fixed PHY poweroff and added PHY reset. ]
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 1351 ++++++++++++++++--
+ 1 file changed, 1234 insertions(+), 117 deletions(-)
+
+--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
+@@ -5,12 +5,16 @@
+ * Authors:
+ * Evan Wang <xswang@marvell.com>
+ * Miquèl Raynal <miquel.raynal@bootlin.com>
++ * Pali Rohár <pali@kernel.org>
++ * Marek Behún <kabel@kernel.org>
+ *
+ * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart.
+- * SMC call initial support done by Grzegorz Jaszczyk.
++ * Comphy code from ARM Trusted Firmware ported by Pali Rohár <pali@kernel.org>
++ * and Marek Behún <kabel@kernel.org>.
+ */
+
+-#include <linux/arm-smccc.h>
++#include <linux/bitfield.h>
++#include <linux/clk.h>
+ #include <linux/io.h>
+ #include <linux/iopoll.h>
+ #include <linux/mfd/syscon.h>
+@@ -18,103 +22,1147 @@
+ #include <linux/phy.h>
+ #include <linux/phy/phy.h>
+ #include <linux/platform_device.h>
++#include <linux/spinlock.h>
+
+-#define MVEBU_A3700_COMPHY_LANES 3
++#define PLL_SET_DELAY_US 600
++#define COMPHY_PLL_SLEEP 1000
++#define COMPHY_PLL_TIMEOUT 150000
++
++/* Comphy lane2 indirect access register offset */
++#define COMPHY_LANE2_INDIR_ADDR 0x0
++#define COMPHY_LANE2_INDIR_DATA 0x4
++
++/* SATA and USB3 PHY offset compared to SATA PHY */
++#define COMPHY_LANE2_REGS_BASE 0x200
++
++/*
++ * When accessing common PHY lane registers directly, we need to shift by 1,
++ * since the registers are 16-bit.
++ */
++#define COMPHY_LANE_REG_DIRECT(reg) (((reg) & 0x7FF) << 1)
++
++/* COMPHY registers */
++#define COMPHY_POWER_PLL_CTRL 0x01
++#define PU_IVREF_BIT BIT(15)
++#define PU_PLL_BIT BIT(14)
++#define PU_RX_BIT BIT(13)
++#define PU_TX_BIT BIT(12)
++#define PU_TX_INTP_BIT BIT(11)
++#define PU_DFE_BIT BIT(10)
++#define RESET_DTL_RX_BIT BIT(9)
++#define PLL_LOCK_BIT BIT(8)
++#define REF_FREF_SEL_MASK GENMASK(4, 0)
++#define REF_FREF_SEL_SERDES_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x1)
++#define REF_FREF_SEL_SERDES_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3)
++#define REF_FREF_SEL_SERDES_50MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x4)
++#define REF_FREF_SEL_PCIE_USB3_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x2)
++#define REF_FREF_SEL_PCIE_USB3_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3)
++#define COMPHY_MODE_MASK GENMASK(7, 5)
++#define COMPHY_MODE_SATA FIELD_PREP(COMPHY_MODE_MASK, 0x0)
++#define COMPHY_MODE_PCIE FIELD_PREP(COMPHY_MODE_MASK, 0x3)
++#define COMPHY_MODE_SERDES FIELD_PREP(COMPHY_MODE_MASK, 0x4)
++#define COMPHY_MODE_USB3 FIELD_PREP(COMPHY_MODE_MASK, 0x5)
++
++#define COMPHY_KVCO_CAL_CTRL 0x02
++#define USE_MAX_PLL_RATE_BIT BIT(12)
++#define SPEED_PLL_MASK GENMASK(7, 2)
++#define SPEED_PLL_VALUE_16 FIELD_PREP(SPEED_PLL_MASK, 0x10)
++
++#define COMPHY_DIG_LOOPBACK_EN 0x23
++#define SEL_DATA_WIDTH_MASK GENMASK(11, 10)
++#define DATA_WIDTH_10BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x0)
++#define DATA_WIDTH_20BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x1)
++#define DATA_WIDTH_40BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x2)
++#define PLL_READY_TX_BIT BIT(4)
++
++#define COMPHY_SYNC_PATTERN 0x24
++#define TXD_INVERT_BIT BIT(10)
++#define RXD_INVERT_BIT BIT(11)
++
++#define COMPHY_SYNC_MASK_GEN 0x25
++#define PHY_GEN_MAX_MASK GENMASK(11, 10)
++#define PHY_GEN_MAX_USB3_5G FIELD_PREP(PHY_GEN_MAX_MASK, 0x1)
++
++#define COMPHY_ISOLATION_CTRL 0x26
++#define PHY_ISOLATE_MODE BIT(15)
++
++#define COMPHY_GEN2_SET2 0x3e
++#define GS2_TX_SSC_AMP_MASK GENMASK(15, 9)
++#define GS2_TX_SSC_AMP_4128 FIELD_PREP(GS2_TX_SSC_AMP_MASK, 0x20)
++#define GS2_VREG_RXTX_MAS_ISET_MASK GENMASK(8, 7)
++#define GS2_VREG_RXTX_MAS_ISET_60U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
++ 0x0)
++#define GS2_VREG_RXTX_MAS_ISET_80U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
++ 0x1)
++#define GS2_VREG_RXTX_MAS_ISET_100U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
++ 0x2)
++#define GS2_VREG_RXTX_MAS_ISET_120U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
++ 0x3)
++#define GS2_RSVD_6_0_MASK GENMASK(6, 0)
++
++#define COMPHY_GEN3_SET2 0x3f
++
++#define COMPHY_IDLE_SYNC_EN 0x48
++#define IDLE_SYNC_EN BIT(12)
++
++#define COMPHY_MISC_CTRL0 0x4F
++#define CLK100M_125M_EN BIT(4)
++#define TXDCLK_2X_SEL BIT(6)
++#define CLK500M_EN BIT(7)
++#define PHY_REF_CLK_SEL BIT(10)
++
++#define COMPHY_SFT_RESET 0x52
++#define SFT_RST BIT(9)
++#define SFT_RST_NO_REG BIT(10)
++
++#define COMPHY_MISC_CTRL1 0x73
++#define SEL_BITS_PCIE_FORCE BIT(15)
++
++#define COMPHY_GEN2_SET3 0x112
++#define GS3_FFE_CAP_SEL_MASK GENMASK(3, 0)
++#define GS3_FFE_CAP_SEL_VALUE FIELD_PREP(GS3_FFE_CAP_SEL_MASK, 0xF)
++
++/* PIPE registers */
++#define COMPHY_PIPE_LANE_CFG0 0x180
++#define PRD_TXDEEMPH0_MASK BIT(0)
++#define PRD_TXMARGIN_MASK GENMASK(3, 1)
++#define PRD_TXSWING_MASK BIT(4)
++#define CFG_TX_ALIGN_POS_MASK GENMASK(8, 5)
++
++#define COMPHY_PIPE_LANE_CFG1 0x181
++#define PRD_TXDEEMPH1_MASK BIT(15)
++#define USE_MAX_PLL_RATE_EN BIT(9)
++#define TX_DET_RX_MODE BIT(6)
++#define GEN2_TX_DATA_DLY_MASK GENMASK(4, 3)
++#define GEN2_TX_DATA_DLY_DEFT FIELD_PREP(GEN2_TX_DATA_DLY_MASK, 2)
++#define TX_ELEC_IDLE_MODE_EN BIT(0)
++
++#define COMPHY_PIPE_LANE_STAT1 0x183
++#define TXDCLK_PCLK_EN BIT(0)
++
++#define COMPHY_PIPE_LANE_CFG4 0x188
++#define SPREAD_SPECTRUM_CLK_EN BIT(7)
++
++#define COMPHY_PIPE_RST_CLK_CTRL 0x1C1
++#define PIPE_SOFT_RESET BIT(0)
++#define PIPE_REG_RESET BIT(1)
++#define MODE_CORE_CLK_FREQ_SEL BIT(9)
++#define MODE_PIPE_WIDTH_32 BIT(3)
++#define MODE_REFDIV_MASK GENMASK(5, 4)
++#define MODE_REFDIV_BY_4 FIELD_PREP(MODE_REFDIV_MASK, 0x2)
++
++#define COMPHY_PIPE_TEST_MODE_CTRL 0x1C2
++#define MODE_MARGIN_OVERRIDE BIT(2)
++
++#define COMPHY_PIPE_CLK_SRC_LO 0x1C3
++#define MODE_CLK_SRC BIT(0)
++#define BUNDLE_PERIOD_SEL BIT(1)
++#define BUNDLE_PERIOD_SCALE_MASK GENMASK(3, 2)
++#define BUNDLE_SAMPLE_CTRL BIT(4)
++#define PLL_READY_DLY_MASK GENMASK(7, 5)
++#define CFG_SEL_20B BIT(15)
++
++#define COMPHY_PIPE_PWR_MGM_TIM1 0x1D0
++#define CFG_PM_OSCCLK_WAIT_MASK GENMASK(15, 12)
++#define CFG_PM_RXDEN_WAIT_MASK GENMASK(11, 8)
++#define CFG_PM_RXDEN_WAIT_1_UNIT FIELD_PREP(CFG_PM_RXDEN_WAIT_MASK, 0x1)
++#define CFG_PM_RXDLOZ_WAIT_MASK GENMASK(7, 0)
++#define CFG_PM_RXDLOZ_WAIT_7_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0x7)
++#define CFG_PM_RXDLOZ_WAIT_12_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0xC)
++
++/*
++ * This register is not from PHY lane register space. It only exists in the
++ * indirect register space, before the actual PHY lane 2 registers. So the
++ * offset is absolute, not relative to COMPHY_LANE2_REGS_BASE.
++ * It is used only for SATA PHY initialization.
++ */
++#define COMPHY_RESERVED_REG 0x0E
++#define PHYCTRL_FRM_PIN_BIT BIT(13)
+
+-/* COMPHY Fast SMC function identifiers */
+-#define COMPHY_SIP_POWER_ON 0x82000001
+-#define COMPHY_SIP_POWER_OFF 0x82000002
+-#define COMPHY_SIP_PLL_LOCK 0x82000003
+-
+-#define COMPHY_FW_MODE_SATA 0x1
+-#define COMPHY_FW_MODE_SGMII 0x2
+-#define COMPHY_FW_MODE_2500BASEX 0x3
+-#define COMPHY_FW_MODE_USB3H 0x4
+-#define COMPHY_FW_MODE_USB3D 0x5
+-#define COMPHY_FW_MODE_PCIE 0x6
+-#define COMPHY_FW_MODE_USB3 0xa
+-
+-#define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */
+-#define COMPHY_FW_SPEED_2_5G 1
+-#define COMPHY_FW_SPEED_3_125G 2 /* 2500BASE-X */
+-#define COMPHY_FW_SPEED_5G 3
+-#define COMPHY_FW_SPEED_MAX 0x3F
+-
+-#define COMPHY_FW_MODE(mode) ((mode) << 12)
+-#define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \
+- ((idx) << 8) | \
+- ((speed) << 2))
+-#define COMPHY_FW_PCIE(mode, speed, width) (COMPHY_FW_NET(mode, 0, speed) | \
+- ((width) << 18))
++/* South Bridge PHY Configuration Registers */
++#define COMPHY_PHY_REG(lane, reg) (((1 - (lane)) * 0x28) + ((reg) & 0x3f))
++
++/*
++ * lane0: USB3/GbE1 PHY Configuration 1
++ * lane1: PCIe/GbE0 PHY Configuration 1
++ * (used only by SGMII code)
++ */
++#define COMPHY_PHY_CFG1 0x0
++#define PIN_PU_IVREF_BIT BIT(1)
++#define PIN_RESET_CORE_BIT BIT(11)
++#define PIN_RESET_COMPHY_BIT BIT(12)
++#define PIN_PU_PLL_BIT BIT(16)
++#define PIN_PU_RX_BIT BIT(17)
++#define PIN_PU_TX_BIT BIT(18)
++#define PIN_TX_IDLE_BIT BIT(19)
++#define GEN_RX_SEL_MASK GENMASK(25, 22)
++#define GEN_RX_SEL_VALUE(val) FIELD_PREP(GEN_RX_SEL_MASK, (val))
++#define GEN_TX_SEL_MASK GENMASK(29, 26)
++#define GEN_TX_SEL_VALUE(val) FIELD_PREP(GEN_TX_SEL_MASK, (val))
++#define SERDES_SPEED_1_25_G 0x6
++#define SERDES_SPEED_3_125_G 0x8
++#define PHY_RX_INIT_BIT BIT(30)
++
++/*
++ * lane0: USB3/GbE1 PHY Status 1
++ * lane1: PCIe/GbE0 PHY Status 1
++ * (used only by SGMII code)
++ */
++#define COMPHY_PHY_STAT1 0x18
++#define PHY_RX_INIT_DONE_BIT BIT(0)
++#define PHY_PLL_READY_RX_BIT BIT(2)
++#define PHY_PLL_READY_TX_BIT BIT(3)
++
++/* PHY Selector */
++#define COMPHY_SELECTOR_PHY_REG 0xFC
++/* bit0: 0: Lane1 is GbE0; 1: Lane1 is PCIe */
++#define COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT BIT(0)
++/* bit4: 0: Lane0 is GbE1; 1: Lane0 is USB3 */
++#define COMPHY_SELECTOR_USB3_GBE1_SEL_BIT BIT(4)
++/* bit8: 0: Lane0 is USB3 instead of GbE1, Lane2 is SATA; 1: Lane2 is USB3 */
++#define COMPHY_SELECTOR_USB3_PHY_SEL_BIT BIT(8)
+
+ struct mvebu_a3700_comphy_conf {
+ unsigned int lane;
+ enum phy_mode mode;
+ int submode;
+- u32 fw_mode;
+ };
+
+-#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _fw) \
++#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode) \
+ { \
+ .lane = _lane, \
+ .mode = _mode, \
+ .submode = _smode, \
+- .fw_mode = _fw, \
+ }
+
+-#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _fw) \
+- MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _fw)
++#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode) \
++ MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA)
+
+-#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _fw) \
+- MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _fw)
++#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode) \
++ MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode)
+
+ static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
+ /* lane 0 */
+- MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS,
+- COMPHY_FW_MODE_USB3H),
+- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII,
+- COMPHY_FW_MODE_SGMII),
+- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX,
+- COMPHY_FW_MODE_2500BASEX),
++ MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS),
++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII),
++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_1000BASEX),
++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX),
+ /* lane 1 */
+- MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE),
+- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII,
+- COMPHY_FW_MODE_SGMII),
+- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX,
+- COMPHY_FW_MODE_2500BASEX),
++ MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE),
++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII),
++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_1000BASEX),
++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX),
+ /* lane 2 */
+- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, COMPHY_FW_MODE_SATA),
+- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS,
+- COMPHY_FW_MODE_USB3H),
++ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA),
++ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS),
++};
++
++struct mvebu_a3700_comphy_priv {
++ void __iomem *comphy_regs;
++ void __iomem *lane0_phy_regs; /* USB3 and GbE1 */
++ void __iomem *lane1_phy_regs; /* PCIe and GbE0 */
++ void __iomem *lane2_phy_indirect; /* SATA and USB3 */
++ spinlock_t lock; /* for PHY selector access */
++ bool xtal_is_40m;
+ };
+
+ struct mvebu_a3700_comphy_lane {
++ struct mvebu_a3700_comphy_priv *priv;
+ struct device *dev;
+ unsigned int id;
+ enum phy_mode mode;
+ int submode;
++ bool invert_tx;
++ bool invert_rx;
++ bool needs_reset;
++};
++
++struct gbe_phy_init_data_fix {
++ u16 addr;
++ u16 value;
++};
++
++/* Changes to 40M1G25 mode data required for running 40M3G125 init mode */
++static struct gbe_phy_init_data_fix gbe_phy_init_fix[] = {
++ { 0x005, 0x07CC }, { 0x015, 0x0000 }, { 0x01B, 0x0000 },
++ { 0x01D, 0x0000 }, { 0x01E, 0x0000 }, { 0x01F, 0x0000 },
++ { 0x020, 0x0000 }, { 0x021, 0x0030 }, { 0x026, 0x0888 },
++ { 0x04D, 0x0152 }, { 0x04F, 0xA020 }, { 0x050, 0x07CC },
++ { 0x053, 0xE9CA }, { 0x055, 0xBD97 }, { 0x071, 0x3015 },
++ { 0x076, 0x03AA }, { 0x07C, 0x0FDF }, { 0x0C2, 0x3030 },
++ { 0x0C3, 0x8000 }, { 0x0E2, 0x5550 }, { 0x0E3, 0x12A4 },
++ { 0x0E4, 0x7D00 }, { 0x0E6, 0x0C83 }, { 0x101, 0xFCC0 },
++ { 0x104, 0x0C10 }
+ };
+
+-static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
+- unsigned long mode)
++/* 40M1G25 mode init data */
++static u16 gbe_phy_init[512] = {
++ /* 0 1 2 3 4 5 6 7 */
++ /*-----------------------------------------------------------*/
++ /* 8 9 A B C D E F */
++ 0x3110, 0xFD83, 0x6430, 0x412F, 0x82C0, 0x06FA, 0x4500, 0x6D26, /* 00 */
++ 0xAFC0, 0x8000, 0xC000, 0x0000, 0x2000, 0x49CC, 0x0BC9, 0x2A52, /* 08 */
++ 0x0BD2, 0x0CDE, 0x13D2, 0x0CE8, 0x1149, 0x10E0, 0x0000, 0x0000, /* 10 */
++ 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x4134, 0x0D2D, 0xFFFF, /* 18 */
++ 0xFFE0, 0x4030, 0x1016, 0x0030, 0x0000, 0x0800, 0x0866, 0x0000, /* 20 */
++ 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, /* 28 */
++ 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 30 */
++ 0x0000, 0x0000, 0x000F, 0x6A62, 0x1988, 0x3100, 0x3100, 0x3100, /* 38 */
++ 0x3100, 0xA708, 0x2430, 0x0830, 0x1030, 0x4610, 0xFF00, 0xFF00, /* 40 */
++ 0x0060, 0x1000, 0x0400, 0x0040, 0x00F0, 0x0155, 0x1100, 0xA02A, /* 48 */
++ 0x06FA, 0x0080, 0xB008, 0xE3ED, 0x5002, 0xB592, 0x7A80, 0x0001, /* 50 */
++ 0x020A, 0x8820, 0x6014, 0x8054, 0xACAA, 0xFC88, 0x2A02, 0x45CF, /* 58 */
++ 0x000F, 0x1817, 0x2860, 0x064F, 0x0000, 0x0204, 0x1800, 0x6000, /* 60 */
++ 0x810F, 0x4F23, 0x4000, 0x4498, 0x0850, 0x0000, 0x000E, 0x1002, /* 68 */
++ 0x9D3A, 0x3009, 0xD066, 0x0491, 0x0001, 0x6AB0, 0x0399, 0x3780, /* 70 */
++ 0x0040, 0x5AC0, 0x4A80, 0x0000, 0x01DF, 0x0000, 0x0007, 0x0000, /* 78 */
++ 0x2D54, 0x00A1, 0x4000, 0x0100, 0xA20A, 0x0000, 0x0000, 0x0000, /* 80 */
++ 0x0000, 0x0000, 0x0000, 0x7400, 0x0E81, 0x1000, 0x1242, 0x0210, /* 88 */
++ 0x80DF, 0x0F1F, 0x2F3F, 0x4F5F, 0x6F7F, 0x0F1F, 0x2F3F, 0x4F5F, /* 90 */
++ 0x6F7F, 0x4BAD, 0x0000, 0x0000, 0x0800, 0x0000, 0x2400, 0xB651, /* 98 */
++ 0xC9E0, 0x4247, 0x0A24, 0x0000, 0xAF19, 0x1004, 0x0000, 0x0000, /* A0 */
++ 0x0000, 0x0013, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* A8 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* B0 */
++ 0x0000, 0x0000, 0x0000, 0x0060, 0x0000, 0x0000, 0x0000, 0x0000, /* B8 */
++ 0x0000, 0x0000, 0x3010, 0xFA00, 0x0000, 0x0000, 0x0000, 0x0003, /* C0 */
++ 0x1618, 0x8200, 0x8000, 0x0400, 0x050F, 0x0000, 0x0000, 0x0000, /* C8 */
++ 0x4C93, 0x0000, 0x1000, 0x1120, 0x0010, 0x1242, 0x1242, 0x1E00, /* D0 */
++ 0x0000, 0x0000, 0x0000, 0x00F8, 0x0000, 0x0041, 0x0800, 0x0000, /* D8 */
++ 0x82A0, 0x572E, 0x2490, 0x14A9, 0x4E00, 0x0000, 0x0803, 0x0541, /* E0 */
++ 0x0C15, 0x0000, 0x0000, 0x0400, 0x2626, 0x0000, 0x0000, 0x4200, /* E8 */
++ 0x0000, 0xAA55, 0x1020, 0x0000, 0x0000, 0x5010, 0x0000, 0x0000, /* F0 */
++ 0x0000, 0x0000, 0x5000, 0x0000, 0x0000, 0x0000, 0x02F2, 0x0000, /* F8 */
++ 0x101F, 0xFDC0, 0x4000, 0x8010, 0x0110, 0x0006, 0x0000, 0x0000, /*100 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*108 */
++ 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04C6, 0x0000, /*110 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*118 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*120 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*128 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*130 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*138 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*140 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*148 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*150 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*158 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*160 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*168 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*170 */
++ 0x0000, 0x0000, 0x0000, 0x00F0, 0x08A2, 0x3112, 0x0A14, 0x0000, /*178 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*180 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*188 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*190 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*198 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A0 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A8 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B0 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B8 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C0 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C8 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D0 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D8 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E0 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E8 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1F0 */
++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 /*1F8 */
++};
++
++static inline void comphy_reg_set(void __iomem *addr, u32 data, u32 mask)
+ {
+- struct arm_smccc_res res;
+- s32 ret;
++ u32 val;
+
+- arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
+- ret = res.a0;
++ val = readl(addr);
++ val = (val & ~mask) | (data & mask);
++ writel(val, addr);
++}
+
+- switch (ret) {
+- case SMCCC_RET_SUCCESS:
+- return 0;
+- case SMCCC_RET_NOT_SUPPORTED:
+- return -EOPNOTSUPP;
++static inline void comphy_reg_set16(void __iomem *addr, u16 data, u16 mask)
++{
++ u16 val;
++
++ val = readw(addr);
++ val = (val & ~mask) | (data & mask);
++ writew(val, addr);
++}
++
++/* Used for accessing lane 2 registers (SATA/USB3 PHY) */
++static void comphy_set_indirect(struct mvebu_a3700_comphy_priv *priv,
++ u32 offset, u16 data, u16 mask)
++{
++ writel(offset,
++ priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_ADDR);
++ comphy_reg_set(priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_DATA,
++ data, mask);
++}
++
++static void comphy_lane_reg_set(struct mvebu_a3700_comphy_lane *lane,
++ u16 reg, u16 data, u16 mask)
++{
++ if (lane->id == 2) {
++ /* lane 2 PHY registers are accessed indirectly */
++ comphy_set_indirect(lane->priv,
++ reg + COMPHY_LANE2_REGS_BASE,
++ data, mask);
++ } else {
++ void __iomem *base = lane->id == 1 ?
++ lane->priv->lane1_phy_regs :
++ lane->priv->lane0_phy_regs;
++
++ comphy_reg_set16(base + COMPHY_LANE_REG_DIRECT(reg),
++ data, mask);
++ }
++}
++
++static int comphy_lane_reg_poll(struct mvebu_a3700_comphy_lane *lane,
++ u16 reg, u16 bits,
++ ulong sleep_us, ulong timeout_us)
++{
++ int ret;
++
++ if (lane->id == 2) {
++ u32 data;
++
++ /* lane 2 PHY registers are accessed indirectly */
++ writel(reg + COMPHY_LANE2_REGS_BASE,
++ lane->priv->lane2_phy_indirect +
++ COMPHY_LANE2_INDIR_ADDR);
++
++ ret = readl_poll_timeout(lane->priv->lane2_phy_indirect +
++ COMPHY_LANE2_INDIR_DATA,
++ data, (data & bits) == bits,
++ sleep_us, timeout_us);
++ } else {
++ void __iomem *base = lane->id == 1 ?
++ lane->priv->lane1_phy_regs :
++ lane->priv->lane0_phy_regs;
++ u16 data;
++
++ ret = readw_poll_timeout(base + COMPHY_LANE_REG_DIRECT(reg),
++ data, (data & bits) == bits,
++ sleep_us, timeout_us);
++ }
++
++ return ret;
++}
++
++static void comphy_periph_reg_set(struct mvebu_a3700_comphy_lane *lane,
++ u8 reg, u32 data, u32 mask)
++{
++ comphy_reg_set(lane->priv->comphy_regs + COMPHY_PHY_REG(lane->id, reg),
++ data, mask);
++}
++
++static int comphy_periph_reg_poll(struct mvebu_a3700_comphy_lane *lane,
++ u8 reg, u32 bits,
++ ulong sleep_us, ulong timeout_us)
++{
++ u32 data;
++
++ return readl_poll_timeout(lane->priv->comphy_regs +
++ COMPHY_PHY_REG(lane->id, reg),
++ data, (data & bits) == bits,
++ sleep_us, timeout_us);
++}
++
++/* PHY selector configures with corresponding modes */
++static int
++mvebu_a3700_comphy_set_phy_selector(struct mvebu_a3700_comphy_lane *lane)
++{
++ u32 old, new, clr = 0, set = 0;
++ unsigned long flags;
++
++ switch (lane->mode) {
++ case PHY_MODE_SATA:
++ /* SATA must be in Lane2 */
++ if (lane->id == 2)
++ clr = COMPHY_SELECTOR_USB3_PHY_SEL_BIT;
++ else
++ goto error;
++ break;
++
++ case PHY_MODE_ETHERNET:
++ if (lane->id == 0)
++ clr = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT;
++ else if (lane->id == 1)
++ clr = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT;
++ else
++ goto error;
++ break;
++
++ case PHY_MODE_USB_HOST_SS:
++ if (lane->id == 2)
++ set = COMPHY_SELECTOR_USB3_PHY_SEL_BIT;
++ else if (lane->id == 0)
++ set = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT;
++ else
++ goto error;
++ break;
++
++ case PHY_MODE_PCIE:
++ /* PCIE must be in Lane1 */
++ if (lane->id == 1)
++ set = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT;
++ else
++ goto error;
++ break;
++
++ default:
++ goto error;
++ }
++
++ spin_lock_irqsave(&lane->priv->lock, flags);
++
++ old = readl(lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG);
++ new = (old & ~clr) | set;
++ writel(new, lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG);
++
++ spin_unlock_irqrestore(&lane->priv->lock, flags);
++
++ dev_dbg(lane->dev,
++ "COMPHY[%d] mode[%d] changed PHY selector 0x%08x -> 0x%08x\n",
++ lane->id, lane->mode, old, new);
++
++ return 0;
++error:
++ dev_err(lane->dev, "COMPHY[%d] mode[%d] is invalid\n", lane->id,
++ lane->mode);
++ return -EINVAL;
++}
++
++static int
++mvebu_a3700_comphy_sata_power_on(struct mvebu_a3700_comphy_lane *lane)
++{
++ u32 mask, data, ref_clk;
++ int ret;
++
++ /* Configure phy selector for SATA */
++ ret = mvebu_a3700_comphy_set_phy_selector(lane);
++ if (ret)
++ return ret;
++
++ /* Clear phy isolation mode to make it work in normal mode */
++ comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL,
++ 0x0, PHY_ISOLATE_MODE);
++
++ /* 0. Check the Polarity invert bits */
++ data = 0x0;
++ if (lane->invert_tx)
++ data |= TXD_INVERT_BIT;
++ if (lane->invert_rx)
++ data |= RXD_INVERT_BIT;
++ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
++ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
++
++ /* 1. Select 40-bit data width */
++ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN,
++ DATA_WIDTH_40BIT, SEL_DATA_WIDTH_MASK);
++
++ /* 2. Select reference clock(25M) and PHY mode (SATA) */
++ if (lane->priv->xtal_is_40m)
++ ref_clk = REF_FREF_SEL_SERDES_40MHZ;
++ else
++ ref_clk = REF_FREF_SEL_SERDES_25MHZ;
++
++ data = ref_clk | COMPHY_MODE_SATA;
++ mask = REF_FREF_SEL_MASK | COMPHY_MODE_MASK;
++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
++
++ /* 3. Use maximum PLL rate (no power save) */
++ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL,
++ USE_MAX_PLL_RATE_BIT, USE_MAX_PLL_RATE_BIT);
++
++ /* 4. Reset reserved bit */
++ comphy_set_indirect(lane->priv, COMPHY_RESERVED_REG,
++ 0x0, PHYCTRL_FRM_PIN_BIT);
++
++ /* 5. Set vendor-specific configuration (It is done in sata driver) */
++ /* XXX: in U-Boot below sequence was executed in this place, in Linux
++ * not. Now it is done only in U-Boot before this comphy
++ * initialization - tests shows that it works ok, but in case of any
++ * future problem it is left for reference.
++ * reg_set(MVEBU_REGS_BASE + 0xe00a0, 0, 0xffffffff);
++ * reg_set(MVEBU_REGS_BASE + 0xe00a4, BIT(6), BIT(6));
++ */
++
++ /* Wait for > 55 us to allow PLL be enabled */
++ udelay(PLL_SET_DELAY_US);
++
++ /* Polling status */
++ ret = comphy_lane_reg_poll(lane, COMPHY_DIG_LOOPBACK_EN,
++ PLL_READY_TX_BIT, COMPHY_PLL_SLEEP,
++ COMPHY_PLL_TIMEOUT);
++ if (ret) {
++ dev_err(lane->dev, "Failed to lock SATA PLL\n");
++ return ret;
++ }
++
++ return 0;
++}
++
++static void comphy_gbe_phy_init(struct mvebu_a3700_comphy_lane *lane,
++ bool is_1gbps)
++{
++ int addr, fix_idx;
++ u16 val;
++
++ fix_idx = 0;
++ for (addr = 0; addr < 512; addr++) {
++ /*
++ * All PHY register values are defined in full for 3.125Gbps
++ * SERDES speed. The values required for 1.25 Gbps are almost
++ * the same and only few registers should be "fixed" in
++ * comparison to 3.125 Gbps values. These register values are
++ * stored in "gbe_phy_init_fix" array.
++ */
++ if (!is_1gbps && gbe_phy_init_fix[fix_idx].addr == addr) {
++ /* Use new value */
++ val = gbe_phy_init_fix[fix_idx].value;
++ if (fix_idx < ARRAY_SIZE(gbe_phy_init_fix))
++ fix_idx++;
++ } else {
++ val = gbe_phy_init[addr];
++ }
++
++ comphy_lane_reg_set(lane, addr, val, 0xFFFF);
++ }
++}
++
++static int
++mvebu_a3700_comphy_ethernet_power_on(struct mvebu_a3700_comphy_lane *lane)
++{
++ u32 mask, data, speed_sel;
++ int ret;
++
++ /* Set selector */
++ ret = mvebu_a3700_comphy_set_phy_selector(lane);
++ if (ret)
++ return ret;
++
++ /*
++ * 1. Reset PHY by setting PHY input port PIN_RESET=1.
++ * 2. Set PHY input port PIN_TX_IDLE=1, PIN_PU_IVREF=1 to keep
++ * PHY TXP/TXN output to idle state during PHY initialization
++ * 3. Set PHY input port PIN_PU_PLL=0, PIN_PU_RX=0, PIN_PU_TX=0.
++ */
++ data = PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT | PIN_RESET_COMPHY_BIT;
++ mask = data | PIN_RESET_CORE_BIT | PIN_PU_PLL_BIT | PIN_PU_RX_BIT |
++ PIN_PU_TX_BIT | PHY_RX_INIT_BIT;
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
++
++ /* 4. Release reset to the PHY by setting PIN_RESET=0. */
++ data = 0x0;
++ mask = PIN_RESET_COMPHY_BIT;
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
++
++ /*
++ * 5. Set PIN_PHY_GEN_TX[3:0] and PIN_PHY_GEN_RX[3:0] to decide COMPHY
++ * bit rate
++ */
++ switch (lane->submode) {
++ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_1000BASEX:
++ /* SGMII 1G, SerDes speed 1.25G */
++ speed_sel = SERDES_SPEED_1_25_G;
++ break;
++ case PHY_INTERFACE_MODE_2500BASEX:
++ /* 2500Base-X, SerDes speed 3.125G */
++ speed_sel = SERDES_SPEED_3_125_G;
++ break;
+ default:
++ /* Other rates are not supported */
++ dev_err(lane->dev,
++ "unsupported phy speed %d on comphy lane%d\n",
++ lane->submode, lane->id);
+ return -EINVAL;
+ }
++ data = GEN_RX_SEL_VALUE(speed_sel) | GEN_TX_SEL_VALUE(speed_sel);
++ mask = GEN_RX_SEL_MASK | GEN_TX_SEL_MASK;
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
++
++ /*
++ * 6. Wait 10mS for bandgap and reference clocks to stabilize; then
++ * start SW programming.
++ */
++ mdelay(10);
++
++ /* 7. Program COMPHY register PHY_MODE */
++ data = COMPHY_MODE_SERDES;
++ mask = COMPHY_MODE_MASK;
++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
++
++ /*
++ * 8. Set COMPHY register REFCLK_SEL to select the correct REFCLK
++ * source
++ */
++ data = 0x0;
++ mask = PHY_REF_CLK_SEL;
++ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask);
++
++ /*
++ * 9. Set correct reference clock frequency in COMPHY register
++ * REF_FREF_SEL.
++ */
++ if (lane->priv->xtal_is_40m)
++ data = REF_FREF_SEL_SERDES_50MHZ;
++ else
++ data = REF_FREF_SEL_SERDES_25MHZ;
++
++ mask = REF_FREF_SEL_MASK;
++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
++
++ /* 10. Program COMPHY register PHY_GEN_MAX[1:0]
++ * This step is mentioned in the flow received from verification team.
++ * However the PHY_GEN_MAX value is only meaningful for other interfaces
++ * (not SERDES). For instance, it selects SATA speed 1.5/3/6 Gbps or
++ * PCIe speed 2.5/5 Gbps
++ */
++
++ /*
++ * 11. Program COMPHY register SEL_BITS to set correct parallel data
++ * bus width
++ */
++ data = DATA_WIDTH_10BIT;
++ mask = SEL_DATA_WIDTH_MASK;
++ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, data, mask);
++
++ /*
++ * 12. As long as DFE function needs to be enabled in any mode,
++ * COMPHY register DFE_UPDATE_EN[5:0] shall be programmed to 0x3F
++ * for real chip during COMPHY power on.
++ * The step 14 exists (and empty) in the original initialization flow
++ * obtained from the verification team. According to the functional
++ * specification DFE_UPDATE_EN already has the default value 0x3F
++ */
++
++ /*
++ * 13. Program COMPHY GEN registers.
++ * These registers should be programmed based on the lab testing result
++ * to achieve optimal performance. Please contact the CEA group to get
++ * the related GEN table during real chip bring-up. We only required to
++ * run though the entire registers programming flow defined by
++ * "comphy_gbe_phy_init" when the REF clock is 40 MHz. For REF clock
++ * 25 MHz the default values stored in PHY registers are OK.
++ */
++ dev_dbg(lane->dev, "Running C-DPI phy init %s mode\n",
++ lane->submode == PHY_INTERFACE_MODE_2500BASEX ? "2G5" : "1G");
++ if (lane->priv->xtal_is_40m)
++ comphy_gbe_phy_init(lane,
++ lane->submode != PHY_INTERFACE_MODE_2500BASEX);
++
++ /*
++ * 14. [Simulation Only] should not be used for real chip.
++ * By pass power up calibration by programming EXT_FORCE_CAL_DONE
++ * (R02h[9]) to 1 to shorten COMPHY simulation time.
++ */
++
++ /*
++ * 15. [Simulation Only: should not be used for real chip]
++ * Program COMPHY register FAST_DFE_TIMER_EN=1 to shorten RX training
++ * simulation time.
++ */
++
++ /*
++ * 16. Check the PHY Polarity invert bit
++ */
++ data = 0x0;
++ if (lane->invert_tx)
++ data |= TXD_INVERT_BIT;
++ if (lane->invert_rx)
++ data |= RXD_INVERT_BIT;
++ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
++ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
++
++ /*
++ * 17. Set PHY input ports PIN_PU_PLL, PIN_PU_TX and PIN_PU_RX to 1 to
++ * start PHY power up sequence. All the PHY register programming should
++ * be done before PIN_PU_PLL=1. There should be no register programming
++ * for normal PHY operation from this point.
++ */
++ data = PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT;
++ mask = data;
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
++
++ /*
++ * 18. Wait for PHY power up sequence to finish by checking output ports
++ * PIN_PLL_READY_TX=1 and PIN_PLL_READY_RX=1.
++ */
++ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
++ PHY_PLL_READY_TX_BIT |
++ PHY_PLL_READY_RX_BIT,
++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
++ if (ret) {
++ dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n",
++ lane->id);
++ return ret;
++ }
++
++ /*
++ * 19. Set COMPHY input port PIN_TX_IDLE=0
++ */
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, 0x0, PIN_TX_IDLE_BIT);
++
++ /*
++ * 20. After valid data appear on PIN_RXDATA bus, set PIN_RX_INIT=1. To
++ * start RX initialization. PIN_RX_INIT_DONE will be cleared to 0 by the
++ * PHY After RX initialization is done, PIN_RX_INIT_DONE will be set to
++ * 1 by COMPHY Set PIN_RX_INIT=0 after PIN_RX_INIT_DONE= 1. Please
++ * refer to RX initialization part for details.
++ */
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1,
++ PHY_RX_INIT_BIT, PHY_RX_INIT_BIT);
++
++ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
++ PHY_PLL_READY_TX_BIT |
++ PHY_PLL_READY_RX_BIT,
++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
++ if (ret) {
++ dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n",
++ lane->id);
++ return ret;
++ }
++
++ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
++ PHY_RX_INIT_DONE_BIT,
++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
++ if (ret) {
++ dev_err(lane->dev, "Failed to init RX of SERDES PHY %d\n",
++ lane->id);
++ return ret;
++ }
++
++ return 0;
+ }
+
+-static int mvebu_a3700_comphy_get_fw_mode(int lane,
++static int
++mvebu_a3700_comphy_usb3_power_on(struct mvebu_a3700_comphy_lane *lane)
++{
++ u32 mask, data, cfg, ref_clk;
++ int ret;
++
++ /* Set phy seclector */
++ ret = mvebu_a3700_comphy_set_phy_selector(lane);
++ if (ret)
++ return ret;
++
++ /*
++ * 0. Set PHY OTG Control(0x5d034), bit 4, Power up OTG module The
++ * register belong to UTMI module, so it is set in UTMI phy driver.
++ */
++
++ /*
++ * 1. Set PRD_TXDEEMPH (3.5db de-emph)
++ */
++ data = PRD_TXDEEMPH0_MASK;
++ mask = PRD_TXDEEMPH0_MASK | PRD_TXMARGIN_MASK | PRD_TXSWING_MASK |
++ CFG_TX_ALIGN_POS_MASK;
++ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG0, data, mask);
++
++ /*
++ * 2. Set BIT0: enable transmitter in high impedance mode
++ * Set BIT[3:4]: delay 2 clock cycles for HiZ off latency
++ * Set BIT6: Tx detect Rx at HiZ mode
++ * Unset BIT15: set to 0 to set USB3 De-emphasize level to -3.5db
++ * together with bit 0 of COMPHY_PIPE_LANE_CFG0 register
++ */
++ data = TX_DET_RX_MODE | GEN2_TX_DATA_DLY_DEFT | TX_ELEC_IDLE_MODE_EN;
++ mask = PRD_TXDEEMPH1_MASK | TX_DET_RX_MODE | GEN2_TX_DATA_DLY_MASK |
++ TX_ELEC_IDLE_MODE_EN;
++ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1, data, mask);
++
++ /*
++ * 3. Set Spread Spectrum Clock Enabled
++ */
++ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG4,
++ SPREAD_SPECTRUM_CLK_EN, SPREAD_SPECTRUM_CLK_EN);
++
++ /*
++ * 4. Set Override Margining Controls From the MAC:
++ * Use margining signals from lane configuration
++ */
++ comphy_lane_reg_set(lane, COMPHY_PIPE_TEST_MODE_CTRL,
++ MODE_MARGIN_OVERRIDE, 0xFFFF);
++
++ /*
++ * 5. Set Lane-to-Lane Bundle Clock Sampling Period = per PCLK cycles
++ * set Mode Clock Source = PCLK is generated from REFCLK
++ */
++ data = 0x0;
++ mask = MODE_CLK_SRC | BUNDLE_PERIOD_SEL | BUNDLE_PERIOD_SCALE_MASK |
++ BUNDLE_SAMPLE_CTRL | PLL_READY_DLY_MASK;
++ comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO, data, mask);
++
++ /*
++ * 6. Set G2 Spread Spectrum Clock Amplitude at 4K
++ */
++ comphy_lane_reg_set(lane, COMPHY_GEN2_SET2,
++ GS2_TX_SSC_AMP_4128, GS2_TX_SSC_AMP_MASK);
++
++ /*
++ * 7. Unset G3 Spread Spectrum Clock Amplitude
++ * set G3 TX and RX Register Master Current Select
++ */
++ data = GS2_VREG_RXTX_MAS_ISET_60U;
++ mask = GS2_TX_SSC_AMP_MASK | GS2_VREG_RXTX_MAS_ISET_MASK |
++ GS2_RSVD_6_0_MASK;
++ comphy_lane_reg_set(lane, COMPHY_GEN3_SET2, data, mask);
++
++ /*
++ * 8. Check crystal jumper setting and program the Power and PLL Control
++ * accordingly Change RX wait
++ */
++ if (lane->priv->xtal_is_40m) {
++ ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ;
++ cfg = CFG_PM_RXDLOZ_WAIT_12_UNIT;
++ } else {
++ ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ;
++ cfg = CFG_PM_RXDLOZ_WAIT_7_UNIT;
++ }
++
++ data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
++ PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_USB3 | ref_clk;
++ mask = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
++ PU_TX_INTP_BIT | PU_DFE_BIT | PLL_LOCK_BIT | COMPHY_MODE_MASK |
++ REF_FREF_SEL_MASK;
++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
++
++ data = CFG_PM_RXDEN_WAIT_1_UNIT | cfg;
++ mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK |
++ CFG_PM_RXDLOZ_WAIT_MASK;
++ comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask);
++
++ /*
++ * 9. Enable idle sync
++ */
++ comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN,
++ IDLE_SYNC_EN, IDLE_SYNC_EN);
++
++ /*
++ * 10. Enable the output of 500M clock
++ */
++ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, CLK500M_EN, CLK500M_EN);
++
++ /*
++ * 11. Set 20-bit data width
++ */
++ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN,
++ DATA_WIDTH_20BIT, 0xFFFF);
++
++ /*
++ * 12. Override Speed_PLL value and use MAC PLL
++ */
++ data = SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT;
++ mask = 0xFFFF;
++ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, data, mask);
++
++ /*
++ * 13. Check the Polarity invert bit
++ */
++ data = 0x0;
++ if (lane->invert_tx)
++ data |= TXD_INVERT_BIT;
++ if (lane->invert_rx)
++ data |= RXD_INVERT_BIT;
++ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
++ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
++
++ /*
++ * 14. Set max speed generation to USB3.0 5Gbps
++ */
++ comphy_lane_reg_set(lane, COMPHY_SYNC_MASK_GEN,
++ PHY_GEN_MAX_USB3_5G, PHY_GEN_MAX_MASK);
++
++ /*
++ * 15. Set capacitor value for FFE gain peaking to 0xF
++ */
++ comphy_lane_reg_set(lane, COMPHY_GEN2_SET3,
++ GS3_FFE_CAP_SEL_VALUE, GS3_FFE_CAP_SEL_MASK);
++
++ /*
++ * 16. Release SW reset
++ */
++ data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32 | MODE_REFDIV_BY_4;
++ mask = 0xFFFF;
++ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask);
++
++ /* Wait for > 55 us to allow PCLK be enabled */
++ udelay(PLL_SET_DELAY_US);
++
++ ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN,
++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
++ if (ret) {
++ dev_err(lane->dev, "Failed to lock USB3 PLL\n");
++ return ret;
++ }
++
++ return 0;
++}
++
++static int
++mvebu_a3700_comphy_pcie_power_on(struct mvebu_a3700_comphy_lane *lane)
++{
++ u32 mask, data, ref_clk;
++ int ret;
++
++ /* Configure phy selector for PCIe */
++ ret = mvebu_a3700_comphy_set_phy_selector(lane);
++ if (ret)
++ return ret;
++
++ /* 1. Enable max PLL. */
++ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1,
++ USE_MAX_PLL_RATE_EN, USE_MAX_PLL_RATE_EN);
++
++ /* 2. Select 20 bit SERDES interface. */
++ comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO,
++ CFG_SEL_20B, CFG_SEL_20B);
++
++ /* 3. Force to use reg setting for PCIe mode */
++ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL1,
++ SEL_BITS_PCIE_FORCE, SEL_BITS_PCIE_FORCE);
++
++ /* 4. Change RX wait */
++ data = CFG_PM_RXDEN_WAIT_1_UNIT | CFG_PM_RXDLOZ_WAIT_12_UNIT;
++ mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK |
++ CFG_PM_RXDLOZ_WAIT_MASK;
++ comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask);
++
++ /* 5. Enable idle sync */
++ comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN,
++ IDLE_SYNC_EN, IDLE_SYNC_EN);
++
++ /* 6. Enable the output of 100M/125M/500M clock */
++ data = CLK500M_EN | TXDCLK_2X_SEL | CLK100M_125M_EN;
++ mask = data;
++ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask);
++
++ /*
++ * 7. Enable TX, PCIE global register, 0xd0074814, it is done in
++ * PCI-E driver
++ */
++
++ /*
++ * 8. Check crystal jumper setting and program the Power and PLL
++ * Control accordingly
++ */
++
++ if (lane->priv->xtal_is_40m)
++ ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ;
++ else
++ ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ;
++
++ data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
++ PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_PCIE | ref_clk;
++ mask = 0xFFFF;
++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
++
++ /* 9. Override Speed_PLL value and use MAC PLL */
++ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL,
++ SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT,
++ 0xFFFF);
++
++ /* 10. Check the Polarity invert bit */
++ data = 0x0;
++ if (lane->invert_tx)
++ data |= TXD_INVERT_BIT;
++ if (lane->invert_rx)
++ data |= RXD_INVERT_BIT;
++ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
++ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
++
++ /* 11. Release SW reset */
++ data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32;
++ mask = data | PIPE_SOFT_RESET | MODE_REFDIV_MASK;
++ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask);
++
++ /* Wait for > 55 us to allow PCLK be enabled */
++ udelay(PLL_SET_DELAY_US);
++
++ ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN,
++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
++ if (ret) {
++ dev_err(lane->dev, "Failed to lock PCIE PLL\n");
++ return ret;
++ }
++
++ return 0;
++}
++
++static void
++mvebu_a3700_comphy_usb3_power_off(struct mvebu_a3700_comphy_lane *lane)
++{
++ /*
++ * Currently the USB3 MAC sets the USB3 PHY to low state, so we do not
++ * need to power off USB3 PHY again.
++ */
++}
++
++static void
++mvebu_a3700_comphy_sata_power_off(struct mvebu_a3700_comphy_lane *lane)
++{
++ /* Set phy isolation mode */
++ comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL,
++ PHY_ISOLATE_MODE, PHY_ISOLATE_MODE);
++
++ /* Power off PLL, Tx, Rx */
++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL,
++ 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT);
++}
++
++static void
++mvebu_a3700_comphy_ethernet_power_off(struct mvebu_a3700_comphy_lane *lane)
++{
++ u32 mask, data;
++
++ data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT | PIN_PU_IVREF_BIT |
++ PHY_RX_INIT_BIT;
++ mask = data;
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
++}
++
++static void
++mvebu_a3700_comphy_pcie_power_off(struct mvebu_a3700_comphy_lane *lane)
++{
++ /* Power off PLL, Tx, Rx */
++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL,
++ 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT);
++}
++
++static int mvebu_a3700_comphy_reset(struct phy *phy)
++{
++ struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
++ u16 mask, data;
++
++ dev_dbg(lane->dev, "resetting lane %d\n", lane->id);
++
++ /* COMPHY reset for internal logic */
++ comphy_lane_reg_set(lane, COMPHY_SFT_RESET,
++ SFT_RST_NO_REG, SFT_RST_NO_REG);
++
++ /* COMPHY register reset (cleared automatically) */
++ comphy_lane_reg_set(lane, COMPHY_SFT_RESET, SFT_RST, SFT_RST);
++
++ /* PIPE soft and register reset */
++ data = PIPE_SOFT_RESET | PIPE_REG_RESET;
++ mask = data;
++ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask);
++
++ /* Release PIPE register reset */
++ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL,
++ 0x0, PIPE_REG_RESET);
++
++ /* Reset SB configuration register (only for lanes 0 and 1) */
++ if (lane->id == 0 || lane->id == 1) {
++ u32 mask, data;
++
++ data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT |
++ PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT;
++ mask = data | PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT;
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
++ }
++
++ return 0;
++}
++
++static bool mvebu_a3700_comphy_check_mode(int lane,
+ enum phy_mode mode,
+ int submode)
+ {
+@@ -122,7 +1170,7 @@ static int mvebu_a3700_comphy_get_fw_mod
+
+ /* Unused PHY mux value is 0x0 */
+ if (mode == PHY_MODE_INVALID)
+- return -EINVAL;
++ return false;
+
+ for (i = 0; i < n; i++) {
+ if (mvebu_a3700_comphy_modes[i].lane == lane &&
+@@ -132,27 +1180,30 @@ static int mvebu_a3700_comphy_get_fw_mod
+ }
+
+ if (i == n)
+- return -EINVAL;
++ return false;
+
+- return mvebu_a3700_comphy_modes[i].fw_mode;
++ return true;
+ }
+
+ static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode,
+ int submode)
+ {
+ struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
+- int fw_mode;
+-
+- if (submode == PHY_INTERFACE_MODE_1000BASEX)
+- submode = PHY_INTERFACE_MODE_SGMII;
+
+- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, mode,
+- submode);
+- if (fw_mode < 0) {
++ if (!mvebu_a3700_comphy_check_mode(lane->id, mode, submode)) {
+ dev_err(lane->dev, "invalid COMPHY mode\n");
+- return fw_mode;
++ return -EINVAL;
+ }
+
++ /* Mode cannot be changed while the PHY is powered on */
++ if (phy->power_count &&
++ (lane->mode != mode || lane->submode != submode))
++ return -EBUSY;
++
++ /* If changing mode, ensure reset is called */
++ if (lane->mode != PHY_MODE_INVALID && lane->mode != mode)
++ lane->needs_reset = true;
++
+ /* Just remember the mode, ->power_on() will do the real setup */
+ lane->mode = mode;
+ lane->submode = submode;
+@@ -163,76 +1214,68 @@ static int mvebu_a3700_comphy_set_mode(s
+ static int mvebu_a3700_comphy_power_on(struct phy *phy)
+ {
+ struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
+- u32 fw_param;
+- int fw_mode;
+- int fw_port;
+ int ret;
+
+- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id,
+- lane->mode, lane->submode);
+- if (fw_mode < 0) {
++ if (!mvebu_a3700_comphy_check_mode(lane->id, lane->mode,
++ lane->submode)) {
+ dev_err(lane->dev, "invalid COMPHY mode\n");
+- return fw_mode;
++ return -EINVAL;
++ }
++
++ if (lane->needs_reset) {
++ ret = mvebu_a3700_comphy_reset(phy);
++ if (ret)
++ return ret;
++
++ lane->needs_reset = false;
+ }
+
+ switch (lane->mode) {
+ case PHY_MODE_USB_HOST_SS:
+ dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id);
+- fw_param = COMPHY_FW_MODE(fw_mode);
+- break;
++ return mvebu_a3700_comphy_usb3_power_on(lane);
+ case PHY_MODE_SATA:
+ dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id);
+- fw_param = COMPHY_FW_MODE(fw_mode);
+- break;
++ return mvebu_a3700_comphy_sata_power_on(lane);
+ case PHY_MODE_ETHERNET:
+- fw_port = (lane->id == 0) ? 1 : 0;
+- switch (lane->submode) {
+- case PHY_INTERFACE_MODE_SGMII:
+- dev_dbg(lane->dev, "set lane %d to SGMII mode\n",
+- lane->id);
+- fw_param = COMPHY_FW_NET(fw_mode, fw_port,
+- COMPHY_FW_SPEED_1_25G);
+- break;
+- case PHY_INTERFACE_MODE_2500BASEX:
+- dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n",
+- lane->id);
+- fw_param = COMPHY_FW_NET(fw_mode, fw_port,
+- COMPHY_FW_SPEED_3_125G);
+- break;
+- default:
+- dev_err(lane->dev, "unsupported PHY submode (%d)\n",
+- lane->submode);
+- return -ENOTSUPP;
+- }
+- break;
++ dev_dbg(lane->dev, "set lane %d to Ethernet mode\n", lane->id);
++ return mvebu_a3700_comphy_ethernet_power_on(lane);
+ case PHY_MODE_PCIE:
+ dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id);
+- fw_param = COMPHY_FW_PCIE(fw_mode, COMPHY_FW_SPEED_5G,
+- phy->attrs.bus_width);
+- break;
++ return mvebu_a3700_comphy_pcie_power_on(lane);
+ default:
+ dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode);
+- return -ENOTSUPP;
++ return -EOPNOTSUPP;
+ }
+-
+- ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param);
+- if (ret == -EOPNOTSUPP)
+- dev_err(lane->dev,
+- "unsupported SMC call, try updating your firmware\n");
+-
+- return ret;
+ }
+
+ static int mvebu_a3700_comphy_power_off(struct phy *phy)
+ {
+ struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
+
+- return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0);
++ switch (lane->mode) {
++ case PHY_MODE_USB_HOST_SS:
++ mvebu_a3700_comphy_usb3_power_off(lane);
++ return 0;
++ case PHY_MODE_SATA:
++ mvebu_a3700_comphy_sata_power_off(lane);
++ return 0;
++ case PHY_MODE_ETHERNET:
++ mvebu_a3700_comphy_ethernet_power_off(lane);
++ return 0;
++ case PHY_MODE_PCIE:
++ mvebu_a3700_comphy_pcie_power_off(lane);
++ return 0;
++ default:
++ dev_err(lane->dev, "invalid COMPHY mode\n");
++ return -EINVAL;
++ }
+ }
+
+ static const struct phy_ops mvebu_a3700_comphy_ops = {
+ .power_on = mvebu_a3700_comphy_power_on,
+ .power_off = mvebu_a3700_comphy_power_off,
++ .reset = mvebu_a3700_comphy_reset,
+ .set_mode = mvebu_a3700_comphy_set_mode,
+ .owner = THIS_MODULE,
+ };
+@@ -256,13 +1299,75 @@ static struct phy *mvebu_a3700_comphy_xl
+ return ERR_PTR(-EINVAL);
+ }
+
++ lane->invert_tx = args->args[1] & BIT(0);
++ lane->invert_rx = args->args[1] & BIT(1);
++
+ return phy;
+ }
+
+ static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
+ {
++ struct mvebu_a3700_comphy_priv *priv;
+ struct phy_provider *provider;
+ struct device_node *child;
++ struct resource *res;
++ struct clk *clk;
++ int ret;
++
++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ spin_lock_init(&priv->lock);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "comphy");
++ priv->comphy_regs = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(priv->comphy_regs))
++ return PTR_ERR(priv->comphy_regs);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++ "lane1_pcie_gbe");
++ priv->lane1_phy_regs = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(priv->lane1_phy_regs))
++ return PTR_ERR(priv->lane1_phy_regs);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++ "lane0_usb3_gbe");
++ priv->lane0_phy_regs = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(priv->lane0_phy_regs))
++ return PTR_ERR(priv->lane0_phy_regs);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++ "lane2_sata_usb3");
++ priv->lane2_phy_indirect = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(priv->lane2_phy_indirect))
++ return PTR_ERR(priv->lane2_phy_indirect);
++
++ /*
++ * Driver needs to know if reference xtal clock is 40MHz or 25MHz.
++ * Old DT bindings do not have xtal clk present. So do not fail here
++ * and expects that default 25MHz reference clock is used.
++ */
++ clk = clk_get(&pdev->dev, "xtal");
++ if (IS_ERR(clk)) {
++ if (PTR_ERR(clk) == -EPROBE_DEFER)
++ return -EPROBE_DEFER;
++ dev_warn(&pdev->dev, "missing 'xtal' clk (%ld)\n",
++ PTR_ERR(clk));
++ } else {
++ ret = clk_prepare_enable(clk);
++ if (ret) {
++ dev_warn(&pdev->dev, "enabling xtal clk failed (%d)\n",
++ ret);
++ } else {
++ if (clk_get_rate(clk) == 40000000)
++ priv->xtal_is_40m = true;
++ clk_disable_unprepare(clk);
++ }
++ clk_put(clk);
++ }
++
++ dev_set_drvdata(&pdev->dev, priv);
+
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
+ struct mvebu_a3700_comphy_lane *lane;
+@@ -277,7 +1382,7 @@ static int mvebu_a3700_comphy_probe(stru
+ continue;
+ }
+
+- if (lane_id >= MVEBU_A3700_COMPHY_LANES) {
++ if (lane_id >= 3) {
+ dev_err(&pdev->dev, "invalid 'reg' property\n");
+ continue;
+ }
+@@ -295,11 +1400,21 @@ static int mvebu_a3700_comphy_probe(stru
+ return PTR_ERR(phy);
+ }
+
++ lane->priv = priv;
+ lane->dev = &pdev->dev;
+ lane->mode = PHY_MODE_INVALID;
+ lane->submode = PHY_INTERFACE_MODE_NA;
+ lane->id = lane_id;
++ lane->invert_tx = false;
++ lane->invert_rx = false;
+ phy_set_drvdata(phy, lane);
++
++ /*
++ * To avoid relying on the bootloader/firmware configuration,
++ * power off all comphys.
++ */
++ mvebu_a3700_comphy_reset(phy);
++ lane->needs_reset = false;
+ }
+
+ provider = devm_of_phy_provider_register(&pdev->dev,
+@@ -323,5 +1438,7 @@ static struct platform_driver mvebu_a370
+ module_platform_driver(mvebu_a3700_comphy_driver);
+
+ MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
++MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
++MODULE_AUTHOR("Marek Behún <kabel@kernel.org>");
+ MODULE_DESCRIPTION("Common PHY driver for A3700");
+ MODULE_LICENSE("GPL v2");
--- /dev/null
+From 66c51c39fd4bf05e99debf0e71de5704231c57dc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 23 Sep 2021 19:26:26 +0200
+Subject: [PATCH] arm64: dts: marvell: armada-37xx: Add xtal clock to comphy
+ node
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Kernel driver phy-mvebu-a3700-comphy.c needs to know the rate of the
+reference xtal clock. So add missing xtal clock source into comphy device
+tree node. If the property is not present, the driver defaults to 25 MHz
+xtal rate (which, as far as we know, is used by all the existing boards).
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
++++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+@@ -265,6 +265,8 @@
+ "lane2_sata_usb3";
+ #address-cells = <1>;
+ #size-cells = <0>;
++ clocks = <&xtalclk>;
++ clock-names = "xtal";
+
+ comphy0: phy@0 {
+ reg = <0>;
--- /dev/null
+From 750bb44dbbe9dfb4ba3e1f8a746b831b39ba3cd9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 23 Sep 2021 19:35:57 +0200
+Subject: [PATCH] Revert "ata: ahci: mvebu: Make SATA PHY optional for Armada
+ 3720"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit 45aefe3d2251e4e229d7662052739f96ad1d08d9.
+
+Armada 3720 PHY driver (phy-mvebu-a3700-comphy.c) does not return
+-EOPNOTSUPP from phy_power_on() callback anymore.
+
+So remove AHCI_HFLAG_IGN_NOTSUPP_POWER_ON flag from Armada 3720 plat data.
+
+AHCI_HFLAG_IGN_NOTSUPP_POWER_ON is not used by any other ahci driver, so
+remove this flag completely.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/ata/ahci.h | 2 --
+ drivers/ata/ahci_mvebu.c | 2 +-
+ drivers/ata/libahci_platform.c | 2 +-
+ 3 files changed, 2 insertions(+), 4 deletions(-)
+
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -240,8 +240,6 @@ enum {
+ as default lpm_policy */
+ AHCI_HFLAG_SUSPEND_PHYS = (1 << 26), /* handle PHYs during
+ suspend/resume */
+- AHCI_HFLAG_IGN_NOTSUPP_POWER_ON = (1 << 27), /* ignore -EOPNOTSUPP
+- from phy_power_on() */
+ AHCI_HFLAG_NO_SXS = (1 << 28), /* SXS not supported */
+
+ /* ap->flags bits */
+--- a/drivers/ata/ahci_mvebu.c
++++ b/drivers/ata/ahci_mvebu.c
+@@ -227,7 +227,7 @@ static const struct ahci_mvebu_plat_data
+
+ static const struct ahci_mvebu_plat_data ahci_mvebu_armada_3700_plat_data = {
+ .plat_config = ahci_mvebu_armada_3700_config,
+- .flags = AHCI_HFLAG_SUSPEND_PHYS | AHCI_HFLAG_IGN_NOTSUPP_POWER_ON,
++ .flags = AHCI_HFLAG_SUSPEND_PHYS,
+ };
+
+ static const struct of_device_id ahci_mvebu_of_match[] = {
+--- a/drivers/ata/libahci_platform.c
++++ b/drivers/ata/libahci_platform.c
+@@ -59,7 +59,7 @@ int ahci_platform_enable_phys(struct ahc
+ }
+
+ rc = phy_power_on(hpriv->phys[i]);
+- if (rc && !(rc == -EOPNOTSUPP && (hpriv->flags & AHCI_HFLAG_IGN_NOTSUPP_POWER_ON))) {
++ if (rc) {
+ phy_exit(hpriv->phys[i]);
+ goto disable_phys;
+ }
--- /dev/null
+From 9f0dfb279b1dd505d5e10b10e4a78a62030978d8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 23 Sep 2021 19:40:06 +0200
+Subject: [PATCH] Revert "usb: host: xhci: mvebu: make USB 3.0 PHY optional for
+ Armada 3720"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit 3241929b67d28c83945d3191c6816a3271fd6b85.
+
+Armada 3720 phy driver (phy-mvebu-a3700-comphy.c) does not return
+-EOPNOTSUPP from phy_power_on() callback anymore.
+
+So remove XHCI_SKIP_PHY_INIT flag from xhci_mvebu_a3700_plat_setup() and
+then also whole xhci_mvebu_a3700_plat_setup() function which is there just
+to handle -EOPNOTSUPP for XHCI_SKIP_PHY_INIT.
+
+xhci plat_setup callback is not used by any other xhci plat driver, so
+remove this callback completely.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/usb/host/xhci-mvebu.c | 42 -----------------------------------
+ drivers/usb/host/xhci-mvebu.h | 6 -----
+ drivers/usb/host/xhci-plat.c | 20 +----------------
+ drivers/usb/host/xhci-plat.h | 1 -
+ 4 files changed, 1 insertion(+), 68 deletions(-)
+
+--- a/drivers/usb/host/xhci-mvebu.c
++++ b/drivers/usb/host/xhci-mvebu.c
+@@ -8,7 +8,6 @@
+ #include <linux/mbus.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+-#include <linux/phy/phy.h>
+
+ #include <linux/usb.h>
+ #include <linux/usb/hcd.h>
+@@ -74,47 +73,6 @@ int xhci_mvebu_mbus_init_quirk(struct us
+
+ return 0;
+ }
+-
+-int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd)
+-{
+- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+- struct device *dev = hcd->self.controller;
+- struct phy *phy;
+- int ret;
+-
+- /* Old bindings miss the PHY handle */
+- phy = of_phy_get(dev->of_node, "usb3-phy");
+- if (IS_ERR(phy) && PTR_ERR(phy) == -EPROBE_DEFER)
+- return -EPROBE_DEFER;
+- else if (IS_ERR(phy))
+- goto phy_out;
+-
+- ret = phy_init(phy);
+- if (ret)
+- goto phy_put;
+-
+- ret = phy_set_mode(phy, PHY_MODE_USB_HOST_SS);
+- if (ret)
+- goto phy_exit;
+-
+- ret = phy_power_on(phy);
+- if (ret == -EOPNOTSUPP) {
+- /* Skip initializatin of XHCI PHY when it is unsupported by firmware */
+- dev_warn(dev, "PHY unsupported by firmware\n");
+- xhci->quirks |= XHCI_SKIP_PHY_INIT;
+- }
+- if (ret)
+- goto phy_exit;
+-
+- phy_power_off(phy);
+-phy_exit:
+- phy_exit(phy);
+-phy_put:
+- of_phy_put(phy);
+-phy_out:
+-
+- return 0;
+-}
+
+ int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
+ {
+--- a/drivers/usb/host/xhci-mvebu.h
++++ b/drivers/usb/host/xhci-mvebu.h
+@@ -12,18 +12,12 @@ struct usb_hcd;
+
+ #if IS_ENABLED(CONFIG_USB_XHCI_MVEBU)
+ int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd);
+-int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd);
+ int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd);
+ #else
+ static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
+ {
+ return 0;
+ }
+-
+-static inline int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd)
+-{
+- return 0;
+-}
+
+ static inline int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
+ {
+--- a/drivers/usb/host/xhci-plat.c
++++ b/drivers/usb/host/xhci-plat.c
+@@ -44,16 +44,6 @@ static void xhci_priv_plat_start(struct
+ priv->plat_start(hcd);
+ }
+
+-static int xhci_priv_plat_setup(struct usb_hcd *hcd)
+-{
+- struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+-
+- if (!priv->plat_setup)
+- return 0;
+-
+- return priv->plat_setup(hcd);
+-}
+-
+ static int xhci_priv_init_quirk(struct usb_hcd *hcd)
+ {
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+@@ -121,7 +111,6 @@ static const struct xhci_plat_priv xhci_
+ };
+
+ static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
+- .plat_setup = xhci_mvebu_a3700_plat_setup,
+ .init_quirk = xhci_mvebu_a3700_init_quirk,
+ };
+
+@@ -341,14 +330,7 @@ static int xhci_plat_probe(struct platfo
+
+ hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
+ xhci->shared_hcd->tpl_support = hcd->tpl_support;
+-
+- if (priv) {
+- ret = xhci_priv_plat_setup(hcd);
+- if (ret)
+- goto disable_usb_phy;
+- }
+-
+- if ((xhci->quirks & XHCI_SKIP_PHY_INIT) || (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)))
++ if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))
+ hcd->skip_phy_initialization = 1;
+
+ if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK))
+--- a/drivers/usb/host/xhci-plat.h
++++ b/drivers/usb/host/xhci-plat.h
+@@ -13,7 +13,6 @@
+ struct xhci_plat_priv {
+ const char *firmware_name;
+ unsigned long long quirks;
+- int (*plat_setup)(struct usb_hcd *);
+ void (*plat_start)(struct usb_hcd *);
+ int (*init_quirk)(struct usb_hcd *);
+ int (*suspend_quirk)(struct usb_hcd *);
--- /dev/null
+From 9a352062b7e3857742389dff6f64393481dc755e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 23 Sep 2021 19:37:05 +0200
+Subject: [PATCH] Revert "PCI: aardvark: Fix initialization with old Marvell's
+ Arm Trusted Firmware"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit b0c6ae0f8948a2be6bf4e8b4bbab9ca1343289b6.
+
+Armada 3720 phy driver (phy-mvebu-a3700-comphy.c) does not return
+-EOPNOTSUPP from phy_power_on() callback anymore.
+
+So remove dead code which handles -EOPNOTSUPP return value.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1637,9 +1637,7 @@ static int advk_pcie_enable_phy(struct a
+ }
+
+ ret = phy_power_on(pcie->phy);
+- if (ret == -EOPNOTSUPP) {
+- dev_warn(&pcie->pdev->dev, "PHY unsupported by firmware\n");
+- } else if (ret) {
++ if (ret) {
+ phy_exit(pcie->phy);
+ return ret;
+ }
--- /dev/null
+From: Imre Kaloz <kaloz@openwrt.org>
+Subject: init: add CONFIG_MANGLE_BOOTARGS and disable it by default
+
+Enabling this option renames the bootloader supplied root=
+and rootfstype= variables, which might have to be know but
+would break the automatisms OpenWrt uses.
+
+Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
+---
+ init/Kconfig | 9 +++++++++
+ init/main.c | 24 ++++++++++++++++++++++++
+ 2 files changed, 33 insertions(+)
+
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -1800,6 +1800,15 @@ config EMBEDDED
+ an embedded system so certain expert options are available
+ for configuration.
+
++config MANGLE_BOOTARGS
++ bool "Rename offending bootargs"
++ depends on EXPERT
++ help
++ Sometimes the bootloader passed bogus root= and rootfstype=
++ parameters to the kernel, and while you want to ignore them,
++ you need to know the values f.e. to support dual firmware
++ layouts on the flash.
++
+ config HAVE_PERF_EVENTS
+ bool
+ help
+--- a/init/main.c
++++ b/init/main.c
+@@ -608,6 +608,29 @@ static inline void setup_nr_cpu_ids(void
+ static inline void smp_prepare_cpus(unsigned int maxcpus) { }
+ #endif
+
++#ifdef CONFIG_MANGLE_BOOTARGS
++static void __init mangle_bootargs(char *command_line)
++{
++ char *rootdev;
++ char *rootfs;
++
++ rootdev = strstr(command_line, "root=/dev/mtdblock");
++
++ if (rootdev)
++ strncpy(rootdev, "mangled_rootblock=", 18);
++
++ rootfs = strstr(command_line, "rootfstype");
++
++ if (rootfs)
++ strncpy(rootfs, "mangled_fs", 10);
++
++}
++#else
++static void __init mangle_bootargs(char *command_line)
++{
++}
++#endif
++
+ /*
+ * We need to store the untouched command line for future reference.
+ * We also need to store the touched command line since the parameter
+@@ -869,6 +892,7 @@ asmlinkage __visible void __init __no_sa
+ pr_notice("%s", linux_banner);
+ early_security_init();
+ setup_arch(&command_line);
++ mangle_bootargs(command_line);
+ setup_boot_config(command_line);
+ setup_command_line(command_line);
+ setup_nr_cpu_ids();