diff options
124 files changed, 43203 insertions, 4937 deletions
diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 8a88ddebc1a2..248acd840d11 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -99,6 +99,10 @@ its hardware characteristcs. * arm,scatter-gather: boolean. Indicates that the TMC-ETR can safely use the SG mode on this system. + * cache-lock: Enables cache locking for the allocated trace buffer. + As of now, this is supported only for secure buffer allocations + on Marvell OcteonTx2 platform. + * Optional property for CATU : * interrupts : Exactly one SPI may be listed for reporting the address error diff --git a/Documentation/devicetree/bindings/gpio/gpio-i2c.txt b/Documentation/devicetree/bindings/gpio/gpio-i2c.txt new file mode 100644 index 000000000000..ad2743bc65ed --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-i2c.txt @@ -0,0 +1,32 @@ +Device-Tree bindings for gpio i2c driver + +A simple parameterized no-irq of_driven i2c->gpio expander, +cut down from gpio-pcf857x.c to be totally device-tree driven. + +Suitable for any "memory-like" device, where a 1-byte i2c read yields data +which can safely be written back, possibly with a bit changed, with the +effect of changing only the output level of that bit's GPIO pin. + +Required properties: + - compatible = "gpio-i2c" + - reg = i2c-address + - gpio-controller + - #gpio-cells = <1>; + - ngpios = number of pins modeled + +Example nodes: + +&i2c_bus { + gpio1: cpld@6c { + compatible = "gpio-i2c"; + gpio-controller; + reg = <0x6c>; + #gpio-cells = <1>; + ngpios = <160>; // 8bits for each reg 0..0x13 + }; +}; + +_some_device_ { + // pin controlled by bitmask 0x2 of byte 0x4 of gpio1 + enable-gpios = <&gpio1 33>; +} diff --git a/Documentation/devicetree/bindings/mmc/cavium-mmc.txt b/Documentation/devicetree/bindings/mmc/cavium-mmc.txt index 1433e6201dff..21ed6d4fedcc 100644 --- a/Documentation/devicetree/bindings/mmc/cavium-mmc.txt +++ b/Documentation/devicetree/bindings/mmc/cavium-mmc.txt @@ -17,16 +17,56 @@ Required properties: - clocks : phandle Optional properties: - - for cd, bus-width and additional generic mmc parameters - please refer to mmc.txt within this directory + - for cd, bus-width, vmmc-supply, vqmmc-supply, and additional generic + mmc parameters please refer to mmc.txt within this directory - cavium,cmd-clk-skew : number of coprocessor clocks before sampling command - cavium,dat-clk-skew : number of coprocessor clocks before sampling data Deprecated properties: -- spi-max-frequency : use max-frequency instead -- cavium,bus-max-width : use bus-width instead -- power-gpios : use vmmc-supply instead -- cavium,octeon-6130-mmc-slot : use mmc-slot instead + - spi-max-frequency : use max-frequency instead + - cavium,bus-max-width : use bus-width instead + - power-gpios : use vmmc-supply instead + - cavium,octeon-6130-mmc-slot : use mmc-slot instead + +GPIO control via vmmc-supply & vqmmc-supply: + Two types of regulator object can be specified as mmc properties, + typically regulator-fixed controlled by GPIO pins. + + Octeon/OcteonTX chips commonly use GPIO8 as an MMC-reset pin. + In systems which may boot from MMC, it starts as input, and is gently + pulled up/down by board logic to indicate the active sense of the + signal. Chip reset then drives the signal in the opposite direction + to effect a reset of target devices. + Device tree should model this with a vmmc-supply regulator, gated by + GPIO8, so GPIO8 is driven in the non-reset direction when MMC devices + are probed, and held there until rmmod/shutdown/suspend. + This allows a warm reboot to reset the MMC devices. + + Octeon/OcteonTX MMC supports up to 3 mmc slots, but any + level-shifting to accommodate different signal voltages is + done by external hardware, under control of an optional + vqmmc regulator object, typically controlled by GPIO. + + If any mmc-slots have a vqmmc-supply property, it is taken as a warning + that we must switch carefully between slots (unless they have the same + vqmmc object), tri-stating MMC signals to avoid any transient states + as level-shifters are enabled/disabled. + + Even when so-called bi-directional level shifters are used, + this technique should be employed when using different bus-widths + on different slots, disabling level shifters to avoid presenting + non-uniform impedance across DATA0-7 & CMD when non-selected + 4-wide slots are left enabled, while accessing 8-wide targets. + + Note that it's not possible to specify multiple regulators + controlled by same GPIO pin, but with different active state. + If one GPIO line is require to switch voltage/routing between + different mmc-slots, specify a vqmmc-supply on one slot, but + not the other. The regulator_disable call on leaving that slot + will implicitly switch the state to support the unmarked slot. + + There's no need to list vqmmc-supply if all the mmc-slots on + a board run at same voltage, and have same width. Examples: mmc_1_4: mmc@1,4 { @@ -40,7 +80,8 @@ Examples: compatible = "mmc-slot"; reg = <0>; vmmc-supply = <&mmc_supply_3v3>; - max-frequency = <42000000>; + vqmmc-supply = <&vqmmc_3v3>; + max-frequency = <52000000>; bus-width = <4>; cap-sd-highspeed; }; @@ -49,7 +90,8 @@ Examples: compatible = "mmc-slot"; reg = <1>; vmmc-supply = <&mmc_supply_3v3>; - max-frequency = <42000000>; + vqmmc-supply = <&vqmmc_1v8>; + max-frequency = <100000000>; bus-width = <8>; cap-mmc-highspeed; non-removable; diff --git a/Documentation/devicetree/bindings/perf/arm_smmuv3_pmu.txt b/Documentation/devicetree/bindings/perf/arm_smmuv3_pmu.txt new file mode 100644 index 000000000000..39c95d5d03ef --- /dev/null +++ b/Documentation/devicetree/bindings/perf/arm_smmuv3_pmu.txt @@ -0,0 +1,34 @@ +* ARM SMMU-V3 PMU (Performance Monitor Extension) + +Required properties: + +- compatible: (standard compatible string) should be: + "arm,smmu-pmu-v3" + +- reg: (standard registers property) physical address and + size of the configuration registers blocks. + + SMMU-V3 PMU can have one or more Performance monitor + counter Group (PMCG). Each PMCG have one 4K page and + additional optional one more 4K page + +- msi-parent: See the generic MSI binding described in + devicetree/bindings/interrupt-controller/msi.txt + for a description of the msi-parent property. +Example: + + smmupmcg0@30100000 { + compatible = "arm,smmu-pmu-v3"; + + /* Single configuration register page */ + reg = <0x30100000 0x1000>; + msi-parent = <&its 0x10>; + }; + + smmupmcg0@830000100000 { + compatible = "arm,smmu-pmu-v3"; + + /* Additional optional configuration register page */ + reg = <0x30100000 0x1000>, <0x30110000 0x1000>; + msi-parent = <&its 0x11>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index c2a7491ddc68..609ec66e2ce0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6643,6 +6643,11 @@ F: drivers/net/wan/pc300too.c F: drivers/net/wan/pci200syn.c F: drivers/net/wan/wanxl* +GENERIC I2C GPIO DRIVER +M: Peter Swain <pswain@marvell.com> +S: Maintained +F: drivers/gpio/gpio-i2c.c + GENERIC INCLUDE/ASM HEADER FILES M: Arnd Bergmann <arnd@arndb.de> L: linux-arch@vger.kernel.org @@ -9435,7 +9440,7 @@ F: Documentation/devicetree/bindings/phy/phy-mvebu-utmi.txt MARVELL CRYPTO DRIVER M: Boris Brezillon <bbrezillon@kernel.org> M: Arnaud Ebalard <arno@natisbad.org> -F: drivers/crypto/marvell/ +F: drivers/crypto/marvell/cesa/ S: Maintained L: linux-crypto@vger.kernel.org diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index cf5f1dafcf74..0c2270ebcf30 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -597,6 +597,31 @@ config CAVIUM_ERRATUM_30115 If unsure, say Y. +config CAVIUM_ERRATUM_36890 + bool "Cavium erratum 36890" + default y + help + Enable workaround for erratum 36890. On all ThunderX T88xx and + OcteonTX1 T81/T83 and some OcteonTX2 chips, the "dc zva" instruction + does not work all the time. This happens when there are two VAs + that match up with one PA; including when the two VAs match but have + different asids. The fix is to disable "dc zva" in userspace. + + If unsure, say Y. + +config MRVL_ERRATUM_37119 + bool "Marvell erratum 37119" + default y + help + Enable workaround for erratum 37119. On some OcteonTX2 chips, + while using the SSO workslots in userspace can cause problems + as the cache are not invalidated correctly. To workaround + this erratum, a local tlbi is placed before the eret. + This TLBI does not need to invalidate anything so a tlbi against + asid 0 is used. + + If unsure, say Y. + config QCOM_FALKOR_ERRATUM_1003 bool "Falkor E1003: Incorrect translation due to ASID change" default y @@ -682,6 +707,14 @@ config FUJITSU_ERRATUM_010001 endmenu +config MRVL_OCTEONTX_EL0_INTR + bool "Handle interrupts in EL0 via EL3" + default y + help + Handle certain interrupts in EL0 with the help of EL3 firmware to achieve + low latency and also not break task isolation. + Generally implemented and tested on OcteonTx and its successive + generation CPUs. choice prompt "Page size" diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h index f19fe4b9acc4..782e5403027c 100644 --- a/arch/arm64/include/asm/cpucaps.h +++ b/arch/arm64/include/asm/cpucaps.h @@ -52,7 +52,9 @@ #define ARM64_HAS_IRQ_PRIO_MASKING 42 #define ARM64_HAS_DCPODP 43 #define ARM64_WORKAROUND_1463225 44 +#define ARM64_WORKAROUND_CAVIUM_36890 45 +#define ARM64_WORKAROUND_MRVL_37119 46 -#define ARM64_NCAPS 45 +#define ARM64_NCAPS 47 #endif /* __ASM_CPUCAPS_H */ diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index aca07c2f6e6e..f04cdf8a41fa 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -79,6 +79,9 @@ #define CAVIUM_CPU_PART_THUNDERX_83XX 0x0A3 #define CAVIUM_CPU_PART_THUNDERX2 0x0AF +#define MRVL_CPU_PART_OCTEONTX2_96XX 0x0B2 +#define MRVL_CPU_PART_OCTEONTX2_95XX 0x0B3 + #define BRCM_CPU_PART_BRAHMA_B53 0x100 #define BRCM_CPU_PART_VULCAN 0x516 @@ -106,6 +109,8 @@ #define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX) #define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX) #define MIDR_CAVIUM_THUNDERX2 MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX2) +#define MIDR_MRVL_OCTEONTX2_96XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, MRVL_CPU_PART_OCTEONTX2_96XX) +#define MIDR_MRVL_OCTEONTX2_95XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, MRVL_CPU_PART_OCTEONTX2_95XX) #define MIDR_BRAHMA_B53 MIDR_CPU_MODEL(ARM_CPU_IMP_BRCM, BRCM_CPU_PART_BRAHMA_B53) #define MIDR_BRCM_VULCAN MIDR_CPU_MODEL(ARM_CPU_IMP_BRCM, BRCM_CPU_PART_VULCAN) #define MIDR_QCOM_FALKOR_V1 MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_FALKOR_V1) diff --git a/arch/arm64/include/asm/irq.h b/arch/arm64/include/asm/irq.h index b2b0c6405eb0..882de50937e9 100644 --- a/arch/arm64/include/asm/irq.h +++ b/arch/arm64/include/asm/irq.h @@ -6,6 +6,15 @@ #include <asm-generic/irq.h> +/* Platforms with multiple SR-IOV capable PCI devices will + * need large number of MSIX vectors, hence keep this number + * fairly high. + */ +#ifdef CONFIG_PCI_MSI +#undef NR_IRQS +#define NR_IRQS 65536 +#endif + struct pt_regs; static inline int nr_legacy_irqs(void) diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index 7ed0adb187a8..d6f46d8a72be 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -248,6 +248,12 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, void verify_cpu_asid_bits(void); void post_ttbr_update_workaround(void); +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +int lock_context(struct mm_struct *mm, int index); +int unlock_context_by_index(int index); +bool unlock_context_by_mm(struct mm_struct *mm); +#endif + #endif /* !__ASSEMBLY__ */ #endif /* !__ASM_MMU_CONTEXT_H */ diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index a38b0a60f807..c87593ea489b 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -97,6 +97,23 @@ cpu_enable_trap_ctr_access(const struct arm64_cpu_capabilities *__unused) sysreg_clear_set(sctlr_el1, SCTLR_EL1_UCT, 0); } +#ifdef CONFIG_CAVIUM_ERRATUM_36890 +static void +cpu_enable_trap_zva_access(const struct arm64_cpu_capabilities *__unused) +{ + /* + * Clear SCTLR_EL2.DZE or SCTLR_EL1.DZE depending + * on if we are in EL2. + */ + if (!is_kernel_in_hyp_mode()) + sysreg_clear_set(sctlr_el1, SCTLR_EL1_DZE, 0); + else + sysreg_clear_set(sctlr_el2, SCTLR_EL1_DZE, 0); + + return; +} +#endif + atomic_t arm64_el2_vector_last_slot = ATOMIC_INIT(-1); #include <asm/mmu_context.h> @@ -666,6 +683,32 @@ static const struct midr_range cavium_erratum_30115_cpus[] = { }; #endif +#ifdef CONFIG_CAVIUM_ERRATUM_36890 +static const struct midr_range cavium_erratum_36890_cpus[] = { + /* Cavium ThunderX, T88 all passes */ + MIDR_ALL_VERSIONS(MIDR_THUNDERX), + /* Cavium ThunderX, T81 all passes */ + MIDR_ALL_VERSIONS(MIDR_THUNDERX_81XX), + /* Cavium ThunderX, T83 all passes */ + MIDR_ALL_VERSIONS(MIDR_THUNDERX_83XX), + /* Marvell OcteonTX 2, 96xx pass A0, A1, and B0 */ + MIDR_RANGE(MIDR_MRVL_OCTEONTX2_96XX, 0, 0, 1, 0), + /* Marvell OcteonTX 2, 95 pass A0/A1 */ + MIDR_RANGE(MIDR_MRVL_OCTEONTX2_95XX, 0, 0, 0, 1), + {}, +}; +#endif + +#ifdef CONFIG_MRVL_ERRATUM_37119 +static const struct midr_range mrvl_erratum_37119_cpus[] = { + /* Marvell OcteonTX 2, 96xx pass A0, A1, and B0 */ + MIDR_RANGE(MIDR_MRVL_OCTEONTX2_96XX, 0, 0, 1, 0), + /* Marvell OcteonTX 2, 95 pass A0/A1 */ + MIDR_RANGE(MIDR_MRVL_OCTEONTX2_95XX, 0, 0, 0, 1), + {}, +}; +#endif + #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003 static const struct arm64_cpu_capabilities qcom_erratum_1003_list[] = { { @@ -803,6 +846,21 @@ const struct arm64_cpu_capabilities arm64_errata[] = { ERRATA_MIDR_RANGE_LIST(cavium_erratum_30115_cpus), }, #endif +#ifdef CONFIG_CAVIUM_ERRATUM_36890 + { + .desc = "Cavium erratum 36890", + .capability = ARM64_WORKAROUND_CAVIUM_36890, + ERRATA_MIDR_RANGE_LIST(cavium_erratum_36890_cpus), + .cpu_enable = cpu_enable_trap_zva_access, + }, +#endif +#ifdef CONFIG_MRVL_ERRATUM_37119 + { + .desc = "Marvell erratum 37119", + .capability = ARM64_WORKAROUND_MRVL_37119, + ERRATA_MIDR_RANGE_LIST(mrvl_erratum_37119_cpus), + }, +#endif { .desc = "Mismatched cache type (CTR_EL0)", .capability = ARM64_MISMATCHED_CACHE_TYPE, diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index e304fe04b098..9e3c33f56312 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -364,6 +364,9 @@ alternative_else_nop_endif ldr lr, [sp, #S_LR] add sp, sp, #S_FRAME_SIZE // restore sp +#ifdef CONFIG_MRVL_ERRATUM_37119 +alternative_insn nop, "tlbi aside1, xzr", ARM64_WORKAROUND_MRVL_37119 +#endif .if \el == 0 alternative_insn eret, nop, ARM64_UNMAP_KERNEL_AT_EL0 #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index b5e329fde2dd..aa3e43e87dab 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -24,6 +24,13 @@ static unsigned long *asid_map; static DEFINE_PER_CPU(atomic64_t, active_asids); static DEFINE_PER_CPU(u64, reserved_asids); + +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +#define LOCKED_ASIDS_COUNT 128 + +static u64 locked_asids[LOCKED_ASIDS_COUNT]; +#endif + static cpumask_t tlb_flush_pending; #define ASID_MASK (~GENMASK(asid_bits - 1, 0)) @@ -100,6 +107,14 @@ static void flush_context(void) per_cpu(reserved_asids, i) = asid; } +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR + /* Set bits for locked ASIDs. */ + for (i = 0; i < LOCKED_ASIDS_COUNT; i++) { + asid = locked_asids[i]; + if (asid != 0) + __set_bit(asid & ~ASID_MASK, asid_map); + } +#endif /* * Queue a TLB invalidation for each CPU to perform on next * context-switch @@ -107,9 +122,61 @@ static void flush_context(void) cpumask_setall(&tlb_flush_pending); } +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +int lock_context(struct mm_struct *mm, int index) +{ + unsigned long flags; + u64 asid; + + if ((index < 0) || (index >= LOCKED_ASIDS_COUNT)) + return -1; + raw_spin_lock_irqsave(&cpu_asid_lock, flags); + asid = atomic64_read(&mm->context.id); + locked_asids[index] = asid; + raw_spin_unlock_irqrestore(&cpu_asid_lock, flags); + return 0; +} +EXPORT_SYMBOL(lock_context); + +int unlock_context_by_index(int index) +{ + unsigned long flags; + + if ((index < 0) || (index >= LOCKED_ASIDS_COUNT)) + return -1; + raw_spin_lock_irqsave(&cpu_asid_lock, flags); + locked_asids[index] = 0; + raw_spin_unlock_irqrestore(&cpu_asid_lock, flags); + return 0; +} +EXPORT_SYMBOL(unlock_context_by_index); + +bool unlock_context_by_mm(struct mm_struct *mm) +{ + int i; + unsigned long flags; + bool hit = false; + u64 asid; + + raw_spin_lock_irqsave(&cpu_asid_lock, flags); + asid = atomic64_read(&mm->context.id); + + for (i = 0; i < LOCKED_ASIDS_COUNT; i++) { + if (locked_asids[i] == asid) { + hit = true; + locked_asids[i] = 0; + } + } + + raw_spin_unlock_irqrestore(&cpu_asid_lock, flags); + + return hit; +} +#endif + static bool check_update_reserved_asid(u64 asid, u64 newasid) { - int cpu; + int i, cpu; bool hit = false; /* @@ -128,6 +195,16 @@ static bool check_update_reserved_asid(u64 asid, u64 newasid) } } +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR + /* Same mechanism for locked ASIDs */ + for (i = 0; i < LOCKED_ASIDS_COUNT; i++) { + if (locked_asids[i] == asid) { + hit = true; + locked_asids[i] = newasid; + } + } +#endif + return hit; } diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index a23a7197fcd7..a48e7b907c40 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o obj-$(CONFIG_CRYPTO_DEV_IMGTEC_HASH) += img-hash.o obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o -obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell/ +obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell/cesa/ obj-$(CONFIG_CRYPTO_DEV_MEDIATEK) += mediatek/ obj-$(CONFIG_CRYPTO_DEV_MXS_DCP) += mxs-dcp.o obj-$(CONFIG_CRYPTO_DEV_NIAGARA2) += n2_crypto.o diff --git a/drivers/crypto/marvell/Makefile b/drivers/crypto/marvell/cesa/Makefile index b27cab65e696..b27cab65e696 100644 --- a/drivers/crypto/marvell/Makefile +++ b/drivers/crypto/marvell/cesa/Makefile diff --git a/drivers/crypto/marvell/cesa.c b/drivers/crypto/marvell/cesa/cesa.c index 8a5f0b0bdf77..8a5f0b0bdf77 100644 --- a/drivers/crypto/marvell/cesa.c +++ b/drivers/crypto/marvell/cesa/cesa.c diff --git a/drivers/crypto/marvell/cesa.h b/drivers/crypto/marvell/cesa/cesa.h index d63a6ee905c9..d63a6ee905c9 100644 --- a/drivers/crypto/marvell/cesa.h +++ b/drivers/crypto/marvell/cesa/cesa.h diff --git a/drivers/crypto/marvell/cipher.c b/drivers/crypto/marvell/cesa/cipher.c index f4321f3c0777..f4321f3c0777 100644 --- a/drivers/crypto/marvell/cipher.c +++ b/drivers/crypto/marvell/cesa/cipher.c diff --git a/drivers/crypto/marvell/hash.c b/drivers/crypto/marvell/cesa/hash.c index 0f0ac851f4eb..0f0ac851f4eb 100644 --- a/drivers/crypto/marvell/hash.c +++ b/drivers/crypto/marvell/cesa/hash.c diff --git a/drivers/crypto/marvell/tdma.c b/drivers/crypto/marvell/cesa/tdma.c index 45939d53e8d6..45939d53e8d6 100644 --- a/drivers/crypto/marvell/tdma.c +++ b/drivers/crypto/marvell/cesa/tdma.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index acd40eb51c46..44ebb0b186f1 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -544,6 +544,7 @@ config GPIO_THUNDERX depends on PCI_MSI select IRQ_DOMAIN_HIERARCHY select IRQ_FASTEOI_HIERARCHY_HANDLERS + select GPIOLIB_IRQCHIP help Say yes here to support the on-chip GPIO lines on the ThunderX and OCTEON-TX families of SoCs. @@ -847,6 +848,18 @@ config GPIO_GW_PLD Say yes here to provide access to the Gateworks I2C PLD GPIO Expander. This is used at least on the Cambria GW2358-4. +config GPIO_I2C + tristate "Generic I2C->GPIO no-irq expander" + depends on OF + default n + help + Select this option to enable GPIO for simple I2C devices, + parameterized by device-tree, and having no interrupts. + Developed to model a custom board's CPLD, but may be useful + for various hardware where i2c-poking flips external pins. + + If unsure, say N. + config GPIO_MAX7300 tristate "Maxim MAX7300 GPIO expander" select GPIO_MAX730X diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 6700eee860b7..07a30dbf0930 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o +obj-$(CONFIG_GPIO_I2C) += gpio-i2c.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o obj-$(CONFIG_GPIO_IOP) += gpio-iop.o obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o diff --git a/drivers/gpio/gpio-i2c.c b/drivers/gpio/gpio-i2c.c new file mode 100644 index 000000000000..f24b7df4c69d --- /dev/null +++ b/drivers/gpio/gpio-i2c.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * simple parameterized no-irq of_driven i2c->gpio expander, + * cut down from gpio-pcf857x.c to be totally device-tree driven. + * + * Suitable for any "memory-like" device, where a 1-byte i2c read yields data + * which can safely be written back, possibly with a bit changed, with the + * effect of changing only the output level of that bit's GPIO pin. + * + * Copyright (C) 2016 Cavium Inc. + * Copyright (C) 2019 Marvell International Ltd. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + */ + +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +struct gpio_i2c_platform_data { + unsigned int i2c_addr; + unsigned int pins; +}; + + +static const struct of_device_id gpio_i2c_of_table[] = { + { .compatible = "gpio-i2c" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpio_i2c_of_table); + +struct gpio_i2c { + unsigned int i2c_addr; + struct gpio_chip chip; + struct i2c_client *client; + struct mutex lock; /* protect 'out' */ + u8 out[]; /* software latch */ +}; + +/*-------------------------------------------------------------------------*/ + +static int gpio_i2c_get(struct gpio_chip *chip, unsigned int offset) +{ + struct gpio_i2c *gpio = container_of(chip, struct gpio_i2c, chip); + int value; + unsigned int byte = (offset >> 3); + unsigned int bit = (offset & 7); + + mutex_lock(&gpio->lock); + value = i2c_smbus_read_byte_data(gpio->client, byte); + mutex_unlock(&gpio->lock); + return (value < 0) ? 0 : ((value >> bit) & 1); +} + +static int gpio_i2c_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct gpio_i2c *gpio = container_of(chip, struct gpio_i2c, chip); + unsigned int byte = (offset >> 3); + unsigned int bit = (offset & 7); + unsigned int mask = (1 << bit); + int status; + u8 was; + + mutex_lock(&gpio->lock); + was = i2c_smbus_read_byte_data(gpio->client, byte); + if (value) + was |= mask; + else + was &= ~mask; + status = i2c_smbus_write_byte_data(gpio->client, byte, was); + gpio->out[byte] = was; + mutex_unlock(&gpio->lock); + + return status; +} + +static void gpio_i2c_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + gpio_i2c_output(chip, offset, value); +} + +/* for open-drain: set as input by letting output go high */ +static int gpio_i2c_input(struct gpio_chip *chip, unsigned int offset) +{ + return gpio_i2c_output(chip, offset, 1); +} + +/*-------------------------------------------------------------------------*/ + +static int gpio_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct gpio_i2c_platform_data *pdata = dev_get_platdata(&client->dev); + struct device_node *np = client->dev.of_node; + struct gpio_i2c *gpio; + u32 pins; + u32 i2c_addr; + int status; + + if (np) { + status = of_property_read_u32(np, "reg", &i2c_addr); + if (status < 0) { + dev_dbg(&client->dev, "missing reg property\n"); + return status; + } + status = of_property_read_u32(np, "ngpios", &pins); + if (status < 0) { + dev_dbg(&client->dev, "missing ngpios property\n"); + return status; + } + } else if (pdata) { + i2c_addr = pdata->i2c_addr; + pins = pdata->pins; + } else { + dev_dbg(&client->dev, "no platform data\n"); + return -EPROBE_DEFER; + } + + /* Allocate, initialize, and register this gpio_chip. */ + gpio = devm_kzalloc(&client->dev, + sizeof(*gpio) + (pins + 7) / 8, GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + mutex_init(&gpio->lock); + + gpio->i2c_addr = i2c_addr; + gpio->chip.base = -1; + gpio->chip.can_sleep = true; + gpio->chip.parent = &client->dev; + gpio->chip.owner = THIS_MODULE; + gpio->chip.get = gpio_i2c_get; + gpio->chip.set = gpio_i2c_set; + gpio->chip.direction_input = gpio_i2c_input; + gpio->chip.direction_output = gpio_i2c_output; + gpio->chip.ngpio = pins; + gpio->chip.label = client->name; + gpio->client = client; + gpio->client->addr = i2c_addr; + i2c_set_clientdata(client, gpio); + + status = gpiochip_add(&gpio->chip); + if (status < 0) + goto fail; + + dev_info(&client->dev, "probed\n"); + + return 0; + +fail: + dev_dbg(&client->dev, "probe error %d for '%s'\n", status, + client->name); + + return status; +} + +static int gpio_i2c_remove(struct i2c_client *client) +{ + struct gpio_i2c *gpio = i2c_get_clientdata(client); + int status = 0; + + gpiochip_remove(&gpio->chip); + return status; +} + +/* this must _exist_ for i2c_device_probe() to call our probe, may be empty */ +static struct i2c_device_id empty_id_table[] = { + { }, +}; +MODULE_DEVICE_TABLE(i2c, empty_id_table); + +static struct i2c_driver gpio_i2c_driver = { + .driver = { + .name = "gpio-i2c", + .of_match_table = of_match_ptr(gpio_i2c_of_table), + }, + .probe = gpio_i2c_probe, + .remove = gpio_i2c_remove, + .id_table = empty_id_table, +}; + +module_i2c_driver(gpio_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); +MODULE_AUTHOR("Peter Swain <pswain@cavium.com>"); +/* + * arguably this name defies convention, but correct(?) alias has been + * taken by the inverse function in + * drivers/i2c/busses/i2c-gpio.c:MODULE_ALIAS("platform:i2c-gpio"); + */ +MODULE_ALIAS("platform:gpio-i2c"); diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index 715371b5102a..686588e928ec 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -13,9 +13,19 @@ #include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_irq.h> #include <linux/pci.h> #include <linux/spinlock.h> - +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +#include <linux/arm-smccc.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/moduleparam.h> +#include <linux/uaccess.h> +#include <linux/mmu_context.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#endif #define GPIO_RX_DAT 0x0 #define GPIO_TX_SET 0x8 @@ -31,17 +41,63 @@ #define GPIO_BIT_CFG_FIL_CNT_SHIFT 4 #define GPIO_BIT_CFG_FIL_SEL_SHIFT 8 #define GPIO_BIT_CFG_TX_OD BIT(12) -#define GPIO_BIT_CFG_PIN_SEL_MASK GENMASK(25, 16) +#define GPIO_BIT_CFG_PIN_SEL_MASK GENMASK(26, 16) #define GPIO_INTR 0x800 #define GPIO_INTR_INTR BIT(0) #define GPIO_INTR_INTR_W1S BIT(1) #define GPIO_INTR_ENA_W1C BIT(2) #define GPIO_INTR_ENA_W1S BIT(3) #define GPIO_2ND_BANK 0x1400 +#define MRVL_OCTEONTX2_96XX_PARTNUM 0xB2 + #define GLITCH_FILTER_400NS ((4u << GPIO_BIT_CFG_FIL_SEL_SHIFT) | \ (9u << GPIO_BIT_CFG_FIL_CNT_SHIFT)) +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +#define DEVICE_NAME "otx-gpio-ctr" +#define OTX_IOC_MAGIC 0xF2 +#define MAX_GPIO 80 + +static struct device *otx_device; +static struct class *otx_class; +static struct cdev *otx_cdev; +static dev_t otx_dev; +static DEFINE_SPINLOCK(el3_inthandler_lock); +static int gpio_in_use; +static int gpio_installed[MAX_GPIO]; +static struct thread_info *gpio_installed_threads[MAX_GPIO]; +static struct task_struct *gpio_installed_tasks[MAX_GPIO]; + +/* THUNDERX SMC definitons */ +/* X1 - gpio_num, X2 - sp, X3 - cpu, X4 - ttbr0 */ +#define THUNDERX_INSTALL_GPIO_INT 0xC2000801 +/* X1 - gpio_num */ +#define THUNDERX_REMOVE_GPIO_INT 0xC2000802 + +struct intr_hand { + u64 mask; + char name[50]; + u64 coffset; + u64 soffset; + irqreturn_t (*handler)(int, void *); +}; + +struct otx_gpio_usr_data { + u64 isr_base; + u64 sp; + u64 cpu; + u64 gpio_num; +}; + + +#define OTX_IOC_SET_GPIO_HANDLER \ + _IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data) + +#define OTX_IOC_CLR_GPIO_HANDLER \ + _IO(OTX_IOC_MAGIC, 2) +#endif + struct thunderx_gpio; struct thunderx_line { @@ -62,6 +118,159 @@ struct thunderx_gpio { int base_msi; }; +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +static inline int __install_el3_inthandler(unsigned long gpio_num, + unsigned long sp, + unsigned long cpu, + unsigned long ttbr0) +{ + struct arm_smccc_res res; + unsigned long flags; + int retval = -1; + + spin_lock_irqsave(&el3_inthandler_lock, flags); + if (!gpio_installed[gpio_num]) { + lock_context(current->group_leader->mm, gpio_num); + arm_smccc_smc(THUNDERX_INSTALL_GPIO_INT, gpio_num, + sp, cpu, ttbr0, 0, 0, 0, &res); + if (res.a0 == 0) { + gpio_installed[gpio_num] = 1; + gpio_installed_threads[gpio_num] + = current_thread_info(); + gpio_installed_tasks[gpio_num] + = current->group_leader; + retval = 0; + } else { + unlock_context_by_index(gpio_num); + } + } + spin_unlock_irqrestore(&el3_inthandler_lock, flags); + return retval; +} + +static inline int __remove_el3_inthandler(unsigned long gpio_num) +{ + struct arm_smccc_res res; + unsigned long flags; + unsigned int retval; + + spin_lock_irqsave(&el3_inthandler_lock, flags); + if (gpio_installed[gpio_num]) { + arm_smccc_smc(THUNDERX_REMOVE_GPIO_INT, gpio_num, + 0, 0, 0, 0, 0, 0, &res); + gpio_installed[gpio_num] = 0; + gpio_installed_threads[gpio_num] = NULL; + gpio_installed_tasks[gpio_num] = NULL; + unlock_context_by_index(gpio_num); + retval = 0; + } else { + retval = -1; + } + spin_unlock_irqrestore(&el3_inthandler_lock, flags); + return retval; +} + +static long otx_dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct otx_gpio_usr_data gpio_usr; + u64 gpio_ttbr, gpio_isr_base, gpio_sp, gpio_cpu, gpio_num; + int ret; + //struct task_struct *task = current; + + if (!gpio_in_use) + return -EINVAL; + + if (_IOC_TYPE(cmd) != OTX_IOC_MAGIC) + return -ENOTTY; + + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_TYPE(cmd) & _IOC_WRITE) + err = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); + + if (err) + return -EFAULT; + + switch (cmd) { + case OTX_IOC_SET_GPIO_HANDLER: /*Install GPIO ISR handler*/ + ret = copy_from_user(&gpio_usr, (void *)arg, _IOC_SIZE(cmd)); + if (gpio_usr.gpio_num >= MAX_GPIO) + return -EINVAL; + if (ret) + return -EFAULT; + gpio_ttbr = 0; + //TODO: reserve a asid to avoid asid rollovers + asm volatile("mrs %0, ttbr0_el1\n\t" : "=r"(gpio_ttbr)); + gpio_isr_base = gpio_usr.isr_base; + gpio_sp = gpio_usr.sp; + gpio_cpu = gpio_usr.cpu; + gpio_num = gpio_usr.gpio_num; + ret = __install_el3_inthandler(gpio_num, gpio_sp, + gpio_cpu, gpio_isr_base); + if (ret != 0) + return -EEXIST; + break; + case OTX_IOC_CLR_GPIO_HANDLER: /*Clear GPIO ISR handler*/ + gpio_usr.gpio_num = arg; + if (gpio_usr.gpio_num >= MAX_GPIO) + return -EINVAL; + ret = __remove_el3_inthandler(gpio_usr.gpio_num); + if (ret != 0) + return -ENOENT; + break; + default: + return -ENOTTY; + } + return 0; +} + +static void cleanup_el3_irqs(struct task_struct *task) +{ + int i; + + for (i = 0; i < MAX_GPIO; i++) { + if (gpio_installed[i] && + gpio_installed_tasks[i] && + (gpio_installed_tasks[i] == task)) { + pr_alert("Exiting, removing handler for GPIO %d\n", + i); + __remove_el3_inthandler(i); + pr_alert("Exited, removed handler for GPIO %d\n", + i); + } else { + if (gpio_installed[i] && + (gpio_installed_threads[i] + == current_thread_info())) + pr_alert( + "Exiting, thread info matches, not removing handler for GPIO %d\n", + i); + } + } +} + +static int otx_dev_open(struct inode *inode, struct file *fp) +{ + gpio_in_use = 1; + return 0; +} + +static int otx_dev_release(struct inode *inode, struct file *fp) +{ + if (gpio_in_use == 0) + return -EINVAL; + gpio_in_use = 0; + return 0; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = otx_dev_open, + .release = otx_dev_release, + .unlocked_ioctl = otx_dev_ioctl +}; +#endif + static unsigned int bit_cfg_reg(unsigned int line) { return 8 * line + GPIO_BIT_CFG; @@ -104,16 +313,17 @@ static int thunderx_gpio_request(struct gpio_chip *chip, unsigned int line) static int thunderx_gpio_dir_in(struct gpio_chip *chip, unsigned int line) { struct thunderx_gpio *txgpio = gpiochip_get_data(chip); + unsigned long flags; if (!thunderx_gpio_is_gpio(txgpio, line)) return -EIO; - raw_spin_lock(&txgpio->lock); + raw_spin_lock_irqsave(&txgpio->lock, flags); clear_bit(line, txgpio->invert_mask); clear_bit(line, txgpio->od_mask); writeq(txgpio->line_entries[line].fil_bits, txgpio->register_base + bit_cfg_reg(line)); - raw_spin_unlock(&txgpio->lock); + raw_spin_unlock_irqrestore(&txgpio->lock, flags); return 0; } @@ -135,11 +345,12 @@ static int thunderx_gpio_dir_out(struct gpio_chip *chip, unsigned int line, { struct thunderx_gpio *txgpio = gpiochip_get_data(chip); u64 bit_cfg = txgpio->line_entries[line].fil_bits | GPIO_BIT_CFG_TX_OE; + unsigned long flags; if (!thunderx_gpio_is_gpio(txgpio, line)) return -EIO; - raw_spin_lock(&txgpio->lock); + raw_spin_lock_irqsave(&txgpio->lock, flags); thunderx_gpio_set(chip, line, value); @@ -151,7 +362,7 @@ static int thunderx_gpio_dir_out(struct gpio_chip *chip, unsigned int line, writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(line)); - raw_spin_unlock(&txgpio->lock); + raw_spin_unlock_irqrestore(&txgpio->lock, flags); return 0; } @@ -185,11 +396,12 @@ static int thunderx_gpio_set_config(struct gpio_chip *chip, int ret = -ENOTSUPP; struct thunderx_gpio *txgpio = gpiochip_get_data(chip); void __iomem *reg = txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET; + unsigned long flags; if (!thunderx_gpio_is_gpio(txgpio, line)) return -EIO; - raw_spin_lock(&txgpio->lock); + raw_spin_lock_irqsave(&txgpio->lock, flags); orig_invert = test_bit(line, txgpio->invert_mask); new_invert = orig_invert; orig_od = test_bit(line, txgpio->od_mask); @@ -240,7 +452,7 @@ static int thunderx_gpio_set_config(struct gpio_chip *chip, default: break; } - raw_spin_unlock(&txgpio->lock); + raw_spin_unlock_irqrestore(&txgpio->lock, flags); /* * If currently output and OPEN_DRAIN changed, install the new @@ -283,6 +495,149 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip, } } +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +static void thunderx_gpio_spi_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + writeq(GPIO_INTR_INTR, + gpio->register_base + intr_reg(line)); +} + +static void thunderx_gpio_spi_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + writeq(GPIO_INTR_ENA_W1C, gpio->register_base + intr_reg(line)); +} + +static void thunderx_gpio_spi_irq_mask_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR, + gpio->register_base + intr_reg(line)); +} + +static void thunderx_gpio_spi_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + writeq(GPIO_INTR_ENA_W1S, gpio->register_base + intr_reg(line)); +} + +/* + * Do not set msix_entries for SPI IRQs. + */ +static int thunderx_gpio_spi_irq_request_resources(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + if (!thunderx_gpio_is_gpio(gpio, line)) + return -EIO; + + writeq(GPIO_INTR_ENA_W1C, gpio->register_base + intr_reg(line)); + + return 0; +} + +static void thunderx_gpio_spi_irq_release_resources(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + + writeq(GPIO_INTR_ENA_W1C, gpio->register_base + intr_reg(line)); + +} + +static int thunderx_gpio_spi_irq_set_type(struct irq_data *data, + unsigned int flow_type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + unsigned int line = data->hwirq; + u64 bit_cfg; + unsigned long flags; + + irqd_set_trigger_type(data, flow_type); + + bit_cfg = GLITCH_FILTER_400NS | GPIO_BIT_CFG_INT_EN; + + raw_spin_lock_irqsave(&gpio->lock, flags); + if (flow_type & IRQ_TYPE_EDGE_BOTH) { + irq_set_handler_locked(data, handle_edge_irq); + bit_cfg |= GPIO_BIT_CFG_INT_TYPE; + } else { + irq_set_handler_locked(data, handle_level_irq); + } + + if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW)) { + bit_cfg |= GPIO_BIT_CFG_PIN_XOR; + set_bit(line, gpio->invert_mask); + } else { + clear_bit(line, gpio->invert_mask); + } + clear_bit(line, gpio->od_mask); + writeq(bit_cfg, gpio->register_base + bit_cfg_reg(line)); + raw_spin_unlock_irqrestore(&gpio->lock, flags); + + return IRQ_SET_MASK_OK; +} + +static void thunderx_gpio_spi_irq_handler(struct irq_desc *desc) +{ + unsigned int line; + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + struct thunderx_gpio *gpio = + container_of(chip, struct thunderx_gpio, chip); + + chained_irq_enter(irqchip, desc); + for (line = 0; line < chip->ngpio; line++) { + if (readq(gpio->register_base + intr_reg(line)) & + GPIO_INTR_INTR) { + generic_handle_irq(irq_find_mapping(chip->irq.domain, + line)); + writeq(GPIO_INTR_INTR, + gpio->register_base + intr_reg(line)); + } + } + chained_irq_exit(irqchip, desc); +} + +static struct irq_chip thunderx_gpio_spi_irq_chip = { + .name = "GPIO", + .irq_enable = thunderx_gpio_spi_irq_unmask, + .irq_disable = thunderx_gpio_spi_irq_mask, + .irq_ack = thunderx_gpio_spi_irq_ack, + .irq_mask = thunderx_gpio_spi_irq_mask, + .irq_mask_ack = thunderx_gpio_spi_irq_mask_ack, + .irq_unmask = thunderx_gpio_spi_irq_unmask, + .irq_set_type = thunderx_gpio_spi_irq_set_type, + .irq_request_resources = thunderx_gpio_spi_irq_request_resources, + .irq_release_resources = thunderx_gpio_spi_irq_release_resources, + .flags = IRQCHIP_SET_TYPE_MASKED +}; +#endif + static void thunderx_gpio_irq_ack(struct irq_data *data) { struct thunderx_line *txline = irq_data_get_irq_chip_data(data); @@ -321,11 +676,13 @@ static int thunderx_gpio_irq_set_type(struct irq_data *data, struct thunderx_line *txline = irq_data_get_irq_chip_data(data); struct thunderx_gpio *txgpio = txline->txgpio; u64 bit_cfg; + unsigned long flags; irqd_set_trigger_type(data, flow_type); bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN; + raw_spin_lock_irqsave(&txgpio->lock, flags); if (flow_type & IRQ_TYPE_EDGE_BOTH) { irq_set_handler_locked(data, handle_fasteoi_ack_irq); bit_cfg |= GPIO_BIT_CFG_INT_TYPE; @@ -333,7 +690,6 @@ static int thunderx_gpio_irq_set_type(struct irq_data *data, irq_set_handler_locked(data, handle_fasteoi_mask_irq); } - raw_spin_lock(&txgpio->lock); if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW)) { bit_cfg |= GPIO_BIT_CFG_PIN_XOR; set_bit(txline->line, txgpio->invert_mask); @@ -342,7 +698,7 @@ static int thunderx_gpio_irq_set_type(struct irq_data *data, } clear_bit(txline->line, txgpio->od_mask); writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(txline->line)); - raw_spin_unlock(&txgpio->lock); + raw_spin_unlock_irqrestore(&txgpio->lock, flags); return IRQ_SET_MASK_OK; } @@ -493,7 +849,13 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, u64 c = readq(txgpio->register_base + GPIO_CONST); ngpio = c & GPIO_CONST_GPIOS_MASK; - txgpio->base_msi = (c >> 8) & 0xff; + + /* Workaround for all passes of T96xx */ + if (((pdev->subsystem_device >> 8) & 0xFF) == MRVL_OCTEONTX2_96XX_PARTNUM) { + txgpio->base_msi = 0x36; + } else { + txgpio->base_msi = (c >> 8) & 0xff; + } } txgpio->msix_entries = devm_kcalloc(dev, @@ -504,10 +866,15 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, goto out; } +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR + pdev->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); +#endif + txgpio->line_entries = devm_kcalloc(dev, ngpio, sizeof(struct thunderx_line), GFP_KERNEL); + if (!txgpio->line_entries) { err = -ENOMEM; goto out; @@ -533,31 +900,32 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, set_bit(i, txgpio->invert_mask); } - /* Enable all MSI-X for interrupts on all possible lines. */ err = pci_enable_msix_range(pdev, txgpio->msix_entries, ngpio, ngpio); if (err < 0) goto out; - /* - * Push GPIO specific irqdomain on hierarchy created as a side - * effect of the pci_enable_msix() - */ - txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain, - 0, 0, of_node_to_fwnode(dev->of_node), - &thunderx_gpio_irqd_ops, txgpio); - if (!txgpio->irqd) { - err = -ENOMEM; - goto out; - } + if (pdev->irq == 0) { + /* + * Push GPIO specific irqdomain on hierarchy created as a side + * effect of the pci_enable_msix() + */ + txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain, + 0, 0, of_node_to_fwnode(dev->of_node), + &thunderx_gpio_irqd_ops, txgpio); + if (!txgpio->irqd) { + err = -ENOMEM; + goto out; + } - /* Push on irq_data and the domain for each line. */ - for (i = 0; i < ngpio; i++) { - err = irq_domain_push_irq(txgpio->irqd, - txgpio->msix_entries[i].vector, - &txgpio->line_entries[i]); - if (err < 0) - dev_err(dev, "irq_domain_push_irq: %d\n", err); + /* Push on irq_data and the domain for each line. */ + for (i = 0; i < ngpio; i++) { + err = irq_domain_push_irq(txgpio->irqd, + txgpio->msix_entries[i].vector, + &txgpio->line_entries[i]); + if (err < 0) + dev_err(dev, "irq_domain_push_irq: %d\n", err); + } } chip->label = KBUILD_MODNAME; @@ -581,7 +949,81 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n", ngpio, chip->base); + +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR + if (pdev->irq != 0) { + err = gpiochip_irqchip_add(chip, &thunderx_gpio_spi_irq_chip, 0, + handle_bad_irq, IRQ_TYPE_NONE); + if (err) { + dev_err(dev, "gpiochip_irqchip_add failed: %d\n", err); + goto irqchip_out; + } + + gpiochip_set_chained_irqchip(chip, + &thunderx_gpio_spi_irq_chip, + pdev->irq, + thunderx_gpio_spi_irq_handler); + } + + /* Register task cleanup handler */ + err = task_cleanup_handler_add(cleanup_el3_irqs); + if (err != 0) { + dev_err(dev, "Failed to register cleanup handler: %d\n", err); + goto cleanup_handler_err; + } + + /* create a character device */ + err = alloc_chrdev_region(&otx_dev, 1, 1, DEVICE_NAME); + if (err != 0) { + dev_err(dev, "Failed to create device: %d\n", err); + goto alloc_chrdev_err; + } + + otx_cdev = cdev_alloc(); + if (!otx_cdev) { + err = -ENODEV; + goto cdev_alloc_err; + } + + cdev_init(otx_cdev, &fops); + err = cdev_add(otx_cdev, otx_dev, 1); + if (err < 0) { + err = -ENODEV; + goto cdev_add_err; + } + + /* create new class for sysfs*/ + otx_class = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(otx_class)) { + err = -ENODEV; + goto class_create_err; + } + + otx_device = device_create(otx_class, NULL, otx_dev, NULL, + DEVICE_NAME); + if (IS_ERR(otx_device)) { + err = -ENODEV; + goto device_create_err; + } +#endif + return 0; + +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +device_create_err: + class_destroy(otx_class); + +class_create_err: +cdev_add_err: + cdev_del(otx_cdev); +cdev_alloc_err: + unregister_chrdev_region(otx_dev, 1); +alloc_chrdev_err: + task_cleanup_handler_remove(cleanup_el3_irqs); +cleanup_handler_err: +irqchip_out: + gpiochip_remove(chip); +#endif out: pci_set_drvdata(pdev, NULL); return err; @@ -592,13 +1034,26 @@ static void thunderx_gpio_remove(struct pci_dev *pdev) int i; struct thunderx_gpio *txgpio = pci_get_drvdata(pdev); - for (i = 0; i < txgpio->chip.ngpio; i++) - irq_domain_pop_irq(txgpio->irqd, - txgpio->msix_entries[i].vector); + if (pdev->irq == 0) { + for (i = 0; i < txgpio->chip.ngpio; i++) + irq_domain_pop_irq(txgpio->irqd, + txgpio->msix_entries[i].vector); - irq_domain_remove(txgpio->irqd); + irq_domain_remove(txgpio->irqd); + } else { + gpiochip_remove(&txgpio->chip); + } pci_set_drvdata(pdev, NULL); + +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR + device_destroy(otx_class, otx_dev); + class_destroy(otx_class); + cdev_del(otx_cdev); + unregister_chrdev_region(otx_dev, 1); + + task_cleanup_handler_remove(cleanup_el3_irqs); +#endif } static const struct pci_device_id thunderx_gpio_id_table[] = { diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 1ef098ff27c3..6558dd7652a5 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -223,7 +223,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, id = (u32)event->attr.config2; sink = coresight_get_sink_by_id(id); } else { - sink = coresight_get_enabled_sink(true); + sink = coresight_get_enabled_sink(NULL, true); } if (!sink) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 27e047ebb0ba..af4dfa955f7d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -1202,6 +1202,7 @@ static const struct amba_id etm4_ids[] = { CS_AMBA_ID(0x000bb95a), /* Cortex-A72 */ CS_AMBA_ID(0x000bb959), /* Cortex-A73 */ CS_AMBA_UCI_ID(0x000bb9da, uci_id_etm4), /* Cortex-A35 */ + CS_AMBA_ID(0x000cc210), /* Marvell-OcteonTx-CN9xxx */ {}, }; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index e0684d06e9ee..83325b0f86f0 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -147,7 +147,8 @@ static inline void coresight_write_reg_pair(void __iomem *addr, u64 val, void coresight_disable_path(struct list_head *path); int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data); struct coresight_device *coresight_get_sink(struct list_head *path); -struct coresight_device *coresight_get_enabled_sink(bool reset); +struct coresight_device *coresight_get_enabled_sink(struct coresight_device *cs, + bool reset); struct coresight_device *coresight_get_sink_by_id(u32 id); struct list_head *coresight_build_path(struct coresight_device *csdev, struct coresight_device *sink); diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 9f293b9dce8c..3fe7967efa51 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -16,6 +16,7 @@ #include <linux/vmalloc.h> #include "coresight-catu.h" #include "coresight-etm-perf.h" +#include <linux/arm-smccc.h> #include "coresight-priv.h" #include "coresight-tmc.h" @@ -590,6 +591,9 @@ static int tmc_etr_alloc_flat_buf(struct tmc_drvdata *drvdata, void **pages) { struct etr_flat_buf *flat_buf; + dma_addr_t s_paddr = 0; + int buff_sec_mapped = 0; + int ret; /* We cannot reuse existing pages for flat buf */ if (pages) @@ -606,12 +610,44 @@ static int tmc_etr_alloc_flat_buf(struct tmc_drvdata *drvdata, return -ENOMEM; } + if (!(drvdata->etr_options & CORESIGHT_OPTS_SECURE_BUFF)) + goto skip_secure_buffer; + + /* Register driver allocated dma buffer for necessary + * mapping in the secure world + */ + if (tmc_register_drvbuf(drvdata, flat_buf->daddr, etr_buf->size)) { + ret = -ENOMEM; + goto err; + } + buff_sec_mapped = 1; + + /* Allocate secure trace buffer */ + if (tmc_alloc_secbuf(drvdata, etr_buf->size, &s_paddr)) { + ret = -ENOMEM; + goto err; + } + +skip_secure_buffer: flat_buf->size = etr_buf->size; flat_buf->dev = drvdata->dev; etr_buf->hwaddr = flat_buf->daddr; + etr_buf->s_paddr= s_paddr; etr_buf->mode = ETR_MODE_FLAT; etr_buf->private = flat_buf; return 0; + +err: + kfree(flat_buf); + dma_free_coherent(drvdata->dev, etr_buf->size, flat_buf->vaddr, + flat_buf->daddr); + if (buff_sec_mapped) + tmc_unregister_drvbuf(drvdata, flat_buf->daddr, + etr_buf->size); + if (s_paddr) + tmc_free_secbuf(drvdata, s_paddr, etr_buf->size); + + return ret; } static void tmc_etr_free_flat_buf(struct etr_buf *etr_buf) @@ -937,10 +973,17 @@ static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata) CS_UNLOCK(drvdata->base); + if (drvdata->etr_options & CORESIGHT_OPTS_RESET_CTL_REG) + tmc_disable_hw(drvdata); + /* Wait for TMCSReady bit to be set */ tmc_wait_for_tmcready(drvdata); writel_relaxed(etr_buf->size / 4, drvdata->base + TMC_RSZ); + if (drvdata && CORESIGHT_OPTS_BUFFSIZE_8BX) + writel_relaxed(etr_buf->size / 8, drvdata->base + TMC_RSZ); + else + writel_relaxed(etr_buf->size / 4, drvdata->base + TMC_RSZ); writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); axictl = readl_relaxed(drvdata->base + TMC_AXICTL); @@ -957,7 +1000,10 @@ static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata) axictl |= TMC_AXICTL_SCT_GAT_MODE; writel_relaxed(axictl, drvdata->base + TMC_AXICTL); - tmc_write_dba(drvdata, etr_buf->hwaddr); + if (drvdata->etr_options & CORESIGHT_OPTS_SECURE_BUFF) + tmc_write_dba(drvdata, etr_buf->s_paddr); + else + tmc_write_dba(drvdata, etr_buf->hwaddr); /* * If the TMC pointers must be programmed before the session, * we have to set it properly (i.e, RRP/RWP to base address and @@ -965,7 +1011,10 @@ static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata) */ if (tmc_etr_has_cap(drvdata, TMC_ETR_SAVE_RESTORE)) { tmc_write_rrp(drvdata, etr_buf->hwaddr); - tmc_write_rwp(drvdata, etr_buf->hwaddr); + if (drvdata->etr_options & CORESIGHT_OPTS_SECURE_BUFF) + tmc_write_rwp(drvdata, etr_buf->s_paddr); + else + tmc_write_rwp(drvdata, etr_buf->hwaddr); sts = readl_relaxed(drvdata->base + TMC_STS) & ~TMC_STS_FULL; writel_relaxed(sts, drvdata->base + TMC_STS); } @@ -1696,3 +1745,22 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) return 0; } + +int tmc_copy_secure_buffer(struct tmc_drvdata *drvdata, + char *bufp, size_t len) +{ + struct arm_smccc_res res; + uint64_t offset; + char *vaddr; + struct etr_buf *etr_buf = drvdata->etr_buf; + + tmc_etr_buf_get_data(etr_buf, 0, 0, &vaddr); + offset = bufp - vaddr; + + arm_smccc_smc(OCTEONTX_TRC_COPY_TO_DRVBUF, etr_buf->hwaddr + offset, + etr_buf->s_paddr + offset, len, 0, 0, 0, 0, &res); + if (res.a0 != SMCCC_RET_SUCCESS) + return -EFAULT; + + return 0; +} diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 3f718729d741..17759762abdc 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -151,6 +151,10 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len, if (actual <= 0) return 0; + if ((drvdata->etr_options & CORESIGHT_OPTS_SECURE_BUFF) && + tmc_copy_secure_buffer(drvdata, bufp, len)) + return -EFAULT; + if (copy_to_user(data, bufp, actual)) { dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__); return -EFAULT; @@ -419,11 +423,21 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) spin_lock_init(&drvdata->spinlock); + drvdata->cpu = pdata ? pdata->cpu : 0; + + /* Enable options for Silicon issues */ + if (id->id == OCTEONTX_CN9XXX_ETR) + drvdata->etr_options = CORESIGHT_OPTS_BUFFSIZE_8BX | + CORESIGHT_OPTS_SECURE_BUFF | + CORESIGHT_OPTS_RESET_CTL_REG; + devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); drvdata->config_type = BMVAL(devid, 6, 7); drvdata->memwidth = tmc_get_memwidth(devid); /* This device is not associated with a session */ drvdata->pid = -1; + drvdata->formatter_en = !(readl_relaxed(drvdata->base + TMC_FFSR) & + TMC_FFSR_FT_NOT_PRESENT); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { if (np) @@ -432,6 +446,11 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) &drvdata->size); if (ret) drvdata->size = SZ_1M; + + /* Cache locked buffer */ + if (np) + drvdata->cache_lock_en = of_property_read_bool(np, + "cache-lock"); } else { drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4; } @@ -494,6 +513,8 @@ static const struct amba_id tmc_ids[] = { CS_AMBA_ID(0x000bb9e9), /* Coresight SoC 600 TMC-ETF */ CS_AMBA_ID(0x000bb9ea), + /* Marvell OcteonTx CN9xxx */ + CS_AMBA_ID_DATA(OCTEONTX_CN9XXX_ETR, (unsigned long)OCTEONTX_CN9XXX_ETR_CAPS), { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 503f1b3a3741..b66c82d271fa 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -12,6 +12,7 @@ #include <linux/miscdevice.h> #include <linux/mutex.h> #include <linux/refcount.h> +#include <linux/arm-smccc.h> #define TMC_RSZ 0x004 #define TMC_STS 0x00c @@ -73,6 +74,9 @@ #define TMC_AXICTL_AXCACHE_OS (0xf << 2) #define TMC_AXICTL_ARCACHE_OS (0xf << 16) +/* TMC_FFSR - 0x300 */ +#define TMC_FFSR_FT_NOT_PRESENT BIT(4) + /* TMC_FFCR - 0x304 */ #define TMC_FFCR_FLUSHMAN_BIT 6 #define TMC_FFCR_EN_FMT BIT(0) @@ -126,6 +130,33 @@ enum tmc_mem_intf_width { #define CORESIGHT_SOC_600_ETR_CAPS \ (TMC_ETR_SAVE_RESTORE | TMC_ETR_AXI_ARCACHE) +/* Marvell OcteonTx CN9xxx TMC-ETR unadvertised capabilities */ +#define OCTEONTX_CN9XXX_ETR_CAPS \ + (TMC_ETR_SAVE_RESTORE) + +/* Marvell OcteonTx CN9xxx device */ +#define OCTEONTX_CN9XXX_ETR 0x000cc213 + +/* Marvell OcteonTx CN9xxx HW issues */ +#define CORESIGHT_OPTS_BUFFSIZE_8BX (0x1U << 0) /* 8 byte size multiplier */ +#define CORESIGHT_OPTS_SECURE_BUFF (0x1U << 1) /* Trace buffer is Secure */ +#define CORESIGHT_OPTS_RESET_CTL_REG (0x1U << 2) /* Reset CTL on reset */ + +/* SMC call ids for managing the secure trace buffer */ + +/* Args: x1 - size, x2 - cpu, x3 - llc lock flag + * Returns: x0 - status, x1 - secure buffer address + */ +#define OCTEONTX_TRC_ALLOC_SBUF 0xc2000c05 +/* Args: x1 - non secure buffer address, x2 - size */ +#define OCTEONTX_TRC_REGISTER_DRVBUF 0xc2000c06 +/* Args: x1 - dst(non secure), x2 - src(secure), x3 - size */ +#define OCTEONTX_TRC_COPY_TO_DRVBUF 0xc2000c07 +/* Args: x1 - secure buffer address, x2 - size */ +#define OCTEONTX_TRC_FREE_SBUF 0xc2000c08 +/* Args: x1 - non secure buffer address, x2 - size */ +#define OCTEONTX_TRC_UNREGISTER_DRVBUF 0xc2000c09 + enum etr_mode { ETR_MODE_FLAT, /* Uses contiguous flat buffer */ ETR_MODE_ETR_SG, /* Uses in-built TMC ETR SG mechanism */ @@ -141,6 +172,7 @@ struct etr_buf_operations; * @full : Trace data overflow * @size : Size of the buffer. * @hwaddr : Address to be programmed in the TMC:DBA{LO,HI} + * @s_paddr : Secure trace buffer * @offset : Offset of the trace data in the buffer for consumption. * @len : Available trace data @buf (may round up to the beginning). * @ops : ETR buffer operations for the mode. @@ -152,6 +184,7 @@ struct etr_buf { bool full; ssize_t size; dma_addr_t hwaddr; + dma_addr_t s_paddr; unsigned long offset; s64 len; const struct etr_buf_operations *ops; @@ -164,13 +197,14 @@ struct etr_buf { * @dev: the device entity associated to this component. * @csdev: component vitals needed by the framework. * @miscdev: specifics to handle "/dev/xyz.tmc" entry. - * @spinlock: only one at a time pls. - * @pid: Process ID of the process being monitored by the session + * @spinlock: only one at a time pls. * @pid: Process ID of the process being monitored by the session * that is using this component. * @buf: Snapshot of the trace data for ETF/ETB. * @etr_buf: details of buffer used in TMC-ETR * @len: size of the available trace for ETF/ETB. * @size: trace buffer size for this TMC (common for all modes). + * @formatter_en: Formatter enable/disable status + * @cache_lock_en: Cache lock status * @mode: how this TMC is being used. * @config_type: TMC variant, must be of type @tmc_config_type. * @memwidth: width of the memory interface databus, in bytes. @@ -181,6 +215,8 @@ struct etr_buf { * @idr_mutex: Access serialisation for idr. * @perf_data: PERF buffer for ETR. * @sysfs_data: SYSFS buffer for ETR. + * @etr_options: Bitmask of options to manage Silicon issues + * @cpu: CPU id this component is associated with */ struct tmc_drvdata { void __iomem *base; @@ -194,6 +230,8 @@ struct tmc_drvdata { char *buf; /* TMC ETB */ struct etr_buf *etr_buf; /* TMC ETR */ }; + bool formatter_en; + bool cache_lock_en; u32 len; u32 size; u32 mode; @@ -205,6 +243,8 @@ struct tmc_drvdata { struct mutex idr_mutex; struct etr_buf *sysfs_buf; void *perf_data; + u32 etr_options; + int cpu; }; struct etr_buf_operations { @@ -322,4 +362,54 @@ tmc_sg_table_buf_size(struct tmc_sg_table *sg_table) struct coresight_device *tmc_etr_get_catu_device(struct tmc_drvdata *drvdata); +static inline int tmc_alloc_secbuf(struct tmc_drvdata *drvdata, + size_t len, dma_addr_t *s_paddr) +{ + struct arm_smccc_res res; + + arm_smccc_smc(OCTEONTX_TRC_ALLOC_SBUF, len, drvdata->cpu, + drvdata->cache_lock_en, 0, 0, 0, 0, &res); + if (res.a0 != SMCCC_RET_SUCCESS) + return -EFAULT; + + *s_paddr = res.a1; + return 0; +} + +static inline int tmc_free_secbuf(struct tmc_drvdata *drvdata, + dma_addr_t s_paddr, size_t len) +{ + struct arm_smccc_res res; + + arm_smccc_smc(OCTEONTX_TRC_FREE_SBUF, s_paddr, len, + 0, 0, 0, 0, 0, &res); + return 0; +} + +static inline int tmc_register_drvbuf(struct tmc_drvdata *drvdata, + dma_addr_t paddr, size_t len) +{ + struct arm_smccc_res res; + + arm_smccc_smc(OCTEONTX_TRC_REGISTER_DRVBUF, paddr, len, + 0, 0, 0, 0, 0, &res); + if (res.a0 != SMCCC_RET_SUCCESS) + return -EFAULT; + + return 0; +} + +static inline int tmc_unregister_drvbuf(struct tmc_drvdata *drvdata, + dma_addr_t paddr, size_t len) +{ + struct arm_smccc_res res; + + arm_smccc_smc(OCTEONTX_TRC_UNREGISTER_DRVBUF, paddr, len, + 0, 0, 0, 0, 0, &res); + return 0; + +} + +int tmc_copy_secure_buffer(struct tmc_drvdata *drvdata, + char *bufp, size_t len); #endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 4b130281236a..1fbf7e125b23 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -524,6 +524,7 @@ static int coresight_enabled_sink(struct device *dev, void *data) /** * coresight_get_enabled_sink - returns the first enabled sink found on the bus + * In case the child port is a single source ETR, returns the child port as sink * @deactivate: Whether the 'enable_sink' flag should be reset * * When operated from perf the deactivate parameter should be set to 'true'. @@ -534,10 +535,30 @@ static int coresight_enabled_sink(struct device *dev, void *data) * parameter should be set to 'false', hence mandating users to explicitly * clear the flag. */ -struct coresight_device *coresight_get_enabled_sink(bool deactivate) +struct coresight_device *coresight_get_enabled_sink(struct coresight_device *s, + bool deactivate) { + struct coresight_device *child; struct device *dev = NULL; + if (s == NULL) + goto skip_single_source; + + /* If the connected port is an ETR with single trace source, + * nothing to search further. + */ + child = s->conns[0].child_dev; + if (s->nr_outport == 1 && + child->type == CORESIGHT_DEV_TYPE_SINK && + child->subtype.sink_subtype == CORESIGHT_DEV_SUBTYPE_SINK_BUFFER && + child->nr_inport == 1 && + child->activated) { + if (deactivate) + child->activated = false; + return child; + } + +skip_single_source: dev = bus_find_device(&coresight_bustype, NULL, &deactivate, coresight_enabled_sink); @@ -782,7 +803,7 @@ int coresight_enable(struct coresight_device *csdev) * Search for a valid sink for this session but don't reset the * "enable_sink" flag in sysFS. Users get to do that explicitly. */ - sink = coresight_get_enabled_sink(false); + sink = coresight_get_enabled_sink(csdev, false); if (!sink) { ret = -EINVAL; goto out; diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c index d9607905dc2f..75e3779bb650 100644 --- a/drivers/i2c/busses/i2c-octeon-core.c +++ b/drivers/i2c/busses/i2c-octeon-core.c @@ -17,6 +17,7 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/pci.h> #include "i2c-octeon-core.h" @@ -658,7 +659,16 @@ out: void octeon_i2c_set_clock(struct octeon_i2c *i2c) { int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; - int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000; + /* starting value on search for lowest diff */ + const int huge_delta = 1000000; + /* + * Find divisors to produce target frequency, start with large delta + * to cover wider range of divisors, note thp = TCLK half period. + */ + int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = huge_delta; + + if (octeon_i2c_is_otx2(to_pci_dev(i2c->dev))) + thp = 0x3; for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { /* @@ -672,17 +682,25 @@ void octeon_i2c_set_clock(struct octeon_i2c *i2c) */ tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; tclk *= (1 << ndiv_idx); - thp_base = (i2c->sys_freq / (tclk * 2)) - 1; + if (octeon_i2c_is_otx2(to_pci_dev(i2c->dev))) + thp_base = (i2c->sys_freq / tclk) - 2; + else + thp_base = (i2c->sys_freq / (tclk * 2)) - 1; for (inc = 0; inc <= 1; inc++) { thp_idx = thp_base + inc; if (thp_idx < 5 || thp_idx > 0xff) continue; - foscl = i2c->sys_freq / (2 * (thp_idx + 1)); + if (octeon_i2c_is_otx2(to_pci_dev(i2c->dev))) + foscl = i2c->sys_freq / (thp_idx + 2); + else + foscl = i2c->sys_freq / + (2 * (thp_idx + 1)); foscl = foscl / (1 << ndiv_idx); foscl = foscl / (mdiv_idx + 1) / 10; diff = abs(foscl - i2c->twsi_freq); + /* Use it if smaller diff from target */ if (diff < delta_hz) { delta_hz = diff; thp = thp_idx; diff --git a/drivers/i2c/busses/i2c-octeon-core.h b/drivers/i2c/busses/i2c-octeon-core.h index 9bb9f64fdda0..15d379bf1d3c 100644 --- a/drivers/i2c/busses/i2c-octeon-core.h +++ b/drivers/i2c/busses/i2c-octeon-core.h @@ -211,6 +211,20 @@ static inline void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) octeon_i2c_writeq_flush(data, i2c->twsi_base + TWSI_INT(i2c)); } +#define PCI_SUBSYS_DEVID_9XXX 0xB +/** + * octeon_i2c_is_otx2 - check for chip ID + * @pdev: PCI dev structure + * + * Returns TRUE if OcteonTX2, FALSE otherwise. + */ +static inline bool octeon_i2c_is_otx2(struct pci_dev *pdev) +{ + u32 chip_id = (pdev->subsystem_device >> 12) & 0xF; + + return (chip_id == PCI_SUBSYS_DEVID_9XXX); +} + /* Prototypes */ irqreturn_t octeon_i2c_isr(int irq, void *dev_id); int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); diff --git a/drivers/i2c/busses/i2c-thunderx-pcidrv.c b/drivers/i2c/busses/i2c-thunderx-pcidrv.c index 19f8eec38717..2449a6dac19b 100644 --- a/drivers/i2c/busses/i2c-thunderx-pcidrv.c +++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c @@ -200,6 +200,12 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev, if (ret) goto error; + /* + * For OcteonTX2 chips, set reference frequency to 100MHz + * as refclk_src in TWSI_MODE register defaults to 100MHz. + */ + if (octeon_i2c_is_otx2(pdev)) + i2c->sys_freq = 100000000; octeon_i2c_set_clock(i2c); i2c->adap = thunderx_i2c_ops; diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index f04c13d52598..be788aee14e7 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -86,6 +86,12 @@ #define IDR5_VAX GENMASK(11, 10) #define IDR5_VAX_52_BIT 1 +#define ARM_SMMU_IIDR 0x18 +#define IIDR_CN96XX_A0 0x2b20034c +#define IIDR_CN96XX_B0 0x2b20134c +#define IIDR_CN95XX_A0 0x2b30034c +#define IIDR_CN95XX_A1 0x2b30134c + #define ARM_SMMU_CR0 0x20 #define CR0_ATSCHK (1 << 4) #define CR0_CMDQEN (1 << 3) @@ -558,6 +564,7 @@ struct arm_smmu_device { #define ARM_SMMU_OPT_SKIP_PREFETCH (1 << 0) #define ARM_SMMU_OPT_PAGE0_REGS_ONLY (1 << 1) +#define ARM_SMMU_OPT_FORCE_QDRAIN (1 << 2) u32 options; struct arm_smmu_cmdq cmdq; @@ -945,6 +952,12 @@ static void arm_smmu_cmdq_insert_cmd(struct arm_smmu_device *smmu, u64 *cmd) smmu->prev_cmd_opcode = FIELD_GET(CMDQ_0_OP, cmd[0]); + if (smmu->options & ARM_SMMU_OPT_FORCE_QDRAIN) { + /* Ensure command queue has atmost two entries */ + if (!(q->prod & 0x1) && queue_poll_cons(q, true, false)) + dev_err(smmu->dev, "command drain timeout\n"); + } + while (queue_insert_raw(q, cmd) == -ENOSPC) { if (queue_poll_cons(q, false, wfe)) dev_err_ratelimited(smmu->dev, "CMDQ timeout\n"); @@ -2970,6 +2983,25 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu) dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n", smmu->ias, smmu->oas, smmu->features); + + /* Options based on implementation */ + reg = readl_relaxed(smmu->base + ARM_SMMU_IIDR); + + /* Marvell Octeontx2 SMMU wrongly issues unsupported + * 64 byte memory reads under certain conditions for + * reading commands from the command queue. + * Force command queue drain for every two writes, + * so that SMMU issues only 32 byte reads. + */ + switch (reg) { + case IIDR_CN96XX_A0: + case IIDR_CN96XX_B0: + case IIDR_CN95XX_A0: + case IIDR_CN95XX_A1: + smmu->options |= ARM_SMMU_OPT_FORCE_QDRAIN; + break; + } + return 0; } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 85fc77148d19..1dd9b939bcc1 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -493,6 +493,14 @@ config PVPANIC a paravirtualized device provided by QEMU; it lets a virtual machine (guest) communicate panic events to the host. +config MARVELL_OTX_BPHY_CTR + bool "Marvell OcteonTX BPHY Control driver" + ---help--- + Enables BPHY control driver which handles ioctl calls + to set/clear IRQ handlers in EL3 using SMC calls. + The purpose of this is to handle some BPHY Interrupts in + user space directly without kernel's intervention. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b9affcdaa3d6..71cbbd1e480f 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -59,3 +59,4 @@ obj-$(CONFIG_OCXL) += ocxl/ obj-y += cardreader/ obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_HABANA_AI) += habanalabs/ +obj-$(CONFIG_MARVELL_OTX_BPHY_CTR) += otx_bphy_ctr.o diff --git a/drivers/misc/otx_bphy_ctr.c b/drivers/misc/otx_bphy_ctr.c new file mode 100644 index 000000000000..0bdb1616b7c8 --- /dev/null +++ b/drivers/misc/otx_bphy_ctr.c @@ -0,0 +1,281 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2016, 2018 Cavium Inc. + */ +#include <linux/init.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/arm-smccc.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/moduleparam.h> +#include <linux/uaccess.h> +#include <linux/mmu_context.h> +#include <linux/ioctl.h> +#include <linux/fs.h> + +#define DEVICE_NAME "otx-bphy-ctr" +#define OTX_IOC_MAGIC 0xF3 +#define MAX_IRQ 27 + +static struct device *otx_device; +static struct class *otx_class; +static struct cdev *otx_cdev; +static dev_t otx_dev; +static DEFINE_SPINLOCK(el3_inthandler_lock); +static int in_use; +static int irq_installed[MAX_IRQ]; +static struct thread_info *irq_installed_threads[MAX_IRQ]; +static struct task_struct *irq_installed_tasks[MAX_IRQ]; + +/* SMC definitons */ +/* X1 - irq_num, X2 - sp, X3 - cpu, X4 - ttbr0 */ +#define OCTEONTX_INSTALL_BPHY_PSM_ERRINT 0xc2000803 +/* X1 - irq_num */ +#define OCTEONTX_REMOVE_BPHY_PSM_ERRINT 0xc2000804 + +struct otx_irq_usr_data { + u64 isr_base; + u64 sp; + u64 cpu; + u64 irq_num; +}; + + +#define OTX_IOC_SET_BPHY_HANDLER \ + _IOW(OTX_IOC_MAGIC, 1, struct otx_irq_usr_data) + +#define OTX_IOC_CLR_BPHY_HANDLER \ + _IO(OTX_IOC_MAGIC, 2) + +static inline int __install_el3_inthandler(unsigned long irq_num, + unsigned long sp, + unsigned long cpu, + unsigned long ttbr0) +{ + struct arm_smccc_res res; + unsigned long flags; + int retval = -1; + + spin_lock_irqsave(&el3_inthandler_lock, flags); + + if (!irq_installed[irq_num]) { + lock_context(current->group_leader->mm, irq_num); + arm_smccc_smc(OCTEONTX_INSTALL_BPHY_PSM_ERRINT, irq_num, + sp, cpu, ttbr0, 0, 0, 0, &res); + if (res.a0 == 0) { + irq_installed[irq_num] = 1; + irq_installed_threads[irq_num] + = current_thread_info(); + irq_installed_tasks[irq_num] + = current->group_leader; + retval = 0; + } else { + unlock_context_by_index(irq_num); + } + } + spin_unlock_irqrestore(&el3_inthandler_lock, flags); + return retval; +} + +static inline int __remove_el3_inthandler(unsigned long irq_num) +{ + struct arm_smccc_res res; + unsigned long flags; + unsigned int retval; + + spin_lock_irqsave(&el3_inthandler_lock, flags); + + if (irq_installed[irq_num]) { + arm_smccc_smc(OCTEONTX_REMOVE_BPHY_PSM_ERRINT, irq_num, + 0, 0, 0, 0, 0, 0, &res); + irq_installed[irq_num] = 0; + irq_installed_threads[irq_num] = NULL; + irq_installed_tasks[irq_num] = NULL; + unlock_context_by_index(irq_num); + retval = 0; + } else { + retval = -1; + } + spin_unlock_irqrestore(&el3_inthandler_lock, flags); + return retval; +} + +static long otx_dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct otx_irq_usr_data irq_usr; + u64 irq_ttbr, irq_isr_base, irq_sp, irq_cpu, irq_num; + int ret; + //struct task_struct *task = current; + + if (!in_use) + return -EINVAL; + + if (_IOC_TYPE(cmd) != OTX_IOC_MAGIC) + return -ENOTTY; + + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_TYPE(cmd) & _IOC_WRITE) + err = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); + + if (err) + return -EFAULT; + + switch (cmd) { + case OTX_IOC_SET_BPHY_HANDLER: /*Install ISR handler*/ + ret = copy_from_user(&irq_usr, (void *)arg, _IOC_SIZE(cmd)); + if (irq_usr.irq_num >= MAX_IRQ) + return -EINVAL; + if (ret) + return -EFAULT; + irq_ttbr = 0; + //TODO: reserve a asid to avoid asid rollovers + asm volatile("mrs %0, ttbr0_el1\n\t" : "=r"(irq_ttbr)); + irq_isr_base = irq_usr.isr_base; + irq_sp = irq_usr.sp; + irq_cpu = irq_usr.cpu; + irq_num = irq_usr.irq_num; + ret = __install_el3_inthandler(irq_num, irq_sp, + irq_cpu, irq_isr_base); + if (ret != 0) + return -EEXIST; + break; + case OTX_IOC_CLR_BPHY_HANDLER: /*Clear ISR handler*/ + irq_usr.irq_num = arg; + if (irq_usr.irq_num >= MAX_IRQ) + return -EINVAL; + ret = __remove_el3_inthandler(irq_usr.irq_num); + if (ret != 0) + return -ENOENT; + break; + default: + return -ENOTTY; + } + return 0; +} + +static void cleanup_el3_irqs(struct task_struct *task) +{ + int i; + + for (i = 0; i < MAX_IRQ; i++) { + if (irq_installed[i] && + irq_installed_tasks[i] && + (irq_installed_tasks[i] == task)) { + pr_alert("Exiting, removing handler for BPHY IRQ %d\n", + i); + __remove_el3_inthandler(i); + pr_alert("Exited, removed handler for BPHY IRQ %d\n", + i); + } else { + if (irq_installed[i] && + (irq_installed_threads[i] + == current_thread_info())) + pr_alert("Exiting, thread info matches, not removing handler for BPHY IRQ %d\n", i); + } + } +} + +static int otx_dev_open(struct inode *inode, struct file *fp) +{ + in_use = 1; + return 0; +} + +static int otx_dev_release(struct inode *inode, struct file *fp) +{ + if (in_use == 0) + return -EINVAL; + in_use = 0; + return 0; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = otx_dev_open, + .release = otx_dev_release, + .unlocked_ioctl = otx_dev_ioctl +}; + +static int __init otx_ctr_dev_init(void) +{ + int err = 0; + + /* create a character device */ + err = alloc_chrdev_region(&otx_dev, 1, 1, DEVICE_NAME); + if (err != 0) { + pr_err("Failed to create device: %d\n", err); + goto alloc_chrdev_err; + } + + otx_cdev = cdev_alloc(); + if (!otx_cdev) { + err = -ENODEV; + goto cdev_alloc_err; + } + + cdev_init(otx_cdev, &fops); + err = cdev_add(otx_cdev, otx_dev, 1); + if (err < 0) { + err = -ENODEV; + goto cdev_add_err; + } + + /* create new class for sysfs*/ + otx_class = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(otx_class)) { + err = -ENODEV; + goto class_create_err; + } + + otx_device = device_create(otx_class, NULL, otx_dev, NULL, + DEVICE_NAME); + if (IS_ERR(otx_device)) { + err = -ENODEV; + goto device_create_err; + } + + /* Register task cleanup handler */ + err = task_cleanup_handler_add(cleanup_el3_irqs); + if (err != 0) { + dev_err(otx_device, "Failed to register cleanup handler: %d\n", err); + goto cleanup_handler_err; + } + + return err; + +device_create_err: + class_destroy(otx_class); + +class_create_err: +cdev_add_err: + cdev_del(otx_cdev); +cdev_alloc_err: + unregister_chrdev_region(otx_dev, 1); +alloc_chrdev_err: + task_cleanup_handler_remove(cleanup_el3_irqs); +cleanup_handler_err: + return err; +} + +static void __exit otx_ctr_dev_exit(void) +{ + device_destroy(otx_class, otx_dev); + class_destroy(otx_class); + cdev_del(otx_cdev); + unregister_chrdev_region(otx_dev, 1); + + task_cleanup_handler_remove(cleanup_el3_irqs); +} + +module_init(otx_ctr_dev_init); +module_exit(otx_ctr_dev_exit); + +MODULE_DESCRIPTION("Marvell OTX Control Device Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/cavium-octeon.c b/drivers/mmc/host/cavium-octeon.c index 22aded1065ae..302c052b8d93 100644 --- a/drivers/mmc/host/cavium-octeon.c +++ b/drivers/mmc/host/cavium-octeon.c @@ -247,8 +247,8 @@ static int octeon_mmc_probe(struct platform_device *pdev) /* Only CMD_DONE, DMA_DONE, CMD_ERR, DMA_ERR */ for (i = 1; i <= 4; i++) { ret = devm_request_irq(&pdev->dev, mmc_irq[i], - cvm_mmc_interrupt, - 0, cvm_mmc_irq_names[i], host); + cvm_mmc_interrupt, IRQF_NO_THREAD, + cvm_mmc_irq_names[i], host); if (ret < 0) { dev_err(&pdev->dev, "Error: devm_request_irq %d\n", mmc_irq[i]); @@ -257,8 +257,8 @@ static int octeon_mmc_probe(struct platform_device *pdev) } } else { ret = devm_request_irq(&pdev->dev, mmc_irq[0], - cvm_mmc_interrupt, 0, KBUILD_MODNAME, - host); + cvm_mmc_interrupt, IRQF_NO_THREAD, + KBUILD_MODNAME, host); if (ret < 0) { dev_err(&pdev->dev, "Error: devm_request_irq %d\n", mmc_irq[0]); @@ -277,7 +277,7 @@ static int octeon_mmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); i = 0; - for_each_child_of_node(node, cn) { + for_each_available_child_of_node(node, cn) { host->slot_pdev[i] = of_platform_device_create(cn, NULL, &pdev->dev); if (!host->slot_pdev[i]) { diff --git a/drivers/mmc/host/cavium-thunderx.c b/drivers/mmc/host/cavium-thunderx.c index eee08d81b242..7fbf33e34217 100644 --- a/drivers/mmc/host/cavium-thunderx.c +++ b/drivers/mmc/host/cavium-thunderx.c @@ -15,6 +15,8 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/pci.h> +#include <linux/delay.h> +#include <linux/bitfield.h> #include "cavium.h" static void thunder_mmc_acquire_bus(struct cvm_mmc_host *host) @@ -31,6 +33,8 @@ static void thunder_mmc_int_enable(struct cvm_mmc_host *host, u64 val) { writeq(val, host->base + MIO_EMM_INT(host)); writeq(val, host->base + MIO_EMM_INT_EN_SET(host)); + writeq(MIO_EMM_DMA_INT_DMA, + host->dma_base + MIO_EMM_DMA_INT(host)); } static int thunder_mmc_register_interrupts(struct cvm_mmc_host *host, @@ -45,14 +49,125 @@ static int thunder_mmc_register_interrupts(struct cvm_mmc_host *host, /* register interrupts */ for (i = 0; i < nvec; i++) { ret = devm_request_irq(&pdev->dev, pci_irq_vector(pdev, i), - cvm_mmc_interrupt, - 0, cvm_mmc_irq_names[i], host); + cvm_mmc_interrupt, IRQF_NO_THREAD, + cvm_mmc_irq_names[i], host); if (ret) return ret; } return 0; } +/* calibration evaluates the per tap delay */ +static void thunder_calibrate_mmc(struct cvm_mmc_host *host) +{ + u32 retries = 10; + u32 delay = 4; + unsigned int ps; + const char *how = "default"; + + if (is_mmc_8xxx(host)) + return; + + /* set _DEBUG[CLK_ON]=1 as workaround for clock issue */ + if (is_mmc_otx2_A0(host) || is_mmc_95xx(host)) + writeq(1, host->base + MIO_EMM_DEBUG(host)); + + if (is_mmc_otx2_A0(host)) { + /* + * Operation of up to 100 MHz may be achieved by skipping the + * steps that establish the tap delays and instead assuming + * that MIO_EMM_TAP[DELAY] returns 0x4 indicating 78 pS/tap. + */ + } else { + u64 tap; + u64 emm_cfg = readq(host->base + MIO_EMM_CFG(host)); + u64 tcfg; + u64 emm_io_ctl; + u64 emm_switch; + u64 emm_wdog; + u64 emm_sts_mask; + u64 emm_debug; + u64 emm_timing; + u64 emm_rca; + + /* + * MIO_EMM_CFG[BUS_ENA] must be zero for calibration, + * but that resets whole host, so save state. + */ + emm_io_ctl = readq(host->base + MIO_EMM_IO_CTL(host)); + emm_switch = readq(host->base + MIO_EMM_SWITCH(host)); + emm_wdog = readq(host->base + MIO_EMM_WDOG(host)); + emm_sts_mask = + readq(host->base + MIO_EMM_STS_MASK(host)); + emm_debug = readq(host->base + MIO_EMM_DEBUG(host)); + emm_timing = readq(host->base + MIO_EMM_TIMING(host)); + emm_rca = readq(host->base + MIO_EMM_RCA(host)); + + /* reset controller */ + tcfg = emm_cfg; + tcfg &= ~MIO_EMM_CFG_BUS_ENA; + writeq(tcfg, host->base + MIO_EMM_CFG(host)); + udelay(1); + + /* restart with phantom slot 3 */ + tcfg |= FIELD_PREP(MIO_EMM_CFG_BUS_ENA, 1ull << 3); + writeq(tcfg, host->base + MIO_EMM_CFG(host)); + mdelay(1); + + /* Start calibration */ + writeq(0, host->base + MIO_EMM_CALB(host)); + udelay(5); + writeq(START_CALIBRATION, host->base + MIO_EMM_CALB(host)); + udelay(5); + + do { + /* wait for approximately 300 coprocessor clock */ + udelay(5); + tap = readq(host->base + MIO_EMM_TAP(host)); + } while (!tap && retries--); + + /* leave calibration mode */ + writeq(0, host->base + MIO_EMM_CALB(host)); + udelay(5); + + if (retries <= 0 || !tap) { + how = "fallback"; + } else { + /* calculate the per-tap delay */ + delay = tap & MIO_EMM_TAP_DELAY; + how = "calibrated"; + } + + /* restore old state */ + writeq(emm_cfg, host->base + MIO_EMM_CFG(host)); + mdelay(1); + writeq(emm_rca, host->base + MIO_EMM_RCA(host)); + writeq(emm_timing, host->base + MIO_EMM_TIMING(host)); + writeq(emm_debug, host->base + MIO_EMM_DEBUG(host)); + writeq(emm_sts_mask, + host->base + MIO_EMM_STS_MASK(host)); + writeq(emm_wdog, host->base + MIO_EMM_WDOG(host)); + writeq(emm_switch, host->base + MIO_EMM_SWITCH(host)); + writeq(emm_io_ctl, host->base + MIO_EMM_IO_CTL(host)); + mdelay(1); + + } + + /* + * Scale measured/guessed calibration value to pS: + * The delay value should be multiplied by 10 ns(or 10000 ps) + * and then divided by no of taps to determine the estimated + * delay in pico second. The nominal value is 125 ps per tap. + */ + ps = (delay * PS_10000) / TOTAL_NO_OF_TAPS; + if (host->per_tap_delay != ps) { + dev_info(host->dev, "%s delay:%d per_tap_delay:%dpS\n", + how, delay, ps); + host->per_tap_delay = ps; + host->delay_logged = 0; + } +} + static int thunder_mmc_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -81,6 +196,7 @@ static int thunder_mmc_probe(struct pci_dev *pdev, /* On ThunderX these are identical */ host->dma_base = host->base; + host->pdev = pdev; host->reg_off = 0x2000; host->reg_off_dma = 0x160; @@ -107,24 +223,32 @@ static int thunder_mmc_probe(struct pci_dev *pdev, host->need_irq_handler_lock = true; host->last_slot = -1; - ret = dma_set_mask(dev, DMA_BIT_MASK(48)); if (ret) goto error; /* * Clear out any pending interrupts that may be left over from * bootloader. Writing 1 to the bits clears them. + * Clear DMA FIFO after IRQ disable, then stub any dangling events */ - writeq(127, host->base + MIO_EMM_INT_EN(host)); - writeq(3, host->base + MIO_EMM_DMA_INT_ENA_W1C(host)); - /* Clear DMA FIFO */ - writeq(BIT_ULL(16), host->base + MIO_EMM_DMA_FIFO_CFG(host)); + writeq(~0, host->base + MIO_EMM_INT(host)); + writeq(~0, host->dma_base + MIO_EMM_DMA_INT_ENA_W1C(host)); + writeq(~0, host->base + MIO_EMM_INT_EN_CLR(host)); + writeq(MIO_EMM_DMA_FIFO_CFG_CLR, + host->dma_base + MIO_EMM_DMA_FIFO_CFG(host)); + writeq(~0, host->dma_base + MIO_EMM_DMA_INT(host)); ret = thunder_mmc_register_interrupts(host, pdev); if (ret) goto error; - for_each_child_of_node(node, child_node) { + /* Run the calibration to calculate per tap delay that would be + * used to evaluate values. These values would be programmed in + * MIO_EMM_TIMING. + */ + thunder_calibrate_mmc(host); + + for_each_available_child_of_node(node, child_node) { /* * mmc_of_parse and devm* require one device per slot. * Create a dummy device per slot and set the node pointer to @@ -137,12 +261,15 @@ static int thunder_mmc_probe(struct pci_dev *pdev, if (!host->slot_pdev[i]) continue; + dev_info(dev, "Probing slot %d\n", i); + ret = cvm_mmc_of_slot_probe(&host->slot_pdev[i]->dev, host); if (ret) goto error; } i++; } + dev_info(dev, "probed\n"); return 0; @@ -171,8 +298,11 @@ static void thunder_mmc_remove(struct pci_dev *pdev) cvm_mmc_of_slot_remove(host->slot[i]); dma_cfg = readq(host->dma_base + MIO_EMM_DMA_CFG(host)); - dma_cfg &= ~MIO_EMM_DMA_CFG_EN; + dma_cfg |= MIO_EMM_DMA_CFG_CLR; writeq(dma_cfg, host->dma_base + MIO_EMM_DMA_CFG(host)); + do { + dma_cfg = readq(host->dma_base + MIO_EMM_DMA_CFG(host)); + } while (dma_cfg & MIO_EMM_DMA_CFG_EN); clk_disable_unprepare(host->clk); } diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c index 89deb451e0ac..5005efd113ee 100644 --- a/drivers/mmc/host/cavium.c +++ b/drivers/mmc/host/cavium.c @@ -25,6 +25,8 @@ #include <linux/regulator/consumer.h> #include <linux/scatterlist.h> #include <linux/time.h> +#include <linux/iommu.h> +#include <linux/swiotlb.h> #include "cavium.h" @@ -38,6 +40,8 @@ const char *cvm_mmc_irq_names[] = { "MMC Switch Error", "MMC DMA int Fifo", "MMC DMA int", + "MMC NCB Fault", + "MMC RAS", }; /* @@ -71,7 +75,7 @@ static struct cvm_mmc_cr_type cvm_mmc_cr_types[] = { {0, 1}, /* CMD16 */ {1, 1}, /* CMD17 */ {1, 1}, /* CMD18 */ - {3, 1}, /* CMD19 */ + {2, 1}, /* CMD19 */ {2, 1}, /* CMD20 */ {0, 0}, /* CMD21 */ {0, 0}, /* CMD22 */ @@ -118,6 +122,156 @@ static struct cvm_mmc_cr_type cvm_mmc_cr_types[] = { {0, 0} /* CMD63 */ }; +static int tapdance; +module_param(tapdance, int, 0644); +MODULE_PARM_DESC(tapdance, "adjust bus-timing: (0=mid-eye, positive=Nth_fastest_tap)"); + +static int clk_scale = 100; +module_param(clk_scale, int, 0644); +MODULE_PARM_DESC(clk_scale, "percent scale data_/cmd_out taps (default 100)"); + +static bool fixed_timing; +module_param(fixed_timing, bool, 0444); +MODULE_PARM_DESC(fixed_timing, "use fixed data_/cmd_out taps"); + +static bool ddr_cmd_taps; +module_param(ddr_cmd_taps, bool, 0644); +MODULE_PARM_DESC(ddr_cmd_taps, "reduce cmd_out_taps in DDR modes, as before"); + +static bool cvm_is_mmc_timing_ddr(struct cvm_mmc_slot *slot) +{ + if ((slot->mmc->ios.timing == MMC_TIMING_UHS_DDR50) || + (slot->mmc->ios.timing == MMC_TIMING_MMC_DDR52) || + (slot->mmc->ios.timing == MMC_TIMING_MMC_HS400)) + return true; + else + return false; +} + +static void cvm_mmc_set_timing(struct cvm_mmc_slot *slot) +{ + if (!is_mmc_otx2(slot->host)) + return; + + writeq(slot->taps, slot->host->base + MIO_EMM_TIMING(slot->host)); +} + +static int tout(struct cvm_mmc_slot *slot, int ps, int hint) +{ + struct cvm_mmc_host *host = slot->host; + struct mmc_host *mmc = slot->mmc; + int tap_ps = host->per_tap_delay; + int timing = mmc->ios.timing; + static int old_scale; + int taps; + + if (fixed_timing) + return hint; + + if (!hint) + hint = 63; + + if (!tap_ps) + return hint; + + taps = min((int)(ps * clk_scale) / (tap_ps * 100), 63); + + /* when modparam is adjusted, re-announce timing */ + if (old_scale != clk_scale) { + host->delay_logged = 0; + old_scale = clk_scale; + } + + if (!test_and_set_bit(timing, + &host->delay_logged)) + dev_info(host->dev, "mmc%d.ios_timing:%d %dpS hint:%d taps:%d\n", + mmc->index, timing, ps, hint, taps); + + return taps; +} + +static int cvm_mmc_configure_delay(struct cvm_mmc_slot *slot) +{ + struct cvm_mmc_host *host = slot->host; + struct mmc_host *mmc = slot->mmc; + + pr_debug("slot%d.configure_delay\n", slot->bus_id); + + if (is_mmc_8xxx(host)) { + /* MIO_EMM_SAMPLE is till T83XX */ + u64 emm_sample = + FIELD_PREP(MIO_EMM_SAMPLE_CMD_CNT, slot->cmd_cnt) | + FIELD_PREP(MIO_EMM_SAMPLE_DAT_CNT, slot->data_cnt); + writeq(emm_sample, host->base + MIO_EMM_SAMPLE(host)); + } else { + int half = MAX_NO_OF_TAPS / 2; + int cin = FIELD_GET(MIO_EMM_TIMING_CMD_IN, slot->taps); + int din = FIELD_GET(MIO_EMM_TIMING_DATA_IN, slot->taps); + int cout, dout; + + if (!slot->taps) + cin = din = half; + /* + * EMM_CMD hold time from rising edge of EMMC_CLK. + * Typically 3.0 ns at frequencies < 26 MHz. + * Typically 3.0 ns at frequencies <= 52 MHz SDR. + * Typically 2.5 ns at frequencies <= 52 MHz DDR. + * Typically 0.8 ns at frequencies > 52 MHz SDR. + * Typically 0.4 ns at frequencies > 52 MHz DDR. + */ + switch (mmc->ios.timing) { + case MMC_TIMING_LEGACY: + default: + if (mmc->card && mmc_card_mmc(mmc->card)) + cout = tout(slot, 5000, 39); + else + cout = tout(slot, 8000, 63); + break; + case MMC_TIMING_UHS_SDR12: + cout = tout(slot, 3000, 39); + break; + case MMC_TIMING_MMC_HS: + cout = tout(slot, 2500, 32); + break; + case MMC_TIMING_SD_HS: + case MMC_TIMING_UHS_SDR25: + case MMC_TIMING_UHS_SDR50: + cout = tout(slot, 2000, 26); + break; + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + cout = tout(slot, 1500, 20); + break; + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: + case MMC_TIMING_MMC_HS400: + cout = tout(slot, 800, 10); + break; + } + + if (!is_mmc_95xx(host)) { + if (!cvm_is_mmc_timing_ddr(slot)) + dout = cout; + else if (ddr_cmd_taps) + cout = dout = cout / 2; + else + dout = cout / 2; + } else + dout = tout(slot, 800, 10); + + slot->taps = + FIELD_PREP(MIO_EMM_TIMING_CMD_IN, cin) | + FIELD_PREP(MIO_EMM_TIMING_CMD_OUT, cout) | + FIELD_PREP(MIO_EMM_TIMING_DATA_IN, din) | + FIELD_PREP(MIO_EMM_TIMING_DATA_OUT, dout); + + pr_debug("slot%d.taps %llx\n", slot->bus_id, slot->taps); + cvm_mmc_set_timing(slot); + } + + return 0; +} + static struct cvm_mmc_cr_mods cvm_mmc_get_cr_mods(struct mmc_command *cmd) { struct cvm_mmc_cr_type *cr; @@ -175,14 +329,14 @@ static void check_switch_errors(struct cvm_mmc_host *host) dev_err(host->dev, "Switch bus width error\n"); } -static void clear_bus_id(u64 *reg) +static inline void clear_bus_id(u64 *reg) { u64 bus_id_mask = GENMASK_ULL(61, 60); *reg &= ~bus_id_mask; } -static void set_bus_id(u64 *reg, int bus_id) +static inline void set_bus_id(u64 *reg, int bus_id) { clear_bus_id(reg); *reg |= FIELD_PREP(GENMASK(61, 60), bus_id); @@ -193,25 +347,69 @@ static int get_bus_id(u64 reg) return FIELD_GET(GENMASK_ULL(61, 60), reg); } -/* - * We never set the switch_exe bit since that would interfere - * with the commands send by the MMC core. - */ -static void do_switch(struct cvm_mmc_host *host, u64 emm_switch) +/* save old slot details, switch power */ +static bool pre_switch(struct cvm_mmc_host *host, u64 emm_switch) { - int retries = 100; - u64 rsp_sts; - int bus_id; + int bus_id = get_bus_id(emm_switch); + struct cvm_mmc_slot *slot = host->slot[bus_id]; + struct cvm_mmc_slot *old_slot; + bool same_vqmmc = false; - /* - * Modes setting only taken from slot 0. Work around that hardware - * issue by first switching to slot 0. + if (host->last_slot == bus_id) + return false; + + /* when VQMMC is switched, tri-state CMDn over any slot change + * to avoid transient states on D0-7 or CLK from level-shifters */ - bus_id = get_bus_id(emm_switch); - clear_bus_id(&emm_switch); - writeq(emm_switch, host->base + MIO_EMM_SWITCH(host)); + if (host->use_vqmmc) { + writeq(1ull << 3, host->base + MIO_EMM_CFG(host)); + udelay(10); + } + + if (host->last_slot >= 0 && host->slot[host->last_slot]) { + old_slot = host->slot[host->last_slot]; + old_slot->cached_switch = + readq(host->base + MIO_EMM_SWITCH(host)); + old_slot->cached_rca = readq(host->base + MIO_EMM_RCA(host)); + + same_vqmmc = (slot->mmc->supply.vqmmc == + old_slot->mmc->supply.vqmmc); + if (!same_vqmmc && !IS_ERR_OR_NULL(old_slot->mmc->supply.vqmmc)) + regulator_disable(old_slot->mmc->supply.vqmmc); + } + + if (!same_vqmmc && !IS_ERR_OR_NULL(slot->mmc->supply.vqmmc)) { + int e = regulator_enable(slot->mmc->supply.vqmmc); + + if (e) + dev_err(host->dev, "mmc-slot@%d.vqmmc err %d\n", + bus_id, e); + } + + host->last_slot = slot->bus_id; + + return true; +} + +static void post_switch(struct cvm_mmc_host *host, u64 emm_switch) +{ + int bus_id = get_bus_id(emm_switch); + struct cvm_mmc_slot *slot = host->slot[bus_id]; + + if (host->use_vqmmc) { + /* enable new CMDn */ + writeq(1ull << bus_id, host->base + MIO_EMM_CFG(host)); + udelay(10); + } + + writeq(slot->cached_rca, host->base + MIO_EMM_RCA(host)); +} + +static inline void mode_switch(struct cvm_mmc_host *host, u64 emm_switch) +{ + u64 rsp_sts; + int retries = 100; - set_bus_id(&emm_switch, bus_id); writeq(emm_switch, host->base + MIO_EMM_SWITCH(host)); /* wait for the switch to finish */ @@ -221,15 +419,49 @@ static void do_switch(struct cvm_mmc_host *host, u64 emm_switch) break; udelay(10); } while (--retries); +} + +/* + * We never set the switch_exe bit since that would interfere + * with the commands send by the MMC core. + */ +static void do_switch(struct cvm_mmc_host *host, u64 emm_switch) +{ + int bus_id = get_bus_id(emm_switch); + struct cvm_mmc_slot *slot = host->slot[bus_id]; + bool slot_changed = pre_switch(host, emm_switch); + + /* + * Modes setting only taken from slot 0. Work around that hardware + * issue by first switching to slot 0. + */ + if (bus_id) { + u64 switch0 = emm_switch; + + clear_bus_id(&switch0); + mode_switch(host, switch0); + } + + mode_switch(host, emm_switch); check_switch_errors(host); + if (slot_changed) + post_switch(host, emm_switch); + slot->cached_switch = emm_switch; + if (emm_switch & MIO_EMM_SWITCH_CLK) + slot->cmd6_pending = false; } +/* need to change hardware state to match software requirements? */ static bool switch_val_changed(struct cvm_mmc_slot *slot, u64 new_val) { /* Match BUS_ID, HS_TIMING, BUS_WIDTH, POWER_CLASS, CLK_HI, CLK_LO */ - u64 match = 0x3001070fffffffffull; + /* For 9xxx add HS200_TIMING and HS400_TIMING */ + u64 match = (is_mmc_otx2(slot->host)) ? + 0x3007070fffffffffull : 0x3001070fffffffffull; + if (!slot->host->powered) + return true; return (slot->cached_switch & match) != (new_val & match); } @@ -247,58 +479,62 @@ static void set_wdog(struct cvm_mmc_slot *slot, unsigned int ns) writeq(timeout, slot->host->base + MIO_EMM_WDOG(slot->host)); } +static void emmc_io_drive_setup(struct cvm_mmc_slot *slot) +{ + u64 ioctl_cfg; + struct cvm_mmc_host *host = slot->host; + + /* Setup drive and slew only for 9x */ + if (is_mmc_otx2(host)) { + if ((slot->drive < 0) || (slot->slew < 0)) + return; + /* Setup the emmc interface current drive + * strength & clk slew rate. + */ + ioctl_cfg = FIELD_PREP(MIO_EMM_IO_CTL_DRIVE, slot->drive) | + FIELD_PREP(MIO_EMM_IO_CTL_SLEW, slot->slew); + writeq(ioctl_cfg, host->base + MIO_EMM_IO_CTL(host)); + } +} + static void cvm_mmc_reset_bus(struct cvm_mmc_slot *slot) { struct cvm_mmc_host *host = slot->host; u64 emm_switch, wdog; - emm_switch = readq(slot->host->base + MIO_EMM_SWITCH(host)); - emm_switch &= ~(MIO_EMM_SWITCH_EXE | MIO_EMM_SWITCH_ERR0 | - MIO_EMM_SWITCH_ERR1 | MIO_EMM_SWITCH_ERR2); + emm_switch = readq(host->base + MIO_EMM_SWITCH(host)); + emm_switch &= ~(MIO_EMM_SWITCH_EXE | MIO_EMM_SWITCH_ERRS); set_bus_id(&emm_switch, slot->bus_id); - wdog = readq(slot->host->base + MIO_EMM_WDOG(host)); - do_switch(slot->host, emm_switch); - - slot->cached_switch = emm_switch; + wdog = readq(host->base + MIO_EMM_WDOG(host)); + do_switch(host, emm_switch); + host->powered = true; msleep(20); - writeq(wdog, slot->host->base + MIO_EMM_WDOG(host)); + writeq(wdog, host->base + MIO_EMM_WDOG(host)); } /* Switch to another slot if needed */ static void cvm_mmc_switch_to(struct cvm_mmc_slot *slot) { struct cvm_mmc_host *host = slot->host; - struct cvm_mmc_slot *old_slot; - u64 emm_sample, emm_switch; if (slot->bus_id == host->last_slot) return; - if (host->last_slot >= 0 && host->slot[host->last_slot]) { - old_slot = host->slot[host->last_slot]; - old_slot->cached_switch = readq(host->base + MIO_EMM_SWITCH(host)); - old_slot->cached_rca = readq(host->base + MIO_EMM_RCA(host)); - } - - writeq(slot->cached_rca, host->base + MIO_EMM_RCA(host)); - emm_switch = slot->cached_switch; - set_bus_id(&emm_switch, slot->bus_id); - do_switch(host, emm_switch); - - emm_sample = FIELD_PREP(MIO_EMM_SAMPLE_CMD_CNT, slot->cmd_cnt) | - FIELD_PREP(MIO_EMM_SAMPLE_DAT_CNT, slot->dat_cnt); - writeq(emm_sample, host->base + MIO_EMM_SAMPLE(host)); + do_switch(host, slot->cached_switch); + host->powered = true; - host->last_slot = slot->bus_id; + emmc_io_drive_setup(slot); + cvm_mmc_configure_delay(slot); } -static void do_read(struct cvm_mmc_host *host, struct mmc_request *req, +static void do_read(struct cvm_mmc_slot *slot, struct mmc_request *req, u64 dbuf) { - struct sg_mapping_iter *smi = &host->smi; + struct cvm_mmc_host *host = slot->host; + struct sg_mapping_iter *smi = &slot->smi; int data_len = req->data->blocks * req->data->blksz; int bytes_xfered, shift = -1; u64 dat = 0; @@ -365,7 +601,7 @@ static void set_cmd_response(struct cvm_mmc_host *host, struct mmc_request *req, } } -static int get_dma_dir(struct mmc_data *data) +static inline int get_dma_dir(struct mmc_data *data) { return (data->flags & MMC_DATA_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; } @@ -374,6 +610,9 @@ static int finish_dma_single(struct cvm_mmc_host *host, struct mmc_data *data) { data->bytes_xfered = data->blocks * data->blksz; data->error = 0; + + writeq(MIO_EMM_DMA_FIFO_CFG_CLR, + host->dma_base + MIO_EMM_DMA_FIFO_CFG(host)); dma_unmap_sg(host->dev, data->sg, data->sg_len, get_dma_dir(data)); return 1; } @@ -382,6 +621,7 @@ static int finish_dma_sg(struct cvm_mmc_host *host, struct mmc_data *data) { u64 fifo_cfg; int count; + void __iomem *dma_intp = host->dma_base + MIO_EMM_DMA_INT(host); /* Check if there are any pending requests left */ fifo_cfg = readq(host->dma_base + MIO_EMM_DMA_FIFO_CFG(host)); @@ -392,8 +632,16 @@ static int finish_dma_sg(struct cvm_mmc_host *host, struct mmc_data *data) data->bytes_xfered = data->blocks * data->blksz; data->error = 0; - /* Clear and disable FIFO */ - writeq(BIT_ULL(16), host->dma_base + MIO_EMM_DMA_FIFO_CFG(host)); + writeq(MIO_EMM_DMA_FIFO_CFG_CLR, + host->dma_base + MIO_EMM_DMA_FIFO_CFG(host)); + + /* on read, wait for internal buffer to flush out to mem */ + if (get_dma_dir(data) == DMA_FROM_DEVICE) { + while (!(readq(dma_intp) & MIO_EMM_DMA_INT_DMA)) + udelay(10); + writeq(MIO_EMM_DMA_INT_DMA, dma_intp); + } + dma_unmap_sg(host->dev, data->sg, data->sg_len, get_dma_dir(data)); return 1; } @@ -415,7 +663,8 @@ static int check_status(u64 rsp_sts) if (rsp_sts & MIO_EMM_RSP_STS_RSP_TIMEOUT || rsp_sts & MIO_EMM_RSP_STS_BLK_TIMEOUT) return -ETIMEDOUT; - if (rsp_sts & MIO_EMM_RSP_STS_DBUF_ERR) + if (rsp_sts & MIO_EMM_RSP_STS_DBUF_ERR || + rsp_sts & MIO_EMM_RSP_STS_BLK_CRC_ERR) return -EIO; return 0; } @@ -435,16 +684,24 @@ static void cleanup_dma(struct cvm_mmc_host *host, u64 rsp_sts) irqreturn_t cvm_mmc_interrupt(int irq, void *dev_id) { struct cvm_mmc_host *host = dev_id; - struct mmc_request *req; + struct mmc_request *req = NULL; + struct cvm_mmc_slot *slot = NULL; unsigned long flags = 0; u64 emm_int, rsp_sts; bool host_done; + int bus_id; if (host->need_irq_handler_lock) spin_lock_irqsave(&host->irq_handler_lock, flags); else __acquire(&host->irq_handler_lock); + rsp_sts = readq(host->base + MIO_EMM_RSP_STS(host)); + bus_id = get_bus_id(rsp_sts); + slot = host->slot[bus_id]; + if (slot) + req = slot->current_req; + /* Clear interrupt bits (write 1 clears ). */ emm_int = readq(host->base + MIO_EMM_INT(host)); writeq(emm_int, host->base + MIO_EMM_INT(host)); @@ -452,25 +709,32 @@ irqreturn_t cvm_mmc_interrupt(int irq, void *dev_id) if (emm_int & MIO_EMM_INT_SWITCH_ERR) check_switch_errors(host); - req = host->current_req; if (!req) goto out; - rsp_sts = readq(host->base + MIO_EMM_RSP_STS(host)); + /* + * dma_pend means DMA has stalled with CRC errs. + * start teardown, get irq on completion, mmc stack retries. + */ + if ((rsp_sts & MIO_EMM_RSP_STS_DMA_PEND) && slot->dma_active) { + cleanup_dma(host, rsp_sts); + goto out; + } + /* * dma_val set means DMA is still in progress. Don't touch * the request and wait for the interrupt indicating that * the DMA is finished. */ - if ((rsp_sts & MIO_EMM_RSP_STS_DMA_VAL) && host->dma_active) + if ((rsp_sts & MIO_EMM_RSP_STS_DMA_VAL) && slot->dma_active) goto out; - if (!host->dma_active && req->data && + if (!slot->dma_active && req->data && (emm_int & MIO_EMM_INT_BUF_DONE)) { unsigned int type = (rsp_sts >> 7) & 3; if (type == 1) - do_read(host, req, rsp_sts & MIO_EMM_RSP_STS_DBUF); + do_read(slot, req, rsp_sts & MIO_EMM_RSP_STS_DBUF); else if (type == 2) do_write(req); } @@ -480,12 +744,16 @@ irqreturn_t cvm_mmc_interrupt(int irq, void *dev_id) emm_int & MIO_EMM_INT_CMD_ERR || emm_int & MIO_EMM_INT_DMA_ERR; + /* Add NCB_FLT interrupt for octtx2 */ + if (is_mmc_otx2(host)) + host_done = host_done || emm_int & MIO_EMM_INT_NCB_FLT; + if (!(host_done && req->done)) goto no_req_done; req->cmd->error = check_status(rsp_sts); - if (host->dma_active && req->data) + if (slot->dma_active && req->data) if (!finish_dma(host, req->data)) goto no_req_done; @@ -494,7 +762,18 @@ irqreturn_t cvm_mmc_interrupt(int irq, void *dev_id) (rsp_sts & MIO_EMM_RSP_STS_DMA_PEND)) cleanup_dma(host, rsp_sts); - host->current_req = NULL; + /* follow CMD6 timing/width with IMMEDIATE switch */ + if (slot && slot->cmd6_pending) { + if (host_done && !req->cmd->error) { + do_switch(host, slot->want_switch); + emmc_io_drive_setup(slot); + cvm_mmc_configure_delay(slot); + } else if (slot) { + slot->cmd6_pending = false; + } + } + + slot->current_req = NULL; req->done(req); no_req_done: @@ -609,9 +888,9 @@ static u64 prepare_dma_sg(struct cvm_mmc_host *host, struct mmc_data *data) error: WARN_ON_ONCE(1); + writeq(MIO_EMM_DMA_FIFO_CFG_CLR, + host->dma_base + MIO_EMM_DMA_FIFO_CFG(host)); dma_unmap_sg(host->dev, data->sg, data->sg_len, get_dma_dir(data)); - /* Disable FIFO */ - writeq(BIT_ULL(16), host->dma_base + MIO_EMM_DMA_FIFO_CFG(host)); return 0; } @@ -653,7 +932,11 @@ static void cvm_mmc_dma_request(struct mmc_host *mmc, struct cvm_mmc_slot *slot = mmc_priv(mmc); struct cvm_mmc_host *host = slot->host; struct mmc_data *data; - u64 emm_dma, addr; + u64 emm_dma, addr, int_enable_mask = 0; + int seg; + + /* cleared by successful termination */ + mrq->cmd->error = -EINVAL; if (!mrq->data || !mrq->data->sg || !mrq->data->sg_len || !mrq->stop || mrq->stop->opcode != MMC_STOP_TRANSMISSION) { @@ -662,17 +945,27 @@ static void cvm_mmc_dma_request(struct mmc_host *mmc, goto error; } + /* unaligned multi-block DMA has problems, so forbid all unaligned */ + for (seg = 0; seg < mrq->data->sg_len; seg++) { + struct scatterlist *sg = &mrq->data->sg[seg]; + u64 align = (sg->offset | sg->length); + + if (!(align & 7)) + continue; + dev_info(&mmc->card->dev, + "Error:64bit alignment required\n"); + goto error; + } + cvm_mmc_switch_to(slot); data = mrq->data; + pr_debug("DMA request blocks: %d block_size: %d total_size: %d\n", data->blocks, data->blksz, data->blocks * data->blksz); if (data->timeout_ns) set_wdog(slot, data->timeout_ns); - WARN_ON(host->current_req); - host->current_req = mrq; - emm_dma = prepare_ext_dma(mmc, mrq); addr = prepare_dma(host, data); if (!addr) { @@ -680,9 +973,19 @@ static void cvm_mmc_dma_request(struct mmc_host *mmc, goto error; } - host->dma_active = true; - host->int_enable(host, MIO_EMM_INT_CMD_ERR | MIO_EMM_INT_DMA_DONE | - MIO_EMM_INT_DMA_ERR); + mrq->host = mmc; + WARN_ON(slot->current_req); + slot->current_req = mrq; + slot->dma_active = true; + + int_enable_mask = MIO_EMM_INT_CMD_ERR | MIO_EMM_INT_DMA_DONE | + MIO_EMM_INT_DMA_ERR; + + /* Add NCB_FLT interrupt for octtx2 */ + if (is_mmc_otx2(host)) + int_enable_mask |= MIO_EMM_INT_NCB_FLT; + + host->int_enable(host, int_enable_mask); if (host->dmar_fixup) host->dmar_fixup(host, mrq->cmd, data, addr); @@ -700,22 +1003,22 @@ static void cvm_mmc_dma_request(struct mmc_host *mmc, return; error: - mrq->cmd->error = -EINVAL; if (mrq->done) mrq->done(mrq); host->release_bus(host); } -static void do_read_request(struct cvm_mmc_host *host, struct mmc_request *mrq) +static void do_read_request(struct cvm_mmc_slot *slot, struct mmc_request *mrq) { - sg_miter_start(&host->smi, mrq->data->sg, mrq->data->sg_len, + sg_miter_start(&slot->smi, mrq->data->sg, mrq->data->sg_len, SG_MITER_ATOMIC | SG_MITER_TO_SG); } -static void do_write_request(struct cvm_mmc_host *host, struct mmc_request *mrq) +static void do_write_request(struct cvm_mmc_slot *slot, struct mmc_request *mrq) { + struct cvm_mmc_host *host = slot->host; unsigned int data_len = mrq->data->blocks * mrq->data->blksz; - struct sg_mapping_iter *smi = &host->smi; + struct sg_mapping_iter *smi = &slot->smi; unsigned int bytes_xfered; int shift = 56; u64 dat = 0; @@ -749,6 +1052,51 @@ static void do_write_request(struct cvm_mmc_host *host, struct mmc_request *mrq) sg_miter_stop(smi); } +static void cvm_mmc_track_switch(struct cvm_mmc_slot *slot, u32 cmd_arg) +{ + u8 how = (cmd_arg >> 24) & 3; + u8 where = (u8)(cmd_arg >> 16); + u8 val = (u8)(cmd_arg >> 8); + + slot->want_switch = slot->cached_switch; + + /* + * track ext_csd assignments (how==3) for critical entries + * to make sure we follow up with MIO_EMM_SWITCH adjustment + * before ANY mmc/core interaction at old settings. + * Current mmc/core logic (linux 4.14) does not set/clear + * bits (how = 1 or 2), which would require more complex + * logic to track the intent of a change + */ + + if (how != 3) + return; + + switch (where) { + case EXT_CSD_BUS_WIDTH: + slot->want_switch &= ~MIO_EMM_SWITCH_BUS_WIDTH; + slot->want_switch |= + FIELD_PREP(MIO_EMM_SWITCH_BUS_WIDTH, val); + break; + case EXT_CSD_POWER_CLASS: + slot->want_switch &= ~MIO_EMM_SWITCH_POWER_CLASS; + slot->want_switch |= + FIELD_PREP(MIO_EMM_SWITCH_POWER_CLASS, val); + break; + case EXT_CSD_HS_TIMING: + slot->want_switch &= ~MIO_EMM_SWITCH_TIMING; + if (val) + slot->want_switch |= + FIELD_PREP(MIO_EMM_SWITCH_TIMING, + (1 << (val - 1))); + break; + default: + return; + } + + slot->cmd6_pending = true; +} + static void cvm_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct cvm_mmc_slot *slot = mmc_priv(mmc); @@ -777,23 +1125,27 @@ static void cvm_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) mods = cvm_mmc_get_cr_mods(cmd); - WARN_ON(host->current_req); - host->current_req = mrq; + WARN_ON(slot->current_req); + mrq->host = mmc; + slot->current_req = mrq; if (cmd->data) { if (cmd->data->flags & MMC_DATA_READ) - do_read_request(host, mrq); + do_read_request(slot, mrq); else - do_write_request(host, mrq); + do_write_request(slot, mrq); if (cmd->data->timeout_ns) set_wdog(slot, cmd->data->timeout_ns); } else set_wdog(slot, 0); - host->dma_active = false; + slot->dma_active = false; host->int_enable(host, MIO_EMM_INT_CMD_DONE | MIO_EMM_INT_CMD_ERR); + if (cmd->opcode == MMC_SWITCH) + cvm_mmc_track_switch(slot, cmd->arg); + emm_cmd = FIELD_PREP(MIO_EMM_CMD_VAL, 1) | FIELD_PREP(MIO_EMM_CMD_CTYPE_XOR, mods.ctype_xor) | FIELD_PREP(MIO_EMM_CMD_RTYPE_XOR, mods.rtype_xor) | @@ -819,37 +1171,257 @@ retry: if (!retries) dev_err(host->dev, "Bad status: %llx before command write\n", rsp_sts); writeq(emm_cmd, host->base + MIO_EMM_CMD(host)); + if (cmd->opcode == MMC_SWITCH) + udelay(1300); +} + +static void cvm_mmc_wait_done(struct mmc_request *cvm_mrq) +{ + complete(&cvm_mrq->completion); +} + +static int cvm_mmc_r1_cmd(struct mmc_host *mmc, u32 *statp, u32 opcode) +{ + static struct mmc_command cmd = {}; + static struct mmc_request cvm_mrq = {}; + + if (!opcode) + opcode = MMC_SEND_STATUS; + cmd.opcode = opcode; + if (mmc->card) + cmd.arg = mmc->card->rca << 16; + else + cmd.arg = 1 << 16; + cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; + cmd.data = NULL; + cvm_mrq.cmd = &cmd; + + init_completion(&cvm_mrq.completion); + cvm_mrq.done = cvm_mmc_wait_done; + + cvm_mmc_request(mmc, &cvm_mrq); + if (!wait_for_completion_timeout(&cvm_mrq.completion, + msecs_to_jiffies(10))) { + mmc_abort_tuning(mmc, opcode); + return -ETIMEDOUT; + } + + if (statp) + *statp = cmd.resp[0]; + + return cvm_mrq.cmd->error; +} + +static int cvm_mmc_data_tuning(struct mmc_host *mmc, u32 *statp, u32 opcode) +{ + int err = 0; + u8 *ext_csd; + static struct mmc_command cmd = {}; + static struct mmc_data data = {}; + static struct mmc_request cvm_mrq = {}; + static struct scatterlist sg; + struct cvm_mmc_slot *slot = mmc_priv(mmc); + struct mmc_card *card = mmc->card; + + if (!(slot->cached_switch & MIO_EMM_SWITCH_HS400_TIMING)) { + int edetail = -EINVAL; + int core_opinion; + + core_opinion = + mmc_send_tuning(mmc, opcode, &edetail); + + /* only accept mmc/core opinion when it's happy */ + if (!core_opinion) + return core_opinion; + } + + /* EXT_CSD supported only after ver 3 */ + if (card && card->csd.mmca_vsn <= CSD_SPEC_VER_3) + return -EOPNOTSUPP; + /* + * As the ext_csd is so large and mostly unused, we don't store the + * raw block in mmc_card. + */ + ext_csd = kzalloc(BLKSZ_EXT_CSD, GFP_KERNEL); + if (!ext_csd) + return -ENOMEM; + + cvm_mrq.cmd = &cmd; + cvm_mrq.data = &data; + cmd.data = &data; + + cmd.opcode = MMC_SEND_EXT_CSD; + cmd.arg = 0; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = BLKSZ_EXT_CSD; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, ext_csd, BLKSZ_EXT_CSD); + + /* set timeout */ + if (card) { + /* SD cards use a 100 multiplier rather than 10 */ + u32 mult = mmc_card_sd(card) ? 100 : 10; + + data.timeout_ns = card->csd.taac_ns * mult; + data.timeout_clks = card->csd.taac_clks * mult; + } else { + data.timeout_ns = 50 * NSEC_PER_MSEC; + } + + init_completion(&cvm_mrq.completion); + cvm_mrq.done = cvm_mmc_wait_done; + + cvm_mmc_request(mmc, &cvm_mrq); + if (!wait_for_completion_timeout(&cvm_mrq.completion, + msecs_to_jiffies(100))) { + mmc_abort_tuning(mmc, cmd.opcode); + err = -ETIMEDOUT; + } + + data.sg_len = 0; /* FIXME: catch over-time completions? */ + kfree(ext_csd); + + if (err) + return err; + + if (statp) + *statp = cvm_mrq.cmd->resp[0]; + + return cvm_mrq.cmd->error; +} + +/* adjusters for the 4 otx2 delay line taps */ +struct adj { + const char *name; + u64 mask; + int (*test)(struct mmc_host *mmc, u32 *statp, u32 opcode); + u32 opcode; + bool ddr_only; +}; + +static int adjust_tuning(struct mmc_host *mmc, struct adj *adj, u32 opcode) +{ + int err, start_run = -1, best_run = 0, best_start = -1; + int last_good = -1; + bool prev_ok = false; + u64 timing, tap; + struct cvm_mmc_slot *slot = mmc_priv(mmc); + struct cvm_mmc_host *host = slot->host; + char how[MAX_NO_OF_TAPS+1] = ""; + + /* loop over range+1 to simplify processing */ + for (tap = 0; tap <= MAX_NO_OF_TAPS; tap++, prev_ok = !err) { + if (tap < MAX_NO_OF_TAPS) { + timing = readq(host->base + MIO_EMM_TIMING(host)); + timing &= ~adj->mask; + timing |= (tap << __bf_shf(adj->mask)); + writeq(timing, host->base + MIO_EMM_TIMING(host)); + + err = adj->test(mmc, NULL, opcode); + + how[tap] = "-+"[!err]; + if (!err) + last_good = tap; + } else { + /* + * putting the end+1 case in loop simplifies + * logic, allowing 'prev_ok' to process a + * sweet spot in tuning which extends to wall. + */ + err = -EINVAL; + } + + if (!err) { + /* + * If no CRC/etc errors in response, but previous + * failed, note the start of a new run + */ + if (!prev_ok) + start_run = tap; + } else if (prev_ok) { + int run = tap - 1 - start_run; + + /* did we just exit a wider sweet spot? */ + if (start_run >= 0 && run > best_run) { + best_start = start_run; + best_run = run; + } + } + } + + if (best_start < 0) { + dev_warn(host->dev, "%s %lldMHz tuning %s failed\n", + mmc_hostname(mmc), slot->clock / 1000000, adj->name); + return -EINVAL; + } + + tap = best_start + best_run / 2; + how[tap] = '@'; + if (tapdance) { + tap = last_good - tapdance; + how[tap] = 'X'; + } + dev_dbg(host->dev, "%s/%s %d/%lld/%d %s\n", + mmc_hostname(mmc), adj->name, + best_start, tap, best_start + best_run, + how); + slot->taps &= ~adj->mask; + slot->taps |= (tap << __bf_shf(adj->mask)); + cvm_mmc_set_timing(slot); + return 0; +} + +static u32 max_supported_frequency(struct cvm_mmc_host *host) +{ + /* Default maximum freqeuncey is 52000000 for chip prior to 9X */ + u32 max_frequency = MHZ_52; + + if (is_mmc_otx2(host)) { + /* Default max frequency is 200MHz for 9X chips */ + max_frequency = MHZ_200; + + /* Erratum is only applicable pass A0 */ + if (is_mmc_otx2_A0(host)) + max_frequency = MHZ_100; + } + return max_frequency; } static void cvm_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { + struct cvm_mmc_slot *slot = mmc_priv(mmc); struct cvm_mmc_host *host = slot->host; int clk_period = 0, power_class = 10, bus_width = 0; - u64 clock, emm_switch; + u64 clock, emm_switch, mode; + u32 max_f; + + if (ios->power_mode == MMC_POWER_OFF) { + if (host->powered) { + cvm_mmc_reset_bus(slot); + if (host->global_pwr_gpiod) + host->set_shared_power(host, 0); + else if (!IS_ERR_OR_NULL(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + host->powered = false; + } + set_wdog(slot, 0); + return; + } host->acquire_bus(host); cvm_mmc_switch_to(slot); - /* Set the power state */ - switch (ios->power_mode) { - case MMC_POWER_ON: - break; - - case MMC_POWER_OFF: - cvm_mmc_reset_bus(slot); - if (host->global_pwr_gpiod) - host->set_shared_power(host, 0); - else if (!IS_ERR(mmc->supply.vmmc)) - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); - break; - - case MMC_POWER_UP: + if (ios->power_mode == MMC_POWER_UP) { if (host->global_pwr_gpiod) host->set_shared_power(host, 1); - else if (!IS_ERR(mmc->supply.vmmc)) + else if (!IS_ERR_OR_NULL(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); - break; } /* Convert bus width to HW definition */ @@ -866,41 +1438,201 @@ static void cvm_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } /* DDR is available for 4/8 bit bus width */ - if (ios->bus_width && ios->timing == MMC_TIMING_MMC_DDR52) - bus_width |= 4; + switch (ios->timing) { + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + if (ios->bus_width) + bus_width |= 4; + break; + case MMC_TIMING_MMC_HS400: + if (ios->bus_width & 2) + bus_width |= 4; + break; + } /* Change the clock frequency. */ clock = ios->clock; - if (clock > 52000000) - clock = 52000000; + max_f = max_supported_frequency(host); + + if (clock < mmc->f_min) + clock = mmc->f_min; + if (clock > max_f) + clock = max_f; + slot->clock = clock; - if (clock) - clk_period = (host->sys_freq + clock - 1) / (2 * clock); + if (clock) { + clk_period = host->sys_freq / (2 * clock); + /* check to not exceed requested speed */ + while (1) { + int hz = host->sys_freq / (2 * clk_period); - emm_switch = FIELD_PREP(MIO_EMM_SWITCH_HS_TIMING, - (ios->timing == MMC_TIMING_MMC_HS)) | + if (hz <= clock) + break; + clk_period++; + } + } + + emm_switch = FIELD_PREP(MIO_EMM_SWITCH_BUS_WIDTH, bus_width) | FIELD_PREP(MIO_EMM_SWITCH_POWER_CLASS, power_class) | FIELD_PREP(MIO_EMM_SWITCH_CLK_HI, clk_period) | FIELD_PREP(MIO_EMM_SWITCH_CLK_LO, clk_period); + switch (ios->timing) { + case MMC_TIMING_LEGACY: + break; + case MMC_TIMING_MMC_HS: + case MMC_TIMING_SD_HS: + case MMC_TIMING_UHS_SDR12: + case MMC_TIMING_UHS_SDR25: + case MMC_TIMING_UHS_SDR50: + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + emm_switch |= FIELD_PREP(MIO_EMM_SWITCH_HS_TIMING, 1); + break; + case MMC_TIMING_MMC_HS200: + emm_switch |= FIELD_PREP(MIO_EMM_SWITCH_HS200_TIMING, 1); + break; + case MMC_TIMING_MMC_HS400: + emm_switch |= FIELD_PREP(MIO_EMM_SWITCH_HS400_TIMING, 1); + break; + } set_bus_id(&emm_switch, slot->bus_id); + pr_debug("mmc-slot%d trying switch %llx w%lld hs%lld hs200:%lld hs400:%lld\n", + slot->bus_id, emm_switch, + FIELD_GET(MIO_EMM_SWITCH_BUS_WIDTH, emm_switch), + FIELD_GET(MIO_EMM_SWITCH_HS_TIMING, emm_switch), + FIELD_GET(MIO_EMM_SWITCH_HS200_TIMING, emm_switch), + FIELD_GET(MIO_EMM_SWITCH_HS400_TIMING, emm_switch)); + if (!switch_val_changed(slot, emm_switch)) goto out; set_wdog(slot, 0); do_switch(host, emm_switch); + + mode = readq(host->base + MIO_EMM_MODE(host, slot->bus_id)); + pr_debug("mmc-slot%d mode %llx w%lld hs%lld hs200:%lld hs400:%lld\n", + slot->bus_id, mode, + (mode >> 40) & 7, (mode >> 48) & 1, + (mode >> 49) & 1, (mode >> 50) & 1); + slot->cached_switch = emm_switch; + host->powered = true; + cvm_mmc_configure_delay(slot); out: host->release_bus(host); } +static struct adj adj[] = { + { "CMD_IN", MIO_EMM_TIMING_CMD_IN, + cvm_mmc_r1_cmd, MMC_SEND_STATUS, }, + { "DATA_IN", MIO_EMM_TIMING_DATA_IN, + cvm_mmc_data_tuning, }, + { NULL, }, +}; + +static int cvm_scan_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct cvm_mmc_slot *slot = mmc_priv(mmc); + struct adj *a; + int ret; + + for (a = adj; a->name; a++) { + if (a->ddr_only && !cvm_is_mmc_timing_ddr(slot)) + continue; + + ret = adjust_tuning(mmc, a, + a->opcode ?: opcode); + + if (ret) + return ret; + } + + cvm_mmc_set_timing(slot); + return 0; +} + +static int cvm_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct cvm_mmc_slot *slot = mmc_priv(mmc); + struct cvm_mmc_host *host = slot->host; + int clk_period, hz; + + int ret; + + do { + u64 emm_switch = + readq(host->base + MIO_EMM_MODE(host, slot->bus_id)); + + clk_period = FIELD_GET(MIO_EMM_SWITCH_CLK_LO, emm_switch); + dev_info(slot->host->dev, "%s re-tuning\n", + mmc_hostname(mmc)); + ret = cvm_scan_tuning(mmc, opcode); + if (ret) { + int inc = clk_period >> 3; + + if (!inc) + inc++; + clk_period += inc; + hz = host->sys_freq / (2 * clk_period); + pr_debug("clk_period %d += %d, now %d Hz\n", + clk_period - inc, inc, hz); + + if (hz < 400000) + break; + + slot->clock = hz; + mmc->ios.clock = hz; + + emm_switch &= ~MIO_EMM_SWITCH_CLK_LO; + emm_switch |= FIELD_PREP(MIO_EMM_SWITCH_CLK_LO, + clk_period); + emm_switch &= ~MIO_EMM_SWITCH_CLK_HI; + emm_switch |= FIELD_PREP(MIO_EMM_SWITCH_CLK_HI, + clk_period); + do_switch(host, emm_switch); + } + } while (ret); + + return ret; +} + +static int cvm_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct cvm_mmc_slot *slot = mmc_priv(mmc); + + return cvm_mmc_configure_delay(slot); +} + +static void cvm_mmc_reset(struct mmc_host *mmc) +{ + struct cvm_mmc_slot *slot = mmc_priv(mmc); + struct cvm_mmc_host *host = slot->host; + u64 r; + + cvm_mmc_reset_bus(slot); + + r = FIELD_PREP(MIO_EMM_CMD_VAL, 1) | + FIELD_PREP(MIO_EMM_CMD_BUS_ID, slot->bus_id); + + writeq(r, host->base + MIO_EMM_CMD(host)); + + do { + r = readq(host->base + MIO_EMM_RSP_STS(host)); + } while (!(r & MIO_EMM_RSP_STS_CMD_DONE)); +} + static const struct mmc_host_ops cvm_mmc_ops = { .request = cvm_mmc_request, .set_ios = cvm_mmc_set_ios, .get_ro = mmc_gpio_get_ro, .get_cd = mmc_gpio_get_cd, + .hw_reset = cvm_mmc_reset, + .execute_tuning = cvm_execute_tuning, + .prepare_hs400_tuning = cvm_prepare_hs400_tuning, }; static void cvm_mmc_set_clock(struct cvm_mmc_slot *slot, unsigned int clock) @@ -917,7 +1649,7 @@ static int cvm_mmc_init_lowlevel(struct cvm_mmc_slot *slot) struct cvm_mmc_host *host = slot->host; u64 emm_switch; - /* Enable this bus slot. */ + /* Enable this bus slot. Overridden when vqmmc-switching engaged */ host->emm_cfg |= (1ull << slot->bus_id); writeq(host->emm_cfg, slot->host->base + MIO_EMM_CFG(host)); udelay(10); @@ -933,8 +1665,8 @@ static int cvm_mmc_init_lowlevel(struct cvm_mmc_slot *slot) /* Make the changes take effect on this bus slot. */ set_bus_id(&emm_switch, slot->bus_id); do_switch(host, emm_switch); - slot->cached_switch = emm_switch; + host->powered = true; /* * Set watchdog timeout value and default reset value @@ -953,7 +1685,7 @@ static int cvm_mmc_of_parse(struct device *dev, struct cvm_mmc_slot *slot) u32 id, cmd_skew = 0, dat_skew = 0, bus_width = 0; struct device_node *node = dev->of_node; struct mmc_host *mmc = slot->mmc; - u64 clock_period; + u32 max_frequency, current_drive, clk_slew; int ret; ret = of_property_read_u32(node, "reg", &id); @@ -962,8 +1694,14 @@ static int cvm_mmc_of_parse(struct device *dev, struct cvm_mmc_slot *slot) return ret; } - if (id >= CAVIUM_MAX_MMC || slot->host->slot[id]) { - dev_err(dev, "Invalid reg property on %pOF\n", node); + if (id >= CAVIUM_MAX_MMC) { + dev_err(dev, "Invalid reg=<%d> property on %pOF\n", id, node); + return -EINVAL; + } + + if (slot->host->slot[id]) { + dev_err(dev, "Duplicate reg=<%d> property on %pOF\n", + id, node); return -EINVAL; } @@ -974,7 +1712,7 @@ static int cvm_mmc_of_parse(struct device *dev, struct cvm_mmc_slot *slot) * Legacy Octeon firmware has no regulator entry, fall-back to * a hard-coded voltage to get a sane OCR. */ - if (IS_ERR(mmc->supply.vmmc)) + if (IS_ERR_OR_NULL(mmc->supply.vmmc)) mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; /* Common MMC bindings */ @@ -982,7 +1720,7 @@ static int cvm_mmc_of_parse(struct device *dev, struct cvm_mmc_slot *slot) if (ret) return ret; - /* Set bus width */ + /* Set bus width from obsolete properties, if unset */ if (!(mmc->caps & (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA))) { of_property_read_u32(node, "cavium,bus-max-width", &bus_width); if (bus_width == 8) @@ -991,19 +1729,40 @@ static int cvm_mmc_of_parse(struct device *dev, struct cvm_mmc_slot *slot) mmc->caps |= MMC_CAP_4_BIT_DATA; } + max_frequency = max_supported_frequency(slot->host); + /* Set maximum and minimum frequency */ if (!mmc->f_max) of_property_read_u32(node, "spi-max-frequency", &mmc->f_max); - if (!mmc->f_max || mmc->f_max > 52000000) - mmc->f_max = 52000000; - mmc->f_min = 400000; + if (!mmc->f_max || mmc->f_max > max_frequency) + mmc->f_max = max_frequency; + mmc->f_min = KHZ_400; /* Sampling register settings, period in picoseconds */ - clock_period = 1000000000000ull / slot->host->sys_freq; of_property_read_u32(node, "cavium,cmd-clk-skew", &cmd_skew); of_property_read_u32(node, "cavium,dat-clk-skew", &dat_skew); - slot->cmd_cnt = (cmd_skew + clock_period / 2) / clock_period; - slot->dat_cnt = (dat_skew + clock_period / 2) / clock_period; + if (is_mmc_8xxx(slot->host) || is_mmc_otx2(slot->host)) { + slot->cmd_cnt = cmd_skew; + slot->data_cnt = dat_skew; + } else { + u64 clock_period = 1000000000000ull / slot->host->sys_freq; + + slot->cmd_cnt = (cmd_skew + clock_period / 2) / clock_period; + slot->data_cnt = (dat_skew + clock_period / 2) / clock_period; + } + + /* Get current drive and clk skew */ + ret = of_property_read_u32(node, "cavium,drv-strength", ¤t_drive); + if (ret) + slot->drive = -1; + else + slot->drive = current_drive; + + ret = of_property_read_u32(node, "cavium,clk-slew", &clk_slew); + if (ret) + slot->slew = -1; + else + slot->slew = clk_slew; return id; } @@ -1012,6 +1771,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) { struct cvm_mmc_slot *slot; struct mmc_host *mmc; + struct iommu_domain *dom; int ret, id; mmc = mmc_alloc_host(sizeof(struct cvm_mmc_slot), dev); @@ -1030,16 +1790,19 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) /* Set up host parameters */ mmc->ops = &cvm_mmc_ops; + mmc->caps |= MMC_CAP_ERASE | MMC_CAP_BUS_WIDTH_TEST; + mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD; + /* - * We only have a 3.3v supply, we cannot support any - * of the UHS modes. We do support the high speed DDR - * modes up to 52MHz. + * We only have a 3.3v supply for slots, we cannot + * support any of the UHS modes. We do support the + * high speed DDR modes up to 52MHz. * * Disable bounce buffers for max_segs = 1 */ - mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD | - MMC_CAP_3_3V_DDR; + + if (!is_mmc_otx2(host)) + mmc->caps |= MMC_CAP_3_3V_DDR; if (host->use_sg) mmc->max_segs = 16; @@ -1055,14 +1818,30 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) /* DMA block count field is 15 bits */ mmc->max_blk_count = 32767; + dom = iommu_get_domain_for_dev(dev->parent); + if (dom && dom->type == IOMMU_DOMAIN_IDENTITY) { + unsigned int max_size = (1 << IO_TLB_SHIFT) * IO_TLB_SEGSIZE; + + if (mmc->max_seg_size > max_size) + mmc->max_seg_size = max_size; + + max_size *= mmc->max_segs; + + if (mmc->max_req_size > max_size) + mmc->max_req_size = max_size; + } + + mmc_can_retune(mmc); + slot->clock = mmc->f_min; slot->bus_id = id; slot->cached_rca = 1; host->acquire_bus(host); host->slot[id] = slot; - cvm_mmc_switch_to(slot); + host->use_vqmmc |= !IS_ERR_OR_NULL(slot->mmc->supply.vqmmc); cvm_mmc_init_lowlevel(slot); + cvm_mmc_switch_to(slot); host->release_bus(host); ret = mmc_add_host(mmc); diff --git a/drivers/mmc/host/cavium.h b/drivers/mmc/host/cavium.h index f3eea5eaa678..f38353171678 100644 --- a/drivers/mmc/host/cavium.h +++ b/drivers/mmc/host/cavium.h @@ -19,8 +19,42 @@ #include <linux/of.h> #include <linux/scatterlist.h> #include <linux/semaphore.h> +#include <linux/pci.h> #define CAVIUM_MAX_MMC 4 +#define BLKSZ_EXT_CSD 512 +#define MRVL_OCTEONTX2_96XX_PARTNUM 0xB2 + +/* Subsystem Device ID */ +#define PCI_SUBSYS_DEVID_8XXX 0xA +#define PCI_SUBSYS_DEVID_9XXX 0xB +#define PCI_SUBSYS_DEVID_95XX 0xB3 + +#define KHZ_400 (400000) +#define MHZ_26 (26000000) +#define MHZ_52 (52000000) +#define MHZ_100 (100000000) +#define MHZ_200 (200000000) + +/* octtx2: emmc interface io current drive strength */ +#define MILLI_AMP_2 (0x0) +#define MILLI_AMP_4 (0x1) +#define MILLI_AMP_8 (0x2) +#define MILLI_AMP_16 (0x3) + +/* octtx2: emmc interface io clk skew */ +#define LOW_SLEW_RATE (0x0) +#define HIGH_SLEW_RATE (0x1) + +/* octtx2: emmc interface calibration */ +#define START_CALIBRATION (0x1) +#define TOTAL_NO_OF_TAPS (512) +#define PS_10000 (10 * 1000) +#define PS_5000 (5000) +#define PS_2500 (2500) +#define PS_400 (400) +#define MAX_NO_OF_TAPS 64 + /* DMA register addresses */ #define MIO_EMM_DMA_FIFO_CFG(x) (0x00 + x->reg_off_dma) @@ -33,8 +67,17 @@ #define MIO_EMM_DMA_INT_ENA_W1S(x) (0x40 + x->reg_off_dma) #define MIO_EMM_DMA_INT_ENA_W1C(x) (0x48 + x->reg_off_dma) +/* octtx2 specific registers */ +#define MIO_EMM_CALB(x) (0xC0 + x->reg_off) +#define MIO_EMM_TAP(x) (0xC8 + x->reg_off) +#define MIO_EMM_TIMING(x) (0xD0 + x->reg_off) +#define MIO_EMM_DEBUG(x) (0xF8 + x->reg_off) + /* register addresses */ #define MIO_EMM_CFG(x) (0x00 + x->reg_off) +#define MIO_EMM_MODE(x, s) (0x08 + 8*(s) + (x)->reg_off) +/* octtx2 specific register */ +#define MIO_EMM_IO_CTL(x) (0x40 + x->reg_off) #define MIO_EMM_SWITCH(x) (0x48 + x->reg_off) #define MIO_EMM_DMA(x) (0x50 + x->reg_off) #define MIO_EMM_CMD(x) (0x58 + x->reg_off) @@ -56,6 +99,7 @@ struct cvm_mmc_host { struct device *dev; void __iomem *base; void __iomem *dma_base; + struct pci_dev *pdev; int reg_off; int reg_off_dma; u64 emm_cfg; @@ -64,12 +108,10 @@ struct cvm_mmc_host { struct clk *clk; int sys_freq; - struct mmc_request *current_req; - struct sg_mapping_iter smi; - bool dma_active; bool use_sg; - bool has_ciu3; + bool powered; + bool use_vqmmc; /* must disable slots over switch */ bool big_dma_addr; bool need_irq_handler_lock; spinlock_t irq_handler_lock; @@ -80,6 +122,9 @@ struct cvm_mmc_host { struct cvm_mmc_slot *slot[CAVIUM_MAX_MMC]; struct platform_device *slot_pdev[CAVIUM_MAX_MMC]; + /* octtx2 specific */ + unsigned int per_tap_delay; /* per tap delay in pico second */ + unsigned long delay_logged; /* per-ios.timing bitmask */ void (*set_shared_power)(struct cvm_mmc_host *, int); void (*acquire_bus)(struct cvm_mmc_host *); @@ -94,16 +139,27 @@ struct cvm_mmc_host { struct cvm_mmc_slot { struct mmc_host *mmc; /* slot-level mmc_core object */ struct cvm_mmc_host *host; /* common hw for all slots */ + struct mmc_request *current_req; u64 clock; + u32 ecount, gcount; u64 cached_switch; u64 cached_rca; - unsigned int cmd_cnt; /* sample delay */ - unsigned int dat_cnt; /* sample delay */ + struct sg_mapping_iter smi; + bool dma_active; + + u64 taps; /* otx2: MIO_EMM_TIMING */ + unsigned int cmd_cnt; /* otx: sample cmd in delay */ + unsigned int data_cnt; /* otx: sample data in delay */ + + int drive; /* Current drive */ + int slew; /* clock skew */ int bus_id; + bool cmd6_pending; + u64 want_switch; }; struct cvm_mmc_cr_type { @@ -161,6 +217,21 @@ struct cvm_mmc_cr_mods { #define MIO_EMM_DMA_CFG_SIZE GENMASK_ULL(55, 36) #define MIO_EMM_DMA_CFG_ADR GENMASK_ULL(35, 0) +#define MIO_EMM_CFG_BUS_ENA GENMASK_ULL(3, 0) + +#define MIO_EMM_IO_CTL_DRIVE GENMASK_ULL(3, 2) +#define MIO_EMM_IO_CTL_SLEW BIT_ULL(0) + +#define MIO_EMM_CALB_START BIT_ULL(0) +#define MIO_EMM_TAP_DELAY GENMASK_ULL(7, 0) + +#define MIO_EMM_TIMING_CMD_IN GENMASK_ULL(53, 48) +#define MIO_EMM_TIMING_CMD_OUT GENMASK_ULL(37, 32) +#define MIO_EMM_TIMING_DATA_IN GENMASK_ULL(21, 16) +#define MIO_EMM_TIMING_DATA_OUT GENMASK_ULL(5, 0) + +#define MIO_EMM_INT_NCB_RAS BIT_ULL(8) +#define MIO_EMM_INT_NCB_FLT BIT_ULL(7) #define MIO_EMM_INT_SWITCH_ERR BIT_ULL(6) #define MIO_EMM_INT_SWITCH_DONE BIT_ULL(5) #define MIO_EMM_INT_DMA_ERR BIT_ULL(4) @@ -169,6 +240,9 @@ struct cvm_mmc_cr_mods { #define MIO_EMM_INT_CMD_DONE BIT_ULL(1) #define MIO_EMM_INT_BUF_DONE BIT_ULL(0) +#define MIO_EMM_DMA_INT_FIFO BIT_ULL(1) +#define MIO_EMM_DMA_INT_DMA BIT_ULL(0) + #define MIO_EMM_RSP_STS_BUS_ID GENMASK_ULL(61, 60) #define MIO_EMM_RSP_STS_CMD_VAL BIT_ULL(59) #define MIO_EMM_RSP_STS_SWITCH_VAL BIT_ULL(58) @@ -200,9 +274,14 @@ struct cvm_mmc_cr_mods { #define MIO_EMM_SWITCH_ERR0 BIT_ULL(58) #define MIO_EMM_SWITCH_ERR1 BIT_ULL(57) #define MIO_EMM_SWITCH_ERR2 BIT_ULL(56) +#define MIO_EMM_SWITCH_ERRS GENMASK_ULL(58, 56) +#define MIO_EMM_SWITCH_HS400_TIMING BIT_ULL(50) +#define MIO_EMM_SWITCH_HS200_TIMING BIT_ULL(49) #define MIO_EMM_SWITCH_HS_TIMING BIT_ULL(48) +#define MIO_EMM_SWITCH_TIMING GENMASK_ULL(50, 48) #define MIO_EMM_SWITCH_BUS_WIDTH GENMASK_ULL(42, 40) #define MIO_EMM_SWITCH_POWER_CLASS GENMASK_ULL(35, 32) +#define MIO_EMM_SWITCH_CLK GENMASK_ULL(31, 0) #define MIO_EMM_SWITCH_CLK_HI GENMASK_ULL(31, 16) #define MIO_EMM_SWITCH_CLK_LO GENMASK_ULL(15, 0) @@ -210,6 +289,52 @@ struct cvm_mmc_cr_mods { irqreturn_t cvm_mmc_interrupt(int irq, void *dev_id); int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host); int cvm_mmc_of_slot_remove(struct cvm_mmc_slot *slot); + extern const char *cvm_mmc_irq_names[]; +static inline bool is_mmc_8xxx(struct cvm_mmc_host *host) +{ +#ifdef CONFIG_ARM64 + struct pci_dev *pdev = host->pdev; + u32 chip_id = (pdev->subsystem_device >> 12) & 0xF; + + return (chip_id == PCI_SUBSYS_DEVID_8XXX); +#else + return false; +#endif +} + +static inline bool is_mmc_otx2(struct cvm_mmc_host *host) +{ +#ifdef CONFIG_ARM64 + struct pci_dev *pdev = host->pdev; + u32 chip_id = (pdev->subsystem_device >> 12) & 0xF; + + return (chip_id == PCI_SUBSYS_DEVID_9XXX); +#else + return false; +#endif +} + +static inline bool is_mmc_otx2_A0(struct cvm_mmc_host *host) +{ +#ifdef CONFIG_ARM64 + struct pci_dev *pdev = host->pdev; + u32 chip_id = (pdev->subsystem_device >> 8) & 0xFF; + + return (pdev->revision == 0x00) && + (chip_id == MRVL_OCTEONTX2_96XX_PARTNUM); +#else + return false; +#endif +} + +static inline bool is_mmc_95xx(struct cvm_mmc_host *host) +{ + struct pci_dev *pdev = host->pdev; + u32 chip_id = (pdev->subsystem_device >> 8) & 0xFF; + + return (chip_id == PCI_SUBSYS_DEVID_95XX); +} + #endif diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 0c2ec1c21434..33ec2e7d2904 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1952,7 +1952,7 @@ static const struct flash_info spi_nor_ids[] = { { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, - { "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, /* Micron <--> ST Micro */ @@ -1967,9 +1967,10 @@ static const struct flash_info spi_nor_ids[] = { { "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, - { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE | SPI_NOR_4B_OPCODES) }, { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, { "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, + { "mt25ql02g", INFO(0x20ba22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | NO_CHIP_ERASE) }, /* Micron */ { diff --git a/drivers/net/ethernet/cavium/common/cavium_ptp.c b/drivers/net/ethernet/cavium/common/cavium_ptp.c index b821c9e1604c..81ff9ac73f9a 100644 --- a/drivers/net/ethernet/cavium/common/cavium_ptp.c +++ b/drivers/net/ethernet/cavium/common/cavium_ptp.c @@ -13,6 +13,9 @@ #define DRV_NAME "cavium_ptp" #define PCI_DEVICE_ID_CAVIUM_PTP 0xA00C +#define PCI_SUBSYS_DEVID_88XX_PTP 0xA10C +#define PCI_SUBSYS_DEVID_81XX_PTP 0XA20C +#define PCI_SUBSYS_DEVID_83XX_PTP 0xA30C #define PCI_DEVICE_ID_CAVIUM_RST 0xA00E #define PCI_PTP_BAR_NO 0 @@ -321,7 +324,12 @@ static void cavium_ptp_remove(struct pci_dev *pdev) } static const struct pci_device_id cavium_ptp_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_PTP) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_PTP, + PCI_VENDOR_ID_CAVIUM, PCI_SUBSYS_DEVID_88XX_PTP) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_PTP, + PCI_VENDOR_ID_CAVIUM, PCI_SUBSYS_DEVID_81XX_PTP) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_PTP, + PCI_VENDOR_ID_CAVIUM, PCI_SUBSYS_DEVID_83XX_PTP) }, { 0, } }; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c index 5e0b16bb95a0..61c4fcdf7e25 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c @@ -514,25 +514,42 @@ static int nicvf_set_ringparam(struct net_device *netdev, static int nicvf_get_rss_hash_opts(struct nicvf *nic, struct ethtool_rxnfc *info) { + u64 rss_cfg = nicvf_reg_read(nic, NIC_VNIC_RSS_CFG); + info->data = 0; + if (!(rss_cfg & BIT_ULL(RSS_HASH_IP))) + return 0; + + info->data = RXH_IP_SRC | RXH_IP_DST; + switch (info->flow_type) { case TCP_V4_FLOW: case TCP_V6_FLOW: + if (rss_cfg & BIT_ULL(RSS_HASH_TCP)) + info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; case UDP_V4_FLOW: case UDP_V6_FLOW: + if (rss_cfg & BIT_ULL(RSS_HASH_UDP)) + info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; case SCTP_V4_FLOW: case SCTP_V6_FLOW: info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; /* Fall through */ + case AH_ESP_V4_FLOW: + case AH_V4_FLOW: + case ESP_V4_FLOW: case IPV4_FLOW: + case AH_ESP_V6_FLOW: + case AH_V6_FLOW: + case ESP_V6_FLOW: case IPV6_FLOW: - info->data |= RXH_IP_SRC | RXH_IP_DST; break; default: return -EINVAL; } - return 0; } @@ -598,19 +615,6 @@ static int nicvf_set_rss_hash_opts(struct nicvf *nic, return -EINVAL; } break; - case SCTP_V4_FLOW: - case SCTP_V6_FLOW: - switch (info->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { - case 0: - rss_cfg &= ~(1ULL << RSS_HASH_L4ETC); - break; - case (RXH_L4_B_0_1 | RXH_L4_B_2_3): - rss_cfg |= (1ULL << RSS_HASH_L4ETC); - break; - default: - return -EINVAL; - } - break; case IPV4_FLOW: case IPV6_FLOW: rss_cfg = RSS_HASH_IP; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 40a44dcb3d9b..48757afeb34e 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -126,8 +126,7 @@ static void nicvf_write_to_mbx(struct nicvf *nic, union nic_mbx *mbx) int nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx) { - int timeout = NIC_MBOX_MSG_TIMEOUT; - int sleep = 10; + unsigned long timeout; int ret = 0; mutex_lock(&nic->rx_mode_mtx); @@ -137,6 +136,7 @@ int nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx) nicvf_write_to_mbx(nic, mbx); + timeout = jiffies + msecs_to_jiffies(NIC_MBOX_MSG_TIMEOUT); /* Wait for previous message to be acked, timeout 2sec */ while (!nic->pf_acked) { if (nic->pf_nacked) { @@ -146,11 +146,10 @@ int nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx) ret = -EINVAL; break; } - msleep(sleep); + usleep_range(8000, 10000); if (nic->pf_acked) break; - timeout -= sleep; - if (!timeout) { + if (time_after(jiffies, timeout)) { netdev_err(nic->netdev, "PF didn't ACK to mbox msg 0x%02x from VF%d\n", (mbx->msg.msg & 0xFF), nic->vf_id); diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 192bc92da881..0d9c2ca25f35 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -369,7 +369,7 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr) } head++; } - + kfree(rbdr->pgcache); /* Free RBDR ring */ nicvf_free_q_desc_mem(nic, &rbdr->dmem); } diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index bc2427c49b89..2460451fc48f 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -100,8 +100,8 @@ * RED accepts pkt if unused CQE < 2304 & >= 2560 * DROPs pkts if unused CQE < 2304 */ -#define RQ_PASS_CQ_LVL 192ULL -#define RQ_DROP_CQ_LVL 184ULL +#define RQ_PASS_CQ_LVL 224ULL +#define RQ_DROP_CQ_LVL 216ULL /* RED and Backpressure levels of RBDR for pkt reception * For RBDR, level is a measure of fullness i.e 0x0 means empty diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c index ad22554857bf..431234a2a390 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c @@ -80,6 +80,7 @@ static struct bgx *bgx_vnic[MAX_BGX_THUNDER]; static int lmac_count; /* Total no of LMACs in system */ static int bgx_xaui_check_link(struct lmac *lmac); +static int bgx_lmac_sgmii_init(struct bgx *bgx, struct lmac *lmac); /* Supported devices */ static const struct pci_device_id bgx_id_table[] = { @@ -570,6 +571,14 @@ static void bgx_sgmii_change_link_state(struct lmac *lmac) } bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL, misc_ctl); bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG, port_cfg); + if (!bgx->is_rgx) { + bgx_reg_modify(bgx, lmac->lmacid, BGX_GMP_PCS_MRX_CTL, + PCS_MRX_CTL_RESET); + if (bgx_poll_reg(bgx, lmac->lmacid, BGX_GMP_PCS_MRX_CTL, + PCS_MRX_CTL_RESET, true)) { + dev_err(&bgx->pdev->dev, "BGX PCS reset not completed\n"); + } + } /* Restore CMR config settings */ cmr_cfg |= (rx_en ? CMR_PKT_RX_EN : 0) | (tx_en ? CMR_PKT_TX_EN : 0); @@ -583,35 +592,21 @@ static void bgx_lmac_handler(struct net_device *netdev) { struct lmac *lmac = container_of(netdev, struct lmac, netdev); struct phy_device *phydev; - int link_changed = 0; if (!lmac) return; phydev = lmac->phydev; - if (!phydev->link && lmac->last_link) - link_changed = -1; - - if (phydev->link && - (lmac->last_duplex != phydev->duplex || - lmac->last_link != phydev->link || - lmac->last_speed != phydev->speed)) { - link_changed = 1; - } + if (phydev->link == 1) + lmac->link_up = true; + else + lmac->link_up = false; lmac->last_link = phydev->link; lmac->last_speed = phydev->speed; lmac->last_duplex = phydev->duplex; - if (!link_changed) - return; - - if (link_changed > 0) - lmac->link_up = true; - else - lmac->link_up = false; - if (lmac->is_sgmii) bgx_sgmii_change_link_state(lmac); else diff --git a/drivers/net/ethernet/marvell/octeontx2/Kconfig b/drivers/net/ethernet/marvell/octeontx2/Kconfig index 711ada7139d3..e808fe9e1043 100644 --- a/drivers/net/ethernet/marvell/octeontx2/Kconfig +++ b/drivers/net/ethernet/marvell/octeontx2/Kconfig @@ -9,10 +9,32 @@ config OCTEONTX2_MBOX config OCTEONTX2_AF tristate "Marvell OcteonTX2 RVU Admin Function driver" select OCTEONTX2_MBOX - depends on (64BIT && COMPILE_TEST) || ARM64 depends on PCI help This driver supports Marvell's OcteonTX2 Resource Virtualization Unit's admin function manager which manages all RVU HW resources and provides a medium to other PF/VFs to configure HW. Should be enabled for other RVU device drivers to work. + +config NDC_DIS_DYNAMIC_CACHING + bool "Disable caching of dynamic entries in NDC" + depends on OCTEONTX2_AF + default n + ---help--- + This config option disables caching of dynamic entries such as NIX SQEs + , NPA stack pages etc in NDC. Also locks down NIX SQ/CQ/RQ/RSS and + NPA Aura/Pool contexts. + +config OCTEONTX2_PF + tristate "Marvell OcteonTX2 NIC Physical Function driver" + select OCTEONTX2_MBOX + depends on PCI + help + This driver supports Marvell's OcteonTX2 Resource Virtualization + Unit's physical function NIC driver. + +config OCTEONTX2_VF + tristate "Marvell OcteonTX2 NIC Virtual Function driver" + depends on OCTEONTX2_PF + help + This driver supports Marvell's OcteonTX2 NIC virtual function. diff --git a/drivers/net/ethernet/marvell/octeontx2/Makefile b/drivers/net/ethernet/marvell/octeontx2/Makefile index e579dcd54c97..0064a69e0f72 100644 --- a/drivers/net/ethernet/marvell/octeontx2/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/Makefile @@ -3,4 +3,6 @@ # Makefile for Marvell OcteonTX2 device drivers. # +obj-$(CONFIG_OCTEONTX2_MBOX) += af/ obj-$(CONFIG_OCTEONTX2_AF) += af/ +obj-$(CONFIG_OCTEONTX2_PF) += nic/ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/Makefile b/drivers/net/ethernet/marvell/octeontx2/af/Makefile index 06329acf9c2c..a64046727ede 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/af/Makefile @@ -8,4 +8,6 @@ obj-$(CONFIG_OCTEONTX2_AF) += octeontx2_af.o octeontx2_mbox-y := mbox.o octeontx2_af-y := cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \ - rvu_reg.o rvu_npc.o + rvu_reg.o rvu_npc.o rvu_validation.o rvu_sso.o \ + rvu_tim.o rvu_cpt.o rvu_debugfs.o rvu_npc_fs.o \ + ptp.o rvu_ptp.o rvu_fixes.o rvu_sdp.o diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index 6d55e3d0b7ea..0a1f54de40ce 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -14,12 +14,14 @@ #include <linux/pci.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/ethtool.h> #include <linux/phy.h> #include <linux/of.h> #include <linux/of_mdio.h> #include <linux/of_net.h> #include "cgx.h" +#include "rvu.h" #define DRV_NAME "octeontx2-cgx" #define DRV_STRING "Marvell OcteonTX2 CGX/MAC Driver" @@ -30,6 +32,7 @@ * @cmd_lock: Lock to serialize the command interface * @resp: command response * @link_info: link related information + * @mac_to_index_bmap: Mac address to CGX table index mapping * @event_cb: callback for linkchange events * @event_cb_lock: lock for serializing callback with unregister * @cmd_pend: flag set before new command is started @@ -43,6 +46,7 @@ struct lmac { struct mutex cmd_lock; u64 resp; struct cgx_link_user_info link_info; + struct rsrc_bmap mac_to_index_bmap; struct cgx_event_cb event_cb; spinlock_t event_cb_lock; bool cmd_pend; @@ -138,6 +142,16 @@ void *cgx_get_pdata(int cgx_id) } EXPORT_SYMBOL(cgx_get_pdata); +int cgx_get_cgxid(void *cgxd) +{ + struct cgx *cgx = cgxd; + + if (!cgx) + return -EINVAL; + + return cgx->cgx_id; +} + /* Ensure the required lock for event queue(where asynchronous events are * posted) is acquired before calling this API. Else an asynchronous event(with * latest link status) can reach the destination before this function returns @@ -180,13 +194,102 @@ int cgx_lmac_addr_set(u8 cgx_id, u8 lmac_id, u8 *mac_addr) cfg | CGX_DMAC_CAM_ADDR_ENABLE | ((u64)lmac_id << 49)); cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); - cfg |= CGX_DMAC_CTL0_CAM_ENABLE; + cfg |= (CGX_DMAC_CTL0_CAM_ENABLE | CGX_DMAC_BCAST_MODE | + CGX_DMAC_MCAST_MODE); cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); return 0; } EXPORT_SYMBOL(cgx_lmac_addr_set); +int cgx_lmac_addr_add(u8 cgx_id, u8 lmac_id, u8 *mac_addr) +{ + struct cgx *cgx_dev = cgx_get_pdata(cgx_id); + struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev); + int index, idx; + u64 cfg = 0; + + /* Get available index where entry is to be installed */ + idx = rvu_alloc_rsrc(&lmac->mac_to_index_bmap); + if (idx < 0) + return idx; + + /* Calculate real index of CGX DMAC table */ + index = lmac_id * lmac->mac_to_index_bmap.max + idx; + + cfg = mac2u64 (mac_addr); + cfg |= CGX_DMAC_CAM_ADDR_ENABLE; + cfg |= ((u64)lmac_id << 49); + cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), cfg); + + cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); + cfg |= (CGX_DMAC_BCAST_MODE | CGX_DMAC_MCAST_MODE | + CGX_DMAC_CAM_ACCEPT); + cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); + + return idx; +} +EXPORT_SYMBOL(cgx_lmac_addr_add); + +int cgx_lmac_addr_reset(u8 cgx_id, u8 lmac_id) +{ + struct cgx *cgx_dev = cgx_get_pdata(cgx_id); + struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev); + u8 index = 0; + u64 cfg; + + /* Restore index 0 to its default init value as done during + * cgx_lmac_init + */ + set_bit(0, lmac->mac_to_index_bmap.bmap); + + index = lmac_id * lmac->mac_to_index_bmap.max + index; + cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), 0); + + /* Reset CGXX_CMRX_RX_DMAC_CTL0 register to default state */ + cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); + cfg &= ~CGX_DMAC_CAM_ACCEPT; + cfg |= (CGX_DMAC_BCAST_MODE | CGX_DMAC_MCAST_MODE); + cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); + + return 0; +} +EXPORT_SYMBOL(cgx_lmac_addr_reset); + +int cgx_lmac_addr_del(u8 cgx_id, u8 lmac_id, u8 index) +{ + struct cgx *cgx_dev = cgx_get_pdata(cgx_id); + struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev); + + /* Validate the index */ + if (index >= lmac->mac_to_index_bmap.max) + return -EINVAL; + + /* Skip deletion for reserved index i.e. index 0 */ + if (index == 0) + return 0; + + rvu_free_rsrc(&lmac->mac_to_index_bmap, index); + + index = lmac_id * lmac->mac_to_index_bmap.max + index; + cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), 0); + + return 0; +} +EXPORT_SYMBOL(cgx_lmac_addr_del); + +int cgx_lmac_addr_max_entries_get(u8 cgx_id, u8 lmac_id) +{ + struct cgx *cgx_dev = cgx_get_pdata(cgx_id); + struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev); + + if (lmac) + return lmac->mac_to_index_bmap.max; + + return 0; +} +EXPORT_SYMBOL(cgx_lmac_addr_max_entries_get); + u64 cgx_lmac_addr_get(u8 cgx_id, u8 lmac_id) { struct cgx *cgx_dev = cgx_get_pdata(cgx_id); @@ -258,8 +361,8 @@ void cgx_lmac_promisc_config(int cgx_id, int lmac_id, bool enable) if (enable) { /* Enable promiscuous mode on LMAC */ cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); - cfg &= ~(CGX_DMAC_CAM_ACCEPT | CGX_DMAC_MCAST_MODE); - cfg |= CGX_DMAC_BCAST_MODE; + cfg &= ~CGX_DMAC_CAM_ACCEPT; + cfg |= (CGX_DMAC_BCAST_MODE | CGX_DMAC_MCAST_MODE); cgx_write(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); cfg = cgx_read(cgx, 0, @@ -281,12 +384,46 @@ void cgx_lmac_promisc_config(int cgx_id, int lmac_id, bool enable) } EXPORT_SYMBOL(cgx_lmac_promisc_config); +/* Enable or disable forwarding received pause frames to Tx block */ +void cgx_lmac_enadis_rx_pause_fwding(void *cgxd, int lmac_id, bool enable) +{ + struct cgx *cgx = cgxd; + u64 cfg; + + if (!cgx) + return; + + if (enable) { + cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); + cfg |= CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + cfg |= CGX_SMUX_RX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + } else { + cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); + cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + } +} +EXPORT_SYMBOL(cgx_lmac_enadis_rx_pause_fwding); + int cgx_get_rx_stats(void *cgxd, int lmac_id, int idx, u64 *rx_stat) { struct cgx *cgx = cgxd; if (!cgx || lmac_id >= cgx->lmac_count) return -ENODEV; +#define CGX_RX_STAT_GLOBAL_INDEX 9 + /* pass lmac as 0 for CGX_CMR_RX_STAT9-12 */ + if (idx >= CGX_RX_STAT_GLOBAL_INDEX) + lmac_id = 0; + *rx_stat = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_STAT0 + (idx * 8)); return 0; } @@ -303,6 +440,68 @@ int cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat) } EXPORT_SYMBOL(cgx_get_tx_stats); +static int cgx_set_fec_stats_count(struct cgx_link_user_info *linfo) +{ + if (linfo->fec) { + switch (linfo->lmac_type_id) { + case LMAC_MODE_SGMII: + case LMAC_MODE_XAUI: + case LMAC_MODE_RXAUI: + case LMAC_MODE_QSGMII: + return 0; + case LMAC_MODE_10G_R: + case LMAC_MODE_25G_R: + case LMAC_MODE_100G_R: + case LMAC_MODE_USXGMII: + return 1; + case LMAC_MODE_40G_R: + return 4; + case LMAC_MODE_50G_R: + if (linfo->fec == OTX2_FEC_BASER) + return 2; + else + return 1; + } + } + return 0; +} + +int cgx_get_fec_stats(void *cgxd, int lmac_id, struct cgx_fec_stats_rsp *rsp) +{ + int stats, fec_stats_count = 0; + int corr_reg, uncorr_reg; + struct cgx *cgx = cgxd; + + if (!cgx || lmac_id >= cgx->lmac_count) + return -ENODEV; + fec_stats_count = + cgx_set_fec_stats_count(&cgx->lmac_idmap[lmac_id]->link_info); + if (cgx->lmac_idmap[lmac_id]->link_info.fec == OTX2_FEC_BASER) { + corr_reg = CGXX_SPUX_LNX_FEC_CORR_BLOCKS; + uncorr_reg = CGXX_SPUX_LNX_FEC_UNCORR_BLOCKS; + } else { + corr_reg = CGXX_SPUX_RSFEC_CORR; + uncorr_reg = CGXX_SPUX_RSFEC_UNCORR; + } + for (stats = 0; stats < fec_stats_count; stats++) { + rsp->fec_corr_blks += + cgx_read(cgx, lmac_id, corr_reg + (stats * 8)); + rsp->fec_uncorr_blks += + cgx_read(cgx, lmac_id, uncorr_reg + (stats * 8)); + } + return 0; +} + +u64 cgx_get_lmac_tx_fifo_status(void *cgxd, int lmac_id) +{ + struct cgx *cgx = cgxd; + + if (!cgx || lmac_id >= cgx->lmac_count) + return 0; + return cgx_read(cgx, lmac_id, CGXX_CMRX_TX_FIFO_LEN); +} +EXPORT_SYMBOL(cgx_get_lmac_tx_fifo_status); + int cgx_lmac_rx_tx_enable(void *cgxd, int lmac_id, bool enable) { struct cgx *cgx = cgxd; @@ -313,14 +512,169 @@ int cgx_lmac_rx_tx_enable(void *cgxd, int lmac_id, bool enable) cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG); if (enable) - cfg |= CMR_EN | DATA_PKT_RX_EN | DATA_PKT_TX_EN; + cfg |= DATA_PKT_RX_EN | DATA_PKT_TX_EN; else - cfg &= ~(CMR_EN | DATA_PKT_RX_EN | DATA_PKT_TX_EN); + cfg &= ~(DATA_PKT_RX_EN | DATA_PKT_TX_EN); cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg); return 0; } EXPORT_SYMBOL(cgx_lmac_rx_tx_enable); +int cgx_lmac_tx_enable(void *cgxd, int lmac_id, bool enable) +{ + struct cgx *cgx = cgxd; + u64 cfg, last; + + if (!cgx || lmac_id >= cgx->lmac_count) + return -ENODEV; + + cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG); + last = cfg; + if (enable) + cfg |= DATA_PKT_TX_EN; + else + cfg &= ~DATA_PKT_TX_EN; + + if (cfg != last) + cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg); + return !!(last & DATA_PKT_TX_EN); +} +EXPORT_SYMBOL(cgx_lmac_tx_enable); + +int cgx_lmac_get_pause_frm(void *cgxd, int lmac_id, + u8 *tx_pause, u8 *rx_pause) +{ + struct cgx *cgx = cgxd; + u64 cfg; + + if (!cgx || lmac_id >= cgx->lmac_count) + return -ENODEV; + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + *rx_pause = !!(cfg & CGX_SMUX_RX_FRM_CTL_CTL_BCK); + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); + *tx_pause = !!(cfg & CGX_SMUX_TX_CTL_L2P_BP_CONV); + return 0; +} +EXPORT_SYMBOL(cgx_lmac_get_pause_frm); + +int cgx_lmac_set_pause_frm(void *cgxd, int lmac_id, + u8 tx_pause, u8 rx_pause) +{ + struct cgx *cgx = cgxd; + u64 cfg; + + if (!cgx || lmac_id >= cgx->lmac_count) + return -ENODEV; + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; + cfg |= rx_pause ? CGX_SMUX_RX_FRM_CTL_CTL_BCK : 0x0; + cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); + cfg &= ~CGX_SMUX_TX_CTL_L2P_BP_CONV; + cfg |= tx_pause ? CGX_SMUX_TX_CTL_L2P_BP_CONV : 0x0; + cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg); + + cfg = cgx_read(cgx, 0, CGXX_CMR_RX_OVR_BP); + if (tx_pause) { + cfg &= ~CGX_CMR_RX_OVR_BP_EN(lmac_id); + } else { + cfg |= CGX_CMR_RX_OVR_BP_EN(lmac_id); + cfg &= ~CGX_CMR_RX_OVR_BP_BP(lmac_id); + } + cgx_write(cgx, 0, CGXX_CMR_RX_OVR_BP, cfg); + return 0; +} +EXPORT_SYMBOL(cgx_lmac_set_pause_frm); + +void cgx_lmac_ptp_config(void *cgxd, int lmac_id, bool enable) +{ + struct cgx *cgx = cgxd; + u64 cfg; + + if (!cgx) + return; + + if (enable) { + /* Enable inbound PTP timestamping */ + cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); + cfg |= CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE; + cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + cfg |= CGX_SMUX_RX_FRM_CTL_PTP_MODE; + cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + } else { + /* Disable inbound PTP stamping */ + cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); + cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE; + cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + cfg &= ~CGX_SMUX_RX_FRM_CTL_PTP_MODE; + cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + } +} +EXPORT_SYMBOL(cgx_lmac_ptp_config); + +static void cgx_lmac_pause_frm_config(struct cgx *cgx, int lmac_id, bool enable) +{ + u64 cfg; + + if (!cgx || lmac_id >= cgx->lmac_count) + return; + if (enable) { + /* Enable receive pause frames */ + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + cfg |= CGX_SMUX_RX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + + cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); + cfg |= CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); + + /* Enable pause frames transmission */ + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); + cfg |= CGX_SMUX_TX_CTL_L2P_BP_CONV; + cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg); + + /* Set pause time and interval*/ + cgx_write(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_TIME, + DEFAULT_PAUSE_TIME); + /* Set pause interval as the hardware default is too short */ + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_INTERVAL); + cfg &= ~0xFFFFULL; + cgx_write(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_INTERVAL, + cfg | (DEFAULT_PAUSE_TIME - 0x1000)); + + cgx_write(cgx, lmac_id, CGXX_GMP_GMI_TX_PAUSE_PKT_TIME, + DEFAULT_PAUSE_TIME); + + cfg = cgx_read(cgx, lmac_id, + CGXX_GMP_GMI_TX_PAUSE_PKT_INTERVAL); + cfg &= ~0xFFFFULL; + cgx_write(cgx, lmac_id, CGXX_GMP_GMI_TX_PAUSE_PKT_INTERVAL, + cfg | (DEFAULT_PAUSE_TIME - 0x1000)); + } else { + /* ALL pause frames received are completely ignored */ + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + + cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); + cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); + + /* Disable pause frames transmission */ + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); + cfg &= ~CGX_SMUX_TX_CTL_L2P_BP_CONV; + cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg); + } +} + /* CGX Firmware interface low level support */ static int cgx_fwi_cmd_send(u64 req, u64 *resp, struct lmac *lmac) { @@ -406,6 +760,7 @@ static inline void cgx_link_usertable_init(void) cgx_speed_mbps[CGX_LINK_25G] = 25000; cgx_speed_mbps[CGX_LINK_40G] = 40000; cgx_speed_mbps[CGX_LINK_50G] = 50000; + cgx_speed_mbps[CGX_LINK_80G] = 80000; cgx_speed_mbps[CGX_LINK_100G] = 100000; cgx_lmactype_string[LMAC_MODE_SGMII] = "SGMII"; @@ -420,6 +775,155 @@ static inline void cgx_link_usertable_init(void) cgx_lmactype_string[LMAC_MODE_USXGMII] = "USXGMII"; } +static inline int cgx_link_usertable_index_map(int speed) +{ + switch (speed) { + case SPEED_10: + return CGX_LINK_10M; + case SPEED_100: + return CGX_LINK_100M; + case SPEED_1000: + return CGX_LINK_1G; + case SPEED_2500: + return CGX_LINK_2HG; + case SPEED_5000: + return CGX_LINK_5G; + case SPEED_10000: + return CGX_LINK_10G; + case SPEED_20000: + return CGX_LINK_20G; + case SPEED_25000: + return CGX_LINK_25G; + case SPEED_40000: + return CGX_LINK_40G; + case SPEED_50000: + return CGX_LINK_50G; + case 80000: + return CGX_LINK_80G; + case SPEED_100000: + return CGX_LINK_100G; + case SPEED_UNKNOWN: + return CGX_LINK_NONE; + } + return CGX_LINK_NONE; +} + +static void set_mod_args(struct cgx_set_link_mode_args *args, + u32 speed, u8 duplex, u8 autoneg, u64 mode) +{ + /* firmware requires this value in the reverse format */ + args->duplex = duplex; + args->speed = speed; + args->mode = mode; + args->an = autoneg; + args->ports = 0; +} + +static void otx2_map_ethtool_link_modes(u64 bitmask, + struct cgx_set_link_mode_args *args) +{ + switch (bitmask) { + case BIT_ULL(ETHTOOL_LINK_MODE_10baseT_Half_BIT): + set_mod_args(args, 10, 1, 1, BIT_ULL(CGX_MODE_SGMII)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_10baseT_Full_BIT): + set_mod_args(args, 10, 0, 1, BIT_ULL(CGX_MODE_SGMII)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_100baseT_Half_BIT): + set_mod_args(args, 100, 1, 1, BIT_ULL(CGX_MODE_SGMII)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_100baseT_Full_BIT): + set_mod_args(args, 100, 0, 1, BIT_ULL(CGX_MODE_SGMII)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_1000baseT_Half_BIT): + set_mod_args(args, 1000, 1, 1, BIT_ULL(CGX_MODE_SGMII)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_1000baseT_Full_BIT): + set_mod_args(args, 1000, 0, 1, BIT_ULL(CGX_MODE_SGMII)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_1000baseX_Full_BIT): + set_mod_args(args, 1000, 0, 0, BIT_ULL(CGX_MODE_1000_BASEX)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_10000baseT_Full_BIT): + set_mod_args(args, 1000, 0, 1, BIT_ULL(CGX_MODE_QSGMII)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT): + set_mod_args(args, 10000, 0, 0, BIT_ULL(CGX_MODE_10G_C2C)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT): + set_mod_args(args, 10000, 0, 0, BIT_ULL(CGX_MODE_10G_C2M)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT): + set_mod_args(args, 10000, 0, 1, BIT_ULL(CGX_MODE_10G_KR)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT): + set_mod_args(args, 20000, 0, 0, BIT_ULL(CGX_MODE_20G_C2C)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT): + set_mod_args(args, 25000, 0, 0, BIT_ULL(CGX_MODE_25G_C2C)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_25000baseSR_Full_BIT): + set_mod_args(args, 25000, 0, 0, BIT_ULL(CGX_MODE_25G_C2M)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT): + set_mod_args(args, 25000, 0, 0, BIT_ULL(CGX_MODE_25G_2_C2C)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_25000baseCR_Full_BIT): + set_mod_args(args, 25000, 0, 1, BIT_ULL(CGX_MODE_25G_CR)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_25000baseKR_Full_BIT): + set_mod_args(args, 25000, 0, 1, BIT_ULL(CGX_MODE_25G_KR)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT): + set_mod_args(args, 40000, 0, 0, BIT_ULL(CGX_MODE_40G_C2C)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT): + set_mod_args(args, 40000, 0, 0, BIT_ULL(CGX_MODE_40G_C2M)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT): + set_mod_args(args, 40000, 0, 1, BIT_ULL(CGX_MODE_40G_CR4)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT): + set_mod_args(args, 40000, 0, 1, BIT_ULL(CGX_MODE_40G_KR4)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT): + set_mod_args(args, 40000, 0, 0, BIT_ULL(CGX_MODE_40GAUI_C2C)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT): + set_mod_args(args, 50000, 0, 0, BIT_ULL(CGX_MODE_50G_C2C)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT): + set_mod_args(args, 50000, 0, 0, BIT_ULL(CGX_MODE_50G_4_C2C)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT): + set_mod_args(args, 50000, 0, 0, BIT_ULL(CGX_MODE_50G_C2M)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT): + set_mod_args(args, 50000, 0, 1, BIT_ULL(CGX_MODE_50G_CR)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT): + set_mod_args(args, 50000, 0, 1, BIT_ULL(CGX_MODE_50G_KR)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT): + set_mod_args(args, 80000, 0, 0, BIT_ULL(CGX_MODE_80GAUI_C2C)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT): + set_mod_args(args, 100000, 0, 0, BIT_ULL(CGX_MODE_100G_C2C)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT): + set_mod_args(args, 100000, 0, 0, BIT_ULL(CGX_MODE_100G_C2M)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT): + set_mod_args(args, 100000, 0, 1, BIT_ULL(CGX_MODE_100G_CR4)); + break; + case BIT_ULL(ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT): + set_mod_args(args, 100000, 0, 1, BIT_ULL(CGX_MODE_100G_KR4)); + break; + default: + set_mod_args(args, 0, 1, 0, BIT_ULL(CGX_MODE_MAX)); + break; + } +} static inline void link_status_user_format(u64 lstat, struct cgx_link_user_info *linfo, struct cgx *cgx, u8 lmac_id) @@ -429,6 +933,9 @@ static inline void link_status_user_format(u64 lstat, linfo->link_up = FIELD_GET(RESP_LINKSTAT_UP, lstat); linfo->full_duplex = FIELD_GET(RESP_LINKSTAT_FDUPLEX, lstat); linfo->speed = cgx_speed_mbps[FIELD_GET(RESP_LINKSTAT_SPEED, lstat)]; + linfo->an = FIELD_GET(RESP_LINKSTAT_AN, lstat); + linfo->fec = FIELD_GET(RESP_LINKSTAT_FEC, lstat); + linfo->port = FIELD_GET(RESP_LINKSTAT_PORT, lstat); linfo->lmac_type_id = cgx_get_lmac_type(cgx, lmac_id); lmac_string = cgx_lmactype_string[linfo->lmac_type_id]; strncpy(linfo->lmac_type, lmac_string, LMACTYPE_STR_LEN - 1); @@ -456,6 +963,8 @@ static inline void cgx_link_change_handler(u64 lstat, lmac->link_info = event.link_uinfo; linfo = &lmac->link_info; + if (err_type == CGX_ERR_SPEED_CHANGE_INVALID) + return; /* Ensure callback doesn't get unregistered until we finish it */ spin_lock(&lmac->event_cb_lock); @@ -484,7 +993,8 @@ static inline bool cgx_cmdresp_is_linkevent(u64 event) id = FIELD_GET(EVTREG_ID, event); if (id == CGX_CMD_LINK_BRING_UP || - id == CGX_CMD_LINK_BRING_DOWN) + id == CGX_CMD_LINK_BRING_DOWN || + id == CGX_CMD_MODE_CHANGE) return true; else return false; @@ -498,60 +1008,6 @@ static inline bool cgx_event_is_linkevent(u64 event) return false; } -static inline int cgx_fwi_get_mkex_prfl_sz(u64 *prfl_sz, - struct cgx *cgx) -{ - u64 req = 0; - u64 resp; - int err; - - req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_MKEX_PRFL_SIZE, req); - err = cgx_fwi_cmd_generic(req, &resp, cgx, 0); - if (!err) - *prfl_sz = FIELD_GET(RESP_MKEX_PRFL_SIZE, resp); - - return err; -} - -static inline int cgx_fwi_get_mkex_prfl_addr(u64 *prfl_addr, - struct cgx *cgx) -{ - u64 req = 0; - u64 resp; - int err; - - req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_MKEX_PRFL_ADDR, req); - err = cgx_fwi_cmd_generic(req, &resp, cgx, 0); - if (!err) - *prfl_addr = FIELD_GET(RESP_MKEX_PRFL_ADDR, resp); - - return err; -} - -int cgx_get_mkex_prfl_info(u64 *addr, u64 *size) -{ - struct cgx *cgx_dev; - int err; - - if (!addr || !size) - return -EINVAL; - - cgx_dev = list_first_entry(&cgx_list, struct cgx, cgx_list); - if (!cgx_dev) - return -ENXIO; - - err = cgx_fwi_get_mkex_prfl_sz(size, cgx_dev); - if (err) - return -EIO; - - err = cgx_fwi_get_mkex_prfl_addr(addr, cgx_dev); - if (err) - return -EIO; - - return 0; -} -EXPORT_SYMBOL(cgx_get_mkex_prfl_info); - static irqreturn_t cgx_fwi_event_handler(int irq, void *data) { struct lmac *lmac = data; @@ -582,7 +1038,7 @@ static irqreturn_t cgx_fwi_event_handler(int irq, void *data) /* Release thread waiting for completion */ lmac->cmd_pend = false; - wake_up_interruptible(&lmac->wq_cmd_cmplt); + wake_up(&lmac->wq_cmd_cmplt); break; case CGX_EVT_ASYNC: if (cgx_event_is_linkevent(event)) @@ -637,6 +1093,112 @@ int cgx_lmac_evh_unregister(void *cgxd, int lmac_id) } EXPORT_SYMBOL(cgx_lmac_evh_unregister); +int cgx_get_fwdata_base(u64 *base) +{ + u64 req = 0, resp; + struct cgx *cgx; + int err; + + cgx = list_first_entry_or_null(&cgx_list, struct cgx, cgx_list); + if (!cgx) + return -ENXIO; + + req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FWD_BASE, req); + err = cgx_fwi_cmd_generic(req, &resp, cgx, 0); + if (!err) + *base = FIELD_GET(RESP_FWD_BASE, resp); + + return err; +} + +int cgx_set_fec(u64 fec, int cgx_id, int lmac_id) +{ + u64 req = 0, resp; + struct cgx *cgx; + int err = 0; + + cgx = cgx_get_pdata(cgx_id); + if (!cgx) + return -ENXIO; + + req = FIELD_SET(CMDREG_ID, CGX_CMD_SET_FEC, req); + req = FIELD_SET(CMDSETFEC, fec, req); + err = cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id); + if (!err) { + cgx->lmac_idmap[lmac_id]->link_info.fec = + FIELD_GET(RESP_LINKSTAT_FEC, resp); + return cgx->lmac_idmap[lmac_id]->link_info.fec; + } + return err; +} + +int cgx_set_phy_mod_type(int mod, void *cgxd, int lmac_id) +{ + struct cgx *cgx = cgxd; + u64 req = 0, resp; + + if (!cgx) + return -ENODEV; + + req = FIELD_SET(CMDREG_ID, CGX_CMD_SET_PHY_MOD_TYPE, req); + req = FIELD_SET(CMDSETPHYMODTYPE, mod, req); + return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id); +} + +int cgx_get_phy_mod_type(void *cgxd, int lmac_id) +{ + struct cgx *cgx = cgxd; + u64 req = 0, resp; + int err; + + if (!cgx) + return -ENODEV; + + req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_PHY_MOD_TYPE, req); + err = cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id); + if (!err) + return FIELD_GET(RESP_GETPHYMODTYPE, resp); + return err; +} + +int cgx_get_phy_fec_stats(void *cgxd, int lmac_id) +{ + struct cgx *cgx = cgxd; + u64 req = 0, resp; + + if (!cgx) + return -ENODEV; + + req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_PHY_FEC_STATS, req); + return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id); +} + +int cgx_set_link_mode(void *cgxd, struct cgx_set_link_mode_args args, + int cgx_id, int lmac_id) +{ + struct cgx *cgx = cgxd; + u64 req = 0, resp; + int err = 0; + + if (!cgx) + return -ENODEV; + + if (args.mode) + otx2_map_ethtool_link_modes(args.mode, &args); + if (!args.speed && args.duplex && !args.an) + return -EINVAL; + + req = FIELD_SET(CMDREG_ID, CGX_CMD_MODE_CHANGE, req); + req = FIELD_SET(CMDMODECHANGE_SPEED, + cgx_link_usertable_index_map(args.speed), req); + req = FIELD_SET(CMDMODECHANGE_DUPLEX, args.duplex, req); + req = FIELD_SET(CMDMODECHANGE_AN, args.an, req); + req = FIELD_SET(CMDMODECHANGE_PORT, args.ports, req); + req = FIELD_SET(CMDMODECHANGE_FLAGS, args.mode, req); + err = cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id); + return err; +} + static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool enable) { u64 req = 0; @@ -698,6 +1260,17 @@ static void cgx_lmac_linkup_work(struct work_struct *work) } } +int cgx_set_link_state(void *cgxd, int lmac_id, bool enable) +{ + struct cgx *cgx = cgxd; + + if (!cgx) + return -ENODEV; + + return cgx_fwi_link_change(cgx, lmac_id, enable); +} +EXPORT_SYMBOL(cgx_set_link_state); + int cgx_lmac_linkup_start(void *cgxd) { struct cgx *cgx = cgxd; @@ -730,6 +1303,15 @@ static int cgx_lmac_init(struct cgx *cgx) sprintf(lmac->name, "cgx_fwi_%d_%d", cgx->cgx_id, i); lmac->lmac_id = i; lmac->cgx = cgx; + lmac->mac_to_index_bmap.max = + MAX_DMAC_ENTRIES_PER_CGX / cgx->lmac_count; + err = rvu_alloc_bitmap(&lmac->mac_to_index_bmap); + if (err) + return err; + + /* Reserve first entry for default MAC address */ + set_bit(0, lmac->mac_to_index_bmap.bmap); + init_waitqueue_head(&lmac->wq_cmd_cmplt); mutex_init(&lmac->cmd_lock); spin_lock_init(&lmac->event_cb_lock); @@ -745,6 +1327,7 @@ static int cgx_lmac_init(struct cgx *cgx) /* Add reference */ cgx->lmac_idmap[i] = lmac; + cgx_lmac_pause_frm_config(cgx, i, true); } return cgx_lmac_verify_fwi_version(cgx); @@ -763,10 +1346,12 @@ static int cgx_lmac_exit(struct cgx *cgx) /* Free all lmac related resources */ for (i = 0; i < cgx->lmac_count; i++) { + cgx_lmac_pause_frm_config(cgx, i, false); lmac = cgx->lmac_idmap[i]; if (!lmac) continue; free_irq(pci_irq_vector(cgx->pdev, CGX_LMAC_FWI + i * 9), lmac); + kfree(lmac->mac_to_index_bmap.bmap); kfree(lmac->name); kfree(lmac); } @@ -819,6 +1404,13 @@ static int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id) cgx->cgx_id = (pci_resource_start(pdev, PCI_CFG_REG_BAR_NUM) >> 24) & CGX_ID_MASK; + /* Skip probe if CGX is not mapped to NIX */ + if (!is_cgx_mapped_to_nix(pdev->subsystem_device, cgx->cgx_id)) { + dev_notice(dev, "skip cgx%d probe\n", cgx->cgx_id); + err = -ENOMEM; + goto err_release_regions; + } + /* init wq for processing linkup requests */ INIT_WORK(&cgx->cgx_cmd_work, cgx_lmac_linkup_work); cgx->cgx_cmd_workq = alloc_workqueue("cgx_cmd_workq", 0, 0); @@ -855,8 +1447,11 @@ static void cgx_remove(struct pci_dev *pdev) { struct cgx *cgx = pci_get_drvdata(pdev); - cgx_lmac_exit(cgx); - list_del(&cgx->cgx_list); + if (cgx) { + cgx_lmac_exit(cgx); + list_del(&cgx->cgx_list); + } + pci_free_irq_vectors(pdev); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h index 206dc5dc1df8..4daba85f836a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h @@ -22,6 +22,7 @@ #define CGX_ID_MASK 0x7 #define MAX_LMAC_PER_CGX 4 +#define MAX_DMAC_ENTRIES_PER_CGX 32 #define CGX_FIFO_LEN 65536 /* 64K for both Rx & Tx */ #define CGX_OFFSET(x) ((x) * MAX_LMAC_PER_CGX) @@ -47,22 +48,51 @@ #define CGX_DMAC_CAM_ADDR_ENABLE BIT_ULL(48) #define CGXX_CMRX_RX_DMAC_CAM1 0x400 #define CGX_RX_DMAC_ADR_MASK GENMASK_ULL(47, 0) +#define CGXX_CMRX_TX_FIFO_LEN 0x618 +#define CGXX_CMRX_TX_LMAC_IDLE BIT_ULL(14) +#define CGXX_CMRX_TX_LMAC_E_IDLE BIT_ULL(29) #define CGXX_CMRX_TX_STAT0 0x700 #define CGXX_SCRATCH0_REG 0x1050 #define CGXX_SCRATCH1_REG 0x1058 #define CGX_CONST 0x2000 #define CGXX_SPUX_CONTROL1 0x10000 +#define CGXX_SPUX_LNX_FEC_CORR_BLOCKS 0x10700 +#define CGXX_SPUX_LNX_FEC_UNCORR_BLOCKS 0x10800 +#define CGXX_SPUX_RSFEC_CORR 0x10088 +#define CGXX_SPUX_RSFEC_UNCORR 0x10090 + #define CGXX_SPUX_CONTROL1_LBK BIT_ULL(14) #define CGXX_GMP_PCS_MRX_CTL 0x30000 #define CGXX_GMP_PCS_MRX_CTL_LBK BIT_ULL(14) +#define CGXX_SMUX_RX_FRM_CTL 0x20020 +#define CGX_SMUX_RX_FRM_CTL_CTL_BCK BIT_ULL(3) +#define CGX_SMUX_RX_FRM_CTL_PTP_MODE BIT_ULL(12) +#define CGXX_GMP_GMI_RXX_FRM_CTL 0x38028 +#define CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK BIT_ULL(3) +#define CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE BIT_ULL(12) +#define CGXX_SMUX_TX_CTL 0x20178 +#define CGXX_SMUX_TX_PAUSE_PKT_TIME 0x20110 +#define CGXX_SMUX_TX_PAUSE_PKT_INTERVAL 0x20120 +#define CGXX_GMP_GMI_TX_PAUSE_PKT_TIME 0x38230 +#define CGXX_GMP_GMI_TX_PAUSE_PKT_INTERVAL 0x38248 +#define CGX_SMUX_TX_CTL_L2P_BP_CONV BIT_ULL(7) +#define CGXX_CMR_RX_OVR_BP 0x130 +#define CGX_CMR_RX_OVR_BP_EN(X) BIT_ULL((X + 8)) +#define CGX_CMR_RX_OVR_BP_BP(X) BIT_ULL((X + 4)) #define CGX_COMMAND_REG CGXX_SCRATCH1_REG #define CGX_EVENT_REG CGXX_SCRATCH0_REG #define CGX_CMD_TIMEOUT 2200 /* msecs */ +#define DEFAULT_PAUSE_TIME 0xFFFF #define CGX_NVEC 37 #define CGX_LMAC_FWI 0 +enum cgx_nix_stat_type { + NIX_STATS_RX, + NIX_STATS_TX, +}; + enum LMAC_TYPE { LMAC_MODE_SGMII = 0, LMAC_MODE_XAUI = 1, @@ -96,6 +126,7 @@ struct cgx_event_cb { extern struct pci_driver cgx_driver; int cgx_get_cgxcnt_max(void); +int cgx_get_cgxid(void *cgxd); int cgx_get_lmac_cnt(void *cgxd); void *cgx_get_pdata(int cgx_id); int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind); @@ -103,13 +134,33 @@ int cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id); int cgx_lmac_evh_unregister(void *cgxd, int lmac_id); int cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat); int cgx_get_rx_stats(void *cgxd, int lmac_id, int idx, u64 *rx_stat); +int cgx_get_fec_stats(void *cgxd, int lmac_id, struct cgx_fec_stats_rsp *rsp); +u64 cgx_get_lmac_tx_fifo_status(void *cgxd, int lmac_id); int cgx_lmac_rx_tx_enable(void *cgxd, int lmac_id, bool enable); +int cgx_lmac_tx_enable(void *cgxd, int lmac_id, bool enable); int cgx_lmac_addr_set(u8 cgx_id, u8 lmac_id, u8 *mac_addr); +int cgx_lmac_addr_reset(u8 cgx_id, u8 lmac_id); u64 cgx_lmac_addr_get(u8 cgx_id, u8 lmac_id); +int cgx_lmac_addr_add(u8 cgx_id, u8 lmac_id, u8 *mac_addr); +int cgx_lmac_addr_del(u8 cgx_id, u8 lmac_id, u8 index); +int cgx_lmac_addr_max_entries_get(u8 cgx_id, u8 lmac_id); void cgx_lmac_promisc_config(int cgx_id, int lmac_id, bool enable); +void cgx_lmac_enadis_rx_pause_fwding(void *cgxd, int lmac_id, bool enable); int cgx_lmac_internal_loopback(void *cgxd, int lmac_id, bool enable); int cgx_get_link_info(void *cgxd, int lmac_id, struct cgx_link_user_info *linfo); int cgx_lmac_linkup_start(void *cgxd); -int cgx_get_mkex_prfl_info(u64 *addr, u64 *size); +int cgx_get_fwdata_base(u64 *base); +int cgx_set_fec(u64 fec, int cgx_id, int lmac_id); +int cgx_set_link_mode(void *cgxd, struct cgx_set_link_mode_args args, + int cgx_id, int lmac_id); +int cgx_lmac_get_pause_frm(void *cgxd, int lmac_id, + u8 *tx_pause, u8 *rx_pause); +int cgx_lmac_set_pause_frm(void *cgxd, int lmac_id, + u8 tx_pause, u8 rx_pause); +void cgx_lmac_ptp_config(void *cgxd, int lmac_id, bool enable); +int cgx_set_link_state(void *cgxd, int lmac_id, bool enable); +int cgx_set_phy_mod_type(int mod, void *cgxd, int lmac_id); +int cgx_get_phy_mod_type(void *cgxd, int lmac_id); +int cgx_get_phy_fec_stats(void *cgxd, int lmac_id); #endif /* CGX_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx_fw_if.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx_fw_if.h index fb3ba4968a9b..37a20d66246b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx_fw_if.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx_fw_if.h @@ -43,7 +43,13 @@ enum cgx_error_type { CGX_ERR_TRAINING_FAIL, CGX_ERR_RX_EQU_FAIL, CGX_ERR_SPUX_BER_FAIL, - CGX_ERR_SPUX_RSFEC_ALGN_FAIL, /* = 22 */ + CGX_ERR_SPUX_RSFEC_ALGN_FAIL, + CGX_ERR_SPUX_MARKER_LOCK_FAIL, + CGX_ERR_SET_FEC_INVALID, + CGX_ERR_SET_FEC_FAIL, + CGX_ERR_MODULE_INVALID, + CGX_ERR_MODULE_NOT_PRESENT, + CGX_ERR_SPEED_CHANGE_INVALID, }; /* LINK speed types */ @@ -59,10 +65,42 @@ enum cgx_link_speed { CGX_LINK_25G, CGX_LINK_40G, CGX_LINK_50G, + CGX_LINK_80G, CGX_LINK_100G, CGX_LINK_SPEED_MAX, }; +enum CGX_MODE_ { + CGX_MODE_SGMII, + CGX_MODE_1000_BASEX, + CGX_MODE_QSGMII, + CGX_MODE_10G_C2C, + CGX_MODE_10G_C2M, + CGX_MODE_10G_KR, + CGX_MODE_20G_C2C, + CGX_MODE_25G_C2C, + CGX_MODE_25G_C2M, + CGX_MODE_25G_2_C2C, + CGX_MODE_25G_CR, + CGX_MODE_25G_KR, + CGX_MODE_40G_C2C, + CGX_MODE_40G_C2M, + CGX_MODE_40G_CR4, + CGX_MODE_40G_KR4, + CGX_MODE_40GAUI_C2C, + CGX_MODE_50G_C2C, + CGX_MODE_50G_C2M, + CGX_MODE_50G_4_C2C, + CGX_MODE_50G_CR, + CGX_MODE_50G_KR, + CGX_MODE_80GAUI_C2C, + CGX_MODE_100G_C2C, + CGX_MODE_100G_C2M, + CGX_MODE_100G_CR4, + CGX_MODE_100G_KR4, + CGX_MODE_MAX /* = 29 */ +}; + /* REQUEST ID types. Input to firmware */ enum cgx_cmd_id { CGX_CMD_NONE, @@ -75,11 +113,25 @@ enum cgx_cmd_id { CGX_CMD_INTERNAL_LBK, CGX_CMD_EXTERNAL_LBK, CGX_CMD_HIGIG, - CGX_CMD_LINK_STATE_CHANGE, + CGX_CMD_LINK_STAT_CHANGE, CGX_CMD_MODE_CHANGE, /* hot plug support */ CGX_CMD_INTF_SHUTDOWN, CGX_CMD_GET_MKEX_PRFL_SIZE, - CGX_CMD_GET_MKEX_PRFL_ADDR + CGX_CMD_GET_MKEX_PRFL_ADDR, + CGX_CMD_GET_FWD_BASE, /* get base address of shared FW data */ + CGX_CMD_GET_LINK_MODES, /* Supported Link Modes */ + CGX_CMD_SET_LINK_MODE, + CGX_CMD_GET_SUPPORTED_FEC, + CGX_CMD_SET_FEC, + CGX_CMD_GET_AN, + CGX_CMD_SET_AN, + CGX_CMD_GET_ADV_LINK_MODES, + CGX_CMD_GET_ADV_FEC, + CGX_CMD_GET_PHY_MOD_TYPE, /* line-side modulation type: NRZ or PAM4 */ + CGX_CMD_SET_PHY_MOD_TYPE, + CGX_CMD_PRBS, + CGX_CMD_DISPLAY_EYE, + CGX_CMD_GET_PHY_FEC_STATS, }; /* async event ids */ @@ -148,6 +200,10 @@ enum cgx_cmd_own { * CGX_STAT_SUCCESS */ #define RESP_MKEX_PRFL_ADDR GENMASK_ULL(63, 9) +/* Response to cmd ID as CGX_CMD_GET_FWD_BASE with cmd status as + * CGX_STAT_SUCCESS + */ +#define RESP_FWD_BASE GENMASK_ULL(56, 9) /* Response to cmd ID - CGX_CMD_LINK_BRING_UP/DOWN, event ID CGX_EVT_LINK_CHANGE * status can be either CGX_STAT_FAIL or CGX_STAT_SUCCESS @@ -165,13 +221,19 @@ struct cgx_lnk_sts { uint64_t full_duplex:1; uint64_t speed:4; /* cgx_link_speed */ uint64_t err_type:10; - uint64_t reserved2:39; + uint64_t an:1; /* AN supported or not */ + uint64_t fec:2; /* FEC type if enabled, if not 0 */ + uint64_t port:8; + uint64_t reserved2:28; }; #define RESP_LINKSTAT_UP GENMASK_ULL(9, 9) #define RESP_LINKSTAT_FDUPLEX GENMASK_ULL(10, 10) #define RESP_LINKSTAT_SPEED GENMASK_ULL(14, 11) #define RESP_LINKSTAT_ERRTYPE GENMASK_ULL(24, 15) +#define RESP_LINKSTAT_AN GENMASK_ULL(25, 25) +#define RESP_LINKSTAT_FEC GENMASK_ULL(27, 26) +#define RESP_LINKSTAT_PORT GENMASK_ULL(35, 28) /* scratchx(1) CSR used for non-secure SW->ATF communication * This CSR acts as a command register @@ -192,5 +254,18 @@ struct cgx_lnk_sts { #define CMDLINKCHANGE_LINKUP BIT_ULL(8) #define CMDLINKCHANGE_FULLDPLX BIT_ULL(9) #define CMDLINKCHANGE_SPEED GENMASK_ULL(13, 10) +#define CMDSETFEC GENMASK_ULL(9, 8) +/* command argument to be passed for cmd ID - CGX_CMD_MODE_CHANGE */ +#define CMDMODECHANGE_SPEED GENMASK_ULL(11, 8) +#define CMDMODECHANGE_DUPLEX GENMASK_ULL(12, 12) +#define CMDMODECHANGE_AN GENMASK_ULL(13, 13) +#define CMDMODECHANGE_PORT GENMASK_ULL(21, 14) +#define CMDMODECHANGE_FLAGS GENMASK_ULL(63, 22) + +/* command argument to be passed for cmd ID - CGX_CMD_SET_PHY_MOD_TYPE */ +#define CMDSETPHYMODTYPE GENMASK_ULL(8, 8) + +/* response to cmd ID - RESP_GETPHYMODTYPE */ +#define RESP_GETPHYMODTYPE GENMASK_ULL(9, 9) #endif /* __CGX_FW_INTF_H__ */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/common.h b/drivers/net/ethernet/marvell/octeontx2/af/common.h index e332e82fc066..4f8893ade710 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/common.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/common.h @@ -157,6 +157,8 @@ enum nix_scheduler { #define NIX_RX_ACTIONOP_UCAST_IPSEC (0x2ull) #define NIX_RX_ACTIONOP_MCAST (0x3ull) #define NIX_RX_ACTIONOP_RSS (0x4ull) +/* Use action set in default unicast entry */ +#define NIX_RX_ACTION_DEFAULT (0xfull) /* NIX TX action operation*/ #define NIX_TX_ACTIONOP_DROP (0x0ull) @@ -174,12 +176,15 @@ enum nix_scheduler { #define NIX_INTF_TYPE_CGX 0 #define NIX_INTF_TYPE_LBK 1 +#define NIX_INTF_TYPE_SDP 2 #define MAX_LMAC_PKIND 12 #define NIX_LINK_CGX_LMAC(a, b) (0 + 4 * (a) + (b)) #define NIX_LINK_LBK(a) (12 + (a)) #define NIX_CHAN_CGX_LMAC_CHX(a, b, c) (0x800 + 0x100 * (a) + 0x10 * (b) + (c)) #define NIX_CHAN_LBK_CHX(a, b) (0 + 0x100 * (a) + (b)) +#define NIX_CHAN_SDP_CH_START (0x700ull) +#define NIX_CHAN_SDP_CHX(a) (NIX_CHAN_SDP_CH_START + (a)) /* NIX LSO format indices. * As of now TSO is the only one using, so statically assigning indices. @@ -196,4 +201,20 @@ enum nix_scheduler { #define DEFAULT_RSS_CONTEXT_GROUP 0 #define MAX_RSS_INDIR_TBL_SIZE 256 /* 1 << Max adder bits */ +/* NDC info */ +enum ndc_idx_e { + NIX0_RX = 0x0, + NIX0_TX = 0x1, + NPA0_U = 0x2, +}; + +enum ndc_ctype_e { + CACHING = 0x0, + BYPASS = 0x1, +}; + +#define NDC_MAX_PORT 6 +#define NDC_READ_TRANS 0 +#define NDC_WRITE_TRANS 1 + #endif /* COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.c b/drivers/net/ethernet/marvell/octeontx2/af/mbox.c index d6f9ed8ea966..387e33fa417a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.c @@ -19,17 +19,20 @@ static const u16 msgs_offset = ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN); void otx2_mbox_reset(struct otx2_mbox *mbox, int devid) { + void *hw_mbase = mbox->hwbase + (devid * MBOX_SIZE); struct otx2_mbox_dev *mdev = &mbox->dev[devid]; struct mbox_hdr *tx_hdr, *rx_hdr; - tx_hdr = mdev->mbase + mbox->tx_start; - rx_hdr = mdev->mbase + mbox->rx_start; + tx_hdr = hw_mbase + mbox->tx_start; + rx_hdr = hw_mbase + mbox->rx_start; spin_lock(&mdev->mbox_lock); mdev->msg_size = 0; mdev->rsp_size = 0; tx_hdr->num_msgs = 0; + tx_hdr->msg_size = 0; rx_hdr->num_msgs = 0; + rx_hdr->msg_size = 0; spin_unlock(&mdev->mbox_lock); } EXPORT_SYMBOL(otx2_mbox_reset); @@ -133,16 +136,17 @@ EXPORT_SYMBOL(otx2_mbox_init); int otx2_mbox_wait_for_rsp(struct otx2_mbox *mbox, int devid) { + unsigned long timeout = jiffies + msecs_to_jiffies(MBOX_RSP_TIMEOUT); struct otx2_mbox_dev *mdev = &mbox->dev[devid]; - int timeout = 0, sleep = 1; + struct device *sender = &mbox->pdev->dev; - while (mdev->num_msgs != mdev->msgs_acked) { - msleep(sleep); - timeout += sleep; - if (timeout >= MBOX_RSP_TIMEOUT) - return -EIO; + while (!time_after(jiffies, timeout)) { + if (mdev->num_msgs == mdev->msgs_acked) + return 0; + usleep_range(800, 1000); } - return 0; + dev_dbg(sender, "timed out while waiting for rsp\n"); + return -EIO; } EXPORT_SYMBOL(otx2_mbox_wait_for_rsp); @@ -162,13 +166,25 @@ EXPORT_SYMBOL(otx2_mbox_busy_poll_for_rsp); void otx2_mbox_msg_send(struct otx2_mbox *mbox, int devid) { + void *hw_mbase = mbox->hwbase + (devid * MBOX_SIZE); struct otx2_mbox_dev *mdev = &mbox->dev[devid]; struct mbox_hdr *tx_hdr, *rx_hdr; - tx_hdr = mdev->mbase + mbox->tx_start; - rx_hdr = mdev->mbase + mbox->rx_start; + tx_hdr = hw_mbase + mbox->tx_start; + rx_hdr = hw_mbase + mbox->rx_start; + + /* If bounce buffer is implemented copy mbox messages from + * bounce buffer to hw mbox memory. + */ + if (mdev->mbase != hw_mbase) + memcpy(hw_mbase + mbox->tx_start + msgs_offset, + mdev->mbase + mbox->tx_start + msgs_offset, + mdev->msg_size); spin_lock(&mdev->mbox_lock); + + tx_hdr->msg_size = mdev->msg_size; + /* Reset header for next messages */ mdev->msg_size = 0; mdev->rsp_size = 0; @@ -215,7 +231,7 @@ struct mbox_msghdr *otx2_mbox_alloc_msg_rsp(struct otx2_mbox *mbox, int devid, msghdr = mdev->mbase + mbox->tx_start + msgs_offset + mdev->msg_size; /* Clear the whole msg region */ - memset(msghdr, 0, sizeof(*msghdr) + size); + memset(msghdr, 0, size); /* Init message header with reset values */ msghdr->ver = OTX2_MBOX_VERSION; mdev->msg_size += size; @@ -236,8 +252,10 @@ struct mbox_msghdr *otx2_mbox_get_rsp(struct otx2_mbox *mbox, int devid, struct otx2_mbox_dev *mdev = &mbox->dev[devid]; u16 msgs; + spin_lock(&mdev->mbox_lock); + if (mdev->num_msgs != mdev->msgs_acked) - return ERR_PTR(-ENODEV); + goto error; for (msgs = 0; msgs < mdev->msgs_acked; msgs++) { struct mbox_msghdr *pmsg = mdev->mbase + imsg; @@ -245,18 +263,55 @@ struct mbox_msghdr *otx2_mbox_get_rsp(struct otx2_mbox *mbox, int devid, if (msg == pmsg) { if (pmsg->id != prsp->id) - return ERR_PTR(-ENODEV); + goto error; + spin_unlock(&mdev->mbox_lock); return prsp; } - imsg = pmsg->next_msgoff; - irsp = prsp->next_msgoff; + imsg = mbox->tx_start + pmsg->next_msgoff; + irsp = mbox->rx_start + prsp->next_msgoff; } +error: + spin_unlock(&mdev->mbox_lock); return ERR_PTR(-ENODEV); } EXPORT_SYMBOL(otx2_mbox_get_rsp); +int otx2_mbox_check_rsp_msgs(struct otx2_mbox *mbox, int devid) +{ + unsigned long ireq = mbox->tx_start + msgs_offset; + unsigned long irsp = mbox->rx_start + msgs_offset; + struct otx2_mbox_dev *mdev = &mbox->dev[devid]; + int rc = -ENODEV; + u16 msgs; + + spin_lock(&mdev->mbox_lock); + + if (mdev->num_msgs != mdev->msgs_acked) + goto exit; + + for (msgs = 0; msgs < mdev->msgs_acked; msgs++) { + struct mbox_msghdr *preq = mdev->mbase + ireq; + struct mbox_msghdr *prsp = mdev->mbase + irsp; + + if (preq->id != prsp->id) + goto exit; + if (prsp->rc) { + rc = prsp->rc; + goto exit; + } + + ireq = mbox->tx_start + preq->next_msgoff; + irsp = mbox->rx_start + prsp->next_msgoff; + } + rc = 0; +exit: + spin_unlock(&mdev->mbox_lock); + return rc; +} +EXPORT_SYMBOL(otx2_mbox_check_rsp_msgs); + int otx2_reply_invalid_msg(struct otx2_mbox *mbox, int devid, u16 pcifunc, u16 id) { diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index 76a4575d18ff..8354572fa132 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -36,7 +36,7 @@ #define INTR_MASK(pfvfs) ((pfvfs < 64) ? (BIT_ULL(pfvfs) - 1) : (~0ull)) -#define MBOX_RSP_TIMEOUT 1000 /* in ms, Time to wait for mbox response */ +#define MBOX_RSP_TIMEOUT 2000 /* Time(ms) to wait for mbox response */ #define MBOX_MSG_ALIGN 16 /* Align mbox msg start to 16bytes */ @@ -75,6 +75,7 @@ struct otx2_mbox { /* Header which preceeds all mbox messages */ struct mbox_hdr { + u64 msg_size; /* Total msgs size embedded */ u16 num_msgs; /* No of msgs embedded */ }; @@ -85,7 +86,7 @@ struct mbox_msghdr { #define OTX2_MBOX_REQ_SIG (0xdead) #define OTX2_MBOX_RSP_SIG (0xbeef) u16 sig; /* Signature, for validating corrupted msgs */ -#define OTX2_MBOX_VERSION (0x0001) +#define OTX2_MBOX_VERSION (0x0003) u16 ver; /* Version of msg's structure for this ID */ u16 next_msgoff; /* Offset of next msg within mailbox region */ int rc; /* Msg process'ed response code */ @@ -103,6 +104,7 @@ struct mbox_msghdr *otx2_mbox_alloc_msg_rsp(struct otx2_mbox *mbox, int devid, int size, int size_rsp); struct mbox_msghdr *otx2_mbox_get_rsp(struct otx2_mbox *mbox, int devid, struct mbox_msghdr *msg); +int otx2_mbox_check_rsp_msgs(struct otx2_mbox *mbox, int devid); int otx2_reply_invalid_msg(struct otx2_mbox *mbox, int devid, u16 pcifunc, u16 id); bool otx2_mbox_nonempty(struct otx2_mbox *mbox, int devid); @@ -123,8 +125,12 @@ static inline struct mbox_msghdr *otx2_mbox_alloc_msg(struct otx2_mbox *mbox, M(READY, 0x001, ready, msg_req, ready_msg_rsp) \ M(ATTACH_RESOURCES, 0x002, attach_resources, rsrc_attach, msg_rsp) \ M(DETACH_RESOURCES, 0x003, detach_resources, rsrc_detach, msg_rsp) \ -M(MSIX_OFFSET, 0x004, msix_offset, msg_req, msix_offset_rsp) \ +M(FREE_RSRC_CNT, 0x004, free_rsrc_cnt, msg_req, free_rsrcs_rsp) \ +M(MSIX_OFFSET, 0x005, msix_offset, msg_req, msix_offset_rsp) \ M(VF_FLR, 0x006, vf_flr, msg_req, msg_rsp) \ +M(PTP_OP, 0x007, ptp_op, ptp_req, ptp_rsp) \ +M(GET_HW_CAP, 0x008, get_hw_cap, msg_req, get_hw_cap_rsp) \ +M(NDC_SYNC_OP, 0x009, ndc_sync_op, ndc_sync_op, msg_rsp) \ /* CGX mbox IDs (range 0x200 - 0x3FF) */ \ M(CGX_START_RXTX, 0x200, cgx_start_rxtx, msg_req, msg_rsp) \ M(CGX_STOP_RXTX, 0x201, cgx_stop_rxtx, msg_req, msg_rsp) \ @@ -140,6 +146,28 @@ M(CGX_STOP_LINKEVENTS, 0x208, cgx_stop_linkevents, msg_req, msg_rsp) \ M(CGX_GET_LINKINFO, 0x209, cgx_get_linkinfo, msg_req, cgx_link_info_msg) \ M(CGX_INTLBK_ENABLE, 0x20A, cgx_intlbk_enable, msg_req, msg_rsp) \ M(CGX_INTLBK_DISABLE, 0x20B, cgx_intlbk_disable, msg_req, msg_rsp) \ +M(CGX_PTP_RX_ENABLE, 0x20C, cgx_ptp_rx_enable, msg_req, msg_rsp) \ +M(CGX_PTP_RX_DISABLE, 0x20D, cgx_ptp_rx_disable, msg_req, msg_rsp) \ +M(CGX_CFG_PAUSE_FRM, 0x20E, cgx_cfg_pause_frm, cgx_pause_frm_cfg, \ + cgx_pause_frm_cfg) \ +M(CGX_FW_DATA_GET, 0x20F, cgx_get_aux_link_info, msg_req, cgx_fw_data) \ +M(CGX_FEC_SET, 0x210, cgx_set_fec_param, fec_mode, fec_mode) \ +M(CGX_MAC_ADDR_ADD, 0x211, cgx_mac_addr_add, cgx_mac_addr_add_req, \ + cgx_mac_addr_add_rsp) \ +M(CGX_MAC_ADDR_DEL, 0x212, cgx_mac_addr_del, cgx_mac_addr_del_req, \ + msg_rsp) \ +M(CGX_MAC_MAX_ENTRIES_GET, 0x213, cgx_mac_max_entries_get, msg_req, \ + cgx_max_dmac_entries_get_rsp) \ +M(CGX_SET_LINK_STATE, 0x214, cgx_set_link_state, \ + cgx_set_link_state_msg, msg_rsp) \ +M(CGX_GET_PHY_MOD_TYPE, 0x215, cgx_get_phy_mod_type, msg_req, \ + cgx_phy_mod_type) \ +M(CGX_SET_PHY_MOD_TYPE, 0x216, cgx_set_phy_mod_type, cgx_phy_mod_type, \ + msg_rsp) \ +M(CGX_FEC_STATS, 0x217, cgx_fec_stats, msg_req, cgx_fec_stats_rsp) \ +M(CGX_SET_LINK_MODE, 0x218, cgx_set_link_mode, cgx_set_link_mode_req,\ + cgx_set_link_mode_rsp) \ +M(CGX_GET_PHY_FEC_STATS, 0x219, cgx_get_phy_fec_stats, msg_req, msg_rsp) \ /* NPA mbox IDs (range 0x400 - 0x5FF) */ \ M(NPA_LF_ALLOC, 0x400, npa_lf_alloc, \ npa_lf_alloc_req, npa_lf_alloc_rsp) \ @@ -147,8 +175,42 @@ M(NPA_LF_FREE, 0x401, npa_lf_free, msg_req, msg_rsp) \ M(NPA_AQ_ENQ, 0x402, npa_aq_enq, npa_aq_enq_req, npa_aq_enq_rsp) \ M(NPA_HWCTX_DISABLE, 0x403, npa_hwctx_disable, hwctx_disable_req, msg_rsp)\ /* SSO/SSOW mbox IDs (range 0x600 - 0x7FF) */ \ +M(SSO_LF_ALLOC, 0x600, sso_lf_alloc, \ + sso_lf_alloc_req, sso_lf_alloc_rsp) \ +M(SSO_LF_FREE, 0x601, sso_lf_free, \ + sso_lf_free_req, msg_rsp) \ +M(SSOW_LF_ALLOC, 0x602, ssow_lf_alloc, \ + ssow_lf_alloc_req, msg_rsp) \ +M(SSOW_LF_FREE, 0x603, ssow_lf_free, \ + ssow_lf_free_req, msg_rsp) \ +M(SSO_HW_SETCONFIG, 0x604, sso_hw_setconfig, \ + sso_hw_setconfig, msg_rsp) \ +M(SSO_GRP_SET_PRIORITY, 0x605, sso_grp_set_priority, \ + sso_grp_priority, msg_rsp) \ +M(SSO_GRP_GET_PRIORITY, 0x606, sso_grp_get_priority, \ + sso_info_req, sso_grp_priority) \ +M(SSO_WS_CACHE_INV, 0x607, sso_ws_cache_inv, msg_req, msg_rsp) \ +M(SSO_GRP_QOS_CONFIG, 0x608, sso_grp_qos_config, sso_grp_qos_cfg, msg_rsp)\ +M(SSO_GRP_GET_STATS, 0x609, sso_grp_get_stats, sso_info_req, sso_grp_stats)\ +M(SSO_HWS_GET_STATS, 0x610, sso_hws_get_stats, sso_info_req, sso_hws_stats)\ /* TIM mbox IDs (range 0x800 - 0x9FF) */ \ +M(TIM_LF_ALLOC, 0x800, tim_lf_alloc, \ + tim_lf_alloc_req, tim_lf_alloc_rsp) \ +M(TIM_LF_FREE, 0x801, tim_lf_free, tim_ring_req, msg_rsp) \ +M(TIM_CONFIG_RING, 0x802, tim_config_ring, tim_config_req, msg_rsp)\ +M(TIM_ENABLE_RING, 0x803, tim_enable_ring, tim_ring_req, tim_enable_rsp)\ +M(TIM_DISABLE_RING, 0x804, tim_disable_ring, tim_ring_req, msg_rsp) \ /* CPT mbox IDs (range 0xA00 - 0xBFF) */ \ +M(CPT_LF_ALLOC, 0xA00, cpt_lf_alloc, cpt_lf_alloc_req_msg, \ + cpt_lf_alloc_rsp_msg) \ +M(CPT_LF_FREE, 0xA01, cpt_lf_free, msg_req, msg_rsp) \ +M(CPT_RD_WR_REGISTER, 0xA02, cpt_rd_wr_register, cpt_rd_wr_reg_msg, \ + cpt_rd_wr_reg_msg) \ +M(CPT_SET_CRYPTO_GRP, 0xA03, cpt_set_crypto_grp, \ + cpt_set_crypto_grp_req_msg, \ + cpt_set_crypto_grp_req_msg) \ +M(CPT_INLINE_IPSEC_CFG, 0xA04, cpt_inline_ipsec_cfg, \ + cpt_inline_ipsec_cfg_msg, msg_rsp) \ /* NPC mbox IDs (range 0x6000 - 0x7FFF) */ \ M(NPC_MCAM_ALLOC_ENTRY, 0x6000, npc_mcam_alloc_entry, npc_mcam_alloc_entry_req,\ npc_mcam_alloc_entry_rsp) \ @@ -179,10 +241,19 @@ M(NPC_MCAM_ALLOC_AND_WRITE_ENTRY, 0x600b, npc_mcam_alloc_and_write_entry, \ npc_mcam_alloc_and_write_entry_rsp) \ M(NPC_GET_KEX_CFG, 0x600c, npc_get_kex_cfg, \ msg_req, npc_get_kex_cfg_rsp) \ +M(NPC_INSTALL_FLOW, 0x600d, npc_install_flow, \ + npc_install_flow_req, npc_install_flow_rsp) \ +M(NPC_DELETE_FLOW, 0x600e, npc_delete_flow, \ + npc_delete_flow_req, msg_rsp) \ +M(NPC_MCAM_READ_ENTRY, 0x600f, npc_mcam_read_entry, \ + npc_mcam_read_entry_req, \ + npc_mcam_read_entry_rsp) \ +M(NPC_SET_PKIND, 0x6010, npc_set_pkind, \ + npc_set_pkind, msg_rsp) \ /* NIX mbox IDs (range 0x8000 - 0xFFFF) */ \ M(NIX_LF_ALLOC, 0x8000, nix_lf_alloc, \ nix_lf_alloc_req, nix_lf_alloc_rsp) \ -M(NIX_LF_FREE, 0x8001, nix_lf_free, msg_req, msg_rsp) \ +M(NIX_LF_FREE, 0x8001, nix_lf_free, nix_lf_free_req, msg_rsp) \ M(NIX_AQ_ENQ, 0x8002, nix_aq_enq, nix_aq_enq_req, nix_aq_enq_rsp) \ M(NIX_HWCTX_DISABLE, 0x8003, nix_hwctx_disable, \ hwctx_disable_req, msg_rsp) \ @@ -191,7 +262,8 @@ M(NIX_TXSCH_ALLOC, 0x8004, nix_txsch_alloc, \ M(NIX_TXSCH_FREE, 0x8005, nix_txsch_free, nix_txsch_free_req, msg_rsp) \ M(NIX_TXSCHQ_CFG, 0x8006, nix_txschq_cfg, nix_txschq_config, msg_rsp) \ M(NIX_STATS_RST, 0x8007, nix_stats_rst, msg_req, msg_rsp) \ -M(NIX_VTAG_CFG, 0x8008, nix_vtag_cfg, nix_vtag_config, msg_rsp) \ +M(NIX_VTAG_CFG, 0x8008, nix_vtag_cfg, nix_vtag_config, \ + nix_vtag_config_rsp) \ M(NIX_RSS_FLOWKEY_CFG, 0x8009, nix_rss_flowkey_cfg, \ nix_rss_flowkey_cfg, \ nix_rss_flowkey_cfg_rsp) \ @@ -207,11 +279,22 @@ M(NIX_SET_RX_CFG, 0x8010, nix_set_rx_cfg, nix_rx_cfg, msg_rsp) \ M(NIX_LSO_FORMAT_CFG, 0x8011, nix_lso_format_cfg, \ nix_lso_format_cfg, \ nix_lso_format_cfg_rsp) \ -M(NIX_RXVLAN_ALLOC, 0x8012, nix_rxvlan_alloc, msg_req, msg_rsp) +M(NIX_LF_PTP_TX_ENABLE, 0x8013, nix_lf_ptp_tx_enable, msg_req, msg_rsp) \ +M(NIX_LF_PTP_TX_DISABLE, 0x8014, nix_lf_ptp_tx_disable, msg_req, msg_rsp) \ +M(NIX_SET_VLAN_TPID, 0x8015, nix_set_vlan_tpid, nix_set_vlan_tpid, msg_rsp) \ +M(NIX_BP_ENABLE, 0x8016, nix_bp_enable, nix_bp_cfg_req, \ + nix_bp_cfg_rsp) \ +M(NIX_BP_DISABLE, 0x8017, nix_bp_disable, nix_bp_cfg_req, msg_rsp) \ +M(NIX_GET_MAC_ADDR, 0x8018, nix_get_mac_addr, msg_req, nix_get_mac_addr_rsp) \ +M(NIX_INLINE_IPSEC_CFG, 0x8019, nix_inline_ipsec_cfg, \ + nix_inline_ipsec_cfg, msg_rsp) \ +M(NIX_INLINE_IPSEC_LF_CFG, 0x801a, nix_inline_ipsec_lf_cfg, \ + nix_inline_ipsec_lf_cfg, msg_rsp) /* Messages initiated by AF (range 0xC00 - 0xDFF) */ #define MBOX_UP_CGX_MESSAGES \ -M(CGX_LINK_EVENT, 0xC00, cgx_link_event, cgx_link_info_msg, msg_rsp) +M(CGX_LINK_EVENT, 0xC00, cgx_link_event, cgx_link_info_msg, msg_rsp) \ +M(CGX_PTP_RX_INFO, 0xC01, cgx_ptp_rx_info, cgx_ptp_rx_info_msg, msg_rsp) enum { #define M(_name, _id, _1, _2, _3) MBOX_MSG_ ## _name = _id, @@ -247,7 +330,8 @@ enum rvu_af_status { struct ready_msg_rsp { struct mbox_msghdr hdr; - u16 sclk_feq; /* SCLK frequency */ + u16 sclk_freq; /* SCLK frequency (in MHz) */ + u16 rclk_freq; /* RCLK frequency (in MHz) */ }; /* Structure for requesting resource provisioning. @@ -283,6 +367,21 @@ struct rsrc_detach { u8 cptlfs:1; }; +/* + * Number of resources available to the caller. + * In reply to MBOX_MSG_FREE_RSRC_CNT. + */ +struct free_rsrcs_rsp { + struct mbox_msghdr hdr; + u16 schq[NIX_TXSCH_LVL_CNT]; + u16 sso; + u16 tim; + u16 ssow; + u16 cpt; + u8 npa; + u8 nix; +}; + #define MSIX_VECTOR_INVALID 0xFFFF #define MAX_RVU_BLKLF_CNT 256 @@ -310,6 +409,11 @@ struct cgx_stats_rsp { u64 tx_stats[CGX_TX_STATS_COUNT]; }; +struct cgx_fec_stats_rsp { + struct mbox_msghdr hdr; + u64 fec_corr_blks; + u64 fec_uncorr_blks; +}; /* Structure for requesting the operation for * setting/getting mac address in the CGX interface */ @@ -318,11 +422,46 @@ struct cgx_mac_addr_set_or_get { u8 mac_addr[ETH_ALEN]; }; +/* Structure for requesting the operation to + * add DMAC filter entry into CGX interface + */ +struct cgx_mac_addr_add_req { + struct mbox_msghdr hdr; + u8 mac_addr[ETH_ALEN]; +}; + +/* Structure for response against the operation to + * add DMAC filter entry into CGX interface + */ +struct cgx_mac_addr_add_rsp { + struct mbox_msghdr hdr; + u8 index; +}; + +/* Structure for requesting the operation to + * delete DMAC filter entry from CGX interface + */ +struct cgx_mac_addr_del_req { + struct mbox_msghdr hdr; + u8 index; +}; + +/* Structure for response against the operation to + * get maximum supported DMAC filter entries + */ +struct cgx_max_dmac_entries_get_rsp { + struct mbox_msghdr hdr; + u8 max_dmac_filters; +}; + struct cgx_link_user_info { uint64_t link_up:1; uint64_t full_duplex:1; uint64_t lmac_type_id:4; uint64_t speed:20; /* speed in Mbps */ + uint64_t an:1; /* AN supported or not */ + uint64_t fec:2; /* FEC type if enabled else 0 */ + uint64_t port:8; #define LMACTYPE_STR_LEN 16 char lmac_type[LMACTYPE_STR_LEN]; }; @@ -332,6 +471,111 @@ struct cgx_link_info_msg { struct cgx_link_user_info link_info; }; +struct cgx_ptp_rx_info_msg { + struct mbox_msghdr hdr; + u8 ptp_en; +}; + +struct cgx_pause_frm_cfg { + struct mbox_msghdr hdr; + u8 set; + /* set = 1 if the request is to config pause frames */ + /* set = 0 if the request is to fetch pause frames config */ + u8 rx_pause; + u8 tx_pause; +}; + +struct sfp_eeprom_s { +#define SFP_EEPROM_SIZE 256 + u16 sff_id; + u8 buf[SFP_EEPROM_SIZE]; + u64 reserved; +}; + +enum fec_type { + OTX2_FEC_NONE, + OTX2_FEC_BASER, + OTX2_FEC_RS, +}; + +struct phy_s { + struct { + u64 can_change_mod_type : 1; + u64 mod_type : 1; + u64 has_fec_stats : 1; + } misc; + struct fec_stats_s { + u32 rsfec_corr_cws; + u32 rsfec_uncorr_cws; + u32 brfec_corr_blks; + u32 brfec_uncorr_blks; + } fec_stats; +}; + +struct cgx_lmac_fwdata_s { + u16 rw_valid; + u64 supported_fec; + u64 supported_an; + u64 supported_link_modes; + /* only applicable if AN is supported */ + u64 advertised_fec; + u64 advertised_link_modes; + /* Only applicable if SFP/QSFP slot is present */ + struct sfp_eeprom_s sfp_eeprom; + struct phy_s phy; +#define LMAC_FWDATA_RESERVED_MEM 1021 + u64 reserved[LMAC_FWDATA_RESERVED_MEM]; +}; + +struct cgx_fw_data { + struct mbox_msghdr hdr; + struct cgx_lmac_fwdata_s fwdata; +}; + +struct fec_mode { + struct mbox_msghdr hdr; + int fec; +}; + +struct cgx_set_link_state_msg { + struct mbox_msghdr hdr; + u8 enable; /* '1' for link up, '0' for link down */ +}; + +struct cgx_phy_mod_type { + struct mbox_msghdr hdr; + int mod; +}; + +struct npc_set_pkind { + struct mbox_msghdr hdr; +#define OTX2_PRIV_FLAGS_DEFAULT BIT_ULL(0) +#define OTX2_PRIV_FLAGS_EDSA BIT_ULL(1) +#define OTX2_PRIV_FLAGS_HIGIG BIT_ULL(2) +#define OTX2_PRIV_FLAGS_CUSTOM BIT_ULL(63) + u64 mode; +#define PKIND_TX BIT_ULL(0) +#define PKIND_RX BIT_ULL(1) + u8 dir; + u8 pkind; /* valid only in case custom flag */ +}; +struct cgx_set_link_mode_args { + u32 speed; + u8 duplex; + u8 an; + u8 ports; + u64 mode; +}; + +struct cgx_set_link_mode_req { + struct mbox_msghdr hdr; + struct cgx_set_link_mode_args args; +}; + +struct cgx_set_link_mode_rsp { + struct mbox_msghdr hdr; + int status; +}; /* NPA mbox message formats */ /* NPA mailbox error codes @@ -352,6 +596,7 @@ struct npa_lf_alloc_req { int node; int aura_sz; /* No of auras */ u32 nr_pools; /* No of pools */ + u64 way_mask; }; struct npa_lf_alloc_rsp { @@ -427,6 +672,19 @@ enum nix_af_status { NIX_AF_ERR_LSO_CFG_FAIL = -418, NIX_AF_INVAL_NPA_PF_FUNC = -419, NIX_AF_INVAL_SSO_PF_FUNC = -420, + NIX_AF_ERR_TX_VTAG_NOSPC = -421, +}; + +/* For NIX RX vtag action */ +enum nix_rx_vtag0_type { + NIX_AF_LFX_RX_VTAG_TYPE0, /* reserved for rx vlan offload */ + NIX_AF_LFX_RX_VTAG_TYPE1, + NIX_AF_LFX_RX_VTAG_TYPE2, + NIX_AF_LFX_RX_VTAG_TYPE3, + NIX_AF_LFX_RX_VTAG_TYPE4, + NIX_AF_LFX_RX_VTAG_TYPE5, + NIX_AF_LFX_RX_VTAG_TYPE6, + NIX_AF_LFX_RX_VTAG_TYPE7, }; /* For NIX LF context alloc and init */ @@ -442,6 +700,7 @@ struct nix_lf_alloc_req { u16 npa_func; u16 sso_func; u64 rx_cfg; /* See NIX_AF_LF(0..127)_RX_CFG */ + u64 way_mask; }; struct nix_lf_alloc_rsp { @@ -458,6 +717,13 @@ struct nix_lf_alloc_rsp { u8 lf_tx_stats; /* NIX_AF_CONST1::LF_TX_STATS */ u16 cints; /* NIX_AF_CONST2::CINTS */ u16 qints; /* NIX_AF_CONST2::QINTS */ + u8 hw_rx_tstamp_en; +}; + +struct nix_lf_free_req { + struct mbox_msghdr hdr; +#define NIX_LF_DISABLE_FLOWS 0x1 + u64 flags; }; /* NIX AQ enqueue msg */ @@ -512,6 +778,9 @@ struct nix_txsch_alloc_rsp { /* Scheduler queue list allocated at each level */ u16 schq_contig_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; u16 schq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; + u8 aggr_level; /* Traffic aggregation scheduler level */ + u8 aggr_lvl_rr_prio; /* Aggregation lvl's RR_PRIO config */ + u8 link_cfg_lvl; /* LINKX_CFG CSRs mapped to TL3 or TL2's index ? */ }; struct nix_txsch_free_req { @@ -547,14 +816,40 @@ struct nix_vtag_config { union { /* valid when cfg_type is '0' */ struct { - /* tx vlan0 tag(C-VLAN) */ - u64 vlan0; - /* tx vlan1 tag(S-VLAN) */ - u64 vlan1; - /* insert tx vlan tag */ - u8 insert_vlan :1; - /* insert tx double vlan tag */ - u8 double_vlan :1; + u64 vtag0; + u64 vtag1; + + /* cfg_vtag0 & cfg_vtag1 fields are valid + * when free_vtag0 & free_vtag1 are '0's. + */ + /* cfg_vtag0 = 1 to configure vtag0 */ + u8 cfg_vtag0 :1; + /* cfg_vtag1 = 1 to configure vtag1 */ + u8 cfg_vtag1 :1; + + /* vtag0_idx & vtag1_idx are only valid when + * both cfg_vtag0 & cfg_vtag1 are '0's, + * these fields are used along with free_vtag0 + * & free_vtag1 to free the nix lf's tx_vlan + * configuration. + * + * Denotes the indices of tx_vtag def registers + * that needs to be cleared and freed. + */ + int vtag0_idx; + int vtag1_idx; + + /* free_vtag0 & free_vtag1 fields are valid + * when cfg_vtag0 & cfg_vtag1 are '0's. + */ + /* free_vtag0 = 1 clears vtag0 configuration + * vtag0_idx denotes the index to be cleared. + */ + u8 free_vtag0 :1; + /* free_vtag1 = 1 clears vtag1 configuration + * vtag1_idx denotes the index to be cleared. + */ + u8 free_vtag1 :1; } tx; /* valid when cfg_type is '1' */ @@ -569,6 +864,17 @@ struct nix_vtag_config { }; }; +struct nix_vtag_config_rsp { + struct mbox_msghdr hdr; + int vtag0_idx; + int vtag1_idx; + /* Indices of tx_vtag def registers used to configure + * tx vtag0 & vtag1 headers, these indices are valid + * when nix_vtag_config mbox requested for vtag0 and/ + * or vtag1 configuration. + */ +}; + struct nix_rss_flowkey_cfg { struct mbox_msghdr hdr; int mcam_index; /* MCAM entry index to modify */ @@ -578,6 +884,18 @@ struct nix_rss_flowkey_cfg { #define NIX_FLOW_KEY_TYPE_TCP BIT(3) #define NIX_FLOW_KEY_TYPE_UDP BIT(4) #define NIX_FLOW_KEY_TYPE_SCTP BIT(5) +#define NIX_FLOW_KEY_TYPE_NVGRE BIT(6) +#define NIX_FLOW_KEY_TYPE_VXLAN BIT(7) +#define NIX_FLOW_KEY_TYPE_GENEVE BIT(8) +#define NIX_FLOW_KEY_TYPE_ETH_DMAC BIT(9) +#define NIX_FLOW_KEY_TYPE_IPV6_EXT BIT(10) +#define NIX_FLOW_KEY_TYPE_GTPU BIT(11) +#define NIX_FLOW_KEY_TYPE_INNR_IPV4 BIT(12) +#define NIX_FLOW_KEY_TYPE_INNR_IPV6 BIT(13) +#define NIX_FLOW_KEY_TYPE_INNR_TCP BIT(14) +#define NIX_FLOW_KEY_TYPE_INNR_UDP BIT(15) +#define NIX_FLOW_KEY_TYPE_INNR_SCTP BIT(16) +#define NIX_FLOW_KEY_TYPE_INNR_ETH_DMAC BIT(17) u32 flowkey_cfg; /* Flowkey types selected */ u8 group; /* RSS context or group */ }; @@ -592,6 +910,11 @@ struct nix_set_mac_addr { u8 mac_addr[ETH_ALEN]; /* MAC address to be set for this pcifunc */ }; +struct nix_get_mac_addr_rsp { + struct mbox_msghdr hdr; + u8 mac_addr[ETH_ALEN]; +}; + struct nix_mark_format_cfg { struct mbox_msghdr hdr; u8 offset; @@ -644,6 +967,167 @@ struct nix_lso_format_cfg_rsp { u8 lso_format_idx; }; +struct nix_set_vlan_tpid { + struct mbox_msghdr hdr; +#define NIX_VLAN_TYPE_INNER 0 +#define NIX_VLAN_TYPE_OUTER 1 + u8 vlan_type; + u16 tpid; +}; + +struct nix_bp_cfg_req { + struct mbox_msghdr hdr; + u16 chan_base; /* Starting channel number */ + u8 chan_cnt; /* Number of channels */ + u8 bpid_per_chan; + /* bpid_per_chan = 0 assigns single bp id for range of channels */ + /* bpid_per_chan = 1 assigns separate bp id for each channel */ +}; + +/* PF can be mapped to either CGX or LBK interface, + * so maximum 64 channels are possible. + */ +#define NIX_MAX_BPID_CHAN 64 +struct nix_bp_cfg_rsp { + struct mbox_msghdr hdr; + u16 chan_bpid[NIX_MAX_BPID_CHAN]; /* Channel and bpid mapping */ + u8 chan_cnt; /* Number of channel for which bpids are assigned */ +}; + +/* Global NIX inline IPSec configuration */ +struct nix_inline_ipsec_cfg { + struct mbox_msghdr hdr; + u32 cpt_credit; + struct { + u8 egrp; + u8 opcode; + } gen_cfg; + struct { + u16 cpt_pf_func; + u8 cpt_slot; + } inst_qsel; + u8 enable; +}; + +/* Per NIX LF inline IPSec configuration */ +struct nix_inline_ipsec_lf_cfg { + struct mbox_msghdr hdr; + u64 sa_base_addr; + struct { + u32 tag_const; + u16 lenm1_max; + u8 sa_pow2_size; + u8 tt; + } ipsec_cfg0; + struct { + u32 sa_idx_max; + u8 sa_idx_w; + } ipsec_cfg1; + u8 enable; +}; + +/* SSO mailbox error codes + * Range 501 - 600. + */ +enum sso_af_status { + SSO_AF_ERR_PARAM = -501, + SSO_AF_ERR_LF_INVALID = -502, + SSO_AF_ERR_AF_LF_ALLOC = -503, + SSO_AF_ERR_GRP_EBUSY = -504, + SSO_AF_INVAL_NPA_PF_FUNC = -505, +}; + +struct sso_lf_alloc_req { + struct mbox_msghdr hdr; + int node; + u16 hwgrps; +}; + +struct sso_lf_alloc_rsp { + struct mbox_msghdr hdr; + u32 xaq_buf_size; + u32 xaq_wq_entries; + u32 in_unit_entries; + u16 hwgrps; +}; + +struct sso_lf_free_req { + struct mbox_msghdr hdr; + int node; + u16 hwgrps; +}; + +struct sso_hw_setconfig { + struct mbox_msghdr hdr; + u32 npa_aura_id; + u16 npa_pf_func; + u16 hwgrps; +}; + +struct sso_info_req { + struct mbox_msghdr hdr; + union { + u16 grp; + u16 hws; + }; +}; + +struct sso_grp_priority { + struct mbox_msghdr hdr; + u16 grp; + u8 priority; + u8 affinity; + u8 weight; +}; + +/* SSOW mailbox error codes + * Range 601 - 700. + */ +enum ssow_af_status { + SSOW_AF_ERR_PARAM = -601, + SSOW_AF_ERR_LF_INVALID = -602, + SSOW_AF_ERR_AF_LF_ALLOC = -603, +}; + +struct ssow_lf_alloc_req { + struct mbox_msghdr hdr; + int node; + u16 hws; +}; + +struct ssow_lf_free_req { + struct mbox_msghdr hdr; + int node; + u16 hws; +}; + +struct sso_grp_qos_cfg { + struct mbox_msghdr hdr; + u16 grp; + u32 xaq_limit; + u16 taq_thr; + u16 iaq_thr; +}; + +struct sso_grp_stats { + struct mbox_msghdr hdr; + u16 grp; + u64 ws_pc; + u64 ext_pc; + u64 wa_pc; + u64 ts_pc; + u64 ds_pc; + u64 dq_pc; + u64 aw_status; + u64 page_cnt; +}; + +struct sso_hws_stats { + struct mbox_msghdr hdr; + u16 hws; + u64 arbitration; +}; + /* NPC mbox message structs */ #define NPC_MCAM_ENTRY_INVALID 0xFFFF @@ -792,4 +1276,253 @@ struct npc_get_kex_cfg_rsp { u8 mkex_pfl_name[MKEX_NAME_LEN]; }; +enum ptp_op { + PTP_OP_ADJFINE = 0, /* adjfine(req.scaled_ppm); */ + PTP_OP_GET_CLOCK = 1, /* rsp.clk = get_clock() */ +}; + +struct ptp_req { + struct mbox_msghdr hdr; + u8 op; + s64 scaled_ppm; + u8 is_pmu; +}; + +struct ptp_rsp { + struct mbox_msghdr hdr; + u64 clk; + u64 tsc; +}; + +enum header_fields { + NPC_DMAC, + NPC_SMAC, + NPC_ETYPE, + NPC_OUTER_VID, + NPC_TOS, + NPC_SIP_IPV4, + NPC_DIP_IPV4, + NPC_SIP_IPV6, + NPC_DIP_IPV6, + NPC_SPORT_TCP, + NPC_DPORT_TCP, + NPC_SPORT_UDP, + NPC_DPORT_UDP, + NPC_HEADER_FIELDS_MAX, +}; + +struct flow_msg { + unsigned char dmac[6]; + unsigned char smac[6]; + __be16 etype; + __be16 vlan_etype; + __be16 vlan_tci; + union { + __be32 ip4src; + __be32 ip6src[4]; + }; + union { + __be32 ip4dst; + __be32 ip6dst[4]; + }; + u8 tos; + u8 ip_ver; + u8 ip_proto; + u8 tc; + __be16 sport; + __be16 dport; +}; + +struct npc_install_flow_req { + struct mbox_msghdr hdr; + struct flow_msg packet; + struct flow_msg mask; + u64 features; + u16 entry; + u16 channel; + u8 intf; + u8 set_cntr; /* If counter is available set counter for this entry ? */ + u8 default_rule; + u8 append; /* overwrite(0) or append(1) flow to default rule? */ + u16 vf; + /* action */ + u32 index; + u16 match_id; + u8 flow_key_alg; + u8 op; + /* vtag rx action */ + u8 vtag0_type; + u8 vtag0_valid; + u8 vtag1_type; + u8 vtag1_valid; + /* vtag tx action */ + u16 vtag0_def; + u8 vtag0_op; + u16 vtag1_def; + u8 vtag1_op; +}; + +struct npc_install_flow_rsp { + struct mbox_msghdr hdr; + int counter; /* negative if no counter else counter number */ +}; + +struct npc_delete_flow_req { + struct mbox_msghdr hdr; + u16 entry; + u16 start;/*Disable range of entries */ + u16 end; + u8 all; /* PF + VFs */ +}; + +struct npc_mcam_read_entry_req { + struct mbox_msghdr hdr; + u16 entry; /* MCAM entry to read */ +}; + +struct npc_mcam_read_entry_rsp { + struct mbox_msghdr hdr; + struct mcam_entry entry_data; + u8 intf; + u8 enable; +}; + +/* TIM mailbox error codes + * Range 801 - 900. + */ +enum tim_af_status { + TIM_AF_NO_RINGS_LEFT = -801, + TIM_AF_INVALID_NPA_PF_FUNC = -802, + TIM_AF_INVALID_SSO_PF_FUNC = -803, + TIM_AF_RING_STILL_RUNNING = -804, + TIM_AF_LF_INVALID = -805, + TIM_AF_CSIZE_NOT_ALIGNED = -806, + TIM_AF_CSIZE_TOO_SMALL = -807, + TIM_AF_CSIZE_TOO_BIG = -808, + TIM_AF_INTERVAL_TOO_SMALL = -809, + TIM_AF_INVALID_BIG_ENDIAN_VALUE = -810, + TIM_AF_INVALID_CLOCK_SOURCE = -811, + TIM_AF_GPIO_CLK_SRC_NOT_ENABLED = -812, + TIM_AF_INVALID_BSIZE = -813, + TIM_AF_INVALID_ENABLE_PERIODIC = -814, + TIM_AF_INVALID_ENABLE_DONTFREE = -815, + TIM_AF_ENA_DONTFRE_NSET_PERIODIC = -816, + TIM_AF_RING_ALREADY_DISABLED = -817, +}; + +enum tim_clk_srcs { + TIM_CLK_SRCS_TENNS = 0, + TIM_CLK_SRCS_GPIO = 1, + TIM_CLK_SRCS_GTI = 2, + TIM_CLK_SRCS_PTP = 3, + TIM_CLK_SRSC_INVALID, +}; + +enum tim_gpio_edge { + TIM_GPIO_NO_EDGE = 0, + TIM_GPIO_LTOH_TRANS = 1, + TIM_GPIO_HTOL_TRANS = 2, + TIM_GPIO_BOTH_TRANS = 3, + TIM_GPIO_INVALID, +}; + +struct tim_lf_alloc_req { + struct mbox_msghdr hdr; + u16 ring; + u16 npa_pf_func; + u16 sso_pf_func; +}; + +struct tim_ring_req { + struct mbox_msghdr hdr; + u16 ring; +}; + +struct tim_config_req { + struct mbox_msghdr hdr; + u16 ring; + u8 bigendian; + u8 clocksource; + u8 enableperiodic; + u8 enabledontfreebuffer; + u32 bucketsize; + u32 chunksize; + u32 interval; +}; + +struct tim_lf_alloc_rsp { + struct mbox_msghdr hdr; + u64 tenns_clk; +}; + +struct tim_enable_rsp { + struct mbox_msghdr hdr; + u64 timestarted; + u32 currentbucket; +}; + +struct get_hw_cap_rsp { + struct mbox_msghdr hdr; + u8 nix_fixed_txschq_mapping; /* Schq mapping fixed or flexible */ + u8 nix_shaping; /* Is shaping and coloring supported */ +}; + +struct ndc_sync_op { + struct mbox_msghdr hdr; + u8 nix_lf_tx_sync; + u8 nix_lf_rx_sync; + u8 npa_lf_sync; +}; + +/* CPT mailbox error codes + * Range 901 - 1000. + */ +enum cpt_af_status { + CPT_AF_ERR_PARAM = -901, + CPT_AF_ERR_GRP_INVALID = -902, + CPT_AF_ERR_LF_INVALID = -903, + CPT_AF_ERR_ACCESS_DENIED = -904, + CPT_AF_ERR_SSO_PF_FUNC_INVALID = -905, + CPT_AF_ERR_NIX_PF_FUNC_INVALID = -906, + CPT_AF_ERR_INLINE_IPSEC_INB_ENA = -907, + CPT_AF_ERR_INLINE_IPSEC_OUT_ENA = -908 +}; + +/* CPT mbox message formats */ + +struct cpt_rd_wr_reg_msg { + struct mbox_msghdr hdr; + u64 reg_offset; + u64 *ret_val; + u64 val; + u8 is_write; +}; + +struct cpt_set_crypto_grp_req_msg { + struct mbox_msghdr hdr; + u8 crypto_eng_grp; +}; + +struct cpt_lf_alloc_req_msg { + struct mbox_msghdr hdr; + u16 nix_pf_func; + u16 sso_pf_func; +}; + +struct cpt_lf_alloc_rsp_msg { + struct mbox_msghdr hdr; + u8 crypto_eng_grp; +}; + +#define CPT_INLINE_INBOUND 0 +#define CPT_INLINE_OUTBOUND 1 +struct cpt_inline_ipsec_cfg_msg { + struct mbox_msghdr hdr; + u8 enable; + u8 slot; + u8 dir; + u16 sso_pf_func; /* inbound path SSO_PF_FUNC */ + u16 nix_pf_func; /* outbound path NIX_PF_FUNC */ +}; + #endif /* MBOX_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/npc.h index 8d6d90fdfb73..6c1c50247adb 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/npc.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/npc.h @@ -27,26 +27,45 @@ enum NPC_LID_E { enum npc_kpu_la_ltype { NPC_LT_LA_8023 = 1, NPC_LT_LA_ETHER, + NPC_LT_LA_IH_NIX_ETHER, + NPC_LT_LA_IH_8_ETHER, + NPC_LT_LA_IH_4_ETHER, + NPC_LT_LA_IH_2_ETHER, + NPC_LT_LA_HIGIG2_ETHER, + NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_LT_LA_CUSTOM0 = 0xE, + NPC_LT_LA_CUSTOM1 = 0xF, }; enum npc_kpu_lb_ltype { NPC_LT_LB_ETAG = 1, NPC_LT_LB_CTAG, - NPC_LT_LB_STAG, + NPC_LT_LB_STAG_QINQ, NPC_LT_LB_BTAG, - NPC_LT_LB_QINQ, NPC_LT_LB_ITAG, + NPC_LT_LB_DSA, + NPC_LT_LB_DSA_VLAN, + NPC_LT_LB_EDSA, + NPC_LT_LB_EDSA_VLAN, + NPC_LT_LB_EXDSA, + NPC_LT_LB_EXDSA_VLAN, + NPC_LT_LB_CUSTOM0 = 0xE, + NPC_LT_LB_CUSTOM1 = 0xF, }; enum npc_kpu_lc_ltype { NPC_LT_LC_IP = 1, + NPC_LT_LC_IP_OPT, NPC_LT_LC_IP6, + NPC_LT_LC_IP6_EXT, NPC_LT_LC_ARP, NPC_LT_LC_RARP, NPC_LT_LC_MPLS, NPC_LT_LC_NSH, NPC_LT_LC_PTP, NPC_LT_LC_FCOE, + NPC_LT_LC_CUSTOM0 = 0xE, + NPC_LT_LC_CUSTOM1 = 0xF, }; /* Don't modify Ltypes upto SCTP, otherwise it will @@ -57,49 +76,74 @@ enum npc_kpu_ld_ltype { NPC_LT_LD_UDP, NPC_LT_LD_ICMP, NPC_LT_LD_SCTP, - NPC_LT_LD_IGMP, NPC_LT_LD_ICMP6, + NPC_LT_LD_IGMP = 8, NPC_LT_LD_ESP, NPC_LT_LD_AH, NPC_LT_LD_GRE, - NPC_LT_LD_GRE_MPLS, - NPC_LT_LD_GRE_NSH, - NPC_LT_LD_TU_MPLS, + NPC_LT_LD_NVGRE, + NPC_LT_LD_NSH, + NPC_LT_LD_TU_MPLS_IN_NSH, + NPC_LT_LD_TU_MPLS_IN_IP, + NPC_LT_LD_CUSTOM0 = 0xE, + NPC_LT_LD_CUSTOM1 = 0xF, }; enum npc_kpu_le_ltype { - NPC_LT_LE_TU_ETHER = 1, - NPC_LT_LE_TU_PPP, - NPC_LT_LE_TU_MPLS_IN_NSH, - NPC_LT_LE_TU_3RD_NSH, + NPC_LT_LE_VXLAN = 1, + NPC_LT_LE_GENEVE, + NPC_LT_LE_GTPU = 4, + NPC_LT_LE_VXLANGPE, + NPC_LT_LE_GTPC, + NPC_LT_LE_NSH, + NPC_LT_LE_TU_MPLS_IN_GRE, + NPC_LT_LE_TU_NSH_IN_GRE, + NPC_LT_LE_TU_MPLS_IN_UDP, + NPC_LT_LE_CUSTOM0 = 0xE, + NPC_LT_LE_CUSTOM1 = 0xF, }; enum npc_kpu_lf_ltype { - NPC_LT_LF_TU_IP = 1, - NPC_LT_LF_TU_IP6, - NPC_LT_LF_TU_ARP, - NPC_LT_LF_TU_MPLS_IP, - NPC_LT_LF_TU_MPLS_IP6, - NPC_LT_LF_TU_MPLS_ETHER, + NPC_LT_LF_TU_ETHER = 1, + NPC_LT_LF_TU_PPP, + NPC_LT_LF_TU_MPLS_IN_VXLANGPE, + NPC_LT_LF_TU_NSH_IN_VXLANGPE, + NPC_LT_LF_TU_MPLS_IN_NSH, + NPC_LT_LF_TU_3RD_NSH, + NPC_LT_LF_CUSTOM0 = 0xE, + NPC_LT_LF_CUSTOM1 = 0xF, }; enum npc_kpu_lg_ltype { - NPC_LT_LG_TU_TCP = 1, - NPC_LT_LG_TU_UDP, - NPC_LT_LG_TU_SCTP, - NPC_LT_LG_TU_ICMP, - NPC_LT_LG_TU_IGMP, - NPC_LT_LG_TU_ICMP6, - NPC_LT_LG_TU_ESP, - NPC_LT_LG_TU_AH, + NPC_LT_LG_TU_IP = 1, + NPC_LT_LG_TU_IP6, + NPC_LT_LG_TU_ARP, + NPC_LT_LG_TU_ETHER_IN_NSH, + NPC_LT_LG_CUSTOM0 = 0xE, + NPC_LT_LG_CUSTOM1 = 0xF, }; +/* Don't modify Ltypes upto SCTP, otherwise it will + * effect flow tag calculation and thus RSS. + */ enum npc_kpu_lh_ltype { - NPC_LT_LH_TCP_DATA = 1, - NPC_LT_LH_HTTP_DATA, - NPC_LT_LH_HTTPS_DATA, - NPC_LT_LH_PPTP_DATA, - NPC_LT_LH_UDP_DATA, + NPC_LT_LH_TU_TCP = 1, + NPC_LT_LH_TU_UDP, + NPC_LT_LH_TU_ICMP, + NPC_LT_LH_TU_SCTP, + NPC_LT_LH_TU_ICMP6, + NPC_LT_LH_TU_IGMP = 8, + NPC_LT_LH_TU_ESP, + NPC_LT_LH_TU_AH, + NPC_LT_LH_CUSTOM0 = 0xE, + NPC_LT_LH_CUSTOM1 = 0xF, +}; + +enum npc_pkind_type { + NPC_TX_HIGIG_PKIND = 60ULL, + NPC_RX_HIGIG_PKIND, + NPC_RX_EDSA_PKIND, + NPC_TX_DEF_PKIND, }; struct npc_kpu_profile_cam { @@ -259,11 +303,41 @@ struct nix_rx_action { #endif }; +struct nix_tx_action { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 rsvd_63_48 :16; + u64 match_id :16; + u64 index :20; + u64 rsvd_11_8 :8; + u64 op :4; +#else + u64 op :4; + u64 rsvd_11_8 :8; + u64 index :20; + u64 match_id :16; + u64 rsvd_63_48 :16; +#endif +}; + /* NIX Receive Vtag Action Structure */ -#define VTAG0_VALID_BIT BIT_ULL(15) -#define VTAG0_TYPE_MASK GENMASK_ULL(14, 12) -#define VTAG0_LID_MASK GENMASK_ULL(10, 8) -#define VTAG0_RELPTR_MASK GENMASK_ULL(7, 0) +#define RX_VTAG0_VALID_BIT BIT_ULL(15) +#define RX_VTAG0_TYPE_MASK GENMASK_ULL(14, 12) +#define RX_VTAG0_LID_MASK GENMASK_ULL(10, 8) +#define RX_VTAG0_RELPTR_MASK GENMASK_ULL(7, 0) +#define RX_VTAG1_VALID_BIT BIT_ULL(47) +#define RX_VTAG1_TYPE_MASK GENMASK_ULL(46, 44) +#define RX_VTAG1_LID_MASK GENMASK_ULL(42, 40) +#define RX_VTAG1_RELPTR_MASK GENMASK_ULL(39, 32) + +/* NIX Transmit Vtag Action Structure */ +#define TX_VTAG0_DEF_MASK GENMASK_ULL(25, 16) +#define TX_VTAG0_OP_MASK GENMASK_ULL(13, 12) +#define TX_VTAG0_LID_MASK GENMASK_ULL(10, 8) +#define TX_VTAG0_RELPTR_MASK GENMASK_ULL(7, 0) +#define TX_VTAG1_DEF_MASK GENMASK_ULL(57, 48) +#define TX_VTAG1_OP_MASK GENMASK_ULL(45, 44) +#define TX_VTAG1_LID_MASK GENMASK_ULL(42, 40) +#define TX_VTAG1_RELPTR_MASK GENMASK_ULL(39, 32) struct npc_mcam_kex { /* MKEX Profle Header */ @@ -283,4 +357,23 @@ struct npc_mcam_kex { u64 intf_ld_flags[NPC_MAX_INTF][NPC_MAX_LD][NPC_MAX_LFL]; } __packed; +struct rvu_npc_mcam_rule { + struct flow_msg packet; + struct flow_msg mask; + u8 intf; + union { + struct nix_tx_action tx_action; + struct nix_rx_action rx_action; + }; + u64 vtag_action; + struct list_head list; + u64 features; + u16 owner; + u16 entry; + u16 cntr; + bool has_cntr; + u8 default_rule; + bool enable; +}; + #endif /* NPC_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h b/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h index b2ce957605bb..832810ad6b02 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h @@ -11,6 +11,11 @@ #ifndef NPC_PROFILE_H #define NPC_PROFILE_H +#define NPC_KPU_PROFILE_VER 0x0000000100050000 + +#define NPC_IH_W 0x8000 +#define NPC_IH_UTAG 0x2000 + #define NPC_ETYPE_IP 0x0800 #define NPC_ETYPE_IP6 0x86dd #define NPC_ETYPE_ARP 0x0806 @@ -27,6 +32,7 @@ #define NPC_ETYPE_TRANS_ETH_BR 0x6558 #define NPC_ETYPE_PPP 0x880b #define NPC_ETYPE_NSH 0x894f +#define NPC_ETYPE_DSA 0xdada #define NPC_IPNH_HOP 0 #define NPC_IPNH_ICMP 1 @@ -44,13 +50,19 @@ #define NPC_IPNH_NONH 59 #define NPC_IPNH_DEST 60 #define NPC_IPNH_SCTP 132 +#define NPC_IPNH_MOBILITY 135 #define NPC_IPNH_MPLS 137 +#define NPC_IPNH_HOSTID 139 +#define NPC_IPNH_SHIM6 140 +#define NPC_UDP_PORT_PTP_E 319 +#define NPC_UDP_PORT_PTP_G 320 #define NPC_UDP_PORT_GTPC 2123 #define NPC_UDP_PORT_GTPU 2152 #define NPC_UDP_PORT_VXLAN 4789 #define NPC_UDP_PORT_VXLANGPE 4790 #define NPC_UDP_PORT_GENEVE 6081 +#define NPC_UDP_PORT_MPLS 6635 #define NPC_VXLANGPE_NP_IP 0x1 #define NPC_VXLANGPE_NP_IP6 0x2 @@ -72,11 +84,17 @@ #define NPC_MPLS_S 0x0100 +#define NPC_IP_TTL_MASK 0xff00 #define NPC_IP_VER_4 0x4000 #define NPC_IP_VER_6 0x6000 #define NPC_IP_VER_MASK 0xf000 #define NPC_IP_HDR_LEN_5 0x0500 #define NPC_IP_HDR_LEN_MASK 0x0f00 +#define NPC_IP_HDR_MF 0x2000 +#define NPC_IP_HDR_FRAGOFF 0x1fff + +#define NPC_IP6_HOP_MASK 0x00ff +#define NPC_IP6_FRAG_FRAGOFF 0xfff8 #define NPC_GRE_F_CSUM (0x1 << 15) #define NPC_GRE_F_ROUTE (0x1 << 14) @@ -108,22 +126,44 @@ #define NPC_GTP_MT_G_PDU 0xff #define NPC_GTP_MT_MASK 0xff +#define NPC_TCP_FLAGS_FIN 0x0001 +#define NPC_TCP_FLAGS_SYN 0x0002 +#define NPC_TCP_FLAGS_RST 0x0004 +#define NPC_TCP_FLAGS_PSH 0x0008 +#define NPC_TCP_FLAGS_ACK 0x0010 +#define NPC_TCP_FLAGS_URG 0x0020 +#define NPC_TCP_FLAGS_MASK 0x003f + #define NPC_TCP_DATA_OFFSET_5 0x5000 #define NPC_TCP_DATA_OFFSET_MASK 0xf000 +#define NPC_DSA_EXTEND 0x1000 +#define NPC_DSA_EDSA 0x8000 + enum npc_kpu_parser_state { NPC_S_NA = 0, NPC_S_KPU1_ETHER, - NPC_S_KPU1_PKI, + NPC_S_KPU1_IH_NIX, + NPC_S_KPU1_IH, + NPC_S_KPU1_EXDSA, + NPC_S_KPU1_HIGIG2, + NPC_S_KPU1_IH_NIX_HIGIG2, NPC_S_KPU2_CTAG, + NPC_S_KPU2_CTAG2, NPC_S_KPU2_SBTAG, NPC_S_KPU2_QINQ, NPC_S_KPU2_ETAG, NPC_S_KPU2_ITAG, + NPC_S_KPU2_PREHEADER, + NPC_S_KPU2_EXDSA, NPC_S_KPU3_CTAG, NPC_S_KPU3_STAG, NPC_S_KPU3_QINQ, NPC_S_KPU3_ITAG, + NPC_S_KPU3_CTAG_C, + NPC_S_KPU3_STAG_C, + NPC_S_KPU3_QINQ_C, + NPC_S_KPU3_DSA, NPC_S_KPU4_MPLS, NPC_S_KPU4_NSH, NPC_S_KPU5_IP, @@ -136,7 +176,12 @@ enum npc_kpu_parser_state { NPC_S_KPU5_MPLS_PL, NPC_S_KPU5_NSH, NPC_S_KPU6_IP6_EXT, + NPC_S_KPU6_IP6_HOP_DEST, + NPC_S_KPU6_IP6_ROUT, + NPC_S_KPU6_IP6_FRAG, NPC_S_KPU7_IP6_EXT, + NPC_S_KPU7_IP6_ROUT, + NPC_S_KPU7_IP6_FRAG, NPC_S_KPU8_TCP, NPC_S_KPU8_UDP, NPC_S_KPU8_SCTP, @@ -146,16 +191,26 @@ enum npc_kpu_parser_state { NPC_S_KPU8_GRE, NPC_S_KPU8_ESP, NPC_S_KPU8_AH, - NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, - NPC_S_KPU9_TU_MPLS, - NPC_S_KPU9_TU_NSH, + NPC_S_KPU9_TU_MPLS_IN_GRE, + NPC_S_KPU9_TU_MPLS_IN_NSH, + NPC_S_KPU9_TU_MPLS_IN_IP, + NPC_S_KPU9_TU_MPLS_IN_UDP, + NPC_S_KPU9_TU_NSH_IN_GRE, + NPC_S_KPU9_VXLAN, + NPC_S_KPU9_VXLANGPE, + NPC_S_KPU9_GENEVE, + NPC_S_KPU9_GTPC, + NPC_S_KPU9_GTPU, + NPC_S_KPU10_TU_MPLS_IN_VXLANGPE, NPC_S_KPU10_TU_MPLS_PL, NPC_S_KPU10_TU_MPLS, - NPC_S_KPU10_TU_NSH, + NPC_S_KPU10_TU_NSH_IN_VXLANGPE, NPC_S_KPU11_TU_ETHER, NPC_S_KPU11_TU_PPP, NPC_S_KPU11_TU_MPLS_IN_NSH, - NPC_S_KPU11_TU_3RD_NSH, + NPC_S_KPU11_TU_MPLS_PL, + NPC_S_KPU11_TU_MPLS, + NPC_S_KPU11_TU_ETHER_IN_NSH, NPC_S_KPU12_TU_IP, NPC_S_KPU12_TU_IP6, NPC_S_KPU12_TU_ARP, @@ -174,135 +229,172 @@ enum npc_kpu_parser_state { NPC_S_KPU16_PPTP_DATA, NPC_S_KPU16_TCP_DATA, NPC_S_KPU16_UDP_DATA, + NPC_S_KPU16_UDP_PTP, NPC_S_LAST /* has to be the last item */ }; -enum npc_kpu_parser_flag { - NPC_F_NA = 0, - NPC_F_PKI, - NPC_F_PKI_VLAN, - NPC_F_PKI_ETAG, - NPC_F_PKI_ITAG, - NPC_F_PKI_MPLS, - NPC_F_PKI_NSH, - NPC_F_ETYPE_UNK, - NPC_F_ETHER_VLAN, - NPC_F_ETHER_ETAG, - NPC_F_ETHER_ITAG, - NPC_F_ETHER_MPLS, - NPC_F_ETHER_NSH, - NPC_F_STAG_CTAG, - NPC_F_STAG_CTAG_UNK, - NPC_F_STAG_STAG_CTAG, - NPC_F_STAG_STAG_STAG, - NPC_F_QINQ_CTAG, - NPC_F_QINQ_CTAG_UNK, - NPC_F_QINQ_QINQ_CTAG, - NPC_F_QINQ_QINQ_QINQ, - NPC_F_BTAG_ITAG, - NPC_F_BTAG_ITAG_STAG, - NPC_F_BTAG_ITAG_CTAG, - NPC_F_BTAG_ITAG_UNK, - NPC_F_ETAG_CTAG, - NPC_F_ETAG_BTAG_ITAG, - NPC_F_ETAG_STAG, - NPC_F_ETAG_QINQ, - NPC_F_ETAG_ITAG, - NPC_F_ETAG_ITAG_STAG, - NPC_F_ETAG_ITAG_CTAG, - NPC_F_ETAG_ITAG_UNK, - NPC_F_ITAG_STAG_CTAG, - NPC_F_ITAG_STAG, - NPC_F_ITAG_CTAG, - NPC_F_MPLS_4_LABELS, - NPC_F_MPLS_3_LABELS, - NPC_F_MPLS_2_LABELS, - NPC_F_IP_HAS_OPTIONS, - NPC_F_IP_IP_IN_IP, - NPC_F_IP_6TO4, - NPC_F_IP_MPLS_IN_IP, - NPC_F_IP_UNK_PROTO, - NPC_F_IP_IP_IN_IP_HAS_OPTIONS, - NPC_F_IP_6TO4_HAS_OPTIONS, - NPC_F_IP_MPLS_IN_IP_HAS_OPTIONS, - NPC_F_IP_UNK_PROTO_HAS_OPTIONS, - NPC_F_IP6_HAS_EXT, - NPC_F_IP6_TUN_IP6, - NPC_F_IP6_MPLS_IN_IP, - NPC_F_TCP_HAS_OPTIONS, - NPC_F_TCP_HTTP, - NPC_F_TCP_HTTPS, - NPC_F_TCP_PPTP, - NPC_F_TCP_UNK_PORT, - NPC_F_TCP_HTTP_HAS_OPTIONS, - NPC_F_TCP_HTTPS_HAS_OPTIONS, - NPC_F_TCP_PPTP_HAS_OPTIONS, - NPC_F_TCP_UNK_PORT_HAS_OPTIONS, - NPC_F_UDP_VXLAN, - NPC_F_UDP_VXLAN_NOVNI, - NPC_F_UDP_VXLAN_NOVNI_NSH, - NPC_F_UDP_VXLANGPE, - NPC_F_UDP_VXLANGPE_NSH, - NPC_F_UDP_VXLANGPE_MPLS, - NPC_F_UDP_VXLANGPE_NOVNI, - NPC_F_UDP_VXLANGPE_NOVNI_NSH, - NPC_F_UDP_VXLANGPE_NOVNI_MPLS, - NPC_F_UDP_VXLANGPE_UNK, - NPC_F_UDP_VXLANGPE_NONP, - NPC_F_UDP_GTP_GTPC, - NPC_F_UDP_GTP_GTPU_G_PDU, - NPC_F_UDP_GTP_GTPU_UNK, - NPC_F_UDP_UNK_PORT, - NPC_F_UDP_GENEVE, - NPC_F_UDP_GENEVE_OAM, - NPC_F_UDP_GENEVE_CRI_OPT, - NPC_F_UDP_GENEVE_OAM_CRI_OPT, - NPC_F_GRE_NVGRE, - NPC_F_GRE_HAS_SRE, - NPC_F_GRE_HAS_CSUM, - NPC_F_GRE_HAS_KEY, - NPC_F_GRE_HAS_SEQ, - NPC_F_GRE_HAS_CSUM_KEY, - NPC_F_GRE_HAS_CSUM_SEQ, - NPC_F_GRE_HAS_KEY_SEQ, - NPC_F_GRE_HAS_CSUM_KEY_SEQ, - NPC_F_GRE_HAS_ROUTE, - NPC_F_GRE_UNK_PROTO, - NPC_F_GRE_VER1, - NPC_F_GRE_VER1_HAS_SEQ, - NPC_F_GRE_VER1_HAS_ACK, - NPC_F_GRE_VER1_HAS_SEQ_ACK, - NPC_F_GRE_VER1_UNK_PROTO, - NPC_F_TU_ETHER_UNK, - NPC_F_TU_ETHER_CTAG, - NPC_F_TU_ETHER_CTAG_UNK, - NPC_F_TU_ETHER_STAG_CTAG, - NPC_F_TU_ETHER_STAG_CTAG_UNK, - NPC_F_TU_ETHER_STAG, - NPC_F_TU_ETHER_STAG_UNK, - NPC_F_TU_ETHER_QINQ_CTAG, - NPC_F_TU_ETHER_QINQ_CTAG_UNK, - NPC_F_TU_ETHER_QINQ, - NPC_F_TU_ETHER_QINQ_UNK, - NPC_F_LAST /* has to be the last item */ +enum npc_kpu_la_uflag { + NPC_F_LA_U_HAS_TAG = 0x10, + NPC_F_LA_U_HAS_IH_NIX = 0x20, + NPC_F_LA_U_HAS_HIGIG2 = 0x40, +}; +enum npc_kpu_la_lflag { + NPC_F_LA_L_UNK_ETYPE = 1, + NPC_F_LA_L_WITH_VLAN, + NPC_F_LA_L_WITH_ETAG, + NPC_F_LA_L_WITH_ITAG, + NPC_F_LA_L_WITH_MPLS, + NPC_F_LA_L_WITH_NSH, +}; + +enum npc_kpu_lb_uflag { + NPC_F_LB_U_UNK_ETYPE = 0x80, + NPC_F_LB_U_MORE_TAG = 0x40, +}; +enum npc_kpu_lb_lflag { + NPC_F_LB_L_WITH_CTAG = 1, + NPC_F_LB_L_WITH_CTAG_UNK, + NPC_F_LB_L_WITH_STAG_CTAG, + NPC_F_LB_L_WITH_STAG_STAG, + NPC_F_LB_L_WITH_QINQ_CTAG, + NPC_F_LB_L_WITH_QINQ_QINQ, + NPC_F_LB_L_WITH_ITAG, + NPC_F_LB_L_WITH_ITAG_STAG, + NPC_F_LB_L_WITH_ITAG_CTAG, + NPC_F_LB_L_WITH_ITAG_UNK, + NPC_F_LB_L_WITH_BTAG_ITAG, + NPC_F_LB_L_WITH_STAG, + NPC_F_LB_L_WITH_QINQ, + NPC_F_LB_L_DSA, + NPC_F_LB_L_DSA_VLAN, + NPC_F_LB_L_EDSA, + NPC_F_LB_L_EDSA_VLAN, + NPC_F_LB_L_EXDSA, + NPC_F_LB_L_EXDSA_VLAN, +}; + +enum npc_kpu_lc_uflag { + NPC_F_LC_U_UNK_PROTO = 0x10, + NPC_F_LC_U_IP_FRAG = 0x20, + NPC_F_LC_U_IP6_FRAG = 0x40, +}; +enum npc_kpu_lc_lflag { + NPC_F_LC_L_IP_IN_IP = 1, + NPC_F_LC_L_6TO4, + NPC_F_LC_L_MPLS_IN_IP, + NPC_F_LC_L_IP6_TUN_IP6, + NPC_F_LC_L_IP6_MPLS_IN_IP, + NPC_F_LC_L_MPLS_4_LABELS, + NPC_F_LC_L_MPLS_3_LABELS, + NPC_F_LC_L_MPLS_2_LABELS, + NPC_F_LC_L_EXT_HOP, + NPC_F_LC_L_EXT_DEST, + NPC_F_LC_L_EXT_ROUT, + NPC_F_LC_L_EXT_MOBILITY, + NPC_F_LC_L_EXT_HOSTID, + NPC_F_LC_L_EXT_SHIM6, +}; + +enum npc_kpu_ld_lflag { + NPC_F_LD_L_TCP_UNK_PORT = 1, + NPC_F_LD_L_TCP_HAS_OPTIONS, + NPC_F_LD_L_TCP_UNK_PORT_HAS_OPTIONS, + NPC_F_LD_L_UDP_UNK_PORT, + NPC_F_LD_L_GRE_NVGRE, + NPC_F_LD_L_GRE_HAS_SRE, + NPC_F_LD_L_GRE_HAS_CSUM, + NPC_F_LD_L_GRE_HAS_KEY, + NPC_F_LD_L_GRE_HAS_SEQ, + NPC_F_LD_L_GRE_HAS_CSUM_KEY, + NPC_F_LD_L_GRE_HAS_CSUM_SEQ, + NPC_F_LD_L_GRE_HAS_KEY_SEQ, + NPC_F_LD_L_GRE_HAS_CSUM_KEY_SEQ, + NPC_F_LD_L_GRE_HAS_ROUTE, + NPC_F_LD_L_GRE_UNK_PROTO, + NPC_F_LD_L_GRE_VER1, + NPC_F_LD_L_GRE_VER1_HAS_SEQ, + NPC_F_LD_L_GRE_VER1_HAS_ACK, + NPC_F_LD_L_GRE_VER1_HAS_SEQ_ACK, + NPC_F_LD_L_GRE_VER1_UNK_PROTO, + NPC_F_LD_L_MPLS_4_LABELS, + NPC_F_LD_L_MPLS_3_LABELS, + NPC_F_LD_L_MPLS_2_LABELS, +}; + +enum npc_kpu_le_lflag { + NPC_F_LE_L_VXLAN_NOVNI, + NPC_F_LE_L_VXLANGPE_NOVNI, + NPC_F_LE_L_VXLANGPE_UNK, + NPC_F_LE_L_VXLANGPE_NONP, + NPC_F_LE_L_GENEVE_OAM, + NPC_F_LE_L_GENEVE_CRI_OPT, + NPC_F_LE_L_GENEVE_OAM_CRI_OPT, + NPC_F_LE_L_GTPU_G_PDU, + NPC_F_LE_L_GTPU_UNK, +}; + +enum npc_kpu_lf_uflag { + NPC_F_LF_U_UNK_ETYPE = 0x10, + NPC_F_LF_U_HAS_TAG = 0x20, +}; + +enum npc_kpu_lf_lflag { + NPC_F_LF_L_WITH_CTAG = 1, + NPC_F_LF_L_WITH_STAG_CTAG, + NPC_F_LF_L_WITH_STAG, + NPC_F_LF_L_WITH_QINQ_CTAG, + NPC_F_LF_L_WITH_QINQ, +}; + +enum npc_kpu_lg_uflag { + NPC_F_LG_U_UNK_IP_PROTO = 0x10, + NPC_F_LG_U_IP_HAS_OPTIONS = 0x20, + NPC_F_LG_U_IP6_HAS_EXT = 0x40, +}; + +enum npc_kpu_lh_uflag { + NPC_F_LH_U_TCP_HAS_OPTIONS = 0x80, +}; + +enum npc_kpu_lh_lflag { + NPC_F_LH_L_TCP_HTTP = 1, + NPC_F_LH_L_TCP_HTTPS, + NPC_F_LH_L_TCP_PPTP, + NPC_F_LH_L_TCP_UNK_PORT, + NPC_F_LH_L_UDP_UNK_PORT, }; enum npc_kpu_err_code { NPC_EC_NOERR = 0, /* has to be zero */ NPC_EC_UNK, + NPC_EC_IH_LENGTH, + NPC_EC_EDSA_UNK, NPC_EC_L2_K1, NPC_EC_L2_K2, NPC_EC_L2_K3, NPC_EC_L2_K3_ETYPE_UNK, - NPC_EC_L2_MPLS_2MANY, NPC_EC_L2_K4, + NPC_EC_MPLS_2MANY, + NPC_EC_MPLS_UNK, + NPC_EC_NSH_UNK, + NPC_EC_IP_TTL_0, + NPC_EC_IP_FRAG_OFFSET_1, NPC_EC_IP_VER, + NPC_EC_IP6_HOP_0, NPC_EC_IP6_VER, + NPC_EC_TCP_FLAGS_FIN_ONLY, + NPC_EC_TCP_FLAGS_ZERO, + NPC_EC_TCP_FLAGS_RST_FIN, + NPC_EC_TCP_FLAGS_URG_SYN, + NPC_EC_TCP_FLAGS_RST_SYN, + NPC_EC_TCP_FLAGS_SYN_FIN, NPC_EC_VXLAN, NPC_EC_NVGRE, NPC_EC_GRE, NPC_EC_GRE_VER1, NPC_EC_L4, + NPC_EC_OIP4_CSUM, + NPC_EC_IIP4_CSUM, NPC_EC_LAST /* has to be the last item */ }; @@ -328,5282 +420,12598 @@ enum NPC_ERRLEV_E { static struct npc_kpu_profile_action ikpu_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_ETHER, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 36, 40, 44, 0, 0, + NPC_S_KPU1_IH_NIX_HIGIG2, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 28, 32, 36, 0, 0, + NPC_S_KPU1_HIGIG2, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU1_EXDSA, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 14, 16, - 0, 0, NPC_S_KPU1_ETHER, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 1, 0xff, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 20, 24, 28, 0, 0, + NPC_S_KPU1_IH_NIX, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, }; static struct npc_kpu_profile_cam kpu1_cam_entries[] = { { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_RARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_PTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_FCOE, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_CTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_SBTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_QINQ, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_ETAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_MPLSU, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_MPLSM, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_NSH, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, 0x0000, 0xfc00, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, 0x0400, 0xfe00, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_ETHER, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_RARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_PTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_FCOE, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_CTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_SBTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_QINQ, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_ETAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_MPLSU, 0xffff, - 0x0010, 0x0010, 0x0000, 0xffff, - }, - { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_MPLSM, 0xffff, - 0x0010, 0x0010, 0x0000, 0xffff, + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_ETAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_DSA, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + 0x0000, + 0xfc00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + 0x0400, + 0xfe00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_ETHER, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_ETAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH, 0xff, + NPC_IH_W|NPC_IH_UTAG, + NPC_IH_W|NPC_IH_UTAG, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH, 0xff, + NPC_IH_W, + NPC_IH_W|NPC_IH_UTAG, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH, 0xff, + 0x0000, + NPC_IH_W|NPC_IH_UTAG, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_EXDSA, 0xff, + NPC_DSA_EXTEND, + NPC_DSA_EXTEND, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_EXDSA, 0xff, + 0x0000, + NPC_DSA_EXTEND, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_ETAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_HIGIG2, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_ETAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_IH_NIX_HIGIG2, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu2_cam_entries[] = { { - NPC_S_KPU1_PKI, 0xff, NPC_ETYPE_NSH, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_IP, + 0xffff, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_IP6, + 0xffff, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_ARP, + 0xffff, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_RARP, + 0xffff, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_PTP, + 0xffff, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_FCOE, + 0xffff, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_MPLSU, + 0xffff, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_MPLSM, + 0xffff, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_NSH, + 0xffff, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_SBTAG, + 0xffff, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_CTAG, + 0xffff, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + NPC_ETYPE_QINQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_IP, + 0xffff, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_IP6, + 0xffff, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_ARP, + 0xffff, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_SBTAG, + 0xffff, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_CTAG, + 0xffff, + }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_ITAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ETAG, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_ITAG, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG2, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG2, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG2, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG2, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG2, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG2, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG2, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG2, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG2, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG2, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG2, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_PREHEADER, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + NPC_DSA_EDSA, + NPC_DSA_EDSA, + 0x0000, + 0x0000, + NPC_ETYPE_IP, + 0xffff, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + NPC_DSA_EDSA, + NPC_DSA_EDSA, + 0x0000, + 0x0000, + NPC_ETYPE_IP6, + 0xffff, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + NPC_DSA_EDSA, + NPC_DSA_EDSA, + 0x0000, + 0x0000, + NPC_ETYPE_ARP, + 0xffff, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + NPC_DSA_EDSA, + NPC_DSA_EDSA, + 0x0000, + 0x0000, + NPC_ETYPE_RARP, + 0xffff, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + NPC_DSA_EDSA, + NPC_DSA_EDSA, + 0x0000, + 0x0000, + NPC_ETYPE_PTP, + 0xffff, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + NPC_DSA_EDSA, + NPC_DSA_EDSA, + 0x0000, + 0x0000, + NPC_ETYPE_FCOE, + 0xffff, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + NPC_DSA_EDSA, + NPC_DSA_EDSA, + 0x0000, + 0x0000, + NPC_ETYPE_CTAG, + 0xffff, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + NPC_DSA_EDSA, + NPC_DSA_EDSA, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + 0x0000, + NPC_DSA_EDSA, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + 0x0000, + NPC_DSA_EDSA, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + 0x0000, + NPC_DSA_EDSA, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + 0x0000, + NPC_DSA_EDSA, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + 0x0000, + NPC_DSA_EDSA, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + 0x0000, + NPC_DSA_EDSA, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + 0x0000, + NPC_DSA_EDSA, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_EXDSA, 0xff, + 0x0000, + NPC_DSA_EDSA, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu3_cam_entries[] = { { - NPC_S_KPU1_PKI, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU3_CTAG, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_ITAG, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_STAG_C, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_QINQ_C, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_RARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_FCOE, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_DSA, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu4_cam_entries[] = { { - NPC_S_NA, 0X00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU4_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU4_MPLS, 0xff, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU4_MPLS, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + }, + { + NPC_S_KPU4_MPLS, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + }, + { + NPC_S_KPU4_NSH, 0xff, + NPC_NSH_NP_IP, + NPC_NSH_NP_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU4_NSH, 0xff, + NPC_NSH_NP_IP6, + NPC_NSH_NP_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU4_NSH, 0xff, + NPC_NSH_NP_ETH, + NPC_NSH_NP_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU4_NSH, 0xff, + NPC_NSH_NP_MPLS, + NPC_NSH_NP_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU4_NSH, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, }; -static struct npc_kpu_profile_cam kpu2_cam_entries[] = { +static struct npc_kpu_profile_cam kpu5_cam_entries[] = { { - NPC_S_KPU2_CTAG, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU5_IP, 0xff, + 0x0000, + NPC_IP_TTL_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0001, + NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_TCP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_UDP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_SCTP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_ICMP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_IGMP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_ESP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_AH, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_GRE, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_IP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_IP6, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_MPLS, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + 0x0000, + 0x0000, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + 0x0000, + 0x0000, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_TCP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_UDP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_SCTP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_ICMP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_IGMP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_ESP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_AH, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_GRE, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_IP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_IP6, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + NPC_IPNH_MPLS, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + 0x0000, + 0x0000, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + NPC_IP_HDR_MF|NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU5_IP, 0xff, + 0x0000, + 0x0000, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_ARP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_RARP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_PTP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_FCOE, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + 0x0000, + NPC_IP6_HOP_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_TCP << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_UDP << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_SCTP << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_ICMP << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_ICMP6 << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_GRE << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_IP6 << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_MPLS << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_HOP << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_DEST << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_ROUT << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_FRAG << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_ESP << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_AH << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_MOBILITY << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_HOSTID << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_SHIM6 << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + 0x0000, + 0x0000, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_IP6, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_MPLS, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_MPLS_PL, 0xff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_MPLS_PL, 0xff, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_MPLS_PL, 0xff, + 0x0000, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_MPLS_PL, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu6_cam_entries[] = { { - NPC_S_KPU2_CTAG, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU6_IP6_EXT, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, + NPC_IPNH_TCP << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, + NPC_IPNH_UDP << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, + NPC_IPNH_SCTP << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, + NPC_IPNH_ICMP << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, + NPC_IPNH_ICMP6 << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, + NPC_IPNH_ESP << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, + NPC_IPNH_AH << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, + NPC_IPNH_GRE << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, + NPC_IPNH_IP6 << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, + NPC_IPNH_MPLS << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_TCP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_UDP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_SCTP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_ICMP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_ICMP6 << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_ESP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_AH << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_GRE << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_IP6 << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_MPLS << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_ROUT << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_FRAG << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_TCP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_UDP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_SCTP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_ICMP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_ICMP6 << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_ESP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_AH << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_GRE << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_IP6 << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_MPLS << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_FRAG << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu7_cam_entries[] = { { - NPC_S_KPU2_CTAG, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU7_IP6_EXT, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, + NPC_IPNH_TCP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, + NPC_IPNH_UDP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, + NPC_IPNH_SCTP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, + NPC_IPNH_ICMP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, + NPC_IPNH_ICMP6 << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, + NPC_IPNH_ESP << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, + NPC_IPNH_AH << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, + NPC_IPNH_GRE << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, + NPC_IPNH_IP6 << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, + NPC_IPNH_MPLS << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, + NPC_IPNH_TCP << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, + NPC_IPNH_UDP << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, + NPC_IPNH_SCTP << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, + NPC_IPNH_ICMP << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, + NPC_IPNH_ICMP6 << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, + NPC_IPNH_ESP << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, + NPC_IPNH_AH << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, + NPC_IPNH_GRE << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, + NPC_IPNH_IP6 << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, + NPC_IPNH_MPLS << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu8_cam_entries[] = { { - NPC_S_KPU2_CTAG, 0xff, NPC_ETYPE_RARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU8_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_FIN, + NPC_TCP_FLAGS_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + 0x0000, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_RST|NPC_TCP_FLAGS_FIN, + NPC_TCP_FLAGS_RST|NPC_TCP_FLAGS_FIN, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_URG|NPC_TCP_FLAGS_SYN, + NPC_TCP_FLAGS_URG|NPC_TCP_FLAGS_SYN, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_RST|NPC_TCP_FLAGS_SYN, + NPC_TCP_FLAGS_RST|NPC_TCP_FLAGS_SYN, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_SYN|NPC_TCP_FLAGS_FIN, + NPC_TCP_FLAGS_SYN|NPC_TCP_FLAGS_FIN, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + NPC_TCP_PORT_HTTP, + 0xffff, + NPC_TCP_DATA_OFFSET_5, + NPC_TCP_DATA_OFFSET_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + NPC_TCP_PORT_HTTPS, + 0xffff, + NPC_TCP_DATA_OFFSET_5, + NPC_TCP_DATA_OFFSET_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + NPC_TCP_PORT_PPTP, + 0xffff, + NPC_TCP_DATA_OFFSET_5, + NPC_TCP_DATA_OFFSET_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_DATA_OFFSET_5, + NPC_TCP_DATA_OFFSET_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + NPC_TCP_PORT_HTTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + NPC_TCP_PORT_HTTPS, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + NPC_TCP_PORT_PPTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_TCP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_UDP, 0xff, + NPC_UDP_PORT_VXLAN, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_UDP, 0xff, + NPC_UDP_PORT_VXLANGPE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_UDP, 0xff, + NPC_UDP_PORT_GENEVE, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_UDP, 0xff, + NPC_UDP_PORT_GTPC, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_UDP, 0xff, + NPC_UDP_PORT_GTPU, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_UDP, 0xff, + NPC_UDP_PORT_PTP_E, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_UDP, 0xff, + NPC_UDP_PORT_PTP_G, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_UDP, 0xff, + NPC_UDP_PORT_MPLS, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_UDP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_SCTP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_ICMP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_IGMP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_ICMP6, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_ESP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_AH, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_TRANS_ETH_BR, + 0xffff, + NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_TRANS_ETH_BR, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + NPC_GRE_F_CSUM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + NPC_GRE_F_KEY|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSU, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_KEY|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + NPC_GRE_F_CSUM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + NPC_GRE_F_KEY|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_MPLSM, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_KEY|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_NSH, + 0xffff, + 0x0000, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_NSH, + 0xffff, + NPC_GRE_F_CSUM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_NSH, + 0xffff, + NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_NSH, + 0xffff, + NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_NSH, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_NSH, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_NSH, + 0xffff, + NPC_GRE_F_KEY|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_NSH, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_KEY|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP, + 0xffff, + NPC_GRE_F_CSUM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP, + 0xffff, + NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP, + 0xffff, + NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP, + 0xffff, + NPC_GRE_F_KEY|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_KEY|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP6, + 0xffff, + NPC_GRE_F_CSUM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP6, + 0xffff, + NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP6, + 0xffff, + NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP6, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP6, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP6, + 0xffff, + NPC_GRE_F_KEY|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_IP6, + 0xffff, + NPC_GRE_F_CSUM|NPC_GRE_F_KEY|NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + 0x0000, + 0xffff, + NPC_GRE_F_ROUTE, + 0x4fff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + 0x0000, + 0xffff, + 0x0000, + 0x4fff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + 0x0000, + 0xffff, + 0x0000, + 0x0003, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_PPP, + 0xffff, + NPC_GRE_F_KEY|NPC_GRE_VER_1, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_PPP, + 0xffff, + NPC_GRE_F_KEY|NPC_GRE_F_SEQ|NPC_GRE_VER_1, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_PPP, + 0xffff, + NPC_GRE_F_KEY|NPC_GRE_F_ACK|NPC_GRE_VER_1, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_PPP, + 0xffff, + NPC_GRE_F_KEY|NPC_GRE_F_SEQ|NPC_GRE_F_ACK|NPC_GRE_VER_1, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + 0x0000, + 0xffff, + 0x2001, + 0xef7f, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + 0x0000, + 0xffff, + 0x0001, + 0x0003, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu9_cam_entries[] = { { - NPC_S_KPU2_CTAG, 0xff, NPC_ETYPE_PTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU9_TU_MPLS_IN_GRE, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_MPLS_IN_GRE, 0xff, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_MPLS_IN_GRE, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + }, + { + NPC_S_KPU9_TU_MPLS_IN_GRE, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + }, + { + NPC_S_KPU9_TU_MPLS_IN_NSH, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_MPLS_IN_NSH, 0xff, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_MPLS_IN_NSH, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + }, + { + NPC_S_KPU9_TU_MPLS_IN_NSH, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + }, + { + NPC_S_KPU9_TU_MPLS_IN_IP, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_MPLS_IN_IP, 0xff, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_MPLS_IN_IP, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + }, + { + NPC_S_KPU9_TU_MPLS_IN_IP, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + }, + { + NPC_S_KPU9_TU_NSH_IN_GRE, 0xff, + NPC_NSH_NP_IP, + NPC_NSH_NP_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_NSH_IN_GRE, 0xff, + NPC_NSH_NP_IP6, + NPC_NSH_NP_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_NSH_IN_GRE, 0xff, + NPC_NSH_NP_ETH, + NPC_NSH_NP_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_NSH_IN_GRE, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_VXLAN, 0xff, + 0x0000, + 0x0000, + NPC_VXLAN_I, + NPC_VXLAN_I, + 0x0000, + 0xffff, + }, + { + NPC_S_KPU9_VXLAN, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0xffff, + 0x0000, + 0xffff, + }, + { + NPC_S_KPU9_VXLAN, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_NP_IP, + NPC_VXLANGPE_NP_MASK, }, { - NPC_S_KPU2_CTAG, 0xff, NPC_ETYPE_FCOE, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_NP_IP6, + NPC_VXLANGPE_NP_MASK, }, { - NPC_S_KPU2_CTAG, 0xff, NPC_ETYPE_MPLSU, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_NP_ETH, + NPC_VXLANGPE_NP_MASK, }, { - NPC_S_KPU2_CTAG, 0xff, NPC_ETYPE_MPLSM, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_NP_NSH, + NPC_VXLANGPE_NP_MASK, }, { - NPC_S_KPU2_CTAG, 0xff, NPC_ETYPE_NSH, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_NP_MPLS, + NPC_VXLANGPE_NP_MASK, }, { - NPC_S_KPU2_CTAG, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + NPC_VXLANGPE_P, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_NP_IP, + NPC_VXLANGPE_NP_MASK, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP, 0xffff, 0x0000, 0x0000, + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + NPC_VXLANGPE_P, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_NP_IP6, + NPC_VXLANGPE_NP_MASK, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP6, 0xffff, 0x0000, 0x0000, + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + NPC_VXLANGPE_P, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_NP_ETH, + NPC_VXLANGPE_NP_MASK, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_ARP, 0xffff, 0x0000, 0x0000, + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + NPC_VXLANGPE_P, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_NP_NSH, + NPC_VXLANGPE_NP_MASK, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_RARP, 0xffff, 0x0000, 0x0000, + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + NPC_VXLANGPE_P, + NPC_VXLANGPE_P | NPC_VXLANGPE_I, + NPC_VXLANGPE_NP_MPLS, + NPC_VXLANGPE_NP_MASK, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_PTP, 0xffff, 0x0000, 0x0000, + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + NPC_VXLANGPE_P, + NPC_VXLANGPE_P, + 0x0000, + 0x0000, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_FCOE, 0xffff, 0x0000, 0x0000, + NPC_S_KPU9_VXLANGPE, 0xff, + 0x0000, + 0x0000, + 0x0000, + NPC_VXLANGPE_P, + 0x0000, + 0x0000, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_MPLSU, 0xffff, 0x0000, 0x0000, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + 0x0000, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_TRANS_ETH_BR, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_MPLSM, 0xffff, 0x0000, 0x0000, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + NPC_GENEVE_F_OAM, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_TRANS_ETH_BR, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_NSH, 0xffff, 0x0000, 0x0000, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + NPC_GENEVE_F_CRI_OPT, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_TRANS_ETH_BR, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_TRANS_ETH_BR, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, 0x0000, 0x0000, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + 0x0000, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_IP, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_SBTAG, 0xffff, 0x0000, 0x0000, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + NPC_GENEVE_F_OAM, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_IP, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_IP, 0xffff, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + NPC_GENEVE_F_CRI_OPT, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_IP, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_IP6, 0xffff, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_IP, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_ARP, 0xffff, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + 0x0000, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_IP6, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_RARP, 0xffff, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + NPC_GENEVE_F_OAM, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_IP6, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_PTP, 0xffff, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + NPC_GENEVE_F_CRI_OPT, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_IP6, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_FCOE, 0xffff, + NPC_S_KPU9_GENEVE, 0xff, + 0x0000, + 0x0000, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, + NPC_ETYPE_IP6, + 0xffff, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_MPLSU, 0xffff, + NPC_S_KPU9_GTPC, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_MPLSM, 0xffff, + NPC_S_KPU9_GTPU, 0xff, + 0x0000, + 0x0000, + NPC_GTP_PT_GTP | NPC_GTP_VER1 | NPC_GTP_MT_G_PDU, + NPC_GTP_PT_MASK | NPC_GTP_VER_MASK | NPC_GTP_MT_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_GTPU, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_MPLS_IN_UDP, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_MPLS_IN_UDP, 0xff, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_TU_MPLS_IN_UDP, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + }, + { + NPC_S_KPU9_TU_MPLS_IN_UDP, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu10_cam_entries[] = { { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_NSH, 0xffff, + NPC_S_KPU10_TU_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_MPLS, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_MPLS_PL, 0xff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_MPLS_PL, 0xff, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_MPLS_PL, 0xff, + 0x0000, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_MPLS_PL, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_MPLS_IN_VXLANGPE, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_MPLS_IN_VXLANGPE, 0xff, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_MPLS_IN_VXLANGPE, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_MPLS_S, + }, + { + NPC_S_KPU10_TU_MPLS_IN_VXLANGPE, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + 0x0000, + NPC_MPLS_S, + }, + { + NPC_S_KPU10_TU_NSH_IN_VXLANGPE, 0xff, + NPC_NSH_NP_IP, + NPC_NSH_NP_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_NSH_IN_VXLANGPE, 0xff, + NPC_NSH_NP_IP6, + NPC_NSH_NP_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_NSH_IN_VXLANGPE, 0xff, + NPC_NSH_NP_ETH, + NPC_NSH_NP_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU10_TU_NSH_IN_VXLANGPE, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu11_cam_entries[] = { { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_SBTAG, 0xffff, + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + NPC_ETYPE_ARP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_PPP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_MPLS, 0xff, + NPC_MPLS_S, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_MPLS, 0xff, + 0x0000, + NPC_MPLS_S, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_MPLS_PL, 0xff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_MPLS_PL, 0xff, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_MPLS_PL, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU11_TU_ETHER_IN_NSH, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu12_cam_entries[] = { { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_CTAG, 0xffff, + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_TCP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_UDP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_SCTP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_ICMP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_IGMP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_ESP, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_AH, + 0x00ff, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + 0x0000, + 0x0000, + NPC_IP_VER_4|NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK|NPC_IP_HDR_LEN_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_TCP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_UDP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_SCTP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_ICMP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_IGMP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_ESP, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + NPC_IPNH_AH, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + 0x0000, + 0x0000, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_ARP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP6, 0xff, + NPC_IPNH_TCP << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP6, 0xff, + NPC_IPNH_UDP << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP6, 0xff, + NPC_IPNH_SCTP << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP6, 0xff, + NPC_IPNH_ICMP << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP6, 0xff, + NPC_IPNH_ICMP6 << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP6, 0xff, + NPC_IPNH_ESP << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP6, 0xff, + NPC_IPNH_AH << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP6, 0xff, + 0x0000, + 0x0000, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP6, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu13_cam_entries[] = { { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU13_TU_IP6_EXT, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu14_cam_entries[] = { { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU14_TU_IP6_EXT, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu15_cam_entries[] = { { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU15_TU_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_FIN, + NPC_TCP_FLAGS_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + 0x0000, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_RST|NPC_TCP_FLAGS_FIN, + NPC_TCP_FLAGS_RST|NPC_TCP_FLAGS_FIN, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_URG|NPC_TCP_FLAGS_SYN, + NPC_TCP_FLAGS_URG|NPC_TCP_FLAGS_SYN, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_RST|NPC_TCP_FLAGS_SYN, + NPC_TCP_FLAGS_RST|NPC_TCP_FLAGS_SYN, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_FLAGS_SYN|NPC_TCP_FLAGS_FIN, + NPC_TCP_FLAGS_SYN|NPC_TCP_FLAGS_FIN, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + NPC_TCP_PORT_HTTP, + 0xffff, + NPC_TCP_DATA_OFFSET_5, + NPC_TCP_DATA_OFFSET_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + NPC_TCP_PORT_HTTPS, + 0xffff, + NPC_TCP_DATA_OFFSET_5, + NPC_TCP_DATA_OFFSET_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + NPC_TCP_PORT_PPTP, + 0xffff, + NPC_TCP_DATA_OFFSET_5, + NPC_TCP_DATA_OFFSET_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + 0x0000, + 0x0000, + NPC_TCP_DATA_OFFSET_5, + NPC_TCP_DATA_OFFSET_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + NPC_TCP_PORT_HTTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + NPC_TCP_PORT_HTTPS, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + NPC_TCP_PORT_PPTP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_TCP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_UDP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_SCTP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_ICMP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_IGMP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_ICMP6, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_ESP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU15_TU_AH, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_NA, 0X00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_cam kpu16_cam_entries[] = { { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_S_KPU16_TCP_DATA, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU16_HTTP_DATA, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU16_HTTPS_DATA, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU16_PPTP_DATA, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU16_UDP_DATA, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU16_UDP_PTP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, }, +}; + +static struct npc_kpu_profile_action kpu1_action_entries[] = { { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_RARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU5_IP, 14, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_PTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU5_IP6, 14, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_FCOE, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_ARP, 14, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_MPLSU, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_RARP, 14, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_MPLSM, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_PTP, 14, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU2_SBTAG, 0xff, NPC_ETYPE_NSH, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_FCOE, 14, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU2_SBTAG, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 0, 0, 0, + NPC_S_KPU2_CTAG2, 12, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU2_CTAG, 12, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP6, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 22, 0, 0, + NPC_S_KPU2_SBTAG, 12, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_ARP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU2_QINQ, 12, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_RARP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 26, 0, 0, + NPC_S_KPU2_ETAG, 12, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_ETAG, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_PTP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 18, 22, 26, 0, 0, + NPC_S_KPU2_ITAG, 12, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_FCOE, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU4_MPLS, 14, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_L_WITH_MPLS, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_MPLSU, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU4_MPLS, 14, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_L_WITH_MPLS, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_MPLSM, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU4_NSH, 14, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_L_WITH_NSH, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_NSH, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 0, 1, 0, + NPC_S_KPU3_DSA, 12, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LA, NPC_LT_LA_8023, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_QINQ, 0xffff, - NPC_ETYPE_CTAG, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LA, NPC_LT_LA_8023, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_QINQ, 0xffff, - NPC_ETYPE_QINQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_L_UNK_ETYPE, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU5_IP, 22, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU5_IP6, 22, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_ARP, 22, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_RARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_RARP, 22, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_PTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_PTP, 22, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_FCOE, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_FCOE, 22, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_MPLSU, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 0, 0, 0, + NPC_S_KPU2_CTAG2, 20, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_MPLSM, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU2_CTAG, 20, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, NPC_ETYPE_NSH, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 22, 0, 0, + NPC_S_KPU2_SBTAG, 20, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_QINQ, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU2_QINQ, 20, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 26, 0, 0, + NPC_S_KPU2_ETAG, 20, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_ETAG, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 18, 22, 26, 0, 0, + NPC_S_KPU2_ITAG, 20, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU4_MPLS, 22, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_L_WITH_MPLS, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_RARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU4_MPLS, 22, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_L_WITH_MPLS, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_PTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU4_NSH, 22, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_L_WITH_NSH, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_FCOE, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_L_UNK_ETYPE, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_MPLSU, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 14, 16, 0, 0, + NPC_S_KPU2_PREHEADER, 8, 1, + NPC_LID_LA, NPC_LT_LA_IH_8_ETHER, + 0, + 1, 0xff, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_MPLSM, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 14, 16, 0, 0, + NPC_S_KPU2_PREHEADER, 4, 1, + NPC_LID_LA, NPC_LT_LA_IH_4_ETHER, + 0, + 1, 0xff, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_NSH, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 14, 16, 0, 0, + NPC_S_KPU2_PREHEADER, 2, 1, + NPC_LID_LA, NPC_LT_LA_IH_2_ETHER, + 0, + 1, 0xff, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_LA, NPC_EC_IH_LENGTH, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_L_UNK_ETYPE, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_ITAG, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 16, 0, 0, + NPC_S_KPU2_EXDSA, 12, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_LA, NPC_EC_EDSA_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_QINQ, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU5_IP, 30, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_IP, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU5_IP6, 30, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_IP6, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_ARP, 30, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_ARP, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_RARP, 30, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_SBTAG, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_PTP, 30, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, NPC_ETYPE_CTAG, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_FCOE, 30, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_ITAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 0, 0, 0, + NPC_S_KPU2_CTAG2, 28, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2 | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ETAG, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU2_CTAG, 28, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2 | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 22, 0, 0, + NPC_S_KPU2_SBTAG, 28, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2 | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU2_QINQ, 28, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2 | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 26, 0, 0, + NPC_S_KPU2_ETAG, 28, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2 | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_ETAG, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_RARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 18, 22, 26, 0, 0, + NPC_S_KPU2_ITAG, 28, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2 | NPC_F_LA_U_HAS_TAG + | NPC_F_LA_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_IP, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU4_MPLS, 30, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2 | NPC_F_LA_L_WITH_MPLS, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_IP6, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU4_MPLS, 30, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2 | NPC_F_LA_L_WITH_MPLS, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_ARP, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU4_NSH, 30, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2 | NPC_F_LA_L_WITH_NSH, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, + NPC_F_LA_U_HAS_HIGIG2 | NPC_F_LA_L_UNK_ETYPE, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_IP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU5_IP, 38, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_IP6, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU5_IP6, 38, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_ARP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_ARP, 38, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_RARP, 38, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_PTP, 38, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP6, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_FCOE, 38, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_ARP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 0, 0, 0, + NPC_S_KPU2_CTAG2, 36, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2 + | NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU2_CTAG, 36, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2 + | NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU2_ITAG, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 22, 0, 0, + NPC_S_KPU2_SBTAG, 36, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2 + | NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, { - NPC_S_NA, 0X00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU2_QINQ, 36, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2 + | NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_cam kpu3_cam_entries[] = { { - NPC_S_KPU3_CTAG, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 26, 0, 0, + NPC_S_KPU2_ETAG, 36, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2 + | NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_ETAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_CTAG, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 18, 22, 26, 0, 0, + NPC_S_KPU2_ITAG, 36, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2 + | NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_CTAG, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU4_MPLS, 38, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2 + | NPC_F_LA_L_WITH_MPLS, + 0, 0, 0, 0, }, { - NPC_S_KPU3_CTAG, 0xff, NPC_ETYPE_RARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU4_MPLS, 38, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2 + | NPC_F_LA_L_WITH_MPLS, + 0, 0, 0, 0, }, { - NPC_S_KPU3_CTAG, 0xff, NPC_ETYPE_PTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU4_NSH, 38, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2 + | NPC_F_LA_L_WITH_NSH, + 0, 0, 0, 0, }, { - NPC_S_KPU3_CTAG, 0xff, NPC_ETYPE_FCOE, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, + NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2 + | NPC_F_LA_L_UNK_ETYPE, + 0, 0, 0, 0, }, { - NPC_S_KPU3_CTAG, 0xff, NPC_ETYPE_MPLSU, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_LA, NPC_EC_L2_K1, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LA, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, +}; + +static struct npc_kpu_profile_action kpu2_action_entries[] = { { - NPC_S_KPU3_CTAG, 0xff, NPC_ETYPE_MPLSM, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 6, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_CTAG, 0xff, NPC_ETYPE_NSH, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 6, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_CTAG, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 6, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_RARP, 6, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP6, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_PTP, 6, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_ARP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_FCOE, 6, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_RARP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 6, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_PTP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 6, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_FCOE, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 1, 0, + NPC_S_KPU4_NSH, 6, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_MPLSU, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + NPC_F_LB_U_UNK_ETYPE, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_MPLSM, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_NSH, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_RARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_PTP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_RARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_FCOE, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_MPLSU, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_MPLSM, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, NPC_ETYPE_NSH, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 1, 0, + NPC_S_KPU4_NSH, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_STAG, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG_UNK, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_CTAG, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_STAG_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP6, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_STAG, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_STAG_STAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_ARP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 24, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_RARP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 24, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_PTP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 24, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_FCOE, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_RARP, 24, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_MPLSU, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_PTP, 24, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_MPLSM, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_FCOE, 24, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_NSH, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 24, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 24, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 1, 0, + NPC_S_KPU4_NSH, 24, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU3_STAG, 24, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG_STAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_RARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU3_CTAG, 24, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_PTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_BTAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG_UNK, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_FCOE, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_UNK_ETYPE, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_MPLSU, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_MPLSM, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, NPC_ETYPE_NSH, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_QINQ, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_RARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_PTP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_FCOE, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_RARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_IP, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 1, 0, + NPC_S_KPU4_NSH, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_IP6, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG_UNK, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_ARP, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_CTAG, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_QINQ_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_IP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_QINQ, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_QINQ_QINQ, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_IP6, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_UNK_ETYPE, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_ARP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_SBTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_RARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP6, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_PTP, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_ARP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_FCOE, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, NPC_ETYPE_CTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + 1, + 0, 0, 0, 0, }, { - NPC_S_KPU3_ITAG, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + 2, + 0, 0, 0, 0, }, { - NPC_S_NA, 0X00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_NSH, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + 2, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_cam kpu4_cam_entries[] = { { - NPC_S_KPU4_MPLS, 0xff, NPC_MPLS_S, NPC_MPLS_S, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU3_CTAG, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU4_MPLS, 0xff, 0x0000, NPC_MPLS_S, - NPC_MPLS_S, NPC_MPLS_S, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 16, 20, 24, 0, 0, + NPC_S_KPU3_ITAG, 14, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_BTAG_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU4_MPLS, 0xff, 0x0000, NPC_MPLS_S, - 0x0000, NPC_MPLS_S, NPC_MPLS_S, NPC_MPLS_S, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_STAG, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_STAG, + 0, 0, 0, 0, }, { - NPC_S_KPU4_MPLS, 0xff, 0x0000, NPC_MPLS_S, - 0x0000, NPC_MPLS_S, 0x0000, NPC_MPLS_S, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_QINQ, 10, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_QINQ, + 0, 0, 0, 0, }, { - NPC_S_KPU4_NSH, 0xff, NPC_NSH_NP_IP, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 28, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU4_NSH, 0xff, NPC_NSH_NP_IP6, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 28, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU4_NSH, 0xff, NPC_NSH_NP_ETH, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 28, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG, + 0, 0, 0, 0, }, { - NPC_S_KPU4_NSH, 0xff, NPC_NSH_NP_NSH, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU3_STAG, 28, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG_STAG, + 0, 0, 0, 0, }, { - NPC_S_KPU4_NSH, 0xff, NPC_NSH_NP_MPLS, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU3_CTAG, 28, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG_CTAG, + 0, 0, 0, 0, }, { - NPC_S_NA, 0X00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_ITAG_UNK, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_cam kpu5_cam_entries[] = { { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_TCP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_UNK_ETYPE, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_UDP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 20, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_SCTP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 20, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_ICMP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 20, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_IGMP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_RARP, 20, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_ESP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 28, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_STAG_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_AH, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 28, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_STAG_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_GRE, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 28, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_STAG_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_IP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_IP6, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 24, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_STAG, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_MPLS, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 24, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_STAG, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, 0x0000, 0x0000, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 24, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_STAG, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_TCP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_UDP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 24, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_SCTP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 24, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_ICMP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 24, 1, + NPC_LID_LB, NPC_LT_LB_ITAG, + NPC_F_LB_U_MORE_TAG|NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_IGMP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_ESP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_AH, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_GRE, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_IP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_IP6, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_RARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, NPC_IPNH_MPLS, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_PTP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, 0x0000, 0x0000, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_FCOE, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_ARP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_RARP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 1, 0, + NPC_S_KPU4_NSH, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_PTP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_QINQ, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_QINQ_QINQ, + 0, 0, 0, 0, }, { - NPC_S_KPU5_FCOE, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_UNK_ETYPE, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, NPC_IPNH_TCP << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, NPC_IPNH_UDP << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, NPC_IPNH_SCTP << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, NPC_IPNH_ICMP << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_RARP, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, NPC_IPNH_ICMP6 << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_PTP, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, NPC_IPNH_ESP << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_FCOE, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, NPC_IPNH_AH << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_CTAG_C, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, NPC_IPNH_GRE << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 20, 0, 0, + NPC_S_KPU3_STAG_C, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, NPC_IPNH_IP6 << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_QINQ_C, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, NPC_IPNH_MPLS << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, 0x0000, 0x0000, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU4_MPLS, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_IP6, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 1, 0, + NPC_S_KPU4_NSH, 14, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU5_MPLS, 0xff, NPC_MPLS_S, NPC_MPLS_S, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 18, 1, + NPC_LID_LB, NPC_LT_LB_EDSA, + NPC_F_LB_L_EDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_MPLS, 0xff, NPC_MPLS_S, NPC_MPLS_S, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 18, 1, + NPC_LID_LB, NPC_LT_LB_EDSA, + NPC_F_LB_L_EDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_MPLS, 0xff, NPC_MPLS_S, NPC_MPLS_S, - 0x0000, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 18, 1, + NPC_LID_LB, NPC_LT_LB_EDSA, + NPC_F_LB_L_EDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_MPLS, 0xff, NPC_MPLS_S, NPC_MPLS_S, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_RARP, 18, 1, + NPC_LID_LB, NPC_LT_LB_EDSA, + NPC_F_LB_L_EDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_MPLS, 0xff, 0x0000, NPC_MPLS_S, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_PTP, 18, 1, + NPC_LID_LB, NPC_LT_LB_EDSA, + NPC_F_LB_L_EDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_MPLS_PL, 0xff, NPC_IP_VER_4, NPC_IP_VER_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_FCOE, 18, 1, + NPC_LID_LB, NPC_LT_LB_EDSA, + NPC_F_LB_L_EDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_MPLS_PL, 0xff, NPC_IP_VER_6, NPC_IP_VER_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU3_CTAG, 16, 1, + NPC_LID_LB, NPC_LT_LB_EDSA_VLAN, + NPC_F_LB_L_EDSA_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU5_MPLS_PL, 0xff, 0x0000, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_EDSA, + NPC_F_LB_U_UNK_ETYPE | NPC_F_LB_L_EDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_MPLS_PL, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 10, 1, + NPC_LID_LB, NPC_LT_LB_EXDSA, + NPC_F_LB_L_EXDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_NSH, 0xff, NPC_NSH_NP_IP, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 10, 1, + NPC_LID_LB, NPC_LT_LB_EXDSA, + NPC_F_LB_L_EXDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_NSH, 0xff, NPC_NSH_NP_IP6, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_EXDSA, + NPC_F_LB_L_EXDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_NSH, 0xff, NPC_NSH_NP_ETH, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_RARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_EXDSA, + NPC_F_LB_L_EXDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_NSH, 0xff, NPC_NSH_NP_NSH, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_PTP, 10, 1, + NPC_LID_LB, NPC_LT_LB_EXDSA, + NPC_F_LB_L_EXDSA, + 0, 0, 0, 0, }, { - NPC_S_KPU5_NSH, 0xff, NPC_NSH_NP_MPLS, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_FCOE, 10, 1, + NPC_LID_LB, NPC_LT_LB_EXDSA, + NPC_F_LB_L_EXDSA, + 0, 0, 0, 0, }, { - NPC_S_NA, 0X00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU3_CTAG, 8, 1, + NPC_LID_LB, NPC_LT_LB_EXDSA_VLAN, + NPC_F_LB_L_EXDSA_VLAN, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_cam kpu6_cam_entries[] = { { - NPC_S_KPU6_IP6_EXT, 0xff, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_EXDSA, + NPC_F_LB_U_UNK_ETYPE | NPC_F_LB_L_EXDSA, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_cam kpu7_cam_entries[] = { { - NPC_S_KPU7_IP6_EXT, 0xff, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, }; -static struct npc_kpu_profile_cam kpu8_cam_entries[] = { - { - NPC_S_KPU8_TCP, 0xff, NPC_TCP_PORT_HTTP, 0xffff, - NPC_TCP_DATA_OFFSET_5, NPC_TCP_DATA_OFFSET_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU8_TCP, 0xff, NPC_TCP_PORT_HTTPS, 0xffff, - NPC_TCP_DATA_OFFSET_5, NPC_TCP_DATA_OFFSET_MASK, 0x0000, 0x0000, - }, +static struct npc_kpu_profile_action kpu3_action_entries[] = { { - NPC_S_KPU8_TCP, 0xff, NPC_TCP_PORT_PPTP, 0xffff, - NPC_TCP_DATA_OFFSET_5, NPC_TCP_DATA_OFFSET_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_TCP, 0xff, 0x0000, 0x0000, - NPC_TCP_DATA_OFFSET_5, NPC_TCP_DATA_OFFSET_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_TCP, 0xff, NPC_TCP_PORT_HTTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_TCP, 0xff, NPC_TCP_PORT_HTTPS, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_TCP, 0xff, NPC_TCP_PORT_PPTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_PTP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_TCP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_FCOE, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLAN, 0xffff, - NPC_VXLAN_I, NPC_VXLAN_I, 0x0000, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLAN, 0xffff, - 0x0000, 0xffff, 0x0000, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLAN, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU4_NSH, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_NP_IP, NPC_VXLANGPE_NP_MASK, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_NP_IP6, NPC_VXLANGPE_NP_MASK, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_NP_ETH, NPC_VXLANGPE_NP_MASK, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_NP_NSH, NPC_VXLANGPE_NP_MASK, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_NP_MPLS, NPC_VXLANGPE_NP_MASK, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - NPC_VXLANGPE_P, NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_NP_IP, NPC_VXLANGPE_NP_MASK, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_PTP, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - NPC_VXLANGPE_P, NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_NP_IP6, NPC_VXLANGPE_NP_MASK, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_FCOE, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - NPC_VXLANGPE_P, NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_NP_ETH, NPC_VXLANGPE_NP_MASK, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - NPC_VXLANGPE_P, NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_NP_NSH, NPC_VXLANGPE_NP_MASK, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - NPC_VXLANGPE_P, NPC_VXLANGPE_P | NPC_VXLANGPE_I, - NPC_VXLANGPE_NP_MPLS, NPC_VXLANGPE_NP_MASK, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU4_NSH, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - NPC_VXLANGPE_P, NPC_VXLANGPE_P, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLANGPE, 0xffff, - 0x0000, NPC_VXLANGPE_P, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - 0x0000, NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_ETYPE_TRANS_ETH_BR, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - NPC_GENEVE_F_OAM, NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_ETYPE_TRANS_ETH_BR, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - NPC_GENEVE_F_CRI_OPT, NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_ETYPE_TRANS_ETH_BR, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_ETYPE_TRANS_ETH_BR, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - 0x0000, NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_ETYPE_IP, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU4_NSH, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - NPC_GENEVE_F_OAM, NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_ETYPE_IP, 0xffff, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - NPC_GENEVE_F_CRI_OPT, NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_ETYPE_IP, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, NPC_ETYPE_IP, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - 0x0000, NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_ETYPE_IP6, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - NPC_GENEVE_F_OAM, NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_ETYPE_IP6, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - NPC_GENEVE_F_CRI_OPT, - NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, NPC_ETYPE_IP6, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_PTP, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GENEVE, 0xffff, - NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, - NPC_GENEVE_F_OAM | NPC_GENEVE_F_CRI_OPT, NPC_ETYPE_IP6, 0xffff, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_FCOE, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GTPC, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GTPU, 0xffff, - NPC_GTP_PT_GTP | NPC_GTP_VER1 | NPC_GTP_MT_G_PDU, - NPC_GTP_PT_MASK | NPC_GTP_VER_MASK | NPC_GTP_MT_MASK, - 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_GTPU, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU4_NSH, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_UDP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_SCTP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_ICMP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_IGMP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_ICMP6, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_PTP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_ESP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_FCOE, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_AH, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_TRANS_ETH_BR, 0xffff, - NPC_GRE_F_KEY, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_TRANS_ETH_BR, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU4_NSH, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSU, 0xffff, - 0x0000, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSU, 0xffff, - NPC_GRE_F_CSUM, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 18, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSU, 0xffff, - NPC_GRE_F_KEY, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU5_IP6, 18, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSU, 0xffff, - NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_ARP, 18, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSU, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_KEY, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU5_RARP, 18, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSU, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 26, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSU, 0xffff, - NPC_GRE_F_KEY | NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 26, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSU, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_KEY | NPC_GRE_F_SEQ, - 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 26, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSM, 0xffff, - 0x0000, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 22, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSM, 0xffff, - NPC_GRE_F_CSUM, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 22, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSM, 0xffff, - NPC_GRE_F_KEY, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 22, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSM, 0xffff, - NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSM, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_KEY, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSM, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 22, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSM, 0xffff, - NPC_GRE_F_KEY | NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 22, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_MPLSM, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_KEY | NPC_GRE_F_SEQ, - 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 22, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_NSH, 0xffff, - 0x0000, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_NSH, 0xffff, - NPC_GRE_F_CSUM, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_NSH, 0xffff, - NPC_GRE_F_KEY, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 4, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_NSH, 0xffff, - NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 4, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_NSH, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_KEY, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 4, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_NSH, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 4, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_NSH, 0xffff, - NPC_GRE_F_KEY | NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_PTP, 4, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_NSH, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_KEY | NPC_GRE_F_SEQ, - 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_FCOE, 4, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP, 0xffff, - NPC_GRE_F_CSUM, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP, 0xffff, - NPC_GRE_F_KEY, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU4_NSH, 4, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP, 0xffff, - NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_KEY, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP, 0xffff, - NPC_GRE_F_KEY | NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_KEY | NPC_GRE_F_SEQ, - 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_PTP, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP6, 0xffff, - NPC_GRE_F_CSUM, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_FCOE, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP6, 0xffff, - NPC_GRE_F_KEY, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP6, 0xffff, - NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP6, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_KEY, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU4_NSH, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP6, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP6, 0xffff, - NPC_GRE_F_KEY | NPC_GRE_F_SEQ, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_IP6, 0xffff, - NPC_GRE_F_CSUM | NPC_GRE_F_KEY | NPC_GRE_F_SEQ, - 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, 0x0000, 0xffff, - NPC_GRE_F_ROUTE, 0x4fff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, 0x0000, 0xffff, - 0x0000, 0x4fff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, 0x0000, 0xffff, - 0x0000, 0x0003, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_PPP, 0xffff, - NPC_GRE_F_KEY | NPC_GRE_VER_1, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU4_NSH, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_PPP, 0xffff, - NPC_GRE_F_KEY | NPC_GRE_F_SEQ | NPC_GRE_VER_1, - 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_PPP, 0xffff, - NPC_GRE_F_KEY | NPC_GRE_F_ACK | NPC_GRE_VER_1, - 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, NPC_ETYPE_PPP, 0xffff, - NPC_GRE_F_KEY | NPC_GRE_F_SEQ | NPC_GRE_F_ACK | NPC_GRE_VER_1, - 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, 0x0000, 0xffff, - 0x2001, 0xef7f, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU8_GRE, 0xff, 0x0000, 0xffff, - 0x0001, 0x0003, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_NA, 0X00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_PTP, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_cam kpu9_cam_entries[] = { { - NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 0xff, NPC_MPLS_S, NPC_MPLS_S, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_FCOE, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 0xff, 0x0000, NPC_MPLS_S, - NPC_MPLS_S, NPC_MPLS_S, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 0xff, 0x0000, NPC_MPLS_S, - 0x0000, NPC_MPLS_S, NPC_MPLS_S, NPC_MPLS_S, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 0xff, 0x0000, NPC_MPLS_S, - 0x0000, NPC_MPLS_S, 0x0000, NPC_MPLS_S, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU4_NSH, 8, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_MPLS, 0xff, NPC_MPLS_S, NPC_MPLS_S, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_MPLS, 0xff, 0x0000, NPC_MPLS_S, - NPC_MPLS_S, NPC_MPLS_S, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_MPLS, 0xff, 0x0000, NPC_MPLS_S, - 0x0000, NPC_MPLS_S, NPC_MPLS_S, NPC_MPLS_S, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_MPLS, 0xff, 0x0000, NPC_MPLS_S, - 0x0000, NPC_MPLS_S, 0x0000, NPC_MPLS_S, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_NSH, 0xff, NPC_NSH_NP_IP, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_PTP, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_NSH, 0xff, NPC_NSH_NP_IP6, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_FCOE, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_NSH, 0xff, NPC_NSH_NP_ETH, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_NSH, 0xff, NPC_NSH_NP_NSH, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU9_TU_NSH, 0xff, NPC_NSH_NP_MPLS, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU4_NSH, 4, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, { - NPC_S_NA, 0X00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_cam kpu10_cam_entries[] = { { - NPC_S_KPU10_TU_MPLS, 0xff, NPC_MPLS_S, NPC_MPLS_S, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 10, 1, + NPC_LID_LB, NPC_LT_LB_DSA, + NPC_F_LB_L_DSA, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_MPLS, 0xff, NPC_MPLS_S, NPC_MPLS_S, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 10, 1, + NPC_LID_LB, NPC_LT_LB_DSA, + NPC_F_LB_L_DSA, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_MPLS, 0xff, NPC_MPLS_S, NPC_MPLS_S, - 0x0000, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_DSA, + NPC_F_LB_L_DSA, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_MPLS, 0xff, NPC_MPLS_S, NPC_MPLS_S, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 10, 1, + NPC_LID_LB, NPC_LT_LB_DSA, + NPC_F_LB_L_DSA, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_MPLS, 0xff, 0x0000, NPC_MPLS_S, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_PTP, 10, 1, + NPC_LID_LB, NPC_LT_LB_DSA, + NPC_F_LB_L_DSA, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_MPLS_PL, 0xff, NPC_IP_VER_4, NPC_IP_VER_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_FCOE, 10, 1, + NPC_LID_LB, NPC_LT_LB_DSA, + NPC_F_LB_L_DSA, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_MPLS_PL, 0xff, NPC_IP_VER_6, NPC_IP_VER_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 14, 1, + NPC_LID_LB, NPC_LT_LB_DSA_VLAN, + NPC_F_LB_L_DSA_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_MPLS_PL, 0xff, 0x0000, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU5_IP6, 14, 1, + NPC_LID_LB, NPC_LT_LB_DSA_VLAN, + NPC_F_LB_L_DSA_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_MPLS_PL, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_ARP, 14, 1, + NPC_LID_LB, NPC_LT_LB_DSA_VLAN, + NPC_F_LB_L_DSA_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_NSH, 0xff, NPC_NSH_NP_IP, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_RARP, 14, 1, + NPC_LID_LB, NPC_LT_LB_DSA_VLAN, + NPC_F_LB_L_DSA_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_NSH, 0xff, NPC_NSH_NP_IP6, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_PTP, 14, 1, + NPC_LID_LB, NPC_LT_LB_DSA_VLAN, + NPC_F_LB_L_DSA_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_NSH, 0xff, NPC_NSH_NP_ETH, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU5_FCOE, 14, 1, + NPC_LID_LB, NPC_LT_LB_DSA_VLAN, + NPC_F_LB_L_DSA_VLAN, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_NSH, 0xff, NPC_NSH_NP_NSH, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_DSA_VLAN, + NPC_F_LB_U_UNK_ETYPE | NPC_F_LB_L_DSA, + 0, 0, 0, 0, }, { - NPC_S_KPU10_TU_NSH, 0xff, NPC_NSH_NP_MPLS, NPC_NSH_NP_MASK, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LB, NPC_LT_LB_DSA, + NPC_F_LB_U_UNK_ETYPE | NPC_F_LB_L_DSA_VLAN, + 0, 0, 0, 0, }, { - NPC_S_NA, 0X00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K3, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, }; -static struct npc_kpu_profile_cam kpu11_cam_entries[] = { - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_IP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_IP6, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_ARP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP, 0xffff, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_IP6, 0xffff, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_CTAG, 0xffff, - NPC_ETYPE_ARP, 0xffff, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_CTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_IP, 0xffff, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_IP6, 0xffff, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_ARP, 0xffff, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_CTAG, 0xffff, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_IP, 0xffff, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_IP6, 0xffff, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_SBTAG, 0xffff, - NPC_ETYPE_ARP, 0xffff, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_SBTAG, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_QINQ, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_IP, 0xffff, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_QINQ, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_IP6, 0xffff, - }, - { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_QINQ, 0xffff, - NPC_ETYPE_CTAG, 0xffff, NPC_ETYPE_ARP, 0xffff, - }, +static struct npc_kpu_profile_action kpu4_action_entries[] = { { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_QINQ, 0xffff, - NPC_ETYPE_CTAG, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU5_MPLS_PL, 4, 1, + NPC_LID_LC, NPC_LT_LC_MPLS, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_QINQ, 0xffff, - NPC_ETYPE_IP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU5_MPLS_PL, 8, 1, + NPC_LID_LC, NPC_LT_LC_MPLS, + NPC_F_LC_L_MPLS_2_LABELS, + 0, 0, 0, 0, }, { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_QINQ, 0xffff, - NPC_ETYPE_IP6, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU5_MPLS_PL, 12, 1, + NPC_LID_LC, NPC_LT_LC_MPLS, + NPC_F_LC_L_MPLS_3_LABELS, + 0, 0, 0, 0, }, { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_QINQ, 0xffff, - NPC_ETYPE_ARP, 0xffff, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 4, 0, 0, 0, + NPC_S_KPU5_MPLS, 12, 1, + NPC_LID_LC, NPC_LT_LC_MPLS, + NPC_F_LC_L_MPLS_4_LABELS, + 0, 0, 0, 0, }, { - NPC_S_KPU11_TU_ETHER, 0xff, NPC_ETYPE_QINQ, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 7, 0, + NPC_S_KPU12_TU_IP, 0, 1, + NPC_LID_LC, NPC_LT_LC_NSH, + 0, + 1, 0x3f, 0, 2, }, { - NPC_S_KPU11_TU_ETHER, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 7, 0, + NPC_S_KPU12_TU_IP6, 0, 1, + NPC_LID_LC, NPC_LT_LC_NSH, + 0, + 1, 0x3f, 0, 2, }, { - NPC_S_KPU11_TU_PPP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 6, 0, + NPC_S_KPU11_TU_ETHER, 0, 1, + NPC_LID_LC, NPC_LT_LC_NSH, + 0, + 1, 0x3f, 0, 2, }, { - NPC_S_KPU11_TU_MPLS_IN_NSH, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 4, 0, + NPC_S_KPU9_TU_MPLS_IN_NSH, 0, 1, + NPC_LID_LC, NPC_LT_LC_NSH, + 0, + 1, 0x3f, 0, 2, }, { - NPC_S_KPU11_TU_3RD_NSH, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_LC, NPC_EC_NSH_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_NSH, + 0, + 0, 0, 0, 0, }, { - NPC_S_NA, 0X00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_LB, NPC_EC_L2_K4, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, }; -static struct npc_kpu_profile_cam kpu12_cam_entries[] = { - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_TCP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_UDP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_SCTP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_ICMP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_IGMP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_ESP, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_AH, 0x00ff, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, 0x0000, 0x0000, - NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, - NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_TCP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_UDP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_SCTP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_ICMP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_IGMP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_ESP, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_AH, 0x00ff, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, 0x0000, 0x0000, - NPC_IP_VER_4, NPC_IP_VER_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_ARP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP6, 0xff, NPC_IPNH_TCP << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP6, 0xff, NPC_IPNH_UDP << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, - }, - { - NPC_S_KPU12_TU_IP6, 0xff, NPC_IPNH_SCTP << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, - }, +static struct npc_kpu_profile_action kpu5_action_entries[] = { { - NPC_S_KPU12_TU_IP6, 0xff, NPC_IPNH_ICMP << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_LC, NPC_EC_IP_TTL_0, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU12_TU_IP6, 0xff, NPC_IPNH_ICMP6 << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_LC, NPC_EC_IP_FRAG_OFFSET_1, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP, + NPC_F_LC_U_IP_FRAG, + 0, 0, 0, 0, }, { - NPC_S_KPU12_TU_IP6, 0xff, NPC_IPNH_ESP << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 12, 0, 2, 0, + NPC_S_KPU8_TCP, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU12_TU_IP6, 0xff, NPC_IPNH_AH << 8, 0xff00, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU8_UDP, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU12_TU_IP6, 0xff, 0x0000, 0x0000, - NPC_IP_VER_6, NPC_IP_VER_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_SCTP, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU12_TU_IP6, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_ICMP, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + 0, + 0, 0, 0, 0, }, { - NPC_S_NA, 0X00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_IGMP, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_cam kpu13_cam_entries[] = { { - NPC_S_KPU13_TU_IP6_EXT, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_ESP, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_cam kpu14_cam_entries[] = { { - NPC_S_KPU14_TU_IP6_EXT, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_AH, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_cam kpu15_cam_entries[] = { { - NPC_S_KPU15_TU_TCP, 0xff, NPC_TCP_PORT_HTTP, 0xffff, - NPC_TCP_DATA_OFFSET_5, NPC_TCP_DATA_OFFSET_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU8_GRE, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU15_TU_TCP, 0xff, NPC_TCP_PORT_HTTPS, 0xffff, - NPC_TCP_DATA_OFFSET_5, NPC_TCP_DATA_OFFSET_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 6, 0, + NPC_S_KPU12_TU_IP, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + NPC_F_LC_L_IP_IN_IP, + 0, 0, 0, 0, }, { - NPC_S_KPU15_TU_TCP, 0xff, NPC_TCP_PORT_PPTP, 0xffff, - NPC_TCP_DATA_OFFSET_5, NPC_TCP_DATA_OFFSET_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 6, 0, + NPC_S_KPU12_TU_IP6, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + NPC_F_LC_L_6TO4, + 0, 0, 0, 0, }, { - NPC_S_KPU15_TU_TCP, 0xff, 0x0000, 0x0000, - NPC_TCP_DATA_OFFSET_5, NPC_TCP_DATA_OFFSET_MASK, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 3, 0, + NPC_S_KPU9_TU_MPLS_IN_IP, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + NPC_F_LC_L_MPLS_IN_IP, + 0, 0, 0, 0, }, { - NPC_S_KPU15_TU_TCP, 0xff, NPC_TCP_PORT_HTTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP, + NPC_F_LC_U_UNK_PROTO, + 0, 0, 0, 0, }, { - NPC_S_KPU15_TU_TCP, 0xff, NPC_TCP_PORT_HTTPS, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP, + NPC_F_LC_U_IP_FRAG, + 0, 0, 0, 0, }, { - NPC_S_KPU15_TU_TCP, 0xff, NPC_TCP_PORT_PPTP, 0xffff, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 12, 0, 2, 0, + NPC_S_KPU8_TCP, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + 0, + 0, 0xf, 0, 2, }, { - NPC_S_KPU15_TU_TCP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 8, 10, 2, 0, + NPC_S_KPU8_UDP, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + 0, + 0, 0xf, 0, 2, }, { - NPC_S_KPU15_TU_UDP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_SCTP, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + 0, + 0, 0xf, 0, 2, }, { - NPC_S_KPU15_TU_SCTP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_ICMP, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + 0, + 0, 0xf, 0, 2, }, { - NPC_S_KPU15_TU_ICMP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_IGMP, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + 0, + 0, 0xf, 0, 2, }, { - NPC_S_KPU15_TU_IGMP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_ESP, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + 0, + 0, 0xf, 0, 2, }, { - NPC_S_KPU15_TU_ICMP6, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_AH, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + 0, + 0, 0xf, 0, 2, }, { - NPC_S_KPU15_TU_ESP, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU8_GRE, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + 0, + 0, 0xf, 0, 2, }, { - NPC_S_KPU15_TU_AH, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 6, 0, + NPC_S_KPU12_TU_IP, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + NPC_F_LC_L_IP_IN_IP, + 0, 0xf, 0, 2, }, { - NPC_S_NA, 0X00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 6, 0, + NPC_S_KPU12_TU_IP6, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + NPC_F_LC_L_6TO4, + 0, 0xf, 0, 2, }, -}; - -static struct npc_kpu_profile_cam kpu16_cam_entries[] = { { - NPC_S_KPU16_TCP_DATA, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 3, 0, + NPC_S_KPU9_TU_MPLS_IN_IP, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + NPC_F_LC_L_MPLS_IN_IP, + 0, 0xf, 0, 2, }, { - NPC_S_KPU16_HTTP_DATA, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + NPC_F_LC_U_UNK_PROTO, + 0, 0, 0, 0, }, { - NPC_S_KPU16_HTTPS_DATA, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + NPC_F_LC_U_IP_FRAG, + 0, 0, 0, 0, }, { - NPC_S_KPU16_PPTP_DATA, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_LC, NPC_EC_IP_VER, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP, + 0, + 0, 0, 0, 0, }, { - NPC_S_KPU16_UDP_DATA, 0xff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_ARP, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_action kpu1_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU5_IP, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_RARP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU5_IP6, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_PTP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 3, 0, NPC_S_KPU5_ARP, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_FCOE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 3, 0, NPC_S_KPU5_RARP, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LC, NPC_EC_IP6_HOP_0, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 3, 0, NPC_S_KPU5_PTP, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 12, 0, 2, 0, + NPC_S_KPU8_TCP, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 3, 0, NPC_S_KPU5_FCOE, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU8_UDP, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 0, - 0, 0, NPC_S_KPU2_CTAG, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_ETHER_VLAN, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_SCTP, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 20, - 0, 0, NPC_S_KPU2_SBTAG, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_ETHER_VLAN, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_ICMP, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 0, - 0, 0, NPC_S_KPU2_QINQ, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_ETHER_VLAN, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_ICMP6, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 10, 24, - 0, 0, NPC_S_KPU2_ETAG, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_ETHER_ETAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_GRE, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 16, 20, 24, - 0, 0, NPC_S_KPU2_ITAG, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_ETHER_ITAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 6, 0, + NPC_S_KPU12_TU_IP6, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + NPC_F_LC_L_IP6_TUN_IP6, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 2, 0, NPC_S_KPU4_MPLS, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_ETHER_MPLS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 3, 0, + NPC_S_KPU9_TU_MPLS_IN_IP, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + NPC_F_LC_L_IP6_MPLS_IN_IP, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 2, 0, NPC_S_KPU4_MPLS, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_ETHER_MPLS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU6_IP6_HOP_DEST, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6_EXT, + NPC_F_LC_L_EXT_HOP, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 2, 0, NPC_S_KPU4_NSH, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_ETHER_NSH, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU6_IP6_HOP_DEST, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6_EXT, + NPC_F_LC_L_EXT_DEST, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LA, NPC_LT_LA_8023, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU6_IP6_ROUT, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6_EXT, + NPC_F_LC_L_EXT_ROUT, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LA, NPC_LT_LA_8023, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 2, 0, 0, 0, + NPC_S_KPU6_IP6_FRAG, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6_EXT, + NPC_F_LC_U_IP6_FRAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_ETYPE_UNK, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_ESP, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6_EXT, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU5_IP, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_AH, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6_EXT, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU5_IP6, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP6_EXT, + NPC_F_LC_L_EXT_MOBILITY, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 3, 0, NPC_S_KPU5_ARP, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP6_EXT, + NPC_F_LC_L_EXT_HOSTID, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 3, 0, NPC_S_KPU5_RARP, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP6_EXT, + NPC_F_LC_L_EXT_SHIM6, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 3, 0, NPC_S_KPU5_PTP, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + NPC_F_LC_U_UNK_PROTO, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 3, 0, NPC_S_KPU5_FCOE, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI, 0, 0, - 0, 0, + NPC_ERRLEV_LC, NPC_EC_IP6_VER, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 0, - 0, 0, NPC_S_KPU2_CTAG, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI_VLAN, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 6, 0, + NPC_S_KPU12_TU_IP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 20, - 0, 0, NPC_S_KPU2_SBTAG, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI_VLAN, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 6, 0, + NPC_S_KPU12_TU_IP6, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 0, - 0, 0, NPC_S_KPU2_QINQ, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI_VLAN, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 5, 0, + NPC_S_KPU11_TU_ETHER, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 10, 24, - 0, 0, NPC_S_KPU2_ETAG, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI_ETAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 5, 0, + NPC_S_KPU11_TU_ETHER, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 16, 20, 24, - 0, 0, NPC_S_KPU2_ITAG, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI_ITAG, 0, 0, - 0, 0, + NPC_ERRLEV_LB, NPC_EC_MPLS_2MANY, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 2, 0, NPC_S_KPU4_MPLS, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI_MPLS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 6, 0, + NPC_S_KPU12_TU_IP, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 2, 0, NPC_S_KPU4_MPLS, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI_MPLS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 6, 0, + NPC_S_KPU12_TU_IP6, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 2, 0, NPC_S_KPU4_NSH, 14, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_PKI_NSH, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 5, 0, + NPC_S_KPU11_TU_ETHER, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LA, NPC_LT_LA_ETHER, NPC_F_ETYPE_UNK, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 5, 0, + NPC_S_KPU11_TU_ETHER, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LA, NPC_EC_L2_K1, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LA, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LC, NPC_EC_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, }; -static struct npc_kpu_profile_action kpu2_action_entries[] = { - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_RARP, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_PTP, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_FCOE, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 1, 0, NPC_S_KPU4_NSH, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, NPC_F_ETYPE_UNK, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 8, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 8, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 8, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_RARP, 8, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_PTP, 8, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_FCOE, 8, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 8, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 8, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 1, 0, NPC_S_KPU4_NSH, 8, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_CTAG_UNK, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 0, - 0, 0, NPC_S_KPU3_CTAG, 8, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 0, - 0, 0, NPC_S_KPU3_STAG, 8, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_STAG_STAG_STAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 22, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 22, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 22, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_RARP, 22, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_PTP, 22, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_FCOE, 22, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 22, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 22, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 1, 0, NPC_S_KPU4_NSH, 22, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU3_STAG, 22, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG_STAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU3_CTAG, 22, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_BTAG_ITAG_UNK, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 4, 1, - NPC_LID_LB, NPC_LT_LB_STAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 4, 1, - NPC_LID_LB, NPC_LT_LB_STAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 4, 1, - NPC_LID_LB, NPC_LT_LB_STAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_RARP, 4, 1, - NPC_LID_LB, NPC_LT_LB_STAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_PTP, 4, 1, - NPC_LID_LB, NPC_LT_LB_STAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_FCOE, 4, 1, - NPC_LID_LB, NPC_LT_LB_STAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 4, 1, - NPC_LID_LB, NPC_LT_LB_STAG, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 4, 1, - NPC_LID_LB, NPC_LT_LB_STAG, 0, 0, 0, - 0, 0, - }, +static struct npc_kpu_profile_action kpu6_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 1, 0, NPC_S_KPU4_NSH, 4, 1, - NPC_LID_LB, NPC_LT_LB_STAG, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LB, NPC_LT_LB_STAG, NPC_F_ETYPE_UNK, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 12, 0, 1, 0, + NPC_S_KPU8_TCP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 8, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 8, 10, 1, 0, + NPC_S_KPU8_UDP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 8, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_SCTP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 8, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_ICMP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_RARP, 8, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_ICMP6, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_PTP, 8, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_ESP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_FCOE, 8, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_AH, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 8, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_GRE, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 8, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 5, 0, + NPC_S_KPU12_TU_IP6, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 1, 0, NPC_S_KPU4_NSH, 8, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU9_TU_MPLS_IN_IP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_CTAG_UNK, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 0, - 0, 0, NPC_S_KPU3_CTAG, 8, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_QINQ_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 12, 0, 1, 0, + NPC_S_KPU8_TCP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 0, - 0, 0, NPC_S_KPU3_QINQ, 8, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_QINQ_QINQ_QINQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 8, 10, 1, 0, + NPC_S_KPU8_UDP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 4, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_SCTP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 4, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_ICMP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 4, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_ICMP6, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_RARP, 4, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_ESP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_PTP, 4, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_AH, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_FCOE, 4, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_GRE, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 4, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 5, 0, + NPC_S_KPU12_TU_IP6, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 4, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU9_TU_MPLS_IN_IP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 1, 0, NPC_S_KPU4_NSH, 4, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU7_IP6_ROUT, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LB, NPC_LT_LB_QINQ, NPC_F_ETYPE_UNK, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 2, 0, 0, 0, + NPC_S_KPU7_IP6_FRAG, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 12, 0, 1, 0, + NPC_S_KPU8_TCP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 8, 10, 1, 0, + NPC_S_KPU8_UDP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_RARP, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_SCTP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_PTP, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_ICMP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_FCOE, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_ICMP6, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, 1, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_ESP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_MPLS, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, 2, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_AH, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 1, 0, NPC_S_KPU4_NSH, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, 2, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_GRE, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU3_CTAG, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_ETAG_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 5, 0, + NPC_S_KPU12_TU_IP6, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 16, 20, 24, - 0, 0, NPC_S_KPU3_ITAG, 12, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_ETAG_BTAG_ITAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU9_TU_MPLS_IN_IP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 0, - 0, 0, NPC_S_KPU3_STAG, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_ETAG_STAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 2, 0, 0, 0, + NPC_S_KPU7_IP6_FRAG, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 0, - 0, 0, NPC_S_KPU3_QINQ, 8, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_ETAG_QINQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 26, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_ETAG_ITAG, 0, 0, - 0, 0, + NPC_ERRLEV_LC, NPC_EC_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, +}; + +static struct npc_kpu_profile_action kpu7_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 26, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_ETAG_ITAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 26, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_ETAG_ITAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 12, 0, 0, 0, + NPC_S_KPU8_TCP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU3_STAG, 26, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_ETAG_ITAG_STAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 8, 10, 0, 0, + NPC_S_KPU8_UDP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU3_CTAG, 26, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_ETAG_ITAG_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_SCTP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_ETAG_ITAG_UNK, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_ICMP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_ETYPE_UNK, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_ICMP6, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 18, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_ESP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 18, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_AH, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 18, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_GRE, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_RARP, 18, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 4, 0, + NPC_S_KPU12_TU_IP6, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 26, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, NPC_F_ITAG_STAG_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU9_TU_MPLS_IN_IP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 26, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, NPC_F_ITAG_STAG_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 26, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, NPC_F_ITAG_STAG_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 12, 0, 0, 0, + NPC_S_KPU8_TCP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 8, 10, 0, 0, + NPC_S_KPU8_UDP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 22, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, NPC_F_ITAG_STAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_SCTP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 22, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, NPC_F_ITAG_STAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_ICMP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 22, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, NPC_F_ITAG_STAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_ICMP6, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_ESP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 22, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, NPC_F_ITAG_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_AH, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 22, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, NPC_F_ITAG_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_GRE, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 22, 1, - NPC_LID_LB, NPC_LT_LB_ITAG, NPC_F_ITAG_CTAG, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 4, 0, + NPC_S_KPU12_TU_IP6, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 1, 0, + NPC_S_KPU9_TU_MPLS_IN_IP, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LC, NPC_EC_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, }; -static struct npc_kpu_profile_action kpu3_action_entries[] = { +static struct npc_kpu_profile_action kpu8_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 1, 0, NPC_S_KPU5_IP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LD, NPC_EC_TCP_FLAGS_FIN_ONLY, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 1, 0, NPC_S_KPU5_IP6, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LD, NPC_EC_TCP_FLAGS_ZERO, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_ARP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LD, NPC_EC_TCP_FLAGS_RST_FIN, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_RARP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LD, NPC_EC_TCP_FLAGS_URG_SYN, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_PTP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LD, NPC_EC_TCP_FLAGS_RST_SYN, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_FCOE, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LD, NPC_EC_TCP_FLAGS_SYN_FIN, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU4_MPLS, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 7, 0, + NPC_S_KPU16_HTTP_DATA, 20, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU4_MPLS, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 7, 0, + NPC_S_KPU16_HTTPS_DATA, 20, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU4_NSH, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 7, 0, + NPC_S_KPU16_PPTP_DATA, 20, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 7, 0, + NPC_S_KPU16_TCP_DATA, 20, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + NPC_F_LD_L_TCP_UNK_PORT, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 1, 0, NPC_S_KPU5_IP, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 7, 0, + NPC_S_KPU16_HTTP_DATA, 0, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + NPC_F_LD_L_TCP_HAS_OPTIONS, + 12, 0xf0, 1, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 1, 0, NPC_S_KPU5_IP6, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 7, 0, + NPC_S_KPU16_HTTPS_DATA, 0, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + NPC_F_LD_L_TCP_HAS_OPTIONS, + 12, 0xf0, 1, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_ARP, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 7, 0, + NPC_S_KPU16_PPTP_DATA, 0, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + NPC_F_LD_L_TCP_HAS_OPTIONS, + 12, 0xf0, 1, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_RARP, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 7, 0, + NPC_S_KPU16_TCP_DATA, 0, 1, + NPC_LID_LD, NPC_LT_LD_TCP, + NPC_F_LD_L_TCP_UNK_PORT_HAS_OPTIONS, + 12, 0xf0, 1, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_PTP, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 2, 0, 0, + NPC_S_KPU9_VXLAN, 8, 1, + NPC_LID_LD, NPC_LT_LD_UDP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_FCOE, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 2, 0, 0, + NPC_S_KPU9_VXLANGPE, 8, 1, + NPC_LID_LD, NPC_LT_LD_UDP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU4_MPLS, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 2, 0, 0, + NPC_S_KPU9_GENEVE, 8, 1, + NPC_LID_LD, NPC_LT_LD_UDP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU4_MPLS, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 2, 0, 0, + NPC_S_KPU9_GTPC, 8, 1, + NPC_LID_LD, NPC_LT_LD_UDP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU4_NSH, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 2, 0, 0, + NPC_S_KPU9_GTPU, 8, 1, + NPC_LID_LD, NPC_LT_LD_UDP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 1, 0, NPC_S_KPU5_IP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_KPU16_UDP_PTP, 0, 1, + NPC_LID_LD, NPC_LT_LD_UDP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 1, 0, NPC_S_KPU5_IP6, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_KPU16_UDP_PTP, 0, 1, + NPC_LID_LD, NPC_LT_LD_UDP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_ARP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_UDP, 8, 1, + NPC_LID_LD, NPC_LT_LD_UDP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_RARP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 7, 0, + NPC_S_KPU16_UDP_DATA, 8, 1, + NPC_LID_LD, NPC_LT_LD_UDP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU4_MPLS, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_SCTP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU4_MPLS, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_ICMP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU4_NSH, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_IGMP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_ICMP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 1, 0, NPC_S_KPU5_IP, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_ESP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 1, 0, NPC_S_KPU5_IP6, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_AH, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_ARP, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 2, 0, + NPC_S_KPU11_TU_ETHER, 8, 1, + NPC_LID_LD, NPC_LT_LD_NVGRE, + NPC_F_LD_L_GRE_NVGRE, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_RARP, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LD, NPC_EC_NVGRE, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LD, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_PTP, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 4, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_FCOE, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU4_MPLS, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU4_MPLS, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU4_NSH, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 1, 0, NPC_S_KPU5_IP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 1, 0, NPC_S_KPU5_IP6, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_ARP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 16, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_RARP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 4, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_PTP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_FCOE, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU4_MPLS, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU4_MPLS, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU4_NSH, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU5_IP, 18, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU9_TU_MPLS_IN_GRE, 16, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU5_IP6, 18, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU9_TU_NSH_IN_GRE, 4, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_ARP, 18, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU9_TU_NSH_IN_GRE, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU5_RARP, 18, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU9_TU_NSH_IN_GRE, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 1, 0, NPC_S_KPU5_IP, 26, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU9_TU_NSH_IN_GRE, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 1, 0, NPC_S_KPU5_IP6, 26, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU9_TU_NSH_IN_GRE, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_ARP, 26, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU9_TU_NSH_IN_GRE, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 1, 0, NPC_S_KPU5_IP, 22, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU9_TU_NSH_IN_GRE, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 1, 0, NPC_S_KPU5_IP6, 22, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU9_TU_NSH_IN_GRE, 16, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_ARP, 22, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU12_TU_IP, 4, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU12_TU_IP, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU12_TU_IP, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 1, 0, NPC_S_KPU5_IP, 22, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU12_TU_IP, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 1, 0, NPC_S_KPU5_IP6, 22, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU12_TU_IP, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU5_ARP, 22, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU12_TU_IP, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU12_TU_IP, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU12_TU_IP, 16, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU12_TU_IP6, 4, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_action kpu4_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU5_MPLS_PL, 4, 1, - NPC_LID_LC, NPC_LT_LC_MPLS, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU12_TU_IP6, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU5_MPLS_PL, 8, 1, - NPC_LID_LC, NPC_LT_LC_MPLS, NPC_F_MPLS_2_LABELS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU12_TU_IP6, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU5_MPLS_PL, 12, 1, - NPC_LID_LC, NPC_LT_LC_MPLS, NPC_F_MPLS_3_LABELS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU12_TU_IP6, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 4, 0, - 0, 0, NPC_S_KPU5_MPLS, 12, 1, - NPC_LID_LC, NPC_LT_LC_MPLS, NPC_F_MPLS_4_LABELS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU12_TU_IP6, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 7, 0, NPC_S_KPU12_TU_IP, 0, 1, - NPC_LID_LC, NPC_LT_LC_NSH, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU12_TU_IP6, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 7, 0, NPC_S_KPU12_TU_IP6, 0, 1, - NPC_LID_LC, NPC_LT_LC_NSH, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU12_TU_IP6, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 6, 0, NPC_S_KPU11_TU_ETHER, 0, 1, - NPC_LID_LC, NPC_LT_LC_NSH, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 3, 0, + NPC_S_KPU12_TU_IP6, 16, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU5_NSH, 0, 1, - NPC_LID_LC, NPC_LT_LC_NSH, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_ROUTE, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 4, 0, NPC_S_KPU9_TU_MPLS, 0, 1, - NPC_LID_LC, NPC_LT_LC_NSH, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_UNK_PROTO, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K4, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LC, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LD, NPC_EC_GRE, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LD, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_action kpu5_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 12, 0, - 2, 0, NPC_S_KPU8_TCP, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU11_TU_PPP, 8, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_VER1, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 8, 10, - 2, 0, NPC_S_KPU8_UDP, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU11_TU_PPP, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_VER1_HAS_SEQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_SCTP, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU11_TU_PPP, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_VER1_HAS_ACK, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_ICMP, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU11_TU_PPP, 16, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_VER1_HAS_SEQ_ACK, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_IGMP, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_VER1_UNK_PROTO, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU8_ESP, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LD, NPC_EC_GRE_VER1, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LD, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU8_AH, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LD, NPC_EC_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LD, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, +}; + +static struct npc_kpu_profile_action kpu9_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 2, 0, NPC_S_KPU8_GRE, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 4, 1, + NPC_LID_LE, NPC_LT_LE_TU_MPLS_IN_GRE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 6, 0, NPC_S_KPU12_TU_IP, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_IP_IN_IP, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 8, 1, + NPC_LID_LE, NPC_LT_LE_TU_MPLS_IN_GRE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 6, 0, NPC_S_KPU12_TU_IP6, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_6TO4, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 12, 1, + NPC_LID_LE, NPC_LT_LE_TU_MPLS_IN_GRE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 3, 0, NPC_S_KPU9_TU_MPLS, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_MPLS_IN_IP, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 4, 0, 0, 0, + NPC_S_KPU10_TU_MPLS, 12, 1, + NPC_LID_LE, NPC_LT_LE_TU_MPLS_IN_GRE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_UNK_PROTO, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 4, 1, + NPC_LID_LD, NPC_LT_LD_TU_MPLS_IN_NSH, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 12, 0, - 2, 0, NPC_S_KPU8_TCP, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 8, 1, + NPC_LID_LD, NPC_LT_LD_TU_MPLS_IN_NSH, + NPC_F_LD_L_MPLS_2_LABELS, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 8, 10, - 2, 0, NPC_S_KPU8_UDP, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 12, 1, + NPC_LID_LD, NPC_LT_LD_TU_MPLS_IN_NSH, + NPC_F_LD_L_MPLS_3_LABELS, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_SCTP, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 4, 0, 0, 0, + NPC_S_KPU10_TU_MPLS, 12, 1, + NPC_LID_LD, NPC_LT_LD_TU_MPLS_IN_NSH, + NPC_F_LD_L_MPLS_4_LABELS, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_ICMP, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 4, 1, + NPC_LID_LD, NPC_LT_LD_TU_MPLS_IN_IP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_IGMP, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 8, 1, + NPC_LID_LD, NPC_LT_LD_TU_MPLS_IN_IP, + NPC_F_LD_L_MPLS_2_LABELS, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU8_ESP, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 12, 1, + NPC_LID_LD, NPC_LT_LD_TU_MPLS_IN_IP, + NPC_F_LD_L_MPLS_3_LABELS, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU8_AH, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 4, 0, 0, 0, + NPC_S_KPU10_TU_MPLS, 12, 1, + NPC_LID_LD, NPC_LT_LD_TU_MPLS_IN_IP, + NPC_F_LD_L_MPLS_4_LABELS, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 2, 0, NPC_S_KPU8_GRE, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU12_TU_IP, 0, 1, + NPC_LID_LE, NPC_LT_LE_TU_NSH_IN_GRE, + 0, + 1, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 6, 0, NPC_S_KPU12_TU_IP, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_IP_IN_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU12_TU_IP6, 0, 1, + NPC_LID_LE, NPC_LT_LE_TU_NSH_IN_GRE, + 0, + 1, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 6, 0, NPC_S_KPU12_TU_IP6, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_6TO4_HAS_OPTIONS, 0, 0xf, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 1, 0, + NPC_S_KPU11_TU_ETHER, 0, 1, + NPC_LID_LE, NPC_LT_LE_TU_NSH_IN_GRE, + 0, + 1, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 3, 0, NPC_S_KPU9_TU_MPLS, 20, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_MPLS_IN_IP_HAS_OPTIONS, - 0, 0xf, 0, 2, + NPC_ERRLEV_LE, NPC_EC_NSH_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LE, NPC_LT_LE_TU_NSH_IN_GRE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, NPC_F_IP_UNK_PROTO_HAS_OPTIONS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 1, 0, + NPC_S_KPU11_TU_ETHER, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLAN, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LC, NPC_EC_IP_VER, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 1, 0, + NPC_S_KPU11_TU_ETHER, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLAN, + NPC_F_LE_L_VXLAN_NOVNI, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LC, NPC_LT_LC_ARP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LE, NPC_EC_VXLAN, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LE, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LC, NPC_LT_LC_RARP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU12_TU_IP, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LC, NPC_LT_LC_PTP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU12_TU_IP6, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LC, NPC_LT_LC_FCOE, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 1, 0, + NPC_S_KPU11_TU_ETHER, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 12, 0, - 2, 0, NPC_S_KPU8_TCP, 40, 1, - NPC_LID_LC, NPC_LT_LC_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU10_TU_NSH_IN_VXLANGPE, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 8, 10, - 2, 0, NPC_S_KPU8_UDP, 40, 1, - NPC_LID_LC, NPC_LT_LC_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU10_TU_MPLS_IN_VXLANGPE, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_SCTP, 40, 1, - NPC_LID_LC, NPC_LT_LC_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU12_TU_IP, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + NPC_F_LE_L_VXLANGPE_NOVNI, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_ICMP, 40, 1, - NPC_LID_LC, NPC_LT_LC_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU12_TU_IP6, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + NPC_F_LE_L_VXLANGPE_NOVNI, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_ICMP6, 40, 1, - NPC_LID_LC, NPC_LT_LC_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 1, 0, + NPC_S_KPU11_TU_ETHER, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + NPC_F_LE_L_VXLANGPE_NOVNI, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_ESP, 40, 1, - NPC_LID_LC, NPC_LT_LC_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU10_TU_NSH_IN_VXLANGPE, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + NPC_F_LE_L_VXLANGPE_NOVNI, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_AH, 40, 1, - NPC_LID_LC, NPC_LT_LC_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU10_TU_MPLS_IN_VXLANGPE, 8, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + NPC_F_LE_L_VXLANGPE_NOVNI, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU8_GRE, 40, 1, - NPC_LID_LC, NPC_LT_LC_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + NPC_F_LE_L_VXLANGPE_UNK, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 6, 0, NPC_S_KPU12_TU_IP6, 40, 1, - NPC_LID_LC, NPC_LT_LC_IP6, NPC_F_IP6_TUN_IP6, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LE, NPC_LT_LE_VXLANGPE, + NPC_F_LE_L_VXLANGPE_NONP, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 3, 0, NPC_S_KPU9_TU_MPLS, 40, 1, - NPC_LID_LC, NPC_LT_LC_IP6, NPC_F_IP6_MPLS_IN_IP, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 1, 0, + NPC_S_KPU11_TU_ETHER, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + 0, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU6_IP6_EXT, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP6, NPC_F_IP6_HAS_EXT, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 1, 0, + NPC_S_KPU11_TU_ETHER, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + NPC_F_LE_L_GENEVE_OAM, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_LC, NPC_EC_IP6_VER, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LC, NPC_LT_LC_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 1, 0, + NPC_S_KPU11_TU_ETHER, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + NPC_F_LE_L_GENEVE_CRI_OPT, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 6, 0, NPC_S_KPU12_TU_IP, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 1, 0, + NPC_S_KPU11_TU_ETHER, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + NPC_F_LE_L_GENEVE_OAM_CRI_OPT, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 6, 0, NPC_S_KPU12_TU_IP6, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU12_TU_IP, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + 0, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 5, 0, NPC_S_KPU11_TU_ETHER, 8, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU12_TU_IP, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + NPC_F_LE_L_GENEVE_OAM, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 5, 0, NPC_S_KPU11_TU_ETHER, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU12_TU_IP, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + NPC_F_LE_L_GENEVE_CRI_OPT, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_LB, NPC_EC_L2_MPLS_2MANY, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU12_TU_IP, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + NPC_F_LE_L_GENEVE_OAM_CRI_OPT, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 6, 0, NPC_S_KPU12_TU_IP, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU12_TU_IP6, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + 0, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 6, 0, NPC_S_KPU12_TU_IP6, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU12_TU_IP6, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + NPC_F_LE_L_GENEVE_OAM, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 5, 0, NPC_S_KPU11_TU_ETHER, 4, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU12_TU_IP6, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + NPC_F_LE_L_GENEVE_CRI_OPT, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 5, 0, NPC_S_KPU11_TU_ETHER, 0, 0, - NPC_LID_LB, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU12_TU_IP6, 8, 1, + NPC_LID_LE, NPC_LT_LE_GENEVE, + NPC_F_LE_L_GENEVE_OAM_CRI_OPT, + 0, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 6, 0, NPC_S_KPU12_TU_IP, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LE, NPC_LT_LE_GTPC, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 6, 0, NPC_S_KPU12_TU_IP6, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU12_TU_IP, 8, 1, + NPC_LID_LE, NPC_LT_LE_GTPU, + NPC_F_LE_L_GTPU_G_PDU, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 5, 0, NPC_S_KPU11_TU_ETHER, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LE, NPC_LT_LE_GTPU, + NPC_F_LE_L_GTPU_UNK, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 5, 0, NPC_S_KPU11_TU_3RD_NSH, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 4, 1, + NPC_LID_LE, NPC_LT_LE_TU_MPLS_IN_UDP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 3, 0, NPC_S_KPU9_TU_MPLS, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 8, 1, + NPC_LID_LE, NPC_LT_LE_TU_MPLS_IN_UDP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LC, NPC_EC_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LC, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU10_TU_MPLS_PL, 12, 1, + NPC_LID_LE, NPC_LT_LE_TU_MPLS_IN_UDP, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_action kpu6_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LC, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 4, 0, 0, 0, + NPC_S_KPU10_TU_MPLS, 12, 1, + NPC_LID_LE, NPC_LT_LE_TU_MPLS_IN_UDP, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_action kpu7_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LC, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LE, NPC_EC_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LE, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, }; -static struct npc_kpu_profile_action kpu8_action_entries[] = { - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 7, 0, NPC_S_KPU16_HTTP_DATA, 20, 1, - NPC_LID_LD, NPC_LT_LD_TCP, NPC_F_TCP_HTTP, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 7, 0, NPC_S_KPU16_HTTPS_DATA, 20, 1, - NPC_LID_LD, NPC_LT_LD_TCP, NPC_F_TCP_HTTPS, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 7, 0, NPC_S_KPU16_PPTP_DATA, 20, 1, - NPC_LID_LD, NPC_LT_LD_TCP, NPC_F_TCP_PPTP, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 7, 0, NPC_S_KPU16_TCP_DATA, 20, 1, - NPC_LID_LD, NPC_LT_LD_TCP, NPC_F_TCP_UNK_PORT, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 7, 0, NPC_S_KPU16_HTTP_DATA, 0, 1, - NPC_LID_LD, NPC_LT_LD_TCP, NPC_F_TCP_HTTP_HAS_OPTIONS, - 12, 0xf0, 1, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 7, 0, NPC_S_KPU16_HTTPS_DATA, 0, 1, - NPC_LID_LD, NPC_LT_LD_TCP, NPC_F_TCP_HTTPS_HAS_OPTIONS, - 12, 0xf0, 1, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 7, 0, NPC_S_KPU16_PPTP_DATA, 0, 1, - NPC_LID_LD, NPC_LT_LD_TCP, NPC_F_TCP_PPTP_HAS_OPTIONS, - 12, 0xf0, 1, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 7, 0, NPC_S_KPU16_TCP_DATA, 0, 1, - NPC_LID_LD, NPC_LT_LD_TCP, NPC_F_TCP_UNK_PORT_HAS_OPTIONS, - 12, 0xf0, 1, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 2, 0, NPC_S_KPU11_TU_ETHER, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLAN, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 2, 0, NPC_S_KPU11_TU_ETHER, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLAN_NOVNI, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_LD, NPC_EC_VXLAN, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 2, 0, NPC_S_KPU11_TU_ETHER, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU9_TU_NSH, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE_NSH, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE_MPLS, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE_NOVNI, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE_NOVNI, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 2, 0, NPC_S_KPU11_TU_ETHER, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE_NOVNI, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU9_TU_NSH, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE_NOVNI_NSH, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE_NOVNI_MPLS, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE_UNK, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_VXLANGPE_NONP, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 2, 0, NPC_S_KPU11_TU_ETHER, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE, 8, 0x3f, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 2, 0, NPC_S_KPU11_TU_ETHER, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE_OAM, 8, 0x3f, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 2, 0, NPC_S_KPU11_TU_ETHER, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE_CRI_OPT, 8, 0x3f, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 2, 0, NPC_S_KPU11_TU_ETHER, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE_OAM_CRI_OPT, - 8, 0x3f, 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE, 8, 0x3f, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE_OAM, - 8, 0x3f, 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE_CRI_OPT, - 8, 0x3f, 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE_OAM_CRI_OPT, - 8, 0x3f, 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE, 8, 0x3f, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE_OAM, 8, 0x3f, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE_CRI_OPT, - 8, 0x3f, 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GENEVE_OAM_CRI_OPT, - 8, 0x3f, 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GTP_GTPC, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 16, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GTP_GTPU_G_PDU, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_GTP_GTPU_UNK, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 7, 0, NPC_S_KPU16_UDP_DATA, 8, 1, - NPC_LID_LD, NPC_LT_LD_UDP, NPC_F_UDP_UNK_PORT, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_SCTP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_ICMP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_IGMP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_ICMP6, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_ESP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_AH, 0, 0, 0, - 0, 0, - }, +static struct npc_kpu_profile_action kpu10_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 2, 0, NPC_S_KPU11_TU_ETHER, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_NVGRE, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU12_TU_IP, 4, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LD, NPC_EC_NVGRE, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU12_TU_IP6, 4, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 4, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU11_TU_ETHER, 8, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_CSUM, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU11_TU_ETHER, 4, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_KEY, 0, 0, - 0, 0, + NPC_ERRLEV_LE, NPC_EC_MPLS_2MANY, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU12_TU_IP, 0, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_CSUM_KEY, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU12_TU_IP6, 0, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_CSUM_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU11_TU_ETHER, 4, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_KEY_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 0, 0, + NPC_S_KPU11_TU_ETHER, 0, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 16, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_CSUM_KEY_SEQ, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU11_TU_MPLS_PL, 4, 1, + NPC_LID_LF, NPC_LT_LF_TU_MPLS_IN_VXLANGPE, + 0, 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 4, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU11_TU_MPLS_PL, 8, 1, + NPC_LID_LF, NPC_LT_LF_TU_MPLS_IN_VXLANGPE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_CSUM, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU11_TU_MPLS_PL, 12, 1, + NPC_LID_LF, NPC_LT_LF_TU_MPLS_IN_VXLANGPE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_KEY, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 4, 0, 0, 0, + NPC_S_KPU11_TU_MPLS, 12, 1, + NPC_LID_LF, NPC_LT_LF_TU_MPLS_IN_VXLANGPE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU12_TU_IP, 0, 1, + NPC_LID_LF, NPC_LT_LF_TU_NSH_IN_VXLANGPE, + 0, + 1, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_CSUM_KEY, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 1, 0, + NPC_S_KPU12_TU_IP6, 0, 1, + NPC_LID_LF, NPC_LT_LF_TU_NSH_IN_VXLANGPE, + 0, + 1, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_CSUM_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 0, 0, + NPC_S_KPU11_TU_ETHER_IN_NSH, 0, 1, + NPC_LID_LF, NPC_LT_LF_TU_NSH_IN_VXLANGPE, + 0, + 1, 0x3f, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_KEY_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_LF, NPC_EC_NSH_UNK, + 6, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LF, NPC_LT_LF_TU_NSH_IN_VXLANGPE, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, - 0, 0, NPC_S_KPU9_TU_MPLS_IN_GRE_VXLAN, 16, 1, - NPC_LID_LD, NPC_LT_LD_GRE_MPLS, NPC_F_GRE_HAS_CSUM_KEY_SEQ, + NPC_ERRLEV_LE, NPC_EC_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LF, NPC_LT_NA, + 0, 0, 0, 0, 0, }, +}; + +static struct npc_kpu_profile_action kpu11_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU9_TU_NSH, 4, 1, - NPC_LID_LD, NPC_LT_LD_GRE_NSH, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 0, 0, + NPC_S_KPU12_TU_IP, 14, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU9_TU_NSH, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE_NSH, NPC_F_GRE_HAS_CSUM, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 0, 0, + NPC_S_KPU12_TU_IP6, 14, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU9_TU_NSH, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE_NSH, NPC_F_GRE_HAS_KEY, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU12_TU_ARP, 14, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU9_TU_NSH, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE_NSH, NPC_F_GRE_HAS_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 0, 0, + NPC_S_KPU12_TU_IP, 18, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU9_TU_NSH, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE_NSH, NPC_F_GRE_HAS_CSUM_KEY, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 0, 0, + NPC_S_KPU12_TU_IP6, 18, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU9_TU_NSH, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE_NSH, NPC_F_GRE_HAS_CSUM_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU12_TU_ARP, 18, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU9_TU_NSH, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE_NSH, NPC_F_GRE_HAS_KEY_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_U_UNK_ETYPE | NPC_F_LF_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU9_TU_NSH, 16, 1, - NPC_LID_LD, NPC_LT_LD_GRE_NSH, NPC_F_GRE_HAS_CSUM_KEY_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 0, 0, + NPC_S_KPU12_TU_IP, 22, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_STAG_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 4, 1, - NPC_LID_LD, NPC_LT_LD_GRE, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 0, 0, + NPC_S_KPU12_TU_IP6, 22, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_STAG_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_CSUM, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU12_TU_ARP, 22, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_STAG_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_KEY, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_U_UNK_ETYPE | NPC_F_LF_L_WITH_STAG_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 0, 0, + NPC_S_KPU12_TU_IP, 18, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_CSUM_KEY, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 0, 0, + NPC_S_KPU12_TU_IP6, 18, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_CSUM_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU12_TU_ARP, 18, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_KEY_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_U_UNK_ETYPE | NPC_F_LF_L_WITH_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP, 16, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_CSUM_KEY_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 0, 0, + NPC_S_KPU12_TU_IP, 22, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_QINQ_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 4, 1, - NPC_LID_LD, NPC_LT_LD_GRE, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 0, 0, + NPC_S_KPU12_TU_IP6, 22, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_QINQ_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_CSUM, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU12_TU_ARP, 22, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_QINQ_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_KEY, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_U_UNK_ETYPE | NPC_F_LF_L_WITH_QINQ_CTAG, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 0, 0, + NPC_S_KPU12_TU_IP, 18, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_QINQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_CSUM_KEY, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 0, 0, + NPC_S_KPU12_TU_IP6, 18, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_QINQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_CSUM_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU12_TU_ARP, 18, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_L_WITH_QINQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_KEY_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_U_UNK_ETYPE | NPC_F_LF_L_WITH_QINQ, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 3, 0, NPC_S_KPU12_TU_IP6, 16, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_CSUM_KEY_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LF, NPC_LT_LF_TU_ETHER, + NPC_F_LF_U_UNK_ETYPE, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_HAS_ROUTE, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LF, NPC_LT_LF_TU_PPP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_UNK_PROTO, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 0, 0, + NPC_S_KPU12_TU_IP, 4, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LD, NPC_EC_GRE, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 0, 0, + NPC_S_KPU12_TU_IP6, 4, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU11_TU_PPP, 8, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_VER1, 0, 0, - 0, 0, + NPC_ERRLEV_LF, NPC_EC_MPLS_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU11_TU_PPP, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_VER1_HAS_SEQ, 0, 0, - 0, 0, + NPC_ERRLEV_LF, NPC_EC_MPLS_2MANY, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU11_TU_PPP, 12, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_VER1_HAS_ACK, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 0, 0, + NPC_S_KPU12_TU_IP, 0, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU11_TU_PPP, 16, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_VER1_HAS_SEQ_ACK, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 0, 0, + NPC_S_KPU12_TU_IP6, 0, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LD, NPC_LT_LD_GRE, NPC_F_GRE_VER1_UNK_PROTO, 0, 0, - 0, 0, + NPC_ERRLEV_LF, NPC_EC_MPLS_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LD, NPC_EC_GRE_VER1, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_ETHER_IN_NSH, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LD, NPC_EC_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LF, NPC_EC_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LF, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, }; -static struct npc_kpu_profile_action kpu9_action_entries[] = { - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU10_TU_MPLS_PL, 4, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, - }, +static struct npc_kpu_profile_action kpu12_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU10_TU_MPLS_PL, 8, 0, - NPC_LID_LD, NPC_LT_NA, NPC_F_MPLS_2_LABELS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 12, 0, 2, 0, + NPC_S_KPU15_TU_TCP, 20, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU10_TU_MPLS_PL, 12, 0, - NPC_LID_LD, NPC_LT_NA, NPC_F_MPLS_3_LABELS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU15_TU_UDP, 20, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 4, 0, - 0, 0, NPC_S_KPU10_TU_MPLS, 12, 0, - NPC_LID_LD, NPC_LT_NA, NPC_F_MPLS_4_LABELS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_SCTP, 20, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU10_TU_MPLS_PL, 4, 1, - NPC_LID_LD, NPC_LT_LD_TU_MPLS, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_ICMP, 20, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU10_TU_MPLS_PL, 8, 1, - NPC_LID_LD, NPC_LT_LD_TU_MPLS, NPC_F_MPLS_2_LABELS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_IGMP, 20, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU10_TU_MPLS_PL, 12, 1, - NPC_LID_LD, NPC_LT_LD_TU_MPLS, NPC_F_MPLS_3_LABELS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_ESP, 20, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 4, 0, - 0, 0, NPC_S_KPU10_TU_MPLS, 12, 1, - NPC_LID_LD, NPC_LT_LD_TU_MPLS, NPC_F_MPLS_4_LABELS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_AH, 20, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 2, 0, NPC_S_KPU12_TU_IP, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + NPC_F_LG_U_UNK_IP_PROTO, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 2, 0, NPC_S_KPU12_TU_IP6, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 12, 0, 2, 0, + NPC_S_KPU15_TU_TCP, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + NPC_F_LG_U_IP_HAS_OPTIONS, + 0, 0xf, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 1, 0, NPC_S_KPU11_TU_ETHER, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU15_TU_UDP, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + NPC_F_LG_U_IP_HAS_OPTIONS, + 0, 0xf, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU10_TU_NSH, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_SCTP, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + NPC_F_LG_U_IP_HAS_OPTIONS, + 0, 0xf, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 1, 0, NPC_S_KPU11_TU_MPLS_IN_NSH, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_ICMP, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + NPC_F_LG_U_IP_HAS_OPTIONS, + 0, 0xf, 0, 2, }, { - NPC_ERRLEV_LE, NPC_EC_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_IGMP, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + NPC_F_LG_U_IP_HAS_OPTIONS, + 0, 0xf, 0, 2, }, -}; - -static struct npc_kpu_profile_action kpu10_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 1, 0, NPC_S_KPU12_TU_IP, 4, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_ESP, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + NPC_F_LG_U_IP_HAS_OPTIONS, + 0, 0xf, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 1, 0, NPC_S_KPU12_TU_IP6, 4, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_AH, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + NPC_F_LG_U_IP_HAS_OPTIONS, + 0, 0xf, 0, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 0, 0, NPC_S_KPU11_TU_ETHER, 8, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + NPC_F_LG_U_IP_HAS_OPTIONS | NPC_F_LG_U_UNK_IP_PROTO, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 0, 0, NPC_S_KPU11_TU_ETHER, 4, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LF, NPC_EC_IP_VER, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_MPLS_2MANY, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_ARP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 1, 0, NPC_S_KPU12_TU_IP, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 12, 0, 2, 0, + NPC_S_KPU15_TU_TCP, 40, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 1, 0, NPC_S_KPU12_TU_IP6, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU15_TU_UDP, 40, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 0, 0, NPC_S_KPU11_TU_ETHER, 4, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_SCTP, 40, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 0, 0, NPC_S_KPU11_TU_ETHER, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_ICMP, 40, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 1, 0, NPC_S_KPU12_TU_IP, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_ICMP6, 40, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 1, 0, NPC_S_KPU12_TU_IP6, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_ESP, 40, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, - 0, 0, NPC_S_KPU11_TU_ETHER, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU15_TU_AH, 40, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU11_TU_3RD_NSH, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 0, 0, + NPC_S_KPU13_TU_IP6_EXT, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP6, + NPC_F_LG_U_IP6_HAS_EXT, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU11_TU_MPLS_IN_NSH, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 1, 0x3f, - 0, 2, + NPC_ERRLEV_LF, NPC_EC_IP6_VER, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LE, NPC_EC_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LD, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LF, NPC_EC_UNK, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LG, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, }; -static struct npc_kpu_profile_action kpu11_action_entries[] = { - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP, 14, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP6, 14, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU12_TU_ARP, 14, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP, 18, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP6, 18, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU12_TU_ARP, 18, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_CTAG_UNK, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP, 22, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP6, 22, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU12_TU_ARP, 22, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_STAG_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, - NPC_F_TU_ETHER_STAG_CTAG_UNK, 0, 0, 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP, 18, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_STAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP6, 18, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_STAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU12_TU_ARP, 18, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_STAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_STAG_UNK, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP, 22, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_QINQ_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP6, 22, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_QINQ_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU12_TU_ARP, 22, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_QINQ_CTAG, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, - NPC_F_TU_ETHER_QINQ_CTAG_UNK, 0, 0, 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP, 18, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_QINQ, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 6, 0, 0, - 0, 0, NPC_S_KPU12_TU_IP6, 18, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_QINQ, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU12_TU_ARP, 18, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_QINQ, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_QINQ_UNK, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LE, NPC_LT_LE_TU_ETHER, NPC_F_TU_ETHER_UNK, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LE, NPC_LT_LE_TU_PPP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LE, NPC_LT_LE_TU_MPLS_IN_NSH, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LE, NPC_LT_LE_TU_3RD_NSH, 0, 0, 0, - 0, 0, - }, +static struct npc_kpu_profile_action kpu13_action_entries[] = { { - NPC_ERRLEV_LE, NPC_EC_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LE, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, }; -static struct npc_kpu_profile_action kpu12_action_entries[] = { - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 12, 0, - 2, 0, NPC_S_KPU15_TU_TCP, 20, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 2, 0, NPC_S_KPU15_TU_UDP, 20, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_SCTP, 20, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_ICMP, 20, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_IGMP, 20, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_ESP, 20, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_AH, 20, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, NPC_F_IP_UNK_PROTO, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 12, 0, - 2, 0, NPC_S_KPU15_TU_TCP, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 2, 0, NPC_S_KPU15_TU_UDP, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_SCTP, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_ICMP, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_IGMP, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_ESP, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_AH, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, NPC_F_IP_HAS_OPTIONS, 0, 0xf, - 0, 2, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, - NPC_F_IP_UNK_PROTO_HAS_OPTIONS, 0, 0, 0, 0, - }, - { - NPC_ERRLEV_LF, NPC_EC_IP_VER, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_ARP, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 12, 0, - 2, 0, NPC_S_KPU15_TU_TCP, 40, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP6, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 2, 0, NPC_S_KPU15_TU_UDP, 40, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP6, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_SCTP, 40, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP6, 0, 0, 0, - 0, 0, - }, - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_ICMP, 40, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP6, 0, 0, 0, - 0, 0, - }, +static struct npc_kpu_profile_action kpu14_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_ICMP6, 40, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, +}; + +static struct npc_kpu_profile_action kpu15_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_ESP, 40, 1, - NPC_LID_LC, NPC_LT_LF_TU_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LG, NPC_EC_TCP_FLAGS_FIN_ONLY, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 2, 0, NPC_S_KPU15_TU_AH, 40, 1, - NPC_LID_LC, NPC_LT_LF_TU_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LG, NPC_EC_TCP_FLAGS_ZERO, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, - 0, 0, NPC_S_KPU13_TU_IP6_EXT, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP6, NPC_F_IP6_HAS_EXT, 0, 0, - 0, 0, + NPC_ERRLEV_LG, NPC_EC_TCP_FLAGS_RST_FIN, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LF, NPC_EC_IP6_VER, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LF, NPC_LT_LF_TU_IP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LG, NPC_EC_TCP_FLAGS_URG_SYN, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LF, NPC_EC_UNK, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LF, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LG, NPC_EC_TCP_FLAGS_RST_SYN, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_action kpu13_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LC, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LG, NPC_EC_TCP_FLAGS_SYN_FIN, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + 0, + 0, 0, 0, 0, }, -}; - -static struct npc_kpu_profile_action kpu14_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LC, NPC_LT_NA, 0, 0, 0, - 0, 0, - }, -}; - -static struct npc_kpu_profile_action kpu15_action_entries[] = { - { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU16_HTTP_DATA, 20, 1, - NPC_LID_LG, NPC_LT_LG_TU_TCP, NPC_F_TCP_HTTP, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU16_HTTP_DATA, 20, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + NPC_F_LH_L_TCP_HTTP, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU16_HTTPS_DATA, 20, 1, - NPC_LID_LG, NPC_LT_LG_TU_TCP, NPC_F_TCP_HTTPS, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU16_HTTPS_DATA, 20, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + NPC_F_LH_L_TCP_HTTP, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU16_PPTP_DATA, 20, 1, - NPC_LID_LD, NPC_LT_LG_TU_TCP, NPC_F_TCP_PPTP, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU16_PPTP_DATA, 20, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + NPC_F_LH_L_TCP_PPTP, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU16_TCP_DATA, 20, 1, - NPC_LID_LG, NPC_LT_LG_TU_TCP, NPC_F_TCP_UNK_PORT, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU16_TCP_DATA, 20, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + NPC_F_LH_L_TCP_UNK_PORT, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU16_HTTP_DATA, 0, 1, - NPC_LID_LG, NPC_LT_LG_TU_TCP, NPC_F_TCP_HTTP_HAS_OPTIONS, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU16_HTTP_DATA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + NPC_F_LH_U_TCP_HAS_OPTIONS | NPC_F_LH_L_TCP_HTTP, 12, 0xf0, 1, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU16_HTTPS_DATA, 0, 1, - NPC_LID_LG, NPC_LT_LG_TU_TCP, NPC_F_TCP_HTTPS_HAS_OPTIONS, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU16_HTTPS_DATA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + NPC_F_LH_U_TCP_HAS_OPTIONS | NPC_F_LH_L_TCP_HTTPS, 12, 0xf0, 1, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU16_PPTP_DATA, 0, 1, - NPC_LID_LG, NPC_LT_LG_TU_TCP, NPC_F_TCP_PPTP_HAS_OPTIONS, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU16_PPTP_DATA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + NPC_F_LH_U_TCP_HAS_OPTIONS | NPC_F_LH_L_TCP_PPTP, 12, 0xf0, 1, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU16_TCP_DATA, 0, 1, - NPC_LID_LG, NPC_LT_LG_TU_TCP, NPC_F_TCP_UNK_PORT_HAS_OPTIONS, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU16_TCP_DATA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_TCP, + NPC_F_LH_U_TCP_HAS_OPTIONS | NPC_F_LH_L_TCP_UNK_PORT, 12, 0xf0, 1, 2, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 0, NPC_S_KPU16_UDP_DATA, 8, 1, - NPC_LID_LG, NPC_LT_LG_TU_UDP, NPC_F_UDP_UNK_PORT, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU16_UDP_DATA, 8, 1, + NPC_LID_LH, NPC_LT_LH_TU_UDP, + NPC_F_LH_L_UDP_UNK_PORT, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LG, NPC_LT_LG_TU_SCTP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_SCTP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LG, NPC_LT_LG_TU_ICMP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_ICMP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LG, NPC_LT_LG_TU_IGMP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_IGMP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LG, NPC_LT_LG_TU_ICMP6, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_ICMP6, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LG, NPC_LT_LG_TU_ESP, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_ESP, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LG, NPC_LT_LG_TU_AH, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LH, NPC_LT_LH_TU_AH, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_LG, NPC_EC_L4, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 0, - NPC_LID_LG, NPC_LT_NA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_LG, NPC_EC_L4, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LH, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, }; static struct npc_kpu_profile_action kpu16_action_entries[] = { { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LH, NPC_LT_LH_TCP_DATA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LH, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LH, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LH, NPC_LT_LH_HTTP_DATA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LH, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LH, NPC_LT_LH_HTTPS_DATA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LH, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LH, NPC_LT_LH_PPTP_DATA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LH, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, { - NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, - 0, 1, NPC_S_NA, 0, 1, - NPC_LID_LH, NPC_LT_LH_UDP_DATA, 0, 0, 0, - 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 0, + NPC_LID_LH, NPC_LT_NA, + 0, + 0, 0, 0, 0, }, }; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/ptp.c b/drivers/net/ethernet/marvell/octeontx2/af/ptp.c new file mode 100644 index 000000000000..8d1a0ec7ad9f --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/ptp.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell PTP driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/device.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include "ptp.h" + +#define DRV_NAME "Marvell PTP Driver" + +#define PCI_DEVID_OCTEONTX2_PTP 0xA00C +#define PCI_SUBSYS_DEVID_OCTX2_98xx_PTP 0xB100 +#define PCI_SUBSYS_DEVID_OCTX2_96XX_PTP 0xB200 +#define PCI_SUBSYS_DEVID_OCTX2_95XX_PTP 0xB300 +#define PCI_SUBSYS_DEVID_OCTX2_LOKI_PTP 0xB400 +#define PCI_DEVID_OCTEONTX2_RST 0xA085 + +#define PCI_PTP_BAR_NO 0 +#define PCI_RST_BAR_NO 0 + +#define PTP_CLOCK_CFG 0xF00ULL +#define PTP_CLOCK_CFG_PTP_EN BIT(0) +#define PTP_CLOCK_LO 0xF08ULL +#define PTP_CLOCK_HI 0xF10ULL +#define PTP_CLOCK_COMP 0xF18ULL + +#define RST_BOOT 0x1600ULL +#define CLOCK_BASE_RATE 50000000ULL + +static u64 get_clock_rate(void) +{ + u64 ret = CLOCK_BASE_RATE * 16; + struct pci_dev *pdev; + void __iomem *base; + + pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, + PCI_DEVID_OCTEONTX2_RST, NULL); + if (!pdev) + goto error; + + base = pci_ioremap_bar(pdev, PCI_RST_BAR_NO); + if (!base) + goto error_put_pdev; + + ret = CLOCK_BASE_RATE * ((readq(base + RST_BOOT) >> 33) & 0x3f); + + iounmap(base); + +error_put_pdev: + pci_dev_put(pdev); + +error: + return ret; +} + +struct ptp *ptp_get(void) +{ + struct pci_dev *pdev; + struct ptp *ptp; + + pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, + PCI_DEVID_OCTEONTX2_PTP, NULL); + if (!pdev) + return ERR_PTR(-ENODEV); + + ptp = pci_get_drvdata(pdev); + if (!ptp) + ptp = ERR_PTR(-EPROBE_DEFER); + if (IS_ERR(ptp)) + pci_dev_put(pdev); + + return ptp; +} + +void ptp_put(struct ptp *ptp) +{ + if (!ptp) + return; + + pci_dev_put(ptp->pdev); +} + +int ptp_adjfine(struct ptp *ptp, long scaled_ppm) +{ + bool neg_adj = false; + u64 comp; + u64 adj; + s64 ppb; + + if (scaled_ppm < 0) { + neg_adj = true; + scaled_ppm = -scaled_ppm; + } + + /* The hardware adds the clock compensation value to the PTP clock + * on every coprocessor clock cycle. Typical convention is that it + * represent number of nanosecond betwen each cycle. In this + * convention compensation value is in 64 bit fixed-point + * representation where upper 32 bits are number of nanoseconds + * and lower is fractions of nanosecond. + * The scaled_ppm represent the ratio in "parts per bilion" by which the + * compensation value should be corrected. + * To calculate new compenstation value we use 64bit fixed point + * arithmetic on following formula + * comp = tbase + tbase * scaled_ppm / (1M * 2^16) + * where tbase is the basic compensation value calculated initialy + * in cavium_ptp_init() -> tbase = 1/Hz. Then we use endian + * independent structure definition to write data to PTP register. + */ + comp = ((u64)1000000000ull << 32) / ptp->clock_rate; + /* convert scaled_ppm to ppb */ + ppb = 1 + scaled_ppm; + ppb *= 125; + ppb >>= 13; + adj = comp * ppb; + adj = div_u64(adj, 1000000000ull); + comp = neg_adj ? comp - adj : comp + adj; + + writeq(comp, ptp->reg_base + PTP_CLOCK_COMP); + + return 0; +} + +static inline u64 get_tsc(bool is_pmu) +{ +#if defined(CONFIG_ARM64) + return is_pmu ? read_sysreg(pmccntr_el0) : read_sysreg(cntvct_el0); +#else + return 0; +#endif +} + +int ptp_get_clock(struct ptp *ptp, bool is_pmu, u64 *clk, u64 *tsc) +{ + u64 end, start; + u8 retries = 0; + + do { + start = get_tsc(0); + *tsc = get_tsc(is_pmu); + *clk = readq(ptp->reg_base + PTP_CLOCK_HI); + end = get_tsc(0); + retries++; + } while (((end - start) > 50) && retries < 5); + + return 0; +} + +static int ptp_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + struct ptp *ptp; + u64 clock_comp; + u64 clock_cfg; + int err; + + ptp = devm_kzalloc(dev, sizeof(*ptp), GFP_KERNEL); + if (!ptp) { + err = -ENOMEM; + goto error; + } + + ptp->pdev = pdev; + + err = pcim_enable_device(pdev); + if (err) + goto error_free; + + err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev)); + if (err) + goto error_free; + + ptp->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO]; + + ptp->clock_rate = get_clock_rate(); + + clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG); + clock_cfg |= PTP_CLOCK_CFG_PTP_EN; + writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG); + + clock_comp = ((u64)1000000000ull << 32) / ptp->clock_rate; + writeq(clock_comp, ptp->reg_base + PTP_CLOCK_COMP); + + pci_set_drvdata(pdev, ptp); + + return 0; + +error_free: + devm_kfree(dev, ptp); + +error: + /* For `ptp_get()` we need to differentiate between the case + * when the core has not tried to probe this device and the case when + * the probe failed. In the later case we pretend that the + * initialization was successful and keep the error in + * `dev->driver_data`. + */ + pci_set_drvdata(pdev, ERR_PTR(err)); + return 0; +} + +static void ptp_remove(struct pci_dev *pdev) +{ + struct ptp *ptp = pci_get_drvdata(pdev); + u64 clock_cfg; + + if (IS_ERR_OR_NULL(ptp)) + return; + + clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG); + clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN; + writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG); +} + +static const struct pci_device_id ptp_id_table[] = { + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, + PCI_VENDOR_ID_CAVIUM, + PCI_SUBSYS_DEVID_OCTX2_98xx_PTP) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, + PCI_VENDOR_ID_CAVIUM, + PCI_SUBSYS_DEVID_OCTX2_96XX_PTP) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, + PCI_VENDOR_ID_CAVIUM, + PCI_SUBSYS_DEVID_OCTX2_95XX_PTP) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, + PCI_VENDOR_ID_CAVIUM, + PCI_SUBSYS_DEVID_OCTX2_LOKI_PTP) }, + { 0, } +}; + +struct pci_driver ptp_driver = { + .name = DRV_NAME, + .id_table = ptp_id_table, + .probe = ptp_probe, + .remove = ptp_remove, +}; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/ptp.h b/drivers/net/ethernet/marvell/octeontx2/af/ptp.h new file mode 100644 index 000000000000..91a7b8c9c27b --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/ptp.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Marvell PTP driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ + +#ifndef PTP_H +#define PTP_H + +#include <linux/timecounter.h> +#include <linux/time64.h> +#include <linux/spinlock.h> + +struct ptp { + struct pci_dev *pdev; + void __iomem *reg_base; + u32 clock_rate; +}; + +struct ptp *ptp_get(void); +void ptp_put(struct ptp *ptp); + +int ptp_adjfine(struct ptp *ptp, long scaled_ppm); +int ptp_get_clock(struct ptp *ptp, bool is_pmu, u64 *clki, u64 *tsc); + +extern struct pci_driver ptp_driver; + +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index e581091c09c4..3868898757f6 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -18,6 +18,7 @@ #include "cgx.h" #include "rvu.h" #include "rvu_reg.h" +#include "ptp.h" #define DRV_NAME "octeontx2-af" #define DRV_STRING "Marvell OcteonTX2 RVU Admin Function Driver" @@ -56,12 +57,34 @@ static char *mkex_profile; /* MKEX profile name */ module_param(mkex_profile, charp, 0000); MODULE_PARM_DESC(mkex_profile, "MKEX profile name string"); +static void rvu_setup_hw_capabilities(struct rvu *rvu) +{ + struct rvu_hwinfo *hw = rvu->hw; + + hw->cap.nix_tx_aggr_lvl = NIX_TXSCH_LVL_TL1; + hw->cap.nix_fixed_txschq_mapping = false; + hw->cap.nix_shaping = true; + hw->cap.nix_tx_link_bp = true; + hw->cap.nix_rx_multicast = true; + + if (is_rvu_96xx_B0(rvu) || is_rvu_95xx_A0(rvu)) { + hw->cap.nix_fixed_txschq_mapping = true; + hw->cap.nix_txsch_per_cgx_lmac = 4; + hw->cap.nix_txsch_per_lbk_lmac = 132; + hw->cap.nix_txsch_per_sdp_lmac = 76; + hw->cap.nix_shaping = false; + hw->cap.nix_tx_link_bp = false; + if (is_rvu_96xx_A0(rvu) || is_rvu_95xx_A0(rvu)) + hw->cap.nix_rx_multicast = false; + } +} + /* Poll a RVU block's register 'offset', for a 'zero' * or 'nonzero' at bits specified by 'mask' */ int rvu_poll_reg(struct rvu *rvu, u64 block, u64 offset, u64 mask, bool zero) { - unsigned long timeout = jiffies + usecs_to_jiffies(100); + unsigned long timeout = jiffies + usecs_to_jiffies(10000); void __iomem *reg; u64 reg_val; @@ -73,7 +96,6 @@ int rvu_poll_reg(struct rvu *rvu, u64 block, u64 offset, u64 mask, bool zero) if (!zero && (reg_val & mask)) return 0; usleep_range(1, 5); - timeout--; } return -EBUSY; } @@ -400,6 +422,19 @@ static void rvu_check_block_implemented(struct rvu *rvu) } } +static void rvu_setup_rvum_blk_revid(struct rvu *rvu) +{ + rvu_write64(rvu, BLKADDR_RVUM, + RVU_PRIV_BLOCK_TYPEX_REV(BLKTYPE_RVUM), + RVU_BLK_RVUM_REVID); +} + +static void rvu_clear_rvum_blk_revid(struct rvu *rvu) +{ + rvu_write64(rvu, BLKADDR_RVUM, + RVU_PRIV_BLOCK_TYPEX_REV(BLKTYPE_RVUM), 0x00); +} + int rvu_lf_reset(struct rvu *rvu, struct rvu_block *block, int lf) { int err; @@ -433,9 +468,9 @@ static void rvu_reset_all_blocks(struct rvu *rvu) rvu_block_reset(rvu, BLKADDR_SSO, SSO_AF_BLK_RST); rvu_block_reset(rvu, BLKADDR_TIM, TIM_AF_BLK_RST); rvu_block_reset(rvu, BLKADDR_CPT0, CPT_AF_BLK_RST); - rvu_block_reset(rvu, BLKADDR_NDC0, NDC_AF_BLK_RST); - rvu_block_reset(rvu, BLKADDR_NDC1, NDC_AF_BLK_RST); - rvu_block_reset(rvu, BLKADDR_NDC2, NDC_AF_BLK_RST); + rvu_block_reset(rvu, BLKADDR_NDC_NIX0_RX, NDC_AF_BLK_RST); + rvu_block_reset(rvu, BLKADDR_NDC_NIX0_TX, NDC_AF_BLK_RST); + rvu_block_reset(rvu, BLKADDR_NDC_NPA0, NDC_AF_BLK_RST); } static void rvu_scan_block(struct rvu *rvu, struct rvu_block *block) @@ -582,7 +617,12 @@ setup_vfmsix: */ cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_CONST); max_msix = cfg & 0xFFFFF; - phy_addr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_MSIXTR_BASE); + if (rvu->fwdata && rvu->fwdata->msixtr_base) + phy_addr = rvu->fwdata->msixtr_base; + else + phy_addr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_MSIXTR_BASE); + /* Register save */ + rvu->msixtr_base_phy = phy_addr; iova = dma_map_resource(rvu->dev, phy_addr, max_msix * PCI_MSIX_ENTRY_SIZE, DMA_BIDIRECTIONAL, 0); @@ -596,6 +636,13 @@ setup_vfmsix: return 0; } +static void rvu_reset_msix(struct rvu *rvu) +{ + /* Restore msixtr base register */ + rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_MSIXTR_BASE, + rvu->msixtr_base_phy); +} + static void rvu_free_hw_resources(struct rvu *rvu) { struct rvu_hwinfo *hw = rvu->hw; @@ -607,6 +654,7 @@ static void rvu_free_hw_resources(struct rvu *rvu) rvu_npa_freemem(rvu); rvu_npc_freemem(rvu); rvu_nix_freemem(rvu); + rvu_sso_freemem(rvu); /* Free block LF bitmaps */ for (id = 0; id < BLK_COUNT; id++) { @@ -614,7 +662,7 @@ static void rvu_free_hw_resources(struct rvu *rvu) kfree(block->lf.bmap); } - /* Free MSIX bitmaps */ + /* Free MSIX and TIM bitmaps */ for (id = 0; id < hw->total_pfs; id++) { pfvf = &rvu->pf[id]; kfree(pfvf->msix.bmap); @@ -634,9 +682,80 @@ static void rvu_free_hw_resources(struct rvu *rvu) max_msix * PCI_MSIX_ENTRY_SIZE, DMA_BIDIRECTIONAL, 0); + rvu_reset_msix(rvu); mutex_destroy(&rvu->rsrc_lock); } +static void rvu_setup_pfvf_macaddress(struct rvu *rvu) +{ + struct rvu_hwinfo *hw = rvu->hw; + int pf, vf, numvfs, hwvf; + struct rvu_pfvf *pfvf; + u64 *mac; + + for (pf = 0; pf < hw->total_pfs; pf++) { + if (!is_pf_cgxmapped(rvu, pf)) + continue; + /* Assign MAC address to PF */ + pfvf = &rvu->pf[pf]; + if (rvu->fwdata && pf < PF_MACNUM_MAX) { + mac = &rvu->fwdata->pf_macs[pf]; + if (*mac) + u64_to_ether_addr(*mac, pfvf->mac_addr); + else + eth_random_addr(pfvf->mac_addr); + } else { + eth_random_addr(pfvf->mac_addr); + } + + /* Assign MAC address to VFs*/ + rvu_get_pf_numvfs(rvu, pf, &numvfs, &hwvf); + for (vf = 0; vf < numvfs; vf++, hwvf++) { + pfvf = &rvu->hwvf[hwvf]; + if (rvu->fwdata && hwvf < VF_MACNUM_MAX) { + mac = &rvu->fwdata->vf_macs[hwvf]; + if (*mac) + u64_to_ether_addr(*mac, pfvf->mac_addr); + else + eth_random_addr(pfvf->mac_addr); + } else { + eth_random_addr(pfvf->mac_addr); + } + } + } +} + +static int rvu_fwdata_init(struct rvu *rvu) +{ + u64 fwdbase; + int err; + + /* Get firmware data base address */ + err = cgx_get_fwdata_base(&fwdbase); + if (err) + goto fail; + rvu->fwdata = ioremap_wc(fwdbase, sizeof(struct rvu_fwdata)); + if (!rvu->fwdata) + goto fail; + if (!is_rvu_fwdata_valid(rvu)) { + dev_err(rvu->dev, + "Mismatch in 'fwdata' struct btw kernel and firmware\n"); + iounmap(rvu->fwdata); + rvu->fwdata = NULL; + return -EINVAL; + } + return 0; +fail: + dev_info(rvu->dev, "Unable to fetch 'fwdata' from firmware\n"); + return -EIO; +} + +static void rvu_fwdata_exit(struct rvu *rvu) +{ + if (rvu->fwdata) + iounmap(rvu->fwdata); +} + static int rvu_setup_hw_resources(struct rvu *rvu) { struct rvu_hwinfo *hw = rvu->hw; @@ -792,6 +911,8 @@ init: mutex_init(&rvu->rsrc_lock); + rvu_fwdata_init(rvu); + err = rvu_setup_msix_resources(rvu); if (err) return err; @@ -804,8 +925,10 @@ init: /* Allocate memory for block LF/slot to pcifunc mapping info */ block->fn_map = devm_kcalloc(rvu->dev, block->lf.max, sizeof(u16), GFP_KERNEL); - if (!block->fn_map) - return -ENOMEM; + if (!block->fn_map) { + err = -ENOMEM; + goto msix_err; + } /* Scan all blocks to check if low level firmware has * already provisioned any of the resources to a PF/VF. @@ -815,11 +938,14 @@ init: err = rvu_npc_init(rvu); if (err) - goto exit; + goto fwdata_err; err = rvu_cgx_init(rvu); if (err) - goto exit; + goto fwdata_err; + + /* Assign MACs for CGX mapped functions */ + rvu_setup_pfvf_macaddress(rvu); err = rvu_npa_init(rvu); if (err) @@ -829,11 +955,30 @@ init: if (err) goto cgx_err; + err = rvu_sso_init(rvu); + if (err) + goto cgx_err; + + err = rvu_tim_init(rvu); + if (err) + goto cgx_err; + + err = rvu_cpt_init(rvu); + if (err) + goto cgx_err; + + err = rvu_sdp_init(rvu); + if (err) + goto cgx_err; + return 0; cgx_err: rvu_cgx_exit(rvu); -exit: +fwdata_err: + rvu_fwdata_exit(rvu); +msix_err: + rvu_reset_msix(rvu); return err; } @@ -877,16 +1022,20 @@ int rvu_aq_alloc(struct rvu *rvu, struct admin_queue **ad_queue, return 0; } -static int rvu_mbox_handler_ready(struct rvu *rvu, struct msg_req *req, - struct ready_msg_rsp *rsp) +int rvu_mbox_handler_ready(struct rvu *rvu, struct msg_req *req, + struct ready_msg_rsp *rsp) { + if (rvu->fwdata) { + rsp->rclk_freq = rvu->fwdata->rclk; + rsp->sclk_freq = rvu->fwdata->sclk; + } return 0; } /* Get current count of a RVU block's LF/slots * provisioned to a given RVU func. */ -static u16 rvu_get_rsrc_mapcount(struct rvu_pfvf *pfvf, int blktype) +u16 rvu_get_rsrc_mapcount(struct rvu_pfvf *pfvf, int blktype) { switch (blktype) { case BLKTYPE_NPA: @@ -915,7 +1064,7 @@ bool is_pffunc_map_valid(struct rvu *rvu, u16 pcifunc, int blktype) pfvf = rvu_get_pfvf(rvu, pcifunc); /* Check if this PFFUNC has a LF of type blktype attached */ - if (!rvu_get_rsrc_mapcount(pfvf, blktype)) + if (blktype != BLKTYPE_SSO && !rvu_get_rsrc_mapcount(pfvf, blktype)) return false; return true; @@ -926,6 +1075,9 @@ static int rvu_lookup_rsrc(struct rvu *rvu, struct rvu_block *block, { u64 val; + if (block->type == BLKTYPE_TIM) + return rvu_tim_lookup_rsrc(rvu, block, pcifunc, slot); + val = ((u64)pcifunc << 24) | (slot << 16) | (1ULL << 13); rvu_write64(rvu, block->addr, block->lookup_reg, val); /* Wait for the lookup to finish */ @@ -1023,9 +1175,9 @@ static int rvu_detach_rsrcs(struct rvu *rvu, struct rsrc_detach *detach, return 0; } -static int rvu_mbox_handler_detach_resources(struct rvu *rvu, - struct rsrc_detach *detach, - struct msg_rsp *rsp) +int rvu_mbox_handler_detach_resources(struct rvu *rvu, + struct rsrc_detach *detach, + struct msg_rsp *rsp) { return rvu_detach_rsrcs(rvu, detach, detach->hdr.pcifunc); } @@ -1076,6 +1228,12 @@ static int rvu_check_rsrc_availability(struct rvu *rvu, struct rvu_block *block; int free_lfs, mappedlfs; + if (rvu_check_rsrc_policy(rvu, req, pcifunc)) { + dev_err(rvu->dev, "Func 0x%x: Resource policy check failed\n", + pcifunc); + return -EINVAL; + } + /* Only one NPA LF can be attached */ if (req->npalf && !rvu_get_rsrc_mapcount(pfvf, BLKTYPE_NPA)) { block = &hw->block[BLKADDR_NPA]; @@ -1171,9 +1329,9 @@ fail: return -ENOSPC; } -static int rvu_mbox_handler_attach_resources(struct rvu *rvu, - struct rsrc_attach *attach, - struct msg_rsp *rsp) +int rvu_mbox_handler_attach_resources(struct rvu *rvu, + struct rsrc_attach *attach, + struct msg_rsp *rsp) { u16 pcifunc = attach->hdr.pcifunc; int err; @@ -1190,12 +1348,12 @@ static int rvu_mbox_handler_attach_resources(struct rvu *rvu, goto exit; /* Now attach the requested resources */ - if (attach->npalf) - rvu_attach_block(rvu, pcifunc, BLKTYPE_NPA, 1); - if (attach->nixlf) rvu_attach_block(rvu, pcifunc, BLKTYPE_NIX, 1); + if (attach->npalf) + rvu_attach_block(rvu, pcifunc, BLKTYPE_NPA, 1); + if (attach->sso) { /* RVU func doesn't know which exact LF or slot is attached * to it, it always sees as slot 0,1,2. So for a 'modify' @@ -1294,8 +1452,8 @@ static void rvu_clear_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf, rvu_free_rsrc_contig(&pfvf->msix, nvecs, offset); } -static int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req, - struct msix_offset_rsp *rsp) +int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req, + struct msix_offset_rsp *rsp) { struct rvu_hwinfo *hw = rvu->hw; u16 pcifunc = req->hdr.pcifunc; @@ -1343,8 +1501,8 @@ static int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req, return 0; } -static int rvu_mbox_handler_vf_flr(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp) +int rvu_mbox_handler_vf_flr(struct rvu *rvu, struct msg_req *req, + struct msg_rsp *rsp) { u16 pcifunc = req->hdr.pcifunc; u16 vf, numvfs; @@ -1363,6 +1521,83 @@ static int rvu_mbox_handler_vf_flr(struct rvu *rvu, struct msg_req *req, return 0; } +int rvu_ndc_sync(struct rvu *rvu, int lfblkaddr, int lfidx, u64 lfoffset) +{ + /* Sync cached info for this LF in NDC to LLC/DRAM */ + rvu_write64(rvu, lfblkaddr, lfoffset, BIT_ULL(12) | lfidx); + return rvu_poll_reg(rvu, lfblkaddr, lfoffset, BIT_ULL(12), true); +} + +int rvu_mbox_handler_get_hw_cap(struct rvu *rvu, struct msg_req *req, + struct get_hw_cap_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + + rsp->nix_fixed_txschq_mapping = hw->cap.nix_fixed_txschq_mapping; + rsp->nix_shaping = hw->cap.nix_shaping; + + return 0; +} + +int rvu_mbox_handler_ndc_sync_op(struct rvu *rvu, + struct ndc_sync_op *req, + struct msg_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + int err, lfidx, lfblkaddr; + + if (req->npa_lf_sync) { + /* Get NPA LF data */ + lfblkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, pcifunc); + if (lfblkaddr < 0) + return NPA_AF_ERR_AF_LF_INVALID; + + lfidx = rvu_get_lf(rvu, &hw->block[lfblkaddr], pcifunc, 0); + if (lfidx < 0) + return NPA_AF_ERR_AF_LF_INVALID; + + /* Sync NPA NDC */ + err = rvu_ndc_sync(rvu, lfblkaddr, + lfidx, NPA_AF_NDC_SYNC); + if (err) + dev_err(rvu->dev, + "NDC-NPA sync failed for LF %u\n", lfidx); + } + + if (!req->nix_lf_tx_sync && !req->nix_lf_rx_sync) + return 0; + + /* Get NIX LF data */ + lfblkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); + if (lfblkaddr < 0) + return NIX_AF_ERR_AF_LF_INVALID; + + lfidx = rvu_get_lf(rvu, &hw->block[lfblkaddr], pcifunc, 0); + if (lfidx < 0) + return NIX_AF_ERR_AF_LF_INVALID; + + if (req->nix_lf_tx_sync) { + /* Sync NIX TX NDC */ + err = rvu_ndc_sync(rvu, lfblkaddr, + lfidx, NIX_AF_NDC_TX_SYNC); + if (err) + dev_err(rvu->dev, + "NDC-NIX-TX sync fail for LF %u\n", lfidx); + } + + if (req->nix_lf_rx_sync) { + /* Sync NIX RX NDC */ + err = rvu_ndc_sync(rvu, lfblkaddr, + lfidx, NIX_AF_NDC_RX_SYNC); + if (err) + dev_err(rvu->dev, + "NDC-NIX-RX sync failed for LF %u\n", lfidx); + } + + return 0; +} + static int rvu_process_mbox_msg(struct otx2_mbox *mbox, int devid, struct mbox_msghdr *req) { @@ -1440,12 +1675,12 @@ static void __rvu_mbox_handler(struct rvu_work *mwork, int type) /* Process received mbox messages */ req_hdr = mdev->mbase + mbox->rx_start; - if (req_hdr->num_msgs == 0) + if (mw->mbox_wrk[devid].num_msgs == 0) return; offset = mbox->rx_start + ALIGN(sizeof(*req_hdr), MBOX_MSG_ALIGN); - for (id = 0; id < req_hdr->num_msgs; id++) { + for (id = 0; id < mw->mbox_wrk[devid].num_msgs; id++) { msg = mdev->mbase + offset; /* Set which PF/VF sent this message based on mbox IRQ */ @@ -1471,13 +1706,14 @@ static void __rvu_mbox_handler(struct rvu_work *mwork, int type) if (msg->pcifunc & RVU_PFVF_FUNC_MASK) dev_warn(rvu->dev, "Error %d when processing message %s (0x%x) from PF%d:VF%d\n", err, otx2_mbox_id2name(msg->id), - msg->id, devid, + msg->id, rvu_get_pf(msg->pcifunc), (msg->pcifunc & RVU_PFVF_FUNC_MASK) - 1); else dev_warn(rvu->dev, "Error %d when processing message %s (0x%x) from PF%d\n", err, otx2_mbox_id2name(msg->id), msg->id, devid); } + mw->mbox_wrk[devid].num_msgs = 0; /* Send mbox responses to VF/PF */ otx2_mbox_msg_send(mbox, devid); @@ -1523,14 +1759,14 @@ static void __rvu_mbox_up_handler(struct rvu_work *mwork, int type) mdev = &mbox->dev[devid]; rsp_hdr = mdev->mbase + mbox->rx_start; - if (rsp_hdr->num_msgs == 0) { + if (mw->mbox_wrk_up[devid].up_num_msgs == 0) { dev_warn(rvu->dev, "mbox up handler: num_msgs = 0\n"); return; } offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); - for (id = 0; id < rsp_hdr->num_msgs; id++) { + for (id = 0; id < mw->mbox_wrk_up[devid].up_num_msgs; id++) { msg = mdev->mbase + offset; if (msg->id >= MBOX_MSG_MAX) { @@ -1560,6 +1796,7 @@ end: offset = mbox->rx_start + msg->next_msgoff; mdev->msgs_acked++; } + mw->mbox_wrk_up[devid].up_num_msgs = 0; otx2_mbox_reset(mbox, devid); } @@ -1697,14 +1934,28 @@ static void rvu_queue_work(struct mbox_wq_info *mw, int first, mbox = &mw->mbox; mdev = &mbox->dev[i]; hdr = mdev->mbase + mbox->rx_start; - if (hdr->num_msgs) - queue_work(mw->mbox_wq, &mw->mbox_wrk[i].work); + /*The hdr->num_msgs is set to zero immediately in the interrupt + * handler to ensure that it holds a correct value next time + * when the interrupt handler is called. + * pf->mbox.num_msgs holds the data for use in pfaf_mbox_handler + * pf>mbox.up_num_msgs holds the data for use in + * pfaf_mbox_up_handler. + */ + + if (hdr->num_msgs) { + mw->mbox_wrk[i].num_msgs = hdr->num_msgs; + hdr->num_msgs = 0; + queue_work(mw->mbox_wq, &mw->mbox_wrk[i].work); + } mbox = &mw->mbox_up; mdev = &mbox->dev[i]; hdr = mdev->mbase + mbox->rx_start; - if (hdr->num_msgs) + if (hdr->num_msgs) { + mw->mbox_wrk_up[i].up_num_msgs = hdr->num_msgs; + hdr->num_msgs = 0; queue_work(mw->mbox_wq, &mw->mbox_wrk_up[i].work); + } } } @@ -1753,6 +2004,94 @@ static void rvu_enable_mbox_intr(struct rvu *rvu) INTR_MASK(hw->total_pfs) & ~1ULL); } +static void rvu_npa_lf_mapped_nix_lf_teardown(struct rvu *rvu, u16 pcifunc) +{ + struct rvu_hwinfo *hw = rvu->hw; + struct rvu_block *nix_block; + struct rsrc_detach detach; + u16 nix_pcifunc; + int blkaddr, lf; + u64 regval; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return; + + nix_block = &hw->block[blkaddr]; + for (lf = 0; lf < nix_block->lf.max; lf++) { + /* Loop through all the NIX LFs and check if the NPA lf is + * being used based on pcifunc. + */ + regval = rvu_read64(rvu, blkaddr, NIX_AF_LFX_CFG(lf)); + if ((regval & 0xFFFF) != pcifunc) + continue; + + nix_pcifunc = nix_block->fn_map[lf]; + + /* Skip NIX LF attached to the pcifunc as it is already + * quiesced. + */ + if (nix_pcifunc == pcifunc) + continue; + + detach.partial = true; + detach.nixlf = true; + /* Teardown the NIX LF. */ + rvu_nix_lf_teardown(rvu, nix_pcifunc, blkaddr, lf); + rvu_lf_reset(rvu, nix_block, lf); + /* Detach the NIX LF. */ + rvu_detach_rsrcs(rvu, &detach, nix_pcifunc); + } +} + +static void rvu_npa_lf_mapped_sso_lf_teardown(struct rvu *rvu, u16 pcifunc) +{ + u16 *pcifunc_arr; + u16 sso_pcifunc, match_cnt = 0; + struct rvu_block *sso_block; + struct rsrc_detach detach; + int blkaddr, lf; + u64 regval; + size_t len; + + len = sizeof(*pcifunc_arr) * (rvu->hw->total_pfs + rvu->hw->total_vfs); + pcifunc_arr = kmalloc(len, GFP_KERNEL); + if (!pcifunc_arr) + return; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) { + kfree(pcifunc_arr); + return; + } + + sso_block = &rvu->hw->block[blkaddr]; + for (lf = 0; lf < sso_block->lf.max; lf++) { + regval = rvu_read64(rvu, blkaddr, SSO_AF_XAQX_GMCTL(lf)); + if ((regval & 0xFFFF) != pcifunc) + continue; + + sso_pcifunc = sso_block->fn_map[lf]; + regval = rvu_read64(rvu, blkaddr, sso_block->lfcfg_reg | + (lf << sso_block->lfshift)); + /* Save SSO PF_FUNC info to detach all LFs of that PF_FUNC at + * once later. + */ + rvu_sso_lf_teardown(rvu, sso_pcifunc, lf, regval & 0xF); + rvu_lf_reset(rvu, sso_block, lf); + pcifunc_arr[match_cnt] = sso_pcifunc; + match_cnt++; + } + + detach.partial = true; + detach.sso = true; + + for (sso_pcifunc = 0; sso_pcifunc < match_cnt; sso_pcifunc++) + rvu_detach_rsrcs(rvu, &detach, pcifunc_arr[sso_pcifunc]); + + kfree(pcifunc_arr); +} + static void rvu_blklf_teardown(struct rvu *rvu, u16 pcifunc, u8 blkaddr) { struct rvu_block *block; @@ -1772,14 +2111,36 @@ static void rvu_blklf_teardown(struct rvu *rvu, u16 pcifunc, u8 blkaddr) /* Cleanup LF and reset it */ if (block->addr == BLKADDR_NIX0) rvu_nix_lf_teardown(rvu, pcifunc, block->addr, lf); - else if (block->addr == BLKADDR_NPA) + else if (block->addr == BLKADDR_NPA) { + rvu_npa_lf_mapped_nix_lf_teardown(rvu, pcifunc); + rvu_npa_lf_mapped_sso_lf_teardown(rvu, pcifunc); rvu_npa_lf_teardown(rvu, pcifunc, lf); + } else if (block->addr == BLKADDR_SSO) + rvu_sso_lf_teardown(rvu, pcifunc, lf, slot); + else if (block->addr == BLKADDR_SSOW) + rvu_ssow_lf_teardown(rvu, pcifunc, lf, slot); + else if (block->addr == BLKADDR_TIM) + rvu_tim_lf_teardown(rvu, pcifunc, lf, slot); err = rvu_lf_reset(rvu, block, lf); if (err) { dev_err(rvu->dev, "Failed to reset blkaddr %d LF%d\n", block->addr, lf); } + + if (block->addr == BLKADDR_SSO) + rvu_sso_hwgrp_config_thresh(rvu, block->addr, lf); + } +} + +static void rvu_sso_pfvf_rst(struct rvu *rvu, u16 pcifunc) +{ + struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc); + struct rvu_hwinfo *hw = rvu->hw; + + if (pfvf->sso_uniq_ident) { + rvu_free_rsrc(&hw->sso.pfvf_ident, pfvf->sso_uniq_ident); + pfvf->sso_uniq_ident = 0; } } @@ -1798,6 +2159,7 @@ static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc) rvu_blklf_teardown(rvu, pcifunc, BLKADDR_SSO); rvu_blklf_teardown(rvu, pcifunc, BLKADDR_NPA); rvu_detach_rsrcs(rvu, NULL, pcifunc); + rvu_sso_pfvf_rst(rvu, pcifunc); mutex_unlock(&rvu->flr_lock); } @@ -1967,6 +2329,11 @@ static void rvu_unregister_interrupts(struct rvu *rvu) { int irq; + rvu_npa_unregister_interrupts(rvu); + rvu_nix_unregister_interrupts(rvu); + rvu_sso_unregister_interrupts(rvu); + rvu_cpt_unregister_interrupts(rvu); + /* Disable the Mbox interrupt */ rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT_ENA_W1C, INTR_MASK(rvu->hw->total_pfs) & ~1ULL); @@ -2080,6 +2447,9 @@ static int rvu_register_interrupts(struct rvu *rvu) } rvu->irq_allocated[RVU_AF_INT_VEC_PFME] = true; + /* Clear TRPEND bit for all PF */ + rvu_write64(rvu, BLKADDR_RVUM, + RVU_AF_PFTRPEND, INTR_MASK(rvu->hw->total_pfs)); /* Enable ME interrupt for all PFs*/ rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT, INTR_MASK(rvu->hw->total_pfs)); @@ -2171,8 +2541,24 @@ static int rvu_register_interrupts(struct rvu *rvu) goto fail; } rvu->irq_allocated[offset] = true; - return 0; + ret = rvu_npa_register_interrupts(rvu); + if (ret) + goto fail; + + ret = rvu_nix_register_interrupts(rvu); + if (ret) + goto fail; + + ret = rvu_sso_register_interrupts(rvu); + if (ret) + goto fail; + + ret = rvu_cpt_register_interrupts(rvu); + if (ret) + goto fail; + + return 0; fail: rvu_unregister_interrupts(rvu); return ret; @@ -2271,7 +2657,7 @@ static void rvu_enable_afvf_intr(struct rvu *rvu) #define PCI_DEVID_OCTEONTX2_LBK 0xA061 -static int lbk_get_num_chans(void) +int rvu_get_num_lbk_chans(void) { struct pci_dev *pdev; void __iomem *base; @@ -2306,7 +2692,7 @@ static int rvu_enable_sriov(struct rvu *rvu) return 0; } - chans = lbk_get_num_chans(); + chans = rvu_get_num_lbk_chans(); if (chans < 0) return chans; @@ -2316,18 +2702,6 @@ static int rvu_enable_sriov(struct rvu *rvu) if (vfs > chans) vfs = chans; - /* AF's VFs work in pairs and talk over consecutive loopback channels. - * Thus we want to enable maximum even number of VFs. In case - * odd number of VFs are available then the last VF on the list - * remains disabled. - */ - if (vfs & 0x1) { - dev_warn(&pdev->dev, - "Number of VFs should be even. Enabling %d out of %d.\n", - vfs - 1, vfs); - vfs--; - } - if (!vfs) return 0; @@ -2415,13 +2789,25 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_release_regions; } + pci_set_master(pdev); + rvu->ptp = ptp_get(); + if (IS_ERR(rvu->ptp)) { + err = PTR_ERR(rvu->ptp); + if (err == -EPROBE_DEFER) { + dev_err(dev, + "PTP driver not loaded, deferring probe\n"); + goto err_release_regions; + } + rvu->ptp = NULL; + } + /* Map Admin function CSRs */ rvu->afreg_base = pcim_iomap(pdev, PCI_AF_REG_BAR_NUM, 0); rvu->pfreg_base = pcim_iomap(pdev, PCI_PF_REG_BAR_NUM, 0); if (!rvu->afreg_base || !rvu->pfreg_base) { dev_err(dev, "Unable to map admin function CSRs, aborting\n"); err = -ENOMEM; - goto err_release_regions; + goto err_put_ptp; } /* Store module params in rvu structure */ @@ -2432,9 +2818,11 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id) rvu_reset_all_blocks(rvu); + rvu_setup_hw_capabilities(rvu); + err = rvu_setup_hw_resources(rvu); if (err) - goto err_release_regions; + goto err_put_ptp; /* Init mailbox btw AF and PFs */ err = rvu_mbox_init(rvu, &rvu->afpf_wq_info, TYPE_AFPF, @@ -2451,12 +2839,22 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto err_flr; + rvu_setup_rvum_blk_revid(rvu); + err = rvu_policy_init(rvu); + if (err) + goto err_irq; + /* Enable AF's VFs (if any) */ err = rvu_enable_sriov(rvu); if (err) - goto err_irq; + goto err_policy; + + /* Initialize debugfs */ + rvu_dbg_init(rvu); return 0; +err_policy: + rvu_policy_destroy(rvu); err_irq: rvu_unregister_interrupts(rvu); err_flr: @@ -2465,8 +2863,12 @@ err_mbox: rvu_mbox_destroy(&rvu->afpf_wq_info); err_hwsetup: rvu_cgx_exit(rvu); + rvu_fwdata_exit(rvu); rvu_reset_all_blocks(rvu); rvu_free_hw_resources(rvu); + rvu_clear_rvum_blk_revid(rvu); +err_put_ptp: + ptp_put(rvu->ptp); err_release_regions: pci_release_regions(pdev); err_disable_device: @@ -2482,14 +2884,18 @@ static void rvu_remove(struct pci_dev *pdev) { struct rvu *rvu = pci_get_drvdata(pdev); + rvu_dbg_exit(rvu); + rvu_policy_destroy(rvu); rvu_unregister_interrupts(rvu); rvu_flr_wq_destroy(rvu); rvu_cgx_exit(rvu); + rvu_fwdata_exit(rvu); rvu_mbox_destroy(&rvu->afpf_wq_info); rvu_disable_sriov(rvu); rvu_reset_all_blocks(rvu); rvu_free_hw_resources(rvu); - + rvu_clear_rvum_blk_revid(rvu); + ptp_put(rvu->ptp); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); @@ -2515,9 +2921,19 @@ static int __init rvu_init_module(void) if (err < 0) return err; + err = pci_register_driver(&ptp_driver); + if (err < 0) + goto ptp_err; + err = pci_register_driver(&rvu_driver); if (err < 0) - pci_unregister_driver(&cgx_driver); + goto rvu_err; + + return 0; +rvu_err: + pci_unregister_driver(&ptp_driver); +ptp_err: + pci_unregister_driver(&cgx_driver); return err; } @@ -2525,6 +2941,7 @@ static int __init rvu_init_module(void) static void __exit rvu_cleanup_module(void) { pci_unregister_driver(&rvu_driver); + pci_unregister_driver(&ptp_driver); pci_unregister_driver(&cgx_driver); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index c9d60b0554c0..5e90047b9a62 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -15,12 +15,16 @@ #include "rvu_struct.h" #include "common.h" #include "mbox.h" +#include "rvu_validation.h" /* PCI device IDs */ #define PCI_DEVID_OCTEONTX2_RVU_AF 0xA065 /* Subsystem Device ID */ #define PCI_SUBSYS_DEVID_96XX 0xB200 +#define PCI_SUBSYS_DEVID_95XX_RVU 0xB200 +#define PCI_SUBSYS_DEVID_95XX 0xB300 +#define PCI_SUBSYS_DEVID_LOKI 0xB400 /* PCI BAR nos */ #define PCI_AF_REG_BAR_NUM 0 @@ -35,9 +39,46 @@ #define RVU_PFVF_FUNC_SHIFT 0 #define RVU_PFVF_FUNC_MASK 0x3FF +/* CONFIG_DEBUG_FS */ +#ifdef CONFIG_DEBUG_FS +struct dump_ctx { + int lf; + int id; + bool all; +}; + +struct cpt_dump_ctx { + char e_type[NAME_SIZE]; +}; + +struct rvu_debugfs { + struct dentry *root; + struct dentry *npa; + struct dentry *cgx_root; + struct dentry *cgx; + struct dentry *lmac; + struct dentry *nix; + struct dentry *npc; + struct dentry *sso; + struct dentry *sso_hwgrp; + struct dentry *sso_hws; + struct dentry *cpt; + struct dump_ctx npa_aura_ctx; + struct dump_ctx npa_pool_ctx; + struct dump_ctx nix_cq_ctx; + struct dump_ctx nix_rq_ctx; + struct dump_ctx nix_sq_ctx; + struct cpt_dump_ctx cpt_ctx; + int npa_qsize_id; + int nix_qsize_id; +}; +#endif /* CONFIG_DEBUG_FS */ + struct rvu_work { struct work_struct work; struct rvu *rvu; + int num_msgs; + int up_num_msgs; }; struct rsrc_bmap { @@ -77,6 +118,68 @@ struct nix_mce_list { int max; }; +/* list of known and supported fields in packet header and + * fields present in key structure. + */ +enum key_fields { + NPC_CHAN = NPC_HEADER_FIELDS_MAX, /* Valid when Rx */ + NPC_PF_FUNC, /* Valid when Tx */ + NPC_ERRLEV, + NPC_ERRCODE, + NPC_LXMB, + NPC_LA, + NPC_LB, + NPC_LC, + NPC_LD, + NPC_LE, + NPC_LF, + NPC_LG, + NPC_LH, + /* ether type for untagged frame */ + NPC_ETYPE_ETHER, + /* ether type for single tagged frame */ + NPC_ETYPE_TAG1, + /* ether type for double tagged frame */ + NPC_ETYPE_TAG2, + /* outer vlan tci for single tagged frame */ + NPC_VLAN_TAG1, + /* outer vlan tci for double tagged frame */ + NPC_VLAN_TAG2, + /* other header fields programmed to extract but not of our interest */ + NPC_UNKNOWN, + NPC_KEY_FIELDS_MAX, +}; + +/* layer meta data to uniquely identify a packet header field */ +struct npc_layer_mdata { + u8 lid; + u8 ltype; + u8 hdr; + u8 key; + u8 len; +}; + +/* Structure to represent a field present in the + * generated key. A key field may present anywhere and can + * be of any size in the generated key. Once this structure + * is populated for fields of interest then field's presence + * and location (if present) can be known. + */ +struct npc_key_field { + /* Masks where all set bits indicate position + * of a field in the key + */ + u64 kw_mask[NPC_MAX_KWS_IN_KEY]; + /* Number of words in the key a field spans. If a field is + * of 16 bytes and key offset is 4 then the field will use + * 4 bytes in KW0, 8 bytes in KW1 and 4 bytes in KW2 and + * nr_kws will be 3(KW0, KW1 and KW2). + */ + int nr_kws; + /* used by packet header fields */ + struct npc_layer_mdata layer_mdata; +}; + struct npc_mcam { struct rsrc_bmap counters; struct mutex lock; /* MCAM entries and counters update lock */ @@ -99,6 +202,26 @@ struct npc_mcam { u16 lprio_start; u16 hprio_count; u16 hprio_end; + u16 rx_miss_act_cntr; /* Counter for RX MISS action */ + /* fields present in the generated key */ + struct npc_key_field tx_key_fields[NPC_KEY_FIELDS_MAX]; + struct npc_key_field rx_key_fields[NPC_KEY_FIELDS_MAX]; + u64 tx_features; + u64 rx_features; + struct list_head mcam_rules; +}; + +struct sso_rsrc { + u8 sso_hws; + u16 sso_hwgrps; + u16 sso_xaq_num_works; + u16 sso_xaq_buf_size; + u16 sso_iue; + u64 iaq_rsvd; + u64 iaq_max; + u64 taq_rsvd; + u64 taq_max; + struct rsrc_bmap pfvf_ident; }; /* Structure for per RVU func info ie PF/VF */ @@ -110,6 +233,7 @@ struct rvu_pfvf { u16 cptlfs; u16 timlfs; u8 cgx_lmac; + u8 sso_uniq_ident; /* Block LF's MSIX vector info */ struct rsrc_bmap msix; /* Bitmap for MSIX vector alloc */ @@ -141,25 +265,32 @@ struct rvu_pfvf { u16 maxlen; u16 minlen; + bool hw_rx_tstamp_en; /* Is rx_tstamp enabled */ u8 mac_addr[ETH_ALEN]; /* MAC address of this PF/VF */ /* Broadcast pkt replication info */ u16 bcast_mce_idx; struct nix_mce_list bcast_mce_list; - /* VLAN offload */ - struct mcam_entry entry; - int rxvlan_index; - bool rxvlan; + /* For resource limits */ + struct pci_dev *pdev; + struct kobject *limits_kobj; + + struct rvu_npc_mcam_rule *def_rule; + + bool cgx_in_use; /* this PF/VF using CGX? */ + int cgx_users; /* number of cgx users - used only by PFs */ }; struct nix_txsch { struct rsrc_bmap schq; u8 lvl; -#define NIX_TXSCHQ_TL1_CFG_DONE BIT_ULL(0) +#define NIX_TXSCHQ_FREE BIT_ULL(1) +#define NIX_TXSCHQ_CFG_DONE BIT_ULL(0) #define TXSCH_MAP_FUNC(__pfvf_map) ((__pfvf_map) & 0xFFFF) #define TXSCH_MAP_FLAGS(__pfvf_map) ((__pfvf_map) >> 16) #define TXSCH_MAP(__func, __flags) (((__func) & 0xFFFF) | ((__flags) << 16)) +#define TXSCH_SET_FLAG(__pfvf_map, flag) ((__pfvf_map) | ((flag) << 16)) u32 *pfvf_map; }; @@ -185,12 +316,36 @@ struct nix_lso { u8 in_use; }; +struct nix_txvlan { +#define NIX_TX_VTAG_DEF_MAX 0x400 + struct rsrc_bmap rsrc; + u16 *entry2pfvf_map; + struct mutex rsrc_lock; /* Serialize resource alloc/free */ +}; + struct nix_hw { struct nix_txsch txsch[NIX_TXSCH_LVL_CNT]; /* Tx schedulers */ struct nix_mcast mcast; struct nix_flowkey flowkey; struct nix_mark_format mark_format; struct nix_lso lso; + struct nix_txvlan txvlan; + void *tx_stall; +}; + +/* RVU block's capabilities or functionality, + * which vary by silicon version/skew. + */ +struct hw_cap { + /* Transmit side supported functionality */ + u8 nix_tx_aggr_lvl; /* Tx link's traffic aggregation level */ + u16 nix_txsch_per_cgx_lmac; /* Max Q's transmitting to CGX LMAC */ + u16 nix_txsch_per_lbk_lmac; /* Max Q's transmitting to LBK LMAC */ + u16 nix_txsch_per_sdp_lmac; /* Max Q's transmitting to SDP LMAC */ + bool nix_fixed_txschq_mapping; /* Schq mapping fixed or flexible */ + bool nix_shaping; /* Is shaping and coloring supported */ + bool nix_tx_link_bp; /* Can link backpressure TL queues ? */ + bool nix_rx_multicast; /* Rx packet replication support */ }; struct rvu_hwinfo { @@ -204,11 +359,12 @@ struct rvu_hwinfo { u8 sdp_links; u8 npc_kpus; /* No of parser units */ - + struct hw_cap cap; struct rvu_block block[BLK_COUNT]; /* Block info */ struct nix_hw *nix0; struct npc_pkind pkind; struct npc_mcam mcam; + struct sso_rsrc sso; }; struct mbox_wq_info { @@ -221,6 +377,32 @@ struct mbox_wq_info { struct workqueue_struct *mbox_wq; }; +struct rvu_fwdata { +#define RVU_FWDATA_HEADER_MAGIC 0xCFDA /*Custom Firmware Data*/ +#define RVU_FWDATA_VERSION 0x0001 + u32 header_magic; + u32 version; /* version id */ + + /* MAC address */ +#define PF_MACNUM_MAX 32 +#define VF_MACNUM_MAX 256 + u64 pf_macs[PF_MACNUM_MAX]; + u64 vf_macs[VF_MACNUM_MAX]; + u64 sclk; + u64 rclk; + u64 mcam_addr; + u64 mcam_sz; + u64 msixtr_base; +#define FWDATA_RESERVED_MEM 1023 + u64 reserved[FWDATA_RESERVED_MEM]; + /* Do not add new fields below this line */ +#define CGX_MAX 4 +#define CGX_LMACS_MAX 4 + struct cgx_lmac_fwdata_s cgx_fw_data[CGX_MAX][CGX_LMACS_MAX]; +}; + +struct ptp; + struct rvu { void __iomem *afreg_base; void __iomem *pfreg_base; @@ -229,6 +411,7 @@ struct rvu { struct rvu_hwinfo *hw; struct rvu_pfvf *pf; struct rvu_pfvf *hwvf; + struct rvu_limits pf_limits; struct mutex rsrc_lock; /* Serialize resource alloc/free */ int vfs; /* Number of VFs attached to RVU */ @@ -246,6 +429,7 @@ struct rvu { char *irq_name; bool *irq_allocated; dma_addr_t msix_base_iova; + u64 msixtr_base_phy;/* Register reset value */ /* CGX */ #define PF_CGXMAP_BASE 1 /* PF 0 is reserved for RVU PF */ @@ -261,8 +445,19 @@ struct rvu { struct workqueue_struct *cgx_evh_wq; spinlock_t cgx_evq_lock; /* cgx event queue lock */ struct list_head cgx_evq_head; /* cgx event queue head */ + struct mutex cgx_cfg_lock; /* serialize cgx configuration */ char mkex_pfl_name[MKEX_NAME_LEN]; /* Configured MKEX profile name */ + + /* Firmware data */ + struct rvu_fwdata *fwdata; + + struct ptp *ptp; + +/* CONFIG_DEBUG_FS */ +#ifdef CONFIG_DEBUG_FS + struct rvu_debugfs rvu_dbg; +#endif /* CONFIG_DEBUG_FS */ }; static inline void rvu_write64(struct rvu *rvu, u64 block, u64 offset, u64 val) @@ -285,7 +480,8 @@ static inline u64 rvupf_read64(struct rvu *rvu, u64 offset) return readq(rvu->pfreg_base + offset); } -static inline bool is_rvu_9xxx_A0(struct rvu *rvu) +/* Silicon revisions */ +static inline bool is_rvu_96xx_A0(struct rvu *rvu) { struct pci_dev *pdev = rvu->pdev; @@ -293,6 +489,28 @@ static inline bool is_rvu_9xxx_A0(struct rvu *rvu) (pdev->subsystem_device == PCI_SUBSYS_DEVID_96XX); } +static inline bool is_rvu_96xx_B0(struct rvu *rvu) +{ + struct pci_dev *pdev = rvu->pdev; + + return ((pdev->revision == 0x00) || (pdev->revision == 0x01)) && + (pdev->subsystem_device == PCI_SUBSYS_DEVID_96XX); +} + +static inline bool is_rvu_95xx_A0(struct rvu *rvu) +{ + struct pci_dev *pdev = rvu->pdev; + + return ((pdev->revision == 0x10) || (pdev->revision == 0x11)) && + (pdev->subsystem_device == PCI_SUBSYS_DEVID_95XX_RVU); +} + +static inline bool is_cgx_mapped_to_nix(unsigned short id, u8 cgx_id) +{ + return !(cgx_id && (id == PCI_SUBSYS_DEVID_95XX || + id == PCI_SUBSYS_DEVID_LOKI)); +} + /* Function Prototypes * RVU */ @@ -301,12 +519,19 @@ static inline int is_afvf(u16 pcifunc) return !(pcifunc & ~RVU_PFVF_FUNC_MASK); } +static inline bool is_rvu_fwdata_valid(struct rvu *rvu) +{ + return (rvu->fwdata->header_magic == RVU_FWDATA_HEADER_MAGIC) && + (rvu->fwdata->version == RVU_FWDATA_VERSION); +} + int rvu_alloc_bitmap(struct rsrc_bmap *rsrc); int rvu_alloc_rsrc(struct rsrc_bmap *rsrc); void rvu_free_rsrc(struct rsrc_bmap *rsrc, int id); int rvu_rsrc_free_count(struct rsrc_bmap *rsrc); int rvu_alloc_rsrc_contig(struct rsrc_bmap *rsrc, int nrsrc); bool rvu_rsrc_check_contig(struct rsrc_bmap *rsrc, int nrsrc); +u16 rvu_get_rsrc_mapcount(struct rvu_pfvf *pfvf, int blktype); int rvu_get_pf(u16 pcifunc); struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc); void rvu_get_pf_numvfs(struct rvu *rvu, int pf, int *numvfs, int *hwvf); @@ -316,6 +541,9 @@ int rvu_get_lf(struct rvu *rvu, struct rvu_block *block, u16 pcifunc, u16 slot); int rvu_lf_reset(struct rvu *rvu, struct rvu_block *block, int lf); int rvu_get_blkaddr(struct rvu *rvu, int blktype, u16 pcifunc); int rvu_poll_reg(struct rvu *rvu, u64 block, u64 offset, u64 mask, bool zero); +u16 rvu_get_rsrc_mapcount(struct rvu_pfvf *pfvf, int blkid); +int rvu_get_num_lbk_chans(void); +int rvu_ndc_sync(struct rvu *rvu, int lfblkid, int lfidx, u64 lfoffset); /* RVU HW reg validation */ enum regmap_block { @@ -342,52 +570,39 @@ static inline void rvu_get_cgx_lmac_id(u8 map, u8 *cgx_id, u8 *lmac_id) *lmac_id = (map & 0xF); } +#define M(_name, _id, fn_name, req, rsp) \ +int rvu_mbox_handler_ ## fn_name(struct rvu *, struct req *, struct rsp *); +MBOX_MESSAGES +#undef M + int rvu_cgx_init(struct rvu *rvu); int rvu_cgx_exit(struct rvu *rvu); void *rvu_cgx_pdata(u8 cgx_id, struct rvu *rvu); int rvu_cgx_config_rxtx(struct rvu *rvu, u16 pcifunc, bool start); -int rvu_mbox_handler_cgx_start_rxtx(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_cgx_stop_rxtx(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_cgx_stats(struct rvu *rvu, struct msg_req *req, - struct cgx_stats_rsp *rsp); -int rvu_mbox_handler_cgx_mac_addr_set(struct rvu *rvu, - struct cgx_mac_addr_set_or_get *req, - struct cgx_mac_addr_set_or_get *rsp); -int rvu_mbox_handler_cgx_mac_addr_get(struct rvu *rvu, - struct cgx_mac_addr_set_or_get *req, - struct cgx_mac_addr_set_or_get *rsp); -int rvu_mbox_handler_cgx_promisc_enable(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_cgx_promisc_disable(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_cgx_start_linkevents(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_cgx_stop_linkevents(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_cgx_get_linkinfo(struct rvu *rvu, struct msg_req *req, - struct cgx_link_info_msg *rsp); -int rvu_mbox_handler_cgx_intlbk_enable(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_cgx_intlbk_disable(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); +void rvu_cgx_enadis_rx_bp(struct rvu *rvu, int pf, bool enable); +void rvu_cgx_disable_dmac_entries(struct rvu *rvu, u16 pcifunc); +int rvu_cgx_start_stop_io(struct rvu *rvu, u16 pcifunc, bool start); +int rvu_cgx_nix_cuml_stats(struct rvu *rvu, void *cgxd, int lmac_id, int index, + int rxtxflag, u64 *stat); +bool is_cgx_config_permitted(struct rvu *rvu, u16 pcifunc); + +/* SSO APIs */ +int rvu_sso_init(struct rvu *rvu); +void rvu_sso_freemem(struct rvu *rvu); +int rvu_sso_register_interrupts(struct rvu *rvu); +void rvu_sso_unregister_interrupts(struct rvu *rvu); +int rvu_sso_lf_teardown(struct rvu *rvu, u16 pcifunc, int lf, int slot_id); +int rvu_ssow_lf_teardown(struct rvu *rvu, u16 pcifunc, int lf, int slot_id); +void rvu_sso_hwgrp_config_thresh(struct rvu *rvu, int blkaddr, int lf); /* NPA APIs */ int rvu_npa_init(struct rvu *rvu); void rvu_npa_freemem(struct rvu *rvu); void rvu_npa_lf_teardown(struct rvu *rvu, u16 pcifunc, int npalf); -int rvu_mbox_handler_npa_aq_enq(struct rvu *rvu, - struct npa_aq_enq_req *req, - struct npa_aq_enq_rsp *rsp); -int rvu_mbox_handler_npa_hwctx_disable(struct rvu *rvu, - struct hwctx_disable_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_npa_lf_alloc(struct rvu *rvu, - struct npa_lf_alloc_req *req, - struct npa_lf_alloc_rsp *rsp); -int rvu_mbox_handler_npa_lf_free(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); +int rvu_npa_aq_enq_inst(struct rvu *rvu, struct npa_aq_enq_req *req, + struct npa_aq_enq_rsp *rsp); +int rvu_npa_register_interrupts(struct rvu *rvu); +void rvu_npa_unregister_interrupts(struct rvu *rvu); /* NIX APIs */ bool is_nixlf_attached(struct rvu *rvu, u16 pcifunc); @@ -397,61 +612,16 @@ int rvu_nix_reserve_mark_format(struct rvu *rvu, struct nix_hw *nix_hw, void rvu_nix_freemem(struct rvu *rvu); int rvu_get_nixlf_count(struct rvu *rvu); void rvu_nix_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int npalf); -int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, - struct nix_lf_alloc_req *req, - struct nix_lf_alloc_rsp *rsp); -int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_aq_enq(struct rvu *rvu, - struct nix_aq_enq_req *req, - struct nix_aq_enq_rsp *rsp); -int rvu_mbox_handler_nix_hwctx_disable(struct rvu *rvu, - struct hwctx_disable_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu, - struct nix_txsch_alloc_req *req, - struct nix_txsch_alloc_rsp *rsp); -int rvu_mbox_handler_nix_txsch_free(struct rvu *rvu, - struct nix_txsch_free_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu, - struct nix_txschq_config *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_stats_rst(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_vtag_cfg(struct rvu *rvu, - struct nix_vtag_config *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_rxvlan_alloc(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_rss_flowkey_cfg(struct rvu *rvu, - struct nix_rss_flowkey_cfg *req, - struct nix_rss_flowkey_cfg_rsp *rsp); -int rvu_mbox_handler_nix_set_mac_addr(struct rvu *rvu, - struct nix_set_mac_addr *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_lf_start_rx(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_lf_stop_rx(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_mark_format_cfg(struct rvu *rvu, - struct nix_mark_format_cfg *req, - struct nix_mark_format_cfg_rsp *rsp); -int rvu_mbox_handler_nix_set_rx_cfg(struct rvu *rvu, struct nix_rx_cfg *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_nix_lso_format_cfg(struct rvu *rvu, - struct nix_lso_format_cfg *req, - struct nix_lso_format_cfg_rsp *rsp); +int nix_get_nixlf(struct rvu *rvu, u16 pcifunc, int *nixlf); +int rvu_nix_register_interrupts(struct rvu *rvu); +void rvu_nix_unregister_interrupts(struct rvu *rvu); /* NPC APIs */ int rvu_npc_init(struct rvu *rvu); void rvu_npc_freemem(struct rvu *rvu); int rvu_npc_get_pkind(struct rvu *rvu, u16 pf); void rvu_npc_set_pkind(struct rvu *rvu, int pkind, struct rvu_pfvf *pfvf); +int npc_config_ts_kpuaction(struct rvu *rvu, int pf, u16 pcifunc, bool en); void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, int nixlf, u64 chan, u8 *mac_addr); void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc, @@ -460,45 +630,69 @@ void rvu_npc_disable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf); void rvu_npc_enable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf); void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc, int nixlf, u64 chan); -int rvu_npc_update_rxvlan(struct rvu *rvu, u16 pcifunc, int nixlf); +void rvu_npc_disable_bcast_entry(struct rvu *rvu, u16 pcifunc); void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf); +void rvu_npc_free_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf); void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf); void rvu_npc_enable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf); void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf, int group, int alg_idx, int mcam_index); -int rvu_mbox_handler_npc_mcam_alloc_entry(struct rvu *rvu, - struct npc_mcam_alloc_entry_req *req, - struct npc_mcam_alloc_entry_rsp *rsp); -int rvu_mbox_handler_npc_mcam_free_entry(struct rvu *rvu, - struct npc_mcam_free_entry_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_npc_mcam_write_entry(struct rvu *rvu, - struct npc_mcam_write_entry_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_npc_mcam_ena_entry(struct rvu *rvu, - struct npc_mcam_ena_dis_entry_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_npc_mcam_dis_entry(struct rvu *rvu, - struct npc_mcam_ena_dis_entry_req *req, - struct msg_rsp *rsp); -int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu, - struct npc_mcam_shift_entry_req *req, - struct npc_mcam_shift_entry_rsp *rsp); -int rvu_mbox_handler_npc_mcam_alloc_counter(struct rvu *rvu, - struct npc_mcam_alloc_counter_req *req, - struct npc_mcam_alloc_counter_rsp *rsp); -int rvu_mbox_handler_npc_mcam_free_counter(struct rvu *rvu, - struct npc_mcam_oper_counter_req *req, struct msg_rsp *rsp); -int rvu_mbox_handler_npc_mcam_clear_counter(struct rvu *rvu, - struct npc_mcam_oper_counter_req *req, struct msg_rsp *rsp); -int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu, - struct npc_mcam_unmap_counter_req *req, struct msg_rsp *rsp); -int rvu_mbox_handler_npc_mcam_counter_stats(struct rvu *rvu, - struct npc_mcam_oper_counter_req *req, - struct npc_mcam_oper_counter_rsp *rsp); -int rvu_mbox_handler_npc_mcam_alloc_and_write_entry(struct rvu *rvu, - struct npc_mcam_alloc_and_write_entry_req *req, - struct npc_mcam_alloc_and_write_entry_rsp *rsp); -int rvu_mbox_handler_npc_get_kex_cfg(struct rvu *rvu, struct msg_req *req, - struct npc_get_kex_cfg_rsp *rsp); +void rvu_npc_get_mcam_entry_alloc_info(struct rvu *rvu, u16 pcifunc, + int blkaddr, int *alloc_cnt, + int *enable_cnt); +void rvu_npc_get_mcam_counter_alloc_info(struct rvu *rvu, u16 pcifunc, + int blkaddr, int *alloc_cnt, + int *enable_cnt); +int npc_flow_steering_init(struct rvu *rvu, int blkaddr); +const char *npc_get_field_name(u8 hdr); +bool rvu_npc_write_default_rule(struct rvu *rvu, int blkaddr, int nixlf, + u16 pcifunc, u8 intf, struct mcam_entry *entry, + int *entry_index); +int npc_mcam_verify_channel(struct rvu *rvu, u16 pcifunc, u8 intf, u16 channel); +int npc_get_bank(struct npc_mcam *mcam, int index); +void npc_mcam_enable_flows(struct rvu *rvu, u16 target); +void npc_enable_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, + int blkaddr, int index, bool enable); + +/* CPT APIs */ +int rvu_cpt_init(struct rvu *rvu); +int rvu_cpt_register_interrupts(struct rvu *rvu); +void rvu_cpt_unregister_interrupts(struct rvu *rvu); + +/* TIM APIs */ +int rvu_tim_init(struct rvu *rvu); +int rvu_tim_lf_teardown(struct rvu *rvu, u16 pcifunc, int lf, int slot); + +/* SDP APIs */ +int rvu_sdp_init(struct rvu *rvu); +bool is_sdp_pf(u16 pcifunc); + +/* CONFIG_DEBUG_FS*/ +#ifdef CONFIG_DEBUG_FS +void rvu_dbg_init(struct rvu *rvu); +void rvu_dbg_exit(struct rvu *rvu); +#else +static inline void rvu_dbg_init(struct rvu *rvu) {} +static inline void rvu_dbg_exit(struct rvu *rvu) {} +#endif /* CONFIG_DEBUG_FS*/ + +/* HW workarounds/fixes */ +#include "npc.h" +void rvu_nix_txsch_lock(struct nix_hw *nix_hw); +void rvu_nix_txsch_unlock(struct nix_hw *nix_hw); +void rvu_nix_update_link_credits(struct rvu *rvu, int blkaddr, + int link, u64 ncredits); + +void rvu_nix_update_sq_smq_mapping(struct rvu *rvu, int blkaddr, int nixlf, + u16 sq, u16 smq); +void rvu_nix_txsch_config_changed(struct nix_hw *nix_hw); +ssize_t rvu_nix_get_tx_stall_counters(struct rvu *rvu, + char __user *buffer, loff_t *ppos); +int rvu_nix_fixes_init(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr); +void rvu_nix_fixes_exit(struct rvu *rvu, struct nix_hw *nix_hw); +int rvu_tim_lookup_rsrc(struct rvu *rvu, struct rvu_block *block, + u16 pcifunc, int slot); +int rvu_npc_get_tx_nibble_cfg(struct rvu *rvu, u64 nibble_ena); +bool is_parse_nibble_config_valid(struct rvu *rvu, + struct npc_mcam_kex *mcam_kex); #endif /* RVU_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c index 7d7133c5f799..145329cf5eaf 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c @@ -14,6 +14,7 @@ #include "rvu.h" #include "cgx.h" +#include "rvu_reg.h" struct cgx_evq_entry { struct list_head evq_node; @@ -45,6 +46,19 @@ static inline u16 cgxlmac_to_pfmap(struct rvu *rvu, u8 cgx_id, u8 lmac_id) return rvu->cgxlmac2pf_map[CGX_OFFSET(cgx_id) + lmac_id]; } +static inline int cgxlmac_to_pf(struct rvu *rvu, int cgx_id, int lmac_id) +{ + unsigned long pfmap; + + pfmap = cgxlmac_to_pfmap(rvu, cgx_id, lmac_id); + + /* Assumes only one pf mapped to a cgx lmac port */ + if (!pfmap) + return -ENODEV; + else + return find_first_bit(&pfmap, 16); +} + static inline u8 cgxlmac_id_to_bmap(u8 cgx_id, u8 lmac_id) { return ((cgx_id & 0xF) << 4) | (lmac_id & 0xF); @@ -171,12 +185,8 @@ static void cgx_notify_pfs(struct cgx_link_event *event, struct rvu *rvu) clear_bit(pfid, &pfmap); /* check if notification is enabled */ - if (!test_bit(pfid, &rvu->pf_notify_bmap)) { - dev_info(rvu->dev, "cgx %d: lmac %d Link status %s\n", - event->cgx_id, event->lmac_id, - linfo->link_up ? "UP" : "DOWN"); + if (!test_bit(pfid, &rvu->pf_notify_bmap)) continue; - } /* Send mbox message to PF */ msg = otx2_mbox_alloc_msg_cgx_link_event(rvu, pfid); @@ -272,7 +282,7 @@ int rvu_cgx_init(struct rvu *rvu) rvu->cgx_cnt_max = cgx_get_cgxcnt_max(); if (!rvu->cgx_cnt_max) { dev_info(rvu->dev, "No CGX devices found!\n"); - return -ENODEV; + return 0; } rvu->cgx_idmap = devm_kzalloc(rvu->dev, rvu->cgx_cnt_max * @@ -294,6 +304,8 @@ int rvu_cgx_init(struct rvu *rvu) if (err) return err; + mutex_init(&rvu->cgx_cfg_lock); + /* Ensure event handler registration is completed, before * we turn on the links */ @@ -334,16 +346,43 @@ int rvu_cgx_exit(struct rvu *rvu) return 0; } +/* Most of the CGX configuration is restricted to the mapped PF only, + * VF's of mapped PF and other PFs are not allowed. This fn() checks + * whether a PFFUNC is permitted to do the config or not. + */ +inline bool is_cgx_config_permitted(struct rvu *rvu, u16 pcifunc) +{ + if ((pcifunc & RVU_PFVF_FUNC_MASK) || + !is_pf_cgxmapped(rvu, rvu_get_pf(pcifunc))) + return false; + return true; +} + +void rvu_cgx_enadis_rx_bp(struct rvu *rvu, int pf, bool enable) +{ + u8 cgx_id, lmac_id; + void *cgxd; + + if (!is_pf_cgxmapped(rvu, pf)) + return; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + + /* Set / clear CTL_BCK to control pause frame forwarding to NIX */ + if (enable) + cgx_lmac_enadis_rx_pause_fwding(cgxd, lmac_id, true); + else + cgx_lmac_enadis_rx_pause_fwding(cgxd, lmac_id, false); +} + int rvu_cgx_config_rxtx(struct rvu *rvu, u16 pcifunc, bool start) { int pf = rvu_get_pf(pcifunc); u8 cgx_id, lmac_id; - /* This msg is expected only from PFs that are mapped to CGX LMACs, - * if received from other PF/VF simply ACK, nothing to do. - */ - if ((pcifunc & RVU_PFVF_FUNC_MASK) || !is_pf_cgxmapped(rvu, pf)) - return -ENODEV; + if (!is_cgx_config_permitted(rvu, pcifunc)) + return -EPERM; rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); @@ -352,6 +391,31 @@ int rvu_cgx_config_rxtx(struct rvu *rvu, u16 pcifunc, bool start) return 0; } +void rvu_cgx_disable_dmac_entries(struct rvu *rvu, u16 pcifunc) +{ + int pf = rvu_get_pf(pcifunc); + int i = 0, lmac_count = 0; + u8 max_dmac_filters; + u8 cgx_id, lmac_id; + void *cgx_dev; + + if (!is_cgx_config_permitted(rvu, pcifunc)) + return; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgx_dev = cgx_get_pdata(cgx_id); + lmac_count = cgx_get_lmac_cnt(cgx_dev); + max_dmac_filters = MAX_DMAC_ENTRIES_PER_CGX / lmac_count; + + for (i = 0; i < max_dmac_filters; i++) + cgx_lmac_addr_del(cgx_id, lmac_id, i); + + /* As cgx_lmac_addr_del does not clear entry for index 0 + * so it needs to be done explicitly + */ + cgx_lmac_addr_reset(cgx_id, lmac_id); +} + int rvu_mbox_handler_cgx_start_rxtx(struct rvu *rvu, struct msg_req *req, struct msg_rsp *rsp) { @@ -375,8 +439,7 @@ int rvu_mbox_handler_cgx_stats(struct rvu *rvu, struct msg_req *req, u8 cgx_idx, lmac; void *cgxd; - if ((req->hdr.pcifunc & RVU_PFVF_FUNC_MASK) || - !is_pf_cgxmapped(rvu, pf)) + if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) return -ENODEV; rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_idx, &lmac); @@ -403,36 +466,108 @@ int rvu_mbox_handler_cgx_stats(struct rvu *rvu, struct msg_req *req, return 0; } +int rvu_mbox_handler_cgx_fec_stats(struct rvu *rvu, + struct msg_req *req, + struct cgx_fec_stats_rsp *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u8 cgx_idx, lmac; + int err = 0; + void *cgxd; + + if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) + return -EPERM; + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_idx, &lmac); + + cgxd = rvu_cgx_pdata(cgx_idx, rvu); + err = cgx_get_fec_stats(cgxd, lmac, rsp); + return err; +} + int rvu_mbox_handler_cgx_mac_addr_set(struct rvu *rvu, struct cgx_mac_addr_set_or_get *req, struct cgx_mac_addr_set_or_get *rsp) { int pf = rvu_get_pf(req->hdr.pcifunc); + struct rvu_pfvf *pfvf; u8 cgx_id, lmac_id; rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + pfvf = &rvu->pf[pf]; + memcpy(pfvf->mac_addr, req->mac_addr, ETH_ALEN); cgx_lmac_addr_set(cgx_id, lmac_id, req->mac_addr); return 0; } -int rvu_mbox_handler_cgx_mac_addr_get(struct rvu *rvu, - struct cgx_mac_addr_set_or_get *req, - struct cgx_mac_addr_set_or_get *rsp) +int rvu_mbox_handler_cgx_mac_addr_add(struct rvu *rvu, + struct cgx_mac_addr_add_req *req, + struct cgx_mac_addr_add_rsp *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u8 cgx_id, lmac_id; + int rc = 0; + + if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) + return -EPERM; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + rc = cgx_lmac_addr_add(cgx_id, lmac_id, req->mac_addr); + if (rc >= 0) { + rsp->index = rc; + return 0; + } + + return rc; +} + +int rvu_mbox_handler_cgx_mac_addr_del(struct rvu *rvu, + struct cgx_mac_addr_del_req *req, + struct msg_rsp *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u8 cgx_id, lmac_id; + + if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) + return -EPERM; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + return cgx_lmac_addr_del(cgx_id, lmac_id, req->index); +} + +int rvu_mbox_handler_cgx_mac_max_entries_get(struct rvu *rvu, + struct msg_req *req, + struct cgx_max_dmac_entries_get_rsp *rsp) { int pf = rvu_get_pf(req->hdr.pcifunc); u8 cgx_id, lmac_id; - int rc = 0, i; - u64 cfg; + + /* If msg is received from PFs(which are not mapped to CGX LMACs) + * or VF then no entries are allocated for DMAC filters at CGX level. + * So returning zero. + */ + if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) { + rsp->max_dmac_filters = 0; + return 0; + } rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + rsp->max_dmac_filters = cgx_lmac_addr_max_entries_get(cgx_id, lmac_id); + return 0; +} + +int rvu_mbox_handler_cgx_mac_addr_get(struct rvu *rvu, + struct cgx_mac_addr_set_or_get *req, + struct cgx_mac_addr_set_or_get *rsp) +{ + struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); + int i; - rsp->hdr.rc = rc; - cfg = cgx_lmac_addr_get(cgx_id, lmac_id); - /* copy 48 bit mac address to req->mac_addr */ + /* copy 48 bit mac address to rsp->mac_addr */ for (i = 0; i < ETH_ALEN; i++) - rsp->mac_addr[i] = cfg >> (ETH_ALEN - 1 - i) * 8; + rsp->mac_addr[i] = pfvf->mac_addr[i]; + return 0; } @@ -443,12 +578,8 @@ int rvu_mbox_handler_cgx_promisc_enable(struct rvu *rvu, struct msg_req *req, int pf = rvu_get_pf(pcifunc); u8 cgx_id, lmac_id; - /* This msg is expected only from PFs that are mapped to CGX LMACs, - * if received from other PF/VF simply ACK, nothing to do. - */ - if ((req->hdr.pcifunc & RVU_PFVF_FUNC_MASK) || - !is_pf_cgxmapped(rvu, pf)) - return -ENODEV; + if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) + return -EPERM; rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); @@ -459,20 +590,90 @@ int rvu_mbox_handler_cgx_promisc_enable(struct rvu *rvu, struct msg_req *req, int rvu_mbox_handler_cgx_promisc_disable(struct rvu *rvu, struct msg_req *req, struct msg_rsp *rsp) { + int pf = rvu_get_pf(req->hdr.pcifunc); + u8 cgx_id, lmac_id; + + if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) + return -EPERM; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + + cgx_lmac_promisc_config(cgx_id, lmac_id, false); + return 0; +} + +static void cgx_notify_up_ptp_info(struct rvu *rvu, int pf, bool enable) +{ + struct cgx_ptp_rx_info_msg *msg; + int err; + + /* Send mbox message to PF */ + msg = otx2_mbox_alloc_msg_cgx_ptp_rx_info(rvu, pf); + if (!msg) { + dev_err(rvu->dev, "ptp notification to pf %d failed\n", pf); + return; + } + + msg->ptp_en = enable; + otx2_mbox_msg_send(&rvu->afpf_wq_info.mbox_up, pf); + err = otx2_mbox_wait_for_rsp(&rvu->afpf_wq_info.mbox_up, pf); + if (err) + dev_err(rvu->dev, "ptp notification to pf %d failed\n", pf); +} + +int rvu_mbox_handler_cgx_ptp_rx_enable(struct rvu *rvu, struct msg_req *req, + struct msg_rsp *rsp) +{ + struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); u16 pcifunc = req->hdr.pcifunc; int pf = rvu_get_pf(pcifunc); u8 cgx_id, lmac_id; + void *cgxd; - /* This msg is expected only from PFs that are mapped to CGX LMACs, - * if received from other PF/VF simply ACK, nothing to do. + if (!is_cgx_config_permitted(rvu, pcifunc)) + return -EPERM; + + cgx_notify_up_ptp_info(rvu, pf, true); + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + + cgx_lmac_ptp_config(cgxd, lmac_id, true); + /* Inform NPC that packets to be parsed by this PF + * will have their data shifted by 8B */ - if ((req->hdr.pcifunc & RVU_PFVF_FUNC_MASK) || - !is_pf_cgxmapped(rvu, pf)) - return -ENODEV; + if (npc_config_ts_kpuaction(rvu, pf, pcifunc, true)) + return -EINVAL; + /* This flag is required to clean up CGX conf if app gets killed */ + pfvf->hw_rx_tstamp_en = true; + + return 0; +} + +int rvu_mbox_handler_cgx_ptp_rx_disable(struct rvu *rvu, struct msg_req *req, + struct msg_rsp *rsp) +{ + struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); + u16 pcifunc = req->hdr.pcifunc; + int pf = rvu_get_pf(pcifunc); + u8 cgx_id, lmac_id; + void *cgxd; + + if (!is_cgx_config_permitted(rvu, pcifunc)) + return -EPERM; + + cgx_notify_up_ptp_info(rvu, pf, false); rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + + cgx_lmac_ptp_config(cgxd, lmac_id, false); + /* Inform NPC that 8B shift is cancelled */ + if (npc_config_ts_kpuaction(rvu, pf, pcifunc, false)) + return -EINVAL; + + pfvf->hw_rx_tstamp_en = false; - cgx_lmac_promisc_config(cgx_id, lmac_id, false); return 0; } @@ -481,11 +682,8 @@ static int rvu_cgx_config_linkevents(struct rvu *rvu, u16 pcifunc, bool en) int pf = rvu_get_pf(pcifunc); u8 cgx_id, lmac_id; - /* This msg is expected only from PFs that are mapped to CGX LMACs, - * if received from other PF/VF simply ACK, nothing to do. - */ - if ((pcifunc & RVU_PFVF_FUNC_MASK) || !is_pf_cgxmapped(rvu, pf)) - return -ENODEV; + if (!is_cgx_config_permitted(rvu, pcifunc)) + return -EPERM; rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); @@ -523,7 +721,7 @@ int rvu_mbox_handler_cgx_get_linkinfo(struct rvu *rvu, struct msg_req *req, pf = rvu_get_pf(req->hdr.pcifunc); if (!is_pf_cgxmapped(rvu, pf)) - return -ENODEV; + return -EPERM; rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); @@ -537,11 +735,8 @@ static int rvu_cgx_config_intlbk(struct rvu *rvu, u16 pcifunc, bool en) int pf = rvu_get_pf(pcifunc); u8 cgx_id, lmac_id; - /* This msg is expected only from PFs that are mapped to CGX LMACs, - * if received from other PF/VF simply ACK, nothing to do. - */ - if ((pcifunc & RVU_PFVF_FUNC_MASK) || !is_pf_cgxmapped(rvu, pf)) - return -ENODEV; + if (!is_cgx_config_permitted(rvu, pcifunc)) + return -EPERM; rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); @@ -562,3 +757,224 @@ int rvu_mbox_handler_cgx_intlbk_disable(struct rvu *rvu, struct msg_req *req, rvu_cgx_config_intlbk(rvu, req->hdr.pcifunc, false); return 0; } + +int rvu_mbox_handler_cgx_cfg_pause_frm(struct rvu *rvu, + struct cgx_pause_frm_cfg *req, + struct cgx_pause_frm_cfg *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u8 cgx_id, lmac_id; + + /* This msg is expected only from PF/VFs that are mapped to CGX LMACs, + * if received from other PF/VF simply ACK, nothing to do. + */ + if (!is_pf_cgxmapped(rvu, pf)) + return -ENODEV; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + + if (req->set) + cgx_lmac_set_pause_frm(rvu_cgx_pdata(cgx_id, rvu), lmac_id, + req->tx_pause, req->rx_pause); + else + cgx_lmac_get_pause_frm(rvu_cgx_pdata(cgx_id, rvu), lmac_id, + &rsp->tx_pause, &rsp->rx_pause); + return 0; +} + +int rvu_mbox_handler_cgx_get_aux_link_info(struct rvu *rvu, struct msg_req *req, + struct cgx_fw_data *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u8 cgx_id, lmac_id; + + if (!rvu->fwdata) + return -ENXIO; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + + memcpy(&rsp->fwdata, &rvu->fwdata->cgx_fw_data[cgx_id][lmac_id], + sizeof(struct cgx_lmac_fwdata_s)); + return 0; +} + +int rvu_mbox_handler_cgx_set_fec_param(struct rvu *rvu, + struct fec_mode *req, + struct fec_mode *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u8 cgx_id, lmac_id; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + rsp->fec = cgx_set_fec(req->fec, cgx_id, lmac_id); + return 0; +} + +int rvu_cgx_start_stop_io(struct rvu *rvu, u16 pcifunc, bool start) +{ + struct rvu_pfvf *parent_pf, *pfvf; + int cgx_users, err = 0; + + if (!is_pf_cgxmapped(rvu, rvu_get_pf(pcifunc))) + return 0; + + parent_pf = &rvu->pf[rvu_get_pf(pcifunc)]; + pfvf = rvu_get_pfvf(rvu, pcifunc); + + mutex_lock(&rvu->cgx_cfg_lock); + + if (start && pfvf->cgx_in_use) + goto exit; /* CGX is already started hence nothing to do */ + if (!start && !pfvf->cgx_in_use) + goto exit; /* CGX is already stopped hence nothing to do */ + + if (start) { + cgx_users = parent_pf->cgx_users; + parent_pf->cgx_users++; + } else { + parent_pf->cgx_users--; + cgx_users = parent_pf->cgx_users; + } + + /* Start CGX when first of all NIXLFs is started. + * Stop CGX when last of all NIXLFs is stopped. + */ + if (!cgx_users) { + err = rvu_cgx_config_rxtx(rvu, pcifunc & ~RVU_PFVF_FUNC_MASK, + start); + if (err) { + dev_err(rvu->dev, "Unable to %s CGX\n", + start ? "start" : "stop"); + /* Revert the usage count in case of error */ + parent_pf->cgx_users = start ? parent_pf->cgx_users - 1 + : parent_pf->cgx_users + 1; + goto exit; + } + } + pfvf->cgx_in_use = start; +exit: + mutex_unlock(&rvu->cgx_cfg_lock); + return err; +} + +int rvu_mbox_handler_cgx_set_link_state(struct rvu *rvu, + struct cgx_set_link_state_msg *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + u8 cgx_id, lmac_id; + int pf, err; + + pf = rvu_get_pf(pcifunc); + + if (!is_cgx_config_permitted(rvu, pcifunc)) + return -EPERM; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + + err = cgx_set_link_state(rvu_cgx_pdata(cgx_id, rvu), lmac_id, + !!req->enable); + if (err) + dev_warn(rvu->dev, "Cannot set link state to %s, err %d\n", + (req->enable) ? "enable" : "disable", err); + + return err; +} + +int rvu_mbox_handler_cgx_set_phy_mod_type(struct rvu *rvu, + struct cgx_phy_mod_type *req, + struct msg_rsp *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u8 cgx_id, lmac_id; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + return cgx_set_phy_mod_type(req->mod, rvu_cgx_pdata(cgx_id, rvu), + lmac_id); +} + +int rvu_mbox_handler_cgx_get_phy_mod_type(struct rvu *rvu, struct msg_req *req, + struct cgx_phy_mod_type *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u8 cgx_id, lmac_id; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + rsp->mod = cgx_get_phy_mod_type(rvu_cgx_pdata(cgx_id, rvu), lmac_id); + if (rsp->mod < 0) + return rsp->mod; + return 0; +} + +int rvu_mbox_handler_cgx_get_phy_fec_stats(struct rvu *rvu, struct msg_req *req, + struct msg_rsp *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u8 cgx_id, lmac_id; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + return cgx_get_phy_fec_stats(rvu_cgx_pdata(cgx_id, rvu), lmac_id); +} + +/* Finds cumulative status of NIX rx/tx counters from LF of a PF and those + * from its VFs as well. ie. NIX rx/tx counters at the CGX port level + */ +int rvu_cgx_nix_cuml_stats(struct rvu *rvu, void *cgxd, int lmac_id, + int index, int rxtxflag, u64 *stat) +{ + struct rvu_block *block; + int blkaddr; + u16 pcifunc; + int pf, lf; + + if (!cgxd || !rvu) + return -EINVAL; + + pf = cgxlmac_to_pf(rvu, cgx_get_cgxid(cgxd), lmac_id); + if (pf < 0) + return pf; + + /* Assumes LF of a PF and all of its VF belongs to the same + * NIX block + */ + pcifunc = pf << RVU_PFVF_PF_SHIFT; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); + if (blkaddr < 0) + return 0; + block = &rvu->hw->block[blkaddr]; + + *stat = 0; + for (lf = 0; lf < block->lf.max; lf++) { + /* Check if a lf is attached to this PF or one of its VFs */ + if (!((block->fn_map[lf] & ~RVU_PFVF_FUNC_MASK) == (pcifunc & + ~RVU_PFVF_FUNC_MASK))) + continue; + if (rxtxflag == NIX_STATS_RX) + *stat += rvu_read64(rvu, blkaddr, + NIX_AF_LFX_RX_STATX(lf, index)); + else + *stat += rvu_read64(rvu, blkaddr, + NIX_AF_LFX_TX_STATX(lf, index)); + } + + return 0; +} + +int rvu_mbox_handler_cgx_set_link_mode(struct rvu *rvu, + struct cgx_set_link_mode_req *req, + struct cgx_set_link_mode_rsp *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u8 cgx_idx, lmac; + void *cgxd; + + if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) + return -EPERM; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_idx, &lmac); + + cgxd = rvu_cgx_pdata(cgx_idx, rvu); + + rsp->status = cgx_set_link_mode(cgxd, req->args, cgx_idx, lmac); + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c new file mode 100644 index 000000000000..f65af65efa66 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Admin Function driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/pci.h> +#include "rvu_struct.h" +#include "rvu_reg.h" +#include "mbox.h" +#include "rvu.h" + +/* CPT PF device id */ +#define PCI_DEVID_OTX2_CPT_PF 0xA0FD + +/* Maximum supported microcode groups */ +#define CPT_MAX_ENGINE_GROUPS 8 + +/* Invalid engine group */ +#define INVALID_ENGINE_GRP 0xFF + +/* Number of engine group for symmetric crypto */ +static int crypto_eng_grp = INVALID_ENGINE_GRP; + +/* CPT PF number */ +static int cpt_pf_num = -1; + +/* Fault interrupts names */ +static const char *cpt_flt_irq_name[2] = { "CPTAF FLT0", "CPTAF FLT1" }; + +static irqreturn_t rvu_cpt_af_flr_intr_handler(int irq, void *ptr) +{ + struct rvu *rvu = (struct rvu *) ptr; + u64 reg0, reg1; + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, 0); + if (blkaddr < 0) + return IRQ_NONE; + + reg0 = rvu_read64(rvu, blkaddr, CPT_AF_FLTX_INT(0)); + reg1 = rvu_read64(rvu, blkaddr, CPT_AF_FLTX_INT(1)); + dev_err(rvu->dev, "Received CPTAF FLT irq : 0x%llx, 0x%llx", + reg0, reg1); + + rvu_write64(rvu, blkaddr, CPT_AF_FLTX_INT(0), reg0); + rvu_write64(rvu, blkaddr, CPT_AF_FLTX_INT(1), reg1); + return IRQ_HANDLED; +} + +static irqreturn_t rvu_cpt_af_rvu_intr_handler(int irq, void *ptr) +{ + struct rvu *rvu = (struct rvu *) ptr; + int blkaddr; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, 0); + if (blkaddr < 0) + return IRQ_NONE; + + reg = rvu_read64(rvu, blkaddr, CPT_AF_RVU_INT); + dev_err(rvu->dev, "Received CPTAF RVU irq : 0x%llx", reg); + + rvu_write64(rvu, blkaddr, CPT_AF_RVU_INT, reg); + return IRQ_HANDLED; +} + +static irqreturn_t rvu_cpt_af_ras_intr_handler(int irq, void *ptr) +{ + struct rvu *rvu = (struct rvu *) ptr; + int blkaddr; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, 0); + if (blkaddr < 0) + return IRQ_NONE; + + reg = rvu_read64(rvu, blkaddr, CPT_AF_RAS_INT); + dev_err(rvu->dev, "Received CPTAF RAS irq : 0x%llx", reg); + + rvu_write64(rvu, blkaddr, CPT_AF_RAS_INT, reg); + return IRQ_HANDLED; +} + +static int rvu_cpt_do_register_interrupt(struct rvu *rvu, int irq_offs, + irq_handler_t handler, + const char *name) +{ + int ret = 0; + + ret = request_irq(pci_irq_vector(rvu->pdev, irq_offs), handler, 0, + name, rvu); + if (ret) { + dev_err(rvu->dev, "RVUAF: %s irq registration failed", name); + goto err; + } + + WARN_ON(rvu->irq_allocated[irq_offs]); + rvu->irq_allocated[irq_offs] = true; +err: + return ret; +} + +void rvu_cpt_unregister_interrupts(struct rvu *rvu) +{ + int blkaddr, i, offs; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, 0); + if (blkaddr < 0) + return; + + offs = rvu_read64(rvu, blkaddr, CPT_PRIV_AF_INT_CFG) & 0x7FF; + if (!offs) { + dev_warn(rvu->dev, + "Failed to get CPT_AF_INT vector offsets\n"); + return; + } + + /* Disable all CPT AF interrupts */ + for (i = 0; i < 2; i++) + rvu_write64(rvu, blkaddr, CPT_AF_FLTX_INT_ENA_W1C(i), 0x1); + rvu_write64(rvu, blkaddr, CPT_AF_RVU_INT_ENA_W1C, 0x1); + rvu_write64(rvu, blkaddr, CPT_AF_RAS_INT_ENA_W1C, 0x1); + + for (i = 0; i < CPT_AF_INT_VEC_CNT; i++) + if (rvu->irq_allocated[offs + i]) { + free_irq(pci_irq_vector(rvu->pdev, offs + i), rvu); + rvu->irq_allocated[offs + i] = false; + } +} + +static bool is_cpt_pf(u16 pcifunc) +{ + if (rvu_get_pf(pcifunc) != cpt_pf_num) + return false; + if (pcifunc & RVU_PFVF_FUNC_MASK) + return false; + + return true; +} + +static bool is_cpt_vf(u16 pcifunc) +{ + if (rvu_get_pf(pcifunc) != cpt_pf_num) + return false; + if (!(pcifunc & RVU_PFVF_FUNC_MASK)) + return false; + + return true; +} + +int rvu_cpt_init(struct rvu *rvu) +{ + struct pci_dev *pdev; + int i; + + for (i = 0; i < rvu->hw->total_pfs; i++) { + pdev = pci_get_domain_bus_and_slot( + pci_domain_nr(rvu->pdev->bus), i + 1, 0); + if (!pdev) + continue; + + if (pdev->device == PCI_DEVID_OTX2_CPT_PF) { + cpt_pf_num = i; + put_device(&pdev->dev); + break; + } + + put_device(&pdev->dev); + } + + return 0; +} + +int rvu_cpt_register_interrupts(struct rvu *rvu) +{ + + int i, offs, blkaddr, ret = 0; + + if (!is_block_implemented(rvu->hw, BLKADDR_CPT0)) + return 0; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, 0); + if (blkaddr < 0) + return blkaddr; + + offs = rvu_read64(rvu, blkaddr, CPT_PRIV_AF_INT_CFG) & 0x7FF; + if (!offs) { + dev_warn(rvu->dev, + "Failed to get CPT_AF_INT vector offsets\n"); + return 0; + } + + for (i = CPT_AF_INT_VEC_FLT0; i < CPT_AF_INT_VEC_RVU; i++) { + ret = rvu_cpt_do_register_interrupt(rvu, offs + i, + rvu_cpt_af_flr_intr_handler, + cpt_flt_irq_name[i]); + if (ret) + goto err; + rvu_write64(rvu, blkaddr, CPT_AF_FLTX_INT_ENA_W1S(i), 0x1); + } + + ret = rvu_cpt_do_register_interrupt(rvu, offs + CPT_AF_INT_VEC_RVU, + rvu_cpt_af_rvu_intr_handler, + "CPTAF RVU"); + if (ret) + goto err; + rvu_write64(rvu, blkaddr, CPT_AF_RVU_INT_ENA_W1S, 0x1); + + ret = rvu_cpt_do_register_interrupt(rvu, offs + CPT_AF_INT_VEC_RAS, + rvu_cpt_af_ras_intr_handler, + "CPTAF RAS"); + if (ret) + goto err; + rvu_write64(rvu, blkaddr, CPT_AF_RAS_INT_ENA_W1S, 0x1); + + return 0; +err: + rvu_cpt_unregister_interrupts(rvu); + return ret; +} + +int rvu_mbox_handler_cpt_lf_alloc(struct rvu *rvu, + struct cpt_lf_alloc_req_msg *req, + struct cpt_lf_alloc_rsp_msg *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + int num_lfs, slot, grp_mask; + struct rvu_block *block; + int cptlf, blkaddr; + u64 val; + + if (crypto_eng_grp == INVALID_ENGINE_GRP) + return CPT_AF_ERR_GRP_INVALID; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, pcifunc); + if (blkaddr < 0) + return blkaddr; + + block = &rvu->hw->block[blkaddr]; + num_lfs = rvu_get_rsrc_mapcount(rvu_get_pfvf(rvu, pcifunc), + block->type); + if (!num_lfs) + return CPT_AF_ERR_LF_INVALID; + + /* Check if requested 'CPTLF <=> NIXLF' mapping is valid */ + if (req->nix_pf_func) { + /* If default, use 'this' CPTLF's PFFUNC */ + if (req->nix_pf_func == RVU_DEFAULT_PF_FUNC) + req->nix_pf_func = pcifunc; + if (!is_pffunc_map_valid(rvu, req->nix_pf_func, BLKTYPE_NIX)) + return CPT_AF_ERR_NIX_PF_FUNC_INVALID; + } + + /* Check if requested 'CPTLF <=> SSOLF' mapping is valid */ + if (req->sso_pf_func) { + /* If default, use 'this' CPTLF's PFFUNC */ + if (req->sso_pf_func == RVU_DEFAULT_PF_FUNC) + req->sso_pf_func = pcifunc; + if (!is_pffunc_map_valid(rvu, req->sso_pf_func, BLKTYPE_SSO)) + return CPT_AF_ERR_SSO_PF_FUNC_INVALID; + } + + for (slot = 0; slot < num_lfs; slot++) { + cptlf = rvu_get_lf(rvu, block, pcifunc, slot); + if (cptlf < 0) + return CPT_AF_ERR_LF_INVALID; + + /* Set CPT LF group and priority */ + grp_mask = 1 << crypto_eng_grp; + val = (u64) grp_mask << 48 | 1; + rvu_write64(rvu, blkaddr, CPT_AF_LFX_CTL(cptlf), val); + + /* Set CPT LF NIX_PF_FUNC and SSO_PF_FUNC */ + val = (u64) req->nix_pf_func << 48 | + (u64) req->sso_pf_func << 32; + rvu_write64(rvu, blkaddr, CPT_AF_LFX_CTL2(cptlf), val); + } + + /* Set SSO_PF_FUNC_OVRD for inline IPSec */ + rvu_write64(rvu, blkaddr, CPT_AF_ECO, 0x1); + + rsp->crypto_eng_grp = crypto_eng_grp; + return 0; +} + +int rvu_mbox_handler_cpt_lf_free(struct rvu *rvu, struct msg_req *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + struct rvu_block *block; + int cptlf, blkaddr; + int num_lfs, slot; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, pcifunc); + if (blkaddr < 0) + return blkaddr; + + block = &rvu->hw->block[blkaddr]; + num_lfs = rvu_get_rsrc_mapcount(rvu_get_pfvf(rvu, pcifunc), + block->type); + if (!num_lfs) + return CPT_AF_ERR_LF_INVALID; + + for (slot = 0; slot < num_lfs; slot++) { + cptlf = rvu_get_lf(rvu, block, pcifunc, slot); + if (cptlf < 0) + return CPT_AF_ERR_LF_INVALID; + + /* Reset CPT LF group and priority */ + rvu_write64(rvu, blkaddr, CPT_AF_LFX_CTL(cptlf), 0x0); + /* Reset CPT LF NIX_PF_FUNC and SSO_PF_FUNC */ + rvu_write64(rvu, blkaddr, CPT_AF_LFX_CTL2(cptlf), 0x0); + } + + return 0; +} + +static int cpt_inline_ipsec_cfg_inbound(struct rvu *rvu, int blkaddr, u8 cptlf, + u8 enable, u16 sso_pf_func, + u16 nix_pf_func) +{ + u64 val; + + val = rvu_read64(rvu, blkaddr, CPT_AF_LFX_CTL(cptlf)); + if (enable && (val & BIT_ULL(16))) { + /* IPSec inline outbound path is already enabled for a given + * CPT LF, HRM states that inline inbound & outbound paths + * must not be enabled at the same time for a given CPT LF + */ + return CPT_AF_ERR_INLINE_IPSEC_INB_ENA; + } + /* Check if requested 'CPTLF <=> SSOLF' mapping is valid */ + if (sso_pf_func && !is_pffunc_map_valid(rvu, sso_pf_func, BLKTYPE_SSO)) + return CPT_AF_ERR_SSO_PF_FUNC_INVALID; + + /* Set PF_FUNC_INST */ + if (enable) + val |= BIT_ULL(9); + else + val &= ~BIT_ULL(9); + rvu_write64(rvu, blkaddr, CPT_AF_LFX_CTL(cptlf), val); + + if (sso_pf_func) { + /* Set SSO_PF_FUNC */ + val = rvu_read64(rvu, blkaddr, CPT_AF_LFX_CTL2(cptlf)); + val |= (u64)sso_pf_func << 32; + val |= (u64)nix_pf_func << 48; + rvu_write64(rvu, blkaddr, CPT_AF_LFX_CTL2(cptlf), val); + } + + return 0; +} + +static int cpt_inline_ipsec_cfg_outbound(struct rvu *rvu, int blkaddr, + u8 cptlf, u8 enable, + u16 nix_pf_func) +{ + u64 val; + + val = rvu_read64(rvu, blkaddr, CPT_AF_LFX_CTL(cptlf)); + if (enable && (val & BIT_ULL(9))) { + /* IPSec inline inbound path is already enabled for a given + * CPT LF, HRM states that inline inbound & outbound paths + * must not be enabled at the same time for a given CPT LF + */ + return CPT_AF_ERR_INLINE_IPSEC_OUT_ENA; + } + + /* Check if requested 'CPTLF <=> NIXLF' mapping is valid */ + if (nix_pf_func && !is_pffunc_map_valid(rvu, nix_pf_func, BLKTYPE_NIX)) + return CPT_AF_ERR_NIX_PF_FUNC_INVALID; + + /* Set PF_FUNC_INST */ + if (enable) + val |= BIT_ULL(16); + else + val &= ~BIT_ULL(16); + rvu_write64(rvu, blkaddr, CPT_AF_LFX_CTL(cptlf), val); + + if (nix_pf_func) { + /* Set NIX_PF_FUNC */ + val = rvu_read64(rvu, blkaddr, CPT_AF_LFX_CTL2(cptlf)); + val |= (u64)nix_pf_func << 48; + rvu_write64(rvu, blkaddr, CPT_AF_LFX_CTL2(cptlf), val); + } + + return 0; +} + +int rvu_mbox_handler_cpt_inline_ipsec_cfg(struct rvu *rvu, + struct cpt_inline_ipsec_cfg_msg *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + struct rvu_block *block; + int cptlf, blkaddr; + int num_lfs, ret; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, pcifunc); + if (blkaddr < 0) + return CPT_AF_ERR_LF_INVALID; + + block = &rvu->hw->block[blkaddr]; + num_lfs = rvu_get_rsrc_mapcount(rvu_get_pfvf(rvu, pcifunc), + block->type); + if (req->slot >= num_lfs) + return CPT_AF_ERR_LF_INVALID; + + cptlf = rvu_get_lf(rvu, block, pcifunc, req->slot); + if (cptlf < 0) + return CPT_AF_ERR_LF_INVALID; + + switch (req->dir) { + case CPT_INLINE_INBOUND: + ret = cpt_inline_ipsec_cfg_inbound(rvu, blkaddr, cptlf, + req->enable, + req->sso_pf_func, + req->nix_pf_func); + break; + + case CPT_INLINE_OUTBOUND: + ret = cpt_inline_ipsec_cfg_outbound(rvu, blkaddr, cptlf, + req->enable, + req->nix_pf_func); + break; + + default: + + return CPT_AF_ERR_PARAM; + } + + return ret; +} + +int rvu_mbox_handler_cpt_set_crypto_grp(struct rvu *rvu, + struct cpt_set_crypto_grp_req_msg *req, + struct cpt_set_crypto_grp_req_msg *rsp) +{ + /* This message is accepted only if sent from CPT PF */ + if (!is_cpt_pf(req->hdr.pcifunc)) + return CPT_AF_ERR_ACCESS_DENIED; + + rsp->crypto_eng_grp = req->crypto_eng_grp; + + if (req->crypto_eng_grp != INVALID_ENGINE_GRP && + req->crypto_eng_grp >= CPT_MAX_ENGINE_GROUPS) + return CPT_AF_ERR_GRP_INVALID; + + crypto_eng_grp = req->crypto_eng_grp; + return 0; +} + +int rvu_mbox_handler_cpt_rd_wr_register(struct rvu *rvu, + struct cpt_rd_wr_reg_msg *req, + struct cpt_rd_wr_reg_msg *rsp) +{ + int blkaddr, num_lfs, offs, lf; + struct rvu_block *block; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, 0); + if (blkaddr < 0) + return blkaddr; + + /* This message is accepted only if sent from CPT PF/VF */ + if (!is_cpt_pf(req->hdr.pcifunc) && + !is_cpt_vf(req->hdr.pcifunc)) + return CPT_AF_ERR_ACCESS_DENIED; + + rsp->reg_offset = req->reg_offset; + rsp->ret_val = req->ret_val; + rsp->is_write = req->is_write; + + if (req->hdr.pcifunc & RVU_PFVF_FUNC_MASK) { + /* Registers that can be accessed from VF */ + switch (req->reg_offset & 0xFF000) { + case CPT_AF_LFX_CTL(0): + offs = req->reg_offset & 0xFFF; + if (offs % 8) + goto error; + lf = offs >> 3; + break; + + default: + goto error; + } + + block = &rvu->hw->block[blkaddr]; + num_lfs = rvu_get_rsrc_mapcount( + rvu_get_pfvf(rvu, req->hdr.pcifunc), + block->type); + if (lf >= num_lfs) + /* Slot is not valid for that VF */ + goto error; + + /* Need to translate CPT LF slot to global number because + * VFs use local numbering from 0 to number of LFs - 1 + */ + lf = rvu_get_lf(rvu, &rvu->hw->block[blkaddr], + req->hdr.pcifunc, lf); + if (lf < 0) + goto error; + + req->reg_offset &= 0xFF000; + req->reg_offset += lf << 3; + rsp->reg_offset = req->reg_offset; + } else { + /* Registers that can be accessed from PF */ + switch (req->reg_offset & 0xFF000) { + case CPT_AF_PF_FUNC: + case CPT_AF_BLK_RST: + case CPT_AF_CONSTANTS1: + if (req->reg_offset & 0xFFF) + goto error; + break; + + case CPT_AF_EXEX_STS(0): + case CPT_AF_EXEX_CTL(0): + case CPT_AF_EXEX_CTL2(0): + case CPT_AF_EXEX_UCODE_BASE(0): + offs = req->reg_offset & 0xFFF; + if ((offs % 8) || (offs >> 3) > 127) + goto error; + break; + + default: + goto error; + } + } + + if (req->is_write) + rvu_write64(rvu, blkaddr, req->reg_offset, req->val); + else + rsp->val = rvu_read64(rvu, blkaddr, req->reg_offset); + + return 0; +error: + /* Access to register denied */ + return CPT_AF_ERR_ACCESS_DENIED; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c new file mode 100644 index 000000000000..63de2b4c75e1 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -0,0 +1,2943 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Admin Function driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ + +#ifdef CONFIG_DEBUG_FS + +#include <linux/fs.h> +#include <linux/debugfs.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include "rvu_struct.h" +#include "rvu_reg.h" +#include "rvu.h" +#include "cgx.h" +#include "npc.h" + +#define DEBUGFS_DIR_NAME "octeontx2" + +enum { + CGX_STAT0, + CGX_STAT1, + CGX_STAT2, + CGX_STAT3, + CGX_STAT4, + CGX_STAT5, + CGX_STAT6, + CGX_STAT7, + CGX_STAT8, + CGX_STAT9, + CGX_STAT10, + CGX_STAT11, + CGX_STAT12, + CGX_STAT13, + CGX_STAT14, + CGX_STAT15, + CGX_STAT16, + CGX_STAT17, + CGX_STAT18, +}; + +/* NIX TX stats */ +enum nix_stat_lf_tx { + TX_UCAST = 0x0, + TX_BCAST = 0x1, + TX_MCAST = 0x2, + TX_DROP = 0x3, + TX_OCTS = 0x4, + TX_STATS_ENUM_LAST, +}; + +/* NIX RX stats */ +enum nix_stat_lf_rx { + RX_OCTS = 0x0, + RX_UCAST = 0x1, + RX_BCAST = 0x2, + RX_MCAST = 0x3, + RX_DROP = 0x4, + RX_DROP_OCTS = 0x5, + RX_FCS = 0x6, + RX_ERR = 0x7, + RX_DRP_BCAST = 0x8, + RX_DRP_MCAST = 0x9, + RX_DRP_L3BCAST = 0xa, + RX_DRP_L3MCAST = 0xb, + RX_STATS_ENUM_LAST, +}; + +static char *cgx_rx_stats_fields[] = { + [CGX_STAT0] = "Received packets", + [CGX_STAT1] = "Octets of received packets", + [CGX_STAT2] = "Received PAUSE packets", + [CGX_STAT3] = "Received PAUSE and control packets", + [CGX_STAT4] = "Filtered DMAC0 (NIX-bound) packets", + [CGX_STAT5] = "Filtered DMAC0 (NIX-bound) octets", + [CGX_STAT6] = "Packets dropped due to RX FIFO full", + [CGX_STAT7] = "Octets dropped due to RX FIFO full", + [CGX_STAT8] = "Error packets", + [CGX_STAT9] = "Filtered DMAC1 (NCSI-bound) packets", + [CGX_STAT10] = "Filtered DMAC1 (NCSI-bound) octets", + [CGX_STAT11] = "NCSI-bound packets dropped", + [CGX_STAT12] = "NCSI-bound octets dropped", +}; + +static char *cgx_tx_stats_fields[] = { + [CGX_STAT0] = "Packets dropped due to excessive collisions", + [CGX_STAT1] = "Packets dropped due to excessive deferral", + [CGX_STAT2] = "Multiple collisions before successful transmission", + [CGX_STAT3] = "Single collisions before successful transmission", + [CGX_STAT4] = "Total octets sent on the interface", + [CGX_STAT5] = "Total frames sent on the interface", + [CGX_STAT6] = "Packets sent with an octet count < 64", + [CGX_STAT7] = "Packets sent with an octet count == 64", + [CGX_STAT8] = "Packets sent with an octet count of 65–127", + [CGX_STAT9] = "Packets sent with an octet count of 128-255", + [CGX_STAT10] = "Packets sent with an octet count of 256-511", + [CGX_STAT11] = "Packets sent with an octet count of 512-1023", + [CGX_STAT12] = "Packets sent with an octet count of 1024-1518", + [CGX_STAT13] = "Packets sent with an octet count of > 1518", + [CGX_STAT14] = "Packets sent to a broadcast DMAC", + [CGX_STAT15] = "Packets sent to the multicast DMAC", + [CGX_STAT16] = "Transmit underflow and were truncated", + [CGX_STAT17] = "Control/PAUSE packets sent", +}; + +#define NDC_MAX_BANK(rvu, blk_addr) (rvu_read64(rvu, \ + blk_addr, NDC_AF_CONST) & 0xFF) + +#define rvu_dbg_NULL NULL +#define rvu_dbg_open_NULL NULL + +#define RVU_DEBUG_SEQ_FOPS(name, read_op, write_op) \ +static int rvu_dbg_open_##name(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, rvu_dbg_##read_op, inode->i_private); \ +} \ +static const struct file_operations rvu_dbg_##name##_fops = { \ + .owner = THIS_MODULE, \ + .open = rvu_dbg_open_##name, \ + .read = seq_read, \ + .write = rvu_dbg_##write_op, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +#define RVU_DEBUG_FOPS(name, read_op, write_op) \ +static const struct file_operations rvu_dbg_##name##_fops = { \ + .owner = THIS_MODULE, \ + .open = simple_open, \ + .read = rvu_dbg_##read_op, \ + .write = rvu_dbg_##write_op \ +} + +static void print_nix_qsize(struct seq_file *filp, struct rvu_pfvf *pfvf); + +/* Dumps current provisioning status of all RVU block LFs */ +static ssize_t rvu_dbg_rsrc_attach_status(struct file *filp, + char __user *buffer, + size_t count, loff_t *ppos) +{ + int index, off = 0, flag = 0, go_back = 0, off_prev; + struct rvu *rvu = filp->private_data; + int lf, pf, vf, pcifunc; + struct rvu_block block; + int bytes_not_copied; + int buf_size = 2048; + char *buf; + + /* don't allow partial reads */ + if (*ppos != 0) + return 0; + + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOSPC; + off += scnprintf(&buf[off], buf_size - 1 - off, "\npcifunc\t\t"); + for (index = 0; index < BLK_COUNT; index++) + if (strlen(rvu->hw->block[index].name)) + off += scnprintf(&buf[off], buf_size - 1 - off, + "%*s\t", (index - 1) * 2, + rvu->hw->block[index].name); + off += scnprintf(&buf[off], buf_size - 1 - off, "\n"); + for (pf = 0; pf < rvu->hw->total_pfs; pf++) { + for (vf = 0; vf <= rvu->hw->total_vfs; vf++) { + pcifunc = pf << 10 | vf; + if (!pcifunc) + continue; + + if (vf) { + go_back = scnprintf(&buf[off], + buf_size - 1 - off, + "PF%d:VF%d\t\t", pf, + vf - 1); + } else { + go_back = scnprintf(&buf[off], + buf_size - 1 - off, + "PF%d\t\t", pf); + } + + off += go_back; + for (index = 0; index < BLKTYPE_MAX; index++) { + block = rvu->hw->block[index]; + if (!strlen(block.name)) + continue; + off_prev = off; + for (lf = 0; lf < block.lf.max; lf++) { + if (block.fn_map[lf] != pcifunc) + continue; + flag = 1; + off += scnprintf(&buf[off], buf_size - 1 + - off, "%3d,", lf); + } + if (flag && off_prev != off) + off--; + else + go_back++; + off += scnprintf(&buf[off], buf_size - 1 - off, + "\t"); + } + if (!flag) + off -= go_back; + else + flag = 0; + off--; + off += scnprintf(&buf[off], buf_size - 1 - off, "\n"); + } + } + + bytes_not_copied = copy_to_user(buffer, buf, off); + kfree(buf); + + if (bytes_not_copied) + return -EFAULT; + + *ppos = off; + return off; +} + +RVU_DEBUG_FOPS(rsrc_status, rsrc_attach_status, NULL); + +static bool rvu_dbg_is_valid_lf(struct rvu *rvu, int blktype, int lf, + u16 *pcifunc) +{ + struct rvu_block *block; + struct rvu_hwinfo *hw; + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, blktype, 0); + if (blkaddr < 0) { + dev_warn(rvu->dev, "Invalid blktype\n"); + return false; + } + + hw = rvu->hw; + block = &hw->block[blkaddr]; + + if (lf < 0 || lf >= block->lf.max) { + dev_warn(rvu->dev, "Invalid LF: valid range: 0-%d\n", + block->lf.max - 1); + return false; + } + + *pcifunc = block->fn_map[lf]; + if (!*pcifunc) { + dev_warn(rvu->dev, + "This LF is not attached to any RVU PFFUNC\n"); + return false; + } + return true; +} + +static void print_npa_qsize(struct seq_file *m, struct rvu_pfvf *pfvf) +{ + char *buf; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return; + + if (!pfvf->aura_ctx) { + seq_puts(m, "Aura context is not initialized\n"); + } else { + bitmap_print_to_pagebuf(false, buf, pfvf->aura_bmap, + pfvf->aura_ctx->qsize); + seq_printf(m, "Aura count : %d\n", pfvf->aura_ctx->qsize); + seq_printf(m, "Aura context ena/dis bitmap : %s\n", buf); + } + + if (!pfvf->pool_ctx) { + seq_puts(m, "Pool context is not initialized\n"); + } else { + bitmap_print_to_pagebuf(false, buf, pfvf->pool_bmap, + pfvf->pool_ctx->qsize); + seq_printf(m, "Pool count : %d\n", pfvf->pool_ctx->qsize); + seq_printf(m, "Pool context ena/dis bitmap : %s\n", buf); + } + kfree(buf); +} + +/* The 'qsize' entry dumps current Aura/Pool context Qsize + * and each context's current enable/disable status in a bitmap. + */ +static int rvu_dbg_qsize_display(struct seq_file *filp, void *unsused, + int blktype) +{ + void (*print_qsize)(struct seq_file *filp, + struct rvu_pfvf *pfvf) = NULL; + struct rvu_pfvf *pfvf; + struct rvu *rvu; + int qsize_id; + u16 pcifunc; + + rvu = filp->private; + switch (blktype) { + case BLKTYPE_NPA: + qsize_id = rvu->rvu_dbg.npa_qsize_id; + print_qsize = print_npa_qsize; + break; + + case BLKTYPE_NIX: + qsize_id = rvu->rvu_dbg.nix_qsize_id; + print_qsize = print_nix_qsize; + break; + + default: + return -EINVAL; + } + + if (!rvu_dbg_is_valid_lf(rvu, blktype, qsize_id, &pcifunc)) + return -EINVAL; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + print_qsize(filp, pfvf); + + return 0; +} + +static ssize_t rvu_dbg_qsize_write(struct file *filp, + const char __user *buffer, size_t count, + loff_t *ppos, int blktype) +{ + char *blk_string = (blktype == BLKTYPE_NPA) ? "npa" : "nix"; + struct seq_file *seqfile = filp->private_data; + char *cmd_buf, *cmd_buf_tmp, *subtoken; + struct rvu *rvu = seqfile->private; + u16 pcifunc; + int ret, lf; + + cmd_buf = memdup_user(buffer, count); + if (IS_ERR(cmd_buf)) + return -ENOMEM; + + cmd_buf[count] = '\0'; + + cmd_buf_tmp = strchr(cmd_buf, '\n'); + if (cmd_buf_tmp) { + *cmd_buf_tmp = '\0'; + count = cmd_buf_tmp - cmd_buf + 1; + } + + cmd_buf_tmp = cmd_buf; + subtoken = strsep(&cmd_buf, " "); + ret = subtoken ? kstrtoint(subtoken, 10, &lf) : -EINVAL; + if (cmd_buf) + ret = -EINVAL; + + if (!strncmp(subtoken, "help", 4) || ret < 0) { + dev_info(rvu->dev, "Use echo <%s-lf > qsize\n", blk_string); + goto qsize_write_done; + } + + if (!rvu_dbg_is_valid_lf(rvu, blktype, lf, &pcifunc)) { + ret = -EINVAL; + goto qsize_write_done; + } + if (blktype == BLKTYPE_NPA) + rvu->rvu_dbg.npa_qsize_id = lf; + else + rvu->rvu_dbg.nix_qsize_id = lf; + +qsize_write_done: + kfree(cmd_buf_tmp); + return ret ? ret : count; +} + +static ssize_t rvu_dbg_npa_qsize_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_qsize_write(filp, buffer, count, ppos, + BLKTYPE_NPA); +} + +static int rvu_dbg_npa_qsize_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_qsize_display(filp, unused, BLKTYPE_NPA); +} + +RVU_DEBUG_SEQ_FOPS(npa_qsize, npa_qsize_display, npa_qsize_write); + +/* Dumps given NPA Aura's context */ +static void print_npa_aura_ctx(struct seq_file *m, struct npa_aq_enq_rsp *rsp) +{ + struct npa_aura_s *aura = &rsp->aura; + + seq_printf(m, "W0: Pool addr\t\t%llx\n", aura->pool_addr); + + seq_printf(m, "W1: ena\t\t\t%d\nW1: pool caching\t%d\n", + aura->ena, aura->pool_caching); + seq_printf(m, "W1: pool way mask\t%d\nW1: avg con\t\t%d\n", + aura->pool_way_mask, aura->avg_con); + seq_printf(m, "W1: pool drop ena\t%d\nW1: aura drop ena\t%d\n", + aura->pool_drop_ena, aura->aura_drop_ena); + seq_printf(m, "W1: bp_ena\t\t%d\nW1: aura drop\t\t%d\n", + aura->bp_ena, aura->aura_drop); + seq_printf(m, "W1: aura shift\t\t%d\nW1: avg_level\t\t%d\n", + aura->shift, aura->avg_level); + + seq_printf(m, "W2: count\t\t%llu\nW2: nix0_bpid\t\t%d\nW2: nix1_bpid\t\t%d\n", + (u64)aura->count, aura->nix0_bpid, aura->nix1_bpid); + + seq_printf(m, "W3: limit\t\t%llu\nW3: bp\t\t\t%d\nW3: fc_ena\t\t%d\n", + (u64)aura->limit, aura->bp, aura->fc_ena); + seq_printf(m, "W3: fc_up_crossing\t%d\nW3: fc_stype\t\t%d\n", + aura->fc_up_crossing, aura->fc_stype); + seq_printf(m, "W3: fc_hyst_bits\t%d\n", aura->fc_hyst_bits); + + seq_printf(m, "W4: fc_addr\t\t%llx\n", aura->fc_addr); + + seq_printf(m, "W5: pool_drop\t\t%d\nW5: update_time\t\t%d\n", + aura->pool_drop, aura->update_time); + seq_printf(m, "W5: err_int \t\t%d\nW5: err_int_ena\t\t%d\n", + aura->err_int, aura->err_int_ena); + seq_printf(m, "W5: thresh_int\t\t%d\nW5: thresh_int_ena \t%d\n", + aura->thresh_int, aura->thresh_int_ena); + seq_printf(m, "W5: thresh_up\t\t%d\nW5: thresh_qint_idx\t%d\n", + aura->thresh_up, aura->thresh_qint_idx); + seq_printf(m, "W5: err_qint_idx \t%d\n", aura->err_qint_idx); + + seq_printf(m, "W6: thresh\t\t%llu\n", (u64)aura->thresh); +} + +/* Dumps given NPA Pool's context */ +static void print_npa_pool_ctx(struct seq_file *m, struct npa_aq_enq_rsp *rsp) +{ + struct npa_pool_s *pool = &rsp->pool; + + seq_printf(m, "W0: Stack base\t\t%llx\n", pool->stack_base); + + seq_printf(m, "W1: ena \t\t%d\nW1: nat_align \t\t%d\n", + pool->ena, pool->nat_align); + seq_printf(m, "W1: stack_caching\t%d\nW1: stack_way_mask\t%d\n", + pool->stack_caching, pool->stack_way_mask); + seq_printf(m, "W1: buf_offset\t\t%d\nW1: buf_size\t\t%d\n", + pool->buf_offset, pool->buf_size); + + seq_printf(m, "W2: stack_max_pages \t%d\nW2: stack_pages\t\t%d\n", + pool->stack_max_pages, pool->stack_pages); + + seq_printf(m, "W3: op_pc \t\t%llu\n", (u64)pool->op_pc); + + seq_printf(m, "W4: stack_offset\t%d\nW4: shift\t\t%d\nW4: avg_level\t\t%d\n", + pool->stack_offset, pool->shift, pool->avg_level); + seq_printf(m, "W4: avg_con \t\t%d\nW4: fc_ena\t\t%d\nW4: fc_stype\t\t%d\n", + pool->avg_con, pool->fc_ena, pool->fc_stype); + seq_printf(m, "W4: fc_hyst_bits\t%d\nW4: fc_up_crossing\t%d\n", + pool->fc_hyst_bits, pool->fc_up_crossing); + seq_printf(m, "W4: update_time\t\t%d\n", pool->update_time); + + seq_printf(m, "W5: fc_addr\t\t%llx\n", pool->fc_addr); + + seq_printf(m, "W6: ptr_start\t\t%llx\n", pool->ptr_start); + + seq_printf(m, "W7: ptr_end\t\t%llx\n", pool->ptr_end); + + seq_printf(m, "W8: err_int\t\t%d\nW8: err_int_ena\t\t%d\n", + pool->err_int, pool->err_int_ena); + seq_printf(m, "W8: thresh_int\t\t%d\n", pool->thresh_int); + seq_printf(m, "W8: thresh_int_ena\t%d\nW8: thresh_up\t\t%d\n", + pool->thresh_int_ena, pool->thresh_up); + seq_printf(m, "W8: thresh_qint_idx\t%d\nW8: err_qint_idx\t\t%d\n", + pool->thresh_qint_idx, pool->err_qint_idx); +} + +/* Reads aura/pool's ctx from admin queue */ +static int rvu_dbg_npa_ctx_display(struct seq_file *m, void *unused, int ctype) +{ + void (*print_npa_ctx)(struct seq_file *m, struct npa_aq_enq_rsp *rsp); + struct npa_aq_enq_req aq_req; + struct npa_aq_enq_rsp rsp; + struct rvu_pfvf *pfvf; + int aura, rc, max_id; + int npalf, id, all; + struct rvu *rvu; + u16 pcifunc; + + rvu = m->private; + + switch (ctype) { + case NPA_AQ_CTYPE_AURA: + npalf = rvu->rvu_dbg.npa_aura_ctx.lf; + id = rvu->rvu_dbg.npa_aura_ctx.id; + all = rvu->rvu_dbg.npa_aura_ctx.all; + break; + + case NPA_AQ_CTYPE_POOL: + npalf = rvu->rvu_dbg.npa_pool_ctx.lf; + id = rvu->rvu_dbg.npa_pool_ctx.id; + all = rvu->rvu_dbg.npa_pool_ctx.all; + break; + default: + return -EINVAL; + } + + if (!rvu_dbg_is_valid_lf(rvu, BLKTYPE_NPA, npalf, &pcifunc)) + return -EINVAL; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + if (ctype == NPA_AQ_CTYPE_AURA && !pfvf->aura_ctx) { + seq_puts(m, "Aura context is not initialized\n"); + return -EINVAL; + } else if (ctype == NPA_AQ_CTYPE_POOL && !pfvf->pool_ctx) { + seq_puts(m, "Pool context is not initialized\n"); + return -EINVAL; + } + + memset(&aq_req, 0, sizeof(struct npa_aq_enq_req)); + aq_req.hdr.pcifunc = pcifunc; + aq_req.ctype = ctype; + aq_req.op = NPA_AQ_INSTOP_READ; + if (ctype == NPA_AQ_CTYPE_AURA) { + max_id = pfvf->aura_ctx->qsize; + print_npa_ctx = print_npa_aura_ctx; + } else { + max_id = pfvf->pool_ctx->qsize; + print_npa_ctx = print_npa_pool_ctx; + } + + if (id < 0 || id >= max_id) { + seq_printf(m, "Invalid %s, valid range is 0-%d\n", + (ctype == NPA_AQ_CTYPE_AURA) ? "aura" : "pool", + max_id - 1); + return -EINVAL; + } + + if (all) + id = 0; + else + max_id = id + 1; + + for (aura = id; aura < max_id; aura++) { + aq_req.aura_id = aura; + seq_printf(m, "======%s : %d=======\n", + (ctype == NPA_AQ_CTYPE_AURA) ? "AURA" : "POOL", + aq_req.aura_id); + rc = rvu_npa_aq_enq_inst(rvu, &aq_req, &rsp); + if (rc) { + seq_puts(m, "Failed to read context\n"); + return -EINVAL; + } + print_npa_ctx(m, &rsp); + } + return 0; +} + +static int write_npa_ctx(struct rvu *rvu, bool all, + int npalf, int id, int ctype) +{ + struct rvu_pfvf *pfvf; + int max_id = 0; + u16 pcifunc; + + if (!rvu_dbg_is_valid_lf(rvu, BLKTYPE_NPA, npalf, &pcifunc)) + return -EINVAL; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + + if (ctype == NPA_AQ_CTYPE_AURA) { + if (!pfvf->aura_ctx) { + dev_warn(rvu->dev, "Aura context is not initialized\n"); + return -EINVAL; + } + max_id = pfvf->aura_ctx->qsize; + } else if (ctype == NPA_AQ_CTYPE_POOL) { + if (!pfvf->pool_ctx) { + dev_warn(rvu->dev, "Pool context is not initialized\n"); + return -EINVAL; + } + max_id = pfvf->pool_ctx->qsize; + } + + if (id < 0 || id >= max_id) { + dev_warn(rvu->dev, "Invalid %s, valid range is 0-%d\n", + (ctype == NPA_AQ_CTYPE_AURA) ? "aura" : "pool", + max_id - 1); + return -EINVAL; + } + + switch (ctype) { + case NPA_AQ_CTYPE_AURA: + rvu->rvu_dbg.npa_aura_ctx.lf = npalf; + rvu->rvu_dbg.npa_aura_ctx.id = id; + rvu->rvu_dbg.npa_aura_ctx.all = all; + break; + + case NPA_AQ_CTYPE_POOL: + rvu->rvu_dbg.npa_pool_ctx.lf = npalf; + rvu->rvu_dbg.npa_pool_ctx.id = id; + rvu->rvu_dbg.npa_pool_ctx.all = all; + break; + default: + return -EINVAL; + } + return 0; +} + +static int parse_cmd_buffer_ctx(char *cmd_buf, size_t *count, + const char __user *buffer, int *npalf, + int *id, bool *all) +{ + int bytes_not_copied; + char *cmd_buf_tmp; + char *subtoken; + int ret; + + bytes_not_copied = copy_from_user(cmd_buf, buffer, *count); + if (bytes_not_copied) + return -EFAULT; + + cmd_buf[*count] = '\0'; + cmd_buf_tmp = strchr(cmd_buf, '\n'); + + if (cmd_buf_tmp) { + *cmd_buf_tmp = '\0'; + *count = cmd_buf_tmp - cmd_buf + 1; + } + + subtoken = strsep(&cmd_buf, " "); + ret = subtoken ? kstrtoint(subtoken, 10, npalf) : -EINVAL; + if (ret < 0) + return ret; + subtoken = strsep(&cmd_buf, " "); + if (subtoken && strcmp(subtoken, "all") == 0) { + *all = true; + } else { + ret = subtoken ? kstrtoint(subtoken, 10, id) : -EINVAL; + if (ret < 0) + return ret; + } + if (cmd_buf) + return -EINVAL; + return ret; +} + +static ssize_t rvu_dbg_npa_ctx_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos, int ctype) +{ + char *cmd_buf, *ctype_string = (ctype == NPA_AQ_CTYPE_AURA) ? + "aura" : "pool"; + struct seq_file *seqfp = filp->private_data; + struct rvu *rvu = seqfp->private; + int npalf, id = 0, ret; + bool all = false; + + if ((*ppos != 0) || !count) + return -EINVAL; + + cmd_buf = kzalloc(count + 1, GFP_KERNEL); + if (!cmd_buf) + return count; + ret = parse_cmd_buffer_ctx(cmd_buf, &count, buffer, + &npalf, &id, &all); + if (ret < 0) { + dev_info(rvu->dev, + "Usage: echo <npalf> [%s number/all] > %s_ctx\n", + ctype_string, ctype_string); + goto done; + } else { + ret = write_npa_ctx(rvu, all, npalf, id, ctype); + } +done: + kfree(cmd_buf); + return ret ? ret : count; +} + +static ssize_t rvu_dbg_npa_aura_ctx_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_npa_ctx_write(filp, buffer, count, ppos, + NPA_AQ_CTYPE_AURA); +} + +static int rvu_dbg_npa_aura_ctx_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_npa_ctx_display(filp, unused, NPA_AQ_CTYPE_AURA); +} + +RVU_DEBUG_SEQ_FOPS(npa_aura_ctx, npa_aura_ctx_display, npa_aura_ctx_write); + +static ssize_t rvu_dbg_npa_pool_ctx_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_npa_ctx_write(filp, buffer, count, ppos, + NPA_AQ_CTYPE_POOL); +} + +static int rvu_dbg_npa_pool_ctx_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_npa_ctx_display(filp, unused, NPA_AQ_CTYPE_POOL); +} + +RVU_DEBUG_SEQ_FOPS(npa_pool_ctx, npa_pool_ctx_display, npa_pool_ctx_write); + +static void ndc_cache_stats(struct seq_file *s, int blk_addr, + int ctype, int transaction) +{ + u64 req, out_req, lat, cant_alloc; + struct rvu *rvu = s->private; + int port; + + for (port = 0; port < NDC_MAX_PORT; port++) { + req = rvu_read64(rvu, blk_addr, NDC_AF_PORTX_RTX_RWX_REQ_PC + (port, ctype, transaction)); + lat = rvu_read64(rvu, blk_addr, NDC_AF_PORTX_RTX_RWX_LAT_PC + (port, ctype, transaction)); + out_req = rvu_read64(rvu, blk_addr, + NDC_AF_PORTX_RTX_RWX_OSTDN_PC + (port, ctype, transaction)); + cant_alloc = rvu_read64(rvu, blk_addr, + NDC_AF_PORTX_RTX_CANT_ALLOC_PC + (port, transaction)); + seq_printf(s, "\nPort:%d\n", port); + seq_printf(s, "\tTotal Requests:\t\t%lld\n", req); + seq_printf(s, "\tTotal Time Taken:\t%lld cycles\n", lat); + seq_printf(s, "\tAvg Latency:\t\t%lld cycles\n", lat / req); + seq_printf(s, "\tOutstanding Requests:\t%lld\n", out_req); + seq_printf(s, "\tCant Alloc Requests:\t%lld\n", cant_alloc); + } +} + +static int ndc_blk_cache_stats(struct seq_file *s, int idx, int blk_addr) +{ + seq_puts(s, "\n***** CACHE mode read stats *****\n"); + ndc_cache_stats(s, blk_addr, CACHING, NDC_READ_TRANS); + seq_puts(s, "\n***** CACHE mode write stats *****\n"); + ndc_cache_stats(s, blk_addr, CACHING, NDC_WRITE_TRANS); + seq_puts(s, "\n***** BY-PASS mode read stats *****\n"); + ndc_cache_stats(s, blk_addr, BYPASS, NDC_READ_TRANS); + seq_puts(s, "\n***** BY-PASS mode write stats *****\n"); + ndc_cache_stats(s, blk_addr, BYPASS, NDC_WRITE_TRANS); + return 0; +} + +static int rvu_dbg_npa_ndc_cache_display(struct seq_file *filp, void *unused) +{ + return ndc_blk_cache_stats(filp, NPA0_U, BLKADDR_NDC_NPA0); +} + +RVU_DEBUG_SEQ_FOPS(npa_ndc_cache, npa_ndc_cache_display, NULL); + +static int ndc_blk_hits_miss_stats(struct seq_file *s, int idx, int blk_addr) +{ + struct rvu *rvu = s->private; + int bank, max_bank; + + max_bank = NDC_MAX_BANK(rvu, blk_addr); + for (bank = 0; bank < max_bank; bank++) { + seq_printf(s, "BANK:%d\n", bank); + seq_printf(s, "\tHits:\t%lld\n", + (u64)rvu_read64(rvu, blk_addr, + NDC_AF_BANKX_HIT_PC(bank))); + seq_printf(s, "\tMiss:\t%lld\n", + (u64)rvu_read64(rvu, blk_addr, + NDC_AF_BANKX_MISS_PC(bank))); + } + return 0; +} + +static int rvu_dbg_nix_ndc_rx_cache_display(struct seq_file *filp, void *unused) +{ + return ndc_blk_cache_stats(filp, NIX0_RX, + BLKADDR_NDC_NIX0_RX); +} + +RVU_DEBUG_SEQ_FOPS(nix_ndc_rx_cache, nix_ndc_rx_cache_display, NULL); + +static int rvu_dbg_nix_ndc_tx_cache_display(struct seq_file *filp, void *unused) +{ + return ndc_blk_cache_stats(filp, NIX0_TX, + BLKADDR_NDC_NIX0_TX); +} + +RVU_DEBUG_SEQ_FOPS(nix_ndc_tx_cache, nix_ndc_tx_cache_display, NULL); + +static int rvu_dbg_npa_ndc_hits_miss_display(struct seq_file *filp, + void *unused) +{ + return ndc_blk_hits_miss_stats(filp, NPA0_U, BLKADDR_NDC_NPA0); +} + +RVU_DEBUG_SEQ_FOPS(npa_ndc_hits_miss, npa_ndc_hits_miss_display, NULL); + +static int rvu_dbg_nix_ndc_rx_hits_miss_display(struct seq_file *filp, + void *unused) +{ + return ndc_blk_hits_miss_stats(filp, + NPA0_U, BLKADDR_NDC_NIX0_RX); +} + +RVU_DEBUG_SEQ_FOPS(nix_ndc_rx_hits_miss, nix_ndc_rx_hits_miss_display, NULL); + +static int rvu_dbg_nix_ndc_tx_hits_miss_display(struct seq_file *filp, + void *unused) +{ + return ndc_blk_hits_miss_stats(filp, + NPA0_U, BLKADDR_NDC_NIX0_TX); +} + +RVU_DEBUG_SEQ_FOPS(nix_ndc_tx_hits_miss, nix_ndc_tx_hits_miss_display, NULL); + +#define PRINT_CGX_CUML_NIXRX_STATUS(idx, name) \ + ({ \ + u64 cnt; \ + err = rvu_cgx_nix_cuml_stats(rvu, cgxd, lmac_id, (idx), \ + NIX_STATS_RX, &(cnt)); \ + if (!err) \ + seq_printf(s, "%s: %llu\n", name, cnt); \ + cnt; \ + }) + +#define PRINT_CGX_CUML_NIXTX_STATUS(idx, name) \ + ({ \ + u64 cnt; \ + err = rvu_cgx_nix_cuml_stats(rvu, cgxd, lmac_id, (idx), \ + NIX_STATS_TX, &(cnt)); \ + if (!err) \ + seq_printf(s, "%s: %llu\n", name, cnt); \ + cnt; \ + }) + +static int cgx_print_stats(struct seq_file *s, int lmac_id) +{ + struct cgx_link_user_info linfo; + void *cgxd = s->private; + u64 ucast, mcast, bcast; + int stat = 0, err = 0; + u64 tx_stat, rx_stat; + struct rvu *rvu; + + rvu = pci_get_drvdata(pci_get_device(PCI_VENDOR_ID_CAVIUM, + PCI_DEVID_OCTEONTX2_RVU_AF, NULL)); + if (!rvu) + return -ENODEV; + + /* Link status */ + seq_puts(s, "\n=======Link Status======\n\n"); + err = cgx_get_link_info(cgxd, lmac_id, &linfo); + if (err) + seq_puts(s, "Failed to read link status\n"); + seq_printf(s, "\nLink is %s %d Mbps\n\n", + linfo.link_up ? "UP" : "DOWN", linfo.speed); + + /* Rx stats */ + seq_puts(s, "\n=======NIX RX_STATS(CGX port level)======\n\n"); + ucast = PRINT_CGX_CUML_NIXRX_STATUS(RX_UCAST, "rx_ucast_frames"); + if (err) + return err; + mcast = PRINT_CGX_CUML_NIXRX_STATUS(RX_MCAST, "rx_mcast_frames"); + if (err) + return err; + bcast = PRINT_CGX_CUML_NIXRX_STATUS(RX_BCAST, "rx_bcast_frames"); + if (err) + return err; + seq_printf(s, "rx_frames: %llu\n", ucast + mcast + bcast); + PRINT_CGX_CUML_NIXRX_STATUS(RX_OCTS, "rx_bytes"); + if (err) + return err; + PRINT_CGX_CUML_NIXRX_STATUS(RX_DROP, "rx_drops"); + if (err) + return err; + PRINT_CGX_CUML_NIXRX_STATUS(RX_ERR, "rx_errors"); + if (err) + return err; + + /* Tx stats */ + seq_puts(s, "\n=======NIX TX_STATS(CGX port level)======\n\n"); + ucast = PRINT_CGX_CUML_NIXTX_STATUS(TX_UCAST, "tx_ucast_frames"); + if (err) + return err; + mcast = PRINT_CGX_CUML_NIXTX_STATUS(TX_MCAST, "tx_mcast_frames"); + if (err) + return err; + bcast = PRINT_CGX_CUML_NIXTX_STATUS(TX_BCAST, "tx_bcast_frames"); + if (err) + return err; + seq_printf(s, "tx_frames: %llu\n", ucast + mcast + bcast); + PRINT_CGX_CUML_NIXTX_STATUS(TX_OCTS, "tx_bytes"); + if (err) + return err; + PRINT_CGX_CUML_NIXTX_STATUS(TX_DROP, "tx_drops"); + if (err) + return err; + + /* Rx stats */ + seq_puts(s, "\n=======CGX RX_STATS======\n\n"); + while (stat < CGX_RX_STATS_COUNT) { + err = cgx_get_rx_stats(cgxd, lmac_id, stat, &rx_stat); + if (err) + return err; + seq_printf(s, "%s: %llu\n", cgx_rx_stats_fields[stat], rx_stat); + stat++; + } + + /* Tx stats */ + stat = 0; + seq_puts(s, "\n=======CGX TX_STATS======\n\n"); + while (stat < CGX_TX_STATS_COUNT) { + err = cgx_get_tx_stats(cgxd, lmac_id, stat, &tx_stat); + if (err) + return err; + seq_printf(s, "%s: %llu\n", cgx_tx_stats_fields[stat], tx_stat); + stat++; + } + + return err; +} + +static int rvu_dbg_cgx_stat_display(struct seq_file *filp, void *unused) +{ + struct dentry *current_dir; + int err, lmac_id; + char *buf; + + current_dir = filp->file->f_path.dentry->d_parent; + buf = strrchr(current_dir->d_name.name, 'c'); + if (!buf) + return -EINVAL; + + err = kstrtoint(buf + 1, 10, &lmac_id); + if (!err) { + err = cgx_print_stats(filp, lmac_id); + if (err) + return err; + } + return err; +} + +RVU_DEBUG_SEQ_FOPS(cgx_stat, cgx_stat_display, NULL); + +/* Dumps given nix_sq's context */ +static void print_nix_sq_ctx(struct seq_file *m, struct nix_aq_enq_rsp *rsp) +{ + struct nix_sq_ctx_s *sq_ctx = &rsp->sq; + + seq_printf(m, "W0: sqe_way_mask \t\t%d\nW0: cq \t\t\t\t%d\n", + sq_ctx->sqe_way_mask, sq_ctx->cq); + seq_printf(m, "W0: sdp_mcast \t\t\t%d\nW0: substream \t\t\t0x%03x\n", + sq_ctx->sdp_mcast, sq_ctx->substream); + seq_printf(m, "W0: qint_idx \t\t\t%d\nW0: ena \t\t\t%d\n\n", + sq_ctx->qint_idx, sq_ctx->ena); + + seq_printf(m, "W1: sqb_count \t\t\t%d\nW1: default_chan \t\t%d\n", + sq_ctx->sqb_count, sq_ctx->default_chan); + seq_printf(m, "W1: smq_rr_quantum \t\t%d\nW1: sso_ena \t\t\t%d\n", + sq_ctx->smq_rr_quantum, sq_ctx->sso_ena); + seq_printf(m, "W1: xoff \t\t\t%d\nW1: cq_ena \t\t\t%d\nW1: smq\t\t\t\t%d\n\n", + sq_ctx->xoff, sq_ctx->cq_ena, sq_ctx->smq); + + seq_printf(m, "W2: sqe_stype \t\t\t%d\nW2: sq_int_ena \t\t\t%d\n", + sq_ctx->sqe_stype, sq_ctx->sq_int_ena); + seq_printf(m, "W2: sq_int \t\t\t%d\nW2: sqb_aura \t\t\t%d\n", + sq_ctx->sq_int, sq_ctx->sqb_aura); + seq_printf(m, "W2: smq_rr_count \t\t%d\n\n", sq_ctx->smq_rr_count); + + seq_printf(m, "W3: smq_next_sq_vld\t\t%d\nW3: smq_pend\t\t\t%d\n", + sq_ctx->smq_next_sq_vld, sq_ctx->smq_pend); + seq_printf(m, "W3: smenq_next_sqb_vld \t\t%d\nW3: head_offset\t\t\t%d\n", + sq_ctx->smenq_next_sqb_vld, sq_ctx->head_offset); + seq_printf(m, "W3: smenq_offset\t\t%d\nW3: tail_offset\t\t\t%d\n", + sq_ctx->smenq_offset, sq_ctx->tail_offset); + seq_printf(m, "W3: smq_lso_segnum \t\t%d\nW3: smq_next_sq\t\t\t%d\n", + sq_ctx->smq_lso_segnum, sq_ctx->smq_next_sq); + seq_printf(m, "W3: mnq_dis \t\t\t%d\nW3: lmt_dis \t\t\t%d\n", + sq_ctx->mnq_dis, sq_ctx->lmt_dis); + seq_printf(m, "W3: cq_limit\t\t\t%d\nW3: max_sqe_size\t\t%d\n\n", + sq_ctx->cq_limit, sq_ctx->max_sqe_size); + + seq_printf(m, "W4: next_sqb \t\t\t%llx\n\n", sq_ctx->next_sqb); + seq_printf(m, "W5: tail_sqb \t\t\t%llx\n\n", sq_ctx->tail_sqb); + seq_printf(m, "W6: smenq_sqb \t\t\t%llx\n\n", sq_ctx->smenq_sqb); + seq_printf(m, "W7: smenq_next_sqb \t\t%llx\n\n", + sq_ctx->smenq_next_sqb); + + seq_printf(m, "W8: head_sqb\t\t\t%llx\n\n", sq_ctx->head_sqb); + + seq_printf(m, "W9: vfi_lso_vld\t\t\t%d\nW9: vfi_lso_vlan1_ins_ena\t%d\n", + sq_ctx->vfi_lso_vld, sq_ctx->vfi_lso_vlan1_ins_ena); + seq_printf(m, "W9: vfi_lso_vlan0_ins_ena\t%d\nW9: vfi_lso_mps\t\t\t%d\n", + sq_ctx->vfi_lso_vlan0_ins_ena, sq_ctx->vfi_lso_mps); + seq_printf(m, "W9: vfi_lso_sb\t\t\t%d\nW9: vfi_lso_sizem1\t\t%d\n", + sq_ctx->vfi_lso_sb, sq_ctx->vfi_lso_sizem1); + seq_printf(m, "W9: vfi_lso_total\t\t%d\n\n", sq_ctx->vfi_lso_total); + + seq_printf(m, "W10: scm_lso_rem \t\t%llu\n\n", + (u64)sq_ctx->scm_lso_rem); + seq_printf(m, "W11: octs \t\t\t%llu\n\n", (u64)sq_ctx->octs); + seq_printf(m, "W12: pkts \t\t\t%llu\n\n", (u64)sq_ctx->pkts); + seq_printf(m, "W14: dropped_octs \t\t%llu\n\n", + (u64)sq_ctx->dropped_octs); + seq_printf(m, "W15: dropped_pkts \t\t%llu\n\n", + (u64)sq_ctx->dropped_pkts); +} + +/* Dumps given nix_rq's context */ +static void print_nix_rq_ctx(struct seq_file *m, struct nix_aq_enq_rsp *rsp) +{ + struct nix_rq_ctx_s *rq_ctx = &rsp->rq; + + seq_printf(m, "W0: wqe_aura \t\t\t%d\nW0: substream \t\t\t0x%03x\n", + rq_ctx->wqe_aura, rq_ctx->substream); + seq_printf(m, "W0: cq \t\t\t\t%d\nW0: ena_wqwd \t\t\t%d\n", + rq_ctx->cq, rq_ctx->ena_wqwd); + seq_printf(m, "W0: ipsech_ena \t\t\t%d\nW0: sso_ena \t\t\t%d\n", + rq_ctx->ipsech_ena, rq_ctx->sso_ena); + seq_printf(m, "W0: ena \t\t\t%d\n\n", rq_ctx->ena); + + seq_printf(m, "W1: lpb_drop_ena \t\t%d\nW1: spb_drop_ena \t\t%d\n", + rq_ctx->lpb_drop_ena, rq_ctx->spb_drop_ena); + seq_printf(m, "W1: xqe_drop_ena \t\t%d\nW1: wqe_caching \t\t%d\n", + rq_ctx->xqe_drop_ena, rq_ctx->wqe_caching); + seq_printf(m, "W1: pb_caching \t\t\t%d\nW1: sso_tt \t\t\t%d\n", + rq_ctx->pb_caching, rq_ctx->sso_tt); + seq_printf(m, "W1: sso_grp \t\t\t%d\nW1: lpb_aura \t\t\t%d\n", + rq_ctx->sso_grp, rq_ctx->lpb_aura); + seq_printf(m, "W1: spb_aura \t\t\t%d\n\n", rq_ctx->spb_aura); + + seq_printf(m, "W2: xqe_hdr_split \t\t%d\nW2: xqe_imm_copy \t\t%d\n", + rq_ctx->xqe_hdr_split, rq_ctx->xqe_imm_copy); + seq_printf(m, "W2: xqe_imm_size \t\t%d\nW2: later_skip \t\t\t%d\n", + rq_ctx->xqe_imm_size, rq_ctx->later_skip); + seq_printf(m, "W2: first_skip \t\t\t%d\nW2: lpb_sizem1 \t\t\t%d\n", + rq_ctx->first_skip, rq_ctx->lpb_sizem1); + seq_printf(m, "W2: spb_ena \t\t\t%d\nW2: wqe_skip \t\t\t%d\n", + rq_ctx->spb_ena, rq_ctx->wqe_skip); + seq_printf(m, "W2: spb_sizem1 \t\t\t%d\n\n", rq_ctx->spb_sizem1); + + seq_printf(m, "W3: spb_pool_pass \t\t%d\nW3: spb_pool_drop \t\t%d\n", + rq_ctx->spb_pool_pass, rq_ctx->spb_pool_drop); + seq_printf(m, "W3: spb_aura_pass \t\t%d\nW3: spb_aura_drop \t\t%d\n", + rq_ctx->spb_aura_pass, rq_ctx->spb_aura_drop); + seq_printf(m, "W3: wqe_pool_pass \t\t%d\nW3: wqe_pool_drop \t\t%d\n", + rq_ctx->wqe_pool_pass, rq_ctx->wqe_pool_drop); + seq_printf(m, "W3: xqe_pass \t\t\t%d\nW3: xqe_drop \t\t\t%d\n\n", + rq_ctx->xqe_pass, rq_ctx->xqe_drop); + + seq_printf(m, "W4: qint_idx \t\t\t%d\nW4: rq_int_ena \t\t\t%d\n", + rq_ctx->qint_idx, rq_ctx->rq_int_ena); + seq_printf(m, "W4: rq_int \t\t\t%d\nW4: lpb_pool_pass \t\t%d\n", + rq_ctx->rq_int, rq_ctx->lpb_pool_pass); + seq_printf(m, "W4: lpb_pool_drop \t\t%d\nW4: lpb_aura_pass \t\t%d\n", + rq_ctx->lpb_pool_drop, rq_ctx->lpb_aura_pass); + seq_printf(m, "W4: lpb_aura_drop \t\t%d\n\n", rq_ctx->lpb_aura_drop); + + seq_printf(m, "W5: flow_tagw \t\t\t%d\nW5: bad_utag \t\t\t%d\n", + rq_ctx->flow_tagw, rq_ctx->bad_utag); + seq_printf(m, "W5: good_utag \t\t\t%d\nW5: ltag \t\t\t%d\n\n", + rq_ctx->good_utag, rq_ctx->ltag); + + seq_printf(m, "W6: octs \t\t\t%llu\n\n", (u64)rq_ctx->octs); + seq_printf(m, "W7: pkts \t\t\t%llu\n\n", (u64)rq_ctx->pkts); + seq_printf(m, "W8: drop_octs \t\t\t%llu\n\n", (u64)rq_ctx->drop_octs); + seq_printf(m, "W9: drop_pkts \t\t\t%llu\n\n", (u64)rq_ctx->drop_pkts); + seq_printf(m, "W10: re_pkts \t\t\t%llu\n", (u64)rq_ctx->re_pkts); +} + +/* Dumps given nix_cq's context */ +static void print_nix_cq_ctx(struct seq_file *m, struct nix_aq_enq_rsp *rsp) +{ + struct nix_cq_ctx_s *cq_ctx = &rsp->cq; + + seq_printf(m, "W0: base \t\t\t%llx\n\n", cq_ctx->base); + + seq_printf(m, "W1: wrptr \t\t\t%llx\n", (u64)cq_ctx->wrptr); + seq_printf(m, "W1: avg_con \t\t\t%d\nW1: cint_idx \t\t\t%d\n", + cq_ctx->avg_con, cq_ctx->cint_idx); + seq_printf(m, "W1: cq_err \t\t\t%d\nW1: qint_idx \t\t\t%d\n", + cq_ctx->cq_err, cq_ctx->qint_idx); + seq_printf(m, "W1: bpid \t\t\t%d\nW1: bp_ena \t\t\t%d\n\n", + cq_ctx->bpid, cq_ctx->bp_ena); + + seq_printf(m, "W2: update_time \t\t%d\nW2:avg_level \t\t\t%d\n", + cq_ctx->update_time, cq_ctx->avg_level); + seq_printf(m, "W2: head \t\t\t%d\nW2:tail \t\t\t%d\n\n", + cq_ctx->head, cq_ctx->tail); + + seq_printf(m, "W3: cq_err_int_ena \t\t%d\nW3:cq_err_int \t\t\t%d\n", + cq_ctx->cq_err_int_ena, cq_ctx->cq_err_int); + seq_printf(m, "W3: qsize \t\t\t%d\nW3:caching \t\t\t%d\n", + cq_ctx->qsize, cq_ctx->caching); + seq_printf(m, "W3: substream \t\t\t0x%03x\nW3: ena \t\t\t%d\n", + cq_ctx->substream, cq_ctx->ena); + seq_printf(m, "W3: drop_ena \t\t\t%d\nW3: drop \t\t\t%d\n", + cq_ctx->drop_ena, cq_ctx->drop); + seq_printf(m, "W3: bp \t\t\t\t%d\n\n", cq_ctx->bp); +} + +static int rvu_dbg_nix_queue_ctx_display(struct seq_file *filp, + void *unused, int ctype) +{ + void (*print_nix_ctx)(struct seq_file *filp, + struct nix_aq_enq_rsp *rsp) = NULL; + struct rvu *rvu = filp->private; + struct nix_aq_enq_req aq_req; + struct nix_aq_enq_rsp rsp; + char *ctype_string = NULL; + int qidx, rc, max_id = 0; + struct rvu_pfvf *pfvf; + int nixlf, id, all; + u16 pcifunc; + + switch (ctype) { + case NIX_AQ_CTYPE_CQ: + nixlf = rvu->rvu_dbg.nix_cq_ctx.lf; + id = rvu->rvu_dbg.nix_cq_ctx.id; + all = rvu->rvu_dbg.nix_cq_ctx.all; + break; + + case NIX_AQ_CTYPE_SQ: + nixlf = rvu->rvu_dbg.nix_sq_ctx.lf; + id = rvu->rvu_dbg.nix_sq_ctx.id; + all = rvu->rvu_dbg.nix_sq_ctx.all; + break; + + case NIX_AQ_CTYPE_RQ: + nixlf = rvu->rvu_dbg.nix_rq_ctx.lf; + id = rvu->rvu_dbg.nix_rq_ctx.id; + all = rvu->rvu_dbg.nix_rq_ctx.all; + break; + + default: + return -EINVAL; + } + + if (!rvu_dbg_is_valid_lf(rvu, BLKTYPE_NIX, nixlf, &pcifunc)) + return -EINVAL; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + if (ctype == NIX_AQ_CTYPE_SQ && !pfvf->sq_ctx) { + seq_puts(filp, "SQ context is not initialized\n"); + return -EINVAL; + } else if (ctype == NIX_AQ_CTYPE_RQ && !pfvf->rq_ctx) { + seq_puts(filp, "RQ context is not initialized\n"); + return -EINVAL; + } else if (ctype == NIX_AQ_CTYPE_CQ && !pfvf->cq_ctx) { + seq_puts(filp, "CQ context is not initialized\n"); + return -EINVAL; + } + + if (ctype == NIX_AQ_CTYPE_SQ) { + max_id = pfvf->sq_ctx->qsize; + ctype_string = "sq"; + print_nix_ctx = print_nix_sq_ctx; + } else if (ctype == NIX_AQ_CTYPE_RQ) { + max_id = pfvf->rq_ctx->qsize; + ctype_string = "rq"; + print_nix_ctx = print_nix_rq_ctx; + } else if (ctype == NIX_AQ_CTYPE_CQ) { + max_id = pfvf->cq_ctx->qsize; + ctype_string = "cq"; + print_nix_ctx = print_nix_cq_ctx; + } + + memset(&aq_req, 0, sizeof(struct nix_aq_enq_req)); + aq_req.hdr.pcifunc = pcifunc; + aq_req.ctype = ctype; + aq_req.op = NIX_AQ_INSTOP_READ; + if (all) + id = 0; + else + max_id = id + 1; + for (qidx = id; qidx < max_id; qidx++) { + aq_req.qidx = qidx; + seq_printf(filp, "=====%s_ctx for nixlf:%d and qidx:%d is=====\n", + ctype_string, nixlf, aq_req.qidx); + rc = rvu_mbox_handler_nix_aq_enq(rvu, &aq_req, &rsp); + if (rc) { + seq_puts(filp, "Failed to read the context\n"); + return -EINVAL; + } + print_nix_ctx(filp, &rsp); + } + return 0; +} + +static int write_nix_queue_ctx(struct rvu *rvu, bool all, int nixlf, + int id, int ctype, char *ctype_string) +{ + struct rvu_pfvf *pfvf; + int max_id = 0; + u16 pcifunc; + + if (!rvu_dbg_is_valid_lf(rvu, BLKTYPE_NIX, nixlf, &pcifunc)) + return -EINVAL; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + + if (ctype == NIX_AQ_CTYPE_SQ) { + if (!pfvf->sq_ctx) { + dev_warn(rvu->dev, "SQ context is not initialized\n"); + return -EINVAL; + } + max_id = pfvf->sq_ctx->qsize; + } else if (ctype == NIX_AQ_CTYPE_RQ) { + if (!pfvf->rq_ctx) { + dev_warn(rvu->dev, "RQ context is not initialized\n"); + return -EINVAL; + } + max_id = pfvf->rq_ctx->qsize; + } else if (ctype == NIX_AQ_CTYPE_CQ) { + if (!pfvf->cq_ctx) { + dev_warn(rvu->dev, "CQ context is not initialized\n"); + return -EINVAL; + } + max_id = pfvf->cq_ctx->qsize; + } + + if (id < 0 || id >= max_id) { + dev_warn(rvu->dev, "Invalid %s_ctx valid range 0-%d\n", + ctype_string, max_id - 1); + return -EINVAL; + } + switch (ctype) { + case NIX_AQ_CTYPE_CQ: + rvu->rvu_dbg.nix_cq_ctx.lf = nixlf; + rvu->rvu_dbg.nix_cq_ctx.id = id; + rvu->rvu_dbg.nix_cq_ctx.all = all; + break; + + case NIX_AQ_CTYPE_SQ: + rvu->rvu_dbg.nix_sq_ctx.lf = nixlf; + rvu->rvu_dbg.nix_sq_ctx.id = id; + rvu->rvu_dbg.nix_sq_ctx.all = all; + break; + + case NIX_AQ_CTYPE_RQ: + rvu->rvu_dbg.nix_rq_ctx.lf = nixlf; + rvu->rvu_dbg.nix_rq_ctx.id = id; + rvu->rvu_dbg.nix_rq_ctx.all = all; + break; + default: + return -EINVAL; + } + return 0; +} + +static ssize_t rvu_dbg_nix_queue_ctx_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos, + int ctype) +{ + struct seq_file *m = filp->private_data; + struct rvu *rvu = m->private; + char *cmd_buf, *ctype_string; + int nixlf, id = 0, ret; + bool all = false; + + if ((*ppos != 0) || !count) + return -EINVAL; + + switch (ctype) { + case NIX_AQ_CTYPE_SQ: + ctype_string = "sq"; + break; + case NIX_AQ_CTYPE_RQ: + ctype_string = "rq"; + break; + case NIX_AQ_CTYPE_CQ: + ctype_string = "cq"; + break; + default: + return -EINVAL; + } + + cmd_buf = kzalloc(count + 1, GFP_KERNEL); + + if (!cmd_buf) + return count; + + ret = parse_cmd_buffer_ctx(cmd_buf, &count, buffer, + &nixlf, &id, &all); + if (ret < 0) { + dev_info(rvu->dev, + "Usage: echo <nixlf> [%s number/all] > %s_ctx\n", + ctype_string, ctype_string); + goto done; + } else { + ret = write_nix_queue_ctx(rvu, all, nixlf, id, ctype, + ctype_string); + } +done: + kfree(cmd_buf); + return ret ? ret : count; +} + +static ssize_t rvu_dbg_nix_sq_ctx_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_nix_queue_ctx_write(filp, buffer, count, ppos, + NIX_AQ_CTYPE_SQ); +} + +static int rvu_dbg_nix_sq_ctx_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_nix_queue_ctx_display(filp, unused, NIX_AQ_CTYPE_SQ); +} + +RVU_DEBUG_SEQ_FOPS(nix_sq_ctx, nix_sq_ctx_display, nix_sq_ctx_write); + +static ssize_t rvu_dbg_nix_rq_ctx_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_nix_queue_ctx_write(filp, buffer, count, ppos, + NIX_AQ_CTYPE_RQ); +} + +static int rvu_dbg_nix_rq_ctx_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_nix_queue_ctx_display(filp, unused, NIX_AQ_CTYPE_RQ); +} + +RVU_DEBUG_SEQ_FOPS(nix_rq_ctx, nix_rq_ctx_display, nix_rq_ctx_write); + +static ssize_t rvu_dbg_nix_cq_ctx_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_nix_queue_ctx_write(filp, buffer, count, ppos, + NIX_AQ_CTYPE_CQ); +} + +static int rvu_dbg_nix_cq_ctx_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_nix_queue_ctx_display(filp, unused, NIX_AQ_CTYPE_CQ); +} + +RVU_DEBUG_SEQ_FOPS(nix_cq_ctx, nix_cq_ctx_display, nix_cq_ctx_write); + +/* NPC debugfs APIs */ +static inline void rvu_print_npc_mcam_info(struct seq_file *s, + u16 pcifunc, int blkaddr) +{ + struct rvu *rvu = s->private; + int entry_acnt, entry_ecnt; + int cntr_acnt, cntr_ecnt; + + /* Skip PF0 */ + if (!pcifunc) + return; + rvu_npc_get_mcam_entry_alloc_info(rvu, pcifunc, blkaddr, + &entry_acnt, &entry_ecnt); + rvu_npc_get_mcam_counter_alloc_info(rvu, pcifunc, blkaddr, + &cntr_acnt, &cntr_ecnt); + if (!entry_acnt && !cntr_acnt) + return; + + if (!(pcifunc & RVU_PFVF_FUNC_MASK)) + seq_printf(s, "\n\t\t Device \t\t: PF%d\n", + rvu_get_pf(pcifunc)); + else + seq_printf(s, "\n\t\t Device \t\t: PF%d VF%d\n", + rvu_get_pf(pcifunc), + (pcifunc & RVU_PFVF_FUNC_MASK) - 1); + + if (entry_acnt) { + seq_printf(s, "\t\t Entries allocated \t: %d\n", entry_acnt); + seq_printf(s, "\t\t Entries enabled \t: %d\n", entry_ecnt); + } + if (cntr_acnt) { + seq_printf(s, "\t\t Counters allocated \t: %d\n", cntr_acnt); + seq_printf(s, "\t\t Counters enabled \t: %d\n", cntr_ecnt); + } +} + +static int rvu_dbg_npc_mcam_info_display(struct seq_file *filp, void *unsued) +{ + struct rvu *rvu = filp->private; + int pf, vf, numvfs, blkaddr; + struct npc_mcam *mcam; + u16 pcifunc; + u64 cfg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return -ENODEV; + + mcam = &rvu->hw->mcam; + + seq_puts(filp, "\nNPC MCAM info:\n"); + /* MCAM keywidth on receive and transmit sides */ + cfg = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_RX)); + cfg = (cfg >> 32) & 0x07; + seq_printf(filp, "\t\t RX keywidth \t: %s\n", (cfg == NPC_MCAM_KEY_X1) ? + "112bits" : ((cfg == NPC_MCAM_KEY_X2) ? + "224bits" : "448bits")); + cfg = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_TX)); + cfg = (cfg >> 32) & 0x07; + seq_printf(filp, "\t\t TX keywidth \t: %s\n", (cfg == NPC_MCAM_KEY_X1) ? + "112bits" : ((cfg == NPC_MCAM_KEY_X2) ? + "224bits" : "448bits")); + + mutex_lock(&mcam->lock); + /* MCAM entries */ + seq_printf(filp, "\n\t\t MCAM entries \t: %d\n", mcam->total_entries); + seq_printf(filp, "\t\t Reserved \t: %d\n", + mcam->total_entries - mcam->bmap_entries); + seq_printf(filp, "\t\t Available \t: %d\n", mcam->bmap_fcnt); + + /* MCAM counters */ + cfg = rvu_read64(rvu, blkaddr, NPC_AF_CONST); + cfg = (cfg >> 48) & 0xFFFF; + seq_printf(filp, "\n\t\t MCAM counters \t: %lld\n", cfg); + seq_printf(filp, "\t\t Reserved \t: %lld\n", cfg - mcam->counters.max); + seq_printf(filp, "\t\t Available \t: %d\n", + rvu_rsrc_free_count(&mcam->counters)); + + if (mcam->bmap_entries == mcam->bmap_fcnt) { + mutex_unlock(&mcam->lock); + return 0; + } + + seq_puts(filp, "\n\t\t Current allocation\n"); + seq_puts(filp, "\t\t====================\n"); + for (pf = 0; pf < rvu->hw->total_pfs; pf++) { + pcifunc = (pf << RVU_PFVF_PF_SHIFT); + rvu_print_npc_mcam_info(filp, pcifunc, blkaddr); + + cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf)); + numvfs = (cfg >> 12) & 0xFF; + for (vf = 0; vf < numvfs; vf++) { + pcifunc = (pf << RVU_PFVF_PF_SHIFT) | (vf + 1); + rvu_print_npc_mcam_info(filp, pcifunc, blkaddr); + } + } + + mutex_unlock(&mcam->lock); + return 0; +} + +RVU_DEBUG_SEQ_FOPS(npc_mcam_info, npc_mcam_info_display, NULL); + +static int rvu_dbg_npc_rx_miss_stats_display(struct seq_file *filp, + void *unused) +{ + struct rvu *rvu = filp->private; + struct npc_mcam *mcam; + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return -ENODEV; + + mcam = &rvu->hw->mcam; + + seq_puts(filp, "\nNPC MCAM RX miss action stats\n"); + seq_printf(filp, "\t\tStat %d: \t%lld\n", mcam->rx_miss_act_cntr, + rvu_read64(rvu, blkaddr, + NPC_AF_MATCH_STATX(mcam->rx_miss_act_cntr))); + + return 0; +} + +RVU_DEBUG_SEQ_FOPS(npc_rx_miss_act, npc_rx_miss_stats_display, NULL); + +static void rvu_dbg_npc_mcam_show_flows(struct seq_file *s, + struct rvu_npc_mcam_rule *rule) +{ + u8 bit; + + for_each_set_bit(bit, (unsigned long *)&rule->features, 64) { + seq_printf(s, "\t%s ", npc_get_field_name(bit)); + switch (bit) { + case NPC_DMAC: + seq_printf(s, "%pM ", rule->packet.dmac); + seq_printf(s, "%pM\n", rule->mask.dmac); + break; + case NPC_SMAC: + seq_printf(s, "%pM ", rule->packet.smac); + seq_printf(s, "%pM\n", rule->mask.smac); + break; + case NPC_ETYPE: + seq_printf(s, "0x%x ", ntohs(rule->packet.etype)); + seq_printf(s, "0x%x\n", ntohs(rule->mask.etype)); + break; + case NPC_OUTER_VID: + seq_printf(s, "%d ", ntohs(rule->packet.vlan_tci)); + seq_printf(s, "mask 0x%x\n", + ntohs(rule->mask.vlan_tci)); + break; + case NPC_TOS: + seq_printf(s, "%d ", rule->packet.tos); + seq_printf(s, "mask 0x%x\n", rule->mask.tos); + break; + case NPC_SIP_IPV4: + seq_printf(s, "%pI4 ", &rule->packet.ip4src); + seq_printf(s, "%pI4\n", &rule->mask.ip4src); + break; + case NPC_DIP_IPV4: + seq_printf(s, "%pI4 ", &rule->packet.ip4dst); + seq_printf(s, "%pI4\n", &rule->mask.ip4dst); + break; + case NPC_SIP_IPV6: + seq_printf(s, "%pI6 ", rule->packet.ip6src); + seq_printf(s, "%pI6\n", rule->mask.ip6src); + break; + case NPC_DIP_IPV6: + seq_printf(s, "%pI6 ", rule->packet.ip6dst); + seq_printf(s, "%pI6\n", rule->mask.ip6dst); + break; + case NPC_SPORT_TCP: + case NPC_SPORT_UDP: + seq_printf(s, "%d ", ntohs(rule->packet.sport)); + seq_printf(s, "mask 0x%x\n", ntohs(rule->mask.sport)); + break; + case NPC_DPORT_TCP: + case NPC_DPORT_UDP: + seq_printf(s, "%d ", ntohs(rule->packet.dport)); + seq_printf(s, "mask 0x%x\n", ntohs(rule->mask.dport)); + break; + default: + break; + } + } +} + +static void rvu_dbg_npc_mcam_show_action(struct seq_file *s, + struct rvu_npc_mcam_rule *rule) +{ + if (rule->intf == NIX_INTF_TX) { + switch (rule->tx_action.op) { + case NIX_TX_ACTIONOP_DROP: + seq_puts(s, "\taction: Drop\n"); + break; + case NIX_TX_ACTIONOP_UCAST_DEFAULT: + seq_puts(s, "\taction: Unicast to default channel\n"); + break; + case NIX_TX_ACTIONOP_UCAST_CHAN: + seq_printf(s, "\taction: Unicast to channel %d\n", + rule->tx_action.index); + break; + case NIX_TX_ACTIONOP_MCAST: + seq_puts(s, "\taction: Multicast\n"); + break; + case NIX_TX_ACTIONOP_DROP_VIOL: + seq_puts(s, "\taction: Lockdown Violation Drop\n"); + break; + default: + break; + }; + } else { + switch (rule->rx_action.op) { + case NIX_RX_ACTIONOP_DROP: + seq_puts(s, "\taction: Drop\n"); + break; + case NIX_RX_ACTIONOP_UCAST: + seq_printf(s, "\taction: Direct to queue %d\n", + rule->rx_action.index); + break; + case NIX_RX_ACTIONOP_RSS: + seq_puts(s, "\taction: RSS\n"); + break; + case NIX_RX_ACTIONOP_UCAST_IPSEC: + seq_puts(s, "\taction: Unicast ipsec\n"); + break; + case NIX_RX_ACTIONOP_MCAST: + seq_puts(s, "\taction: Multicast\n"); + break; + default: + break; + }; + } +} + +static int rvu_dbg_npc_mcam_show_rules(struct seq_file *s, void *unused) +{ + struct rvu_npc_mcam_rule *iter; + struct rvu *rvu = s->private; + struct npc_mcam *mcam; + int pf, vf = -1; + int blkaddr; + u16 target; + u64 hits; + + mcam = &rvu->hw->mcam; + + list_for_each_entry(iter, &mcam->mcam_rules, list) { + pf = (iter->owner >> RVU_PFVF_PF_SHIFT) & RVU_PFVF_PF_MASK; + seq_printf(s, "\n\tPF%d ", pf); + + if (iter->owner & RVU_PFVF_FUNC_MASK) { + vf = (iter->owner & RVU_PFVF_FUNC_MASK) - 1; + seq_printf(s, "VF%d", vf); + } + seq_puts(s, "\n"); + + if (iter->intf == NIX_INTF_RX) { + seq_puts(s, "\tdirection: RX\n"); + target = iter->rx_action.pf_func; + pf = (target >> RVU_PFVF_PF_SHIFT) & RVU_PFVF_PF_MASK; + seq_printf(s, "\ttarget: PF%d ", pf); + + if (target & RVU_PFVF_FUNC_MASK) { + vf = (target & RVU_PFVF_FUNC_MASK) - 1; + seq_printf(s, "VF%d", vf); + } + seq_puts(s, "\n"); + } else { + seq_puts(s, "\tdirection: TX\n"); + } + + seq_printf(s, "\tmcam entry: %d\n", iter->entry); + rvu_dbg_npc_mcam_show_flows(s, iter); + rvu_dbg_npc_mcam_show_action(s, iter); + seq_printf(s, "\tenabled: %s\n", iter->enable ? "yes" : "no"); + + if (!iter->has_cntr) + continue; + seq_printf(s, "\tcounter: %d\n", iter->cntr); + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return 0; + hits = rvu_read64(rvu, blkaddr, NPC_AF_MATCH_STATX(iter->cntr)); + seq_printf(s, "\thits: %lld\n", hits); + } + + return 0; +} + +RVU_DEBUG_SEQ_FOPS(npc_mcam_rules, npc_mcam_show_rules, NULL); + +static void rvu_dbg_npc_init(struct rvu *rvu) +{ + const struct device *dev = &rvu->pdev->dev; + struct dentry *pfile; + + rvu->rvu_dbg.npc = debugfs_create_dir("npc", rvu->rvu_dbg.root); + if (!rvu->rvu_dbg.npc) + return; + + pfile = debugfs_create_file("mcam_info", 0444, rvu->rvu_dbg.npc, + rvu, &rvu_dbg_npc_mcam_info_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("mcam_rules", 0444, rvu->rvu_dbg.npc, + rvu, &rvu_dbg_npc_mcam_rules_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("rx_miss_act_stats", 0444, rvu->rvu_dbg.npc, + rvu, &rvu_dbg_npc_rx_miss_act_fops); + if (!pfile) + goto create_failed; + + return; + +create_failed: + dev_err(dev, "Failed to create debugfs dir/file for NPC\n"); + debugfs_remove_recursive(rvu->rvu_dbg.npc); +} + +static void print_nix_qctx_qsize(struct seq_file *filp, int qsize, + unsigned long *bmap, char *qtype) +{ + char *buf; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return; + + bitmap_print_to_pagebuf(false, buf, bmap, qsize); + seq_printf(filp, "%s context count : %d\n", qtype, qsize); + seq_printf(filp, "%s context ena/dis bitmap : %s\n", + qtype, buf); + kfree(buf); +} + +static void print_nix_qsize(struct seq_file *filp, struct rvu_pfvf *pfvf) +{ + if (!pfvf->cq_ctx) + seq_puts(filp, "cq context is not initialized\n"); + else + print_nix_qctx_qsize(filp, pfvf->cq_ctx->qsize, pfvf->cq_bmap, + "cq"); + + if (!pfvf->rq_ctx) + seq_puts(filp, "rq context is not initialized\n"); + else + print_nix_qctx_qsize(filp, pfvf->rq_ctx->qsize, pfvf->rq_bmap, + "rq"); + + if (!pfvf->sq_ctx) + seq_puts(filp, "sq context is not initialized\n"); + else + print_nix_qctx_qsize(filp, pfvf->sq_ctx->qsize, pfvf->sq_bmap, + "sq"); +} + +static ssize_t rvu_dbg_nix_qsize_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_qsize_write(filp, buffer, count, ppos, + BLKTYPE_NIX); +} + +static int rvu_dbg_nix_qsize_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_qsize_display(filp, unused, BLKTYPE_NIX); +} + +RVU_DEBUG_SEQ_FOPS(nix_qsize, nix_qsize_display, nix_qsize_write); + +static ssize_t rvu_dbg_nix_tx_stall_hwissue_display(struct file *filp, + char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_nix_get_tx_stall_counters(filp->private_data, buffer, ppos); +} + +RVU_DEBUG_FOPS(nix_tx_stall_hwissue, nix_tx_stall_hwissue_display, NULL); + +static void rvu_dbg_nix_init(struct rvu *rvu) +{ + const struct device *dev = &rvu->pdev->dev; + struct dentry *pfile; + + rvu->rvu_dbg.nix = debugfs_create_dir("nix", rvu->rvu_dbg.root); + if (!rvu->rvu_dbg.nix) { + dev_err(rvu->dev, "create debugfs dir failed for nix\n"); + return; + } + + pfile = debugfs_create_file("sq_ctx", 0600, rvu->rvu_dbg.nix, rvu, + &rvu_dbg_nix_sq_ctx_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("rq_ctx", 0600, rvu->rvu_dbg.nix, rvu, + &rvu_dbg_nix_rq_ctx_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("cq_ctx", 0600, rvu->rvu_dbg.nix, rvu, + &rvu_dbg_nix_cq_ctx_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("ndc_tx_cache", 0600, rvu->rvu_dbg.nix, rvu, + &rvu_dbg_nix_ndc_tx_cache_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("ndc_rx_cache", 0600, rvu->rvu_dbg.nix, rvu, + &rvu_dbg_nix_ndc_rx_cache_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("ndc_tx_hits_miss", 0600, rvu->rvu_dbg.nix, + rvu, &rvu_dbg_nix_ndc_tx_hits_miss_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("ndc_rx_hits_miss", 0600, rvu->rvu_dbg.nix, + rvu, &rvu_dbg_nix_ndc_rx_hits_miss_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("qsize", 0600, rvu->rvu_dbg.nix, rvu, + &rvu_dbg_nix_qsize_fops); + if (!pfile) + goto create_failed; + + if (is_rvu_96xx_A0(rvu)) { + pfile = debugfs_create_file("tx_stall_hwissue", 0600, + rvu->rvu_dbg.nix, rvu, + &rvu_dbg_nix_tx_stall_hwissue_fops); + if (!pfile) + goto create_failed; + } + + return; +create_failed: + dev_err(dev, "Failed to create debugfs dir/file for NIX\n"); + debugfs_remove_recursive(rvu->rvu_dbg.nix); +} + +static void rvu_dbg_cgx_init(struct rvu *rvu) +{ + const struct device *dev = &rvu->pdev->dev; + struct dentry *pfile; + int i, lmac_id; + char dname[20]; + void *cgx; + + rvu->rvu_dbg.cgx_root = debugfs_create_dir("cgx", rvu->rvu_dbg.root); + + for (i = 0; i < cgx_get_cgxcnt_max(); i++) { + cgx = rvu_cgx_pdata(i, rvu); + if (!cgx) + continue; + /* cgx debugfs dir */ + sprintf(dname, "cgx%d", i); + rvu->rvu_dbg.cgx = debugfs_create_dir(dname, + rvu->rvu_dbg.cgx_root); + for (lmac_id = 0; lmac_id < cgx_get_lmac_cnt(cgx); lmac_id++) { + /* lmac debugfs dir */ + sprintf(dname, "lmac%d", lmac_id); + rvu->rvu_dbg.lmac = + debugfs_create_dir(dname, rvu->rvu_dbg.cgx); + + pfile = debugfs_create_file("stats", 0600, + rvu->rvu_dbg.lmac, cgx, + &rvu_dbg_cgx_stat_fops); + if (!pfile) + goto create_failed; + } + } + return; + +create_failed: + dev_err(dev, "Failed to create debugfs dir/file for CGX\n"); + debugfs_remove_recursive(rvu->rvu_dbg.cgx_root); +} + +static void rvu_dbg_npa_init(struct rvu *rvu) +{ + const struct device *dev = &rvu->pdev->dev; + struct dentry *pfile; + + rvu->rvu_dbg.npa = debugfs_create_dir("npa", rvu->rvu_dbg.root); + if (!rvu->rvu_dbg.npa) + return; + + pfile = debugfs_create_file("qsize", 0600, rvu->rvu_dbg.npa, rvu, + &rvu_dbg_npa_qsize_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("aura_ctx", 0600, rvu->rvu_dbg.npa, rvu, + &rvu_dbg_npa_aura_ctx_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("pool_ctx", 0600, rvu->rvu_dbg.npa, rvu, + &rvu_dbg_npa_pool_ctx_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("ndc_cache", 0600, rvu->rvu_dbg.npa, rvu, + &rvu_dbg_npa_ndc_cache_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("ndc_hits_miss", 0600, rvu->rvu_dbg.npa, + rvu, &rvu_dbg_npa_ndc_hits_miss_fops); + if (!pfile) + goto create_failed; + + return; + +create_failed: + dev_err(dev, "Failed to create debugfs dir/file for NPA\n"); + debugfs_remove_recursive(rvu->rvu_dbg.npa); +} + +static int parse_sso_cmd_buffer(char *cmd_buf, size_t *count, + const char __user *buffer, int *ssolf, + bool *all) +{ + int ret, bytes_not_copied; + char *cmd_buf_tmp; + char *subtoken; + + bytes_not_copied = copy_from_user(cmd_buf, buffer, *count); + if (bytes_not_copied) + return -EFAULT; + + cmd_buf[*count] = '\0'; + cmd_buf_tmp = strchr(cmd_buf, '\n'); + + if (cmd_buf_tmp) { + *cmd_buf_tmp = '\0'; + *count = cmd_buf_tmp - cmd_buf + 1; + } + + subtoken = strsep(&cmd_buf, " "); + if (subtoken && strcmp(subtoken, "all") == 0) { + *all = true; + } else{ + ret = subtoken ? kstrtoint(subtoken, 10, ssolf) : -EINVAL; + if (ret < 0) + return ret; + } + if (cmd_buf) + return -EINVAL; + + return 0; +} + +static void sso_hwgrp_display_iq_list(struct rvu *rvu, int ssolf, u16 idx, + u16 tail_idx, u8 queue_type) +{ + const char *queue[3] = {"DQ", "CQ", "AQ"}; + int blkaddr; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return; + + pr_info("SSO HWGGRP[%d] [%s] Chain queue head[%d]", ssolf, + queue[queue_type], idx); + pr_info("SSO HWGGRP[%d] [%s] Chain queue tail[%d]", ssolf, + queue[queue_type], tail_idx); + pr_info("--------------------------------------------------\n"); + do { + reg = rvu_read64(rvu, blkaddr, SSO_AF_IENTX_TAG(idx)); + pr_info("SSO HWGGRP[%d] [%s] IE[%d] TAG 0x%llx\n", ssolf, + queue[queue_type], idx, reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_IENTX_GRP(idx)); + pr_info("SSO HWGGRP[%d] [%s] IE[%d] GRP 0x%llx\n", ssolf, + queue[queue_type], idx, reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_IENTX_PENDTAG(idx)); + pr_info("SSO HWGGRP[%d] [%s] IE[%d] PENDTAG 0x%llx\n", ssolf, + queue[queue_type], idx, reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_IENTX_LINKS(idx)); + pr_info("SSO HWGGRP[%d] [%s] IE[%d] LINKS 0x%llx\n", ssolf, + queue[queue_type], idx, reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_IENTX_QLINKS(idx)); + pr_info("SSO HWGGRP[%d] [%s] IE[%d] QLINKS 0x%llx\n", ssolf, + queue[queue_type], idx, reg); + pr_info("--------------------------------------------------\n"); + if (idx == tail_idx) + break; + idx = reg & 0x1FFF; + } while (idx != 0x1FFF); +} + +static void sso_hwgrp_display_taq_list(struct rvu *rvu, int ssolf, u8 wae_head, + u16 ent_head, u8 wae_used, u8 taq_lines) +{ + int i, blkaddr; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return; + + pr_info("--------------------------------------------------\n"); + do { + for (i = wae_head; i < taq_lines && wae_used; i++) { + reg = rvu_read64(rvu, blkaddr, + SSO_AF_TAQX_WAEY_TAG(ent_head, i)); + pr_info("SSO HWGGRP[%d] TAQ[%d] WAE[%d] TAG 0x%llx\n", + ssolf, ent_head, i, reg); + + reg = rvu_read64(rvu, blkaddr, + SSO_AF_TAQX_WAEY_WQP(ent_head, i)); + pr_info("SSO HWGGRP[%d] TAQ[%d] WAE[%d] WQP 0x%llx\n", + ssolf, ent_head, i, reg); + wae_used--; + } + + reg = rvu_read64(rvu, blkaddr, + SSO_AF_TAQX_LINK(ent_head)); + pr_info("SSO HWGGRP[%d] TAQ[%d] LINK 0x%llx\n", + ssolf, ent_head, reg); + ent_head = reg & 0x7FF; + pr_info("--------------------------------------------------\n"); + } while (ent_head && wae_used); +} + +static int read_sso_pc(struct rvu *rvu) +{ + int blkaddr; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return -ENODEV; + + reg = rvu_read64(rvu, blkaddr, SSO_AF_ACTIVE_CYCLES0); + pr_info("SSO Add-Work active cycles %lld\n", reg); + reg = rvu_read64(rvu, blkaddr, SSO_AF_ACTIVE_CYCLES1); + pr_info("SSO Get-Work active cycles %lld\n", reg); + reg = rvu_read64(rvu, blkaddr, SSO_AF_ACTIVE_CYCLES2); + pr_info("SSO Work-Slot active cycles %lld\n", reg); + pr_info("\n"); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_NOS_CNT) & 0x1FFF; + pr_info("SSO work-queue entries on the no-schedule list %lld\n", reg); + pr_info("\n"); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_AW_READ_ARB); + pr_info("SSO XAQ reads outstanding %lld\n", + (reg >> 24) & 0x3F); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_XAQ_REQ_PC); + pr_info("SSO XAQ reads requests %lld\n", reg); + reg = rvu_read64(rvu, blkaddr, SSO_AF_XAQ_LATENCY_PC); + pr_info("SSO XAQ read latency cycles %lld\n", reg); + pr_info("\n"); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_AW_WE); + pr_info("SSO IAQ reserved %lld\n", + (reg >> 16) & 0x3FFF); + pr_info("SSO IAQ total %lld\n", reg & 0x3FFF); + pr_info("\n"); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_TAQ_CNT); + pr_info("SSO TAQ reserved %lld\n", + (reg >> 16) & 0x7FF); + pr_info("SSO TAQ total %lld\n", reg & 0x7FF); + pr_info("\n"); + + return 0; +} + +/* Reads SSO hwgrp perfomance counters */ +static void read_sso_hwgrp_pc(struct rvu *rvu, int ssolf, bool all) +{ + struct rvu_hwinfo *hw = rvu->hw; + struct rvu_block *block; + int blkaddr, max_id; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return; + + block = &hw->block[blkaddr]; + if (ssolf < 0 || ssolf >= block->lf.max) { + pr_info("Invalid SSOLF(HWGRP), valid range is 0-%d\n", + block->lf.max - 1); + return; + } + max_id = block->lf.max; + + if (all) + ssolf = 0; + else + max_id = ssolf + 1; + + pr_info("==================================================\n"); + for (; ssolf < max_id; ssolf++) { + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_WS_PC(ssolf)); + pr_info("SSO HWGGRP[%d] Work-Schedule PC 0x%llx\n", ssolf, + reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_EXT_PC(ssolf)); + pr_info("SSO HWGGRP[%d] External Schedule PC 0x%llx\n", ssolf, + reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_WA_PC(ssolf)); + pr_info("SSO HWGGRP[%d] Work-Add PC 0x%llx\n", ssolf, + reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_TS_PC(ssolf)); + pr_info("SSO HWGGRP[%d] Tag Switch PC 0x%llx\n", ssolf, + reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_DS_PC(ssolf)); + pr_info("SSO HWGGRP[%d] Deschedule PC 0x%llx\n", ssolf, + reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_DQ_PC(ssolf)); + pr_info("SSO HWGGRP[%d] Work-Descheduled PC 0x%llx\n", ssolf, + reg); + + reg = rvu_read64(rvu, blkaddr, + SSO_AF_HWGRPX_PAGE_CNT(ssolf)); + pr_info("SSO HWGGRP[%d] In-use Page Count 0x%llx\n", ssolf, + reg); + pr_info("==================================================\n"); + } +} + +/* Reads SSO hwgrp Threshold */ +static void read_sso_hwgrp_thresh(struct rvu *rvu, int ssolf, bool all) +{ + struct rvu_hwinfo *hw = rvu->hw; + struct rvu_block *block; + int blkaddr, max_id; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return; + + block = &hw->block[blkaddr]; + if (ssolf < 0 || ssolf >= block->lf.max) { + pr_info("Invalid SSOLF(HWGRP), valid range is 0-%d\n", + block->lf.max - 1); + return; + } + max_id = block->lf.max; + + if (all) + ssolf = 0; + else + max_id = ssolf + 1; + + pr_info("==================================================\n"); + for (; ssolf < max_id; ssolf++) { + reg = rvu_read64(rvu, blkaddr, + SSO_AF_HWGRPX_IAQ_THR(ssolf)); + pr_info("SSO HWGGRP[%d] IAQ Threshold 0x%llx\n", ssolf, + reg); + + reg = rvu_read64(rvu, blkaddr, + SSO_AF_HWGRPX_TAQ_THR(ssolf)); + pr_info("SSO HWGGRP[%d] TAQ Threshold 0x%llx\n", ssolf, + reg); + + reg = rvu_read64(rvu, blkaddr, + SSO_AF_HWGRPX_XAQ_AURA(ssolf)); + pr_info("SSO HWGGRP[%d] XAQ Aura 0x%llx\n", ssolf, + reg); + + reg = rvu_read64(rvu, blkaddr, + SSO_AF_HWGRPX_XAQ_LIMIT(ssolf)); + pr_info("SSO HWGGRP[%d] XAQ Limit 0x%llx\n", ssolf, + reg); + + reg = rvu_read64(rvu, blkaddr, + SSO_AF_HWGRPX_IU_ACCNT(ssolf)); + pr_info("SSO HWGGRP[%d] IU Account Index 0x%llx\n", ssolf, + reg); + + reg = rvu_read64(rvu, blkaddr, + SSO_AF_IU_ACCNTX_CFG(reg & 0xFF)); + pr_info("SSO HWGGRP[%d] IU Accounting Cfg 0x%llx\n", ssolf, + reg); + pr_info("==================================================\n"); + } +} + +/* Reads SSO hwgrp TAQ list */ +static void read_sso_hwgrp_taq_list(struct rvu *rvu, int ssolf, bool all) +{ + struct rvu_hwinfo *hw = rvu->hw; + u8 taq_entries, wae_head; + struct rvu_block *block; + u16 ent_head, cl_used; + int blkaddr, max_id; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return; + + block = &hw->block[blkaddr]; + if (ssolf < 0 || ssolf >= block->lf.max) { + pr_info("Invalid SSOLF(HWGRP), valid range is 0-%d\n", + block->lf.max - 1); + return; + } + max_id = block->lf.max; + + if (all) + ssolf = 0; + else + max_id = ssolf + 1; + reg = rvu_read64(rvu, blkaddr, SSO_AF_CONST); + taq_entries = (reg >> 48) & 0xFF; + pr_info("==================================================\n"); + for (; ssolf < max_id; ssolf++) { + pr_info("++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + pr_info("SSO HWGGRP[%d] Transitory Output Admission Queue", + ssolf); + reg = rvu_read64(rvu, blkaddr, SSO_AF_TOAQX_STATUS(ssolf)); + pr_info("SSO HWGGRP[%d] TOAQ Status 0x%llx\n", ssolf, + reg); + ent_head = (reg >> 12) & 0x7FF; + cl_used = (reg >> 32) & 0x7FF; + if (reg & BIT_ULL(61) && cl_used) { + pr_info("SSO HWGGRP[%d] TOAQ CL_USED 0x%x\n", + ssolf, cl_used); + sso_hwgrp_display_taq_list(rvu, ssolf, ent_head, 0, + cl_used * taq_entries, + taq_entries); + } + pr_info("++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + pr_info("SSO HWGGRP[%d] Transitory Input Admission Queue", + ssolf); + reg = rvu_read64(rvu, blkaddr, SSO_AF_TIAQX_STATUS(ssolf)); + pr_info("SSO HWGGRP[%d] TIAQ Status 0x%llx\n", ssolf, + reg); + wae_head = (reg >> 60) & 0xF; + cl_used = (reg >> 32) & 0x7FFF; + ent_head = (reg >> 12) & 0x7FF; + if (reg & BIT_ULL(61) && cl_used) { + pr_info("SSO HWGGRP[%d] TIAQ WAE_USED 0x%x\n", + ssolf, cl_used); + sso_hwgrp_display_taq_list(rvu, ssolf, ent_head, + wae_head, cl_used, + taq_entries); + } + pr_info("++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + pr_info("==================================================\n"); + } +} + +/* Reads SSO hwgrp IAQ list */ +static void read_sso_hwgrp_iaq_list(struct rvu *rvu, int ssolf, bool all) +{ + struct rvu_hwinfo *hw = rvu->hw; + struct rvu_block *block; + u16 head_idx, tail_idx; + int blkaddr, max_id; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return; + + block = &hw->block[blkaddr]; + if (ssolf < 0 || ssolf >= block->lf.max) { + pr_info("Invalid SSOLF(HWGRP), valid range is 0-%d\n", + block->lf.max - 1); + return; + } + max_id = block->lf.max; + + if (all) + ssolf = 0; + else + max_id = ssolf + 1; + pr_info("==================================================\n"); + for (; ssolf < max_id; ssolf++) { + pr_info("++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + pr_info("SSO HWGGRP[%d] Deschedule Queue(DQ)\n", ssolf); + reg = rvu_read64(rvu, blkaddr, SSO_AF_IPL_DESCHEDX(ssolf)); + pr_info("SSO HWGGRP[%d] DQ List 0x%llx\n", ssolf, + reg); + head_idx = (reg >> 13) & 0x1FFF; + tail_idx = reg & 0x1FFF; + if (reg & (BIT_ULL(26) | BIT_ULL(27))) + sso_hwgrp_display_iq_list(rvu, ssolf, head_idx, + tail_idx, 0); + pr_info("++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + pr_info("SSO HWGGRP[%d] Conflict Queue(CQ)\n", ssolf); + reg = rvu_read64(rvu, blkaddr, SSO_AF_IPL_CONFX(ssolf)); + pr_info("SSO HWGGRP[%d] CQ List 0x%llx\n", ssolf, + reg); + head_idx = (reg >> 13) & 0x1FFF; + tail_idx = reg & 0x1FFF; + if (reg & (BIT_ULL(26) | BIT_ULL(27))) + sso_hwgrp_display_iq_list(rvu, ssolf, head_idx, + tail_idx, 1); + pr_info("++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + pr_info("SSO HWGGRP[%d] Admission Queue(AQ)\n", ssolf); + reg = rvu_read64(rvu, blkaddr, SSO_AF_IPL_IAQX(ssolf)); + pr_info("SSO HWGGRP[%d] AQ List 0x%llx\n", ssolf, + reg); + head_idx = (reg >> 13) & 0x1FFF; + tail_idx = reg & 0x1FFF; + if (reg & (BIT_ULL(26) | BIT_ULL(27))) + sso_hwgrp_display_iq_list(rvu, ssolf, head_idx, + tail_idx, 2); + pr_info("++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + pr_info("==================================================\n"); + } +} + +/* Reads SSO hwgrp IENT list */ +static int read_sso_hwgrp_ient_list(struct rvu *rvu) +{ + const char *tt_c[4] = {"SSO_TT_ORDERED_", "SSO_TT_ATOMIC__", + "SSO_TT_UNTAGGED", "SSO_TT_EMPTY___"}; + struct rvu_hwinfo *hw = rvu->hw; + int max_idx = hw->sso.sso_iue; + u64 pendtag, qlinks, links; + int len, idx, blkaddr; + u64 tag, grp, wqp; + char str[300]; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return -ENODEV; + + for (idx = 0; idx < max_idx; idx++) { + len = 0; + tag = rvu_read64(rvu, blkaddr, SSO_AF_IENTX_TAG(idx)); + grp = rvu_read64(rvu, blkaddr, SSO_AF_IENTX_GRP(idx)); + pendtag = rvu_read64(rvu, blkaddr, + SSO_AF_IENTX_PENDTAG(idx)); + links = rvu_read64(rvu, blkaddr, SSO_AF_IENTX_LINKS(idx)); + qlinks = rvu_read64(rvu, blkaddr, + SSO_AF_IENTX_QLINKS(idx)); + wqp = rvu_read64(rvu, blkaddr, SSO_AF_IENTX_WQP(idx)); + len = snprintf(str + len, 300, + "SSO IENT[%4d] TT [%s] HWGRP [%3lld] ", idx, + tt_c[(tag >> 32) & 0x3], (grp >> 48) & 0x1f); + len += snprintf(str + len, 300 - len, + "TAG [0x%010llx] GRP [0x%016llx] ", tag, grp); + len += snprintf(str + len, 300 - len, "PENDTAG [0x%010llx] ", + pendtag); + len += snprintf(str + len, 300 - len, + "LINKS [0x%016llx] QLINKS [0x%010llx] ", links, + qlinks); + snprintf(str + len, 300 - len, "WQP [0x%016llx]\n", wqp); + pr_info("%s", str); + } + + return 0; +} + +/* Reads SSO hwgrp free list */ +static int read_sso_hwgrp_free_list(struct rvu *rvu) +{ + int blkaddr; + u64 reg; + u8 idx; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return -ENODEV; + + pr_info("==================================================\n"); + for (idx = 0; idx < 4; idx++) { + reg = rvu_read64(rvu, blkaddr, SSO_AF_IPL_FREEX(idx)); + pr_info("SSO FREE LIST[%d]\n", idx); + pr_info("qnum_head : %lld qnum_tail : %lld\n", + (reg >> 58) & 0x3, (reg >> 56) & 0x3); + pr_info("queue_cnt : %llx\n", (reg >> 26) & 0x7fff); + pr_info("queue_val : %lld queue_head : %4lld queue_tail %4lld\n" + , (reg >> 40) & 0x1, (reg >> 13) & 0x1fff, + reg & 0x1fff); + pr_info("==================================================\n"); + } + + return 0; +} + +/* Reads SSO hwgrp perfomance counters */ +static void read_sso_hws_info(struct rvu *rvu, int ssowlf, bool all) +{ + struct rvu_hwinfo *hw = rvu->hw; + struct rvu_block *block; + int blkaddr; + int max_id; + u64 reg; + u8 mask; + u8 set; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSOW, 0); + if (blkaddr < 0) + return; + + block = &hw->block[blkaddr]; + if (ssowlf < 0 || ssowlf >= block->lf.max) { + pr_info("Invalid SSOWLF(HWS), valid range is 0-%d\n", + block->lf.max - 1); + return; + } + max_id = block->lf.max; + + if (all) + ssowlf = 0; + else + max_id = ssowlf + 1; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return; + + pr_info("==================================================\n"); + for (; ssowlf < max_id; ssowlf++) { + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWSX_ARB(ssowlf)); + pr_info("SSOW HWS[%d] Arbitration State 0x%llx\n", ssowlf, + reg); + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWSX_GMCTL(ssowlf)); + pr_info("SSOW HWS[%d] Guest Machine Control 0x%llx\n", ssowlf, + reg); + for (set = 0; set < 2; set++) + for (mask = 0; mask < 4; mask++) { + reg = rvu_read64(rvu, blkaddr, + SSO_AF_HWSX_SX_GRPMSKX(ssowlf, + set, + mask)); + pr_info( + "SSOW HWS[%d] SET[%d] Group Mask[%d] 0x%llx\n", + ssowlf, set, mask, reg); + } + pr_info("==================================================\n"); + } +} + +typedef void (*sso_dump_cb)(struct rvu *rvu, int ssolf, bool all); + +static ssize_t rvu_dbg_sso_cmd_parser(struct file *filp, + const char __user *buffer, size_t count, + loff_t *ppos, char *lf_type, + char *file_nm, sso_dump_cb fn) +{ + struct rvu *rvu = filp->private_data; + bool all = false; + char *cmd_buf; + int lf = 0; + + if ((*ppos != 0) || !count) + return -EINVAL; + + cmd_buf = kzalloc(count + 1, GFP_KERNEL); + if (!cmd_buf) + return -ENOSPC; + + if (parse_sso_cmd_buffer(cmd_buf, &count, buffer, + &lf, &all) < 0) { + pr_info("Usage: echo [<%s>/all] > %s\n", lf_type, file_nm); + } else { + fn(rvu, lf, all); + } + kfree(cmd_buf); + + return count; +} + +/* SSO debugfs APIs */ +static ssize_t rvu_dbg_sso_pc_display(struct file *filp, + char __user *buffer, + size_t count, loff_t *ppos) +{ + return read_sso_pc(filp->private_data); +} + +static ssize_t rvu_dbg_sso_hwgrp_pc_display(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_sso_cmd_parser(filp, buffer, count, ppos, "hwgrp", + "sso_hwgrp_pc", read_sso_hwgrp_pc); +} + +static ssize_t rvu_dbg_sso_hwgrp_thresh_display(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_sso_cmd_parser(filp, buffer, count, ppos, "hwgrp", + "sso_hwgrp_thresh", read_sso_hwgrp_thresh); +} + +static ssize_t rvu_dbg_sso_hwgrp_taq_wlk_display(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_sso_cmd_parser(filp, buffer, count, ppos, "hwgrp", + "sso_hwgrp_taq_wlk", read_sso_hwgrp_taq_list); +} + +static ssize_t rvu_dbg_sso_hwgrp_iaq_wlk_display(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_sso_cmd_parser(filp, buffer, count, ppos, "hwgrp", + "sso_hwgrp_iaq_wlk", read_sso_hwgrp_iaq_list); +} + +static ssize_t rvu_dbg_sso_hwgrp_ient_wlk_display(struct file *filp, + char __user *buffer, + size_t count, loff_t *ppos) +{ + return read_sso_hwgrp_ient_list(filp->private_data); +} + +static ssize_t rvu_dbg_sso_hwgrp_fl_wlk_display(struct file *filp, + char __user *buffer, + size_t count, loff_t *ppos) +{ + return read_sso_hwgrp_free_list(filp->private_data); +} + +static ssize_t rvu_dbg_sso_hws_info_display(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_sso_cmd_parser(filp, buffer, count, ppos, "hws", + "sso_hws_info", read_sso_hws_info); +} + +RVU_DEBUG_FOPS(sso_pc, sso_pc_display, NULL); +RVU_DEBUG_FOPS(sso_hwgrp_pc, NULL, sso_hwgrp_pc_display); +RVU_DEBUG_FOPS(sso_hwgrp_thresh, NULL, sso_hwgrp_thresh_display); +RVU_DEBUG_FOPS(sso_hwgrp_taq_wlk, NULL, sso_hwgrp_taq_wlk_display); +RVU_DEBUG_FOPS(sso_hwgrp_iaq_wlk, NULL, sso_hwgrp_iaq_wlk_display); +RVU_DEBUG_FOPS(sso_hwgrp_ient_wlk, sso_hwgrp_ient_wlk_display, NULL); +RVU_DEBUG_FOPS(sso_hwgrp_fl_wlk, sso_hwgrp_fl_wlk_display, NULL); +RVU_DEBUG_FOPS(sso_hws_info, NULL, sso_hws_info_display); + +static void rvu_dbg_sso_init(struct rvu *rvu) +{ + const struct device *dev = &rvu->pdev->dev; + struct dentry *pfile; + + rvu->rvu_dbg.sso = debugfs_create_dir("sso", rvu->rvu_dbg.root); + if (!rvu->rvu_dbg.sso) + return; + + rvu->rvu_dbg.sso_hwgrp = debugfs_create_dir("hwgrp", rvu->rvu_dbg.sso); + if (!rvu->rvu_dbg.sso_hwgrp) + return; + + rvu->rvu_dbg.sso_hws = debugfs_create_dir("hws", rvu->rvu_dbg.sso); + if (!rvu->rvu_dbg.sso_hws) + return; + + pfile = debugfs_create_file("sso_pc", 0600, + rvu->rvu_dbg.sso, rvu, + &rvu_dbg_sso_pc_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("sso_hwgrp_pc", 0600, + rvu->rvu_dbg.sso_hwgrp, rvu, + &rvu_dbg_sso_hwgrp_pc_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("sso_hwgrp_thresh", 0600, + rvu->rvu_dbg.sso_hwgrp, rvu, + &rvu_dbg_sso_hwgrp_thresh_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("sso_hwgrp_taq_walk", 0600, + rvu->rvu_dbg.sso_hwgrp, rvu, + &rvu_dbg_sso_hwgrp_taq_wlk_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("sso_hwgrp_iaq_walk", 0600, + rvu->rvu_dbg.sso_hwgrp, rvu, + &rvu_dbg_sso_hwgrp_iaq_wlk_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("sso_hwgrp_ient_walk", 0600, + rvu->rvu_dbg.sso_hwgrp, rvu, + &rvu_dbg_sso_hwgrp_ient_wlk_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("sso_hwgrp_free_list_walk", 0600, + rvu->rvu_dbg.sso_hwgrp, rvu, + &rvu_dbg_sso_hwgrp_fl_wlk_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("sso_hws_info", 0600, + rvu->rvu_dbg.sso_hws, rvu, + &rvu_dbg_sso_hws_info_fops); + if (!pfile) + goto create_failed; + + return; + +create_failed: + dev_err(dev, "Failed to create debugfs dir/file for SSO\n"); + debugfs_remove_recursive(rvu->rvu_dbg.sso); +} + +/* CPT debugfs APIs */ +static int parse_cpt_cmd_buffer(char *cmd_buf, size_t *count, + const char __user *buffer, char *e_type) +{ + int bytes_not_copied; + char *cmd_buf_tmp; + char *subtoken; + + bytes_not_copied = copy_from_user(cmd_buf, buffer, *count); + if (bytes_not_copied) + return -EFAULT; + + cmd_buf[*count] = '\0'; + cmd_buf_tmp = strchr(cmd_buf, '\n'); + + if (cmd_buf_tmp) { + *cmd_buf_tmp = '\0'; + *count = cmd_buf_tmp - cmd_buf + 1; + } + + subtoken = strsep(&cmd_buf, " "); + if (subtoken) + strcpy(e_type, subtoken); + else + return -EINVAL; + + if (cmd_buf) + return -EINVAL; + + if (strcmp(e_type, "SE") && strcmp(e_type, "IE") && + strcmp(e_type, "AE") && strcmp(e_type, "all")) + return -EINVAL; + + return 0; +} + +static ssize_t rvu_dbg_cpt_cmd_parser(struct file *filp, + const char __user *buffer, size_t count, + loff_t *ppos) +{ + struct seq_file *s = filp->private_data; + struct rvu *rvu = s->private; + char *cmd_buf; + int ret = 0; + + if ((*ppos != 0) || !count) + return -EINVAL; + + cmd_buf = kzalloc(count + 1, GFP_KERNEL); + if (!cmd_buf) + return -ENOSPC; + + if (parse_cpt_cmd_buffer(cmd_buf, &count, buffer, + rvu->rvu_dbg.cpt_ctx.e_type) < 0) + ret = -EINVAL; + + kfree(cmd_buf); + + if (ret) + return -EINVAL; + + return count; +} + +static ssize_t rvu_dbg_cpt_engines_sts_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_cpt_cmd_parser(filp, buffer, count, ppos); +} + +static int rvu_dbg_cpt_engines_sts_display(struct seq_file *filp, void *unused) +{ + u64 busy_sts[2] = {0}, free_sts[2] = {0}; + struct rvu *rvu = filp->private; + u16 max_ses, max_ies, max_aes; + u32 e_min = 0, e_max = 0, e; + int blkaddr; + char *e_type; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, 0); + if (blkaddr < 0) + return -ENODEV; + + reg = rvu_read64(rvu, blkaddr, CPT_AF_CONSTANTS1); + max_ses = reg & 0xffff; + max_ies = (reg >> 16) & 0xffff; + max_aes = (reg >> 32) & 0xffff; + + e_type = rvu->rvu_dbg.cpt_ctx.e_type; + + if (strcmp(e_type, "SE") == 0) { + e_min = 0; + e_max = max_ses - 1; + } else if (strcmp(e_type, "IE") == 0) { + e_min = max_ses; + e_max = max_ses + max_ies - 1; + } else if (strcmp(e_type, "AE") == 0) { + e_min = max_ses + max_ies; + e_max = max_ses + max_ies + max_aes - 1; + } else if (strcmp(e_type, "all") == 0) { + e_min = 0; + e_max = max_ses + max_ies + max_aes - 1; + } else { + return -EINVAL; + } + + for (e = e_min; e <= e_max; e++) { + reg = rvu_read64(rvu, blkaddr, CPT_AF_EXEX_STS(e)); + if (reg & 0x1) { + if (e < max_ses) + busy_sts[0] |= 1ULL << e; + else if (e >= max_ses) + busy_sts[1] |= 1ULL << (e - max_ses); + } + if (reg & 0x2) { + if (e < max_ses) + free_sts[0] |= 1ULL << e; + else if (e >= max_ses) + free_sts[1] |= 1ULL << (e - max_ses); + } + } + seq_printf(filp, "FREE STS : 0x%016llx 0x%016llx\n", free_sts[1], + free_sts[0]); + seq_printf(filp, "BUSY STS : 0x%016llx 0x%016llx\n", busy_sts[1], + busy_sts[0]); + + return 0; +} + +RVU_DEBUG_SEQ_FOPS(cpt_engines_sts, cpt_engines_sts_display, + cpt_engines_sts_write); + +static ssize_t rvu_dbg_cpt_engines_info_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + return rvu_dbg_cpt_cmd_parser(filp, buffer, count, ppos); +} + +static int rvu_dbg_cpt_engines_info_display(struct seq_file *filp, void *unused) +{ + struct rvu *rvu = filp->private; + u16 max_ses, max_ies, max_aes; + u32 e_min, e_max, e; + int blkaddr; + char *e_type; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, 0); + if (blkaddr < 0) + return -ENODEV; + + reg = rvu_read64(rvu, blkaddr, CPT_AF_CONSTANTS1); + max_ses = reg & 0xffff; + max_ies = (reg >> 16) & 0xffff; + max_aes = (reg >> 32) & 0xffff; + + e_type = rvu->rvu_dbg.cpt_ctx.e_type; + + if (strcmp(e_type, "SE") == 0) { + e_min = 0; + e_max = max_ses - 1; + } else if (strcmp(e_type, "IE") == 0) { + e_min = max_ses; + e_max = max_ses + max_ies - 1; + } else if (strcmp(e_type, "AE") == 0) { + e_min = max_ses + max_ies; + e_max = max_ses + max_ies + max_aes - 1; + } else if (strcmp(e_type, "all") == 0) { + e_min = 0; + e_max = max_ses + max_ies + max_aes - 1; + } else { + return -EINVAL; + } + + seq_puts(filp, "===========================================\n"); + for (e = e_min; e <= e_max; e++) { + reg = rvu_read64(rvu, blkaddr, CPT_AF_EXEX_CTL2(e)); + seq_printf(filp, "CPT Engine[%u] Group Enable 0x%02llx\n", e, + reg & 0xff); + reg = rvu_read64(rvu, blkaddr, CPT_AF_EXEX_ACTIVE(e)); + seq_printf(filp, "CPT Engine[%u] Active Info 0x%llx\n", e, + reg); + reg = rvu_read64(rvu, blkaddr, CPT_AF_EXEX_CTL(e)); + seq_printf(filp, "CPT Engine[%u] Control 0x%llx\n", e, + reg); + seq_puts(filp, "===========================================\n"); + } + return 0; +} + +RVU_DEBUG_SEQ_FOPS(cpt_engines_info, cpt_engines_info_display, + cpt_engines_info_write); + +static int rvu_dbg_cpt_lfs_info_display(struct seq_file *filp, void *unused) +{ + struct rvu *rvu = filp->private; + struct rvu_hwinfo *hw = rvu->hw; + struct rvu_block *block; + int blkaddr; + u64 reg; + u32 lf; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, 0); + if (blkaddr < 0) + return -ENODEV; + + block = &hw->block[blkaddr]; + if (!block->lf.bmap) + return -ENODEV; + + seq_puts(filp, "===========================================\n"); + for (lf = 0; lf < block->lf.max; lf++) { + reg = rvu_read64(rvu, blkaddr, CPT_AF_LFX_CTL(lf)); + seq_printf(filp, "CPT Lf[%u] CTL 0x%llx\n", lf, reg); + reg = rvu_read64(rvu, blkaddr, CPT_AF_LFX_CTL2(lf)); + seq_printf(filp, "CPT Lf[%u] CTL2 0x%llx\n", lf, reg); + reg = rvu_read64(rvu, blkaddr, CPT_AF_LFX_PTR_CTL(lf)); + seq_printf(filp, "CPT Lf[%u] PTR_CTL 0x%llx\n", lf, reg); + reg = rvu_read64(rvu, blkaddr, block->lfcfg_reg | + (lf << block->lfshift)); + seq_printf(filp, "CPT Lf[%u] CFG 0x%llx\n", lf, reg); + seq_puts(filp, "===========================================\n"); + } + return 0; +} + +RVU_DEBUG_SEQ_FOPS(cpt_lfs_info, cpt_lfs_info_display, NULL); + +static int rvu_dbg_cpt_err_info_display(struct seq_file *filp, void *unused) +{ + struct rvu *rvu = filp->private; + u64 reg0, reg1; + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, 0); + if (blkaddr < 0) + return -ENODEV; + + reg0 = rvu_read64(rvu, blkaddr, CPT_AF_FLTX_INT(0)); + reg1 = rvu_read64(rvu, blkaddr, CPT_AF_FLTX_INT(1)); + seq_printf(filp, "CPT_AF_FLTX_INT: 0x%llx 0x%llx\n", reg0, reg1); + reg0 = rvu_read64(rvu, blkaddr, CPT_AF_PSNX_EXE(0)); + reg1 = rvu_read64(rvu, blkaddr, CPT_AF_PSNX_EXE(1)); + seq_printf(filp, "CPT_AF_PSNX_EXE: 0x%llx 0x%llx\n", reg0, reg1); + reg0 = rvu_read64(rvu, blkaddr, CPT_AF_PSNX_LF(0)); + seq_printf(filp, "CPT_AF_PSNX_LF: 0x%llx\n", reg0); + reg0 = rvu_read64(rvu, blkaddr, CPT_AF_RVU_INT); + seq_printf(filp, "CPT_AF_RVU_INT: 0x%llx\n", reg0); + reg0 = rvu_read64(rvu, blkaddr, CPT_AF_RAS_INT); + seq_printf(filp, "CPT_AF_RAS_INT: 0x%llx\n", reg0); + reg0 = rvu_read64(rvu, blkaddr, CPT_AF_EXE_ERR_INFO); + seq_printf(filp, "CPT_AF_EXE_ERR_INFO: 0x%llx\n", reg0); + + return 0; +} + +RVU_DEBUG_SEQ_FOPS(cpt_err_info, cpt_err_info_display, NULL); + +static int rvu_dbg_cpt_pc_display(struct seq_file *filp, void *unused) +{ + struct rvu *rvu; + int blkaddr; + u64 reg; + + rvu = filp->private; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_CPT, 0); + if (blkaddr < 0) + return -ENODEV; + + reg = rvu_read64(rvu, blkaddr, CPT_AF_INST_REQ_PC); + seq_printf(filp, "CPT instruction requests %llu\n", reg); + reg = rvu_read64(rvu, blkaddr, CPT_AF_INST_LATENCY_PC); + seq_printf(filp, "CPT instruction latency %llu\n", reg); + reg = rvu_read64(rvu, blkaddr, CPT_AF_RD_REQ_PC); + seq_printf(filp, "CPT NCB read requests %llu\n", reg); + reg = rvu_read64(rvu, blkaddr, CPT_AF_RD_LATENCY_PC); + seq_printf(filp, "CPT NCB read latency %llu\n", reg); + reg = rvu_read64(rvu, blkaddr, CPT_AF_RD_UC_PC); + seq_printf(filp, "CPT read requests caused by UC fills %llu\n", reg); + reg = rvu_read64(rvu, blkaddr, CPT_AF_ACTIVE_CYCLES_PC); + seq_printf(filp, "CPT active cycles pc %llu\n", reg); + reg = rvu_read64(rvu, blkaddr, CPT_AF_CPTCLK_CNT); + seq_printf(filp, "CPT clock count pc %llu\n", reg); + + return 0; +} + +RVU_DEBUG_SEQ_FOPS(cpt_pc, cpt_pc_display, NULL); + +static void rvu_dbg_cpt_init(struct rvu *rvu) +{ + const struct device *dev = &rvu->pdev->dev; + struct dentry *pfile; + + rvu->rvu_dbg.cpt = debugfs_create_dir("cpt", rvu->rvu_dbg.root); + if (!rvu->rvu_dbg.cpt) + return; + + pfile = debugfs_create_file("cpt_pc", 0600, + rvu->rvu_dbg.cpt, rvu, + &rvu_dbg_cpt_pc_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("cpt_engines_sts", 0600, + rvu->rvu_dbg.cpt, rvu, + &rvu_dbg_cpt_engines_sts_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("cpt_engines_info", 0600, + rvu->rvu_dbg.cpt, rvu, + &rvu_dbg_cpt_engines_info_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("cpt_lfs_info", 0600, + rvu->rvu_dbg.cpt, rvu, + &rvu_dbg_cpt_lfs_info_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("cpt_err_info", 0600, + rvu->rvu_dbg.cpt, rvu, + &rvu_dbg_cpt_err_info_fops); + if (!pfile) + goto create_failed; + + return; + +create_failed: + dev_err(dev, "Failed to create debugfs dir/file for CPT\n"); + debugfs_remove_recursive(rvu->rvu_dbg.cpt); +} + +void rvu_dbg_init(struct rvu *rvu) +{ + struct device *dev = &rvu->pdev->dev; + struct dentry *pfile; + + rvu->rvu_dbg.root = debugfs_create_dir(DEBUGFS_DIR_NAME, NULL); + if (!rvu->rvu_dbg.root) { + dev_err(rvu->dev, "%s failed\n", __func__); + return; + } + pfile = debugfs_create_file("rsrc_alloc", 0444, rvu->rvu_dbg.root, rvu, + &rvu_dbg_rsrc_status_fops); + if (!pfile) + goto create_failed; + + rvu_dbg_npa_init(rvu); + rvu_dbg_cgx_init(rvu); + rvu_dbg_nix_init(rvu); + rvu_dbg_npc_init(rvu); + rvu_dbg_sso_init(rvu); + + if (is_block_implemented(rvu->hw, BLKADDR_CPT0)) + rvu_dbg_cpt_init(rvu); + + return; + +create_failed: + dev_err(dev, "Failed to create debugfs dir\n"); + debugfs_remove_recursive(rvu->rvu_dbg.root); +} + +void rvu_dbg_exit(struct rvu *rvu) +{ + debugfs_remove_recursive(rvu->rvu_dbg.root); +} + +#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_fixes.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_fixes.c new file mode 100644 index 000000000000..5dac149dd1e0 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_fixes.c @@ -0,0 +1,1013 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Admin Function driver + * + * Copyright (C) 2019 Marvell International Ltd. + * + * 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/kthread.h> +#include <linux/pci.h> +#include <linux/cpu.h> +#include <linux/sched/signal.h> + +#include "rvu_struct.h" +#include "rvu_reg.h" +#include "common.h" +#include "mbox.h" +#include "rvu.h" +#include "cgx.h" +#include "rvu_fixes.h" + +#define OTX2_MAX_CQ_CNT 64 + +struct nix_tx_stall { + struct rvu *rvu; + int blkaddr; + int smq_count; + int tl4_count; + int tl3_count; + int tl2_count; + int sq_count; + u16 *smq_tl2_map; + u16 *tl4_tl2_map; + u16 *tl3_tl2_map; + u16 *tl2_tl1_map; + u16 *sq_smq_map; +#define LINK_TYPE_SHIFT 7 +#define EXPR_LINK(map) (map & (1 << LINK_TYPE_SHIFT)) +#define LINK_CHAN_SHIFT 8 +#define LINK_CHAN(map) (map >> LINK_CHAN_SHIFT) + u16 *tl2_link_map; + u8 *nixlf_tl2_count; + u64 *nixlf_poll_count; + u64 *nixlf_stall_count; + u64 *nlink_credits; /* Normal link credits */ + u64 poll_cntr; + u64 stalled_cntr; + int pse_link_bp_level; + bool txsch_config_changed; + struct mutex txsch_lock; /* To sync Tx SCHQ config update and poll */ + struct task_struct *poll_thread; /* Tx stall condition polling thread */ +}; + +/* Tranmsit stall hw issue's workaround reads loads of registers + * at frequent intervals, having barrier for every register access + * will increase the cycles spent in stall detection. Hence using + * relaxed counterparts. + */ +static inline void rvu_wr64(struct rvu *rvu, u64 block, u64 offset, u64 val) +{ + writeq_relaxed(val, rvu->afreg_base + ((block << 28) | offset)); +} + +static inline u64 rvu_rd64(struct rvu *rvu, u64 block, u64 offset) +{ + return readq_relaxed(rvu->afreg_base + ((block << 28) | offset)); +} + +/** + * rvu_usleep_interruptible - sleep waiting for signals + * @usecs: Time in microseconds to sleep for + * + * A replica of msleep_interruptable to reduce tx stall + * poll interval. + */ +static unsigned long rvu_usleep_interruptible(unsigned int usecs) +{ + unsigned long timeout = usecs_to_jiffies(usecs) + 1; + + while (timeout && !signal_pending(current)) + timeout = schedule_timeout_interruptible(timeout); + return jiffies_to_usecs(timeout); +} + +void rvu_nix_txsch_lock(struct nix_hw *nix_hw) +{ + struct nix_tx_stall *tx_stall = nix_hw->tx_stall; + + if (tx_stall) + mutex_lock(&tx_stall->txsch_lock); +} + +void rvu_nix_txsch_unlock(struct nix_hw *nix_hw) +{ + struct nix_tx_stall *tx_stall = nix_hw->tx_stall; + + if (tx_stall) + mutex_unlock(&tx_stall->txsch_lock); +} + +void rvu_nix_txsch_config_changed(struct nix_hw *nix_hw) +{ + struct nix_tx_stall *tx_stall = nix_hw->tx_stall; + + if (tx_stall) + tx_stall->txsch_config_changed = true; +} + +static inline struct nix_hw *get_nix_hw(struct rvu_hwinfo *hw, int blkaddr) +{ + if (blkaddr == BLKADDR_NIX0 && hw->nix0) + return hw->nix0; + + return NULL; +} + +void rvu_nix_update_link_credits(struct rvu *rvu, int blkaddr, + int link, u64 ncredits) +{ + struct nix_tx_stall *tx_stall; + struct nix_hw *nix_hw; + + nix_hw = get_nix_hw(rvu->hw, blkaddr); + if (!nix_hw) + return; + + tx_stall = nix_hw->tx_stall; + if (!tx_stall) + return; + + rvu_nix_txsch_lock(nix_hw); + tx_stall->nlink_credits[link] = ncredits; + rvu_nix_txsch_unlock(nix_hw); +} + +void rvu_nix_update_sq_smq_mapping(struct rvu *rvu, int blkaddr, int nixlf, + u16 sq, u16 smq) +{ + struct nix_tx_stall *tx_stall; + struct nix_hw *nix_hw; + int sq_count; + + nix_hw = get_nix_hw(rvu->hw, blkaddr); + if (!nix_hw) + return; + + tx_stall = nix_hw->tx_stall; + if (!tx_stall) + return; + + sq_count = tx_stall->sq_count; + + rvu_nix_txsch_lock(nix_hw); + tx_stall->sq_smq_map[nixlf * sq_count + sq] = smq; + rvu_nix_txsch_unlock(nix_hw); +} + +static void rvu_nix_scan_link_credits(struct rvu *rvu, int blkaddr, + struct nix_tx_stall *tx_stall) +{ + struct rvu_hwinfo *hw = rvu->hw; + u64 credits; + int link; + + for (link = 0; link < (hw->cgx_links + hw->lbk_links); link++) { + credits = rvu_rd64(rvu, blkaddr, + NIX_AF_TX_LINKX_NORM_CREDIT(link)); + tx_stall->nlink_credits[link] = credits; + } +} + +static void rvu_nix_scan_tl2_link_mapping(struct rvu *rvu, + struct nix_tx_stall *tx_stall, + int blkaddr, int tl2, int smq) +{ + struct rvu_hwinfo *hw = rvu->hw; + int link, chan; + u64 link_cfg; + + for (link = 0; link < (hw->cgx_links + hw->lbk_links); link++) { + link_cfg = rvu_rd64(rvu, blkaddr, + NIX_AF_TL3_TL2X_LINKX_CFG(tl2, link)); + if (!(link_cfg & BIT_ULL(12))) + continue; + + /* Get channel of the LINK to which this TL2 is transmitting */ + chan = link_cfg & 0x3F; + tx_stall->tl2_link_map[tl2] = chan << LINK_CHAN_SHIFT; + + /* Save link info */ + tx_stall->tl2_link_map[tl2] |= (link & 0x7F); + + /* Workaround assumes TL2 transmits to only one link. + * So assume the first link enabled is the only one. + */ + break; + } +} + +static bool is_sq_allocated(struct rvu *rvu, struct rvu_pfvf *pfvf, + int blkaddr, int sq) +{ + struct rvu_hwinfo *hw = rvu->hw; + struct rvu_block *block; + struct admin_queue *aq; + + block = &hw->block[blkaddr]; + aq = block->aq; + spin_lock(&aq->lock); + if (test_bit(sq, pfvf->sq_bmap)) { + spin_unlock(&aq->lock); + return true; + } + spin_unlock(&aq->lock); + return false; +} + +static bool is_schq_allocated(struct rvu *rvu, struct nix_hw *nix_hw, + int lvl, int schq) +{ + struct nix_txsch *txsch = &nix_hw->txsch[lvl]; + + mutex_lock(&rvu->rsrc_lock); + if (test_bit(schq, txsch->schq.bmap)) { + mutex_unlock(&rvu->rsrc_lock); + return true; + } + mutex_unlock(&rvu->rsrc_lock); + return false; +} + +static bool is_sw_xoff_set(struct rvu *rvu, int blkaddr, int lvl, int schq) +{ + u64 cfg, swxoff_reg = 0x00; + + switch (lvl) { + case NIX_TXSCH_LVL_MDQ: + swxoff_reg = NIX_AF_MDQX_SW_XOFF(schq); + break; + case NIX_TXSCH_LVL_TL4: + swxoff_reg = NIX_AF_TL4X_SW_XOFF(schq); + break; + case NIX_TXSCH_LVL_TL3: + swxoff_reg = NIX_AF_TL3X_SW_XOFF(schq); + break; + case NIX_TXSCH_LVL_TL2: + swxoff_reg = NIX_AF_TL2X_SW_XOFF(schq); + break; + case NIX_TXSCH_LVL_TL1: + swxoff_reg = NIX_AF_TL1X_SW_XOFF(schq); + break; + } + if (!swxoff_reg) + return false; + + cfg = rvu_rd64(rvu, blkaddr, swxoff_reg); + if (cfg & BIT_ULL(0)) + return true; + + return false; +} + +static void rvu_nix_scan_txsch_hierarchy(struct rvu *rvu, + struct nix_hw *nix_hw, int blkaddr) +{ + struct nix_tx_stall *tx_stall = nix_hw->tx_stall; + struct rvu_hwinfo *hw = rvu->hw; + struct nix_txsch *tl2_txsch; + struct rvu_block *block; + int tl4, tl3, tl2, tl1; + int lf, smq, size; + u16 pcifunc; + u64 cfg; + + /* Clear previous mappings */ + size = sizeof(u16); + memset(tx_stall->smq_tl2_map, U16_MAX, tx_stall->smq_count * size); + memset(tx_stall->tl4_tl2_map, U16_MAX, tx_stall->tl4_count * size); + memset(tx_stall->tl3_tl2_map, U16_MAX, tx_stall->tl3_count * size); + memset(tx_stall->tl2_tl1_map, U16_MAX, tx_stall->tl2_count * size); + memset(tx_stall->tl2_link_map, U16_MAX, tx_stall->tl2_count * size); + + for (smq = 0; smq < tx_stall->smq_count; smq++) { + /* Skip SMQ if it's not assigned to any */ + if (!is_schq_allocated(rvu, nix_hw, NIX_TXSCH_LVL_SMQ, smq)) + continue; + + /* If SW_XOFF is set, ignore the scheduler queue */ + cfg = rvu_rd64(rvu, blkaddr, NIX_AF_SMQX_CFG(smq)); + if (cfg & BIT_ULL(50)) + continue; + if (is_sw_xoff_set(rvu, blkaddr, NIX_TXSCH_LVL_MDQ, smq)) + continue; + + cfg = rvu_rd64(rvu, blkaddr, NIX_AF_MDQX_PARENT(smq)); + tl4 = (cfg >> 16) & 0x1FF; + if (is_sw_xoff_set(rvu, blkaddr, NIX_TXSCH_LVL_TL4, tl4)) + continue; + + cfg = rvu_rd64(rvu, blkaddr, NIX_AF_TL4X_PARENT(tl4)); + tl3 = (cfg >> 16) & 0x1FF; + if (is_sw_xoff_set(rvu, blkaddr, NIX_TXSCH_LVL_TL3, tl3)) + continue; + + cfg = rvu_rd64(rvu, blkaddr, NIX_AF_TL3X_PARENT(tl3)); + tl2 = (cfg >> 16) & 0x1FF; + if (is_sw_xoff_set(rvu, blkaddr, NIX_TXSCH_LVL_TL2, tl2)) + continue; + + cfg = rvu_rd64(rvu, blkaddr, NIX_AF_TL2X_PARENT(tl2)); + tl1 = (cfg >> 16) & 0x1FF; + if (is_sw_xoff_set(rvu, blkaddr, NIX_TXSCH_LVL_TL1, tl1)) + continue; + + tx_stall->smq_tl2_map[smq] = tl2; + tx_stall->tl4_tl2_map[tl4] = tl2; + tx_stall->tl3_tl2_map[tl3] = tl2; + tx_stall->tl2_tl1_map[tl2] = tl1; + rvu_nix_scan_tl2_link_mapping(rvu, tx_stall, blkaddr, tl2, smq); + } + + /* Get count of TL2s attached to each NIXLF */ + tl2_txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL2]; + block = &hw->block[blkaddr]; + memset(tx_stall->nixlf_tl2_count, 0, block->lf.max * sizeof(u8)); + for (lf = 0; lf < block->lf.max; lf++) { + mutex_lock(&rvu->rsrc_lock); + if (!test_bit(lf, block->lf.bmap)) { + mutex_unlock(&rvu->rsrc_lock); + continue; + } + pcifunc = block->fn_map[lf]; + mutex_unlock(&rvu->rsrc_lock); + + for (tl2 = 0; tl2 < tx_stall->tl2_count; tl2++) { + if (!is_schq_allocated(rvu, nix_hw, + NIX_TXSCH_LVL_TL2, tl2)) + continue; + if (pcifunc == TXSCH_MAP_FUNC(tl2_txsch->pfvf_map[tl2])) + tx_stall->nixlf_tl2_count[lf]++; + } + } +} + +#define TX_OCTS 4 +#define RVU_AF_BAR2_SEL (0x9000000ull) +#define RVU_AF_BAR2_ALIASX(a, b) (0x9100000ull | (a) << 12 | (b)) +#define NIX_LF_SQ_OP_OCTS (0xa10) + +static bool is_sq_stalled(struct rvu *rvu, struct nix_hw *nix_hw, int smq) +{ + struct nix_tx_stall *tx_stall = nix_hw->tx_stall; + u64 btx_octs, atx_octs, cfg, incr; + int sq_count = tx_stall->sq_count; + struct rvu_hwinfo *hw = rvu->hw; + int blkaddr = tx_stall->blkaddr; + struct nix_txsch *smq_txsch; + struct rvu_pfvf *pfvf; + atomic64_t *ptr; + int nixlf, sq; + u16 pcifunc; + + smq_txsch = &nix_hw->txsch[NIX_TXSCH_LVL_SMQ]; + pcifunc = TXSCH_MAP_FUNC(smq_txsch->pfvf_map[smq]); + nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0); + if (nixlf < 0) + return false; + + /* If a NIXLF is transmitting pkts via only one TL2, then checking + * global NIXLF TX stats is sufficient. + */ + if (tx_stall->nixlf_tl2_count[nixlf] != 1) + goto poll_sq_stats; + + tx_stall->nixlf_poll_count[nixlf]++; + btx_octs = rvu_rd64(rvu, blkaddr, NIX_AF_LFX_TX_STATX(nixlf, TX_OCTS)); + usleep_range(50, 60); + atx_octs = rvu_rd64(rvu, blkaddr, NIX_AF_LFX_TX_STATX(nixlf, TX_OCTS)); + if (btx_octs == atx_octs) { + tx_stall->nixlf_stall_count[nixlf]++; + return true; + } + return false; + +poll_sq_stats: + if (!tx_stall->nixlf_tl2_count[nixlf]) + return false; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + + /* Enable BAR2 register access from AF BAR2 alias registers*/ + cfg = BIT_ULL(16) | pcifunc; + rvu_wr64(rvu, blkaddr, RVU_AF_BAR2_SEL, cfg); + + for (sq = 0; sq < pfvf->sq_ctx->qsize; sq++) { + if (!is_sq_allocated(rvu, pfvf, blkaddr, sq)) + continue; + + rvu_nix_txsch_lock(nix_hw); + if (tx_stall->sq_smq_map[nixlf * sq_count + sq] != smq) { + rvu_nix_txsch_unlock(nix_hw); + continue; + } + rvu_nix_txsch_unlock(nix_hw); + + incr = (u64)sq << 32; + ptr = (__force atomic64_t *)(rvu->afreg_base + ((blkaddr << 28) + | RVU_AF_BAR2_ALIASX(nixlf, NIX_LF_SQ_OP_OCTS))); + + btx_octs = atomic64_fetch_add_relaxed(incr, ptr); + usleep_range(50, 60); + atx_octs = atomic64_fetch_add_relaxed(incr, ptr); + /* If atleast one SQ is transmitting pkts then SMQ is + * not stalled. + */ + if (btx_octs != atx_octs) + return false; + } + tx_stall->nixlf_stall_count[nixlf]++; + + return true; +} + +static bool rvu_nix_check_smq_stall(struct rvu *rvu, struct nix_hw *nix_hw, + int tl2) +{ + struct nix_tx_stall *tx_stall = nix_hw->tx_stall; + int blkaddr = tx_stall->blkaddr; + u64 mdesc_cnt; + int smq; + + for (smq = 0; smq < tx_stall->smq_count; smq++) { + if (tx_stall->smq_tl2_map[smq] != tl2) + continue; + + mdesc_cnt = rvu_rd64(rvu, blkaddr, NIX_AF_SMQX_STATUS(smq)); + if (!(mdesc_cnt & 0x7F)) + continue; + if (is_sq_stalled(rvu, nix_hw, smq)) + return true; + } + return false; +} + +static bool is_cgx_idle(u64 status, u8 link_map) +{ + if (EXPR_LINK(link_map)) + return status & CGXX_CMRX_TX_LMAC_E_IDLE; + return status & CGXX_CMRX_TX_LMAC_IDLE; +} + +static bool rvu_cgx_tx_idle(struct rvu *rvu, struct nix_hw *nix_hw, + struct nix_txsch *tl2_txsch, int tl2) +{ + unsigned long timeout = jiffies + usecs_to_jiffies(20); + struct nix_tx_stall *tx_stall = nix_hw->tx_stall; + u16 pcifunc, link_map; + u8 cgx_id, lmac_id; + u64 status; + void *cgxd; + int pf; + + pcifunc = TXSCH_MAP_FUNC(tl2_txsch->pfvf_map[tl2]); + pf = rvu_get_pf(pcifunc); + if (!is_pf_cgxmapped(rvu, pf)) + return false; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + if (!cgxd) + return false; + + link_map = tx_stall->tl2_link_map[tl2]; + + /* Wait for LMAC TX_IDLE */ + while (time_before(jiffies, timeout)) { + status = cgx_get_lmac_tx_fifo_status(cgxd, lmac_id); + if (is_cgx_idle(status, link_map)) + return true; + usleep_range(1, 2); + } + return false; +} + +static void rvu_nix_restore_tx(struct rvu *rvu, struct nix_hw *nix_hw, + int blkaddr, int tl2) +{ + struct nix_tx_stall *tx_stall = nix_hw->tx_stall; + struct nix_txsch *tl2_txsch; + int tl, link; + + link = tx_stall->tl2_link_map[tl2] & 0x7F; + + tx_stall->stalled_cntr++; + + tl2_txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL2]; + rvu_nix_txsch_lock(nix_hw); + + /* Set SW_XOFF for every TL2 queue which transmits to + * the associated link. + */ + for (tl = 0; tl < tx_stall->tl2_count; tl++) { + if ((tx_stall->tl2_link_map[tl] & 0x7F) != link) + continue; + /* Full workaround is implemented assuming fixed 1:1 + * TL3:TL2 mapping, ie TL3 and TL2 index can be used + * interchangeably. Hence except in this API, no other + * place we check for PSE backpressure level configured + * in NIX_AF_PSE_CHANNEL_LEVEL reg. + */ + if (tx_stall->pse_link_bp_level == NIX_TXSCH_LVL_TL2) + rvu_wr64(rvu, blkaddr, + NIX_AF_TL2X_SW_XOFF(tl), BIT_ULL(0)); + else + rvu_wr64(rvu, blkaddr, + NIX_AF_TL3X_SW_XOFF(tl), BIT_ULL(0)); + } + usleep_range(20, 25); + + /* Wait for LMAC TX_IDLE */ + if (link < rvu->hw->cgx_links) { + if (!rvu_cgx_tx_idle(rvu, nix_hw, tl2_txsch, tl2)) + goto clear_sw_xoff; + } + + /* Restore link credits */ + rvu_wr64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link), + tx_stall->nlink_credits[link]); + + /* Toggle SW_XOFF of every scheduler queue at every level + * which points to this TL2. + */ + for (tl = 0; tl < tx_stall->smq_count; tl++) { + if (tx_stall->smq_tl2_map[tl] != tl2) + continue; + rvu_wr64(rvu, blkaddr, NIX_AF_MDQX_SW_XOFF(tl), BIT_ULL(0)); + rvu_wr64(rvu, blkaddr, NIX_AF_MDQX_SW_XOFF(tl), 0x00); + } + + for (tl = 0; tl < tx_stall->tl4_count; tl++) { + if (tx_stall->tl4_tl2_map[tl] != tl2) + continue; + rvu_wr64(rvu, blkaddr, NIX_AF_TL4X_SW_XOFF(tl), BIT_ULL(0)); + rvu_wr64(rvu, blkaddr, NIX_AF_TL4X_SW_XOFF(tl), 0x00); + } + + for (tl = 0; tl < tx_stall->tl3_count; tl++) { + if (tx_stall->tl3_tl2_map[tl] != tl2) + continue; + if (tx_stall->pse_link_bp_level == NIX_TXSCH_LVL_TL2) { + rvu_wr64(rvu, blkaddr, + NIX_AF_TL3X_SW_XOFF(tl), BIT_ULL(0)); + rvu_wr64(rvu, blkaddr, NIX_AF_TL3X_SW_XOFF(tl), 0x00); + } else { + /* TL3 and TL2 indices used by this NIXLF are same */ + rvu_wr64(rvu, blkaddr, + NIX_AF_TL2X_SW_XOFF(tl), BIT_ULL(0)); + rvu_wr64(rvu, blkaddr, NIX_AF_TL2X_SW_XOFF(tl), 0x00); + } + } + +clear_sw_xoff: + /* Clear SW_XOFF of all TL2 queues, which are set above */ + for (tl = 0; tl < tx_stall->tl2_count; tl++) { + if ((tx_stall->tl2_link_map[tl] & 0x7F) != link) + continue; + if (tx_stall->pse_link_bp_level == NIX_TXSCH_LVL_TL2) + rvu_wr64(rvu, blkaddr, NIX_AF_TL2X_SW_XOFF(tl), 0x00); + else + rvu_wr64(rvu, blkaddr, NIX_AF_TL3X_SW_XOFF(tl), 0x00); + } + rvu_nix_txsch_unlock(nix_hw); +} + +static bool is_link_backpressured(struct nix_tx_stall *tx_stall, + struct nix_hw *nix_hw, + int blkaddr, int tl2) +{ + struct rvu *rvu = tx_stall->rvu; + struct nix_txsch *tl2_txsch; + int pkt_cnt, unit_cnt; + int link, chan; + u64 cfg; + + /* Skip uninitialized ones */ + if (tx_stall->tl2_link_map[tl2] == U16_MAX) + return true; + + link = tx_stall->tl2_link_map[tl2] & 0x7F; + chan = LINK_CHAN(tx_stall->tl2_link_map[tl2]); + + cfg = rvu_rd64(rvu, blkaddr, NIX_AF_TX_LINKX_HW_XOFF(link)); + if (cfg & BIT_ULL(chan)) + return true; + + /* Skip below checks for LBK links */ + if (link >= rvu->hw->cgx_links) + return false; + + cfg = rvu_rd64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link)); + + /* Check if current credits or pkt count is -ve or simply + * morethan what is configured. + */ + pkt_cnt = (cfg >> 2) & 0x3FF; + unit_cnt = (cfg >> 12) & 0xFFFFF; + if (pkt_cnt > ((tx_stall->nlink_credits[link] >> 2) & 0x3FF) || + unit_cnt > ((tx_stall->nlink_credits[link] >> 12) & 0xFFFFF)) { + return false; + } + + tl2_txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL2]; + if (rvu_cgx_tx_idle(rvu, nix_hw, tl2_txsch, tl2)) + return false; + + return true; +} + +static int rvu_nix_poll_for_tx_stall(void *arg) +{ + struct nix_tx_stall *tx_stall = arg; + struct rvu *rvu = tx_stall->rvu; + int blkaddr = tx_stall->blkaddr; + struct nix_hw *nix_hw; + int tl2; + + nix_hw = get_nix_hw(rvu->hw, blkaddr); + if (!nix_hw) + return -EINVAL; + + while (!kthread_should_stop()) { + for (tl2 = 0; tl2 < tx_stall->tl2_count; tl2++) { + /* Skip TL2 if it's not assigned to any */ + if (!is_schq_allocated(rvu, nix_hw, + NIX_TXSCH_LVL_TL2, tl2)) + continue; + + tx_stall->poll_cntr++; + + if (tx_stall->txsch_config_changed) { + rvu_nix_txsch_lock(nix_hw); + rvu_nix_scan_txsch_hierarchy(rvu, nix_hw, + blkaddr); + tx_stall->txsch_config_changed = false; + rvu_nix_txsch_unlock(nix_hw); + } + + rvu_nix_txsch_lock(nix_hw); + if (is_link_backpressured(tx_stall, nix_hw, + blkaddr, tl2)) { + rvu_nix_txsch_unlock(nix_hw); + continue; + } + rvu_nix_txsch_unlock(nix_hw); + + if (!rvu_nix_check_smq_stall(rvu, nix_hw, tl2)) + continue; + + rvu_nix_restore_tx(rvu, nix_hw, blkaddr, tl2); + } + rvu_usleep_interruptible(250); + } + + return 0; +} + +static int rvu_nix_init_tl_map(struct rvu *rvu, struct nix_hw *nix_hw, int lvl) +{ + struct nix_tx_stall *tx_stall = nix_hw->tx_stall; + struct nix_txsch *txsch; + u16 *tl_map; + + txsch = &nix_hw->txsch[lvl]; + tl_map = devm_kcalloc(rvu->dev, txsch->schq.max, + sizeof(u16), GFP_KERNEL); + if (!tl_map) + return -ENOMEM; + + switch (lvl) { + case NIX_TXSCH_LVL_SMQ: + tx_stall->smq_count = txsch->schq.max; + tx_stall->smq_tl2_map = tl_map; + break; + case NIX_TXSCH_LVL_TL4: + tx_stall->tl4_count = txsch->schq.max; + tx_stall->tl4_tl2_map = tl_map; + break; + case NIX_TXSCH_LVL_TL3: + tx_stall->tl3_count = txsch->schq.max; + tx_stall->tl3_tl2_map = tl_map; + break; + case NIX_TXSCH_LVL_TL2: + tx_stall->tl2_count = txsch->schq.max; + tx_stall->tl2_tl1_map = tl_map; + break; + } + memset(tl_map, U16_MAX, txsch->schq.max * sizeof(u16)); + return 0; +} + +static int rvu_nix_tx_stall_workaround_init(struct rvu *rvu, + struct nix_hw *nix_hw, int blkaddr) +{ + struct rvu_hwinfo *hw = rvu->hw; + struct nix_tx_stall *tx_stall; + struct rvu_block *block; + int links, err; + + if (!hw->cap.nix_fixed_txschq_mapping) + return 0; + + tx_stall = devm_kzalloc(rvu->dev, + sizeof(struct nix_tx_stall), GFP_KERNEL); + if (!tx_stall) + return -ENOMEM; + + tx_stall->blkaddr = blkaddr; + tx_stall->rvu = rvu; + nix_hw->tx_stall = tx_stall; + + /* Get the level at which link/chan will assert backpressure */ + if (rvu_read64(rvu, blkaddr, NIX_AF_PSE_CHANNEL_LEVEL)) + tx_stall->pse_link_bp_level = NIX_TXSCH_LVL_TL3; + else + tx_stall->pse_link_bp_level = NIX_TXSCH_LVL_TL2; + + mutex_init(&tx_stall->txsch_lock); + + /* Alloc memory for saving SMQ/TL4/TL3/TL1 to TL2 mapping */ + err = rvu_nix_init_tl_map(rvu, nix_hw, NIX_TXSCH_LVL_SMQ); + if (err) + return err; + err = rvu_nix_init_tl_map(rvu, nix_hw, NIX_TXSCH_LVL_TL4); + if (err) + return err; + err = rvu_nix_init_tl_map(rvu, nix_hw, NIX_TXSCH_LVL_TL3); + if (err) + return err; + err = rvu_nix_init_tl_map(rvu, nix_hw, NIX_TXSCH_LVL_TL2); + if (err) + return err; + + block = &hw->block[blkaddr]; + tx_stall->sq_count = min_t(int, num_online_cpus(), OTX2_MAX_CQ_CNT); + + /* SMQs to nixlf SQ mapping info */ + tx_stall->sq_smq_map = devm_kcalloc(rvu->dev, + block->lf.max * tx_stall->sq_count, + sizeof(u16), GFP_KERNEL); + if (!tx_stall->sq_smq_map) + return -ENOMEM; + memset(tx_stall->sq_smq_map, U16_MAX, + block->lf.max * tx_stall->sq_count * sizeof(u16)); + + /* TL2 to transmit link mapping info */ + tx_stall->tl2_link_map = devm_kcalloc(rvu->dev, tx_stall->tl2_count, + sizeof(u16), GFP_KERNEL); + if (!tx_stall->tl2_link_map) + return -ENOMEM; + memset(tx_stall->tl2_link_map, U16_MAX, + tx_stall->tl2_count * sizeof(u16)); + + /* Number of Tl2s attached to NIXLF */ + tx_stall->nixlf_tl2_count = devm_kcalloc(rvu->dev, block->lf.max, + sizeof(u8), GFP_KERNEL); + if (!tx_stall->nixlf_tl2_count) + return -ENOMEM; + memset(tx_stall->nixlf_tl2_count, 0, block->lf.max * sizeof(u8)); + + /* Per NIXLF poll and stall counters */ + tx_stall->nixlf_poll_count = devm_kcalloc(rvu->dev, block->lf.max, + sizeof(u64), GFP_KERNEL); + if (!tx_stall->nixlf_poll_count) + return -ENOMEM; + memset(tx_stall->nixlf_poll_count, 0, block->lf.max * sizeof(u64)); + + tx_stall->nixlf_stall_count = devm_kcalloc(rvu->dev, block->lf.max, + sizeof(u64), GFP_KERNEL); + if (!tx_stall->nixlf_stall_count) + return -ENOMEM; + memset(tx_stall->nixlf_stall_count, 0, block->lf.max * sizeof(u64)); + + /* For saving HW link's transmit credits config */ + links = rvu->hw->cgx_links + rvu->hw->lbk_links; + tx_stall->nlink_credits = devm_kcalloc(rvu->dev, links, + sizeof(u64), GFP_KERNEL); + if (!tx_stall->nlink_credits) + return -ENOMEM; + rvu_nix_scan_link_credits(rvu, blkaddr, tx_stall); + + tx_stall->poll_thread = kthread_create(rvu_nix_poll_for_tx_stall, + (void *)tx_stall, + "nix_tx_stall_polling_kthread"); + if (IS_ERR(tx_stall->poll_thread)) + return PTR_ERR(tx_stall->poll_thread); + + kthread_bind(tx_stall->poll_thread, cpumask_first(cpu_online_mask)); + wake_up_process(tx_stall->poll_thread); + return 0; +} + +static void rvu_nix_tx_stall_workaround_exit(struct rvu *rvu, + struct nix_hw *nix_hw) +{ + struct nix_tx_stall *tx_stall = nix_hw->tx_stall; + + if (!tx_stall) + return; + + if (tx_stall->poll_thread) + kthread_stop(tx_stall->poll_thread); + mutex_destroy(&tx_stall->txsch_lock); +} + +ssize_t rvu_nix_get_tx_stall_counters(struct rvu *rvu, + char __user *buffer, loff_t *ppos) +{ + struct rvu_hwinfo *hw = rvu->hw; + struct nix_tx_stall *tx_stall; + struct rvu_block *block; + struct nix_hw *nix_hw; + int blkaddr, len, lf; + char kbuf[2000]; + + if (*ppos) + return 0; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return -EFAULT; + + nix_hw = get_nix_hw(rvu->hw, blkaddr); + if (!nix_hw) + return -EFAULT; + + tx_stall = nix_hw->tx_stall; + if (!tx_stall) + return -EFAULT; + + len = snprintf(kbuf, sizeof(kbuf), "\n NIX transmit stall stats\n"); + len += snprintf(kbuf + len, sizeof(kbuf), + "\t\tPolled: \t\t%lld\n", tx_stall->poll_cntr); + len += snprintf(kbuf + len, sizeof(kbuf), + "\t\tTx stall detected: \t%lld\n\n", + tx_stall->stalled_cntr); + + block = &hw->block[blkaddr]; + mutex_lock(&rvu->rsrc_lock); + for (lf = 0; lf < block->lf.max; lf++) { + if (!test_bit(lf, block->lf.bmap)) + continue; + len += snprintf(kbuf + len, sizeof(kbuf), + "\t\tNIXLF%d Polled: %lld \tStalled: %lld\n", + lf, tx_stall->nixlf_poll_count[lf], + tx_stall->nixlf_stall_count[lf]); + } + mutex_unlock(&rvu->rsrc_lock); + + if (len > 0) { + if (copy_to_user(buffer, kbuf, len)) + return -EFAULT; + } + + *ppos += len; + return len; +} + +static void rvu_nix_enable_internal_bp(struct rvu *rvu, int blkaddr) +{ + /* An issue exists in A0 silicon whereby, NIX CQ may reach in CQ full + * state followed by CQ hang on CQM query response from stale + * CQ context. To avoid such condition, enable internal backpressure + * with BP_TEST registers. + */ + if (is_rvu_96xx_A0(rvu)) { + /* Enable internal backpressure on pipe_stg0 */ + rvu_write64(rvu, blkaddr, NIX_AF_RQM_BP_TEST, + BIT_ULL(51) | BIT_ULL(23) | BIT_ULL(22) | 0x100ULL); + /* Enable internal backpressure on cqm query request */ + rvu_write64(rvu, blkaddr, NIX_AF_CQM_BP_TEST, + BIT_ULL(43) | BIT_ULL(23) | BIT_ULL(22) | 0x100ULL); + } +} + +int rvu_nix_fixes_init(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr) +{ + int err; + u64 cfg; + + if (!is_rvu_96xx_B0(rvu) && !is_rvu_95xx_A0(rvu)) + return 0; + + /* As per a HW errata in 96xx A0 silicon, NIX may corrupt + * internal state when conditional clocks are turned off. + * Hence enable them. + */ + if (is_rvu_96xx_A0(rvu)) + rvu_write64(rvu, blkaddr, NIX_AF_CFG, + rvu_read64(rvu, blkaddr, NIX_AF_CFG) | 0x5EULL); + else + rvu_write64(rvu, blkaddr, NIX_AF_CFG, + rvu_read64(rvu, blkaddr, NIX_AF_CFG) | 0x40ULL); + + /* Set chan/link to backpressure TL3 instead of TL2 */ + rvu_write64(rvu, blkaddr, NIX_AF_PSE_CHANNEL_LEVEL, 0x01); + + /* Disable SQ manager's sticky mode operation (set TM6 = 0) + * This sticky mode is known to cause SQ stalls when multiple + * SQs are mapped to same SMQ and transmitting pkts simultaneously + */ + cfg = rvu_read64(rvu, blkaddr, NIX_AF_SQM_DBG_CTL_STATUS); + cfg &= ~BIT_ULL(15); + rvu_write64(rvu, blkaddr, NIX_AF_SQM_DBG_CTL_STATUS, cfg); + + rvu_nix_enable_internal_bp(rvu, blkaddr); + + if (!is_rvu_96xx_A0(rvu)) + return 0; + + err = rvu_nix_tx_stall_workaround_init(rvu, nix_hw, blkaddr); + if (err) + return err; + + return 0; +} + +void rvu_nix_fixes_exit(struct rvu *rvu, struct nix_hw *nix_hw) +{ + if (!is_rvu_96xx_A0(rvu)) + return; + + rvu_nix_tx_stall_workaround_exit(rvu, nix_hw); +} + +int rvu_tim_lookup_rsrc(struct rvu *rvu, struct rvu_block *block, + u16 pcifunc, int slot) +{ + int lf, blkaddr; + u64 val; + + /* Due to a HW issue LF_CFG_DEBUG register cannot be used to + * find PF_FUNC <=> LF mapping, hence scan through LFX_CFG + * registers to find mapped LF for a given PF_FUNC. + */ + if (is_rvu_96xx_B0(rvu)) { + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_TIM, pcifunc); + if (blkaddr < 0) + return TIM_AF_LF_INVALID; + + for (lf = 0; lf < block->lf.max; lf++) { + val = rvu_read64(rvu, block->addr, block->lfcfg_reg | + (lf << block->lfshift)); + if ((((val >> 8) & 0xffff) == pcifunc) && + (val & 0xff) == slot) + return lf; + } + return -1; + } + + val = ((u64)pcifunc << 24) | (slot << 16) | (1ULL << 13); + rvu_write64(rvu, block->addr, block->lookup_reg, val); + + /* Wait for the lookup to finish */ + while (rvu_read64(rvu, block->addr, block->lookup_reg) & (1ULL << 13)) + ; + + val = rvu_read64(rvu, block->addr, block->lookup_reg); + + /* Check LF valid bit */ + if (!(val & (1ULL << 12))) + return -1; + + return (val & 0xFFF); +} + +int rvu_npc_get_tx_nibble_cfg(struct rvu *rvu, u64 nibble_ena) +{ + /* Due to a HW issue in these silicon versions, parse nibble enable + * configuration has to be identical for both Rx and Tx interfaces. + */ + if (is_rvu_96xx_B0(rvu)) + return nibble_ena; + return 0; +} + +bool is_parse_nibble_config_valid(struct rvu *rvu, + struct npc_mcam_kex *mcam_kex) +{ + if (!is_rvu_96xx_B0(rvu)) + return true; + + /* Due to a HW issue in above silicon versions, parse nibble enable + * configuration has to be identical for both Rx and Tx interfaces. + */ + if (mcam_kex->keyx_cfg[NIX_INTF_RX] != mcam_kex->keyx_cfg[NIX_INTF_TX]) + return false; + return true; +} + +void __weak otx2smqvf_xmit(void) +{ + /* Nothing to do */ +} + +void rvu_smqvf_xmit(struct rvu *rvu) +{ + if (is_rvu_95xx_A0(rvu) || is_rvu_96xx_A0(rvu)) { + usleep_range(50, 60); + otx2smqvf_xmit(); + } +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_fixes.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_fixes.h new file mode 100644 index 000000000000..04ba74b92cca --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_fixes.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Marvell OcteonTx2 RVU Admin Function driver + * + * Copyright (C) 2019 Marvell International Ltd. + * + * 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. + */ + +#ifndef RVU_FIXES_H +#define RVU_FIXES_H + +#define RVU_SMQVF_PCIFUNC 17 + +struct rvu; + +void otx2smqvf_xmit(void); +void rvu_smqvf_xmit(struct rvu *rvu); + +#endif /* RVU_FIXES_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 4a7609fd6dd0..bd125d2f1b38 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -16,8 +16,12 @@ #include "rvu.h" #include "npc.h" #include "cgx.h" +#include "rvu_fixes.h" +static void nix_free_tx_vtag_entries(struct rvu *rvu, u16 pcifunc); static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add); +static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req, + int type, int chan_id); enum mc_tbl_sz { MC_TBL_SZ_256, @@ -64,7 +68,6 @@ enum nix_makr_fmt_indexes { struct mce { struct hlist_node node; - u16 idx; u16 pcifunc; }; @@ -128,16 +131,17 @@ static void nix_rx_sync(struct rvu *rvu, int blkaddr) if (err) dev_err(rvu->dev, "NIX RX software sync failed\n"); - /* As per a HW errata in 9xxx A0 silicon, HW may clear SW_SYNC[ENA] + /* As per a HW errata in 96xx A0 silicon, HW may clear SW_SYNC[ENA] * bit too early. Hence wait for 50us more. */ - if (is_rvu_9xxx_A0(rvu)) + if (is_rvu_96xx_A0(rvu)) usleep_range(50, 60); } static bool is_valid_txschq(struct rvu *rvu, int blkaddr, int lvl, u16 pcifunc, u16 schq) { + struct rvu_hwinfo *hw = rvu->hw; struct nix_txsch *txsch; struct nix_hw *nix_hw; u16 map_func; @@ -155,13 +159,15 @@ static bool is_valid_txschq(struct rvu *rvu, int blkaddr, map_func = TXSCH_MAP_FUNC(txsch->pfvf_map[schq]); mutex_unlock(&rvu->rsrc_lock); - /* For TL1 schq, sharing across VF's of same PF is ok */ - if (lvl == NIX_TXSCH_LVL_TL1 && - rvu_get_pf(map_func) != rvu_get_pf(pcifunc)) - return false; + /* TLs aggegating traffic are shared across PF and VFs */ + if (lvl >= hw->cap.nix_tx_aggr_lvl) { + if (rvu_get_pf(map_func) != rvu_get_pf(pcifunc)) + return false; + else + return true; + } - if (lvl != NIX_TXSCH_LVL_TL1 && - map_func != pcifunc) + if (map_func != pcifunc) return false; return true; @@ -175,7 +181,8 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf) int err; pf = rvu_get_pf(pcifunc); - if (!is_pf_cgxmapped(rvu, pf) && type != NIX_INTF_TYPE_LBK) + if (!is_pf_cgxmapped(rvu, pf) && type != NIX_INTF_TYPE_LBK && + type != NIX_INTF_TYPE_SDP) return 0; switch (type) { @@ -195,9 +202,19 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf) pfvf->tx_chan_cnt = 1; cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind); rvu_npc_set_pkind(rvu, pkind, pfvf); + + /* By default we enable pause frames */ + if ((pcifunc & RVU_PFVF_FUNC_MASK) == 0) + cgx_lmac_set_pause_frm(rvu_cgx_pdata(cgx_id, rvu), + lmac_id, true, true); break; case NIX_INTF_TYPE_LBK: vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1; + + /* Note that AF's VFs work in pairs and talk over consecutive + * loopback channels.Therefore if odd number of AF VFs are + * enabled then the last VF remains with no pair. + */ pfvf->rx_chan_base = NIX_CHAN_LBK_CHX(0, vf); pfvf->tx_chan_base = vf & 0x1 ? NIX_CHAN_LBK_CHX(0, vf - 1) : NIX_CHAN_LBK_CHX(0, vf + 1); @@ -206,6 +223,15 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf) rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf, pfvf->rx_chan_base, false); break; + case NIX_INTF_TYPE_SDP: + /* Added single interface and single channel support for now */ + pfvf->rx_chan_base = NIX_CHAN_SDP_CHX(0); + pfvf->tx_chan_base = pfvf->rx_chan_base; + pfvf->rx_chan_cnt = 1; + pfvf->tx_chan_cnt = 1; + rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf, + pfvf->rx_chan_base, false); + break; } /* Add a UCAST forwarding rule in MCAM with this NIXLF attached @@ -238,7 +264,6 @@ static void nix_interface_deinit(struct rvu *rvu, u16 pcifunc, u8 nixlf) pfvf->maxlen = 0; pfvf->minlen = 0; - pfvf->rxvlan = false; /* Remove this PF_FUNC from bcast pkt replication list */ err = nix_update_bcast_mce_list(rvu, pcifunc, false); @@ -248,8 +273,147 @@ static void nix_interface_deinit(struct rvu *rvu, u16 pcifunc, u8 nixlf) pcifunc); } - /* Free and disable any MCAM entries used by this NIX LF */ - rvu_npc_disable_mcam_entries(rvu, pcifunc, nixlf); + /* Free any tx vtag def entries used by this NIX LF */ + nix_free_tx_vtag_entries(rvu, pcifunc); + + /* Disable DMAC filters used */ + rvu_cgx_disable_dmac_entries(rvu, pcifunc); +} + +int rvu_mbox_handler_nix_bp_disable(struct rvu *rvu, + struct nix_bp_cfg_req *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + struct rvu_pfvf *pfvf; + int blkaddr, pf, type; + u16 chan_base, chan; + u64 cfg; + + pf = rvu_get_pf(pcifunc); + type = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX; + if (!is_pf_cgxmapped(rvu, pf) && type != NIX_INTF_TYPE_LBK) + return 0; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); + + chan_base = pfvf->rx_chan_base + req->chan_base; + for (chan = chan_base; chan < (chan_base + req->chan_cnt); chan++) { + cfg = rvu_read64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan)); + rvu_write64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan), + cfg & ~BIT_ULL(16)); + } + return 0; +} + +static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req, + int type, int chan_id) +{ + int bpid, blkaddr, lmac_chan_cnt; + struct rvu_hwinfo *hw = rvu->hw; + u16 cgx_bpid_cnt, lbk_bpid_cnt; + struct rvu_pfvf *pfvf; + u8 cgx_id, lmac_id; + u64 cfg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, req->hdr.pcifunc); + cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST); + lmac_chan_cnt = cfg & 0xFF; + + cgx_bpid_cnt = hw->cgx_links * lmac_chan_cnt; + lbk_bpid_cnt = hw->lbk_links * ((cfg >> 16) & 0xFF); + + pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); + + /* Backpressure IDs range division + * CGX channles are mapped to (0 - 191) BPIDs + * LBK channles are mapped to (192 - 255) BPIDs + * SDP channles are mapped to (256 - 511) BPIDs + * + * Lmac channles and bpids mapped as follows + * cgx(0)_lmac(0)_chan(0 - 15) = bpid(0 - 15) + * cgx(0)_lmac(1)_chan(0 - 15) = bpid(16 - 31) .... + * cgx(1)_lmac(0)_chan(0 - 15) = bpid(64 - 79) .... + */ + switch (type) { + case NIX_INTF_TYPE_CGX: + if ((req->chan_base + req->chan_cnt) > 15) + return -EINVAL; + rvu_get_cgx_lmac_id(pfvf->cgx_lmac, &cgx_id, &lmac_id); + /* Assign bpid based on cgx, lmac and chan id */ + bpid = (cgx_id * hw->lmac_per_cgx * lmac_chan_cnt) + + (lmac_id * lmac_chan_cnt) + req->chan_base; + + if (req->bpid_per_chan) + bpid += chan_id; + if (bpid > cgx_bpid_cnt) + return -EINVAL; + break; + + case NIX_INTF_TYPE_LBK: + if ((req->chan_base + req->chan_cnt) > 63) + return -EINVAL; + bpid = cgx_bpid_cnt + req->chan_base; + if (req->bpid_per_chan) + bpid += chan_id; + if (bpid > (cgx_bpid_cnt + lbk_bpid_cnt)) + return -EINVAL; + break; + default: + return -EINVAL; + } + return bpid; +} + +int rvu_mbox_handler_nix_bp_enable(struct rvu *rvu, + struct nix_bp_cfg_req *req, + struct nix_bp_cfg_rsp *rsp) +{ + int blkaddr, pf, type, chan_id = 0; + u16 pcifunc = req->hdr.pcifunc; + struct rvu_pfvf *pfvf; + u16 chan_base, chan; + s16 bpid, bpid_base; + u64 cfg; + + pf = rvu_get_pf(pcifunc); + type = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX; + + /* Enable backpressure only for CGX mapped PFs and LBK interface */ + if (!is_pf_cgxmapped(rvu, pf) && type != NIX_INTF_TYPE_LBK) + return 0; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); + + bpid_base = rvu_nix_get_bpid(rvu, req, type, chan_id); + chan_base = pfvf->rx_chan_base + req->chan_base; + bpid = bpid_base; + + for (chan = chan_base; chan < (chan_base + req->chan_cnt); chan++) { + if (bpid < 0) { + dev_warn(rvu->dev, "Fail to enable backpessure\n"); + return -EINVAL; + } + + cfg = rvu_read64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan)); + rvu_write64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan), + cfg | (bpid & 0xFF) | BIT_ULL(16)); + chan_id++; + bpid = rvu_nix_get_bpid(rvu, req, type, chan_id); + } + + for (chan = 0; chan < req->chan_cnt; chan++) { + /* Map channel and bpid assign to it */ + rsp->chan_bpid[chan] = ((req->chan_base + chan) & 0x7F) << 10 | + (bpid_base & 0x3FF); + if (req->bpid_per_chan) + bpid_base++; + } + rsp->chan_cnt = req->chan_cnt; + + return 0; } static void nix_setup_lso_tso_l3(struct rvu *rvu, int blkaddr, @@ -382,7 +546,8 @@ static void nix_ctx_free(struct rvu *rvu, struct rvu_pfvf *pfvf) static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr, struct rvu_pfvf *pfvf, int nixlf, - int rss_sz, int rss_grps, int hwctx_size) + int rss_sz, int rss_grps, int hwctx_size, + u64 way_mask) { int err, grp, num_indices; @@ -402,7 +567,8 @@ static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr, /* Config full RSS table size, enable RSS and caching */ rvu_write64(rvu, blkaddr, NIX_AF_LFX_RSS_CFG(nixlf), BIT_ULL(36) | BIT_ULL(4) | - ilog2(num_indices / MAX_RSS_INDIR_TBL_SIZE)); + ilog2(num_indices / MAX_RSS_INDIR_TBL_SIZE) | + way_mask << 20); /* Config RSS group offset and sizes */ for (grp = 0; grp < rss_grps; grp++) rvu_write64(rvu, blkaddr, NIX_AF_LFX_RSS_GRPX(nixlf, grp), @@ -530,6 +696,8 @@ static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req, if (!is_valid_txschq(rvu, blkaddr, NIX_TXSCH_LVL_SMQ, pcifunc, req->sq.smq)) return NIX_AF_ERR_AQ_ENQUEUE; + rvu_nix_update_sq_smq_mapping(rvu, blkaddr, nixlf, req->qidx, + req->sq.smq); } memset(&inst, 0, sizeof(struct nix_aq_inst_s)); @@ -542,6 +710,11 @@ static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req, */ inst.res_addr = (u64)aq->res->iova; + /* Hardware uses same aq->res->base for updating result of + * previous instruction hence wait here till it is done. + */ + spin_lock(&aq->lock); + /* Clean result + context memory */ memset(aq->res->base, 0, aq->res->entry_sz); /* Context needs to be written at RES_ADDR + 128 */ @@ -586,11 +759,10 @@ static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req, break; default: rc = NIX_AF_ERR_AQ_ENQUEUE; + spin_unlock(&aq->lock); return rc; } - spin_lock(&aq->lock); - /* Submit the instruction to AQ */ rc = nix_aq_enqueue_wait(rvu, block, &inst); if (rc) { @@ -663,6 +835,21 @@ static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req, return 0; } +static const char *nix_get_ctx_name(int ctype) +{ + switch (ctype) { + case NIX_AQ_CTYPE_CQ: + return "CQ"; + case NIX_AQ_CTYPE_SQ: + return "SQ"; + case NIX_AQ_CTYPE_RQ: + return "RQ"; + case NIX_AQ_CTYPE_RSS: + return "RSS"; + } + return ""; +} + static int nix_lf_hwctx_disable(struct rvu *rvu, struct hwctx_disable_req *req) { struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); @@ -680,6 +867,8 @@ static int nix_lf_hwctx_disable(struct rvu *rvu, struct hwctx_disable_req *req) if (req->ctype == NIX_AQ_CTYPE_CQ) { aq_req.cq.ena = 0; aq_req.cq_mask.ena = 1; + aq_req.cq.bp_ena = 0; + aq_req.cq_mask.bp_ena = 1; q_cnt = pfvf->cq_ctx->qsize; bmap = pfvf->cq_bmap; } @@ -707,21 +896,60 @@ static int nix_lf_hwctx_disable(struct rvu *rvu, struct hwctx_disable_req *req) if (rc) { err = rc; dev_err(rvu->dev, "Failed to disable %s:%d context\n", - (req->ctype == NIX_AQ_CTYPE_CQ) ? - "CQ" : ((req->ctype == NIX_AQ_CTYPE_RQ) ? - "RQ" : "SQ"), qidx); + nix_get_ctx_name(req->ctype), qidx); } } return err; } +#ifdef CONFIG_NDC_DIS_DYNAMIC_CACHING +static int nix_lf_hwctx_lockdown(struct rvu *rvu, struct nix_aq_enq_req *req) +{ + struct nix_aq_enq_req lock_ctx_req; + int err; + + if (req->op != NIX_AQ_INSTOP_INIT) + return 0; + + if (req->ctype == NIX_AQ_CTYPE_MCE || + req->ctype == NIX_AQ_CTYPE_DYNO) + return 0; + + memset(&lock_ctx_req, 0, sizeof(struct nix_aq_enq_req)); + lock_ctx_req.hdr.pcifunc = req->hdr.pcifunc; + lock_ctx_req.ctype = req->ctype; + lock_ctx_req.op = NIX_AQ_INSTOP_LOCK; + lock_ctx_req.qidx = req->qidx; + err = rvu_nix_aq_enq_inst(rvu, &lock_ctx_req, NULL); + if (err) + dev_err(rvu->dev, + "PFUNC 0x%x: Failed to lock NIX %s:%d context\n", + req->hdr.pcifunc, + nix_get_ctx_name(req->ctype), req->qidx); + return err; +} + +int rvu_mbox_handler_nix_aq_enq(struct rvu *rvu, + struct nix_aq_enq_req *req, + struct nix_aq_enq_rsp *rsp) +{ + int err; + + err = rvu_nix_aq_enq_inst(rvu, req, rsp); + if (!err) + err = nix_lf_hwctx_lockdown(rvu, req); + return err; +} +#else + int rvu_mbox_handler_nix_aq_enq(struct rvu *rvu, struct nix_aq_enq_req *req, struct nix_aq_enq_rsp *rsp) { return rvu_nix_aq_enq_inst(rvu, req, rsp); } +#endif int rvu_mbox_handler_nix_hwctx_disable(struct rvu *rvu, struct hwctx_disable_req *req, @@ -735,16 +963,20 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, struct nix_lf_alloc_rsp *rsp) { int nixlf, qints, hwctx_size, intf, err, rc = 0; + struct rvu_pfvf *pfvf, *parent_pf; struct rvu_hwinfo *hw = rvu->hw; u16 pcifunc = req->hdr.pcifunc; struct rvu_block *block; - struct rvu_pfvf *pfvf; u64 cfg, ctx_cfg; int blkaddr; if (!req->rq_cnt || !req->sq_cnt || !req->cq_cnt) return NIX_AF_ERR_PARAM; + if (req->way_mask) + req->way_mask &= 0xFFFF; + + parent_pf = &rvu->pf[rvu_get_pf(pcifunc)]; pfvf = rvu_get_pfvf(rvu, pcifunc); blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); if (!pfvf->nixlf || blkaddr < 0) @@ -810,7 +1042,7 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, (u64)pfvf->rq_ctx->iova); /* Set caching and queue count in HW */ - cfg = BIT_ULL(36) | (req->rq_cnt - 1); + cfg = BIT_ULL(36) | (req->rq_cnt - 1) | req->way_mask << 20; rvu_write64(rvu, blkaddr, NIX_AF_LFX_RQS_CFG(nixlf), cfg); /* Alloc NIX SQ HW context memory and config the base */ @@ -825,7 +1057,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, rvu_write64(rvu, blkaddr, NIX_AF_LFX_SQS_BASE(nixlf), (u64)pfvf->sq_ctx->iova); - cfg = BIT_ULL(36) | (req->sq_cnt - 1); + + cfg = BIT_ULL(36) | (req->sq_cnt - 1) | req->way_mask << 20; rvu_write64(rvu, blkaddr, NIX_AF_LFX_SQS_CFG(nixlf), cfg); /* Alloc NIX CQ HW context memory and config the base */ @@ -840,13 +1073,14 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, rvu_write64(rvu, blkaddr, NIX_AF_LFX_CQS_BASE(nixlf), (u64)pfvf->cq_ctx->iova); - cfg = BIT_ULL(36) | (req->cq_cnt - 1); + + cfg = BIT_ULL(36) | (req->cq_cnt - 1) | req->way_mask << 20; rvu_write64(rvu, blkaddr, NIX_AF_LFX_CQS_CFG(nixlf), cfg); /* Initialize receive side scaling (RSS) */ hwctx_size = 1UL << ((ctx_cfg >> 12) & 0xF); - err = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, - req->rss_sz, req->rss_grps, hwctx_size); + err = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz, + req->rss_grps, hwctx_size, req->way_mask); if (err) goto free_mem; @@ -860,7 +1094,9 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_BASE(nixlf), (u64)pfvf->cq_ints_ctx->iova); - rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_CFG(nixlf), BIT_ULL(36)); + + rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_CFG(nixlf), + BIT_ULL(36) | req->way_mask << 20); /* Alloc memory for QINT's HW contexts */ cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2); @@ -872,7 +1108,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_BASE(nixlf), (u64)pfvf->nix_qints_ctx->iova); - rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_CFG(nixlf), BIT_ULL(36)); + rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_CFG(nixlf), + BIT_ULL(36) | req->way_mask << 20); /* Setup VLANX TPID's. * Use VLAN1 for 802.1Q @@ -896,7 +1133,14 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, /* Config Rx pkt length, csum checks and apad enable / disable */ rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_CFG(nixlf), req->rx_cfg); + /* Configure pkind for TX parse config, 63 from npc_profile */ + cfg = NPC_TX_DEF_PKIND; + rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg); + intf = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX; + if (is_sdp_pf(pcifunc)) + intf = NIX_INTF_TYPE_SDP; + err = nix_interface_init(rvu, pcifunc, intf, nixlf); if (err) goto free_mem; @@ -931,10 +1175,11 @@ exit: cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2); rsp->qints = ((cfg >> 12) & 0xFFF); rsp->cints = ((cfg >> 24) & 0xFFF); + rsp->hw_rx_tstamp_en = parent_pf->hw_rx_tstamp_en; return rc; } -int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct msg_req *req, +int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct nix_lf_free_req *req, struct msg_rsp *rsp) { struct rvu_hwinfo *hw = rvu->hw; @@ -953,6 +1198,11 @@ int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct msg_req *req, if (nixlf < 0) return NIX_AF_ERR_AF_LF_INVALID; + if (req->flags & NIX_LF_DISABLE_FLOWS) + rvu_npc_disable_mcam_entries(rvu, pcifunc, nixlf); + else + rvu_npc_free_mcam_entries(rvu, pcifunc, nixlf); + nix_interface_deinit(rvu, pcifunc, nixlf); /* Reset this NIX LF */ @@ -1048,6 +1298,9 @@ static void nix_reset_tx_linkcfg(struct rvu *rvu, int blkaddr, struct rvu_hwinfo *hw = rvu->hw; int link; + if (lvl >= hw->cap.nix_tx_aggr_lvl) + return; + /* Reset TL4's SDP link config */ if (lvl == NIX_TXSCH_LVL_TL4) rvu_write64(rvu, blkaddr, NIX_AF_TL4X_SDP_LINK_CFG(schq), 0x00); @@ -1061,83 +1314,185 @@ static void nix_reset_tx_linkcfg(struct rvu *rvu, int blkaddr, NIX_AF_TL3_TL2X_LINKX_CFG(schq, link), 0x00); } -static int -rvu_get_tl1_schqs(struct rvu *rvu, int blkaddr, u16 pcifunc, - u16 *schq_list, u16 *schq_cnt) +static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc) { - struct nix_txsch *txsch; - struct nix_hw *nix_hw; - struct rvu_pfvf *pfvf; - u8 cgx_id, lmac_id; - u16 schq_base; - u32 *pfvf_map; - int pf, intf; + struct rvu_hwinfo *hw = rvu->hw; + int pf = rvu_get_pf(pcifunc); + u8 cgx_id = 0, lmac_id = 0; - nix_hw = get_nix_hw(rvu->hw, blkaddr); - if (!nix_hw) - return -ENODEV; + if (is_afvf(pcifunc)) {/* LBK links */ + return hw->cgx_links; + } else if (is_pf_cgxmapped(rvu, pf)) { + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + return (cgx_id * hw->lmac_per_cgx) + lmac_id; + } - pfvf = rvu_get_pfvf(rvu, pcifunc); - txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL1]; - pfvf_map = txsch->pfvf_map; - pf = rvu_get_pf(pcifunc); + /* SDP link */ + return hw->cgx_links + hw->lbk_links; +} - /* static allocation as two TL1's per link */ - intf = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX; +static void nix_get_txschq_range(struct rvu *rvu, u16 pcifunc, + int link, int *start, int *end) +{ + struct rvu_hwinfo *hw = rvu->hw; + int pf = rvu_get_pf(pcifunc); - switch (intf) { - case NIX_INTF_TYPE_CGX: - rvu_get_cgx_lmac_id(pfvf->cgx_lmac, &cgx_id, &lmac_id); - schq_base = (cgx_id * MAX_LMAC_PER_CGX + lmac_id) * 2; - break; - case NIX_INTF_TYPE_LBK: - schq_base = rvu->cgx_cnt_max * MAX_LMAC_PER_CGX * 2; - break; - default: - return -ENODEV; + if (is_afvf(pcifunc)) { /* LBK links */ + *start = hw->cap.nix_txsch_per_cgx_lmac * link; + *end = *start + hw->cap.nix_txsch_per_lbk_lmac; + } else if (is_pf_cgxmapped(rvu, pf)) { /* CGX links */ + *start = hw->cap.nix_txsch_per_cgx_lmac * link; + *end = *start + hw->cap.nix_txsch_per_cgx_lmac; + } else { /* SDP link */ + *start = (hw->cap.nix_txsch_per_cgx_lmac * hw->cgx_links) + + (hw->cap.nix_txsch_per_lbk_lmac * hw->lbk_links); + *end = *start + hw->cap.nix_txsch_per_sdp_lmac; } +} - if (schq_base + 1 > txsch->schq.max) - return -ENODEV; +static int nix_check_txschq_alloc_req(struct rvu *rvu, int lvl, u16 pcifunc, + struct nix_hw *nix_hw, + struct nix_txsch_alloc_req *req) +{ + struct rvu_hwinfo *hw = rvu->hw; + int schq, req_schq, free_cnt; + struct nix_txsch *txsch; + int link, start, end; - /* init pfvf_map as we store flags */ - if (pfvf_map[schq_base] == U32_MAX) { - pfvf_map[schq_base] = - TXSCH_MAP((pf << RVU_PFVF_PF_SHIFT), 0); - pfvf_map[schq_base + 1] = - TXSCH_MAP((pf << RVU_PFVF_PF_SHIFT), 0); + txsch = &nix_hw->txsch[lvl]; + req_schq = req->schq_contig[lvl] + req->schq[lvl]; - /* Onetime reset for TL1 */ - nix_reset_tx_linkcfg(rvu, blkaddr, - NIX_TXSCH_LVL_TL1, schq_base); - nix_reset_tx_shaping(rvu, blkaddr, - NIX_TXSCH_LVL_TL1, schq_base); + if (!req_schq) + return 0; - nix_reset_tx_linkcfg(rvu, blkaddr, - NIX_TXSCH_LVL_TL1, schq_base + 1); - nix_reset_tx_shaping(rvu, blkaddr, - NIX_TXSCH_LVL_TL1, schq_base + 1); + link = nix_get_tx_link(rvu, pcifunc); + + /* For traffic aggregating scheduler level, one queue is enough */ + if (lvl >= hw->cap.nix_tx_aggr_lvl) { + if (req_schq != 1) + return NIX_AF_ERR_TLX_ALLOC_FAIL; + return 0; } - if (schq_list && schq_cnt) { - schq_list[0] = schq_base; - schq_list[1] = schq_base + 1; - *schq_cnt = 2; + /* Get free SCHQ count and check if request can be accomodated */ + if (hw->cap.nix_fixed_txschq_mapping) { + nix_get_txschq_range(rvu, pcifunc, link, &start, &end); + schq = start + (pcifunc & RVU_PFVF_FUNC_MASK); + if (end <= txsch->schq.max && schq < end && + !test_bit(schq, txsch->schq.bmap)) + free_cnt = 1; + else + free_cnt = 0; + } else { + free_cnt = rvu_rsrc_free_count(&txsch->schq); } + if (free_cnt < req_schq || req_schq > MAX_TXSCHQ_PER_FUNC) + return NIX_AF_ERR_TLX_ALLOC_FAIL; + + /* If contiguous queues are needed, check for availability */ + if (!hw->cap.nix_fixed_txschq_mapping && req->schq_contig[lvl] && + !rvu_rsrc_check_contig(&txsch->schq, req->schq_contig[lvl])) + return NIX_AF_ERR_TLX_ALLOC_FAIL; + return 0; } +static void nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch, + struct nix_txsch_alloc_rsp *rsp, + int lvl, int start, int end) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = rsp->hdr.pcifunc; + int idx, schq; + + /* For traffic aggregating levels, queue alloc is based + * on transmit link to which PF_FUNC is mapped to. + */ + if (lvl >= hw->cap.nix_tx_aggr_lvl) { + /* A single TL queue is allocated */ + if (rsp->schq_contig[lvl]) { + rsp->schq_contig[lvl] = 1; + rsp->schq_contig_list[lvl][0] = start; + } + + /* Both contig and non-contig reqs doesn't make sense here */ + if (rsp->schq_contig[lvl]) + rsp->schq[lvl] = 0; + + if (rsp->schq[lvl]) { + rsp->schq[lvl] = 1; + rsp->schq_list[lvl][0] = start; + } + return; + } + + /* Adjust the queue request count if HW supports + * only one queue per level configuration. + */ + if (hw->cap.nix_fixed_txschq_mapping) { + idx = pcifunc & RVU_PFVF_FUNC_MASK; + schq = start + idx; + if (idx >= (end - start) || test_bit(schq, txsch->schq.bmap)) { + rsp->schq_contig[lvl] = 0; + rsp->schq[lvl] = 0; + return; + } + + if (rsp->schq_contig[lvl]) { + rsp->schq_contig[lvl] = 1; + set_bit(schq, txsch->schq.bmap); + rsp->schq_contig_list[lvl][0] = schq; + rsp->schq[lvl] = 0; + } else if (rsp->schq[lvl]) { + rsp->schq[lvl] = 1; + set_bit(schq, txsch->schq.bmap); + rsp->schq_list[lvl][0] = schq; + } + return; + } + + /* Allocate contiguous queue indices requesty first */ + if (rsp->schq_contig[lvl]) { + schq = bitmap_find_next_zero_area(txsch->schq.bmap, + txsch->schq.max, start, + rsp->schq_contig[lvl], 0); + if (schq >= end) + rsp->schq_contig[lvl] = 0; + for (idx = 0; idx < rsp->schq_contig[lvl]; idx++) { + set_bit(schq, txsch->schq.bmap); + rsp->schq_contig_list[lvl][idx] = schq; + schq++; + } + } + + /* Allocate non-contiguous queue indices */ + if (rsp->schq[lvl]) { + idx = 0; + for (schq = start; schq < end; schq++) { + if (!test_bit(schq, txsch->schq.bmap)) { + set_bit(schq, txsch->schq.bmap); + rsp->schq_list[lvl][idx++] = schq; + } + if (idx == rsp->schq[lvl]) + break; + } + /* Update how many were allocated */ + rsp->schq[lvl] = idx; + } +} + int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu, struct nix_txsch_alloc_req *req, struct nix_txsch_alloc_rsp *rsp) { + struct rvu_hwinfo *hw = rvu->hw; u16 pcifunc = req->hdr.pcifunc; + int link, blkaddr, rc = 0; + int lvl, idx, start, end; struct nix_txsch *txsch; - int lvl, idx, req_schq; struct rvu_pfvf *pfvf; struct nix_hw *nix_hw; - int blkaddr, rc = 0; u32 *pfvf_map; u16 schq; @@ -1151,83 +1506,74 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu, return -EINVAL; mutex_lock(&rvu->rsrc_lock); - for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { - txsch = &nix_hw->txsch[lvl]; - req_schq = req->schq_contig[lvl] + req->schq[lvl]; - pfvf_map = txsch->pfvf_map; - - if (!req_schq) - continue; - - /* There are only 28 TL1s */ - if (lvl == NIX_TXSCH_LVL_TL1) { - if (req->schq_contig[lvl] || - req->schq[lvl] > 2 || - rvu_get_tl1_schqs(rvu, blkaddr, - pcifunc, NULL, NULL)) - goto err; - continue; - } - - /* Check if request is valid */ - if (req_schq > MAX_TXSCHQ_PER_FUNC) - goto err; - /* If contiguous queues are needed, check for availability */ - if (req->schq_contig[lvl] && - !rvu_rsrc_check_contig(&txsch->schq, req->schq_contig[lvl])) - goto err; + /* Check if request can be accommodated as per limits set by admin */ + if (!hw->cap.nix_fixed_txschq_mapping && + rvu_check_txsch_policy(rvu, req, pcifunc)) { + dev_err(rvu->dev, "Func 0x%x: TXSCH policy check failed\n", + pcifunc); + goto err; + } - /* Check if full request can be accommodated */ - if (req_schq >= rvu_rsrc_free_count(&txsch->schq)) + /* Check if request is valid as per HW capabilities + * and can be accomodated. + */ + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + rc = nix_check_txschq_alloc_req(rvu, lvl, pcifunc, nix_hw, req); + if (rc) goto err; } + /* Allocate requested Tx scheduler queues */ for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { txsch = &nix_hw->txsch[lvl]; - rsp->schq_contig[lvl] = req->schq_contig[lvl]; pfvf_map = txsch->pfvf_map; - rsp->schq[lvl] = req->schq[lvl]; if (!req->schq[lvl] && !req->schq_contig[lvl]) continue; - /* Handle TL1 specially as it is - * allocation is restricted to 2 TL1's - * per link - */ + rsp->schq[lvl] = req->schq[lvl]; + rsp->schq_contig[lvl] = req->schq_contig[lvl]; - if (lvl == NIX_TXSCH_LVL_TL1) { - rsp->schq_contig[lvl] = 0; - rvu_get_tl1_schqs(rvu, blkaddr, pcifunc, - &rsp->schq_list[lvl][0], - &rsp->schq[lvl]); - continue; + link = nix_get_tx_link(rvu, pcifunc); + + if (lvl >= hw->cap.nix_tx_aggr_lvl) { + start = link; + end = link; + } else if (hw->cap.nix_fixed_txschq_mapping) { + nix_get_txschq_range(rvu, pcifunc, link, &start, &end); + } else { + start = 0; + end = txsch->schq.max; } - /* Alloc contiguous queues first */ - if (req->schq_contig[lvl]) { - schq = rvu_alloc_rsrc_contig(&txsch->schq, - req->schq_contig[lvl]); + nix_txsch_alloc(rvu, txsch, rsp, lvl, start, end); - for (idx = 0; idx < req->schq_contig[lvl]; idx++) { + /* Reset queue config */ + for (idx = 0; idx < req->schq_contig[lvl]; idx++) { + schq = rsp->schq_contig_list[lvl][idx]; + if (!(TXSCH_MAP_FLAGS(pfvf_map[schq]) & + NIX_TXSCHQ_CFG_DONE)) pfvf_map[schq] = TXSCH_MAP(pcifunc, 0); - nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq); - nix_reset_tx_shaping(rvu, blkaddr, lvl, schq); - rsp->schq_contig_list[lvl][idx] = schq; - schq++; - } + nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq); + nix_reset_tx_shaping(rvu, blkaddr, lvl, schq); } - /* Alloc non-contiguous queues */ for (idx = 0; idx < req->schq[lvl]; idx++) { - schq = rvu_alloc_rsrc(&txsch->schq); - pfvf_map[schq] = TXSCH_MAP(pcifunc, 0); + schq = rsp->schq_list[lvl][idx]; + if (!(TXSCH_MAP_FLAGS(pfvf_map[schq]) & + NIX_TXSCHQ_CFG_DONE)) + pfvf_map[schq] = TXSCH_MAP(pcifunc, 0); nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq); nix_reset_tx_shaping(rvu, blkaddr, lvl, schq); - rsp->schq_list[lvl][idx] = schq; } } + + rsp->aggr_level = hw->cap.nix_tx_aggr_lvl; + rsp->aggr_lvl_rr_prio = TXSCH_TL1_DFLT_RR_PRIO; + rsp->link_cfg_lvl = rvu_read64(rvu, blkaddr, + NIX_AF_PSE_CHANNEL_LEVEL) & 0x01 ? + NIX_TXSCH_LVL_TL3 : NIX_TXSCH_LVL_TL2; goto exit; err: rc = NIX_AF_ERR_TLX_ALLOC_FAIL; @@ -1236,13 +1582,52 @@ exit: return rc; } +static void nix_smq_flush(struct rvu *rvu, int blkaddr, + int smq, u16 pcifunc, int nixlf) +{ + int pf = rvu_get_pf(pcifunc); + u8 cgx_id = 0, lmac_id = 0; + int err, restore_tx_en = 0; + u64 cfg; + + /* enable cgx tx if disabled */ + if (is_pf_cgxmapped(rvu, pf)) { + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + restore_tx_en = !cgx_lmac_tx_enable(rvu_cgx_pdata(cgx_id, rvu), + lmac_id, true); + } + + cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(smq)); + /* Do SMQ flush and set enqueue xoff */ + cfg |= BIT_ULL(50) | BIT_ULL(49); + rvu_write64(rvu, blkaddr, NIX_AF_SMQX_CFG(smq), cfg); + + /* Disable backpressure from physical link, + * otherwise SMQ flush may stall. + */ + rvu_cgx_enadis_rx_bp(rvu, pf, false); + + rvu_smqvf_xmit(rvu); + + /* Wait for flush to complete */ + err = rvu_poll_reg(rvu, blkaddr, + NIX_AF_SMQX_CFG(smq), BIT_ULL(49), true); + if (err) + dev_err(rvu->dev, + "NIXLF%d: SMQ%d flush failed\n", nixlf, smq); + + rvu_cgx_enadis_rx_bp(rvu, pf, true); + /* restore cgx tx state */ + if (restore_tx_en) + cgx_lmac_tx_enable(rvu_cgx_pdata(cgx_id, rvu), lmac_id, false); +} + static int nix_txschq_free(struct rvu *rvu, u16 pcifunc) { int blkaddr, nixlf, lvl, schq, err; struct rvu_hwinfo *hw = rvu->hw; struct nix_txsch *txsch; struct nix_hw *nix_hw; - u64 cfg; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); if (blkaddr < 0) @@ -1275,26 +1660,15 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc) for (schq = 0; schq < txsch->schq.max; schq++) { if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc) continue; - cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq)); - /* Do SMQ flush and set enqueue xoff */ - cfg |= BIT_ULL(50) | BIT_ULL(49); - rvu_write64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq), cfg); - - /* Wait for flush to complete */ - err = rvu_poll_reg(rvu, blkaddr, - NIX_AF_SMQX_CFG(schq), BIT_ULL(49), true); - if (err) { - dev_err(rvu->dev, - "NIXLF%d: SMQ%d flush failed\n", nixlf, schq); - } + nix_smq_flush(rvu, blkaddr, schq, pcifunc, nixlf); } /* Now free scheduler queues to free pool */ for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { - /* Free all SCHQ's except TL1 as - * TL1 is shared across all VF's for a RVU PF - */ - if (lvl == NIX_TXSCH_LVL_TL1) + /* TLs above aggregation level are shared across all PF + * and it's VFs, hence skip freeing them. + */ + if (lvl >= hw->cap.nix_tx_aggr_lvl) continue; txsch = &nix_hw->txsch[lvl]; @@ -1302,14 +1676,12 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc) if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc) continue; rvu_free_rsrc(&txsch->schq, schq); - txsch->pfvf_map[schq] = 0; + txsch->pfvf_map[schq] = TXSCH_MAP(0, NIX_TXSCHQ_FREE); } } mutex_unlock(&rvu->rsrc_lock); - /* Sync cached info for this LF in NDC-TX to LLC/DRAM */ - rvu_write64(rvu, blkaddr, NIX_AF_NDC_TX_SYNC, BIT_ULL(12) | nixlf); - err = rvu_poll_reg(rvu, blkaddr, NIX_AF_NDC_TX_SYNC, BIT_ULL(12), true); + err = rvu_ndc_sync(rvu, blkaddr, nixlf, NIX_AF_NDC_TX_SYNC); if (err) dev_err(rvu->dev, "NDC-TX sync failed for NIXLF %d\n", nixlf); @@ -1319,13 +1691,12 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc) static int nix_txschq_free_one(struct rvu *rvu, struct nix_txsch_free_req *req) { - int lvl, schq, nixlf, blkaddr, rc; struct rvu_hwinfo *hw = rvu->hw; u16 pcifunc = req->hdr.pcifunc; + int lvl, schq, nixlf, blkaddr; struct nix_txsch *txsch; struct nix_hw *nix_hw; u32 *pfvf_map; - u64 cfg; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); if (blkaddr < 0) @@ -1343,10 +1714,8 @@ static int nix_txschq_free_one(struct rvu *rvu, schq = req->schq; txsch = &nix_hw->txsch[lvl]; - /* Don't allow freeing TL1 */ - if (lvl > NIX_TXSCH_LVL_TL2 || - schq >= txsch->schq.max) - goto err; + if (lvl >= hw->cap.nix_tx_aggr_lvl || schq >= txsch->schq.max) + return 0; pfvf_map = txsch->pfvf_map; mutex_lock(&rvu->rsrc_lock); @@ -1359,24 +1728,12 @@ static int nix_txschq_free_one(struct rvu *rvu, /* Flush if it is a SMQ. Onus of disabling * TL2/3 queue links before SMQ flush is on user */ - if (lvl == NIX_TXSCH_LVL_SMQ) { - cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq)); - /* Do SMQ flush and set enqueue xoff */ - cfg |= BIT_ULL(50) | BIT_ULL(49); - rvu_write64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq), cfg); - - /* Wait for flush to complete */ - rc = rvu_poll_reg(rvu, blkaddr, - NIX_AF_SMQX_CFG(schq), BIT_ULL(49), true); - if (rc) { - dev_err(rvu->dev, - "NIXLF%d: SMQ%d flush failed\n", nixlf, schq); - } - } + if (lvl == NIX_TXSCH_LVL_SMQ) + nix_smq_flush(rvu, blkaddr, schq, pcifunc, nixlf); /* Free the resource */ rvu_free_rsrc(&txsch->schq, schq); - txsch->pfvf_map[schq] = 0; + txsch->pfvf_map[schq] = TXSCH_MAP(0, NIX_TXSCHQ_FREE); mutex_unlock(&rvu->rsrc_lock); return 0; err: @@ -1393,8 +1750,8 @@ int rvu_mbox_handler_nix_txsch_free(struct rvu *rvu, return nix_txschq_free_one(rvu, req); } -static bool is_txschq_config_valid(struct rvu *rvu, u16 pcifunc, int blkaddr, - int lvl, u64 reg, u64 regval) +static bool is_txschq_hierarchy_valid(struct rvu *rvu, u16 pcifunc, int blkaddr, + int lvl, u64 reg, u64 regval) { u64 regbase = reg & 0xFFFF; u16 schq, parent; @@ -1431,79 +1788,82 @@ static bool is_txschq_config_valid(struct rvu *rvu, u16 pcifunc, int blkaddr, return true; } -static int -nix_tl1_default_cfg(struct rvu *rvu, u16 pcifunc) +static bool is_txschq_shaping_valid(struct rvu_hwinfo *hw, int lvl, u64 reg) { - u16 schq_list[2], schq_cnt, schq; - int blkaddr, idx, err = 0; - u16 map_func, map_flags; - struct nix_hw *nix_hw; - u64 reg, regval; - u32 *pfvf_map; + u64 regbase; - blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); - if (blkaddr < 0) - return NIX_AF_ERR_AF_LF_INVALID; - - nix_hw = get_nix_hw(rvu->hw, blkaddr); - if (!nix_hw) - return -EINVAL; - - pfvf_map = nix_hw->txsch[NIX_TXSCH_LVL_TL1].pfvf_map; - - mutex_lock(&rvu->rsrc_lock); + if (hw->cap.nix_shaping) + return true; - err = rvu_get_tl1_schqs(rvu, blkaddr, - pcifunc, schq_list, &schq_cnt); - if (err) - goto unlock; - - for (idx = 0; idx < schq_cnt; idx++) { - schq = schq_list[idx]; - map_func = TXSCH_MAP_FUNC(pfvf_map[schq]); - map_flags = TXSCH_MAP_FLAGS(pfvf_map[schq]); + /* If shaping and coloring is not supported, then + * *_CIR and *_PIR registers should not be configured. + */ + regbase = reg & 0xFFFF; - /* check if config is already done or this is pf */ - if (map_flags & NIX_TXSCHQ_TL1_CFG_DONE) - continue; + switch (lvl) { + case NIX_TXSCH_LVL_TL1: + if (regbase == NIX_AF_TL1X_CIR(0)) + return false; + break; + case NIX_TXSCH_LVL_TL2: + if (regbase == NIX_AF_TL2X_CIR(0) || + regbase == NIX_AF_TL2X_PIR(0)) + return false; + break; + case NIX_TXSCH_LVL_TL3: + if (regbase == NIX_AF_TL3X_CIR(0) || + regbase == NIX_AF_TL3X_PIR(0)) + return false; + break; + case NIX_TXSCH_LVL_TL4: + if (regbase == NIX_AF_TL4X_CIR(0) || + regbase == NIX_AF_TL4X_PIR(0)) + return false; + break; + } + return true; +} - /* default configuration */ - reg = NIX_AF_TL1X_TOPOLOGY(schq); - regval = (TXSCH_TL1_DFLT_RR_PRIO << 1); - rvu_write64(rvu, blkaddr, reg, regval); - reg = NIX_AF_TL1X_SCHEDULE(schq); - regval = TXSCH_TL1_DFLT_RR_QTM; - rvu_write64(rvu, blkaddr, reg, regval); - reg = NIX_AF_TL1X_CIR(schq); - regval = 0; - rvu_write64(rvu, blkaddr, reg, regval); +static void nix_tl1_default_cfg(struct rvu *rvu, struct nix_hw *nix_hw, + u16 pcifunc, int blkaddr) +{ + u32 *pfvf_map; + int schq; - map_flags |= NIX_TXSCHQ_TL1_CFG_DONE; - pfvf_map[schq] = TXSCH_MAP(map_func, map_flags); - } -unlock: - mutex_unlock(&rvu->rsrc_lock); - return err; + schq = nix_get_tx_link(rvu, pcifunc); + pfvf_map = nix_hw->txsch[NIX_TXSCH_LVL_TL1].pfvf_map; + /* Skip if PF has already done the config */ + if (TXSCH_MAP_FLAGS(pfvf_map[schq]) & NIX_TXSCHQ_CFG_DONE) + return; + rvu_write64(rvu, blkaddr, NIX_AF_TL1X_TOPOLOGY(schq), + (TXSCH_TL1_DFLT_RR_PRIO << 1)); + rvu_write64(rvu, blkaddr, NIX_AF_TL1X_SCHEDULE(schq), + TXSCH_TL1_DFLT_RR_QTM); + rvu_write64(rvu, blkaddr, NIX_AF_TL1X_CIR(schq), 0x00); + pfvf_map[schq] = TXSCH_SET_FLAG(pfvf_map[schq], NIX_TXSCHQ_CFG_DONE); } int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu, struct nix_txschq_config *req, struct msg_rsp *rsp) { - u16 schq, pcifunc = req->hdr.pcifunc; struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; u64 reg, regval, schq_regbase; struct nix_txsch *txsch; - u16 map_func, map_flags; struct nix_hw *nix_hw; int blkaddr, idx, err; + int nixlf, schq; u32 *pfvf_map; - int nixlf; if (req->lvl >= NIX_TXSCH_LVL_CNT || req->num_regs > MAX_REGS_PER_MBOX_MSG) return NIX_AF_INVAL_TXSCHQ_CFG; + err = nix_get_nixlf(rvu, pcifunc, &nixlf); + if (err) + return NIX_AF_ERR_AF_LF_INVALID; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); if (blkaddr < 0) return NIX_AF_ERR_AF_LF_INVALID; @@ -1512,29 +1872,33 @@ int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu, if (!nix_hw) return -EINVAL; - nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0); - if (nixlf < 0) - return NIX_AF_ERR_AF_LF_INVALID; - txsch = &nix_hw->txsch[req->lvl]; pfvf_map = txsch->pfvf_map; - /* VF is only allowed to trigger - * setting default cfg on TL1 - */ - if (pcifunc & RVU_PFVF_FUNC_MASK && - req->lvl == NIX_TXSCH_LVL_TL1) { - return nix_tl1_default_cfg(rvu, pcifunc); + if (req->lvl >= hw->cap.nix_tx_aggr_lvl && + pcifunc & RVU_PFVF_FUNC_MASK) { + mutex_lock(&rvu->rsrc_lock); + if (req->lvl == NIX_TXSCH_LVL_TL1) + nix_tl1_default_cfg(rvu, nix_hw, pcifunc, blkaddr); + mutex_unlock(&rvu->rsrc_lock); + return 0; } + rvu_nix_txsch_lock(nix_hw); for (idx = 0; idx < req->num_regs; idx++) { reg = req->reg[idx]; regval = req->regval[idx]; schq_regbase = reg & 0xFFFF; - if (!is_txschq_config_valid(rvu, pcifunc, blkaddr, - txsch->lvl, reg, regval)) + if (!is_txschq_hierarchy_valid(rvu, pcifunc, blkaddr, + txsch->lvl, reg, regval)) { + rvu_nix_txsch_unlock(nix_hw); return NIX_AF_INVAL_TXSCHQ_CFG; + } + + /* Check if shaping and coloring is supported */ + if (!is_txschq_shaping_valid(hw, req->lvl, reg)) + continue; /* Replace PF/VF visible NIXLF slot with HW NIXLF id */ if (schq_regbase == NIX_AF_SMQX_CFG(0)) { @@ -1544,32 +1908,38 @@ int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu, regval |= ((u64)nixlf << 24); } + /* Clear 'BP_ENA' config, if it's not allowed */ + if (!hw->cap.nix_tx_link_bp) { + if (schq_regbase == NIX_AF_TL4X_SDP_LINK_CFG(0) || + (schq_regbase & 0xFF00) == + NIX_AF_TL3_TL2X_LINKX_CFG(0, 0)) + regval &= ~BIT_ULL(13); + } + /* Mark config as done for TL1 by PF */ if (schq_regbase >= NIX_AF_TL1X_SCHEDULE(0) && schq_regbase <= NIX_AF_TL1X_GREEN_BYTES(0)) { schq = TXSCHQ_IDX(reg, TXSCHQ_IDX_SHIFT); - mutex_lock(&rvu->rsrc_lock); - - map_func = TXSCH_MAP_FUNC(pfvf_map[schq]); - map_flags = TXSCH_MAP_FLAGS(pfvf_map[schq]); - - map_flags |= NIX_TXSCHQ_TL1_CFG_DONE; - pfvf_map[schq] = TXSCH_MAP(map_func, map_flags); + pfvf_map[schq] = TXSCH_SET_FLAG(pfvf_map[schq], + NIX_TXSCHQ_CFG_DONE); mutex_unlock(&rvu->rsrc_lock); } - rvu_write64(rvu, blkaddr, reg, regval); - - /* Check for SMQ flush, if so, poll for its completion */ + /* SMQ flush is special hence split register writes such + * that flush first and write rest of the bits later. + */ if (schq_regbase == NIX_AF_SMQX_CFG(0) && (regval & BIT_ULL(49))) { - err = rvu_poll_reg(rvu, blkaddr, - reg, BIT_ULL(49), true); - if (err) - return NIX_AF_SMQ_FLUSH_FAILED; + schq = TXSCHQ_IDX(reg, TXSCHQ_IDX_SHIFT); + nix_smq_flush(rvu, blkaddr, schq, pcifunc, nixlf); + regval &= ~BIT_ULL(49); } + rvu_write64(rvu, blkaddr, reg, regval); } + + rvu_nix_txsch_config_changed(nix_hw); + rvu_nix_txsch_unlock(nix_hw); return 0; } @@ -1578,7 +1948,8 @@ static int nix_rx_vtag_cfg(struct rvu *rvu, int nixlf, int blkaddr, { u64 regval = req->vtag_size; - if (req->rx.vtag_type > 7 || req->vtag_size > VTAGSIZE_T8) + if (req->rx.vtag_type > NIX_AF_LFX_RX_VTAG_TYPE7 || + req->vtag_size > VTAGSIZE_T8) return -EINVAL; if (req->rx.capture_vtag) @@ -1591,9 +1962,149 @@ static int nix_rx_vtag_cfg(struct rvu *rvu, int nixlf, int blkaddr, return 0; } +static int nix_tx_vtag_free(struct rvu *rvu, int blkaddr, + u16 pcifunc, int index) +{ + struct nix_hw *nix_hw = get_nix_hw(rvu->hw, blkaddr); + struct nix_txvlan *vlan = &nix_hw->txvlan; + + if (vlan->entry2pfvf_map[index] != pcifunc) + return NIX_AF_ERR_PARAM; + + rvu_write64(rvu, blkaddr, + NIX_AF_TX_VTAG_DEFX_DATA(index), 0x0ull); + rvu_write64(rvu, blkaddr, + NIX_AF_TX_VTAG_DEFX_CTL(index), 0x0ull); + + vlan->entry2pfvf_map[index] = 0; + rvu_free_rsrc(&vlan->rsrc, index); + + return 0; +} + +static void nix_free_tx_vtag_entries(struct rvu *rvu, u16 pcifunc) +{ + struct nix_txvlan *vlan; + struct nix_hw *nix_hw; + int index, blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); + if (blkaddr < 0) + return; + + nix_hw = get_nix_hw(rvu->hw, blkaddr); + vlan = &nix_hw->txvlan; + + mutex_lock(&vlan->rsrc_lock); + /* Scan all the entries and free the ones mapped to 'pcifunc' */ + for (index = 0; index < vlan->rsrc.max; index++) { + if (vlan->entry2pfvf_map[index] == pcifunc) + nix_tx_vtag_free(rvu, blkaddr, pcifunc, index); + } + mutex_unlock(&vlan->rsrc_lock); +} + +static int nix_tx_vtag_alloc(struct rvu *rvu, int blkaddr, + u64 vtag, u8 size) +{ + struct nix_hw *nix_hw = get_nix_hw(rvu->hw, blkaddr); + struct nix_txvlan *vlan = &nix_hw->txvlan; + u64 regval; + int index; + + mutex_lock(&vlan->rsrc_lock); + + index = rvu_alloc_rsrc(&vlan->rsrc); + if (index < 0) { + mutex_unlock(&vlan->rsrc_lock); + return index; + } + + mutex_unlock(&vlan->rsrc_lock); + + regval = size ? vtag : vtag << 32; + + rvu_write64(rvu, blkaddr, + NIX_AF_TX_VTAG_DEFX_DATA(index), regval); + rvu_write64(rvu, blkaddr, + NIX_AF_TX_VTAG_DEFX_CTL(index), size); + + return index; +} + +static int nix_tx_vtag_decfg(struct rvu *rvu, int blkaddr, + struct nix_vtag_config *req) +{ + struct nix_hw *nix_hw = get_nix_hw(rvu->hw, blkaddr); + struct nix_txvlan *vlan = &nix_hw->txvlan; + u16 pcifunc = req->hdr.pcifunc; + int idx0 = req->tx.vtag0_idx; + int idx1 = req->tx.vtag1_idx; + int err = 0; + + if (req->tx.free_vtag0 && req->tx.free_vtag1) + if (vlan->entry2pfvf_map[idx0] != pcifunc || + vlan->entry2pfvf_map[idx1] != pcifunc) + return NIX_AF_ERR_PARAM; + + mutex_lock(&vlan->rsrc_lock); + + if (req->tx.free_vtag0) { + err = nix_tx_vtag_free(rvu, blkaddr, pcifunc, idx0); + if (err) + goto exit; + } + + if (req->tx.free_vtag1) + err = nix_tx_vtag_free(rvu, blkaddr, pcifunc, idx1); + +exit: + mutex_unlock(&vlan->rsrc_lock); + return err; +} + +static int nix_tx_vtag_cfg(struct rvu *rvu, int blkaddr, + struct nix_vtag_config *req, + struct nix_vtag_config_rsp *rsp) +{ + struct nix_hw *nix_hw = get_nix_hw(rvu->hw, blkaddr); + struct nix_txvlan *vlan = &nix_hw->txvlan; + u16 pcifunc = req->hdr.pcifunc; + + if (req->tx.cfg_vtag0) { + rsp->vtag0_idx = + nix_tx_vtag_alloc(rvu, blkaddr, + req->tx.vtag0, req->vtag_size); + + if (rsp->vtag0_idx < 0) + return NIX_AF_ERR_TX_VTAG_NOSPC; + + vlan->entry2pfvf_map[rsp->vtag0_idx] = pcifunc; + } + + if (req->tx.cfg_vtag1) { + rsp->vtag1_idx = + nix_tx_vtag_alloc(rvu, blkaddr, + req->tx.vtag1, req->vtag_size); + + if (rsp->vtag1_idx < 0) + goto err_free; + + vlan->entry2pfvf_map[rsp->vtag1_idx] = pcifunc; + } + + return 0; + +err_free: + if (req->tx.cfg_vtag0) + nix_tx_vtag_free(rvu, blkaddr, pcifunc, rsp->vtag0_idx); + + return NIX_AF_ERR_TX_VTAG_NOSPC; +} + int rvu_mbox_handler_nix_vtag_cfg(struct rvu *rvu, struct nix_vtag_config *req, - struct msg_rsp *rsp) + struct nix_vtag_config_rsp *rsp) { struct rvu_hwinfo *hw = rvu->hw; u16 pcifunc = req->hdr.pcifunc; @@ -1608,12 +2119,21 @@ int rvu_mbox_handler_nix_vtag_cfg(struct rvu *rvu, return NIX_AF_ERR_AF_LF_INVALID; if (req->cfg_type) { + /* rx vtag configuration */ err = nix_rx_vtag_cfg(rvu, nixlf, blkaddr, req); if (err) return NIX_AF_ERR_PARAM; } else { - /* TODO: handle tx vtag configuration */ - return 0; + /* tx vtag configuration */ + if ((req->tx.cfg_vtag0 || req->tx.cfg_vtag1) && + (req->tx.free_vtag0 || req->tx.free_vtag1)) + return NIX_AF_ERR_PARAM; + + if (req->tx.cfg_vtag0 || req->tx.cfg_vtag1) + return nix_tx_vtag_cfg(rvu, blkaddr, req, rsp); + + if (req->tx.free_vtag0 || req->tx.free_vtag1) + return nix_tx_vtag_decfg(rvu, blkaddr, req); } return 0; @@ -1650,7 +2170,7 @@ static int nix_setup_mce(struct rvu *rvu, int mce, u8 op, } static int nix_update_mce_list(struct nix_mce_list *mce_list, - u16 pcifunc, int idx, bool add) + u16 pcifunc, bool add) { struct mce *mce, *tail = NULL; bool delete = false; @@ -1679,7 +2199,6 @@ static int nix_update_mce_list(struct nix_mce_list *mce_list, mce = kzalloc(sizeof(*mce), GFP_KERNEL); if (!mce) return -ENOMEM; - mce->idx = idx; mce->pcifunc = pcifunc; if (!tail) hlist_add_head(&mce->node, &mce_list->head); @@ -1691,12 +2210,12 @@ static int nix_update_mce_list(struct nix_mce_list *mce_list, static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add) { - int err = 0, idx, next_idx, count; + int err = 0, idx, next_idx, last_idx; struct nix_mce_list *mce_list; - struct mce *mce, *next_mce; struct nix_mcast *mcast; struct nix_hw *nix_hw; struct rvu_pfvf *pfvf; + struct mce *mce; int blkaddr; /* Broadcast pkt replication is not needed for AF's VFs, hence skip */ @@ -1728,31 +2247,31 @@ static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add) mutex_lock(&mcast->mce_lock); - err = nix_update_mce_list(mce_list, pcifunc, idx, add); + err = nix_update_mce_list(mce_list, pcifunc, add); if (err) goto end; /* Disable MCAM entry in NPC */ - - if (!mce_list->count) + if (!mce_list->count) { + rvu_npc_disable_bcast_entry(rvu, pcifunc); goto end; - count = mce_list->count; + } /* Dump the updated list to HW */ + idx = pfvf->bcast_mce_idx; + last_idx = idx + mce_list->count - 1; hlist_for_each_entry(mce, &mce_list->head, node) { - next_idx = 0; - count--; - if (count) { - next_mce = hlist_entry(mce->node.next, - struct mce, node); - next_idx = next_mce->idx; - } + if (idx > last_idx) + break; + + next_idx = idx + 1; /* EOL should be set in last MCE */ - err = nix_setup_mce(rvu, mce->idx, - NIX_AQ_INSTOP_WRITE, mce->pcifunc, - next_idx, count ? false : true); + err = nix_setup_mce(rvu, idx, NIX_AQ_INSTOP_WRITE, + mce->pcifunc, next_idx, + (next_idx > last_idx) ? true : false); if (err) goto end; + idx++; } end: @@ -1846,11 +2365,36 @@ static int nix_setup_mcast(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr) return nix_setup_bcast_tables(rvu, nix_hw); } +static int nix_setup_txvlan(struct rvu *rvu, struct nix_hw *nix_hw) +{ + struct nix_txvlan *vlan = &nix_hw->txvlan; + int err; + + /* Allocate resource bimap for tx vtag def registers*/ + vlan->rsrc.max = NIX_TX_VTAG_DEF_MAX; + err = rvu_alloc_bitmap(&vlan->rsrc); + if (err) + return -ENOMEM; + + /* Alloc memory for saving entry to RVU PFFUNC allocation mapping */ + vlan->entry2pfvf_map = devm_kcalloc(rvu->dev, vlan->rsrc.max, + sizeof(u16), GFP_KERNEL); + if (!vlan->entry2pfvf_map) + goto free_mem; + + mutex_init(&vlan->rsrc_lock); + return 0; + +free_mem: + kfree(vlan->rsrc.bmap); + return -ENOMEM; +} + static int nix_setup_txschq(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr) { struct nix_txsch *txsch; u64 cfg, reg; - int err, lvl; + int err, lvl, schq; /* Get scheduler queue count of each type and alloc * bitmap for each for alloc/free/attach operations. @@ -1888,7 +2432,8 @@ static int nix_setup_txschq(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr) sizeof(u32), GFP_KERNEL); if (!txsch->pfvf_map) return -ENOMEM; - memset(txsch->pfvf_map, U8_MAX, txsch->schq.max * sizeof(u32)); + for (schq = 0; schq < txsch->schq.max; schq++) + txsch->pfvf_map[schq] = TXSCH_MAP(0, NIX_TXSCHQ_FREE); } return 0; } @@ -2032,51 +2577,82 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg) if (field_marker) memset(&tmp, 0, sizeof(tmp)); + field_marker = true; + keyoff_marker = true; switch (key_type) { case NIX_FLOW_KEY_TYPE_PORT: field->sel_chan = true; /* This should be set to 1, when SEL_CHAN is set */ field->bytesm1 = 1; - field_marker = true; - keyoff_marker = true; break; case NIX_FLOW_KEY_TYPE_IPV4: + case NIX_FLOW_KEY_TYPE_INNR_IPV4: field->lid = NPC_LID_LC; field->ltype_match = NPC_LT_LC_IP; + if (key_type == NIX_FLOW_KEY_TYPE_INNR_IPV4) { + field->lid = NPC_LID_LG; + field->ltype_match = NPC_LT_LG_TU_IP; + } field->hdr_offset = 12; /* SIP offset */ field->bytesm1 = 7; /* SIP + DIP, 8 bytes */ field->ltype_mask = 0xF; /* Match only IPv4 */ - field_marker = true; keyoff_marker = false; break; case NIX_FLOW_KEY_TYPE_IPV6: + case NIX_FLOW_KEY_TYPE_INNR_IPV6: field->lid = NPC_LID_LC; field->ltype_match = NPC_LT_LC_IP6; + if (key_type == NIX_FLOW_KEY_TYPE_INNR_IPV6) { + field->lid = NPC_LID_LG; + field->ltype_match = NPC_LT_LG_TU_IP6; + } field->hdr_offset = 8; /* SIP offset */ field->bytesm1 = 31; /* SIP + DIP, 32 bytes */ field->ltype_mask = 0xF; /* Match only IPv6 */ - field_marker = true; - keyoff_marker = true; break; case NIX_FLOW_KEY_TYPE_TCP: case NIX_FLOW_KEY_TYPE_UDP: case NIX_FLOW_KEY_TYPE_SCTP: + case NIX_FLOW_KEY_TYPE_INNR_TCP: + case NIX_FLOW_KEY_TYPE_INNR_UDP: + case NIX_FLOW_KEY_TYPE_INNR_SCTP: field->lid = NPC_LID_LD; + if (key_type == NIX_FLOW_KEY_TYPE_INNR_TCP || + key_type == NIX_FLOW_KEY_TYPE_INNR_UDP || + key_type == NIX_FLOW_KEY_TYPE_INNR_SCTP) + field->lid = NPC_LID_LH; field->bytesm1 = 3; /* Sport + Dport, 4 bytes */ - if (key_type == NIX_FLOW_KEY_TYPE_TCP && valid_key) { + + /* Enum values for NPC_LID_LD and NPC_LID_LG are same, + * so no need to change the ltype_match, just change + * the lid for inner protocols + */ + BUILD_BUG_ON((int)NPC_LT_LD_TCP != + (int)NPC_LT_LH_TU_TCP); + BUILD_BUG_ON((int)NPC_LT_LD_UDP != + (int)NPC_LT_LH_TU_UDP); + BUILD_BUG_ON((int)NPC_LT_LD_SCTP != + (int)NPC_LT_LH_TU_SCTP); + + if ((key_type == NIX_FLOW_KEY_TYPE_TCP || + key_type == NIX_FLOW_KEY_TYPE_INNR_TCP) && + valid_key) { field->ltype_match |= NPC_LT_LD_TCP; group_member = true; - } else if (key_type == NIX_FLOW_KEY_TYPE_UDP && + } else if ((key_type == NIX_FLOW_KEY_TYPE_UDP || + key_type == NIX_FLOW_KEY_TYPE_INNR_UDP) && valid_key) { field->ltype_match |= NPC_LT_LD_UDP; group_member = true; - } else if (key_type == NIX_FLOW_KEY_TYPE_SCTP && + } else if ((key_type == NIX_FLOW_KEY_TYPE_SCTP || + key_type == NIX_FLOW_KEY_TYPE_INNR_SCTP) && valid_key) { field->ltype_match |= NPC_LT_LD_SCTP; group_member = true; } field->ltype_mask = ~field->ltype_match; - if (key_type == NIX_FLOW_KEY_TYPE_SCTP) { + if (key_type == NIX_FLOW_KEY_TYPE_SCTP || + key_type == NIX_FLOW_KEY_TYPE_INNR_SCTP) { /* Handle the case where any of the group item * is enabled in the group but not the final one */ @@ -2084,13 +2660,73 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg) valid_key = true; group_member = false; } - field_marker = true; - keyoff_marker = true; } else { field_marker = false; keyoff_marker = false; } break; + case NIX_FLOW_KEY_TYPE_NVGRE: + field->lid = NPC_LID_LD; + field->hdr_offset = 4; /* VSID offset */ + field->bytesm1 = 2; + field->ltype_match = NPC_LT_LD_NVGRE; + field->ltype_mask = 0xF; + break; + case NIX_FLOW_KEY_TYPE_VXLAN: + case NIX_FLOW_KEY_TYPE_GENEVE: + field->lid = NPC_LID_LE; + field->bytesm1 = 2; + field->hdr_offset = 4; + field->ltype_mask = 0xF; + field_marker = false; + keyoff_marker = false; + + if (key_type == NIX_FLOW_KEY_TYPE_VXLAN && valid_key) { + field->ltype_match |= NPC_LT_LE_VXLAN; + group_member = true; + } + + if (key_type == NIX_FLOW_KEY_TYPE_GENEVE && valid_key) { + field->ltype_match |= NPC_LT_LE_GENEVE; + group_member = true; + } + + if (key_type == NIX_FLOW_KEY_TYPE_GENEVE) { + if (group_member) { + field->ltype_mask = ~field->ltype_match; + field_marker = true; + keyoff_marker = true; + valid_key = true; + group_member = false; + } + } + break; + case NIX_FLOW_KEY_TYPE_ETH_DMAC: + case NIX_FLOW_KEY_TYPE_INNR_ETH_DMAC: + field->lid = NPC_LID_LA; + field->ltype_match = NPC_LT_LA_ETHER; + if (key_type == NIX_FLOW_KEY_TYPE_INNR_ETH_DMAC) { + field->lid = NPC_LID_LF; + field->ltype_match = NPC_LT_LF_TU_ETHER; + } + field->hdr_offset = 0; + field->bytesm1 = 5; /* DMAC 6 Byte */ + field->ltype_mask = 0xF; + break; + case NIX_FLOW_KEY_TYPE_IPV6_EXT: + field->lid = NPC_LID_LC; + field->hdr_offset = 40; /* IPV6 hdr */ + field->bytesm1 = 0; /* 1 Byte ext hdr*/ + field->ltype_match = NPC_LT_LC_IP6_EXT; + field->ltype_mask = 0xF; + break; + case NIX_FLOW_KEY_TYPE_GTPU: + field->lid = NPC_LID_LE; + field->hdr_offset = 4; + field->bytesm1 = 3; /* 4 bytes TID*/ + field->ltype_match = NPC_LT_LE_GTPU; + field->ltype_mask = 0xF; + break; } field->ena = 1; @@ -2259,6 +2895,7 @@ int rvu_mbox_handler_nix_set_mac_addr(struct rvu *rvu, struct nix_set_mac_addr *req, struct msg_rsp *rsp) { + bool from_vf = !!(req->hdr.pcifunc & RVU_PFVF_FUNC_MASK); struct rvu_hwinfo *hw = rvu->hw; u16 pcifunc = req->hdr.pcifunc; struct rvu_pfvf *pfvf; @@ -2273,12 +2910,30 @@ int rvu_mbox_handler_nix_set_mac_addr(struct rvu *rvu, if (nixlf < 0) return NIX_AF_ERR_AF_LF_INVALID; - ether_addr_copy(pfvf->mac_addr, req->mac_addr); + /* Skip updating mac addr if request is from vf */ + if (!from_vf) + ether_addr_copy(pfvf->mac_addr, req->mac_addr); + rvu_npc_install_ucast_entry(rvu, pcifunc, nixlf, pfvf->rx_chan_base, req->mac_addr); - rvu_npc_update_rxvlan(rvu, pcifunc, nixlf); + return 0; +} + +int rvu_mbox_handler_nix_get_mac_addr(struct rvu *rvu, + struct msg_req *req, + struct nix_get_mac_addr_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + struct rvu_pfvf *pfvf; + + if (!is_nixlf_attached(rvu, pcifunc)) + return NIX_AF_ERR_AF_LF_INVALID; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + + ether_addr_copy(rsp->mac_addr, pfvf->mac_addr); return 0; } @@ -2313,9 +2968,6 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, else rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf, pfvf->rx_chan_base, allmulti); - - rvu_npc_update_rxvlan(rvu, pcifunc, nixlf); - return 0; } @@ -2449,70 +3101,11 @@ linkcfg: cfg &= ~(0xFFFFFULL << 12); cfg |= ((lmac_fifo_len - req->maxlen) / 16) << 12; rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link), cfg); - rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_EXPR_CREDIT(link), cfg); + rvu_nix_update_link_credits(rvu, blkaddr, link, cfg); return 0; } -int rvu_mbox_handler_nix_rxvlan_alloc(struct rvu *rvu, struct msg_req *req, - struct msg_rsp *rsp) -{ - struct npc_mcam_alloc_entry_req alloc_req = { }; - struct npc_mcam_alloc_entry_rsp alloc_rsp = { }; - struct npc_mcam_free_entry_req free_req = { }; - u16 pcifunc = req->hdr.pcifunc; - int blkaddr, nixlf, err; - struct rvu_pfvf *pfvf; - - /* LBK VFs do not have separate MCAM UCAST entry hence - * skip allocating rxvlan for them - */ - if (is_afvf(pcifunc)) - return 0; - - pfvf = rvu_get_pfvf(rvu, pcifunc); - if (pfvf->rxvlan) - return 0; - - /* alloc new mcam entry */ - alloc_req.hdr.pcifunc = pcifunc; - alloc_req.count = 1; - - err = rvu_mbox_handler_npc_mcam_alloc_entry(rvu, &alloc_req, - &alloc_rsp); - if (err) - return err; - - /* update entry to enable rxvlan offload */ - blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); - if (blkaddr < 0) { - err = NIX_AF_ERR_AF_LF_INVALID; - goto free_entry; - } - - nixlf = rvu_get_lf(rvu, &rvu->hw->block[blkaddr], pcifunc, 0); - if (nixlf < 0) { - err = NIX_AF_ERR_AF_LF_INVALID; - goto free_entry; - } - - pfvf->rxvlan_index = alloc_rsp.entry_list[0]; - /* all it means is that rxvlan_index is valid */ - pfvf->rxvlan = true; - - err = rvu_npc_update_rxvlan(rvu, pcifunc, nixlf); - if (err) - goto free_entry; - - return 0; -free_entry: - free_req.hdr.pcifunc = pcifunc; - free_req.entry = alloc_rsp.entry_list[0]; - rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, rsp); - pfvf->rxvlan = false; - return err; -} - int rvu_mbox_handler_nix_set_rx_cfg(struct rvu *rvu, struct nix_rx_cfg *req, struct msg_rsp *rsp) { @@ -2583,6 +3176,9 @@ static void nix_link_config(struct rvu *rvu, int blkaddr) */ for (cgx = 0; cgx < hw->cgx; cgx++) { lmac_cnt = cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu)); + /* Skip when cgx is not available or lmac cnt is zero */ + if (lmac_cnt <= 0) + continue; tx_credits = ((CGX_FIFO_LEN / lmac_cnt) - NIC_HW_MAX_FRS) / 16; /* Enable credits and set credit pkt count to max allowed */ tx_credits = (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1); @@ -2591,9 +3187,6 @@ static void nix_link_config(struct rvu *rvu, int blkaddr) rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link), tx_credits); - rvu_write64(rvu, blkaddr, - NIX_AF_TX_LINKX_EXPR_CREDIT(link), - tx_credits); } } @@ -2605,8 +3198,6 @@ static void nix_link_config(struct rvu *rvu, int blkaddr) tx_credits = (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1); rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link), tx_credits); - rvu_write64(rvu, blkaddr, - NIX_AF_TX_LINKX_EXPR_CREDIT(link), tx_credits); } } @@ -2674,6 +3265,10 @@ static int nix_aq_init(struct rvu *rvu, struct rvu_block *block) /* Do not bypass NDC cache */ cfg = rvu_read64(rvu, block->addr, NIX_AF_NDC_CFG); cfg &= ~0x3FFEULL; +#ifdef CONFIG_NDC_DIS_DYNAMIC_CACHING + /* Disable caching of SQB aka SQEs */ + cfg |= 0x04ULL; +#endif rvu_write64(rvu, block->addr, NIX_AF_NDC_CFG, cfg); /* Result structure can be followed by RQ/SQ/CQ context at @@ -2704,14 +3299,6 @@ int rvu_nix_init(struct rvu *rvu) return 0; block = &hw->block[blkaddr]; - /* As per a HW errata in 9xxx A0 silicon, NIX may corrupt - * internal state when conditional clocks are turned off. - * Hence enable them. - */ - if (is_rvu_9xxx_A0(rvu)) - rvu_write64(rvu, blkaddr, NIX_AF_CFG, - rvu_read64(rvu, blkaddr, NIX_AF_CFG) | 0x5EULL); - /* Calibrate X2P bus to check if CGX/LBK links are fine */ err = nix_calibrate_x2p(rvu, blkaddr); if (err) @@ -2751,6 +3338,10 @@ int rvu_nix_init(struct rvu *rvu) if (err) return err; + err = nix_setup_txvlan(rvu, hw->nix0); + if (err) + return err; + /* Configure segmentation offload formats */ nix_setup_lso(rvu, hw->nix0, blkaddr); @@ -2763,23 +3354,23 @@ int rvu_nix_init(struct rvu *rvu) rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OIP4, (NPC_LID_LC << 8) | (NPC_LT_LC_IP << 4) | 0x0F); rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_IIP4, - (NPC_LID_LF << 8) | (NPC_LT_LF_TU_IP << 4) | 0x0F); + (NPC_LID_LG << 8) | (NPC_LT_LG_TU_IP << 4) | 0x0F); rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OIP6, (NPC_LID_LC << 8) | (NPC_LT_LC_IP6 << 4) | 0x0F); rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_IIP6, - (NPC_LID_LF << 8) | (NPC_LT_LF_TU_IP6 << 4) | 0x0F); + (NPC_LID_LG << 8) | (NPC_LT_LG_TU_IP6 << 4) | 0x0F); rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OTCP, (NPC_LID_LD << 8) | (NPC_LT_LD_TCP << 4) | 0x0F); rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_ITCP, - (NPC_LID_LG << 8) | (NPC_LT_LG_TU_TCP << 4) | 0x0F); + (NPC_LID_LH << 8) | (NPC_LT_LH_TU_TCP << 4) | 0x0F); rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OUDP, (NPC_LID_LD << 8) | (NPC_LT_LD_UDP << 4) | 0x0F); rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_IUDP, - (NPC_LID_LG << 8) | (NPC_LT_LG_TU_UDP << 4) | 0x0F); + (NPC_LID_LH << 8) | (NPC_LT_LH_TU_UDP << 4) | 0x0F); rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OSCTP, (NPC_LID_LD << 8) | (NPC_LT_LD_SCTP << 4) | 0x0F); rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_ISCTP, - (NPC_LID_LG << 8) | (NPC_LT_LG_TU_SCTP << 4) | + (NPC_LID_LH << 8) | (NPC_LT_LH_TU_SCTP << 4) | 0x0F); err = nix_rx_flowkey_alg_cfg(rvu, blkaddr); @@ -2788,6 +3379,24 @@ int rvu_nix_init(struct rvu *rvu) /* Initialize CGX/LBK/SDP link credits, min/max pkt lengths */ nix_link_config(rvu, blkaddr); + + /* Enable Channel backpressure */ + rvu_write64(rvu, blkaddr, NIX_AF_RX_CFG, BIT_ULL(0)); + + err = rvu_nix_fixes_init(rvu, hw->nix0, blkaddr); + if (err) + return err; + + if (is_block_implemented(rvu->hw, BLKADDR_CPT0)) { + /* Config IPSec headers identification */ + rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_IPSECX(0), + (NPC_LID_LD << 8) | + (NPC_LT_LD_ESP << 4) | 0x0F); + + rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_IPSECX(1), + (8 << 12) | (NPC_LID_LH << 8) | + (NPC_LT_LH_TU_ESP << 4) | 0x0F); + } } return 0; } @@ -2798,6 +3407,7 @@ void rvu_nix_freemem(struct rvu *rvu) struct rvu_block *block; struct nix_txsch *txsch; struct nix_mcast *mcast; + struct nix_txvlan *vlan; struct nix_hw *nix_hw; int blkaddr, lvl; @@ -2818,14 +3428,20 @@ void rvu_nix_freemem(struct rvu *rvu) kfree(txsch->schq.bmap); } + vlan = &nix_hw->txvlan; + kfree(vlan->rsrc.bmap); + mutex_destroy(&vlan->rsrc_lock); + devm_kfree(rvu->dev, vlan->entry2pfvf_map); + mcast = &nix_hw->mcast; qmem_free(rvu->dev, mcast->mce_ctx); qmem_free(rvu->dev, mcast->mcast_buf); mutex_destroy(&mcast->mce_lock); + rvu_nix_fixes_exit(rvu, nix_hw); } } -static int nix_get_nixlf(struct rvu *rvu, u16 pcifunc, int *nixlf) +int nix_get_nixlf(struct rvu *rvu, u16 pcifunc, int *nixlf) { struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc); struct rvu_hwinfo *hw = rvu->hw; @@ -2853,7 +3469,10 @@ int rvu_mbox_handler_nix_lf_start_rx(struct rvu *rvu, struct msg_req *req, return err; rvu_npc_enable_default_entries(rvu, pcifunc, nixlf); - return 0; + + npc_mcam_enable_flows(rvu, pcifunc); + + return rvu_cgx_start_stop_io(rvu, pcifunc, true); } int rvu_mbox_handler_nix_lf_stop_rx(struct rvu *rvu, struct msg_req *req, @@ -2867,22 +3486,30 @@ int rvu_mbox_handler_nix_lf_stop_rx(struct rvu *rvu, struct msg_req *req, return err; rvu_npc_disable_default_entries(rvu, pcifunc, nixlf); - return 0; + + return rvu_cgx_start_stop_io(rvu, pcifunc, false); } void rvu_nix_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int nixlf) { struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc); struct hwctx_disable_req ctx_req; + int pf = rvu_get_pf(pcifunc); + u8 cgx_id, lmac_id; + void *cgxd; int err; ctx_req.hdr.pcifunc = pcifunc; /* Cleanup NPC MCAM entries, free Tx scheduler queues being used */ + rvu_npc_disable_mcam_entries(rvu, pcifunc, nixlf); + rvu_npc_free_mcam_entries(rvu, pcifunc, nixlf); nix_interface_deinit(rvu, pcifunc, nixlf); nix_rx_sync(rvu, blkaddr); nix_txschq_free(rvu, pcifunc); + rvu_cgx_start_stop_io(rvu, pcifunc, false); + if (pfvf->sq_ctx) { ctx_req.ctype = NIX_AQ_CTYPE_SQ; err = nix_lf_hwctx_disable(rvu, &ctx_req); @@ -2904,7 +3531,80 @@ void rvu_nix_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int nixlf) dev_err(rvu->dev, "CQ ctx disable failed\n"); } + /* Disabling CGX and NPC config done for PTP */ + if (pfvf->hw_rx_tstamp_en) { + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + cgx_lmac_ptp_config(cgxd, lmac_id, false); + /* Undo NPC config done for PTP */ + if (npc_config_ts_kpuaction(rvu, pf, pcifunc, false)) + dev_err(rvu->dev, "NPC config for PTP failed\n"); + pfvf->hw_rx_tstamp_en = false; + } + nix_ctx_free(rvu, pfvf); + + if (is_block_implemented(rvu->hw, BLKADDR_CPT0)) { + /* reset the configuration related to inline ipsec */ + rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_IPSEC_CFG0(nixlf), + 0x0); + rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_IPSEC_CFG1(nixlf), + 0x0); + rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_IPSEC_SA_BASE(nixlf), + 0x0); + } +} + +int rvu_mbox_handler_nix_lf_ptp_tx_enable(struct rvu *rvu, struct msg_req *req, + struct msg_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + struct rvu_block *block; + int blkaddr; + int nixlf; + u64 cfg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); + if (blkaddr < 0) + return NIX_AF_ERR_AF_LF_INVALID; + + block = &hw->block[blkaddr]; + nixlf = rvu_get_lf(rvu, block, pcifunc, 0); + if (nixlf < 0) + return NIX_AF_ERR_AF_LF_INVALID; + + cfg = rvu_read64(rvu, blkaddr, NIX_AF_LFX_TX_CFG(nixlf)); + cfg |= BIT_ULL(32); + rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_CFG(nixlf), cfg); + + return 0; +} + +int rvu_mbox_handler_nix_lf_ptp_tx_disable(struct rvu *rvu, struct msg_req *req, + struct msg_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + struct rvu_block *block; + int blkaddr; + int nixlf; + u64 cfg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); + if (blkaddr < 0) + return NIX_AF_ERR_AF_LF_INVALID; + + block = &hw->block[blkaddr]; + nixlf = rvu_get_lf(rvu, block, pcifunc, 0); + if (nixlf < 0) + return NIX_AF_ERR_AF_LF_INVALID; + + cfg = rvu_read64(rvu, blkaddr, NIX_AF_LFX_TX_CFG(nixlf)); + cfg &= ~BIT_ULL(32); + rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_CFG(nixlf), cfg); + + return 0; } int rvu_mbox_handler_nix_lso_format_cfg(struct rvu *rvu, @@ -2957,3 +3657,317 @@ int rvu_mbox_handler_nix_lso_format_cfg(struct rvu *rvu, return 0; } + +int rvu_mbox_handler_nix_set_vlan_tpid(struct rvu *rvu, + struct nix_set_vlan_tpid *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + int nixlf, err, blkaddr; + u64 cfg; + + err = nix_get_nixlf(rvu, pcifunc, &nixlf); + if (err) + return err; + + if (req->vlan_type != NIX_VLAN_TYPE_OUTER && + req->vlan_type != NIX_VLAN_TYPE_INNER) + return NIX_AF_ERR_PARAM; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); + cfg = rvu_read64(rvu, blkaddr, NIX_AF_LFX_TX_CFG(nixlf)); + + if (req->vlan_type == NIX_VLAN_TYPE_OUTER) + cfg = (cfg & ~GENMASK_ULL(15, 0)) | req->tpid; + else + cfg = (cfg & ~GENMASK_ULL(31, 16)) | ((u64)req->tpid << 16); + + rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_CFG(nixlf), cfg); + return 0; +} + +static irqreturn_t rvu_nix_af_rvu_intr_handler(int irq, void *rvu_irq) +{ + struct rvu *rvu = (struct rvu *)rvu_irq; + int blkaddr; + u64 intr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return IRQ_NONE; + + intr = rvu_read64(rvu, blkaddr, NIX_AF_RVU_INT); + + if (intr & BIT_ULL(0)) + dev_err(rvu->dev, "NIX: Unmapped slot error\n"); + + /* Clear interrupts */ + rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT, intr); + return IRQ_HANDLED; +} + +static irqreturn_t rvu_nix_af_err_intr_handler(int irq, void *rvu_irq) +{ + struct rvu *rvu = (struct rvu *)rvu_irq; + int blkaddr; + u64 intr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return IRQ_NONE; + + intr = rvu_read64(rvu, blkaddr, NIX_AF_ERR_INT); + + if (intr & BIT_ULL(14)) + dev_err(rvu->dev, "NIX: Memory fault on NIX_AQ_INST_S read\n"); + + if (intr & BIT_ULL(13)) + dev_err(rvu->dev, "NIX: Memory fault on NIX_AQ_RES_S write\n"); + + if (intr & BIT_ULL(12)) + dev_err(rvu->dev, "NIX: AQ doorbell error\n"); + + if (intr & BIT_ULL(6)) + dev_err(rvu->dev, "NIX: Rx on unmapped PF_FUNC\n"); + + if (intr & BIT_ULL(5)) + dev_err(rvu->dev, "NIX: Rx multicast replication error\n"); + + if (intr & BIT_ULL(4)) + dev_err(rvu->dev, "NIX: Memory fault on NIX_RX_MCE_S read\n"); + + if (intr & BIT_ULL(3)) + dev_err(rvu->dev, "NIX: Memory fault on multicast WQE read\n"); + + if (intr & BIT_ULL(2)) + dev_err(rvu->dev, "NIX: Memory fault on mirror WQE read\n"); + + if (intr & BIT_ULL(1)) + dev_err(rvu->dev, "NIX: Memory fault on mirror pkt write\n"); + + if (intr & BIT_ULL(0)) + dev_err(rvu->dev, "NIX: Memory fault on multicast pkt write\n"); + + /* Clear interrupts */ + rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT, intr); + return IRQ_HANDLED; +} + +static irqreturn_t rvu_nix_af_ras_intr_handler(int irq, void *rvu_irq) +{ + struct rvu *rvu = (struct rvu *)rvu_irq; + int blkaddr; + u64 intr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return IRQ_NONE; + + intr = rvu_read64(rvu, blkaddr, NIX_AF_RAS); + + if (intr & BIT_ULL(34)) + dev_err(rvu->dev, "NIX: Poisoned data on NIX_AQ_INST_S read\n"); + + if (intr & BIT_ULL(33)) + dev_err(rvu->dev, "NIX: Poisoned data on NIX_AQ_RES_S write\n"); + + if (intr & BIT_ULL(32)) + dev_err(rvu->dev, "NIX: Poisoned data on HW context read\n"); + + if (intr & BIT_ULL(4)) + dev_err(rvu->dev, "NIX: Poisoned data on packet read from mirror buffer\n"); + + if (intr & BIT_ULL(3)) + dev_err(rvu->dev, "NIX: Poisoned data on packet read from multicast buffer\n"); + + if (intr & BIT_ULL(2)) + dev_err(rvu->dev, "NIX: Poisoned data on WQE read from mirror buffer\n"); + + if (intr & BIT_ULL(1)) + dev_err(rvu->dev, "NIX: Poisoned data on WQE read from multicast buffer\n"); + + if (intr & BIT_ULL(0)) + dev_err(rvu->dev, "NIX: Poisoned data on NIX_RX_MCE_S read\n"); + + /* Clear interrupts */ + rvu_write64(rvu, blkaddr, NIX_AF_RAS, intr); + return IRQ_HANDLED; +} + +static bool rvu_nix_af_request_irq(struct rvu *rvu, int blkaddr, int offset, + const char *name, irq_handler_t fn) +{ + int rc; + + WARN_ON(rvu->irq_allocated[offset]); + rvu->irq_allocated[offset] = false; + sprintf(&rvu->irq_name[offset * NAME_SIZE], name); + rc = request_irq(pci_irq_vector(rvu->pdev, offset), fn, 0, + &rvu->irq_name[offset * NAME_SIZE], rvu); + if (rc) + dev_warn(rvu->dev, "Failed to register %s irq\n", name); + else + rvu->irq_allocated[offset] = true; + + return rvu->irq_allocated[offset]; +} + +int rvu_nix_register_interrupts(struct rvu *rvu) +{ + int blkaddr, base; + bool rc; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return blkaddr; + + /* Get NIX AF MSIX vectors offset. */ + base = rvu_read64(rvu, blkaddr, NIX_PRIV_AF_INT_CFG) & 0x3ff; + if (!base) { + dev_warn(rvu->dev, + "Failed to get NIX_AF_INT vector offsets\n"); + return 0; + } + + /* Register and enable NIX_AF_RVU_INT interrupt */ + rc = rvu_nix_af_request_irq(rvu, blkaddr, base + NIX_AF_INT_VEC_RVU, + "NIX_AF_RVU_INT", + rvu_nix_af_rvu_intr_handler); + if (!rc) + goto err; + rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1S, ~0ULL); + + /* Register and enable NIX_AF_ERR_INT interrupt */ + rc = rvu_nix_af_request_irq(rvu, blkaddr, base + NIX_AF_INT_VEC_AF_ERR, + "NIX_AF_ERR_INT", + rvu_nix_af_err_intr_handler); + if (!rc) + goto err; + rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1S, ~0ULL); + + /* Register and enable NIX_AF_RAS interrupt */ + rc = rvu_nix_af_request_irq(rvu, blkaddr, base + NIX_AF_INT_VEC_POISON, + "NIX_AF_RAS", + rvu_nix_af_ras_intr_handler); + if (!rc) + goto err; + rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1S, ~0ULL); + + return 0; +err: + rvu_nix_unregister_interrupts(rvu); + return rc; +} + +void rvu_nix_unregister_interrupts(struct rvu *rvu) +{ + int blkaddr, offs, i; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return; + + offs = rvu_read64(rvu, blkaddr, NIX_PRIV_AF_INT_CFG) & 0x3ff; + if (!offs) + return; + + rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1C, ~0ULL); + rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1C, ~0ULL); + rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1C, ~0ULL); + + if (rvu->irq_allocated[offs + NIX_AF_INT_VEC_RVU]) { + free_irq(pci_irq_vector(rvu->pdev, offs + NIX_AF_INT_VEC_RVU), + rvu); + rvu->irq_allocated[offs + NIX_AF_INT_VEC_RVU] = false; + } + + for (i = NIX_AF_INT_VEC_AF_ERR; i < NIX_AF_INT_VEC_CNT; i++) + if (rvu->irq_allocated[offs + i]) { + free_irq(pci_irq_vector(rvu->pdev, offs + i), rvu); + rvu->irq_allocated[offs + i] = false; + } +} + +int rvu_mbox_handler_nix_inline_ipsec_cfg(struct rvu *rvu, + struct nix_inline_ipsec_cfg *req, + struct msg_rsp *rsp) +{ + int blkaddr; + u64 val; + + if (!is_block_implemented(rvu->hw, BLKADDR_CPT0)) + return 0; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, req->hdr.pcifunc); + if (blkaddr < 0) + return NIX_AF_ERR_AF_LF_INVALID; + + if (req->enable) { + /* Set OPCODE and EGRP */ + val = (u64)req->gen_cfg.egrp << 48 | + (u64)req->gen_cfg.opcode << 32; + rvu_write64(rvu, blkaddr, NIX_AF_RX_IPSEC_GEN_CFG, val); + + /* Set CPT queue for inline IPSec */ + val = (u64)req->inst_qsel.cpt_pf_func << 8 | + req->inst_qsel.cpt_slot; + rvu_write64(rvu, blkaddr, NIX_AF_RX_CPTX_INST_QSEL(0), val); + + /* Set CPT credit */ + rvu_write64(rvu, blkaddr, NIX_AF_RX_CPTX_CREDIT(0), + req->cpt_credit); + } else { + rvu_write64(rvu, blkaddr, NIX_AF_RX_IPSEC_GEN_CFG, 0x0); + rvu_write64(rvu, blkaddr, NIX_AF_RX_CPTX_INST_QSEL(0), 0x0); + rvu_write64(rvu, blkaddr, NIX_AF_RX_CPTX_CREDIT(0), 0x3FFFFF); + } + + return 0; +} + +int rvu_mbox_handler_nix_inline_ipsec_lf_cfg( +struct rvu *rvu, struct nix_inline_ipsec_lf_cfg *req, struct msg_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + struct rvu_block *block; + int lf, blkaddr; + u64 val; + + if (!is_block_implemented(rvu->hw, BLKADDR_CPT0)) + return 0; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); + if (blkaddr < 0) + return NIX_AF_ERR_AF_LF_INVALID; + + block = &hw->block[blkaddr]; + lf = rvu_get_lf(rvu, block, pcifunc, 0); + if (lf < 0) + return NIX_AF_ERR_AF_LF_INVALID; + + if (req->enable) { + /* Set TT, TAG_CONST, SA_POW2_SIZE and LENM1_MAX */ + val = (u64)req->ipsec_cfg0.tt << 44 | + (u64)req->ipsec_cfg0.tag_const << 20 | + (u64)req->ipsec_cfg0.sa_pow2_size << 16 | + req->ipsec_cfg0.lenm1_max; + rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_IPSEC_CFG0(lf), val); + + /* Set SA_IDX_W and SA_IDX_MAX */ + val = (u64)req->ipsec_cfg1.sa_idx_w << 32 | + req->ipsec_cfg1.sa_idx_max; + rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_IPSEC_CFG1(lf), val); + + /* Set SA base address */ + rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_IPSEC_SA_BASE(lf), + req->sa_base_addr); + } else { + rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_IPSEC_CFG0(lf), 0x0); + rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_IPSEC_CFG1(lf), 0x0); + rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_IPSEC_SA_BASE(lf), + 0x0); + } + + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c index c0e165dfc403..2476d20280cb 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c @@ -8,8 +8,10 @@ * published by the Free Software Foundation. */ +#include <linux/bitfield.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/stringify.h> #include "rvu_struct.h" #include "rvu_reg.h" @@ -52,8 +54,8 @@ static int npa_aq_enqueue_wait(struct rvu *rvu, struct rvu_block *block, return 0; } -static int rvu_npa_aq_enq_inst(struct rvu *rvu, struct npa_aq_enq_req *req, - struct npa_aq_enq_rsp *rsp) +int rvu_npa_aq_enq_inst(struct rvu *rvu, struct npa_aq_enq_req *req, + struct npa_aq_enq_rsp *rsp) { struct rvu_hwinfo *hw = rvu->hw; u16 pcifunc = req->hdr.pcifunc; @@ -94,6 +96,11 @@ static int rvu_npa_aq_enq_inst(struct rvu *rvu, struct npa_aq_enq_req *req, */ inst.res_addr = (u64)aq->res->iova; + /* Hardware uses same aq->res->base for updating result of + * previous instruction hence wait here till it is done. + */ + spin_lock(&aq->lock); + /* Clean result + context memory */ memset(aq->res->base, 0, aq->res->entry_sz); /* Context needs to be written at RES_ADDR + 128 */ @@ -138,10 +145,10 @@ static int rvu_npa_aq_enq_inst(struct rvu *rvu, struct npa_aq_enq_req *req, break; } - if (rc) + if (rc) { + spin_unlock(&aq->lock); return rc; - - spin_lock(&aq->lock); + } /* Submit the instruction to AQ */ rc = npa_aq_enqueue_wait(rvu, block, &inst); @@ -218,6 +225,8 @@ static int npa_lf_hwctx_disable(struct rvu *rvu, struct hwctx_disable_req *req) } else if (req->ctype == NPA_AQ_CTYPE_AURA) { aq_req.aura.ena = 0; aq_req.aura_mask.ena = 1; + aq_req.aura.bp_ena = 0; + aq_req.aura_mask.bp_ena = 1; cnt = pfvf->aura_ctx->qsize; bmap = pfvf->aura_bmap; } @@ -241,12 +250,50 @@ static int npa_lf_hwctx_disable(struct rvu *rvu, struct hwctx_disable_req *req) return err; } +#ifdef CONFIG_NDC_DIS_DYNAMIC_CACHING +static int npa_lf_hwctx_lockdown(struct rvu *rvu, struct npa_aq_enq_req *req) +{ + struct npa_aq_enq_req lock_ctx_req; + int err; + + if (req->op != NPA_AQ_INSTOP_INIT) + return 0; + + memset(&lock_ctx_req, 0, sizeof(struct npa_aq_enq_req)); + lock_ctx_req.hdr.pcifunc = req->hdr.pcifunc; + lock_ctx_req.ctype = req->ctype; + lock_ctx_req.op = NPA_AQ_INSTOP_LOCK; + lock_ctx_req.aura_id = req->aura_id; + err = rvu_npa_aq_enq_inst(rvu, &lock_ctx_req, NULL); + if (err) + dev_err(rvu->dev, + "PFUNC 0x%x: Failed to lock NPA context %s:%d\n", + req->hdr.pcifunc, + (req->ctype == NPA_AQ_CTYPE_AURA) ? + "Aura" : "Pool", req->aura_id); + return err; +} + +int rvu_mbox_handler_npa_aq_enq(struct rvu *rvu, + struct npa_aq_enq_req *req, + struct npa_aq_enq_rsp *rsp) +{ + int err; + + err = rvu_npa_aq_enq_inst(rvu, req, rsp); + if (!err) + err = npa_lf_hwctx_lockdown(rvu, req); + return err; +} +#else + int rvu_mbox_handler_npa_aq_enq(struct rvu *rvu, struct npa_aq_enq_req *req, struct npa_aq_enq_rsp *rsp) { return rvu_npa_aq_enq_inst(rvu, req, rsp); } +#endif int rvu_mbox_handler_npa_hwctx_disable(struct rvu *rvu, struct hwctx_disable_req *req, @@ -289,6 +336,9 @@ int rvu_mbox_handler_npa_lf_alloc(struct rvu *rvu, req->aura_sz == NPA_AURA_SZ_0 || !req->nr_pools) return NPA_AF_ERR_PARAM; + if (req->way_mask) + req->way_mask &= 0xFFFF; + pfvf = rvu_get_pfvf(rvu, pcifunc); blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, pcifunc); if (!pfvf->npalf || blkaddr < 0) @@ -345,7 +395,8 @@ int rvu_mbox_handler_npa_lf_alloc(struct rvu *rvu, /* Clear way partition mask and set aura offset to '0' */ cfg &= ~(BIT_ULL(34) - 1); /* Set aura size & enable caching of contexts */ - cfg |= (req->aura_sz << 16) | BIT_ULL(34); + cfg |= (req->aura_sz << 16) | BIT_ULL(34) | req->way_mask; + rvu_write64(rvu, blkaddr, NPA_AF_LFX_AURAS_CFG(npalf), cfg); /* Configure aura HW context's base */ @@ -353,7 +404,8 @@ int rvu_mbox_handler_npa_lf_alloc(struct rvu *rvu, (u64)pfvf->aura_ctx->iova); /* Enable caching of qints hw context */ - rvu_write64(rvu, blkaddr, NPA_AF_LFX_QINTS_CFG(npalf), BIT_ULL(36)); + rvu_write64(rvu, blkaddr, NPA_AF_LFX_QINTS_CFG(npalf), + BIT_ULL(36) | req->way_mask << 20); rvu_write64(rvu, blkaddr, NPA_AF_LFX_QINTS_BASE(npalf), (u64)pfvf->npa_qints_ctx->iova); @@ -422,6 +474,10 @@ static int npa_aq_init(struct rvu *rvu, struct rvu_block *block) /* Do not bypass NDC cache */ cfg = rvu_read64(rvu, block->addr, NPA_AF_NDC_CFG); cfg &= ~0x03DULL; +#ifdef CONFIG_NDC_DIS_DYNAMIC_CACHING + /* Disable caching of stack pages */ + cfg |= 0x10ULL; +#endif rvu_write64(rvu, block->addr, NPA_AF_NDC_CFG, cfg); /* Result structure can be followed by Aura/Pool context at @@ -487,3 +543,231 @@ void rvu_npa_lf_teardown(struct rvu *rvu, u16 pcifunc, int npalf) npa_ctx_free(rvu, pfvf); } + +static irqreturn_t rvu_npa_af_rvu_intr_handler(int irq, void *rvu_irq) +{ + struct rvu *rvu = (struct rvu *)rvu_irq; + int blkaddr; + u64 intr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); + if (blkaddr < 0) + return IRQ_NONE; + + intr = rvu_read64(rvu, blkaddr, NPA_AF_RVU_INT); + + if (intr & BIT_ULL(0)) + dev_err(rvu->dev, "NPA: Unmapped slot error\n"); + + /* Clear interrupts */ + rvu_write64(rvu, blkaddr, NPA_AF_RVU_INT, intr); + return IRQ_HANDLED; +} + +static const char *rvu_npa_inpq_to_str(u16 in) +{ + switch (in) { + case 0: + return NULL; + case BIT(NPA_INPQ_NIX0_RX): + return __stringify(NPA_INPQ_NIX0_RX); + case BIT(NPA_INPQ_NIX0_TX): + return __stringify(NPA_INPQ_NIX0_TX); + case BIT(NPA_INPQ_NIX1_RX): + return __stringify(NPA_INPQ_NIX1_RX); + case BIT(NPA_INPQ_NIX1_TX): + return __stringify(NPA_INPQ_NIX1_TX); + case BIT(NPA_INPQ_SSO): + return __stringify(NPA_INPQ_SSO); + case BIT(NPA_INPQ_TIM): + return __stringify(NPA_INPQ_TIM); + case BIT(NPA_INPQ_DPI): + return __stringify(NPA_INPQ_DPI); + case BIT(NPA_INPQ_AURA_OP): + return __stringify(NPA_INPQ_AURA_OP); + case BIT(NPA_INPQ_INTERNAL_RSV): + return __stringify(NPA_INPQ_INTERNAL_RSV); + } + + return "Reserved"; +} + +static irqreturn_t rvu_npa_af_gen_intr_handler(int irq, void *rvu_irq) +{ + struct rvu *rvu = (struct rvu *)rvu_irq; + const char *err_msg; + int blkaddr, val; + u64 intr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); + if (blkaddr < 0) + return IRQ_NONE; + + intr = rvu_read64(rvu, blkaddr, NPA_AF_GEN_INT); + + if (intr & BIT_ULL(32)) + dev_err(rvu->dev, "NPA: Unmapped PF func error\n"); + + val = FIELD_GET(GENMASK(31, 16), intr); + err_msg = rvu_npa_inpq_to_str(val); + if (err_msg) + dev_err(rvu->dev, "NPA: Alloc disabled for %s\n", err_msg); + + val = FIELD_GET(GENMASK(15, 0), intr); + err_msg = rvu_npa_inpq_to_str(val); + if (err_msg) + dev_err(rvu->dev, "NPA: Free disabled for %s\n", err_msg); + + /* Clear interrupts */ + rvu_write64(rvu, blkaddr, NPA_AF_GEN_INT, intr); + return IRQ_HANDLED; +} + +static irqreturn_t rvu_npa_af_err_intr_handler(int irq, void *rvu_irq) +{ + struct rvu *rvu = (struct rvu *)rvu_irq; + int blkaddr; + u64 intr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); + if (blkaddr < 0) + return IRQ_NONE; + + intr = rvu_read64(rvu, blkaddr, NPA_AF_ERR_INT); + + if (intr & BIT_ULL(14)) + dev_err(rvu->dev, "NPA: Memory fault on NPA_AQ_INST_S read\n"); + + if (intr & BIT_ULL(13)) + dev_err(rvu->dev, "NPA: Memory fault on NPA_AQ_RES_S write\n"); + + if (intr & BIT_ULL(12)) + dev_err(rvu->dev, "NPA: AQ doorbell error\n"); + + /* Clear interrupts */ + rvu_write64(rvu, blkaddr, NPA_AF_ERR_INT, intr); + return IRQ_HANDLED; +} + +static irqreturn_t rvu_npa_af_ras_intr_handler(int irq, void *rvu_irq) +{ + struct rvu *rvu = (struct rvu *)rvu_irq; + int blkaddr; + u64 intr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); + if (blkaddr < 0) + return IRQ_NONE; + + intr = rvu_read64(rvu, blkaddr, NPA_AF_RAS); + + if (intr & BIT_ULL(34)) + dev_err(rvu->dev, "NPA: Poisoned data on NPA_AQ_INST_S read\n"); + + if (intr & BIT_ULL(33)) + dev_err(rvu->dev, "NPA: Poisoned data on NPA_AQ_RES_S write\n"); + + if (intr & BIT_ULL(32)) + dev_err(rvu->dev, "NPA: Poisoned data on HW context read\n"); + + /* Clear interrupts */ + rvu_write64(rvu, blkaddr, NPA_AF_RAS, intr); + return IRQ_HANDLED; +} + +static bool rvu_npa_af_request_irq(struct rvu *rvu, int blkaddr, int offset, + const char *name, irq_handler_t fn) +{ + int rc; + + WARN_ON(rvu->irq_allocated[offset]); + rvu->irq_allocated[offset] = false; + sprintf(&rvu->irq_name[offset * NAME_SIZE], name); + rc = request_irq(pci_irq_vector(rvu->pdev, offset), fn, 0, + &rvu->irq_name[offset * NAME_SIZE], rvu); + if (rc) + dev_warn(rvu->dev, "Failed to register %s irq\n", name); + else + rvu->irq_allocated[offset] = true; + + return rvu->irq_allocated[offset]; +} + +int rvu_npa_register_interrupts(struct rvu *rvu) +{ + int blkaddr, base; + bool rc; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); + if (blkaddr < 0) + return blkaddr; + + /* Get NPA AF MSIX vectors offset. */ + base = rvu_read64(rvu, blkaddr, NPA_PRIV_AF_INT_CFG) & 0x3ff; + if (!base) { + dev_warn(rvu->dev, + "Failed to get NPA_AF_INT vector offsets\n"); + return 0; + } + + /* Register and enable NPA_AF_RVU_INT interrupt */ + rc = rvu_npa_af_request_irq(rvu, blkaddr, base + NPA_AF_INT_VEC_RVU, + "NPA_AF_RVU_INT", + rvu_npa_af_rvu_intr_handler); + if (!rc) + goto err; + rvu_write64(rvu, blkaddr, NPA_AF_RVU_INT_ENA_W1S, ~0ULL); + + /* Register and enable NPA_AF_GEN_INT interrupt */ + rc = rvu_npa_af_request_irq(rvu, blkaddr, base + NPA_AF_INT_VEC_GEN, + "NPA_AF_RVU_GEN", + rvu_npa_af_gen_intr_handler); + if (!rc) + goto err; + rvu_write64(rvu, blkaddr, NPA_AF_GEN_INT_ENA_W1S, ~0ULL); + + /* Register and enable NPA_AF_ERR_INT interrupt */ + rc = rvu_npa_af_request_irq(rvu, blkaddr, base + NPA_AF_INT_VEC_AF_ERR, + "NPA_AF_ERR_INT", + rvu_npa_af_err_intr_handler); + if (!rc) + goto err; + rvu_write64(rvu, blkaddr, NPA_AF_ERR_INT_ENA_W1S, ~0ULL); + + /* Register and enable NPA_AF_RAS interrupt */ + rc = rvu_npa_af_request_irq(rvu, blkaddr, base + NPA_AF_INT_VEC_POISON, + "NPA_AF_RAS", + rvu_npa_af_ras_intr_handler); + if (!rc) + goto err; + rvu_write64(rvu, blkaddr, NPA_AF_RAS_ENA_W1S, ~0ULL); + + return 0; +err: + rvu_npa_unregister_interrupts(rvu); + return rc; +} + +void rvu_npa_unregister_interrupts(struct rvu *rvu) +{ + int i, offs, blkaddr; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); + if (blkaddr < 0) + return; + + reg = rvu_read64(rvu, blkaddr, NPA_PRIV_AF_INT_CFG); + offs = reg & 0x3FF; + + rvu_write64(rvu, blkaddr, NPA_AF_RVU_INT_ENA_W1C, ~0ULL); + rvu_write64(rvu, blkaddr, NPA_AF_GEN_INT_ENA_W1C, ~0ULL); + rvu_write64(rvu, blkaddr, NPA_AF_ERR_INT_ENA_W1C, ~0ULL); + rvu_write64(rvu, blkaddr, NPA_AF_RAS_ENA_W1C, ~0ULL); + + for (i = 0; i < NPA_AF_INT_VEC_CNT; i++) + if (rvu->irq_allocated[offs + i]) { + free_irq(pci_irq_vector(rvu->pdev, offs + i), rvu); + rvu->irq_allocated[offs + i] = false; + } +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 15f70273e29c..b193d4bd3f81 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -28,11 +28,62 @@ #define NPC_PARSE_RESULT_DMAC_OFFSET 8 +#define NPC_KEX_CHAN_MASK 0xFFFULL +#define NPC_KEX_PF_FUNC_MASK 0xFFFFULL + +#define NPC_HW_TSTAMP_OFFSET 8 + static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam, int blkaddr, u16 pcifunc); static void npc_mcam_free_all_counters(struct rvu *rvu, struct npc_mcam *mcam, u16 pcifunc); +static int npc_mcam_verify_pf_func(struct rvu *rvu, + struct mcam_entry *entry_data, + u8 intf, u16 pcifunc) +{ + u16 pf_func, pf_func_mask; + + if (intf == NIX_INTF_RX) + return 0; + + pf_func_mask = (entry_data->kw_mask[0] >> 32) & + NPC_KEX_PF_FUNC_MASK; + pf_func = (entry_data->kw[0] >> 32) & NPC_KEX_PF_FUNC_MASK; + + pf_func = htons(pf_func); + if (pf_func_mask != NPC_KEX_PF_FUNC_MASK || pf_func != pcifunc) + return -EINVAL; + + return 0; +} + +int npc_mcam_verify_channel(struct rvu *rvu, u16 pcifunc, u8 intf, u16 channel) +{ + int pf = rvu_get_pf(pcifunc); + u8 cgx_id, lmac_id; + int base = 0, end; + + if (intf == NIX_INTF_TX) + return 0; + + if (is_afvf(pcifunc)) { + end = rvu_get_num_lbk_chans(); + if (end < 0) + return -EINVAL; + } else { + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + base = NIX_CHAN_CGX_LMAC_CHX(cgx_id, lmac_id, 0x0); + /* CGX mapped functions has maximum of 16 channels */ + end = NIX_CHAN_CGX_LMAC_CHX(cgx_id, lmac_id, 0xF); + } + + if (channel < base || channel > end) + return -EINVAL; + + return 0; +} + void rvu_npc_set_pkind(struct rvu *rvu, int pkind, struct rvu_pfvf *pfvf) { int blkaddr; @@ -61,6 +112,32 @@ int rvu_npc_get_pkind(struct rvu *rvu, u16 pf) return -1; } +int npc_config_ts_kpuaction(struct rvu *rvu, int pf, u16 pcifunc, bool en) +{ + int pkind, blkaddr; + u64 val; + + pkind = rvu_npc_get_pkind(rvu, pf); + if (pkind < 0) { + dev_err(rvu->dev, "%s: pkind not mapped\n", __func__); + return -EINVAL; + } + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, pcifunc); + if (blkaddr < 0) { + dev_err(rvu->dev, "%s: NPC block not implemented\n", __func__); + return -EINVAL; + } + val = rvu_read64(rvu, blkaddr, NPC_AF_PKINDX_ACTION0(pkind)); + val &= ~0xff00000ULL; /* Zero ptr advance field */ + if (en) + /* Set to timestamp offset */ + val |= (NPC_HW_TSTAMP_OFFSET << 20); + rvu_write64(rvu, blkaddr, NPC_AF_PKINDX_ACTION0(pkind), val); + + return 0; +} + static int npc_get_nixlf_mcam_index(struct npc_mcam *mcam, u16 pcifunc, int nixlf, int type) { @@ -84,7 +161,7 @@ static int npc_get_nixlf_mcam_index(struct npc_mcam *mcam, return (mcam->nixlf_offset + (nixlf * RSVD_MCAM_ENTRIES_PER_NIXLF)); } -static int npc_get_bank(struct npc_mcam *mcam, int index) +int npc_get_bank(struct npc_mcam *mcam, int index) { int bank = index / mcam->banksize; @@ -106,8 +183,8 @@ static bool is_mcam_entry_enabled(struct rvu *rvu, struct npc_mcam *mcam, return (cfg & 1); } -static void npc_enable_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, - int blkaddr, int index, bool enable) +void npc_enable_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, + int blkaddr, int index, bool enable) { int bank = npc_get_bank(mcam, index); int actbank = bank; @@ -120,6 +197,31 @@ static void npc_enable_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, } } +static void npc_clear_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, + int blkaddr, int index) +{ + int bank = npc_get_bank(mcam, index); + int actbank = bank; + + index &= (mcam->banksize - 1); + for (; bank < (actbank + mcam->banks_per_entry); bank++) { + rvu_write64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_INTF(index, bank, 1), 0); + rvu_write64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_INTF(index, bank, 0), 0); + + rvu_write64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_W0(index, bank, 1), 0); + rvu_write64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_W0(index, bank, 0), 0); + + rvu_write64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_W1(index, bank, 1), 0); + rvu_write64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_W1(index, bank, 0), 0); + } +} + static void npc_get_keyword(struct mcam_entry *entry, int idx, u64 *cam0, u64 *cam1) { @@ -199,6 +301,58 @@ static void npc_get_keyword(struct mcam_entry *entry, int idx, *cam0 = ~*cam1 & kw_mask; } +static void npc_fill_entryword(struct mcam_entry *entry, int idx, + u64 cam0, u64 cam1) +{ + /* Similar to npc_get_keyword, but fills mcam_entry structure from + * CAM registers. + */ + switch (idx) { + case 0: + entry->kw[0] = cam1; + entry->kw_mask[0] = cam1 ^ cam0; + break; + case 1: + entry->kw[1] = cam1; + entry->kw_mask[1] = cam1 ^ cam0; + break; + case 2: + entry->kw[1] |= (cam1 & CAM_MASK(16)) << 48; + entry->kw[2] = (cam1 >> 16) & CAM_MASK(48); + entry->kw_mask[1] |= ((cam1 ^ cam0) & CAM_MASK(16)) << 48; + entry->kw_mask[2] = ((cam1 ^ cam0) >> 16) & CAM_MASK(48); + break; + case 3: + entry->kw[2] |= (cam1 & CAM_MASK(16)) << 48; + entry->kw[3] = (cam1 >> 16) & CAM_MASK(32); + entry->kw_mask[2] |= ((cam1 ^ cam0) & CAM_MASK(16)) << 48; + entry->kw_mask[3] = ((cam1 ^ cam0) >> 16) & CAM_MASK(32); + break; + case 4: + entry->kw[3] |= (cam1 & CAM_MASK(32)) << 32; + entry->kw[4] = (cam1 >> 32) & CAM_MASK(32); + entry->kw_mask[3] |= ((cam1 ^ cam0) & CAM_MASK(32)) << 32; + entry->kw_mask[4] = ((cam1 ^ cam0) >> 32) & CAM_MASK(32); + break; + case 5: + entry->kw[4] |= (cam1 & CAM_MASK(32)) << 32; + entry->kw[5] = (cam1 >> 32) & CAM_MASK(16); + entry->kw_mask[4] |= ((cam1 ^ cam0) & CAM_MASK(32)) << 32; + entry->kw_mask[5] = ((cam1 ^ cam0) >> 32) & CAM_MASK(16); + break; + case 6: + entry->kw[5] |= (cam1 & CAM_MASK(48)) << 16; + entry->kw[6] = (cam1 >> 48) & CAM_MASK(16); + entry->kw_mask[5] |= ((cam1 ^ cam0) & CAM_MASK(48)) << 16; + entry->kw_mask[6] = ((cam1 ^ cam0) >> 48) & CAM_MASK(16); + break; + case 7: + entry->kw[6] |= (cam1 & CAM_MASK(48)) << 16; + entry->kw_mask[6] |= ((cam1 ^ cam0) & CAM_MASK(48)) << 16; + break; + } +} + static void npc_config_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, int blkaddr, int index, u8 intf, struct mcam_entry *entry, bool enable) @@ -211,6 +365,12 @@ static void npc_config_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, actindex = index; index &= (mcam->banksize - 1); + /* Disable before mcam entry update */ + npc_enable_mcam_entry(rvu, mcam, blkaddr, actindex, false); + + /* Clear mcam entry to avoid writes being suppressed by NPC */ + npc_clear_mcam_entry(rvu, mcam, blkaddr, actindex); + /* CAM1 takes the comparison value and * CAM0 specifies match for a bit in key being '0' or '1' or 'dontcare'. * CAM1<n> = 0 & CAM0<n> = 1 => match if key<n> = 0 @@ -251,8 +411,42 @@ static void npc_config_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, /* Enable the entry */ if (enable) npc_enable_mcam_entry(rvu, mcam, blkaddr, actindex, true); - else - npc_enable_mcam_entry(rvu, mcam, blkaddr, actindex, false); +} + +static void npc_read_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, + int blkaddr, u16 src, + struct mcam_entry *entry, u8 *intf, u8 *ena) +{ + int sbank = npc_get_bank(mcam, src); + int bank, kw = 0; + u64 cam0, cam1; + + src &= (mcam->banksize - 1); + bank = sbank; + + for (; bank < (sbank + mcam->banks_per_entry); bank++, kw = kw + 2) { + cam1 = rvu_read64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_W0(src, bank, 1)); + cam0 = rvu_read64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_W0(src, bank, 0)); + npc_fill_entryword(entry, kw, cam0, cam1); + + cam1 = rvu_read64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_W1(src, bank, 1)); + cam0 = rvu_read64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_W1(src, bank, 0)); + npc_fill_entryword(entry, kw + 1, cam0, cam1); + } + + entry->action = rvu_read64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_ACTION(src, sbank)); + entry->vtag_action = + rvu_read64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_TAG_ACT(src, sbank)); + *intf = rvu_read64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_INTF(src, sbank, 1)) & 3; + *ena = rvu_read64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CFG(src, sbank)) & 1; } static void npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, @@ -308,12 +502,12 @@ static u64 npc_get_mcam_action(struct rvu *rvu, struct npc_mcam *mcam, void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, int nixlf, u64 chan, u8 *mac_addr) { - struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc); + u8 mac_mask[] = { [0 ... ETH_ALEN] = 0xFF }; + struct npc_install_flow_req req = { 0 }; + struct npc_install_flow_rsp rsp = { 0 }; struct npc_mcam *mcam = &rvu->hw->mcam; - struct mcam_entry entry = { {0} }; struct nix_rx_action action; - int blkaddr, index, kwi; - u64 mac = 0; + int blkaddr, index; /* AF's VFs work in promiscuous mode */ if (is_afvf(pcifunc)) @@ -323,20 +517,9 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, if (blkaddr < 0) return; - for (index = ETH_ALEN - 1; index >= 0; index--) - mac |= ((u64)*mac_addr++) << (8 * index); - index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); - /* Match ingress channel and DMAC */ - entry.kw[0] = chan; - entry.kw_mask[0] = 0xFFFULL; - - kwi = NPC_PARSE_RESULT_DMAC_OFFSET / sizeof(u64); - entry.kw[kwi] = mac; - entry.kw_mask[kwi] = BIT_ULL(48) - 1; - /* Don't change the action if entry is already enabled * Otherwise RSS action may get overwritten. */ @@ -349,20 +532,20 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, action.pf_func = pcifunc; } - entry.action = *(u64 *)&action; - npc_config_mcam_entry(rvu, mcam, blkaddr, index, - NIX_INTF_RX, &entry, true); - - /* add VLAN matching, setup action and save entry back for later */ - entry.kw[0] |= (NPC_LT_LB_STAG | NPC_LT_LB_CTAG) << 20; - entry.kw_mask[0] |= (NPC_LT_LB_STAG & NPC_LT_LB_CTAG) << 20; - - entry.vtag_action = VTAG0_VALID_BIT | - FIELD_PREP(VTAG0_TYPE_MASK, 0) | - FIELD_PREP(VTAG0_LID_MASK, NPC_LID_LA) | - FIELD_PREP(VTAG0_RELPTR_MASK, 12); - - memcpy(&pfvf->entry, &entry, sizeof(entry)); + req.default_rule = 1; + ether_addr_copy(req.packet.dmac, mac_addr); + ether_addr_copy(req.mask.dmac, mac_mask); + req.features = BIT_ULL(NPC_DMAC); + req.channel = chan; + req.intf = NIX_INTF_RX; + req.op = action.op; + req.hdr.pcifunc = 0; /* AF is requester */ + req.vf = action.pf_func; + req.index = action.index; + req.match_id = action.match_id; + req.flow_key_alg = action.flow_key_alg; + + rvu_mbox_handler_npc_install_flow(rvu, &req, &rsp); } void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc, @@ -448,74 +631,82 @@ void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc, { struct npc_mcam *mcam = &rvu->hw->mcam; struct mcam_entry entry = { {0} }; + struct rvu_hwinfo *hw = rvu->hw; struct nix_rx_action action; -#ifdef MCAST_MCE struct rvu_pfvf *pfvf; -#endif int blkaddr, index; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); if (blkaddr < 0) return; - /* Only PF can add a bcast match entry */ - if (pcifunc & RVU_PFVF_FUNC_MASK) + /* Skip LBK VFs */ + if (is_afvf(pcifunc)) + return; + + /* If pkt replication is not supported, + * then only PF is allowed to add a bcast match entry. + */ + if (!hw->cap.nix_rx_multicast && pcifunc & RVU_PFVF_FUNC_MASK) return; -#ifdef MCAST_MCE - pfvf = rvu_get_pfvf(rvu, pcifunc & ~RVU_PFVF_FUNC_MASK); -#endif + /* Get 'pcifunc' of PF device */ + pcifunc = pcifunc & ~RVU_PFVF_FUNC_MASK; index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_BCAST_ENTRY); - /* Check for L2B bit and LMAC channel - * NOTE: Since MKEX default profile(a reduced version intended to - * accommodate more capability but igoring few bits) a stap-gap - * approach. - * Since we care for L2B which by HRM NPC_PARSE_KEX_S at BIT_POS[25], So - * moved to BIT_POS[13], ignoring ERRCODE, ERRLEV as we'll loose out - * on capability features needed for CoS (/from ODP PoV) e.g: VLAN, - * DSCP. - * - * Reduced layout of MKEX default profile - - * Includes following are (i.e.CHAN, L2/3{B/M}, LA, LB, LC, LD): - * - * BIT_POS[31:28] : LD - * BIT_POS[27:24] : LC - * BIT_POS[23:20] : LB - * BIT_POS[19:16] : LA - * BIT_POS[15:12] : L3B, L3M, L2B, L2M - * BIT_POS[11:00] : CHAN - * + /* Match ingress channel */ + entry.kw[0] = chan; + entry.kw_mask[0] = 0xfffull; + + /* Match broadcast MAC address. + * DMAC is extracted at 0th bit of PARSE_KEX::KW1 */ - entry.kw[0] = BIT_ULL(13) | chan; - entry.kw_mask[0] = BIT_ULL(13) | 0xFFFULL; + entry.kw[1] = 0xffffffffffffull; + entry.kw_mask[1] = 0xffffffffffffull; *(u64 *)&action = 0x00; -#ifdef MCAST_MCE - /* Early silicon doesn't support pkt replication, - * so install entry with UCAST action, so that PF - * receives all broadcast packets. - */ - action.op = NIX_RX_ACTIONOP_MCAST; - action.pf_func = pcifunc; - action.index = pfvf->bcast_mce_idx; -#else - action.op = NIX_RX_ACTIONOP_UCAST; - action.pf_func = pcifunc; -#endif + if (!hw->cap.nix_rx_multicast) { + /* Early silicon doesn't support pkt replication, + * so install entry with UCAST action, so that PF + * receives all broadcast packets. + */ + action.op = NIX_RX_ACTIONOP_UCAST; + action.pf_func = pcifunc; + } else { + pfvf = rvu_get_pfvf(rvu, pcifunc); + action.index = pfvf->bcast_mce_idx; + action.op = NIX_RX_ACTIONOP_MCAST; + } entry.action = *(u64 *)&action; npc_config_mcam_entry(rvu, mcam, blkaddr, index, NIX_INTF_RX, &entry, true); } +void rvu_npc_disable_bcast_entry(struct rvu *rvu, u16 pcifunc) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + int blkaddr, index; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return; + + /* Get 'pcifunc' of PF device */ + pcifunc = pcifunc & ~RVU_PFVF_FUNC_MASK; + + index = npc_get_nixlf_mcam_index(mcam, pcifunc, 0, NIXLF_BCAST_ENTRY); + npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false); +} + void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf, int group, int alg_idx, int mcam_index) { struct npc_mcam *mcam = &rvu->hw->mcam; struct nix_rx_action action; int blkaddr, index, bank; + struct rvu_pfvf *pfvf; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); if (blkaddr < 0) @@ -552,6 +743,11 @@ void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf, rvu_write64(rvu, blkaddr, NPC_AF_MCAMEX_BANKX_ACTION(index, bank), *(u64 *)&action); + /* update the action change in default rule */ + pfvf = rvu_get_pfvf(rvu, pcifunc); + if (pfvf->def_rule) + pfvf->def_rule->rx_action = action; + index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_PROMISC_ENTRY); @@ -566,8 +762,6 @@ void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf, NPC_AF_MCAMEX_BANKX_ACTION(index, bank), *(u64 *)&action); } - - rvu_npc_update_rxvlan(rvu, pcifunc, nixlf); } static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, @@ -607,8 +801,6 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf); else rvu_npc_disable_promisc_entry(rvu, pcifunc, nixlf); - - rvu_npc_update_rxvlan(rvu, pcifunc, nixlf); } void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf) @@ -623,7 +815,9 @@ void rvu_npc_enable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf) void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf) { + struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc); struct npc_mcam *mcam = &rvu->hw->mcam; + struct rvu_npc_mcam_rule *rule; int blkaddr; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); @@ -632,12 +826,50 @@ void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf) mutex_lock(&mcam->lock); - /* Disable and free all MCAM entries mapped to this 'pcifunc' */ + /* Disable MCAM entries directing traffic to this 'pcifunc' */ + list_for_each_entry(rule, &mcam->mcam_rules, list) { + if (rule->intf == NIX_INTF_RX && + rule->rx_action.pf_func == pcifunc) { + npc_enable_mcam_entry(rvu, mcam, blkaddr, + rule->entry, false); + rule->enable = false; + /* Indicate that default rule is disabled */ + if (rule->default_rule) + pfvf->def_rule = NULL; + } + } + + mutex_unlock(&mcam->lock); + + rvu_npc_disable_default_entries(rvu, pcifunc, nixlf); +} + +void rvu_npc_free_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + struct rvu_npc_mcam_rule *rule, *tmp; + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return; + + mutex_lock(&mcam->lock); + + /* Disable and free all MCAM entries owned by this 'pcifunc' */ npc_mcam_free_all_entries(rvu, mcam, blkaddr, pcifunc); - /* Free all MCAM counters mapped to this 'pcifunc' */ + /* Free all MCAM counters owned by this 'pcifunc' */ npc_mcam_free_all_counters(rvu, mcam, pcifunc); + /* Delete MCAM entries owned by this 'pcifunc' from list */ + list_for_each_entry_safe(rule, tmp, &mcam->mcam_rules, list) { + if (rule->owner == pcifunc && !rule->default_rule) { + list_del(&rule->list); + kfree(rule); + } + } + mutex_unlock(&mcam->lock); rvu_npc_disable_default_entries(rvu, pcifunc, nixlf); @@ -655,37 +887,78 @@ void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf) (((bytesm1) << 16) | ((hdr_ofs) << 8) | ((ena) << 7) | \ ((flags_ena) << 6) | ((key_ofs) & 0x3F)) -static void npc_config_ldata_extract(struct rvu *rvu, int blkaddr) +static void npc_config_tx_ldata_extract(struct rvu *rvu, int blkaddr) { - struct npc_mcam *mcam = &rvu->hw->mcam; - int lid, ltype; - int lid_count; u64 cfg; - cfg = rvu_read64(rvu, blkaddr, NPC_AF_CONST); - lid_count = (cfg >> 4) & 0xF; + /* Default TX MCAM KEX profile */ + /* Layer A: Ethernet: */ - /* First clear any existing config i.e - * disable LDATA and FLAGS extraction. - */ - for (lid = 0; lid < lid_count; lid++) { - for (ltype = 0; ltype < 16; ltype++) { - SET_KEX_LD(NIX_INTF_RX, lid, ltype, 0, 0ULL); - SET_KEX_LD(NIX_INTF_RX, lid, ltype, 1, 0ULL); - SET_KEX_LD(NIX_INTF_TX, lid, ltype, 0, 0ULL); - SET_KEX_LD(NIX_INTF_TX, lid, ltype, 1, 0ULL); + /* PF_FUNC: 2B , KW0 [47:32] */ + cfg = KEX_LD_CFG(0x01, 0x0, 0x1, 0x0, 0x4); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, 0, cfg); - SET_KEX_LDFLAGS(NIX_INTF_RX, 0, ltype, 0ULL); - SET_KEX_LDFLAGS(NIX_INTF_RX, 1, ltype, 0ULL); - SET_KEX_LDFLAGS(NIX_INTF_TX, 0, ltype, 0ULL); - SET_KEX_LDFLAGS(NIX_INTF_TX, 1, ltype, 0ULL); - } - } + /* PF_FUNC incase of higig2 */ + cfg = KEX_LD_CFG(0x01, 0x0, 0x1, 0x0, 0x4); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, 0, + cfg); - if (mcam->keysize != NPC_MCAM_KEY_X2) - return; + /* Layer B: Single VLAN (CTAG) */ + /* CTAG VLAN[2..3] KW0[63:48] */ + cfg = KEX_LD_CFG(0x01, 0x2, 0x1, 0x0, 0x6); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LB, NPC_LT_LB_CTAG, 0, cfg); + + /* CTAG VLAN[2..3] KW1[15:0] */ + cfg = KEX_LD_CFG(0x01, 0x4, 0x1, 0x0, 0x8); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LB, NPC_LT_LB_CTAG, 1, cfg); + + /* Layer B: Stacked VLAN (STAG|QinQ) */ + /* Outer VLAN: 2 bytes, KW0[63:48] */ + cfg = KEX_LD_CFG(0x01, 0x2, 0x1, 0x0, 0x6); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 0, cfg); + + /* Outer VLAN: 2 Bytes, KW1[15:0] */ + cfg = KEX_LD_CFG(0x01, 0x8, 0x1, 0x0, 0x8); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 1, cfg); + + /* DMAC: 6 bytes, KW1[63:16] */ + cfg = KEX_LD_CFG(0x05, 0x8, 0x1, 0x0, 0xa); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, 1, cfg); + + /* clasification in higig2 header */ + cfg = KEX_LD_CFG(0x01, 0x10, 0x1, 0x0, 0xa); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, 1, + cfg); + + /* Layer C: IPv4 */ + /* SIP+DIP: 8 bytes, KW2[63:0] */ + cfg = KEX_LD_CFG(0x07, 0xc, 0x1, 0x0, 0x10); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LC, NPC_LT_LC_IP, 0, cfg); + + /* Layer D:UDP */ + /* SPORT: 2 bytes, KW3[15:0] */ + cfg = KEX_LD_CFG(0x1, 0x0, 0x1, 0x0, 0x18); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LD, NPC_LT_LD_UDP, 0, cfg); - /* Default MCAM KEX profile */ + /* DPORT: 2 bytes, KW3[31:16] */ + cfg = KEX_LD_CFG(0x1, 0x2, 0x1, 0x0, 0x1a); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LD, NPC_LT_LD_UDP, 1, cfg); + + /* Layer D:TCP */ + /* SPORT: 2 bytes, KW3[15:0] */ + cfg = KEX_LD_CFG(0x1, 0x0, 0x1, 0x0, 0x18); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LD, NPC_LT_LD_TCP, 0, cfg); + + /* DPORT: 2 bytes, KW3[31:16] */ + cfg = KEX_LD_CFG(0x1, 0x2, 0x1, 0x0, 0x1a); + SET_KEX_LD(NIX_INTF_TX, NPC_LID_LD, NPC_LT_LD_TCP, 1, cfg); +} + +static void npc_config_rx_ldata_extract(struct rvu *rvu, int blkaddr) +{ + u64 cfg; + + /* Default RX MCAM KEX profile */ /* Layer A: Ethernet: */ /* DMAC: 6 bytes, KW1[47:0] */ @@ -696,22 +969,34 @@ static void npc_config_ldata_extract(struct rvu *rvu, int blkaddr) cfg = KEX_LD_CFG(0x01, 0xc, 0x1, 0x0, 0x4); SET_KEX_LD(NIX_INTF_RX, NPC_LID_LA, NPC_LT_LA_ETHER, 1, cfg); + /* Classification in higig2 header */ + cfg = KEX_LD_CFG(0x01, 0x8, 0x1, 0x0, NPC_PARSE_RESULT_DMAC_OFFSET); + SET_KEX_LD(NIX_INTF_RX, NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, 0, cfg); + + /* Vid in higig2 header */ + cfg = KEX_LD_CFG(0x01, 0xc, 0x1, 0x0, NPC_PARSE_RESULT_DMAC_OFFSET + 2); + SET_KEX_LD(NIX_INTF_RX, NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, 1, cfg); + /* Layer B: Single VLAN (CTAG) */ /* CTAG VLAN[2..3] + Ethertype, 4 bytes, KW0[63:32] */ - cfg = KEX_LD_CFG(0x03, 0x0, 0x1, 0x0, 0x4); + cfg = KEX_LD_CFG(0x03, 0x2, 0x1, 0x0, 0x4); SET_KEX_LD(NIX_INTF_RX, NPC_LID_LB, NPC_LT_LB_CTAG, 0, cfg); /* Layer B: Stacked VLAN (STAG|QinQ) */ - /* CTAG VLAN[2..3] + Ethertype, 4 bytes, KW0[63:32] */ - cfg = KEX_LD_CFG(0x03, 0x4, 0x1, 0x0, 0x4); - SET_KEX_LD(NIX_INTF_RX, NPC_LID_LB, NPC_LT_LB_STAG, 0, cfg); - SET_KEX_LD(NIX_INTF_RX, NPC_LID_LB, NPC_LT_LB_QINQ, 0, cfg); + /* Outer VLAN: 2 bytes, KW0[63:48] */ + cfg = KEX_LD_CFG(0x01, 0x2, 0x1, 0x0, 0x6); + SET_KEX_LD(NIX_INTF_RX, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 0, cfg); + + /* Ethertype: 2 bytes, KW0[47:32] */ + cfg = KEX_LD_CFG(0x01, 0x8, 0x1, 0x0, 0x4); + SET_KEX_LD(NIX_INTF_RX, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 1, cfg); /* Layer C: IPv4 */ /* SIP+DIP: 8 bytes, KW2[63:0] */ cfg = KEX_LD_CFG(0x07, 0xc, 0x1, 0x0, 0x10); SET_KEX_LD(NIX_INTF_RX, NPC_LID_LC, NPC_LT_LC_IP, 0, cfg); /* TOS: 1 byte, KW1[63:56] */ + cfg = KEX_LD_CFG(0x0, 0x1, 0x1, 0x0, 0xf); SET_KEX_LD(NIX_INTF_RX, NPC_LID_LC, NPC_LT_LC_IP, 1, cfg); @@ -719,6 +1004,7 @@ static void npc_config_ldata_extract(struct rvu *rvu, int blkaddr) /* SPORT: 2 bytes, KW3[15:0] */ cfg = KEX_LD_CFG(0x1, 0x0, 0x1, 0x0, 0x18); SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_UDP, 0, cfg); + /* DPORT: 2 bytes, KW3[31:16] */ cfg = KEX_LD_CFG(0x1, 0x2, 0x1, 0x0, 0x1a); SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_UDP, 1, cfg); @@ -727,11 +1013,49 @@ static void npc_config_ldata_extract(struct rvu *rvu, int blkaddr) /* SPORT: 2 bytes, KW3[15:0] */ cfg = KEX_LD_CFG(0x1, 0x0, 0x1, 0x0, 0x18); SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_TCP, 0, cfg); + /* DPORT: 2 bytes, KW3[31:16] */ cfg = KEX_LD_CFG(0x1, 0x2, 0x1, 0x0, 0x1a); SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_TCP, 1, cfg); } +static void npc_config_ldata_extract(struct rvu *rvu, int blkaddr) +{ + int lid, ltype; + int lid_count; + u64 cfg; + + cfg = rvu_read64(rvu, blkaddr, NPC_AF_CONST); + lid_count = (cfg >> 4) & 0xF; + + /* First clear any existing config i.e + * disable LDATA and FLAGS extraction. + */ + for (lid = 0; lid < lid_count; lid++) { + for (ltype = 0; ltype < 16; ltype++) { + SET_KEX_LD(NIX_INTF_RX, lid, ltype, 0, 0ULL); + SET_KEX_LD(NIX_INTF_RX, lid, ltype, 1, 0ULL); + SET_KEX_LD(NIX_INTF_TX, lid, ltype, 0, 0ULL); + SET_KEX_LD(NIX_INTF_TX, lid, ltype, 1, 0ULL); + + SET_KEX_LDFLAGS(NIX_INTF_RX, 0, ltype, 0ULL); + SET_KEX_LDFLAGS(NIX_INTF_RX, 1, ltype, 0ULL); + SET_KEX_LDFLAGS(NIX_INTF_TX, 0, ltype, 0ULL); + SET_KEX_LDFLAGS(NIX_INTF_TX, 1, ltype, 0ULL); + } + } + + cfg = (rvu_read64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(0)) >> 32) & 0x07; + /* Default profile works with key size NPC_MCAM_KEY_X2 */ + if (cfg != NPC_MCAM_KEY_X2) + return; + + /* Config RX ldata extract */ + npc_config_rx_ldata_extract(rvu, blkaddr); + /* Config TX ldata extract */ + npc_config_tx_ldata_extract(rvu, blkaddr); +} + static void npc_program_mkex_profile(struct rvu *rvu, int blkaddr, struct npc_mcam_kex *mkex) { @@ -790,8 +1114,10 @@ static void npc_load_mkex_profile(struct rvu *rvu, int blkaddr) if (!strncmp(mkex_profile, "default", MKEX_NAME_LEN)) goto load_default; - if (cgx_get_mkex_prfl_info(&prfl_addr, &prfl_sz)) + if (!rvu->fwdata) goto load_default; + prfl_addr = rvu->fwdata->mcam_addr; + prfl_sz = rvu->fwdata->mcam_sz; if (!prfl_addr || !prfl_sz) goto load_default; @@ -806,13 +1132,7 @@ static void npc_load_mkex_profile(struct rvu *rvu, int blkaddr) /* Compare with mkex mod_param name string */ if (mcam_kex->mkex_sign == MKEX_SIGN && !strncmp(mcam_kex->name, mkex_profile, MKEX_NAME_LEN)) { - /* Due to an errata (35786) in A0 pass silicon, - * parse nibble enable configuration has to be - * identical for both Rx and Tx interfaces. - */ - if (is_rvu_9xxx_A0(rvu) && - mcam_kex->keyx_cfg[NIX_INTF_RX] != - mcam_kex->keyx_cfg[NIX_INTF_TX]) + if (!is_parse_nibble_config_valid(rvu, mcam_kex)) goto load_default; /* Program selected mkex profile */ @@ -1064,6 +1384,13 @@ static int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) mcam->hprio_count = mcam->lprio_count; mcam->hprio_end = mcam->hprio_count; + /* Reserve last counter for MCAM RX miss action which is set to + * drop pkt. This way we will know how many pkts didn't match + * any MCAM entry. + */ + mcam->counters.max--; + mcam->rx_miss_act_cntr = mcam->counters.max; + /* Allocate bitmap for managing MCAM counters and memory * for saving counter to RVU PFFUNC allocation mapping. */ @@ -1101,6 +1428,7 @@ free_mem: int rvu_npc_init(struct rvu *rvu) { struct npc_pkind *pkind = &rvu->hw->pkind; + struct npc_mcam *mcam = &rvu->hw->mcam; u64 keyz = NPC_MCAM_KEY_X2; int blkaddr, entry, bank, err; u64 cfg, nibble_ena; @@ -1143,39 +1471,47 @@ int rvu_npc_init(struct rvu *rvu) /* Config Inner IPV4 NPC layer info */ rvu_write64(rvu, blkaddr, NPC_AF_PCK_DEF_IIP4, - (NPC_LID_LF << 8) | (NPC_LT_LF_TU_IP << 4) | 0x0F); + (NPC_LID_LG << 8) | (NPC_LT_LG_TU_IP << 4) | 0x0F); /* Enable below for Rx pkts. * - Outer IPv4 header checksum validation. - * - Detect outer L2 broadcast address and set NPC_RESULT_S[L2M]. + * - Detect outer L2 broadcast address and set NPC_RESULT_S[L2B]. + * - Detect outer L2 multicast address and set NPC_RESULT_S[L2M]. * - Inner IPv4 header checksum validation. * - Set non zero checksum error code value */ rvu_write64(rvu, blkaddr, NPC_AF_PCK_CFG, rvu_read64(rvu, blkaddr, NPC_AF_PCK_CFG) | - BIT_ULL(32) | BIT_ULL(24) | BIT_ULL(6) | - BIT_ULL(2) | BIT_ULL(1)); + ((u64)NPC_EC_OIP4_CSUM << 32) | (NPC_EC_IIP4_CSUM << 24) | + BIT_ULL(7) | BIT_ULL(6) | BIT_ULL(2) | BIT_ULL(1)); /* Set RX and TX side MCAM search key size. - * LA..LD (ltype only) + Channel + * LA..LE (ltype only) + Channel */ - nibble_ena = 0x49247; + nibble_ena = 0x249207; rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_RX), ((keyz & 0x3) << 32) | nibble_ena); - /* Due to an errata (35786) in A0 pass silicon, parse nibble enable - * configuration has to be identical for both Rx and Tx interfaces. - */ - if (!is_rvu_9xxx_A0(rvu)) - nibble_ena = (1ULL << 19) - 1; + + /* Extract Ltypes LID_LA to LID_LE */ + nibble_ena = rvu_npc_get_tx_nibble_cfg(rvu, nibble_ena); + if (!nibble_ena) + nibble_ena = 0x249200; rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_TX), ((keyz & 0x3) << 32) | nibble_ena); + /* Configure MKEX profile */ + npc_load_mkex_profile(rvu, blkaddr); + err = npc_mcam_rsrcs_init(rvu, blkaddr); if (err) return err; - /* Configure MKEX profile */ - npc_load_mkex_profile(rvu, blkaddr); + err = npc_flow_steering_init(rvu, blkaddr); + if (err) { + dev_err(rvu->dev, + "Incorrect mkex profile loaded using default mkex\n"); + npc_config_ldata_extract(rvu, blkaddr); + } /* Set TX miss action to UCAST_DEFAULT i.e * transmit the packet on NIX LF SQ's default channel. @@ -1183,10 +1519,13 @@ int rvu_npc_init(struct rvu *rvu) rvu_write64(rvu, blkaddr, NPC_AF_INTFX_MISS_ACT(NIX_INTF_TX), NIX_TX_ACTIONOP_UCAST_DEFAULT); - /* If MCAM lookup doesn't result in a match, drop the received packet */ + /* If MCAM lookup doesn't result in a match, drop the received packet + * And map this action to a counter to count dropped pkts. + */ rvu_write64(rvu, blkaddr, NPC_AF_INTFX_MISS_ACT(NIX_INTF_RX), NIX_RX_ACTIONOP_DROP); - + rvu_write64(rvu, blkaddr, NPC_AF_INTFX_MISS_STAT_ACT(NIX_INTF_RX), + BIT_ULL(9) | mcam->rx_miss_act_cntr); return 0; } @@ -1200,6 +1539,44 @@ void rvu_npc_freemem(struct rvu *rvu) mutex_destroy(&mcam->lock); } +void rvu_npc_get_mcam_entry_alloc_info(struct rvu *rvu, u16 pcifunc, + int blkaddr, int *alloc_cnt, + int *enable_cnt) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + int entry; + + *alloc_cnt = 0; + *enable_cnt = 0; + + for (entry = 0; entry < mcam->bmap_entries; entry++) { + if (mcam->entry2pfvf_map[entry] == pcifunc) { + (*alloc_cnt)++; + if (is_mcam_entry_enabled(rvu, mcam, blkaddr, entry)) + (*enable_cnt)++; + } + } +} + +void rvu_npc_get_mcam_counter_alloc_info(struct rvu *rvu, u16 pcifunc, + int blkaddr, int *alloc_cnt, + int *enable_cnt) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + int cntr; + + *alloc_cnt = 0; + *enable_cnt = 0; + + for (cntr = 0; cntr < mcam->counters.max; cntr++) { + if (mcam->cntr2pfvf_map[cntr] == pcifunc) { + (*alloc_cnt)++; + if (mcam->cntr_refcnt[cntr]) + (*enable_cnt)++; + } + } +} + static int npc_mcam_verify_entry(struct npc_mcam *mcam, u16 pcifunc, int entry) { @@ -1717,18 +2094,47 @@ exit: return rc; } +int rvu_mbox_handler_npc_mcam_read_entry(struct rvu *rvu, + struct npc_mcam_read_entry_req *req, + struct npc_mcam_read_entry_rsp *rsp) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + u16 pcifunc = req->hdr.pcifunc; + int blkaddr, rc; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return NPC_MCAM_INVALID_REQ; + + mutex_lock(&mcam->lock); + rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry); + if (!rc) { + npc_read_mcam_entry(rvu, mcam, blkaddr, req->entry, + &rsp->entry_data, + &rsp->intf, &rsp->enable); + } + + mutex_unlock(&mcam->lock); + return rc; +} + int rvu_mbox_handler_npc_mcam_write_entry(struct rvu *rvu, struct npc_mcam_write_entry_req *req, struct msg_rsp *rsp) { struct npc_mcam *mcam = &rvu->hw->mcam; u16 pcifunc = req->hdr.pcifunc; + u16 channel, chan_mask; int blkaddr, rc; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); if (blkaddr < 0) return NPC_MCAM_INVALID_REQ; + chan_mask = req->entry_data.kw_mask[0] & NPC_KEX_CHAN_MASK; + channel = req->entry_data.kw[0] & NPC_KEX_CHAN_MASK; + channel &= chan_mask; + mutex_lock(&mcam->lock); rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry); if (rc) @@ -1745,6 +2151,17 @@ int rvu_mbox_handler_npc_mcam_write_entry(struct rvu *rvu, goto exit; } + if (npc_mcam_verify_channel(rvu, pcifunc, req->intf, channel)) { + rc = NPC_MCAM_INVALID_REQ; + goto exit; + } + + if (npc_mcam_verify_pf_func(rvu, &req->entry_data, req->intf, + pcifunc)) { + rc = NPC_MCAM_INVALID_REQ; + goto exit; + } + npc_config_mcam_entry(rvu, mcam, blkaddr, req->entry, req->intf, &req->entry_data, req->enable_entry); @@ -1967,10 +2384,11 @@ int rvu_mbox_handler_npc_mcam_free_counter(struct rvu *rvu, index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry); if (index >= mcam->bmap_entries) break; + entry = index + 1; + if (mcam->entry2cntr_map[index] != req->cntr) continue; - entry = index + 1; npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, index, req->cntr); } @@ -2013,10 +2431,11 @@ int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu, index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry); if (index >= mcam->bmap_entries) break; + entry = index + 1; + if (mcam->entry2cntr_map[index] != req->cntr) continue; - entry = index + 1; npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, index, req->cntr); } @@ -2080,6 +2499,7 @@ int rvu_mbox_handler_npc_mcam_alloc_and_write_entry(struct rvu *rvu, struct npc_mcam *mcam = &rvu->hw->mcam; u16 entry = NPC_MCAM_ENTRY_INVALID; u16 cntr = NPC_MCAM_ENTRY_INVALID; + u16 channel, chan_mask; int blkaddr, rc; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); @@ -2089,6 +2509,17 @@ int rvu_mbox_handler_npc_mcam_alloc_and_write_entry(struct rvu *rvu, if (req->intf != NIX_INTF_RX && req->intf != NIX_INTF_TX) return NPC_MCAM_INVALID_REQ; + chan_mask = req->entry_data.kw_mask[0] & NPC_KEX_CHAN_MASK; + channel = req->entry_data.kw[0] & NPC_KEX_CHAN_MASK; + channel &= chan_mask; + + if (npc_mcam_verify_channel(rvu, req->hdr.pcifunc, req->intf, channel)) + return NPC_MCAM_INVALID_REQ; + + if (npc_mcam_verify_pf_func(rvu, &req->entry_data, req->intf, + req->hdr.pcifunc)) + return NPC_MCAM_INVALID_REQ; + /* Try to allocate a MCAM entry */ entry_req.hdr.pcifunc = req->hdr.pcifunc; entry_req.contig = true; @@ -2187,26 +2618,70 @@ int rvu_mbox_handler_npc_get_kex_cfg(struct rvu *rvu, struct msg_req *req, return 0; } -int rvu_npc_update_rxvlan(struct rvu *rvu, u16 pcifunc, int nixlf) +bool rvu_npc_write_default_rule(struct rvu *rvu, int blkaddr, int nixlf, + u16 pcifunc, u8 intf, struct mcam_entry *entry, + int *index) { - struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc); struct npc_mcam *mcam = &rvu->hw->mcam; - int blkaddr, index; bool enable; - blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); - if (blkaddr < 0) - return NIX_AF_ERR_AF_LF_INVALID; + *index = npc_get_nixlf_mcam_index(mcam, pcifunc, + nixlf, NIXLF_UCAST_ENTRY); + /* dont force enable unicast entry */ + enable = is_mcam_entry_enabled(rvu, mcam, blkaddr, *index); + npc_config_mcam_entry(rvu, mcam, blkaddr, *index, intf, entry, enable); - if (!pfvf->rxvlan) - return 0; + return enable; +} - index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, - NIXLF_UCAST_ENTRY); - pfvf->entry.action = npc_get_mcam_action(rvu, mcam, blkaddr, index); - enable = is_mcam_entry_enabled(rvu, mcam, blkaddr, index); - npc_config_mcam_entry(rvu, mcam, blkaddr, pfvf->rxvlan_index, - NIX_INTF_RX, &pfvf->entry, enable); +int rvu_mbox_handler_npc_set_pkind(struct rvu *rvu, + struct npc_set_pkind *req, + struct msg_rsp *rsp) +{ + struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); + int pf = rvu_get_pf(req->hdr.pcifunc); + int blkaddr, nixlf, rc; + u64 rxpkind, txpkind; + u8 cgx_id, lmac_id; + + /* use default pkind to disable edsa/higig */ + rxpkind = rvu_npc_get_pkind(rvu, pf); + txpkind = NPC_TX_DEF_PKIND; + + if (req->mode & OTX2_PRIV_FLAGS_EDSA) { + rxpkind = NPC_RX_EDSA_PKIND; + } else if (req->mode & OTX2_PRIV_FLAGS_HIGIG) { + rxpkind = NPC_RX_HIGIG_PKIND; + txpkind = NPC_TX_HIGIG_PKIND; + } else if (req->mode & OTX2_PRIV_FLAGS_CUSTOM) { + rxpkind = req->pkind; + txpkind = req->pkind; + } + + if (req->dir & PKIND_RX) { + /* rx pkind set req valid only for cgx mapped PFs */ + if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) + return 0; + rvu_get_cgx_lmac_id(pfvf->cgx_lmac, &cgx_id, &lmac_id); + + rc = cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), + lmac_id, rxpkind); + if (rc) + return rc; + } + + if (req->dir & PKIND_TX) { + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, req->hdr.pcifunc); + + /* tx pkind set req valid if NIXLF attached */ + if (!pfvf->nixlf || blkaddr < 0) + return NIX_AF_ERR_AF_LF_INVALID; + nixlf = rvu_get_lf(rvu, &rvu->hw->block[blkaddr], + req->hdr.pcifunc, 0); + + rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), + txpkind); + } return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c new file mode 100644 index 000000000000..c587b8dab336 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -0,0 +1,1175 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Admin Function driver + * + * Copyright (C) 2019 Marvell International Ltd. + */ + +#include <linux/bitfield.h> + +#include "rvu_struct.h" +#include "rvu_reg.h" +#include "rvu.h" +#include "npc.h" + +#define NPC_BYTESM GENMASK_ULL(19, 16) +#define NPC_HDR_OFFSET GENMASK_ULL(15, 8) +#define NPC_KEY_OFFSET GENMASK_ULL(5, 0) +#define NPC_LDATA_EN BIT_ULL(7) + +static const char * const npc_flow_names[] = { + [NPC_DMAC] = "dmac", + [NPC_SMAC] = "smac", + [NPC_ETYPE] = "ether type", + [NPC_OUTER_VID] = "outer vlan id", + [NPC_TOS] = "tos", + [NPC_SIP_IPV4] = "ipv4 source ip", + [NPC_DIP_IPV4] = "ipv4 destination ip", + [NPC_SIP_IPV6] = "ipv6 source ip", + [NPC_DIP_IPV6] = "ipv6 destination ip", + [NPC_SPORT_TCP] = "tcp source port", + [NPC_DPORT_TCP] = "tcp destination port", + [NPC_SPORT_UDP] = "udp source port", + [NPC_DPORT_UDP] = "udp destination port", + [NPC_UNKNOWN] = "unknown", +}; + +const char *npc_get_field_name(u8 hdr) +{ + if (hdr >= ARRAY_SIZE(npc_flow_names)) + return npc_flow_names[NPC_UNKNOWN]; + + return npc_flow_names[hdr]; +} + +/* Compute keyword masks and figure out the number of keywords a field + * spans in the key. + */ +static void npc_set_kw_masks(struct npc_mcam *mcam, enum key_fields type, + u8 nr_bits, int start_kwi, int offset, u8 intf) +{ + struct npc_key_field *field = &mcam->rx_key_fields[type]; + u8 bits_in_kw; + int max_kwi; + + if (mcam->banks_per_entry == 1) + max_kwi = 1; /* NPC_MCAM_KEY_X1 */ + else if (mcam->banks_per_entry == 2) + max_kwi = 3; /* NPC_MCAM_KEY_X2 */ + else + max_kwi = 6; /* NPC_MCAM_KEY_X4 */ + + if (intf == NIX_INTF_TX) + field = &mcam->tx_key_fields[type]; + + if (offset + nr_bits <= 64) { + /* one KW only */ + if (start_kwi > max_kwi) + return; + field->kw_mask[start_kwi] |= (BIT_ULL(nr_bits) - 1) << offset; + field->nr_kws = 1; + } else if (offset + nr_bits > 64 && + offset + nr_bits <= 128) { + /* two KWs */ + if (start_kwi + 1 > max_kwi) + return; + /* first KW mask */ + bits_in_kw = 64 - offset; + field->kw_mask[start_kwi] |= (BIT_ULL(bits_in_kw) - 1) + << offset; + /* second KW mask i.e. mask for rest of bits */ + bits_in_kw = nr_bits + offset - 64; + field->kw_mask[start_kwi + 1] |= BIT_ULL(bits_in_kw) - 1; + field->nr_kws = 2; + } else { + /* three KWs */ + if (start_kwi + 2 > max_kwi) + return; + /* first KW mask */ + bits_in_kw = 64 - offset; + field->kw_mask[start_kwi] |= (BIT_ULL(bits_in_kw) - 1) + << offset; + /* second KW mask */ + field->kw_mask[start_kwi + 1] = ~0ULL; + /* third KW mask i.e. mask for rest of bits */ + bits_in_kw = nr_bits + offset - 128; + field->kw_mask[start_kwi + 2] |= BIT_ULL(bits_in_kw) - 1; + field->nr_kws = 3; + } +} + +/* Helper function to figure out whether field exists in the key */ +static bool npc_is_field_present(struct rvu *rvu, enum key_fields type, u8 intf) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + struct npc_key_field *input; + + input = &mcam->rx_key_fields[type]; + if (intf == NIX_INTF_TX) + input = &mcam->tx_key_fields[type]; + + return input->nr_kws > 0; +} + +static bool npc_is_same(struct npc_key_field *input, + struct npc_key_field *field) +{ + int ret; + + ret = memcmp(&input->layer_mdata, &field->layer_mdata, + sizeof(struct npc_layer_mdata)); + return ret == 0; +} + +static void npc_set_layer_mdata(struct npc_mcam *mcam, enum key_fields type, + u64 cfg, u8 lid, u8 lt, u8 intf) +{ + struct npc_key_field *input = &mcam->rx_key_fields[type]; + + if (intf == NIX_INTF_TX) + input = &mcam->tx_key_fields[type]; + + input->layer_mdata.hdr = FIELD_GET(NPC_HDR_OFFSET, cfg); + input->layer_mdata.key = FIELD_GET(NPC_KEY_OFFSET, cfg); + input->layer_mdata.len = FIELD_GET(NPC_BYTESM, cfg) + 1; + input->layer_mdata.ltype = lt; + input->layer_mdata.lid = lid; +} + +static bool npc_check_overlap_fields(struct npc_key_field *input1, + struct npc_key_field *input2) +{ + int kwi; + + /* Fields with same layer id and different ltypes are mutually + * exclusive hence they can be overlapped + */ + if (input1->layer_mdata.lid == input2->layer_mdata.lid && + input1->layer_mdata.ltype != input2->layer_mdata.ltype) + return false; + + for (kwi = 0; kwi < NPC_MAX_KWS_IN_KEY; kwi++) { + if (input1->kw_mask[kwi] & input2->kw_mask[kwi]) + return true; + } + + return false; +} + +/* Helper function to check whether given field overlaps with any other fields + * in the key. Due to limitations on key size and the key extraction profile in + * use higher layers can overwrite lower layer's header fields. Hence overlap + * needs to be checked. + */ +static bool npc_check_overlap(struct rvu *rvu, int blkaddr, + enum key_fields type, u8 start_lid, u8 intf) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + struct npc_key_field *dummy, *input; + int start_kwi, offset; + u8 nr_bits, lid, lt, ld; + u64 cfg; + + dummy = &mcam->rx_key_fields[NPC_UNKNOWN]; + input = &mcam->rx_key_fields[type]; + + if (intf == NIX_INTF_TX) { + dummy = &mcam->tx_key_fields[NPC_UNKNOWN]; + input = &mcam->tx_key_fields[type]; + } + + for (lid = start_lid; lid < NPC_MAX_LID; lid++) { + for (lt = 0; lt < NPC_MAX_LT; lt++) { + for (ld = 0; ld < NPC_MAX_LD; ld++) { + cfg = rvu_read64(rvu, blkaddr, + NPC_AF_INTFX_LIDX_LTX_LDX_CFG + (intf, lid, lt, ld)); + if (!FIELD_GET(NPC_LDATA_EN, cfg)) + continue; + memset(dummy, 0, sizeof(struct npc_key_field)); + npc_set_layer_mdata(mcam, NPC_UNKNOWN, cfg, + lid, lt, intf); + /* exclude input */ + if (npc_is_same(input, dummy)) + continue; + start_kwi = dummy->layer_mdata.key / 8; + offset = (dummy->layer_mdata.key * 8) % 64; + nr_bits = dummy->layer_mdata.len * 8; + /* form KW masks */ + npc_set_kw_masks(mcam, NPC_UNKNOWN, nr_bits, + start_kwi, offset, intf); + /* check any input field bits falls in any + * other field bits. + */ + if (npc_check_overlap_fields(dummy, input)) + return true; + } + } + } + + return false; +} + +static int npc_check_field(struct rvu *rvu, int blkaddr, enum key_fields type, + u8 intf) +{ + if (!npc_is_field_present(rvu, type, intf) || + npc_check_overlap(rvu, blkaddr, type, 0, intf)) + return -ENOTSUPP; + return 0; +} + +static void npc_scan_parse_result(struct npc_mcam *mcam, u8 bit_number, + u8 key_nibble, u8 intf) +{ + u8 offset = (key_nibble * 4) % 64; /* offset within key word */ + u8 kwi = (key_nibble * 4) / 64; /* which word in key */ + u8 nr_bits = 4; /* bits in a nibble */ + u8 type; + + switch (bit_number) { + case 0 ... 2: + type = NPC_CHAN; + break; + case 3: + type = NPC_ERRLEV; + break; + case 4 ... 5: + type = NPC_ERRCODE; + break; + case 6: + type = NPC_LXMB; + break; + /* check for LTYPE only as of now */ + case 9: + type = NPC_LA; + break; + case 12: + type = NPC_LB; + break; + case 15: + type = NPC_LC; + break; + case 18: + type = NPC_LD; + break; + case 21: + type = NPC_LE; + break; + case 24: + type = NPC_LF; + break; + case 27: + type = NPC_LG; + break; + case 30: + type = NPC_LH; + break; + default: + return; + }; + npc_set_kw_masks(mcam, type, nr_bits, kwi, offset, intf); +} + +static void npc_handle_multi_layer_fields(struct rvu *rvu, int blkaddr, u8 intf) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + struct npc_key_field *key_fields; + /* Ether type can come from three layers + * (ethernet, single tagged, double tagged) + */ + struct npc_key_field *etype_ether; + struct npc_key_field *etype_tag1; + struct npc_key_field *etype_tag2; + /* Outer VLAN TCI can come from two layers + * (single tagged, double tagged) + */ + struct npc_key_field *vlan_tag1; + struct npc_key_field *vlan_tag2; + u64 *features; + u8 start_lid; + int i; + + key_fields = mcam->rx_key_fields; + features = &mcam->rx_features; + + if (intf == NIX_INTF_TX) { + key_fields = mcam->tx_key_fields; + features = &mcam->tx_features; + } + + /* Handle header fields which can come from multiple layers like + * etype, outer vlan tci. These fields should have same position in + * the key otherwise to install a mcam rule more than one entry is + * needed which complicates mcam space management. + */ + etype_ether = &key_fields[NPC_ETYPE_ETHER]; + etype_tag1 = &key_fields[NPC_ETYPE_TAG1]; + etype_tag2 = &key_fields[NPC_ETYPE_TAG2]; + vlan_tag1 = &key_fields[NPC_VLAN_TAG1]; + vlan_tag2 = &key_fields[NPC_VLAN_TAG2]; + + /* if key profile programmed does not extract ether type at all */ + if (!etype_ether->nr_kws && !etype_tag1->nr_kws && !etype_tag2->nr_kws) + goto vlan_tci; + + /* if key profile programmed extracts ether type from one layer */ + if (etype_ether->nr_kws && !etype_tag1->nr_kws && !etype_tag2->nr_kws) + key_fields[NPC_ETYPE] = *etype_ether; + if (!etype_ether->nr_kws && etype_tag1->nr_kws && !etype_tag2->nr_kws) + key_fields[NPC_ETYPE] = *etype_tag1; + if (!etype_ether->nr_kws && !etype_tag1->nr_kws && etype_tag2->nr_kws) + key_fields[NPC_ETYPE] = *etype_tag2; + + /* if key profile programmed extracts ether type from multiple layers */ + if (etype_ether->nr_kws && etype_tag1->nr_kws) { + for (i = 0; i < NPC_MAX_KWS_IN_KEY; i++) { + if (etype_ether->kw_mask[i] != etype_tag1->kw_mask[i]) + goto vlan_tci; + } + key_fields[NPC_ETYPE] = *etype_tag1; + } + if (etype_ether->nr_kws && etype_tag2->nr_kws) { + for (i = 0; i < NPC_MAX_KWS_IN_KEY; i++) { + if (etype_ether->kw_mask[i] != etype_tag2->kw_mask[i]) + goto vlan_tci; + } + key_fields[NPC_ETYPE] = *etype_tag2; + } + if (etype_tag1->nr_kws && etype_tag2->nr_kws) { + for (i = 0; i < NPC_MAX_KWS_IN_KEY; i++) { + if (etype_tag1->kw_mask[i] != etype_tag2->kw_mask[i]) + goto vlan_tci; + } + key_fields[NPC_ETYPE] = *etype_tag2; + } + + /* check none of higher layers overwrite ether type */ + start_lid = key_fields[NPC_ETYPE].layer_mdata.lid + 1; + if (npc_check_overlap(rvu, blkaddr, NPC_ETYPE, start_lid, intf)) + goto vlan_tci; + *features |= BIT_ULL(NPC_ETYPE); +vlan_tci: + /* if key profile does not extract outer vlan tci at all */ + if (!vlan_tag1->nr_kws && !vlan_tag2->nr_kws) + goto done; + + /* if key profile extracts outer vlan tci from one layer */ + if (vlan_tag1->nr_kws && !vlan_tag2->nr_kws) + key_fields[NPC_OUTER_VID] = *vlan_tag1; + if (!vlan_tag1->nr_kws && vlan_tag2->nr_kws) + key_fields[NPC_OUTER_VID] = *vlan_tag2; + + /* if key profile extracts outer vlan tci from multiple layers */ + if (vlan_tag1->nr_kws && vlan_tag2->nr_kws) { + for (i = 0; i < NPC_MAX_KWS_IN_KEY; i++) { + if (vlan_tag1->kw_mask[i] != vlan_tag2->kw_mask[i]) + goto done; + } + key_fields[NPC_OUTER_VID] = *vlan_tag2; + } + /* check none of higher layers overwrite outer vlan tci */ + start_lid = key_fields[NPC_OUTER_VID].layer_mdata.lid + 1; + if (npc_check_overlap(rvu, blkaddr, NPC_OUTER_VID, start_lid, intf)) + goto done; + *features |= BIT_ULL(NPC_OUTER_VID); +done: + return; +} + +static void npc_scan_ldata(struct rvu *rvu, int blkaddr, u8 lid, + u8 lt, u64 cfg, u8 intf) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + u8 hdr, key, nr_bytes, bit_offset; + u8 la_ltype, la_start; + /* starting KW index and starting bit position */ + int start_kwi, offset; + + nr_bytes = FIELD_GET(NPC_BYTESM, cfg) + 1; + hdr = FIELD_GET(NPC_HDR_OFFSET, cfg); + key = FIELD_GET(NPC_KEY_OFFSET, cfg); + start_kwi = key / 8; + offset = (key * 8) % 64; + + /* For Tx, Layer A has NIX_INST_HDR_S(64 bytes) preceding + * ethernet header. + */ + if (intf == NIX_INTF_TX) { + la_ltype = NPC_LT_LA_IH_NIX_ETHER; + la_start = 8; + } else { + la_ltype = NPC_LT_LA_ETHER; + la_start = 0; + } + +#define NPC_SCAN_HDR(name, hlid, hlt, hstart, hlen) \ +do { \ + if (lid == (hlid) && lt == (hlt)) { \ + if ((hstart) >= hdr && \ + ((hstart) + (hlen)) <= (hdr + nr_bytes)) { \ + bit_offset = (hdr + nr_bytes - (hstart) - (hlen)) * 8; \ + npc_set_layer_mdata(mcam, name, cfg, lid, lt, intf); \ + npc_set_kw_masks(mcam, name, (hlen) * 8, \ + start_kwi, offset + bit_offset, intf);\ + } \ + } \ +} while (0) + + /* List LID, LTYPE, start offset from layer and length(in bytes) of + * packet header fields below. + * Example: Source IP is 4 bytes and starts at 12th byte of IP header + */ + NPC_SCAN_HDR(NPC_SIP_IPV4, NPC_LID_LC, NPC_LT_LC_IP, 12, 4); + NPC_SCAN_HDR(NPC_DIP_IPV4, NPC_LID_LC, NPC_LT_LC_IP, 16, 4); + NPC_SCAN_HDR(NPC_SPORT_UDP, NPC_LID_LD, NPC_LT_LD_UDP, 0, 2); + NPC_SCAN_HDR(NPC_DPORT_UDP, NPC_LID_LD, NPC_LT_LD_UDP, 2, 2); + NPC_SCAN_HDR(NPC_SPORT_TCP, NPC_LID_LD, NPC_LT_LD_TCP, 0, 2); + NPC_SCAN_HDR(NPC_DPORT_TCP, NPC_LID_LD, NPC_LT_LD_TCP, 2, 2); + NPC_SCAN_HDR(NPC_ETYPE_ETHER, NPC_LID_LA, NPC_LT_LA_ETHER, 12, 2); + NPC_SCAN_HDR(NPC_ETYPE_TAG1, NPC_LID_LB, NPC_LT_LB_CTAG, 4, 2); + NPC_SCAN_HDR(NPC_ETYPE_TAG2, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 8, 2); + NPC_SCAN_HDR(NPC_VLAN_TAG1, NPC_LID_LB, NPC_LT_LB_CTAG, 2, 2); + NPC_SCAN_HDR(NPC_VLAN_TAG2, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 2, 2); + NPC_SCAN_HDR(NPC_DMAC, NPC_LID_LA, la_ltype, la_start, 6); + NPC_SCAN_HDR(NPC_SMAC, NPC_LID_LA, la_ltype, la_start, 6); + /* PF_FUNC is 2 bytes at 0th byte of NPC_LT_LA_IH_NIX_ETHER */ + NPC_SCAN_HDR(NPC_PF_FUNC, NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, 0, 2); +} + +static void npc_set_features(struct rvu *rvu, int blkaddr, u8 intf) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + u64 *features = &mcam->rx_features; + u64 tcp_udp; + int err, hdr; + + if (intf == NIX_INTF_TX) + features = &mcam->tx_features; + + for (hdr = NPC_DMAC; hdr < NPC_HEADER_FIELDS_MAX; hdr++) { + err = npc_check_field(rvu, blkaddr, hdr, intf); + if (!err) + *features |= BIT_ULL(hdr); + } + + tcp_udp = BIT_ULL(NPC_SPORT_TCP) | BIT_ULL(NPC_SPORT_UDP) | + BIT_ULL(NPC_DPORT_TCP) | BIT_ULL(NPC_DPORT_UDP); + + /* for tcp/udp corresponding layer type should be in the key */ + if (*features & tcp_udp) + if (npc_check_field(rvu, blkaddr, NPC_LD, intf)) + *features &= ~tcp_udp; + + /* for vlan corresponding layer type should be in the key */ + if (*features & BIT_ULL(NPC_OUTER_VID)) + if (npc_check_field(rvu, blkaddr, NPC_LB, intf)) + *features &= ~BIT_ULL(NPC_OUTER_VID); +} + +/* Scan key extraction profile and record how fields of our interest + * fill the key structure. Also verify Channel and DMAC exists in + * key and not overwritten by other header fields. + */ +static int npc_scan_kex(struct rvu *rvu, int blkaddr, u8 intf) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + u8 lid, lt, ld, bitnr; + u8 key_nibble = 0; + u64 cfg; + + /* Scan and note how parse result is going to be in key. + * A bit set in PARSE_NIBBLE_ENA corresponds to a nibble from + * parse result in the key. The enabled nibbles from parse result + * will be concatenated in key. + */ + cfg = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(intf)); + /* PARSE_NIBBLE_ENA <30:0> */ + cfg &= GENMASK_ULL(30, 0); + for_each_set_bit(bitnr, (unsigned long *)&cfg, 31) { + npc_scan_parse_result(mcam, bitnr, key_nibble, intf); + key_nibble++; + } + + /* Scan and note how layer data is going to be in key */ + for (lid = 0; lid < NPC_MAX_LID; lid++) { + for (lt = 0; lt < NPC_MAX_LT; lt++) { + for (ld = 0; ld < NPC_MAX_LD; ld++) { + cfg = rvu_read64(rvu, blkaddr, + NPC_AF_INTFX_LIDX_LTX_LDX_CFG + (intf, lid, lt, ld)); + if (!FIELD_GET(NPC_LDATA_EN, cfg)) + continue; + npc_scan_ldata(rvu, blkaddr, lid, lt, cfg, + intf); + } + } + } + + return 0; +} + +static int npc_scan_verify_kex(struct rvu *rvu, int blkaddr) +{ + int err; + + err = npc_scan_kex(rvu, blkaddr, NIX_INTF_RX); + if (err) + return err; + + err = npc_scan_kex(rvu, blkaddr, NIX_INTF_TX); + if (err) + return err; + + /* Channel is mandatory */ + if (!npc_is_field_present(rvu, NPC_CHAN, NIX_INTF_RX)) { + dev_err(rvu->dev, "Channel not present in Key\n"); + return -EINVAL; + } + /* check that none of the fields overwrite channel */ + if (npc_check_overlap(rvu, blkaddr, NPC_CHAN, 0, NIX_INTF_RX)) { + dev_err(rvu->dev, "Channel cannot be overwritten\n"); + return -EINVAL; + } + /* DMAC should be present in key for unicast filter to work */ + if (!npc_is_field_present(rvu, NPC_DMAC, NIX_INTF_RX)) { + dev_err(rvu->dev, "DMAC not present in Key\n"); + return -EINVAL; + } + /* check that none of the fields overwrite DMAC */ + if (npc_check_overlap(rvu, blkaddr, NPC_DMAC, 0, NIX_INTF_RX)) { + dev_err(rvu->dev, "DMAC cannot be overwritten\n"); + return -EINVAL; + } + + npc_set_features(rvu, blkaddr, NIX_INTF_TX); + npc_set_features(rvu, blkaddr, NIX_INTF_RX); + npc_handle_multi_layer_fields(rvu, blkaddr, NIX_INTF_TX); + npc_handle_multi_layer_fields(rvu, blkaddr, NIX_INTF_RX); + + return 0; +} + +int npc_flow_steering_init(struct rvu *rvu, int blkaddr) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + + INIT_LIST_HEAD(&mcam->mcam_rules); + + return npc_scan_verify_kex(rvu, blkaddr); +} + +static int npc_check_unsupported_flows(struct rvu *rvu, u64 features, u8 intf) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + u64 *mcam_features = &mcam->rx_features; + u64 unsupported; + u8 bit; + + if (intf == NIX_INTF_TX) + mcam_features = &mcam->tx_features; + + unsupported = (*mcam_features ^ features) & ~(*mcam_features); + if (unsupported) { + dev_info(rvu->dev, "Unsupported flow(s):\n"); + for_each_set_bit(bit, (unsigned long *)&unsupported, 64) + dev_info(rvu->dev, "%s ", npc_get_field_name(bit)); + return -ENOTSUPP; + } + + return 0; +} + +/* npc_update_entry - Based on the masks generated during + * the key scanning, updates the given entry with value and + * masks for the field of interest. Maximum 16 bytes of a packet + * header can be extracted by HW hence lo and hi are sufficient. + * When field bytes are less than or equal to 8 then hi should be + * 0 for value and mask. + * + * If exact match of value is required then mask should be all 1's. + * If any bits in mask are 0 then corresponding bits in value are + * dont care. + */ +static void npc_update_entry(struct rvu *rvu, enum key_fields type, + struct mcam_entry *entry, u64 val_lo, + u64 val_hi, u64 mask_lo, u64 mask_hi, u8 intf) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + struct mcam_entry dummy = { {0} }; + struct npc_key_field *field; + u64 kw1, kw2, kw3; + u8 shift; + int i; + + field = &mcam->rx_key_fields[type]; + if (intf == NIX_INTF_TX) + field = &mcam->tx_key_fields[type]; + + if (!field->nr_kws) + return; + + for (i = 0; i < NPC_MAX_KWS_IN_KEY; i++) { + if (!field->kw_mask[i]) + continue; + /* place key value in kw[x] */ + shift = __ffs64(field->kw_mask[i]); + /* update entry value */ + kw1 = (val_lo << shift) & field->kw_mask[i]; + dummy.kw[i] = kw1; + /* update entry mask */ + kw1 = (mask_lo << shift) & field->kw_mask[i]; + dummy.kw_mask[i] = kw1; + + if (field->nr_kws == 1) + break; + /* place remaining bits of key value in kw[x + 1] */ + if (field->nr_kws == 2) { + /* update entry value */ + kw2 = (val_lo >> (64 - shift)) | (val_hi << shift); + kw2 &= field->kw_mask[i + 1]; + dummy.kw[i + 1] = kw2; + /* update entry mask */ + kw2 = (mask_lo >> (64 - shift)) | (mask_hi << shift); + kw2 &= field->kw_mask[i + 1]; + dummy.kw_mask[i + 1] = kw2; + break; + } + /* place remaining bits of key value in kw[x + 1], kw[x + 2] */ + if (field->nr_kws == 3) { + /* update entry value */ + kw2 = (val_lo >> (64 - shift)) | (val_hi << shift); + kw2 &= field->kw_mask[i + 1]; + kw3 = (val_hi >> (64 - shift)); + kw3 &= field->kw_mask[i + 2]; + dummy.kw[i + 1] = kw2; + dummy.kw[i + 2] = kw3; + /* update entry mask */ + kw2 = (mask_lo >> (64 - shift)) | (mask_hi << shift); + kw2 &= field->kw_mask[i + 1]; + kw3 = (mask_hi >> (64 - shift)); + kw3 &= field->kw_mask[i + 2]; + dummy.kw_mask[i + 1] = kw2; + dummy.kw_mask[i + 2] = kw3; + break; + } + } + /* dummy is ready with values and masks for given key + * field now update input entry with those + */ + for (i = 0; i < NPC_MAX_KWS_IN_KEY; i++) { + entry->kw[i] |= dummy.kw[i]; + entry->kw_mask[i] |= dummy.kw_mask[i]; + } +} + +static void npc_update_flow(struct rvu *rvu, struct mcam_entry *entry, + u64 features, struct flow_msg *pkt, + struct flow_msg *mask, + struct rvu_npc_mcam_rule *output, u8 intf) +{ + u64 dmac_mask = ether_addr_to_u64(mask->dmac); + u64 smac_mask = ether_addr_to_u64(mask->smac); + u64 dmac_val = ether_addr_to_u64(pkt->dmac); + u64 smac_val = ether_addr_to_u64(pkt->smac); + struct flow_msg *opkt = &output->packet; + struct flow_msg *omask = &output->mask; + + if (!features) + return; + +#define NPC_WRITE_FLOW(field, member, val_lo, val_hi, mask_lo, mask_hi) \ +do { \ + if (features & BIT_ULL(field)) { \ + npc_update_entry(rvu, field, entry, val_lo, val_hi, \ + mask_lo, mask_hi, intf); \ + memcpy(&opkt->member, &pkt->member, sizeof(pkt->member)); \ + memcpy(&omask->member, &mask->member, sizeof(mask->member)); \ + } \ +} while (0) + + /* For tcp/udp LTYPE should be present in entry */ + if (features & (BIT_ULL(NPC_SPORT_TCP) | BIT_ULL(NPC_DPORT_TCP))) + npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_TCP, + 0, ~0ULL, 0, intf); + if (features & (BIT_ULL(NPC_SPORT_UDP) | BIT_ULL(NPC_DPORT_UDP))) + npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_UDP, + 0, ~0ULL, 0, intf); + if (features & BIT_ULL(NPC_OUTER_VID)) + npc_update_entry(rvu, NPC_LB, entry, + NPC_LT_LB_STAG_QINQ | NPC_LT_LB_CTAG, 0, + NPC_LT_LB_STAG_QINQ & NPC_LT_LB_CTAG, 0, intf); + + NPC_WRITE_FLOW(NPC_DMAC, dmac, dmac_val, 0, dmac_mask, 0); + NPC_WRITE_FLOW(NPC_SMAC, smac, smac_val, 0, smac_mask, 0); + NPC_WRITE_FLOW(NPC_ETYPE, etype, ntohs(pkt->etype), 0, + ntohs(mask->etype), 0); + NPC_WRITE_FLOW(NPC_SIP_IPV4, ip4src, ntohl(pkt->ip4src), 0, + ntohl(mask->ip4src), 0); + NPC_WRITE_FLOW(NPC_DIP_IPV4, ip4dst, ntohl(pkt->ip4dst), 0, + ntohl(mask->ip4dst), 0); + NPC_WRITE_FLOW(NPC_SPORT_TCP, sport, ntohs(pkt->sport), 0, + ntohs(mask->sport), 0); + NPC_WRITE_FLOW(NPC_SPORT_UDP, sport, ntohs(pkt->sport), 0, + ntohs(mask->sport), 0); + NPC_WRITE_FLOW(NPC_DPORT_TCP, dport, ntohs(pkt->dport), 0, + ntohs(mask->dport), 0); + NPC_WRITE_FLOW(NPC_DPORT_UDP, dport, ntohs(pkt->dport), 0, + ntohs(mask->dport), 0); + NPC_WRITE_FLOW(NPC_OUTER_VID, vlan_tci, ntohs(pkt->vlan_tci), 0, + ntohs(mask->vlan_tci), 0); +} + +static struct rvu_npc_mcam_rule *rvu_mcam_find_rule(struct npc_mcam *mcam, + u16 entry) +{ + struct rvu_npc_mcam_rule *iter; + + list_for_each_entry(iter, &mcam->mcam_rules, list) { + if (iter->entry == entry) + return iter; + } + + return NULL; +} + +static void rvu_mcam_add_rule(struct npc_mcam *mcam, + struct rvu_npc_mcam_rule *rule) +{ + struct list_head *head = &mcam->mcam_rules; + struct rvu_npc_mcam_rule *iter; + + list_for_each_entry(iter, &mcam->mcam_rules, list) { + if (iter->entry > rule->entry) + break; + head = &iter->list; + } + + list_add(&rule->list, head); +} + +static void rvu_mcam_remove_counter_from_rule(struct rvu *rvu, u16 pcifunc, + struct rvu_npc_mcam_rule *rule) +{ + struct npc_mcam_oper_counter_req free_req = { 0 }; + struct msg_rsp free_rsp; + + if (!rule->has_cntr) + return; + + free_req.hdr.pcifunc = pcifunc; + free_req.cntr = rule->cntr; + + rvu_mbox_handler_npc_mcam_free_counter(rvu, &free_req, &free_rsp); + rule->has_cntr = false; +} + +static void rvu_mcam_add_counter_to_rule(struct rvu *rvu, u16 pcifunc, + struct rvu_npc_mcam_rule *rule, + struct npc_install_flow_rsp *rsp) +{ + struct npc_mcam_alloc_counter_req cntr_req = { 0 }; + struct npc_mcam_alloc_counter_rsp cntr_rsp = { 0 }; + int err; + + cntr_req.hdr.pcifunc = pcifunc; + cntr_req.contig = true; + cntr_req.count = 1; + + /* we try to allocate a counter to track the stats of this + * rule. If counter could not be allocated then proceed + * without counter because counters are limited than entries. + */ + err = rvu_mbox_handler_npc_mcam_alloc_counter(rvu, &cntr_req, + &cntr_rsp); + if (!err && cntr_rsp.count) { + rule->cntr = cntr_rsp.cntr; + rule->has_cntr = true; + rsp->counter = rule->cntr; + } else { + rsp->counter = err; + } +} + +static void npc_update_rx_entry(struct rvu *rvu, struct rvu_pfvf *pfvf, + struct mcam_entry *entry, + struct npc_install_flow_req *req, u16 target) +{ + struct nix_rx_action action; + + npc_update_entry(rvu, NPC_CHAN, entry, req->channel, 0, + ~0ULL, 0, NIX_INTF_RX); + + *(u64 *)&action = 0x00; + action.pf_func = target; + action.op = req->op; + action.index = req->index; + action.match_id = req->match_id; + action.flow_key_alg = req->flow_key_alg; + + if (req->op == NIX_RX_ACTION_DEFAULT && pfvf->def_rule) + action = pfvf->def_rule->rx_action; + + entry->action = *(u64 *)&action; + + /* VTAG0 starts at 0th byte of LID_B. + * VTAG1 starts at 4th byte of LID_B. + */ + entry->vtag_action = FIELD_PREP(RX_VTAG0_VALID_BIT, req->vtag0_valid) | + FIELD_PREP(RX_VTAG0_TYPE_MASK, req->vtag0_type) | + FIELD_PREP(RX_VTAG0_LID_MASK, NPC_LID_LB) | + FIELD_PREP(RX_VTAG0_RELPTR_MASK, 0) | + FIELD_PREP(RX_VTAG1_VALID_BIT, req->vtag1_valid) | + FIELD_PREP(RX_VTAG1_TYPE_MASK, req->vtag1_type) | + FIELD_PREP(RX_VTAG1_LID_MASK, NPC_LID_LB) | + FIELD_PREP(RX_VTAG1_RELPTR_MASK, 4); +} + +static void npc_update_tx_entry(struct rvu *rvu, struct rvu_pfvf *pfvf, + struct mcam_entry *entry, + struct npc_install_flow_req *req, u16 target) +{ + struct nix_tx_action action; + + npc_update_entry(rvu, NPC_PF_FUNC, entry, htons(req->hdr.pcifunc), + 0, ~0ULL, 0, NIX_INTF_TX); + + *(u64 *)&action = 0x00; + action.op = req->op; + action.index = req->index; + action.match_id = req->match_id; + + entry->action = *(u64 *)&action; + + /* VTAG0 starts at 0th byte of LID_B. + * VTAG1 starts at 4th byte of LID_B. + */ + entry->vtag_action = FIELD_PREP(TX_VTAG0_DEF_MASK, req->vtag0_def) | + FIELD_PREP(TX_VTAG0_OP_MASK, req->vtag0_op) | + FIELD_PREP(TX_VTAG0_LID_MASK, NPC_LID_LB) | + FIELD_PREP(TX_VTAG0_RELPTR_MASK, 0) | + FIELD_PREP(TX_VTAG1_DEF_MASK, req->vtag1_def) | + FIELD_PREP(TX_VTAG1_OP_MASK, req->vtag1_op) | + FIELD_PREP(TX_VTAG1_LID_MASK, NPC_LID_LB) | + FIELD_PREP(TX_VTAG1_RELPTR_MASK, 4); +} + +static int npc_install_flow(struct rvu *rvu, int blkaddr, u16 target, + int nixlf, struct rvu_pfvf *pfvf, + struct npc_install_flow_req *req, + struct npc_install_flow_rsp *rsp, bool enable, + bool pf_set_vfs_mac) +{ + u64 features, installed_features, missing_features = 0; + struct rvu_npc_mcam_rule *def_rule = pfvf->def_rule; + struct npc_mcam_write_entry_req write_req = { 0 }; + bool new = false, msg_from_vf; + struct npc_mcam *mcam = &rvu->hw->mcam; + struct rvu_npc_mcam_rule dummy = { 0 }; + struct rvu_npc_mcam_rule *rule; + u16 owner = req->hdr.pcifunc; + struct msg_rsp write_rsp; + struct mcam_entry *entry; + int entry_index, err; + + msg_from_vf = !!(owner & RVU_PFVF_FUNC_MASK); + + installed_features = req->features; + features = req->features; + entry = &write_req.entry_data; + entry_index = req->entry; + + npc_update_flow(rvu, entry, features, &req->packet, &req->mask, &dummy, + req->intf); + + if (req->intf == NIX_INTF_RX) + npc_update_rx_entry(rvu, pfvf, entry, req, target); + else + npc_update_tx_entry(rvu, pfvf, entry, req, target); + + /* Default unicast rules do not exist for TX */ + if (req->intf == NIX_INTF_TX) + goto find_rule; + + if (def_rule) + missing_features = (def_rule->features ^ features) & + def_rule->features; + + if (req->default_rule && req->append) { + /* add to default rule */ + if (missing_features) + npc_update_flow(rvu, entry, missing_features, + &def_rule->packet, &def_rule->mask, + &dummy, req->intf); + enable = rvu_npc_write_default_rule(rvu, blkaddr, + nixlf, target, + NIX_INTF_RX, entry, + &entry_index); + installed_features = req->features | missing_features; + } else if (req->default_rule && !req->append) { + /* overwrite default rule */ + enable = rvu_npc_write_default_rule(rvu, blkaddr, + nixlf, target, + NIX_INTF_RX, entry, + &entry_index); + } else if (msg_from_vf) { + /* normal rule - include default rule also to it for VF */ + npc_update_flow(rvu, entry, missing_features, &def_rule->packet, + &def_rule->mask, &dummy, req->intf); + installed_features = req->features | missing_features; + } +find_rule: + rule = rvu_mcam_find_rule(mcam, entry_index); + if (!rule) { + rule = kzalloc(sizeof(*rule), GFP_KERNEL); + if (!rule) + return -ENOMEM; + new = true; + } + /* no counter for default rule */ + if (req->default_rule) + goto update_rule; + + /* allocate new counter if rule has no counter */ + if (req->set_cntr && !rule->has_cntr) + rvu_mcam_add_counter_to_rule(rvu, owner, rule, rsp); + + /* if user wants to delete an existing counter for a rule then + * free the counter + */ + if (!req->set_cntr && rule->has_cntr) + rvu_mcam_remove_counter_from_rule(rvu, owner, rule); + + write_req.hdr.pcifunc = owner; + write_req.entry = req->entry; + write_req.intf = req->intf; + write_req.enable_entry = (u8)enable; + /* if counter is available then clear and use it */ + if (req->set_cntr && rule->has_cntr) { + rvu_write64(rvu, blkaddr, NPC_AF_MATCH_STATX(rule->cntr), 0x00); + write_req.set_cntr = 1; + write_req.cntr = rule->cntr; + } + + err = rvu_mbox_handler_npc_mcam_write_entry(rvu, &write_req, + &write_rsp); + if (err) { + rvu_mcam_remove_counter_from_rule(rvu, owner, rule); + if (new) + kfree(rule); + return err; + } +update_rule: + memcpy(&rule->packet, &dummy.packet, sizeof(rule->packet)); + memcpy(&rule->mask, &dummy.mask, sizeof(rule->mask)); + rule->entry = entry_index; + memcpy(&rule->rx_action, &entry->action, sizeof(struct nix_rx_action)); + if (req->intf == NIX_INTF_TX) + memcpy(&rule->tx_action, &entry->action, + sizeof(struct nix_tx_action)); + rule->vtag_action = entry->vtag_action; + rule->features = installed_features; + rule->default_rule = req->default_rule; + rule->owner = owner; + rule->enable = enable; + rule->intf = req->intf; + + if (new) + rvu_mcam_add_rule(mcam, rule); + if (req->default_rule) + pfvf->def_rule = rule; + + /* VF's MAC address is being changed via PF */ + if (pf_set_vfs_mac) + ether_addr_copy(pfvf->mac_addr, req->packet.dmac); + + return 0; +} + +int rvu_mbox_handler_npc_install_flow(struct rvu *rvu, + struct npc_install_flow_req *req, + struct npc_install_flow_rsp *rsp) +{ + bool from_vf = !!(req->hdr.pcifunc & RVU_PFVF_FUNC_MASK); + int blkaddr, nixlf, err; + struct rvu_pfvf *pfvf; + bool pf_set_vfs_mac = false; + bool enable = true; + u16 target; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) { + dev_err(rvu->dev, "%s: NPC block not implemented\n", __func__); + return -ENODEV; + } + + if (from_vf && req->default_rule) + return NPC_MCAM_PERM_DENIED; + + /* Each PF/VF info is maintained in struct rvu_pfvf. + * rvu_pfvf for the target PF/VF needs to be retrieved + * hence modify pcifunc accordingly. + */ + + /* AF installing for a PF/VF */ + if (!req->hdr.pcifunc) + target = req->vf; + /* PF installing for its VF */ + else if (!from_vf && req->vf) { + target = (req->hdr.pcifunc & ~RVU_PFVF_FUNC_MASK) | req->vf; + pf_set_vfs_mac = req->default_rule && + (req->features & BIT_ULL(NPC_DMAC)); + } + /* msg received from PF/VF */ + else + target = req->hdr.pcifunc; + + if (npc_check_unsupported_flows(rvu, req->features, req->intf)) + return -ENOTSUPP; + + if (npc_mcam_verify_channel(rvu, target, req->intf, req->channel)) + return -EINVAL; + + pfvf = rvu_get_pfvf(rvu, target); + + err = nix_get_nixlf(rvu, target, &nixlf); + + /* If interface is uninitialized then do not enable entry */ + if (err || (!req->default_rule && !pfvf->def_rule)) + enable = false; + + /* Packets reaching NPC in Tx path implies that a + * NIXLF is properly setup and transmitting. + * Hence rules can be enabled for Tx. + */ + if (req->intf == NIX_INTF_TX) + enable = true; + + /* Do not allow requests from uninitialized VFs */ + if (from_vf && !enable) + return -EINVAL; + + /* If message is from VF then its flow should not overlap with + * reserved unicast flow. + */ + if (from_vf && pfvf->def_rule && req->intf == NIX_INTF_RX && + pfvf->def_rule->features & req->features) + return -EINVAL; + + return npc_install_flow(rvu, blkaddr, target, nixlf, pfvf, + req, rsp, enable, pf_set_vfs_mac); +} + +static int npc_delete_flow(struct rvu *rvu, u16 entry, u16 pcifunc) +{ + struct npc_mcam_ena_dis_entry_req dis_req = { 0 }; + struct npc_mcam *mcam = &rvu->hw->mcam; + struct rvu_npc_mcam_rule *rule; + struct msg_rsp dis_rsp; + int err; + + rule = rvu_mcam_find_rule(mcam, entry); + if (!rule) + return -ENOENT; + + if (rule->default_rule) + return 0; + + if (rule->has_cntr) + rvu_mcam_remove_counter_from_rule(rvu, pcifunc, rule); + + dis_req.hdr.pcifunc = pcifunc; + dis_req.entry = entry; + err = rvu_mbox_handler_npc_mcam_dis_entry(rvu, &dis_req, &dis_rsp); + if (err) + return err; + + list_del(&rule->list); + kfree(rule); + + return 0; +} + +int rvu_mbox_handler_npc_delete_flow(struct rvu *rvu, + struct npc_delete_flow_req *req, + struct msg_rsp *rsp) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + struct rvu_npc_mcam_rule *iter, *tmp; + u16 pcifunc = req->hdr.pcifunc; + int err; + + if (req->end) { + list_for_each_entry_safe(iter, tmp, &mcam->mcam_rules, list) { + if (iter->owner == pcifunc && + iter->entry >= req->start && + iter->entry <= req->end) { + err = npc_delete_flow(rvu, iter->entry, + pcifunc); + if (err) + return err; + } + } + return 0; + } + + if (!req->all) + return npc_delete_flow(rvu, req->entry, pcifunc); + + list_for_each_entry_safe(iter, tmp, &mcam->mcam_rules, list) { + if (iter->owner == pcifunc) { + err = npc_delete_flow(rvu, iter->entry, pcifunc); + if (err) + return err; + } + } + + return 0; +} + +void npc_mcam_enable_flows(struct rvu *rvu, u16 target) +{ + struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, target); + struct npc_mcam_ena_dis_entry_req ena_req = { 0 }; + struct npc_mcam *mcam = &rvu->hw->mcam; + struct rvu_npc_mcam_rule *rule; + int blkaddr, bank, err; + struct msg_rsp rsp; + u64 def_action; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) { + dev_err(rvu->dev, "%s: NPC block not implemented\n", __func__); + return; + } + + list_for_each_entry(rule, &mcam->mcam_rules, list) { + if (rule->intf == NIX_INTF_RX && + rule->rx_action.pf_func == target && !rule->enable) { + if (rule->default_rule) { + npc_enable_mcam_entry(rvu, mcam, blkaddr, + rule->entry, true); + rule->enable = true; + continue; + } + + if (rule->rx_action.op == NIX_RX_ACTION_DEFAULT) { + if (!pfvf->def_rule) + continue; + /* Use default unicast entry action */ + rule->rx_action = pfvf->def_rule->rx_action; + def_action = *(u64 *)&pfvf->def_rule->rx_action; + bank = npc_get_bank(mcam, rule->entry); + rvu_write64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_ACTION + (rule->entry, bank), def_action); + } + ena_req.hdr.pcifunc = rule->owner; + ena_req.entry = rule->entry; + err = rvu_mbox_handler_npc_mcam_ena_entry(rvu, &ena_req, + &rsp); + if (err) + continue; + rule->enable = true; + } + } +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_ptp.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_ptp.c new file mode 100644 index 000000000000..39cc31bcedb4 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_ptp.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Admin Function driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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 "ptp.h" +#include "mbox.h" +#include "rvu.h" + +int rvu_mbox_handler_ptp_op(struct rvu *rvu, struct ptp_req *req, + struct ptp_rsp *rsp) +{ + int err = 0; + + if (!rvu->ptp) + return -ENODEV; + + switch (req->op) { + case PTP_OP_ADJFINE: + err = ptp_adjfine(rvu->ptp, req->scaled_ppm); + break; + case PTP_OP_GET_CLOCK: + err = ptp_get_clock(rvu->ptp, req->is_pmu, &rsp->clk, + &rsp->tsc); + break; + default: + err = -EINVAL; + break; + } + + return err; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h index 09a8d61f3144..e73101919964 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h @@ -112,6 +112,7 @@ #define NPA_AF_LF_RST (0x0020) #define NPA_AF_GEN_CFG (0x0030) #define NPA_AF_NDC_CFG (0x0040) +#define NPA_AF_NDC_SYNC (0x0050) #define NPA_AF_INP_CTL (0x00D0) #define NPA_AF_ACTIVE_CYCLES_PC (0x00F0) #define NPA_AF_AVG_DELAY (0x0100) @@ -213,9 +214,11 @@ #define NIX_AF_RX_DEF_IUDP (0x0280) #define NIX_AF_RX_DEF_OSCTP (0x0290) #define NIX_AF_RX_DEF_ISCTP (0x02A0) -#define NIX_AF_RX_DEF_IPSECX (0x02B0) +#define NIX_AF_RX_DEF_IPSECX(a) (0x02B0ull | (uint64_t)(a) << 3) #define NIX_AF_RX_IPSEC_GEN_CFG (0x0300) -#define NIX_AF_RX_CPTX_INST_ADDR (0x0310) +#define NIX_AF_RX_CPTX_INST_QSEL(a) (0x0320ull | (uint64_t)(a) << 3) +#define NIX_AF_RX_CPTX_CREDIT(a) (0x0360ull | (uint64_t)(a) << 3) +#define NIX_AF_NDC_RX_SYNC (0x03E0) #define NIX_AF_NDC_TX_SYNC (0x03F0) #define NIX_AF_AQ_CFG (0x0400) #define NIX_AF_AQ_BASE (0x0410) @@ -239,19 +242,18 @@ #define NIX_AF_SEB_ECO (0x0600) #define NIX_AF_SEB_TEST_BP (0x0610) #define NIX_AF_NORM_TX_FIFO_STATUS (0x0620) -#define NIX_AF_EXPR_TX_FIFO_STATUS (0x0630) #define NIX_AF_SDP_TX_FIFO_STATUS (0x0640) #define NIX_AF_TX_NPC_CAPTURE_CONFIG (0x0660) #define NIX_AF_TX_NPC_CAPTURE_INFO (0x0670) #define NIX_AF_DEBUG_NPC_RESP_DATAX(a) (0x680 | (a) << 3) #define NIX_AF_SMQX_CFG(a) (0x700 | (a) << 16) +#define NIX_AF_SMQX_STATUS(a) (0x730 | (a) << 16) +#define NIX_AF_SQM_DBG_CTL_STATUS (0x750) #define NIX_AF_PSE_CHANNEL_LEVEL (0x800) #define NIX_AF_PSE_SHAPER_CFG (0x810) -#define NIX_AF_TX_EXPR_CREDIT (0x830) #define NIX_AF_MARK_FORMATX_CTL(a) (0x900 | (a) << 18) #define NIX_AF_TX_LINKX_NORM_CREDIT(a) (0xA00 | (a) << 16) -#define NIX_AF_TX_LINKX_EXPR_CREDIT(a) (0xA10 | (a) << 16) #define NIX_AF_TX_LINKX_SW_XOFF(a) (0xA20 | (a) << 16) #define NIX_AF_TX_LINKX_HW_XOFF(a) (0xA30 | (a) << 16) #define NIX_AF_SDP_LINK_CREDIT (0xa40) @@ -385,7 +387,7 @@ #define NIX_AF_LFX_RX_IPSEC_CFG0(a) (0x4140 | (a) << 17) #define NIX_AF_LFX_RX_IPSEC_CFG1(a) (0x4148 | (a) << 17) #define NIX_AF_LFX_RX_IPSEC_DYNO_CFG(a) (0x4150 | (a) << 17) -#define NIX_AF_LFX_RX_IPSEC_DYNO_BASE(a) (0x4158 | (a) << 17) +#define NIX_AF_LFX_RX_IPSEC_DYNO_BASE(a)(0x4158 | (a) << 17) #define NIX_AF_LFX_RX_IPSEC_SA_BASE(a) (0x4170 | (a) << 17) #define NIX_AF_LFX_TX_STATUS(a) (0x4180 | (a) << 17) #define NIX_AF_LFX_RX_VTAG_TYPEX(a, b) (0x4200 | (a) << 17 | (b) << 3) @@ -398,20 +400,175 @@ #define NIX_AF_RX_NPC_MIRROR_RCV (0x4720) #define NIX_AF_RX_NPC_MIRROR_DROP (0x4730) #define NIX_AF_RX_ACTIVE_CYCLES_PCX(a) (0x4800 | (a) << 16) +#define NIX_AF_RQM_BP_TEST (0x4880) +#define NIX_AF_CQM_BP_TEST (0x48c0) #define NIX_PRIV_AF_INT_CFG (0x8000000) #define NIX_PRIV_LFX_CFG (0x8000010) #define NIX_PRIV_LFX_INT_CFG (0x8000020) #define NIX_AF_RVU_LF_CFG_DEBUG (0x8000030) +#define NIX_AF_LF_CFG_SHIFT 17 +#define NIX_AF_LF_SSO_PF_FUNC_SHIFT 16 + /* SSO */ #define SSO_AF_CONST (0x1000) #define SSO_AF_CONST1 (0x1008) -#define SSO_AF_BLK_RST (0x10f8) +#define SSO_AF_NOS_CNT (0x1050) +#define SSO_AF_AW_WE (0x1080) #define SSO_AF_LF_HWGRP_RST (0x10e0) +#define SSO_AF_AW_CFG (0x10f0) +#define SSO_AF_BLK_RST (0x10f8) +#define SSO_AF_ACTIVE_CYCLES0 (0x1100) +#define SSO_AF_ACTIVE_CYCLES1 (0x1108) +#define SSO_AF_ACTIVE_CYCLES2 (0x1110) +#define SSO_AF_ERR0 (0x1220) +#define SSO_AF_ERR0_W1S (0x1228) +#define SSO_AF_ERR0_ENA_W1C (0x1230) +#define SSO_AF_ERR0_ENA_W1S (0x1238) +#define SSO_AF_ERR2 (0x1260) +#define SSO_AF_ERR2_W1S (0x1268) +#define SSO_AF_ERR2_ENA_W1C (0x1270) +#define SSO_AF_ERR2_ENA_W1S (0x1278) +#define SSO_AF_UNMAP_INFO (0x12f0) +#define SSO_AF_UNMAP_INFO2 (0x1300) +#define SSO_AF_UNMAP_INFO3 (0x1310) +#define SSO_AF_RAS (0x1420) +#define SSO_AF_RAS_W1S (0x1430) +#define SSO_AF_RAS_ENA_W1C (0x1460) +#define SSO_AF_RAS_ENA_W1S (0x1470) +#define SSO_PRIV_AF_INT_CFG (0x3000) +#define SSO_AF_AW_ADD (0x2080) +#define SSO_AF_AW_READ_ARB (0x2090) +#define SSO_AF_XAQ_REQ_PC (0x20B0) +#define SSO_AF_XAQ_LATENCY_PC (0x20B8) +#define SSO_AF_TAQ_CNT (0x20c0) +#define SSO_AF_TAQ_ADD (0x20e0) +#define SSO_AF_POISONX(a) (0x2100 | (a) << 3) +#define SSO_AF_POISONX_W1S(a) (0x2200 | (a) << 3) #define SSO_AF_RVU_LF_CFG_DEBUG (0x3800) #define SSO_PRIV_LFX_HWGRP_CFG (0x10000) #define SSO_PRIV_LFX_HWGRP_INT_CFG (0x20000) +#define SSO_AF_XAQX_GMCTL(a) (0xe0000 | (a) << 3) +#define SSO_AF_XAQX_HEAD_PTR(a) (0x80000 | (a) << 3) +#define SSO_AF_XAQX_TAIL_PTR(a) (0x90000 | (a) << 3) +#define SSO_AF_XAQX_HEAD_NEXT(a) (0xa0000 | (a) << 3) +#define SSO_AF_XAQX_TAIL_NEXT(a) (0xb0000 | (a) << 3) +#define SSO_AF_TOAQX_STATUS(a) (0xd0000 | (a) << 3) +#define SSO_AF_TIAQX_STATUS(a) (0xc0000 | (a) << 3) +#define SSO_AF_HWGRPX_IAQ_THR(a) (0x200000 | (a) << 12) +#define SSO_AF_HWGRPX_TAQ_THR(a) (0x200010 | (a) << 12) +#define SSO_AF_HWGRPX_PRI(a) (0x200020 | (a) << 12) +#define SSO_AF_HWGRPX_WS_PC(a) (0x200050 | (a) << 12) +#define SSO_AF_HWGRPX_EXT_PC(a) (0x200060 | (a) << 12) +#define SSO_AF_HWGRPX_WA_PC(a) (0x200070 | (a) << 12) +#define SSO_AF_HWGRPX_TS_PC(a) (0x200080 | (a) << 12) +#define SSO_AF_HWGRPX_DS_PC(a) (0x200090 | (a) << 12) +#define SSO_AF_HWGRPX_DQ_PC(a) (0x2000A0 | (a) << 12) +#define SSO_AF_HWGRPX_PAGE_CNT(a) (0x200100 | (a) << 12) +#define SSO_AF_IU_ACCNTX_CFG(a) (0x50000 | (a) << 3) +#define SSO_AF_IU_ACCNTX_RST(a) (0x60000 | (a) << 3) +#define SSO_AF_HWGRPX_AW_STATUS(a) (0x200110 | (a) << 12) +#define SSO_AF_HWGRPX_AW_CFG(a) (0x200120 | (a) << 12) +#define SSO_AF_HWGRPX_AW_TAGSPACE(a) (0x200130 | (a) << 12) +#define SSO_AF_HWGRPX_XAQ_AURA(a) (0x200140 | (a) << 12) +#define SSO_AF_HWGRPX_XAQ_LIMIT(a) (0x200220 | (a) << 12) +#define SSO_AF_HWGRPX_IU_ACCNT(a) (0x200230 | (a) << 12) +#define SSO_AF_HWSX_ARB(a) (0x400100 | (a) << 12) +#define SSO_AF_HWSX_INV(a) (0x400180 | (a) << 12) +#define SSO_AF_HWSX_GMCTL(a) (0x400200 | (a) << 12) +#define SSO_AF_HWSX_SX_GRPMSKX(a, b, c) \ + (0x400400 | (a) << 12 | (b) << 5 | (c) << 3) +#define SSO_AF_TAQX_LINK(a) (0xc00000 | (a) << 3) +#define SSO_AF_TAQX_WAEY_TAG(a, b) (0xe00000 | (a) << 8 | (b) << 4) +#define SSO_AF_TAQX_WAEY_WQP(a, b) (0xe00008 | (a) << 8 | (b) << 4) +#define SSO_AF_IPL_FREEX(a) (0x800000 | (a) << 3) +#define SSO_AF_IPL_IAQX(a) (0x840000 | (a) << 3) +#define SSO_AF_IPL_DESCHEDX(a) (0x860000 | (a) << 3) +#define SSO_AF_IPL_CONFX(a) (0x880000 | (a) << 3) +#define SSO_AF_IENTX_TAG(a) (0Xa00000 | (a) << 3) +#define SSO_AF_IENTX_GRP(a) (0xa20000 | (a) << 3) +#define SSO_AF_IENTX_PENDTAG(a) (0xa40000 | (a) << 3) +#define SSO_AF_IENTX_LINKS(a) (0xa60000 | (a) << 3) +#define SSO_AF_IENTX_QLINKS(a) (0xa80000 | (a) << 3) +#define SSO_AF_IENTX_WQP(a) (0xaa0000 | (a) << 3) +#define SSO_AF_XAQDIS_DIGESTX(a) (0x901000 | (a) << 3) +#define SSO_AF_FLR_AQ_DIGESTX(a) (0x901200 | (a) << 3) +#define SSO_AF_QCTLDIS_DIGESTX(a) (0x900E00 | (a) << 3) +#define SSO_AF_WQP0_DIGESTX(a) (0x900A00 | (a) << 3) +#define SSO_AF_NPA_DIGESTX(a) (0x900000 | (a) << 3) +#define SSO_AF_BFP_DIGESTX(a) (0x900200 | (a) << 3) +#define SSO_AF_BFPN_DIGESTX(a) (0x900400 | (a) << 3) +#define SSO_AF_GRPDIS_DIGESTX(a) (0x900600 | (a) << 3) + +#define SSO_AF_IAQ_FREE_CNT_MASK 0x3FFFull +#define SSO_AF_IAQ_RSVD_FREE_MASK 0x3FFFull +#define SSO_AF_IAQ_RSVD_FREE_SHIFT 16 +#define SSO_AF_IAQ_FREE_CNT_MAX SSO_AF_IAQ_FREE_CNT_MASK +#define SSO_AF_AW_ADD_RSVD_FREE_MASK 0x3FFFull +#define SSO_AF_AW_ADD_RSVD_FREE_SHIFT 16 +#define SSO_HWGRP_IAQ_MAX_THR_MASK 0x3FFFull +#define SSO_HWGRP_IAQ_RSVD_THR_MASK 0x3FFFull +#define SSO_HWGRP_IAQ_MAX_THR_SHIFT 32 +#define SSO_HWGRP_IAQ_RSVD_THR 0x2 +#define SSO_HWGRP_IAQ_GRP_CNT_SHIFT 48 +#define SSO_HWGRP_IAQ_GRP_CNT_MASK 0x3FFFull +#define SSO_AF_HWGRPX_IUEX_NOSCHED(a, b)\ + ((((b >> 48) & 0x3FF) == a) && (b & BIT_ULL(60))) +#define SSO_AF_HWGRP_PAGE_CNT_MASK (BIT_ULL(32) - 1) +#define SSO_AF_HWGRP_PAGE_CNT_MASK (BIT_ULL(32) - 1) +#define SSO_HWGRP_IAQ_MAX_THR_STRM_PERF 0xD0 +#define SSO_AF_HWGRP_IU_ACCNT_MAX_THR 0x7FFFull + +#define SSO_AF_TAQ_FREE_CNT_MASK 0x7FFull +#define SSO_AF_TAQ_RSVD_FREE_MASK 0x7FFull +#define SSO_AF_TAQ_RSVD_FREE_SHIFT 16 +#define SSO_AF_TAQ_FREE_CNT_MAX SSO_AF_TAQ_FREE_CNT_MASK +#define SSO_AF_TAQ_ADD_RSVD_FREE_MASK 0x1FFFull +#define SSO_AF_TAQ_ADD_RSVD_FREE_SHIFT 16 +#define SSO_HWGRP_TAQ_MAX_THR_MASK 0x7FFull +#define SSO_HWGRP_TAQ_RSVD_THR_MASK 0x7FFull +#define SSO_HWGRP_TAQ_MAX_THR_SHIFT 32 +#define SSO_HWGRP_TAQ_RSVD_THR 0x3 +#define SSO_AF_ERR0_MASK 0xFFEull +#define SSO_AF_ERR2_MASK 0xF001F000ull +#define SSO_HWGRP_TAQ_MAX_THR_STRM_PERF 0x10 + +#define SSO_HWGRP_PRI_MASK 0x7ull +#define SSO_HWGRP_PRI_AFF_MASK 0xFull +#define SSO_HWGRP_PRI_AFF_SHIFT 8 +#define SSO_HWGRP_PRI_WGT_MASK 0x3Full +#define SSO_HWGRP_PRI_WGT_SHIFT 16 +#define SSO_HWGRP_PRI_WGT_LEFT_MASK 0x3Full +#define SSO_HWGRP_PRI_WGT_LEFT_SHIFT 24 + +#define SSO_HWGRP_AW_CFG_RWEN BIT_ULL(0) +#define SSO_HWGRP_AW_CFG_LDWB BIT_ULL(1) +#define SSO_HWGRP_AW_CFG_LDT BIT_ULL(2) +#define SSO_HWGRP_AW_CFG_STT BIT_ULL(3) +#define SSO_HWGRP_AW_CFG_XAQ_BYP_DIS BIT_ULL(4) + +#define SSO_HWGRP_AW_STS_TPTR_VLD BIT_ULL(8) +#define SSO_HWGRP_AW_STS_NPA_FETCH BIT_ULL(9) +#define SSO_HWGRP_AW_STS_XAQ_BUFSC_MASK 0x7ull +#define SSO_HWGRP_AW_STS_INIT_STS 0x18ull + +#define SSO_LF_GGRP_OP_ADD_WORK1 (0x8ull) +#define SSO_LF_GGRP_QCTL (0x20ull) +#define SSO_LF_GGRP_INT (0x100ull) +#define SSO_LF_GGRP_INT_ENA_W1S (0x110ull) +#define SSO_LF_GGRP_INT_ENA_W1C (0x118ull) +#define SSO_LF_GGRP_INT_THR (0x140ull) +#define SSO_LF_GGRP_INT_CNT (0x180ull) +#define SSO_LF_GGRP_XAQ_CNT (0x1b0ull) +#define SSO_LF_GGRP_AQ_CNT (0x1c0ull) +#define SSO_LF_GGRP_AQ_THR (0x1e0ull) +#define SSO_LF_GGRP_MISC_CNT (0x200ull) + +#define SSO_LF_GGRP_INT_MASK (0X7) +#define SSO_LF_GGRP_AQ_THR_MASK (BIT_ULL(33) - 1) +#define SSO_LF_GGRP_XAQ_CNT_MASK (BIT_ULL(33) - 1) +#define SSO_LF_GGRP_INT_CNT_MASK (0x3FFF3FFF0000ull) /* SSOW */ #define SSOW_AF_RVU_LF_HWS_CFG_DEBUG (0x0010) @@ -419,6 +576,22 @@ #define SSOW_PRIV_LFX_HWS_CFG (0x1000) #define SSOW_PRIV_LFX_HWS_INT_CFG (0x2000) +#define SSOW_LF_GWS_PENDSTATE (0x50ull) +#define SSOW_LF_GWS_NW_TIM (0x70ull) +#define SSOW_LF_GWS_INT (0x100ull) +#define SSOW_LF_GWS_INT_ENA_W1C (0x118ull) +#define SSOW_LF_GWS_TAG (0x200ull) +#define SSOW_LF_GWS_WQP (0x210ull) +#define SSOW_LF_GWS_OP_GET_WORK (0x600ull) +#define SSOW_LF_GWS_OP_SWTAG_FLUSH (0x800ull) +#define SSOW_LF_GWS_OP_DESCHED (0x880ull) +#define SSOW_LF_GWS_OP_CLR_NSCHED0 (0xA00ull) +#define SSOW_LF_GWS_OP_GWC_INVAL (0xe00ull) + +#define SSO_TT_EMPTY (0x3) +#define SSOW_LF_GWS_INT_MASK (0x7FF) +#define SSOW_LF_GWS_MAX_NW_TIM (BIT_ULL(10) - 1) + /* TIM */ #define TIM_AF_CONST (0x90) #define TIM_PRIV_LFX_CFG (0x20000) @@ -426,17 +599,77 @@ #define TIM_AF_RVU_LF_CFG_DEBUG (0x30000) #define TIM_AF_BLK_RST (0x10) #define TIM_AF_LF_RST (0x20) +#define TIM_AF_BLK_RST (0x10) +#define TIM_AF_RINGX_GMCTL(a) (0x2000 | (a) << 3) +#define TIM_AF_RINGX_CTL0(a) (0x4000 | (a) << 3) +#define TIM_AF_RINGX_CTL1(a) (0x6000 | (a) << 3) +#define TIM_AF_RINGX_CTL2(a) (0x8000 | (a) << 3) +#define TIM_AF_FLAGS_REG (0x80) +#define TIM_AF_FLAGS_REG_ENA_TIM BIT_ULL(0) +#define TIM_AF_RINGX_CTL1_ENA BIT_ULL(47) +#define TIM_AF_RINGX_CTL1_RCF_BUSY BIT_ULL(50) + +#define TIM_AF_RING_GMCTL_SHIFT 3 +#define TIM_AF_RING_SSO_PF_FUNC_SHIFT 0 /* CPT */ -#define CPT_AF_CONSTANTS0 (0x0000) -#define CPT_PRIV_LFX_CFG (0x41000) -#define CPT_PRIV_LFX_INT_CFG (0x43000) -#define CPT_AF_RVU_LF_CFG_DEBUG (0x45000) -#define CPT_AF_LF_RST (0x44000) -#define CPT_AF_BLK_RST (0x46000) +#define CPT_AF_CONSTANTS0 (0x0ull) +#define CPT_AF_CONSTANTS1 (0x1000ull) +#define CPT_AF_DIAG (0x3000ull) +#define CPT_AF_ECO (0x4000ull) +#define CPT_AF_FLTX_INT(a) (0xa000ull | (u64)(a) << 3) +#define CPT_AF_FLTX_INT_W1S(a) (0xb000ull | (u64)(a) << 3) +#define CPT_AF_FLTX_INT_ENA_W1C(a) (0xc000ull | (u64)(a) << 3) +#define CPT_AF_FLTX_INT_ENA_W1S(a) (0xd000ull | (u64)(a) << 3) +#define CPT_AF_PSNX_EXE(a) (0xe000ull | (u64)(a) << 3) +#define CPT_AF_PSNX_EXE_W1S(a) (0xf000ull | (u64)(a) << 3) +#define CPT_AF_PSNX_LF(a) (0x10000ull | (u64)(a) << 3) +#define CPT_AF_PSNX_LF_W1S(a) (0x11000ull | (u64)(a) << 3) +#define CPT_AF_EXEX_CTL2(a) (0x12000ull | (u64)(a) << 3) +#define CPT_AF_EXEX_STS(a) (0x13000ull | (u64)(a) << 3) +#define CPT_AF_EXE_ERR_INFO (0x14000ull) +#define CPT_AF_EXEX_ACTIVE(a) (0x16000ull | (u64)(a) << 3) +#define CPT_AF_INST_REQ_PC (0x17000ull) +#define CPT_AF_INST_LATENCY_PC (0x18000ull) +#define CPT_AF_RD_REQ_PC (0x19000ull) +#define CPT_AF_RD_LATENCY_PC (0x1a000ull) +#define CPT_AF_RD_UC_PC (0x1b000ull) +#define CPT_AF_ACTIVE_CYCLES_PC (0x1c000ull) +#define CPT_AF_EXE_DBG_CTL (0x1d000ull) +#define CPT_AF_EXE_DBG_DATA (0x1e000ull) +#define CPT_AF_EXE_REQ_TIMER (0x1f000ull) +#define CPT_AF_EXEX_CTL(a) (0x20000ull | (u64)(a) << 3) +#define CPT_AF_EXE_PERF_CTL (0x21000ull) +#define CPT_AF_EXE_DBG_CNTX(a) (0x22000ull | (u64)(a) << 3) +#define CPT_AF_EXE_PERF_EVENT_CNT (0x23000ull) +#define CPT_AF_EXE_EPCI_INBX_CNT(a) (0x24000ull | (u64)(a) << 3) +#define CPT_AF_EXE_EPCI_OUTBX_CNT(a) (0x25000ull | (u64)(a) << 3) +#define CPT_AF_EXEX_UCODE_BASE(a) (0x26000ull | (u64)(a) << 3) +#define CPT_AF_LFX_CTL(a) (0x27000ull | (u64)(a) << 3) +#define CPT_AF_LFX_CTL2(a) (0x29000ull | (u64)(a) << 3) +#define CPT_AF_CPTCLK_CNT (0x2a000ull) +#define CPT_AF_PF_FUNC (0x2b000ull) +#define CPT_AF_LFX_PTR_CTL(a) (0x2c000ull | (u64)(a) << 3) +#define CPT_AF_GRPX_THR(a) (0x2d000ull | (u64)(a) << 3) +#define CPT_AF_CTL (0x2e000ull) +#define CPT_AF_XEX_THR(a) (0x2f000ull | (u64)(a) << 3) +#define CPT_PRIV_LFX_CFG (0x41000ull) +#define CPT_PRIV_AF_INT_CFG (0x42000ull) +#define CPT_PRIV_LFX_INT_CFG (0x43000ull) +#define CPT_AF_LF_RST (0x44000ull) +#define CPT_AF_RVU_LF_CFG_DEBUG (0x45000ull) +#define CPT_AF_BLK_RST (0x46000ull) +#define CPT_AF_RVU_INT (0x47000ull) +#define CPT_AF_RVU_INT_W1S (0x47008ull) +#define CPT_AF_RVU_INT_ENA_W1S (0x47010ull) +#define CPT_AF_RVU_INT_ENA_W1C (0x47018ull) +#define CPT_AF_RAS_INT (0x47020ull) +#define CPT_AF_RAS_INT_W1S (0x47028ull) +#define CPT_AF_RAS_INT_ENA_W1S (0x47030ull) +#define CPT_AF_RAS_INT_ENA_W1C (0x47038ull) -#define NDC_AF_BLK_RST (0x002F0) -#define NPC_AF_BLK_RST (0x00040) +#define CPT_AF_LF_CTL2_SHIFT 3 +#define CPT_AF_LF_SSO_PF_FUNC_SHIFT 32 /* NPC */ #define NPC_AF_CFG (0x00000) @@ -499,4 +732,39 @@ #define NPC_AF_DBG_DATAX(a) (0x3001400 | (a) << 4) #define NPC_AF_DBG_RESULTX(a) (0x3001800 | (a) << 4) +/* NDC */ +#define NDC_AF_CONST (0x00000) +#define NDC_AF_CLK_EN (0x00020) +#define NDC_AF_CTL (0x00030) +#define NDC_AF_BANK_CTL (0x00040) +#define NDC_AF_BANK_CTL_DONE (0x00048) +#define NDC_AF_INTR (0x00058) +#define NDC_AF_INTR_W1S (0x00060) +#define NDC_AF_INTR_ENA_W1S (0x00068) +#define NDC_AF_INTR_ENA_W1C (0x00070) +#define NDC_AF_ACTIVE_PC (0x00078) +#define NDC_AF_BP_TEST_ENABLE (0x001F8) +#define NDC_AF_BP_TEST(a) (0x00200 | (a) << 3) +#define NDC_AF_BLK_RST (0x002F0) +#define NDC_PRIV_AF_INT_CFG (0x002F8) +#define NDC_AF_HASHX(a) (0x00300 | (a) << 3) +#define NDC_AF_PORTX_RTX_RWX_REQ_PC(a, b, c) \ + (0x00C00 | (a) << 5 | (b) << 4 | (c) << 3) +#define NDC_AF_PORTX_RTX_RWX_OSTDN_PC(a, b, c) \ + (0x00D00 | (a) << 5 | (b) << 4 | (c) << 3) +#define NDC_AF_PORTX_RTX_RWX_LAT_PC(a, b, c) \ + (0x00E00 | (a) << 5 | (b) << 4 | (c) << 3) +#define NDC_AF_PORTX_RTX_CANT_ALLOC_PC(a, b) \ + (0x00F00 | (a) << 5 | (b) << 4) +#define NDC_AF_BANKX_HIT_PC(a) (0x01000 | (a) << 3) +#define NDC_AF_BANKX_MISS_PC(a) (0x01100 | (a) << 3) + +#define AF_BAR2_ALIASX_SIZE (0x100000ull) +#define SSOW_AF_BAR2_SEL (0x9000000ull) +#define SSO_AF_BAR2_SEL (0x9000000ull) + +#define AF_BAR2_ALIASX(a, b) (0x9100000ull | (a) << 12 | b) +#define SSOW_AF_BAR2_ALIASX(a, b) AF_BAR2_ALIASX(a, b) +#define SSO_AF_BAR2_ALIASX(a, b) AF_BAR2_ALIASX(a, b) + #endif /* RVU_REG_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c new file mode 100644 index 000000000000..071e69d7da15 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Admin Function driver + * + * Copyright (C) 2019 Marvell International Ltd. + * + * 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/pci.h> +#include "rvu.h" + +/* SDP PF device id */ +#define PCI_DEVID_OTX2_SDP_PF 0xA0F6 + +/* SDP PF number */ +static int sdp_pf_num = -1; + +bool is_sdp_pf(u16 pcifunc) +{ + if (rvu_get_pf(pcifunc) != sdp_pf_num) + return false; + if (pcifunc & RVU_PFVF_FUNC_MASK) + return false; + + return true; +} + +int rvu_sdp_init(struct rvu *rvu) +{ + struct pci_dev *pdev; + int i; + + for (i = 0; i < rvu->hw->total_pfs; i++) { + pdev = pci_get_domain_bus_and_slot( + pci_domain_nr(rvu->pdev->bus), i + 1, 0); + if (!pdev) + continue; + + if (pdev->device == PCI_DEVID_OTX2_SDP_PF) { + sdp_pf_num = i; + put_device(&pdev->dev); + break; + } + + put_device(&pdev->dev); + } + + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_sso.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_sso.c new file mode 100644 index 000000000000..dead90cb2f28 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_sso.c @@ -0,0 +1,1323 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Admin Function driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/interrupt.h> +#include <linux/irq.h> +#include <linux/types.h> + +#include "rvu_struct.h" + +#include "rvu_reg.h" +#include "rvu.h" + +#if defined(CONFIG_ARM64) +#define rvu_sso_store_pair(val0, val1, addr) ({ \ + __asm__ volatile("stp %x[x0], %x[x1], [%x[p1]]" \ + : \ + : \ + [x0]"r"(val0), [x1]"r"(val1), [p1]"r"(addr)); \ + }) +#else +#define rvu_sso_store_pair(val0, val1, addr) \ + do { \ + u64 *addr1 = (void *)addr; \ + *addr1 = val0; \ + *(u64 *)(((u8 *)addr1) + 8) = val1; \ + } while (0) +#endif + +#define SSO_AF_INT_DIGEST_PRNT(reg) \ + for (i = 0; i < block->lf.max / 64; i++) { \ + reg0 = rvu_read64(rvu, blkaddr, reg##X(i)); \ + dev_err(rvu->dev, #reg "(%d) : 0x%llx", i, reg0); \ + rvu_write64(rvu, blkaddr, reg##X(i), reg0); \ + } + +void rvu_sso_hwgrp_config_thresh(struct rvu *rvu, int blkaddr, int lf) +{ + struct rvu_hwinfo *hw = rvu->hw; + u64 add, grp_thr, grp_rsvd; + u64 reg; + + /* Configure IAQ Thresholds */ + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_IAQ_THR(lf)); + grp_rsvd = reg & SSO_HWGRP_IAQ_RSVD_THR_MASK; + add = hw->sso.iaq_rsvd - grp_rsvd; + + grp_thr = hw->sso.iaq_rsvd & SSO_HWGRP_IAQ_RSVD_THR_MASK; + grp_thr |= ((hw->sso.iaq_max & SSO_HWGRP_IAQ_MAX_THR_MASK) << + SSO_HWGRP_IAQ_MAX_THR_SHIFT); + + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_IAQ_THR(lf), grp_thr); + + if (add) + rvu_write64(rvu, blkaddr, SSO_AF_AW_ADD, + (add & SSO_AF_AW_ADD_RSVD_FREE_MASK) << + SSO_AF_AW_ADD_RSVD_FREE_SHIFT); + + /* Configure TAQ Thresholds */ + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_TAQ_THR(lf)); + grp_rsvd = reg & SSO_HWGRP_TAQ_RSVD_THR_MASK; + add = hw->sso.taq_rsvd - grp_rsvd; + + grp_thr = hw->sso.taq_rsvd & SSO_HWGRP_TAQ_RSVD_THR_MASK; + grp_thr |= ((hw->sso.taq_max & SSO_HWGRP_TAQ_MAX_THR_MASK) << + SSO_HWGRP_TAQ_MAX_THR_SHIFT); + + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_TAQ_THR(lf), grp_thr); + + if (add) + rvu_write64(rvu, blkaddr, SSO_AF_TAQ_ADD, + (add & SSO_AF_TAQ_RSVD_FREE_MASK) << + SSO_AF_TAQ_ADD_RSVD_FREE_SHIFT); +} + +static void rvu_sso_enable_aw_src(struct rvu *rvu, int lf_cnt, int sub_blkaddr, + u64 addr, int *lf_arr, u16 pcifunc, u8 shift, + u8 addr_off) +{ + u64 reg; + int lf; + + for (lf = 0; lf < lf_cnt; lf++) { + reg = rvu_read64(rvu, sub_blkaddr, addr | + lf_arr[lf] << addr_off); + + reg |= ((u64)pcifunc << shift); + rvu_write64(rvu, sub_blkaddr, addr | + lf_arr[lf] << addr_off, reg); + } +} + +static int rvu_sso_disable_aw_src(struct rvu *rvu, int **lf_arr, + int sub_blkaddr, u8 shift, u8 addr_off, + u16 pcifunc, u64 addr) +{ + struct rvu_hwinfo *hw = rvu->hw; + struct rvu_block *block; + int lf_cnt = 0, lf; + u64 reg; + + if (sub_blkaddr >= 0) { + block = &hw->block[sub_blkaddr]; + *lf_arr = kmalloc(block->lf.max * sizeof(int), GFP_KERNEL); + if (!*lf_arr) + return 0; + + for (lf = 0; lf < block->lf.max; lf++) { + reg = rvu_read64(rvu, sub_blkaddr, + addr | lf << addr_off); + if (((reg >> shift) & 0xFFFFul) != pcifunc) + continue; + + reg &= ~(0xFFFFul << shift); + rvu_write64(rvu, sub_blkaddr, addr | lf << addr_off, + reg); + (*lf_arr)[lf_cnt] = lf; + lf_cnt++; + } + } + + return lf_cnt; +} + +static void rvu_sso_ggrp_taq_flush(struct rvu *rvu, u16 pcifunc, int lf, + int slot, int ssow_lf, u64 blkaddr, + u64 ssow_blkaddr) +{ + int nix_lf_cnt, cpt_lf_cnt, tim_lf_cnt; + int *nix_lf, *cpt_lf, *tim_lf; + u64 reg, val; + + /* Disable add work. */ + rvu_write64(rvu, blkaddr, SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_QCTL), + 0); + + /* Disable all sources of work. */ + nix_lf = NULL; + nix_lf_cnt = rvu_sso_disable_aw_src(rvu, &nix_lf, + rvu_get_blkaddr(rvu, BLKTYPE_NIX, + 0), + NIX_AF_LF_SSO_PF_FUNC_SHIFT, + NIX_AF_LF_CFG_SHIFT, pcifunc, + NIX_AF_LFX_CFG(0)); + + cpt_lf = NULL; + cpt_lf_cnt = rvu_sso_disable_aw_src(rvu, &cpt_lf, + rvu_get_blkaddr(rvu, BLKTYPE_CPT, + 0), + CPT_AF_LF_SSO_PF_FUNC_SHIFT, + CPT_AF_LF_CTL2_SHIFT, pcifunc, + CPT_AF_LFX_CTL2(0)); + + tim_lf = NULL; + tim_lf_cnt = rvu_sso_disable_aw_src(rvu, &tim_lf, + rvu_get_blkaddr(rvu, BLKTYPE_TIM, + 0), + TIM_AF_RING_SSO_PF_FUNC_SHIFT, + TIM_AF_RING_GMCTL_SHIFT, pcifunc, + TIM_AF_RINGX_GMCTL(0)); + + /* ZIP and DPI blocks not yet implemented. */ + + /* Enable add work. */ + rvu_write64(rvu, blkaddr, SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_QCTL), + 0x1); + + /* Prepare WS for GW operations. */ + do { + reg = rvu_read64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_TAG)); + } while (reg & BIT_ULL(63)); + + if (reg & BIT_ULL(62)) + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_OP_DESCHED), + 0x0); + else if (((reg >> 32) & SSO_TT_EMPTY) != SSO_TT_EMPTY) + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_OP_SWTAG_FLUSH), + 0x0); + + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_OP_GWC_INVAL), 0x0); + /* Drain TAQ. */ + val = slot; + val |= BIT_ULL(18); + val |= BIT_ULL(16); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_TAQ_THR(lf)); + while ((reg >> 48) & 0x7FF) { + rvu_write64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_OP_ADD_WORK1), + 0x1 << 3); +get_work: + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_OP_GET_WORK), + val); + do { + reg = rvu_read64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, + SSOW_LF_GWS_TAG)); + } while (reg & BIT_ULL(63)); + + if (!rvu_read64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_WQP))) + goto get_work; + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_TAQ_THR(lf)); + } + + reg = rvu_read64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_TAG)); + if (((reg >> 32) & SSO_TT_EMPTY) != SSO_TT_EMPTY) + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_OP_SWTAG_FLUSH), + 0x0); + + /* Disable add work. */ + rvu_write64(rvu, blkaddr, SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_QCTL), + 0x0); + + /* restore all sources of work. */ + rvu_sso_enable_aw_src(rvu, nix_lf_cnt, rvu_get_blkaddr(rvu, BLKTYPE_NIX, + 0), + NIX_AF_LFX_CFG(0), nix_lf, pcifunc, + NIX_AF_LF_SSO_PF_FUNC_SHIFT, + NIX_AF_LF_CFG_SHIFT); + rvu_sso_enable_aw_src(rvu, cpt_lf_cnt, rvu_get_blkaddr(rvu, BLKTYPE_CPT, + 0), + CPT_AF_LFX_CTL2(0), cpt_lf, pcifunc, + CPT_AF_LF_SSO_PF_FUNC_SHIFT, + CPT_AF_LF_CTL2_SHIFT); + rvu_sso_enable_aw_src(rvu, tim_lf_cnt, rvu_get_blkaddr(rvu, BLKTYPE_TIM, + 0), + TIM_AF_RINGX_GMCTL(0), tim_lf, pcifunc, + TIM_AF_RING_SSO_PF_FUNC_SHIFT, + TIM_AF_RING_GMCTL_SHIFT); + + kfree(nix_lf); + kfree(cpt_lf); + kfree(tim_lf); +} + +int rvu_sso_lf_teardown(struct rvu *rvu, u16 pcifunc, int lf, int slot) +{ + int ssow_lf, iue, blkaddr, ssow_blkaddr, err; + struct sso_rsrc *sso = &rvu->hw->sso; + struct rvu_hwinfo *hw = rvu->hw; + u64 aq_cnt, ds_cnt, cq_ds_cnt; + u64 reg, add, wqp, val; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return SSO_AF_ERR_LF_INVALID; + + /* Enable BAR2 ALIAS for this pcifunc. */ + reg = BIT_ULL(16) | pcifunc; + rvu_write64(rvu, blkaddr, SSO_AF_BAR2_SEL, reg); + + rvu_write64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_INT_THR), 0x0); + rvu_write64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_AQ_THR), + SSO_LF_GGRP_AQ_THR_MASK); + + rvu_write64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_INT), + SSO_LF_GGRP_INT_MASK); + rvu_write64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_INT_ENA_W1C), + SSO_LF_GGRP_INT_MASK); + + ssow_blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSOW, 0); + if (ssow_blkaddr < 0) + goto af_cleanup; + /* Check if LF is in slot 0, if not no HWS are attached. */ + ssow_lf = rvu_get_lf(rvu, &hw->block[ssow_blkaddr], pcifunc, 0); + if (ssow_lf < 0) + goto af_cleanup; + + rvu_write64(rvu, ssow_blkaddr, SSOW_AF_BAR2_SEL, reg); + + /* Ignore all interrupts */ + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_INT_ENA_W1C), + SSOW_LF_GWS_INT_MASK); + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_INT), + SSOW_LF_GWS_INT_MASK); + + /* Prepare WS for GW operations. */ + do { + reg = rvu_read64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_TAG)); + } while (reg & BIT_ULL(63)); + + if (reg & BIT_ULL(62)) + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_OP_DESCHED), 0); + else if (((reg >> 32) & SSO_TT_EMPTY) != SSO_TT_EMPTY) + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_OP_SWTAG_FLUSH), + 0); + + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_OP_GWC_INVAL), 0); + + /* Disable add work. */ + rvu_write64(rvu, blkaddr, SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_QCTL), + 0x0); + + /* HRM 14.13.4 (4) */ + /* Clean up nscheduled IENT let the work flow. */ + for (iue = 0; iue < sso->sso_iue; iue++) { + reg = rvu_read64(rvu, blkaddr, SSO_AF_IENTX_GRP(iue)); + if (SSO_AF_HWGRPX_IUEX_NOSCHED(lf, reg)) { + wqp = rvu_read64(rvu, blkaddr, SSO_AF_IENTX_WQP(iue)); + rvu_sso_store_pair(wqp, iue, rvu->afreg_base + + ((ssow_blkaddr << 28) | + SSOW_AF_BAR2_ALIASX(0, + SSOW_LF_GWS_OP_CLR_NSCHED0))); + } + } + + /* HRM 14.13.4 (6) */ + /* Drain all the work using grouped gw. */ + aq_cnt = rvu_read64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_AQ_CNT)); + ds_cnt = rvu_read64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_MISC_CNT)); + cq_ds_cnt = rvu_read64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_INT_CNT)); + cq_ds_cnt &= SSO_LF_GGRP_INT_CNT_MASK; + + val = slot; /* GGRP ID */ + val |= BIT_ULL(18); /* Grouped */ + val |= BIT_ULL(16); /* WAIT */ + + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_NW_TIM), + SSOW_LF_GWS_MAX_NW_TIM); + + while (aq_cnt || cq_ds_cnt || ds_cnt) { + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_OP_GET_WORK), + val); + do { + reg = rvu_read64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, + SSOW_LF_GWS_TAG)); + } while (reg & BIT_ULL(63)); + if (((reg >> 32) & SSO_TT_EMPTY) != SSO_TT_EMPTY) + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, + SSOW_LF_GWS_OP_SWTAG_FLUSH), + 0x0); + aq_cnt = rvu_read64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_AQ_CNT) + ); + ds_cnt = rvu_read64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, + SSO_LF_GGRP_MISC_CNT)); + cq_ds_cnt = rvu_read64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, + SSO_LF_GGRP_INT_CNT)); + /* Extract cq and ds count */ + cq_ds_cnt &= SSO_LF_GGRP_INT_CNT_MASK; + } + + /* Due to the Errata 35432, SSO doesn't release the partially consumed + * TAQ buffer used by HWGRP when HWGRP is reset. Use SW routine to + * drain it manually. + */ + if (is_rvu_96xx_B0(rvu)) + rvu_sso_ggrp_taq_flush(rvu, pcifunc, lf, slot, ssow_lf, blkaddr, + ssow_blkaddr); + + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_NW_TIM), 0x0); + + /* HRM 14.13.4 (7) */ + reg = rvu_read64(rvu, blkaddr, + SSO_AF_BAR2_ALIASX(slot, SSO_LF_GGRP_XAQ_CNT)) + & SSO_LF_GGRP_XAQ_CNT_MASK; + if (reg != 0) + dev_warn(rvu->dev, + "SSO_LF[%d]_GGRP_XAQ_CNT is %lld expected 0", lf, reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_PAGE_CNT(lf)) + & SSO_AF_HWGRP_PAGE_CNT_MASK; + if (reg != 0) + dev_warn(rvu->dev, + "SSO_AF_HWGRP[%d]_PAGE_CNT is %lld expected 0", lf, + reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_IAQ_THR(lf)) + >> SSO_HWGRP_IAQ_GRP_CNT_SHIFT; + reg &= SSO_HWGRP_IAQ_GRP_CNT_MASK; + if (reg != 0) + dev_warn(rvu->dev, + "SSO_AF_HWGRP[%d]_IAQ_THR is %lld expected 0", lf, + reg); + rvu_write64(rvu, ssow_blkaddr, SSOW_AF_BAR2_SEL, 0); + rvu_write64(rvu, blkaddr, SSO_AF_HWSX_INV(ssow_lf), 0x1); + +af_cleanup: + reg = rvu_read64(rvu, blkaddr, SSO_AF_UNMAP_INFO); + if ((reg & 0xFFF) == pcifunc) + rvu_write64(rvu, blkaddr, SSO_AF_ERR0, SSO_AF_ERR0_MASK); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_UNMAP_INFO2); + if ((reg & 0xFFF) == pcifunc) + rvu_write64(rvu, blkaddr, SSO_AF_ERR2, SSO_AF_ERR2_MASK); + + rvu_write64(rvu, blkaddr, SSO_AF_POISONX(lf / 64), lf % 64); + rvu_write64(rvu, blkaddr, SSO_AF_IU_ACCNTX_RST(lf), 0x1); + + err = rvu_poll_reg(rvu, blkaddr, SSO_AF_HWGRPX_AW_STATUS(lf), + SSO_HWGRP_AW_STS_NPA_FETCH, true); + if (err) + dev_warn(rvu->dev, + "SSO_HWGRP(%d)_AW_STATUS[NPA_FETCH] not cleared", lf); + + /* Remove all pointers from XAQ, HRM 14.13.6 */ + rvu_write64(rvu, blkaddr, SSO_AF_ERR0_ENA_W1C, ~0ULL); + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_AW_CFG(lf)); + reg = (reg & ~SSO_HWGRP_AW_CFG_RWEN) | SSO_HWGRP_AW_CFG_XAQ_BYP_DIS; + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_AW_CFG(lf), reg); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_AW_STATUS(lf)); + if (reg & SSO_HWGRP_AW_STS_TPTR_VLD) { + /* aura will be torn down, no need to free the pointer. */ + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_AW_STATUS(lf), + SSO_HWGRP_AW_STS_TPTR_VLD); + } + + err = rvu_poll_reg(rvu, blkaddr, SSO_AF_HWGRPX_AW_STATUS(lf), + SSO_HWGRP_AW_STS_XAQ_BUFSC_MASK, true); + if (err) { + dev_warn(rvu->dev, + "SSO_HWGRP(%d)_AW_STATUS[XAQ_BUF_CACHED] not cleared", + lf); + return err; + } + + rvu_write64(rvu, blkaddr, SSO_AF_ERR0, ~0ULL); + /* Re-enable error reporting once we're finished */ + rvu_write64(rvu, blkaddr, SSO_AF_ERR0_ENA_W1S, ~0ULL); + + /* HRM 14.13.4 (13) */ + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_AW_STATUS(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_AW_CFG(lf), + SSO_HWGRP_AW_CFG_LDWB | SSO_HWGRP_AW_CFG_LDT | + SSO_HWGRP_AW_CFG_STT); + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_XAQ_AURA(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_XAQX_GMCTL(lf), 0x0); + reg = (SSO_HWGRP_PRI_AFF_MASK << SSO_HWGRP_PRI_AFF_SHIFT) | + (SSO_HWGRP_PRI_WGT_MASK << SSO_HWGRP_PRI_WGT_SHIFT) | + (0x1 << SSO_HWGRP_PRI_WGT_SHIFT); + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_PRI(lf), reg); + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_WS_PC(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_EXT_PC(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_WA_PC(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_TS_PC(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_DS_PC(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_XAQ_LIMIT(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_IU_ACCNT(lf), 0x0); + + /* The delta between the current and default thresholds + * need to be returned to the SSO + */ + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_IAQ_THR(lf)) & + SSO_HWGRP_IAQ_RSVD_THR_MASK; + add = SSO_HWGRP_IAQ_RSVD_THR - reg; + reg = (SSO_HWGRP_IAQ_MAX_THR_MASK << SSO_HWGRP_IAQ_MAX_THR_SHIFT) | + SSO_HWGRP_IAQ_RSVD_THR; + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_IAQ_THR(lf), reg); + + if (add) + rvu_write64(rvu, blkaddr, SSO_AF_AW_ADD, + (add & SSO_AF_AW_ADD_RSVD_FREE_MASK) << + SSO_AF_AW_ADD_RSVD_FREE_SHIFT); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_TAQ_THR(lf)) & + SSO_HWGRP_TAQ_RSVD_THR_MASK; + add = SSO_HWGRP_TAQ_RSVD_THR - reg; + reg = (SSO_HWGRP_TAQ_MAX_THR_MASK << SSO_HWGRP_TAQ_MAX_THR_SHIFT) | + SSO_HWGRP_TAQ_RSVD_THR; + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_TAQ_THR(lf), reg); + if (add) + rvu_write64(rvu, blkaddr, SSO_AF_TAQ_ADD, + (add & SSO_AF_TAQ_RSVD_FREE_MASK) << + SSO_AF_TAQ_ADD_RSVD_FREE_SHIFT); + + rvu_write64(rvu, blkaddr, SSO_AF_XAQX_HEAD_PTR(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_XAQX_TAIL_PTR(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_XAQX_HEAD_NEXT(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_XAQX_TAIL_NEXT(lf), 0x0); + + rvu_write64(rvu, blkaddr, SSO_AF_BAR2_SEL, 0); + + return 0; +} + +int rvu_ssow_lf_teardown(struct rvu *rvu, u16 pcifunc, int lf, int slot) +{ + int blkaddr, ssow_blkaddr; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return SSOW_AF_ERR_LF_INVALID; + + ssow_blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSOW, 0); + if (ssow_blkaddr < 0) + return SSOW_AF_ERR_LF_INVALID; + + /* Enable BAR2 alias access. */ + reg = BIT_ULL(16) | pcifunc; + rvu_write64(rvu, ssow_blkaddr, SSOW_AF_BAR2_SEL, reg); + + /* Ignore all interrupts */ + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_INT_ENA_W1C), + SSOW_LF_GWS_INT_MASK); + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_INT), + SSOW_LF_GWS_INT_MASK); + + /* HRM 14.13.4 (3) */ + /* Wait till waitw/desched completes. */ + do { + reg = rvu_read64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(slot, + SSOW_LF_GWS_PENDSTATE)); + } while (reg & (BIT_ULL(63) | BIT_ULL(58))); + + reg = rvu_read64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(slot, SSOW_LF_GWS_TAG)); + /* Switch Tag Pending */ + if (reg & BIT_ULL(62)) + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(slot, SSOW_LF_GWS_OP_DESCHED), + 0x0); + /* Tag Type != EMPTY use swtag_flush to release tag-chain. */ + else if (((reg >> 32) & SSO_TT_EMPTY) != SSO_TT_EMPTY) + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(slot, + SSOW_LF_GWS_OP_SWTAG_FLUSH), + 0x0); + + /* Wait for desched to complete. */ + do { + reg = rvu_read64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(slot, + SSOW_LF_GWS_PENDSTATE)); + } while (reg & BIT_ULL(58)); + + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_NW_TIM), 0x0); + rvu_write64(rvu, ssow_blkaddr, + SSOW_AF_BAR2_ALIASX(0, SSOW_LF_GWS_OP_GWC_INVAL), 0x0); + + /* set SAI_INVAL bit */ + rvu_write64(rvu, blkaddr, SSO_AF_HWSX_INV(lf), 0x1); + rvu_write64(rvu, blkaddr, SSO_AF_HWSX_ARB(lf), 0x0); + rvu_write64(rvu, blkaddr, SSO_AF_HWSX_GMCTL(lf), 0x0); + + rvu_write64(rvu, ssow_blkaddr, SSOW_AF_BAR2_SEL, 0x0); + + return 0; +} + +int rvu_mbox_handler_sso_hw_setconfig(struct rvu *rvu, + struct sso_hw_setconfig *req, + struct msg_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + int hwgrp, lf, err, blkaddr; + u32 npa_aura_id; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, pcifunc); + if (blkaddr < 0) + return SSO_AF_ERR_LF_INVALID; + + npa_aura_id = req->npa_aura_id; + + /* Check if requested 'SSOLF <=> NPALF' mapping is valid */ + if (req->npa_pf_func) { + /* If default, use 'this' SSOLF's PFFUNC */ + if (req->npa_pf_func == RVU_DEFAULT_PF_FUNC) + req->npa_pf_func = pcifunc; + if (!is_pffunc_map_valid(rvu, req->npa_pf_func, BLKTYPE_NPA)) + return SSO_AF_INVAL_NPA_PF_FUNC; + } + + /* Initialize XAQ ring */ + for (hwgrp = 0; hwgrp < req->hwgrps; hwgrp++) { + lf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, hwgrp); + if (lf < 0) + return SSO_AF_ERR_LF_INVALID; + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_AW_STATUS(lf)); + if (reg & SSO_HWGRP_AW_STS_XAQ_BUFSC_MASK || reg & BIT_ULL(3)) { + reg = rvu_read64(rvu, blkaddr, + SSO_AF_HWGRPX_AW_CFG(lf)); + reg = (reg & ~SSO_HWGRP_AW_CFG_RWEN) | + SSO_HWGRP_AW_CFG_XAQ_BYP_DIS; + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_AW_CFG(lf), + reg); + + reg = rvu_read64(rvu, blkaddr, + SSO_AF_HWGRPX_AW_STATUS(lf)); + if (reg & SSO_HWGRP_AW_STS_TPTR_VLD) { + rvu_poll_reg(rvu, blkaddr, + SSO_AF_HWGRPX_AW_STATUS(lf), + SSO_HWGRP_AW_STS_NPA_FETCH, true); + + rvu_write64(rvu, blkaddr, + SSO_AF_HWGRPX_AW_STATUS(lf), + SSO_HWGRP_AW_STS_TPTR_VLD); + } + + if (rvu_poll_reg(rvu, blkaddr, + SSO_AF_HWGRPX_AW_STATUS(lf), + SSO_HWGRP_AW_STS_XAQ_BUFSC_MASK, true)) + dev_warn(rvu->dev, + "SSO_HWGRP(%d)_AW_STATUS[XAQ_BUF_CACHED] not cleared", + lf); + } + + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_XAQ_AURA(lf), + npa_aura_id); + rvu_write64(rvu, blkaddr, SSO_AF_XAQX_GMCTL(lf), + req->npa_pf_func); + + /* enable XAQ */ + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_AW_CFG(lf), 0xF); + + /* Wait for ggrp to ack. */ + err = rvu_poll_reg(rvu, blkaddr, + SSO_AF_HWGRPX_AW_STATUS(lf), + SSO_HWGRP_AW_STS_INIT_STS, false); + + reg = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_AW_STATUS(lf)); + if (err || (reg & BIT_ULL(4)) || !(reg & BIT_ULL(8))) { + dev_warn(rvu->dev, "SSO_HWGRP(%d) XAQ NPA pointer initialization failed", + lf); + return -ENOMEM; + } + } + + return 0; +} + +int rvu_mbox_handler_sso_grp_set_priority(struct rvu *rvu, + struct sso_grp_priority *req, + struct msg_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + int lf, blkaddr; + u64 regval; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, pcifunc); + if (blkaddr < 0) + return SSO_AF_ERR_LF_INVALID; + + regval = (((u64)(req->weight & SSO_HWGRP_PRI_WGT_MASK) + << SSO_HWGRP_PRI_WGT_SHIFT) | + ((u64)(req->affinity & SSO_HWGRP_PRI_AFF_MASK) + << SSO_HWGRP_PRI_AFF_SHIFT) | + (req->priority & SSO_HWGRP_PRI_MASK)); + + lf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, req->grp); + if (lf < 0) + return SSO_AF_ERR_LF_INVALID; + + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_PRI(lf), regval); + + return 0; +} + +int rvu_mbox_handler_sso_grp_get_priority(struct rvu *rvu, + struct sso_info_req *req, + struct sso_grp_priority *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + int lf, blkaddr; + u64 regval; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, pcifunc); + if (blkaddr < 0) + return SSO_AF_ERR_LF_INVALID; + + lf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, req->grp); + if (lf < 0) + return SSO_AF_ERR_LF_INVALID; + + regval = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_PRI(lf)); + + rsp->weight = (regval >> SSO_HWGRP_PRI_WGT_SHIFT) + & SSO_HWGRP_PRI_WGT_MASK; + rsp->affinity = (regval >> SSO_HWGRP_PRI_AFF_SHIFT) + & SSO_HWGRP_PRI_AFF_MASK; + rsp->priority = regval & SSO_HWGRP_PRI_MASK; + + return 0; +} + +int rvu_mbox_handler_sso_grp_qos_config(struct rvu *rvu, + struct sso_grp_qos_cfg *req, + struct msg_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + u64 regval, grp_rsvd; + int lf, blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, pcifunc); + if (blkaddr < 0) + return SSO_AF_ERR_LF_INVALID; + + lf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, req->grp); + if (lf < 0) + return SSO_AF_ERR_LF_INVALID; + + /* Check if GGRP has been active. */ + regval = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_WA_PC(lf)); + if (regval) + return SSO_AF_ERR_GRP_EBUSY; + + /* Configure XAQ threhold */ + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_XAQ_LIMIT(lf), req->xaq_limit); + + /* Configure TAQ threhold */ + regval = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_TAQ_THR(lf)); + grp_rsvd = regval & SSO_HWGRP_TAQ_RSVD_THR_MASK; + if (req->taq_thr < grp_rsvd) + req->taq_thr = grp_rsvd; + + regval = req->taq_thr & SSO_HWGRP_TAQ_MAX_THR_MASK; + regval = (regval << SSO_HWGRP_TAQ_MAX_THR_SHIFT) | grp_rsvd; + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_TAQ_THR(lf), regval); + + /* Configure IAQ threhold */ + regval = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_IAQ_THR(lf)); + grp_rsvd = regval & SSO_HWGRP_IAQ_RSVD_THR_MASK; + if (req->iaq_thr < grp_rsvd + 4) + req->iaq_thr = grp_rsvd + 4; + + regval = req->iaq_thr & SSO_HWGRP_IAQ_MAX_THR_MASK; + regval = (regval << SSO_HWGRP_IAQ_MAX_THR_SHIFT) | grp_rsvd; + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_IAQ_THR(lf), regval); + + return 0; +} + +int rvu_mbox_handler_sso_grp_get_stats(struct rvu *rvu, + struct sso_info_req *req, + struct sso_grp_stats *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + int lf, blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, pcifunc); + if (blkaddr < 0) + return SSO_AF_ERR_LF_INVALID; + + lf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, req->grp); + if (lf < 0) + return SSO_AF_ERR_LF_INVALID; + + rsp->ws_pc = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_WS_PC(lf)); + rsp->ext_pc = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_EXT_PC(lf)); + rsp->wa_pc = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_WA_PC(lf)); + rsp->ts_pc = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_TS_PC(lf)); + rsp->ds_pc = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_DS_PC(lf)); + rsp->dq_pc = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_DQ_PC(lf)); + rsp->aw_status = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_AW_STATUS(lf)); + rsp->page_cnt = rvu_read64(rvu, blkaddr, SSO_AF_HWGRPX_PAGE_CNT(lf)); + + return 0; +} + +int rvu_mbox_handler_sso_hws_get_stats(struct rvu *rvu, + struct sso_info_req *req, + struct sso_hws_stats *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + int lf, blkaddr, ssow_blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, pcifunc); + if (blkaddr < 0) + return SSO_AF_ERR_LF_INVALID; + + ssow_blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSOW, pcifunc); + if (ssow_blkaddr < 0) + return SSO_AF_ERR_LF_INVALID; + + lf = rvu_get_lf(rvu, &hw->block[ssow_blkaddr], pcifunc, req->hws); + if (lf < 0) + return SSO_AF_ERR_LF_INVALID; + + rsp->arbitration = rvu_read64(rvu, blkaddr, SSO_AF_HWSX_ARB(lf)); + + return 0; +} + +int rvu_mbox_handler_sso_lf_alloc(struct rvu *rvu, struct sso_lf_alloc_req *req, + struct sso_lf_alloc_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + int ssolf, uniq_ident, rc = 0; + struct rvu_pfvf *pfvf; + int hwgrp, blkaddr; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, pcifunc); + if (pfvf->sso <= 0 || blkaddr < 0) + return SSO_AF_ERR_LF_INVALID; + + if (!pfvf->sso_uniq_ident) { + uniq_ident = rvu_alloc_rsrc(&hw->sso.pfvf_ident); + if (uniq_ident < 0) { + rc = SSO_AF_ERR_AF_LF_ALLOC; + goto exit; + } + pfvf->sso_uniq_ident = uniq_ident; + } else { + uniq_ident = pfvf->sso_uniq_ident; + } + + /* Set threshold for the In-Unit Accounting Index*/ + rvu_write64(rvu, blkaddr, SSO_AF_IU_ACCNTX_CFG(uniq_ident), + SSO_AF_HWGRP_IU_ACCNT_MAX_THR << 16); + + for (hwgrp = 0; hwgrp < req->hwgrps; hwgrp++) { + ssolf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, hwgrp); + if (ssolf < 0) + return SSO_AF_ERR_LF_INVALID; + + /* All groups assigned to single SR-IOV function must be + * assigned same unique in-unit accounting index. + */ + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_IU_ACCNT(ssolf), + 0x10000 | uniq_ident); + + /* Assign unique tagspace */ + rvu_write64(rvu, blkaddr, SSO_AF_HWGRPX_AW_TAGSPACE(ssolf), + uniq_ident); + } + +exit: + rsp->xaq_buf_size = hw->sso.sso_xaq_buf_size; + rsp->xaq_wq_entries = hw->sso.sso_xaq_num_works; + rsp->in_unit_entries = hw->sso.sso_iue; + rsp->hwgrps = hw->sso.sso_hwgrps; + return rc; +} + +int rvu_mbox_handler_sso_lf_free(struct rvu *rvu, struct sso_lf_free_req *req, + struct msg_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + int hwgrp, lf, err, blkaddr; + struct rvu_pfvf *pfvf; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, pcifunc); + if (blkaddr < 0) + return SSO_AF_ERR_LF_INVALID; + + /* Perform reset of SSO HW GRPs */ + for (hwgrp = 0; hwgrp < req->hwgrps; hwgrp++) { + lf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, hwgrp); + if (lf < 0) + return SSO_AF_ERR_LF_INVALID; + + err = rvu_sso_lf_teardown(rvu, pcifunc, lf, hwgrp); + if (err) + return err; + + /* Reset this SSO LF */ + err = rvu_lf_reset(rvu, &hw->block[blkaddr], lf); + if (err) + dev_err(rvu->dev, "SSO%d free: failed to reset\n", lf); + /* Reset the IAQ and TAQ thresholds */ + rvu_sso_hwgrp_config_thresh(rvu, blkaddr, lf); + } + + if (pfvf->sso_uniq_ident) { + rvu_free_rsrc(&hw->sso.pfvf_ident, pfvf->sso_uniq_ident); + pfvf->sso_uniq_ident = 0; + } + + return 0; +} + +int rvu_mbox_handler_sso_ws_cache_inv(struct rvu *rvu, struct msg_req *req, + struct msg_rsp *rsp) +{ + int num_lfs, ssowlf, hws, blkaddr; + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + struct rvu_block *block; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSOW, pcifunc); + if (blkaddr < 0) + return SSOW_AF_ERR_LF_INVALID; + + block = &hw->block[blkaddr]; + + num_lfs = rvu_get_rsrc_mapcount(rvu_get_pfvf(rvu, pcifunc), + block->type); + if (!num_lfs) + return SSOW_AF_ERR_LF_INVALID; + + /* SSO HWS invalidate registers are part of SSO AF */ + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, pcifunc); + if (blkaddr < 0) + return SSO_AF_ERR_LF_INVALID; + + for (hws = 0; hws < num_lfs; hws++) { + ssowlf = rvu_get_lf(rvu, block, pcifunc, hws); + if (ssowlf < 0) + return SSOW_AF_ERR_LF_INVALID; + + /* Reset this SSO LF GWS cache */ + rvu_write64(rvu, blkaddr, SSO_AF_HWSX_INV(ssowlf), 1); + } + + return 0; +} + +int rvu_mbox_handler_ssow_lf_alloc(struct rvu *rvu, + struct ssow_lf_alloc_req *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + struct rvu_pfvf *pfvf; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + if (pfvf->ssow <= 0) + return SSOW_AF_ERR_LF_INVALID; + + return 0; +} + +int rvu_mbox_handler_ssow_lf_free(struct rvu *rvu, + struct ssow_lf_free_req *req, + struct msg_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + int ssowlf, hws, err, blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSOW, pcifunc); + if (blkaddr < 0) + return SSOW_AF_ERR_LF_INVALID; + + for (hws = 0; hws < req->hws; hws++) { + ssowlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, hws); + if (ssowlf < 0) + return SSOW_AF_ERR_LF_INVALID; + + err = rvu_ssow_lf_teardown(rvu, pcifunc, ssowlf, hws); + if (err) + return err; + + /* Reset this SSO LF */ + err = rvu_lf_reset(rvu, &hw->block[blkaddr], ssowlf); + if (err) + dev_err(rvu->dev, "SSOW%d free: failed to reset\n", + ssowlf); + } + + return 0; +} + +static int rvu_sso_do_register_interrupt(struct rvu *rvu, int irq_offs, + irq_handler_t handler, + const char *name) +{ + int ret = 0; + + ret = request_irq(pci_irq_vector(rvu->pdev, irq_offs), handler, 0, + name, rvu); + if (ret) { + dev_err(rvu->dev, "SSOAF: %s irq registration failed", name); + goto err; + } + + WARN_ON(rvu->irq_allocated[irq_offs]); + rvu->irq_allocated[irq_offs] = true; +err: + return ret; +} + +static irqreturn_t rvu_sso_af_err0_intr_handler(int irq, void *ptr) +{ + struct rvu *rvu = (struct rvu *)ptr; + struct rvu_block *block; + int i, blkaddr; + u64 reg, reg0; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return IRQ_NONE; + + block = &rvu->hw->block[blkaddr]; + reg = rvu_read64(rvu, blkaddr, SSO_AF_ERR0); + dev_err(rvu->dev, "Received SSO_AF_ERR0 irq : 0x%llx", reg); + + if (reg & BIT_ULL(15)) { + dev_err(rvu->dev, "Received Bad-fill-packet NCB error"); + SSO_AF_INT_DIGEST_PRNT(SSO_AF_POISON) + } + + if (reg & BIT_ULL(14)) { + dev_err(rvu->dev, "An FLR was initiated, but SSO_LF_GGRP_AQ_CNT[AQ_CNT] != 0"); + SSO_AF_INT_DIGEST_PRNT(SSO_AF_FLR_AQ_DIGEST) + } + + if (reg & BIT_ULL(13)) { + dev_err(rvu->dev, "Add work dropped due to XAQ pointers not yet initialized."); + SSO_AF_INT_DIGEST_PRNT(SSO_AF_XAQDIS_DIGEST) + } + + if (reg & (0xF << 9)) { + dev_err(rvu->dev, "PF_FUNC mapping error."); + dev_err(rvu->dev, "SSO_AF_UNMAP_INFO : 0x%llx", + rvu_read64(rvu, blkaddr, SSO_AF_UNMAP_INFO)); + } + + if (reg & BIT_ULL(8)) { + dev_err(rvu->dev, "Add work dropped due to QTL being disabled, 0x0"); + SSO_AF_INT_DIGEST_PRNT(SSO_AF_QCTLDIS_DIGEST) + } + + if (reg & BIT_ULL(7)) { + dev_err(rvu->dev, "Add work dropped due to WQP being 0x0"); + SSO_AF_INT_DIGEST_PRNT(SSO_AF_WQP0_DIGEST) + } + + if (reg & BIT_ULL(6)) + dev_err(rvu->dev, "Add work dropped due to 64 bit write"); + + if (reg & BIT_ULL(5)) + dev_err(rvu->dev, "Set when received add work with tag type is specified as EMPTY"); + + if (reg & BIT_ULL(4)) { + dev_err(rvu->dev, "Add work to disabled hardware group. An ADDWQ was received and dropped to a hardware group with SSO_AF_HWGRP(0..255)_IAQ_THR[RSVD_THR] = 0."); + SSO_AF_INT_DIGEST_PRNT(SSO_AF_GRPDIS_DIGEST) + } + + if (reg & BIT_ULL(3)) { + dev_err(rvu->dev, "Bad-fill-packet NCB error"); + SSO_AF_INT_DIGEST_PRNT(SSO_AF_BFPN_DIGEST) + } + + if (reg & BIT_ULL(2)) { + dev_err(rvu->dev, "Bad-fill-packet error."); + SSO_AF_INT_DIGEST_PRNT(SSO_AF_BFP_DIGEST) + } + + if (reg & BIT_ULL(1)) { + dev_err(rvu->dev, "The NPA returned an error indication"); + SSO_AF_INT_DIGEST_PRNT(SSO_AF_NPA_DIGEST) + } + + rvu_write64(rvu, blkaddr, SSO_AF_ERR0, reg); + return IRQ_HANDLED; +} + +static irqreturn_t rvu_sso_af_err2_intr_handler(int irq, void *ptr) +{ + struct rvu *rvu = (struct rvu *)ptr; + int blkaddr; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return IRQ_NONE; + + reg = rvu_read64(rvu, blkaddr, SSO_AF_ERR2); + dev_err(rvu->dev, "received SSO_AF_ERR2 irq : 0x%llx", reg); + rvu_write64(rvu, blkaddr, SSO_AF_ERR2, reg); + + return IRQ_HANDLED; +} + +static irqreturn_t rvu_sso_af_ras_intr_handler(int irq, void *ptr) +{ + struct rvu *rvu = (struct rvu *)ptr; + struct rvu_block *block; + int i, blkaddr; + u64 reg, reg0; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return IRQ_NONE; + + block = &rvu->hw->block[blkaddr]; + + reg = rvu_read64(rvu, blkaddr, SSO_AF_RAS); + dev_err(rvu->dev, "received SSO_AF_RAS irq : 0x%llx", reg); + rvu_write64(rvu, blkaddr, SSO_AF_RAS, reg); + SSO_AF_INT_DIGEST_PRNT(SSO_AF_POISON) + + return IRQ_HANDLED; +} + +void rvu_sso_unregister_interrupts(struct rvu *rvu) +{ + int i, blkaddr, offs; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return; + + offs = rvu_read64(rvu, blkaddr, SSO_PRIV_AF_INT_CFG) & 0x7FF; + if (!offs) + return; + + rvu_write64(rvu, blkaddr, SSO_AF_RAS_ENA_W1C, ~0ULL); + rvu_write64(rvu, blkaddr, SSO_AF_ERR2_ENA_W1C, ~0ULL); + rvu_write64(rvu, blkaddr, SSO_AF_ERR0_ENA_W1C, ~0ULL); + + for (i = 0; i < SSO_AF_INT_VEC_CNT; i++) + if (rvu->irq_allocated[offs + i]) { + free_irq(pci_irq_vector(rvu->pdev, offs + i), rvu); + rvu->irq_allocated[offs + i] = false; + } +} + +int rvu_sso_register_interrupts(struct rvu *rvu) +{ + int blkaddr, offs, ret = 0; + + if (!is_block_implemented(rvu->hw, BLKADDR_SSO)) + return 0; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return blkaddr; + + offs = rvu_read64(rvu, blkaddr, SSO_PRIV_AF_INT_CFG) & 0x7FF; + if (!offs) { + dev_warn(rvu->dev, + "Failed to get SSO_AF_INT vector offsets\n"); + return 0; + } + + ret = rvu_sso_do_register_interrupt(rvu, offs + SSO_AF_INT_VEC_ERR0, + rvu_sso_af_err0_intr_handler, + "SSO_AF_ERR0"); + if (ret) + goto err; + rvu_write64(rvu, blkaddr, SSO_AF_ERR0_ENA_W1S, ~0ULL); + + ret = rvu_sso_do_register_interrupt(rvu, offs + SSO_AF_INT_VEC_ERR2, + rvu_sso_af_err2_intr_handler, + "SSO_AF_ERR2"); + if (ret) + goto err; + rvu_write64(rvu, blkaddr, SSO_AF_ERR2_ENA_W1S, ~0ULL); + + ret = rvu_sso_do_register_interrupt(rvu, offs + SSO_AF_INT_VEC_RAS, + rvu_sso_af_ras_intr_handler, + "SSO_AF_RAS"); + if (ret) + goto err; + rvu_write64(rvu, blkaddr, SSO_AF_RAS_ENA_W1S, ~0ULL); + + return 0; +err: + rvu_sso_unregister_interrupts(rvu); + return ret; +} + +int rvu_sso_init(struct rvu *rvu) +{ + u64 iaq_free_cnt, iaq_rsvd, iaq_max, iaq_rsvd_cnt = 0; + u64 taq_free_cnt, taq_rsvd, taq_max, taq_rsvd_cnt = 0; + struct sso_rsrc *sso = &rvu->hw->sso; + int blkaddr, hwgrp, grpmsk, hws, err; + u64 reg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_SSO, 0); + if (blkaddr < 0) + return 0; + + reg = rvu_read64(rvu, blkaddr, SSO_AF_CONST); + /* number of SSO hardware work slots */ + sso->sso_hws = (reg >> 56) & 0xFF; + /* number of SSO hardware groups */ + sso->sso_hwgrps = (reg & 0xFFFF); + /* number of SSO In-Unit entries */ + sso->sso_iue = (reg >> 16) & 0xFFFF; + + reg = rvu_read64(rvu, blkaddr, SSO_AF_CONST1); + /* number of work entries in external admission queue (XAQ) */ + sso->sso_xaq_num_works = (reg >> 16) & 0xFFFF; + /* number of bytes in a XAQ buffer */ + sso->sso_xaq_buf_size = (reg & 0xFFFF); + + /* Configure IAQ entries */ + reg = rvu_read64(rvu, blkaddr, SSO_AF_AW_WE); + iaq_free_cnt = reg & SSO_AF_IAQ_FREE_CNT_MASK; + + /* Give out half of buffers fairly, rest left floating */ + iaq_rsvd = iaq_free_cnt / sso->sso_hwgrps / 2; + + /* Enforce minimum per hardware requirements */ + if (iaq_rsvd < SSO_HWGRP_IAQ_RSVD_THR) + iaq_rsvd = SSO_HWGRP_IAQ_RSVD_THR; + /* To ensure full streaming performance should be at least 208. */ + iaq_max = iaq_rsvd + SSO_HWGRP_IAQ_MAX_THR_STRM_PERF; + + if (iaq_max >= (SSO_AF_IAQ_FREE_CNT_MAX + 1)) + iaq_max = SSO_AF_IAQ_FREE_CNT_MAX; + + /* Configure TAQ entries */ + reg = rvu_read64(rvu, blkaddr, SSO_AF_TAQ_CNT); + taq_free_cnt = reg & SSO_AF_TAQ_FREE_CNT_MASK; + + /* Give out half of buffers fairly, rest left floating */ + taq_rsvd = taq_free_cnt / sso->sso_hwgrps / 2; + + /* Enforce minimum per hardware requirements */ + if (taq_rsvd < SSO_HWGRP_TAQ_RSVD_THR) + taq_rsvd = SSO_HWGRP_TAQ_RSVD_THR; + /* To ensure full streaming performance should be at least 16. */ + taq_max = taq_rsvd + SSO_HWGRP_TAQ_MAX_THR_STRM_PERF; + + if (taq_max >= (SSO_AF_TAQ_FREE_CNT_MAX + 1)) + taq_max = SSO_AF_TAQ_FREE_CNT_MAX; + + /* Save thresholds to reprogram HWGRPs on reset */ + sso->iaq_rsvd = iaq_rsvd; + sso->iaq_max = iaq_max; + sso->taq_rsvd = taq_rsvd; + sso->taq_max = taq_max; + + for (hwgrp = 0; hwgrp < sso->sso_hwgrps; hwgrp++) { + rvu_sso_hwgrp_config_thresh(rvu, blkaddr, hwgrp); + iaq_rsvd_cnt += iaq_rsvd; + taq_rsvd_cnt += taq_rsvd; + } + + /* Verify SSO_AW_WE[RSVD_FREE], TAQ_CNT[RSVD_FREE] are greater than + * or equal to sum of IAQ[RSVD_THR], TAQ[RSRVD_THR] fields. + */ + reg = rvu_read64(rvu, blkaddr, SSO_AF_AW_WE); + reg = (reg >> SSO_AF_IAQ_RSVD_FREE_SHIFT) & SSO_AF_IAQ_RSVD_FREE_MASK; + if (reg < iaq_rsvd_cnt) { + dev_warn(rvu->dev, "WARN: Wrong IAQ resource calculations %llx vs %llx\n", + reg, iaq_rsvd_cnt); + rvu_write64(rvu, blkaddr, SSO_AF_AW_WE, + (iaq_rsvd_cnt & SSO_AF_IAQ_RSVD_FREE_MASK) << + SSO_AF_IAQ_RSVD_FREE_SHIFT); + } + + reg = rvu_read64(rvu, blkaddr, SSO_AF_TAQ_CNT); + reg = (reg >> SSO_AF_TAQ_RSVD_FREE_SHIFT) & SSO_AF_TAQ_RSVD_FREE_MASK; + if (reg < taq_rsvd_cnt) { + dev_warn(rvu->dev, "WARN: Wrong TAQ resource calculations %llx vs %llx\n", + reg, taq_rsvd_cnt); + rvu_write64(rvu, blkaddr, SSO_AF_TAQ_CNT, + (taq_rsvd_cnt & SSO_AF_TAQ_RSVD_FREE_MASK) << + SSO_AF_TAQ_RSVD_FREE_SHIFT); + } + + /* Unset the HWS Hardware Group Mask. + * The hardware group mask should be set by PF/VF + * using SSOW_LF_GWS_GRPMSK_CHG based on the LF allocations. + */ + for (grpmsk = 0; grpmsk < (sso->sso_hwgrps / 64); grpmsk++) { + for (hws = 0; hws < sso->sso_hws; hws++) { + rvu_write64(rvu, blkaddr, + SSO_AF_HWSX_SX_GRPMSKX(hws, 0, grpmsk), + 0x0); + rvu_write64(rvu, blkaddr, + SSO_AF_HWSX_SX_GRPMSKX(hws, 1, grpmsk), + 0x0); + } + } + + /* Allocate SSO_AF_CONST::HWS + 1. As the total number of pf/vf are + * limited by the numeber of HWS available. + */ + sso->pfvf_ident.max = sso->sso_hws + 1; + err = rvu_alloc_bitmap(&sso->pfvf_ident); + if (err) + return err; + + /* Reserve one bit so that identifier starts from 1 */ + rvu_alloc_rsrc(&sso->pfvf_ident); + + return 0; +} + +void rvu_sso_freemem(struct rvu *rvu) +{ + struct sso_rsrc *sso = &rvu->hw->sso; + + kfree(sso->pfvf_ident.bmap); +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h index f920dac74e6c..445dfc328a8d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h @@ -11,24 +11,27 @@ #ifndef RVU_STRUCT_H #define RVU_STRUCT_H +/* RVU Block revision IDs */ +#define RVU_BLK_RVUM_REVID 0x01 + /* RVU Block Address Enumeration */ enum rvu_block_addr_e { - BLKADDR_RVUM = 0x0ULL, - BLKADDR_LMT = 0x1ULL, - BLKADDR_MSIX = 0x2ULL, - BLKADDR_NPA = 0x3ULL, - BLKADDR_NIX0 = 0x4ULL, - BLKADDR_NIX1 = 0x5ULL, - BLKADDR_NPC = 0x6ULL, - BLKADDR_SSO = 0x7ULL, - BLKADDR_SSOW = 0x8ULL, - BLKADDR_TIM = 0x9ULL, - BLKADDR_CPT0 = 0xaULL, - BLKADDR_CPT1 = 0xbULL, - BLKADDR_NDC0 = 0xcULL, - BLKADDR_NDC1 = 0xdULL, - BLKADDR_NDC2 = 0xeULL, - BLK_COUNT = 0xfULL, + BLKADDR_RVUM = 0x0ULL, + BLKADDR_LMT = 0x1ULL, + BLKADDR_MSIX = 0x2ULL, + BLKADDR_NPA = 0x3ULL, + BLKADDR_NIX0 = 0x4ULL, + BLKADDR_NIX1 = 0x5ULL, + BLKADDR_NPC = 0x6ULL, + BLKADDR_SSO = 0x7ULL, + BLKADDR_SSOW = 0x8ULL, + BLKADDR_TIM = 0x9ULL, + BLKADDR_CPT0 = 0xaULL, + BLKADDR_CPT1 = 0xbULL, + BLKADDR_NDC_NIX0_RX = 0xcULL, + BLKADDR_NDC_NIX0_TX = 0xdULL, + BLKADDR_NDC_NPA0 = 0xeULL, + BLK_COUNT = 0xfULL, }; /* RVU Block Type Enumeration */ @@ -57,6 +60,43 @@ enum rvu_af_int_vec_e { RVU_AF_INT_VEC_CNT = 0x5, }; +/* NPA Admin function Interrupt Vector Enumeration */ +enum npa_af_int_vec_e { + NPA_AF_INT_VEC_RVU = 0x0, + NPA_AF_INT_VEC_GEN = 0x1, + NPA_AF_INT_VEC_AQ_DONE = 0x2, + NPA_AF_INT_VEC_AF_ERR = 0x3, + NPA_AF_INT_VEC_POISON = 0x4, + NPA_AF_INT_VEC_CNT = 0x5, +}; + +/* NIX Admin function Interrupt Vector Enumeration */ +enum nix_af_int_vec_e { + NIX_AF_INT_VEC_RVU = 0x0, + NIX_AF_INT_VEC_GEN = 0x1, + NIX_AF_INT_VEC_AQ_DONE = 0x2, + NIX_AF_INT_VEC_AF_ERR = 0x3, + NIX_AF_INT_VEC_POISON = 0x4, + NIX_AF_INT_VEC_CNT = 0x5, +}; + +/* SSO Admin function Interrupt Vector Enumeration */ +enum sso_af_int_vec_e { + SSO_AF_INT_VEC_ERR0 = 0x0, + SSO_AF_INT_VEC_ERR2 = 0x1, + SSO_AF_INT_VEC_RAS = 0x2, + SSO_AF_INT_VEC_CNT = 0x3, +}; + +/* CPT Admin function Interrupt Vector Enumeration */ +enum cpt_af_int_vec_e { + CPT_AF_INT_VEC_FLT0 = 0x0, + CPT_AF_INT_VEC_FLT1 = 0x1, + CPT_AF_INT_VEC_RVU = 0x2, + CPT_AF_INT_VEC_RAS = 0x3, + CPT_AF_INT_VEC_CNT = 0x4, +}; + /** * RVU PF Interrupt Vector Enumeration */ @@ -97,6 +137,19 @@ enum npa_aq_instop { NPA_AQ_INSTOP_UNLOCK = 0x5, }; +/* ALLOC/FREE input queues Enumeration from coprocessors */ +enum npa_inpq { + NPA_INPQ_NIX0_RX = 0x0, + NPA_INPQ_NIX0_TX = 0x1, + NPA_INPQ_NIX1_RX = 0x2, + NPA_INPQ_NIX1_TX = 0x3, + NPA_INPQ_SSO = 0x4, + NPA_INPQ_TIM = 0x5, + NPA_INPQ_DPI = 0x6, + NPA_INPQ_AURA_OP = 0xe, + NPA_INPQ_INTERNAL_RSV = 0xf, +}; + /* NPA admin queue instruction structure */ struct npa_aq_inst_s { #if defined(__BIG_ENDIAN_BITFIELD) @@ -474,9 +527,9 @@ struct nix_cq_ctx_s { u64 ena : 1; u64 drop_ena : 1; u64 drop : 8; - u64 dp : 8; + u64 bp : 8; #else - u64 dp : 8; + u64 bp : 8; u64 drop : 8; u64 drop_ena : 1; u64 ena : 1; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_tim.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_tim.c new file mode 100644 index 000000000000..e23c036eb023 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_tim.c @@ -0,0 +1,326 @@ +//SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Admin Function driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/interrupt.h> +#include <linux/irq.h> +#include <linux/types.h> + +#include "rvu_struct.h" +#include "rvu_reg.h" +#include "rvu.h" + +#define TIM_CHUNKSIZE_MULTIPLE (16) +#define TIM_CHUNKSIZE_MIN (TIM_CHUNKSIZE_MULTIPLE * 0x2) +#define TIM_CHUNKSIZE_MAX (TIM_CHUNKSIZE_MULTIPLE * 0x1FFF) + +static inline u64 get_tenns_tsc(void) +{ + u64 tsc; + +#if defined(CONFIG_ARM64) + asm volatile("mrs %0, cntvct_el0" : "=r" (tsc)); +#endif + return tsc; +} + +static inline u64 get_tenns_clk(void) +{ + u64 tsc; + +#if defined(CONFIG_ARM64) + asm volatile("mrs %0, cntfrq_el0" : "=r" (tsc)); +#endif + return tsc; +} + +static int rvu_tim_disable_lf(struct rvu *rvu, int lf, int blkaddr) +{ + u64 regval; + + regval = rvu_read64(rvu, blkaddr, TIM_AF_RINGX_CTL1(lf)); + if ((regval & TIM_AF_RINGX_CTL1_ENA) == 0) + return TIM_AF_RING_ALREADY_DISABLED; + + /* Clear TIM_AF_RING(0..255)_CTL1[ENA]. */ + regval = rvu_read64(rvu, blkaddr, TIM_AF_RINGX_CTL1(lf)); + regval &= ~TIM_AF_RINGX_CTL1_ENA; + rvu_write64(rvu, blkaddr, TIM_AF_RINGX_CTL1(lf), regval); + + /* + * Poll until the corresponding ring’s + * TIM_AF_RING(0..255)_CTL1[RCF_BUSY] is clear. + */ + rvu_poll_reg(rvu, blkaddr, TIM_AF_RINGX_CTL1(lf), + TIM_AF_RINGX_CTL1_RCF_BUSY, true); + return 0; +} + +int rvu_mbox_handler_tim_lf_alloc(struct rvu *rvu, + struct tim_lf_alloc_req *req, + struct tim_lf_alloc_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + int lf, blkaddr; + u64 regval; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_TIM, pcifunc); + if (blkaddr < 0) + return TIM_AF_LF_INVALID; + + lf = rvu_get_lf(rvu, &rvu->hw->block[blkaddr], pcifunc, req->ring); + if (lf < 0) + return TIM_AF_LF_INVALID; + + regval = (((u64)req->npa_pf_func) << 16) | + ((u64)req->sso_pf_func); + rvu_write64(rvu, blkaddr, TIM_AF_RINGX_GMCTL(lf), regval); + + rsp->tenns_clk = get_tenns_clk(); + + return 0; +} + +int rvu_mbox_handler_tim_lf_free(struct rvu *rvu, + struct tim_ring_req *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + int lf, blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_TIM, pcifunc); + if (blkaddr < 0) + return TIM_AF_LF_INVALID; + + lf = rvu_get_lf(rvu, &rvu->hw->block[blkaddr], pcifunc, req->ring); + if (lf < 0) + return TIM_AF_LF_INVALID; + + rvu_tim_lf_teardown(rvu, pcifunc, lf, req->ring); + + return 0; +} + +int rvu_mbox_handler_tim_config_ring(struct rvu *rvu, + struct tim_config_req *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + int lf, blkaddr; + u32 intervalmin; + u64 regval; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_TIM, pcifunc); + if (blkaddr < 0) + return TIM_AF_LF_INVALID; + + lf = rvu_get_lf(rvu, &rvu->hw->block[blkaddr], pcifunc, req->ring); + if (lf < 0) + return TIM_AF_LF_INVALID; + + /* Check the inputs. */ + /* bigendian can only be 1 or 0. */ + if (req->bigendian & ~1) + return TIM_AF_INVALID_BIG_ENDIAN_VALUE; + + /* Check GPIO clock source has the GPIO edge set. */ + if (req->clocksource == TIM_CLK_SRCS_GPIO) { + regval = rvu_read64(rvu, blkaddr, TIM_AF_FLAGS_REG); + if (((regval >> 5) & 0x3) == 0) + return TIM_AF_GPIO_CLK_SRC_NOT_ENABLED; + } + + /* enableperiodic can only be 1 or 0. */ + if (req->enableperiodic & ~1) + return TIM_AF_INVALID_ENABLE_PERIODIC; + + /* enabledontfreebuffer can only be 1 or 0. */ + if (req->enabledontfreebuffer & ~1) + return TIM_AF_INVALID_ENABLE_DONTFREE; + + /* + * enabledontfreebuffer needs to be true if enableperiodic + * is enabled. + */ + if (req->enableperiodic && !req->enabledontfreebuffer) + return TIM_AF_ENA_DONTFRE_NSET_PERIODIC; + + + /* bucketsize needs to between 2 and 2M (1<<20). */ + if (req->bucketsize < 2 || req->bucketsize > 1<<20) + return TIM_AF_INVALID_BSIZE; + + if (req->chunksize % TIM_CHUNKSIZE_MULTIPLE) + return TIM_AF_CSIZE_NOT_ALIGNED; + + if (req->chunksize < TIM_CHUNKSIZE_MIN) + return TIM_AF_CSIZE_TOO_SMALL; + + if (req->chunksize > TIM_CHUNKSIZE_MAX) + return TIM_AF_CSIZE_TOO_BIG; + + switch (req->clocksource) { + case TIM_CLK_SRCS_TENNS: + intervalmin = 256; + break; + case TIM_CLK_SRCS_GPIO: + intervalmin = 256; + break; + case TIM_CLK_SRCS_GTI: + case TIM_CLK_SRCS_PTP: + intervalmin = 300; + break; + default: + return TIM_AF_INVALID_CLOCK_SOURCE; + } + + if (req->interval < intervalmin) + return TIM_AF_INTERVAL_TOO_SMALL; + + /* CTL0 */ + /* EXPIRE_OFFSET = 0 and is set correctly when enabling. */ + regval = req->interval; + rvu_write64(rvu, blkaddr, TIM_AF_RINGX_CTL0(lf), regval); + + /* CTL1 */ + regval = (((u64)req->bigendian) << 53) | + (((u64)req->clocksource) << 51) | + (1ull << 48) | /* LOCK_EN */ + (((u64)req->enableperiodic) << 45) | + (((u64)(req->enableperiodic ^ 1)) << 44) | /* ENA_LDWB */ + (((u64)req->enabledontfreebuffer) << 43) | + (u64)(req->bucketsize - 1); + rvu_write64(rvu, blkaddr, TIM_AF_RINGX_CTL1(lf), regval); + + /* CTL2 */ + regval = ((u64)req->chunksize / TIM_CHUNKSIZE_MULTIPLE) << 40; + rvu_write64(rvu, blkaddr, TIM_AF_RINGX_CTL2(lf), regval); + + return 0; +} + +int rvu_mbox_handler_tim_enable_ring(struct rvu *rvu, + struct tim_ring_req *req, + struct tim_enable_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + int lf, blkaddr; + u64 regval; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_TIM, pcifunc); + if (blkaddr < 0) + return TIM_AF_LF_INVALID; + + lf = rvu_get_lf(rvu, &rvu->hw->block[blkaddr], pcifunc, req->ring); + if (lf < 0) + return TIM_AF_LF_INVALID; + + /* Error out if the ring is already running. */ + regval = rvu_read64(rvu, blkaddr, TIM_AF_RINGX_CTL1(lf)); + if (regval & TIM_AF_RINGX_CTL1_ENA) + return TIM_AF_RING_STILL_RUNNING; + + /* Enable, the ring. */ + regval = rvu_read64(rvu, blkaddr, TIM_AF_RINGX_CTL1(lf)); + regval |= TIM_AF_RINGX_CTL1_ENA; + rvu_write64(rvu, blkaddr, TIM_AF_RINGX_CTL1(lf), regval); + + rsp->timestarted = get_tenns_tsc(); + rsp->currentbucket = (regval >> 20) & 0xfffff; + + return 0; +} + +int rvu_mbox_handler_tim_disable_ring(struct rvu *rvu, + struct tim_ring_req *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + int lf, blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_TIM, pcifunc); + if (blkaddr < 0) + return TIM_AF_LF_INVALID; + + lf = rvu_get_lf(rvu, &rvu->hw->block[blkaddr], pcifunc, req->ring); + if (lf < 0) + return TIM_AF_LF_INVALID; + + return rvu_tim_disable_lf(rvu, lf, blkaddr); +} + +int rvu_tim_lf_teardown(struct rvu *rvu, u16 pcifunc, int lf, int slot) +{ + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_TIM, pcifunc); + if (blkaddr < 0) + return TIM_AF_LF_INVALID; + + /* Ensure TIM ring is disabled prior to clearing the mapping */ + rvu_tim_disable_lf(rvu, lf, blkaddr); + + rvu_write64(rvu, blkaddr, TIM_AF_RINGX_GMCTL(lf), 0); + + return 0; +} + +#define FOR_EACH_TIM_LF(lf) \ +for (lf = 0; lf < hw->block[BLKTYPE_TIM].lf.max; lf++) + +int rvu_tim_init(struct rvu *rvu) +{ + struct rvu_hwinfo *hw = rvu->hw; + int lf, blkaddr; + u8 gpio_edge; + u64 regval; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_TIM, 0); + if (blkaddr < 0) + return 0; + + regval = rvu_read64(rvu, blkaddr, TIM_AF_FLAGS_REG); + + /* Disable the TIM block, if not already disabled. */ + if (regval & TIM_AF_FLAGS_REG_ENA_TIM) { + /* Disable each ring(lf). */ + FOR_EACH_TIM_LF(lf) { + regval = rvu_read64(rvu, blkaddr, + TIM_AF_RINGX_CTL1(lf)); + if (!(regval & TIM_AF_RINGX_CTL1_ENA)) + continue; + + rvu_tim_disable_lf(rvu, lf, blkaddr); + } + + /* Disable the TIM block. */ + regval = rvu_read64(rvu, blkaddr, TIM_AF_FLAGS_REG); + regval &= ~TIM_AF_FLAGS_REG_ENA_TIM; + rvu_write64(rvu, blkaddr, TIM_AF_FLAGS_REG, regval); + } + + /* Reset each LF. */ + FOR_EACH_TIM_LF(lf) { + rvu_lf_reset(rvu, &hw->block[BLKTYPE_TIM], lf); + } + + /* Reset the TIM block; getting a clean slate. */ + rvu_write64(rvu, blkaddr, TIM_AF_BLK_RST, 0x1); + rvu_poll_reg(rvu, blkaddr, TIM_AF_BLK_RST, BIT_ULL(63), true); + + gpio_edge = TIM_GPIO_NO_EDGE; + + /* Enable TIM block. */ + regval = (((u64)gpio_edge) << 6) | + BIT_ULL(2) | /* RESET */ + BIT_ULL(0); /* ENA_TIM */ + rvu_write64(rvu, blkaddr, TIM_AF_FLAGS_REG, regval); + + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_validation.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_validation.c new file mode 100644 index 000000000000..33a7821456d1 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_validation.c @@ -0,0 +1,826 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Admin Function driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/types.h> +#include <linux/pci.h> +#include "rvu.h" + +#define PCI_DEVID_OCTEONTX2_RVU_PF 0xA063 +#define PCI_DEVID_OCTEONTX2_SSO_RVU_PF 0xA0F9 +#define PCI_DEVID_OCTEONTX2_NPA_RVU_PF 0xA0FB +#define PCI_DEVID_OCTEONTX2_CPT_RVU_PF 0xA0FD +#define PCI_DEVID_OCTEONTX2_SDP_RVU_PF 0xA0F6 + +static u64 quotas_get_sum(struct rvu_quotas *quotas) +{ + u64 lf_sum = 0; + int i; + + for (i = 0; i < quotas->cnt; i++) + lf_sum += quotas->a[i].val; + + return lf_sum; +} + +static ssize_t quota_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct rvu_quota *quota; + int val; + + quota = container_of(attr, struct rvu_quota, sysfs); + + if (quota->base->lock) + mutex_lock(quota->base->lock); + val = quota->val; + if (quota->base->lock) + mutex_unlock(quota->base->lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t quota_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int old_val, new_val, res = 0; + struct rvu_quota *quota; + struct rvu_quotas *base; + struct device *dev; + u64 lf_sum; + + quota = container_of(attr, struct rvu_quota, sysfs); + dev = quota->dev; + base = quota->base; + + if (kstrtoint(buf, 0, &new_val)) { + dev_err(dev, "Invalid %s quota: %s\n", attr->attr.name, buf); + return -EIO; + } + if (new_val < 0) { + dev_err(dev, "Invalid %s quota: %d < 0\n", attr->attr.name, + new_val); + return -EIO; + } + + if (new_val > base->max) { + dev_err(dev, "Invalid %s quota: %d > %d\n", attr->attr.name, + new_val, base->max); + return -EIO; + } + + if (base->lock) + mutex_lock(base->lock); + old_val = quota->val; + + if (base->ops.pre_store) + res = base->ops.pre_store(quota->ops_arg, quota, new_val); + + if (res != 0) { + res = -EIO; + goto unlock; + } + + lf_sum = quotas_get_sum(quota->base); + + if (lf_sum + new_val - quota->val > base->max_sum) { + dev_err(dev, + "Not enough resources for %s quota. Used: %lld, Max: %lld\n", + attr->attr.name, lf_sum, base->max_sum); + res = -EIO; + goto unlock; + } + quota->val = new_val; + + if (base->ops.post_store) + base->ops.post_store(quota->ops_arg, quota, old_val); + + res = count; + +unlock: + if (base->lock) + mutex_unlock(base->lock); + return res; +} + +static int quota_sysfs_destroy(struct rvu_quota *quota) +{ + if (quota == NULL) + return -EINVAL; + if (quota->sysfs.attr.mode != 0) { + sysfs_remove_file(quota->parent, "a->sysfs.attr); + quota->sysfs.attr.mode = 0; + } + return 0; +} + +static struct rvu_quotas *quotas_alloc(u32 cnt, u32 max, u64 max_sum, + int init_val, struct mutex *lock, + struct rvu_quota_ops *ops) +{ + struct rvu_quotas *quotas; + u64 i; + + if (cnt == 0) + return NULL; + + quotas = kzalloc(sizeof(struct rvu_quotas) + + cnt * sizeof(struct rvu_quota), GFP_KERNEL); + if (quotas == NULL) + return NULL; + + for (i = 0; i < cnt; i++) { + quotas->a[i].base = quotas; + quotas->a[i].val = init_val; + } + + quotas->cnt = cnt; + quotas->max = max; + quotas->max_sum = max_sum; + if (ops) { + quotas->ops.pre_store = ops->pre_store; + quotas->ops.post_store = ops->post_store; + } + quotas->lock = lock; + + return quotas; +} + +static void quotas_free(struct rvu_quotas *quotas) +{ + u64 i; + + if (quotas == NULL) + return; + WARN_ON(quotas->cnt == 0); + + for (i = 0; i < quotas->cnt; i++) + quota_sysfs_destroy("as->a[i]); + + kfree(quotas); +} + +static int quota_sysfs_create(const char *name, struct kobject *parent, + struct device *log_dev, struct rvu_quota *quota, + void *ops_arg) +{ + int err; + + if (name == NULL || quota == NULL || log_dev == NULL) + return -EINVAL; + + quota->sysfs.show = quota_show; + quota->sysfs.store = quota_store; + quota->sysfs.attr.name = name; + quota->sysfs.attr.mode = 0644; + quota->parent = parent; + quota->dev = log_dev; + quota->ops_arg = ops_arg; + + sysfs_attr_init("a->sysfs.attr); + err = sysfs_create_file(quota->parent, "a->sysfs.attr); + if (err) { + dev_err(quota->dev, + "Failed to create '%s' quota sysfs for '%s'\n", + name, kobject_name(quota->parent)); + return -EFAULT; + } + + return 0; +} + +static int rvu_blk_count_rsrc(struct rvu_block *block, u16 pcifunc, u8 rshift) +{ + int count = 0, lf; + + for (lf = 0; lf < block->lf.max; lf++) + if ((block->fn_map[lf] >> rshift) == (pcifunc >> rshift)) + count++; + + return count; +} + +static int rvu_txsch_count_rsrc(struct rvu *rvu, int lvl, u16 pcifunc, + u8 rshift) +{ + struct nix_txsch *txsch = &rvu->hw->nix0->txsch[lvl]; + int count = 0, schq; + + if (lvl == NIX_TXSCH_LVL_TL1) + return 0; + + for (schq = 0; schq < txsch->schq.max; schq++) { + if (TXSCH_MAP_FLAGS(txsch->pfvf_map[schq]) & NIX_TXSCHQ_FREE) + continue; + if ((TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) >> rshift) == + (pcifunc >> rshift)) + count++; + } + + return count; +} + +int rvu_mbox_handler_free_rsrc_cnt(struct rvu *rvu, struct msg_req *req, + struct free_rsrcs_rsp *rsp) +{ + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + struct rvu_block *block; + int pf, curlfs; + + mutex_lock(&rvu->rsrc_lock); + pf = rvu_get_pf(pcifunc); + + block = &hw->block[BLKADDR_NPA]; + curlfs = rvu_blk_count_rsrc(block, pcifunc, RVU_PFVF_PF_SHIFT); + rsp->npa = rvu->pf_limits.npa->a[pf].val - curlfs; + + block = &hw->block[BLKADDR_NIX0]; + curlfs = rvu_blk_count_rsrc(block, pcifunc, RVU_PFVF_PF_SHIFT); + rsp->nix = rvu->pf_limits.nix->a[pf].val - curlfs; + + block = &hw->block[BLKADDR_SSO]; + curlfs = rvu_blk_count_rsrc(block, pcifunc, RVU_PFVF_PF_SHIFT); + rsp->sso = rvu->pf_limits.sso->a[pf].val - curlfs; + + block = &hw->block[BLKADDR_SSOW]; + curlfs = rvu_blk_count_rsrc(block, pcifunc, RVU_PFVF_PF_SHIFT); + rsp->ssow = rvu->pf_limits.ssow->a[pf].val - curlfs; + + block = &hw->block[BLKADDR_TIM]; + curlfs = rvu_blk_count_rsrc(block, pcifunc, RVU_PFVF_PF_SHIFT); + rsp->tim = rvu->pf_limits.tim->a[pf].val - curlfs; + + block = &hw->block[BLKADDR_CPT0]; + curlfs = rvu_blk_count_rsrc(block, pcifunc, RVU_PFVF_PF_SHIFT); + rsp->cpt = rvu->pf_limits.cpt->a[pf].val - curlfs; + + if (rvu->hw->cap.nix_fixed_txschq_mapping) { + rsp->schq[NIX_TXSCH_LVL_SMQ] = 1; + rsp->schq[NIX_TXSCH_LVL_TL4] = 1; + rsp->schq[NIX_TXSCH_LVL_TL3] = 1; + rsp->schq[NIX_TXSCH_LVL_TL2] = 1; + } else { + curlfs = rvu_txsch_count_rsrc(rvu, NIX_TXSCH_LVL_SMQ, pcifunc, + RVU_PFVF_PF_SHIFT); + rsp->schq[NIX_TXSCH_LVL_SMQ] = + rvu->pf_limits.smq->a[pf].val - curlfs; + + curlfs = rvu_txsch_count_rsrc(rvu, NIX_TXSCH_LVL_TL4, pcifunc, + RVU_PFVF_PF_SHIFT); + rsp->schq[NIX_TXSCH_LVL_TL4] = + rvu->pf_limits.tl4->a[pf].val - curlfs; + + curlfs = rvu_txsch_count_rsrc(rvu, NIX_TXSCH_LVL_TL3, pcifunc, + RVU_PFVF_PF_SHIFT); + rsp->schq[NIX_TXSCH_LVL_TL3] = + rvu->pf_limits.tl3->a[pf].val - curlfs; + + curlfs = rvu_txsch_count_rsrc(rvu, NIX_TXSCH_LVL_TL2, pcifunc, + RVU_PFVF_PF_SHIFT); + rsp->schq[NIX_TXSCH_LVL_TL2] = + rvu->pf_limits.tl2->a[pf].val - curlfs; + } + + rsp->schq[NIX_TXSCH_LVL_TL1] = 1; + + mutex_unlock(&rvu->rsrc_lock); + + return 0; +} + +int rvu_check_txsch_policy(struct rvu *rvu, struct nix_txsch_alloc_req *req, + u16 pcifunc) +{ + struct nix_txsch *txsch; + int lvl, req_schq, pf = rvu_get_pf(pcifunc); + int limit, familylfs, delta; + + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + txsch = &rvu->hw->nix0->txsch[lvl]; + req_schq = req->schq_contig[lvl] + req->schq[lvl]; + + switch (lvl) { + case NIX_TXSCH_LVL_SMQ: + limit = rvu->pf_limits.smq->a[pf].val; + break; + case NIX_TXSCH_LVL_TL4: + limit = rvu->pf_limits.tl4->a[pf].val; + break; + case NIX_TXSCH_LVL_TL3: + limit = rvu->pf_limits.tl3->a[pf].val; + break; + case NIX_TXSCH_LVL_TL2: + limit = rvu->pf_limits.tl2->a[pf].val; + break; + case NIX_TXSCH_LVL_TL1: + if (req_schq > 2) + return -ENOSPC; + continue; + } + + familylfs = rvu_txsch_count_rsrc(rvu, lvl, pcifunc, + RVU_PFVF_PF_SHIFT); + delta = req_schq - rvu_txsch_count_rsrc(rvu, lvl, pcifunc, 0); + + if ((delta > 0) && /* always allow usage decrease */ + ((limit < familylfs + delta) || + (delta > rvu_rsrc_free_count(&txsch->schq)))) + return -ENOSPC; + } + + return 0; +} + +int rvu_check_rsrc_policy(struct rvu *rvu, struct rsrc_attach *req, + u16 pcifunc) +{ + struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc); + int free_lfs, mappedlfs, familylfs, limit, delta; + struct rvu_hwinfo *hw = rvu->hw; + int pf = rvu_get_pf(pcifunc); + struct rvu_block *block; + + /* Only one NPA LF can be attached */ + if (req->npalf) { + block = &hw->block[BLKADDR_NPA]; + free_lfs = rvu_rsrc_free_count(&block->lf); + limit = rvu->pf_limits.npa->a[pf].val; + familylfs = rvu_blk_count_rsrc(block, pcifunc, + RVU_PFVF_PF_SHIFT); + if (!free_lfs || (limit == familylfs)) + goto fail; + } + + /* Only one NIX LF can be attached */ + if (req->nixlf) { + block = &hw->block[BLKADDR_NIX0]; + free_lfs = rvu_rsrc_free_count(&block->lf); + limit = rvu->pf_limits.nix->a[pf].val; + familylfs = rvu_blk_count_rsrc(block, pcifunc, + RVU_PFVF_PF_SHIFT); + if (!free_lfs || (limit == familylfs)) + goto fail; + } + + if (req->sso) { + block = &hw->block[BLKADDR_SSO]; + mappedlfs = rvu_get_rsrc_mapcount(pfvf, block->type); + free_lfs = rvu_rsrc_free_count(&block->lf); + limit = rvu->pf_limits.sso->a[pf].val; + familylfs = rvu_blk_count_rsrc(block, pcifunc, + RVU_PFVF_PF_SHIFT); + /* Check if additional resources are available */ + delta = req->sso - mappedlfs; + if ((delta > 0) && /* always allow usage decrease */ + ((limit < familylfs + delta) || + (delta > free_lfs))) + goto fail; + } + + if (req->ssow) { + block = &hw->block[BLKADDR_SSOW]; + mappedlfs = rvu_get_rsrc_mapcount(pfvf, block->type); + free_lfs = rvu_rsrc_free_count(&block->lf); + limit = rvu->pf_limits.ssow->a[pf].val; + familylfs = rvu_blk_count_rsrc(block, pcifunc, + RVU_PFVF_PF_SHIFT); + /* Check if additional resources are available */ + delta = req->ssow - mappedlfs; + if ((delta > 0) && /* always allow usage decrease */ + ((limit < familylfs + delta) || + (delta > free_lfs))) + goto fail; + } + + if (req->timlfs) { + block = &hw->block[BLKADDR_TIM]; + mappedlfs = rvu_get_rsrc_mapcount(pfvf, block->type); + free_lfs = rvu_rsrc_free_count(&block->lf); + limit = rvu->pf_limits.tim->a[pf].val; + familylfs = rvu_blk_count_rsrc(block, pcifunc, + RVU_PFVF_PF_SHIFT); + /* Check if additional resources are available */ + delta = req->timlfs - mappedlfs; + if ((delta > 0) && /* always allow usage decrease */ + ((limit < familylfs + delta) || + (delta > free_lfs))) + goto fail; + } + + if (req->cptlfs) { + block = &hw->block[BLKADDR_CPT0]; + mappedlfs = rvu_get_rsrc_mapcount(pfvf, block->type); + free_lfs = rvu_rsrc_free_count(&block->lf); + limit = rvu->pf_limits.cpt->a[pf].val; + familylfs = rvu_blk_count_rsrc(block, pcifunc, + RVU_PFVF_PF_SHIFT); + /* Check if additional resources are available */ + delta = req->cptlfs - mappedlfs; + if ((delta > 0) && /* always allow usage decrease */ + ((limit < familylfs + delta) || + (delta > free_lfs))) + goto fail; + } + + return 0; + +fail: + dev_info(rvu->dev, "Request for %s failed\n", block->name); + return -ENOSPC; +} + +static int check_mapped_rsrcs(void *arg, struct rvu_quota *quota, int new_val) +{ + struct rvu_pfvf *pf = arg; + int type; + + for (type = 0; type < BLKTYPE_MAX; type++) { + if (rvu_get_rsrc_mapcount(pf, type) > 0) + return 1; + } + return 0; +} + +static struct rvu_quota_ops pf_limit_ops = { + .pre_store = check_mapped_rsrcs, +}; + +static void rvu_set_default_limits(struct rvu *rvu) +{ + struct nix_hw *nix_hw = rvu->hw->nix0; + int i, sso_rvus = 0, nix_rvus = 0, totalvfs; + + /* First pass, count number of SSO/TIM PFs. */ + for (i = 0; i < rvu->hw->total_pfs; i++) { + if (rvu->pf[i].pdev == NULL) + continue; + if (rvu->pf[i].pdev->device == PCI_DEVID_OCTEONTX2_SSO_RVU_PF) + sso_rvus++; + if (rvu->pf[i].pdev->device == PCI_DEVID_OCTEONTX2_RVU_PF || + rvu->pf[i].pdev->device == PCI_DEVID_OCTEONTX2_RVU_AF || + rvu->pf[i].pdev->device == PCI_DEVID_OCTEONTX2_SDP_RVU_PF) + nix_rvus++; + } + + /* Second pass, set the default limit values. */ + for (i = 0; i < rvu->hw->total_pfs; i++) { + if (rvu->pf[i].pdev == NULL) + continue; + totalvfs = pci_sriov_get_totalvfs(rvu->pf[i].pdev); + switch (rvu->pf[i].pdev->device) { + case PCI_DEVID_OCTEONTX2_RVU_AF: + rvu->pf_limits.nix->a[i].val = totalvfs; + rvu->pf_limits.npa->a[i].val = totalvfs; + if (rvu->hw->cap.nix_fixed_txschq_mapping) + break; + rvu->pf_limits.smq->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_SMQ].schq.max / + nix_rvus; + rvu->pf_limits.tl4->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_TL4].schq.max / + nix_rvus; + rvu->pf_limits.tl3->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_TL3].schq.max / + nix_rvus; + rvu->pf_limits.tl2->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_TL2].schq.max / + nix_rvus; + break; + case PCI_DEVID_OCTEONTX2_RVU_PF: + rvu->pf_limits.nix->a[i].val = 1 + totalvfs; + rvu->pf_limits.npa->a[i].val = 1 + totalvfs; + if (rvu->hw->cap.nix_fixed_txschq_mapping) + break; + rvu->pf_limits.smq->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_SMQ].schq.max / + nix_rvus; + rvu->pf_limits.tl4->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_TL4].schq.max / + nix_rvus; + rvu->pf_limits.tl3->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_TL3].schq.max / + nix_rvus; + rvu->pf_limits.tl2->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_TL2].schq.max / + nix_rvus; + break; + case PCI_DEVID_OCTEONTX2_SSO_RVU_PF: + rvu->pf_limits.npa->a[i].val = 1 + totalvfs; + rvu->pf_limits.sso->a[i].val = + rvu->hw->block[BLKADDR_SSO].lf.max / sso_rvus; + rvu->pf_limits.ssow->a[i].val = + rvu->hw->block[BLKADDR_SSOW].lf.max / sso_rvus; + rvu->pf_limits.tim->a[i].val = + rvu->hw->block[BLKADDR_TIM].lf.max / sso_rvus; + /* All users of CPT should not share CPUs so if there + * are multiple SSO/TIM PFs, then divide CPTs equally. + */ + rvu->pf_limits.cpt->a[i].val = + num_online_cpus() / sso_rvus; + break; + case PCI_DEVID_OCTEONTX2_NPA_RVU_PF: + rvu->pf_limits.npa->a[i].val = 1 + totalvfs; + break; + case PCI_DEVID_OCTEONTX2_CPT_RVU_PF: + rvu->pf_limits.cpt->a[i].val = num_online_cpus(); + rvu->pf_limits.npa->a[i].val = 1; + break; + case PCI_DEVID_OCTEONTX2_SDP_RVU_PF: + rvu->pf_limits.nix->a[i].val = 1 + totalvfs; + rvu->pf_limits.npa->a[i].val = 1 + totalvfs; + if (rvu->hw->cap.nix_fixed_txschq_mapping) + break; + rvu->pf_limits.smq->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_SMQ].schq.max / + nix_rvus; + rvu->pf_limits.tl4->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_TL4].schq.max / + nix_rvus; + rvu->pf_limits.tl3->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_TL3].schq.max / + nix_rvus; + rvu->pf_limits.tl2->a[i].val = + nix_hw->txsch[NIX_TXSCH_LVL_TL2].schq.max / + nix_rvus; + break; + } + } +} + +static int rvu_create_limits_sysfs(struct rvu *rvu) +{ + struct pci_dev *pdev; + struct rvu_pfvf *pf; + int i, err = 0; + + for (i = 0; i < rvu->hw->total_pfs; i++) { + pf = &rvu->pf[i]; + if (!pf->pdev) + continue; + pdev = pf->pdev; + + pf->limits_kobj = kobject_create_and_add("limits", + &pdev->dev.kobj); + + if (quota_sysfs_create("sso", pf->limits_kobj, rvu->dev, + &rvu->pf_limits.sso->a[i], pf)) { + dev_err(rvu->dev, + "Failed to allocate quota for sso on %s\n", + pci_name(pdev)); + err = -EFAULT; + break; + } + + if (quota_sysfs_create("ssow", pf->limits_kobj, rvu->dev, + &rvu->pf_limits.ssow->a[i], pf)) { + dev_err(rvu->dev, + "Failed to allocate quota for ssow, on %s\n", + pci_name(pdev)); + err = -EFAULT; + break; + } + + if (quota_sysfs_create("tim", pf->limits_kobj, rvu->dev, + &rvu->pf_limits.tim->a[i], pf)) { + dev_err(rvu->dev, + "Failed to allocate quota for tim, on %s\n", + pci_name(pdev)); + err = -EFAULT; + break; + } + + if (quota_sysfs_create("cpt", pf->limits_kobj, rvu->dev, + &rvu->pf_limits.cpt->a[i], pf)) { + dev_err(rvu->dev, + "Failed to allocate quota for cpt, on %s\n", + pci_name(pdev)); + err = -EFAULT; + break; + } + + if (quota_sysfs_create("npa", pf->limits_kobj, rvu->dev, + &rvu->pf_limits.npa->a[i], pf)) { + dev_err(rvu->dev, + "Failed to allocate quota for npa, on %s\n", + pci_name(pdev)); + err = -EFAULT; + break; + } + + if (quota_sysfs_create("nix", pf->limits_kobj, rvu->dev, + &rvu->pf_limits.nix->a[i], pf)) { + dev_err(rvu->dev, + "Failed to allocate quota for nix, on %s\n", + pci_name(pdev)); + err = -EFAULT; + break; + } + + /* In fixed TXSCHQ case each LF is assigned only 1 queue. */ + if (rvu->hw->cap.nix_fixed_txschq_mapping) + continue; + + if (quota_sysfs_create("smq", pf->limits_kobj, rvu->dev, + &rvu->pf_limits.smq->a[i], pf)) { + dev_err(rvu->dev, "Failed to allocate quota for smq on %s\n", + pci_name(pf->pdev)); + err = -EFAULT; + break; + } + + if (quota_sysfs_create("tl4", pf->limits_kobj, rvu->dev, + &rvu->pf_limits.tl4->a[i], pf)) { + dev_err(rvu->dev, "Failed to allocate quota for tl4 on %s\n", + pci_name(pf->pdev)); + err = -EFAULT; + break; + } + + if (quota_sysfs_create("tl3", pf->limits_kobj, rvu->dev, + &rvu->pf_limits.tl3->a[i], pf)) { + dev_err(rvu->dev, "Failed to allocate quota for tl3 on %s\n", + pci_name(pf->pdev)); + err = -EFAULT; + break; + } + + if (quota_sysfs_create("tl2", pf->limits_kobj, rvu->dev, + &rvu->pf_limits.tl2->a[i], pf)) { + dev_err(rvu->dev, "Failed to allocate quota for tl2 on %s\n", + pci_name(pf->pdev)); + err = -EFAULT; + break; + } + } + + return err; +} + +void rvu_policy_destroy(struct rvu *rvu) +{ + struct rvu_pfvf *pf = NULL; + int i; + + quotas_free(rvu->pf_limits.sso); + quotas_free(rvu->pf_limits.ssow); + quotas_free(rvu->pf_limits.npa); + quotas_free(rvu->pf_limits.cpt); + quotas_free(rvu->pf_limits.tim); + quotas_free(rvu->pf_limits.nix); + + rvu->pf_limits.sso = NULL; + rvu->pf_limits.ssow = NULL; + rvu->pf_limits.npa = NULL; + rvu->pf_limits.cpt = NULL; + rvu->pf_limits.tim = NULL; + rvu->pf_limits.nix = NULL; + + if (rvu->hw->cap.nix_fixed_txschq_mapping) { + quotas_free(rvu->pf_limits.smq); + quotas_free(rvu->pf_limits.tl4); + quotas_free(rvu->pf_limits.tl3); + quotas_free(rvu->pf_limits.tl2); + + rvu->pf_limits.smq = NULL; + rvu->pf_limits.tl4 = NULL; + rvu->pf_limits.tl3 = NULL; + rvu->pf_limits.tl2 = NULL; + } + + for (i = 0; i < rvu->hw->total_pfs; i++) { + pf = &rvu->pf[i]; + kobject_del(pf->limits_kobj); + } +} + +int rvu_policy_init(struct rvu *rvu) +{ + struct pci_dev *pdev = rvu->pdev; + struct rvu_hwinfo *hw = rvu->hw; + struct nix_hw *nix_hw = rvu->hw->nix0; + int err, i = 0; + u32 max = 0; + + max = hw->block[BLKADDR_SSO].lf.max; + rvu->pf_limits.sso = quotas_alloc(rvu->hw->total_pfs, max, max, + 0, &rvu->rsrc_lock, &pf_limit_ops); + if (!rvu->pf_limits.sso) { + dev_err(rvu->dev, "Failed to allocate sso limits\n"); + err = -EFAULT; + goto error; + } + + max = hw->block[BLKADDR_SSOW].lf.max; + rvu->pf_limits.ssow = quotas_alloc(rvu->hw->total_pfs, max, max, + 0, &rvu->rsrc_lock, &pf_limit_ops); + if (!rvu->pf_limits.ssow) { + dev_err(rvu->dev, "Failed to allocate ssow limits\n"); + err = -EFAULT; + goto error; + } + + max = hw->block[BLKADDR_TIM].lf.max; + rvu->pf_limits.tim = quotas_alloc(rvu->hw->total_pfs, max, max, + 0, &rvu->rsrc_lock, &pf_limit_ops); + if (!rvu->pf_limits.tim) { + dev_err(rvu->dev, "Failed to allocate tim limits\n"); + err = -EFAULT; + goto error; + } + + max = hw->block[BLKADDR_CPT0].lf.max; + rvu->pf_limits.cpt = quotas_alloc(rvu->hw->total_pfs, max, max, + 0, &rvu->rsrc_lock, &pf_limit_ops); + if (!rvu->pf_limits.cpt) { + dev_err(rvu->dev, "Failed to allocate cpt limits\n"); + err = -EFAULT; + goto error; + } + + /* Because limits track also VFs under PF, the maximum NPA LF limit for + * a single PF has to be max, not 1. Same for NIX below. + */ + max = hw->block[BLKADDR_NPA].lf.max; + rvu->pf_limits.npa = quotas_alloc(rvu->hw->total_pfs, max, max, + 0, &rvu->rsrc_lock, &pf_limit_ops); + if (!rvu->pf_limits.npa) { + dev_err(rvu->dev, "Failed to allocate npa limits\n"); + err = -EFAULT; + goto error; + } + + max = hw->block[BLKADDR_NIX0].lf.max; + rvu->pf_limits.nix = quotas_alloc(rvu->hw->total_pfs, max, max, + 0, &rvu->rsrc_lock, &pf_limit_ops); + if (!rvu->pf_limits.nix) { + dev_err(rvu->dev, "Failed to allocate nix limits\n"); + err = -EFAULT; + goto error; + } + + if (rvu->hw->cap.nix_fixed_txschq_mapping) + goto skip_txschq_limits; + + max = nix_hw->txsch[NIX_TXSCH_LVL_SMQ].schq.max; + rvu->pf_limits.smq = quotas_alloc(hw->total_pfs, max, max, 0, + &rvu->rsrc_lock, &pf_limit_ops); + if (!rvu->pf_limits.smq) { + dev_err(rvu->dev, "Failed to allocate SQM txschq limits\n"); + err = -EFAULT; + goto error; + } + + max = nix_hw->txsch[NIX_TXSCH_LVL_TL4].schq.max; + rvu->pf_limits.tl4 = quotas_alloc(hw->total_pfs, max, max, 0, + &rvu->rsrc_lock, &pf_limit_ops); + if (!rvu->pf_limits.tl4) { + dev_err(rvu->dev, "Failed to allocate TL4 txschq limits\n"); + err = -EFAULT; + goto error; + } + + max = nix_hw->txsch[NIX_TXSCH_LVL_TL3].schq.max; + rvu->pf_limits.tl3 = quotas_alloc(hw->total_pfs, max, max, 0, + &rvu->rsrc_lock, &pf_limit_ops); + if (!rvu->pf_limits.tl3) { + dev_err(rvu->dev, "Failed to allocate TL3 txschq limits\n"); + err = -EFAULT; + goto error; + } + + max = nix_hw->txsch[NIX_TXSCH_LVL_TL2].schq.max; + rvu->pf_limits.tl2 = quotas_alloc(hw->total_pfs, max, max, 0, + &rvu->rsrc_lock, &pf_limit_ops); + if (!rvu->pf_limits.tl2) { + dev_err(rvu->dev, "Failed to allocate TL2 txschq limits\n"); + err = -EFAULT; + goto error; + } + +skip_txschq_limits: + for (i = 0; i < hw->total_pfs; i++) + rvu->pf[i].pdev = + pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus), + i + 1, 0); + + rvu_set_default_limits(rvu); + + err = rvu_create_limits_sysfs(rvu); + if (err) { + dev_err(rvu->dev, "Failed to create limits sysfs\n"); + goto error; + } + + return 0; + +error: + rvu_policy_destroy(rvu); + return err; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_validation.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_validation.h new file mode 100644 index 000000000000..9dc8252d9986 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_validation.h @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Admin Function driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ + +#ifndef RVU_VALIDATION_H +#define RVU_VALIDATION_H + +struct rvu; +struct rvu_quotas; + +struct rvu_quota { + struct kobj_attribute sysfs; + /* Device to scope logs to */ + struct device *dev; + /* Kobject of the sysfs file */ + struct kobject *parent; + /* Pointer to base structure */ + struct rvu_quotas *base; + /* Argument passed to the quota_ops when this quota is modified */ + void *ops_arg; + /* Value of the quota */ + int val; +}; + +struct rvu_quota_ops { + /* + * Called before sysfs store(). store() will proceed if returns 0. + * It is called with struct rvu_quotas::lock taken. + */ + int (*pre_store)(void *arg, struct rvu_quota *quota, int new_val); + /** called after sysfs store(). */ + void (*post_store)(void *arg, struct rvu_quota *quota, int old_val); +}; + +struct rvu_quotas { + struct rvu_quota_ops ops; + struct mutex *lock; /* lock taken for each sysfs operation */ + u32 cnt; /* number of elements in arr */ + u32 max; /* maximum value for a single quota */ + u64 max_sum; /* maximum sum of all quotas */ + struct rvu_quota a[0]; /* array of quota assignments */ +}; + +struct rvu_limits { + struct rvu_quotas *sso; + struct rvu_quotas *ssow; + struct rvu_quotas *tim; + struct rvu_quotas *cpt; + struct rvu_quotas *npa; + struct rvu_quotas *nix; + struct rvu_quotas *smq; + struct rvu_quotas *tl4; + struct rvu_quotas *tl3; + struct rvu_quotas *tl2; +}; + +int rvu_policy_init(struct rvu *rvu); +void rvu_policy_destroy(struct rvu *rvu); +int rvu_check_rsrc_policy(struct rvu *rvu, + struct rsrc_attach *req, u16 pcifunc); +int rvu_check_txsch_policy(struct rvu *rvu, struct nix_txsch_alloc_req *req, + u16 pcifunc); + +int rvu_mbox_handler_free_rsrc_cnt(struct rvu *rvu, struct msg_req *req, + struct free_rsrcs_rsp *rsp); +#endif /* RVU_VALIDATION_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile new file mode 100644 index 000000000000..48846096f4c7 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Marvell's OcteonTX2 ethernet device drivers +# + +obj-$(CONFIG_OCTEONTX2_PF) += octeontx2_nicpf.o +obj-$(CONFIG_OCTEONTX2_VF) += octeontx2_nicvf.o + +octeontx2_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \ + otx2_ptp.o otx2_flows.o +octeontx2_nicvf-y := otx2_vf.o otx2_smqvf.o + +ccflags-y += -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c new file mode 100644 index 000000000000..faf3365e2a11 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -0,0 +1,1474 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/interrupt.h> +#include <linux/pci.h> +#include <linux/ethtool.h> +#include <net/tso.h> + +#include "otx2_reg.h" +#include "otx2_common.h" +#include "otx2_struct.h" + +static inline void otx2_nix_rq_op_stats(struct queue_stats *stats, + struct otx2_nic *pfvf, int qidx); +static inline void otx2_nix_sq_op_stats(struct queue_stats *stats, + struct otx2_nic *pfvf, int qidx); + +void otx2_update_lmac_stats(struct otx2_nic *pfvf) +{ + struct msg_req *req; + + if (!netif_running(pfvf->netdev)) + return; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_cgx_stats(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return; + } + + otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); +} + +void otx2_update_lmac_fec_stats(struct otx2_nic *pfvf) +{ + struct msg_req *req; + + if (!netif_running(pfvf->netdev)) + return; + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_cgx_fec_stats(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return; + } + otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); +} + +int otx2_update_rq_stats(struct otx2_nic *pfvf, int qidx) +{ + struct otx2_rcv_queue *rq = &pfvf->qset.rq[qidx]; + + if (!pfvf->qset.rq) + return 0; + + otx2_nix_rq_op_stats(&rq->stats, pfvf, qidx); + return 1; +} + +int otx2_update_sq_stats(struct otx2_nic *pfvf, int qidx) +{ + struct otx2_snd_queue *sq = &pfvf->qset.sq[qidx]; + + if (!pfvf->qset.sq) + return 0; + + otx2_nix_sq_op_stats(&sq->stats, pfvf, qidx); + return 1; +} + +void otx2_get_dev_stats(struct otx2_nic *pfvf) +{ + struct otx2_dev_stats *dev_stats = &pfvf->hw.dev_stats; + +#define OTX2_GET_RX_STATS(reg) \ + otx2_read64(pfvf, NIX_LF_RX_STATX(reg)) +#define OTX2_GET_TX_STATS(reg) \ + otx2_read64(pfvf, NIX_LF_TX_STATX(reg)) + + dev_stats->rx_bytes = OTX2_GET_RX_STATS(RX_OCTS); + dev_stats->rx_drops = OTX2_GET_RX_STATS(RX_DROP); + dev_stats->rx_bcast_frames = OTX2_GET_RX_STATS(RX_BCAST); + dev_stats->rx_mcast_frames = OTX2_GET_RX_STATS(RX_MCAST); + dev_stats->rx_ucast_frames = OTX2_GET_RX_STATS(RX_UCAST); + dev_stats->rx_frames = dev_stats->rx_bcast_frames + + dev_stats->rx_mcast_frames + + dev_stats->rx_ucast_frames; + + dev_stats->tx_bytes = OTX2_GET_TX_STATS(TX_OCTS); + dev_stats->tx_drops = OTX2_GET_TX_STATS(TX_DROP); + dev_stats->tx_bcast_frames = OTX2_GET_TX_STATS(TX_BCAST); + dev_stats->tx_mcast_frames = OTX2_GET_TX_STATS(TX_MCAST); + dev_stats->tx_ucast_frames = OTX2_GET_TX_STATS(TX_UCAST); + dev_stats->tx_frames = dev_stats->tx_bcast_frames + + dev_stats->tx_mcast_frames + + dev_stats->tx_ucast_frames; +} + +void otx2_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct otx2_dev_stats *dev_stats = &pfvf->hw.dev_stats; + + otx2_get_dev_stats(pfvf); + + stats->rx_bytes = dev_stats->rx_bytes; + stats->rx_packets = dev_stats->rx_frames; + stats->rx_dropped = dev_stats->rx_drops; + stats->multicast = dev_stats->rx_mcast_frames; + + stats->tx_bytes = dev_stats->tx_bytes; + stats->tx_packets = dev_stats->tx_frames; + stats->tx_dropped = dev_stats->tx_drops; +} +EXPORT_SYMBOL(otx2_get_stats64); + +/* Sync MAC address with RVU */ +int otx2_hw_set_mac_addr(struct otx2_nic *pfvf, struct net_device *netdev) +{ + struct nix_set_mac_addr *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_nix_set_mac_addr(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + ether_addr_copy(req->mac_addr, netdev->dev_addr); + + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} + +static int otx2_hw_get_mac_addr(struct otx2_nic *pfvf, + struct net_device *netdev) +{ + struct nix_get_mac_addr_rsp *rsp; + struct mbox_msghdr *msghdr; + struct msg_req *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_nix_get_mac_addr(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + otx2_mbox_unlock(&pfvf->mbox); + return err; + } + + msghdr = otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr); + if (!msghdr) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + rsp = (struct nix_get_mac_addr_rsp *)msghdr; + ether_addr_copy(netdev->dev_addr, rsp->mac_addr); + otx2_mbox_unlock(&pfvf->mbox); + + return 0; +} + +int otx2_set_mac_address(struct net_device *netdev, void *p) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + + otx2_hw_set_mac_addr(pfvf, netdev); + + return 0; +} +EXPORT_SYMBOL(otx2_set_mac_address); + +int otx2_hw_set_mtu(struct otx2_nic *pfvf, int mtu) +{ + struct nix_frs_cfg *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_nix_set_hw_frs(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + req->update_smq = true; + /* Add EDSA/HIGIG2 header len to maxlen */ + pfvf->max_frs = mtu + OTX2_ETH_HLEN + pfvf->addl_mtu; + req->maxlen = pfvf->max_frs; + + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} + +int otx2_set_flowkey_cfg(struct otx2_nic *pfvf) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + struct nix_rss_flowkey_cfg *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_nix_rss_flowkey_cfg(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + req->mcam_index = -1; /* Default or reserved index */ + req->flowkey_cfg = rss->flowkey_cfg; + req->group = DEFAULT_RSS_CONTEXT_GROUP; + + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} + +int otx2_set_rss_table(struct otx2_nic *pfvf) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + struct mbox *mbox = &pfvf->mbox; + struct nix_aq_enq_req *aq; + int idx, err; + + otx2_mbox_lock(mbox); + /* Get memory to put this msg */ + for (idx = 0; idx < rss->rss_size; idx++) { + aq = otx2_mbox_alloc_msg_nix_aq_enq(mbox); + if (!aq) { + /* The shared memory buffer can be full. + * Flush it and retry + */ + err = otx2_sync_mbox_msg(mbox); + if (err) { + otx2_mbox_unlock(mbox); + return err; + } + aq = otx2_mbox_alloc_msg_nix_aq_enq(mbox); + if (!aq) { + otx2_mbox_unlock(mbox); + return -ENOMEM; + } + } + + aq->rss.rq = rss->ind_tbl[idx]; + + /* Fill AQ info */ + aq->qidx = idx; + aq->ctype = NIX_AQ_CTYPE_RSS; + aq->op = NIX_AQ_INSTOP_INIT; + } + err = otx2_sync_mbox_msg(mbox); + otx2_mbox_unlock(mbox); + return err; +} + +void otx2_set_rss_key(struct otx2_nic *pfvf) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + u64 *key = (u64 *)&rss->key[4]; + int idx; + + /* 352bit or 44byte key needs to be configured as below + * NIX_LF_RX_SECRETX0 = key<351:288> + * NIX_LF_RX_SECRETX1 = key<287:224> + * NIX_LF_RX_SECRETX2 = key<223:160> + * NIX_LF_RX_SECRETX3 = key<159:96> + * NIX_LF_RX_SECRETX4 = key<95:32> + * NIX_LF_RX_SECRETX5<63:32> = key<31:0> + */ + otx2_write64(pfvf, NIX_LF_RX_SECRETX(5), + (u64)(*((u32 *)&rss->key)) << 32); + idx = sizeof(rss->key) / sizeof(u64); + while (idx > 0) { + idx--; + otx2_write64(pfvf, NIX_LF_RX_SECRETX(idx), *key++); + } +} + +int otx2_rss_init(struct otx2_nic *pfvf) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + int idx, ret = 0; + + /* Enable RSS */ + rss->enable = true; + rss->rss_size = sizeof(rss->ind_tbl); + + /* Init RSS key here */ + netdev_rss_key_fill(rss->key, sizeof(rss->key)); + otx2_set_rss_key(pfvf); + + /* Default indirection table */ + for (idx = 0; idx < rss->rss_size; idx++) + rss->ind_tbl[idx] = + ethtool_rxfh_indir_default(idx, pfvf->hw.rx_queues); + + ret = otx2_set_rss_table(pfvf); + if (ret) + return ret; + + /* Default flowkey or hash config to be used for generating flow tag */ + rss->flowkey_cfg = NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6 | + NIX_FLOW_KEY_TYPE_TCP | NIX_FLOW_KEY_TYPE_UDP | + NIX_FLOW_KEY_TYPE_SCTP; + + return otx2_set_flowkey_cfg(pfvf); +} + +void otx2_config_irq_coalescing(struct otx2_nic *pfvf, int qidx) +{ + /* Configure CQE interrupt coalescing parameters + * + * HW triggers an irq when ECOUNT > cq_ecount_wait, hence + * set 1 less than cq_ecount_wait. And cq_time_wait is in + * usecs, convert that to 100ns count. + */ + otx2_write64(pfvf, NIX_LF_CINTX_WAIT(qidx), + ((u64)(pfvf->hw.cq_time_wait * 10) << 48) | + ((u64)pfvf->hw.cq_qcount_wait << 32) | + (pfvf->hw.cq_ecount_wait - 1)); +} + +dma_addr_t otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, + gfp_t gfp) +{ + dma_addr_t iova; + + /* Check if request can be accommodated in previous allocated page */ + if (pool->page && + ((pool->page_offset + pool->rbsize) <= PAGE_SIZE)) { + pool->pageref++; + goto ret; + } + + otx2_get_page(pool); + + /* Allocate a new page */ + pool->page = alloc_pages(gfp | __GFP_COMP | __GFP_NOWARN, 0); + if (unlikely(!pool->page)) + return -ENOMEM; + + pool->page_offset = 0; +ret: + iova = (u64)dma_map_page_attrs(pfvf->dev, pool->page, + pool->page_offset, pool->rbsize, + DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + if (unlikely(dma_mapping_error(pfvf->dev, iova))) { + if (!pool->page_offset) + __free_pages(pool->page, 0); + pool->page = NULL; + return -ENOMEM; + } + pool->page_offset += pool->rbsize; + return iova; +} + +void otx2_tx_timeout(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + schedule_work(&pfvf->reset_task); +} +EXPORT_SYMBOL(otx2_tx_timeout); + +void otx2_get_mac_from_af(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + int err; + + err = otx2_hw_get_mac_addr(pfvf, netdev); + if (err) + dev_warn(pfvf->dev, "Failed to read mac from hardware\n"); + + /* Normally AF should provide mac addresses for both PFs and CGX mapped + * VFs which means random mac gets generated either in case of error + * or LBK netdev. + */ + if (!is_valid_ether_addr(netdev->dev_addr)) + eth_hw_addr_random(netdev); +} +EXPORT_SYMBOL(otx2_get_mac_from_af); + +static int otx2_get_link(struct otx2_nic *pfvf) +{ + int link = 0; + u16 map; + + /* cgx lmac link */ + if (pfvf->hw.tx_chan_base >= CGX_CHAN_BASE) { + map = pfvf->hw.tx_chan_base & 0x7FF; + link = 4 * ((map >> 8) & 0xF) + ((map >> 4) & 0xF); + } + /* LBK channel */ + if (pfvf->hw.tx_chan_base < SDP_CHAN_BASE) + link = 12; + + return link; +} + +int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) +{ + struct nix_txschq_config *req; + struct otx2_hw *hw = &pfvf->hw; + u64 schq, parent; + + req = otx2_mbox_alloc_msg_nix_txschq_cfg(&pfvf->mbox); + if (!req) + return -ENOMEM; + + req->lvl = lvl; + req->num_regs = 1; + + schq = hw->txschq_list[lvl][0]; + /* Set topology e.t.c configuration */ + if (lvl == NIX_TXSCH_LVL_SMQ) { + /* Set min and max Tx packet lengths */ + req->reg[0] = NIX_AF_SMQX_CFG(schq); + req->regval[0] = ((pfvf->netdev->mtu + OTX2_ETH_HLEN) << 8) | + OTX2_MIN_MTU; + + req->regval[0] |= (0x20ULL << 51) | (0x80ULL << 39); + req->num_regs++; + /* MDQ config */ + parent = hw->txschq_list[NIX_TXSCH_LVL_TL4][0]; + req->reg[1] = NIX_AF_MDQX_PARENT(schq); + req->regval[1] = parent << 16; + req->num_regs++; + /* Set DWRR quantum */ + req->reg[2] = NIX_AF_MDQX_SCHEDULE(schq); + req->regval[2] = pfvf->netdev->mtu; + } else if (lvl == NIX_TXSCH_LVL_TL4) { + parent = hw->txschq_list[NIX_TXSCH_LVL_TL3][0]; + req->reg[0] = NIX_AF_TL4X_PARENT(schq); + req->regval[0] = parent << 16; + } else if (lvl == NIX_TXSCH_LVL_TL3) { + parent = hw->txschq_list[NIX_TXSCH_LVL_TL2][0]; + req->reg[0] = NIX_AF_TL3X_PARENT(schq); + req->regval[0] = parent << 16; + } else if (lvl == NIX_TXSCH_LVL_TL2) { + parent = hw->txschq_list[NIX_TXSCH_LVL_TL1][0]; + req->reg[0] = NIX_AF_TL2X_PARENT(schq); + req->regval[0] = parent << 16; + + req->num_regs++; + req->reg[1] = NIX_AF_TL2X_SCHEDULE(schq); + req->regval[1] = TXSCH_TL1_DFLT_RR_PRIO << 24; + + req->num_regs++; + req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, + otx2_get_link(pfvf)); + /* Enable this queue and backpressure */ + req->regval[2] = BIT_ULL(13) | BIT_ULL(12); + + } else if (lvl == NIX_TXSCH_LVL_TL1) { + /* Default config for TL1. + * For VF this is always ignored. + */ + + /* Set DWRR quantum */ + req->reg[0] = NIX_AF_TL1X_SCHEDULE(schq); + req->regval[0] = TXSCH_TL1_DFLT_RR_QTM; + + req->num_regs++; + req->reg[1] = NIX_AF_TL1X_TOPOLOGY(schq); + req->regval[1] = (TXSCH_TL1_DFLT_RR_PRIO << 1); + + req->num_regs++; + req->reg[2] = NIX_AF_TL1X_CIR(schq); + req->regval[2] = 0; + } + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +int otx2_txsch_alloc(struct otx2_nic *pfvf) +{ + struct nix_txsch_alloc_req *req; + int lvl, err; + + /* Get memory to put this msg */ + req = otx2_mbox_alloc_msg_nix_txsch_alloc(&pfvf->mbox); + if (!req) + return -ENOMEM; + + /* Request one schq per level */ + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) + req->schq[lvl] = 1; + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + return err; + return 0; +} + +int otx2_txschq_stop(struct otx2_nic *pfvf) +{ + struct nix_txsch_free_req *free_req; + int lvl, schq; + + otx2_mbox_lock(&pfvf->mbox); + /* Free the transmit schedulers */ + free_req = otx2_mbox_alloc_msg_nix_txsch_free(&pfvf->mbox); + if (!free_req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + free_req->flags = TXSCHQ_FREE_ALL; + WARN_ON(otx2_sync_mbox_msg(&pfvf->mbox)); + otx2_mbox_unlock(&pfvf->mbox); + + /* Clear the txschq list */ + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + for (schq = 0; schq < MAX_TXSCHQ_PER_FUNC; schq++) + pfvf->hw.txschq_list[lvl][schq] = 0; + } + return 0; +} + +/* RED and drop levels of CQ on packet reception. + * For CQ level is measure of emptiness ( 0x0 = full, 255 = empty). + */ +#define RQ_PASS_LVL_CQ(skid, qsize) ((((skid) + 16) * 256) / (qsize)) +#define RQ_DROP_LVL_CQ(skid, qsize) (((skid) * 256) / (qsize)) + +/* RED and drop levels of AURA for packet reception. + * For AURA level is measure of fullness (0x0 = empty, 255 = full). + * Eg: For RQ length 1K, for pass/drop level 204/230. + * RED accepts pkts if free pointers > 102 & <= 205. + * Drops pkts if free pointers < 102. + */ +#define RQ_PASS_LVL_AURA (255 - ((95 * 256) / 100)) /* RED when 95% is full */ +#define RQ_DROP_LVL_AURA (255 - ((99 * 256) / 100)) /* Drop when 99% is full */ + +/* Send skid of 2000 packets required for CQ size of 4K CQEs. */ +#define SEND_CQ_SKID 2000 + +static int otx2_rq_init(struct otx2_nic *pfvf, u16 qidx, u16 lpb_aura) +{ + struct otx2_qset *qset = &pfvf->qset; + struct nix_aq_enq_req *aq; + + /* Get memory to put this msg */ + aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); + if (!aq) + return -ENOMEM; + + aq->rq.cq = qidx; + aq->rq.ena = 1; + aq->rq.pb_caching = 1; + aq->rq.lpb_aura = lpb_aura; /* Use large packet buffer aura */ + aq->rq.lpb_sizem1 = (DMA_BUFFER_LEN / 8) - 1; + aq->rq.xqe_imm_size = 0; /* Copying of packet to CQE not needed */ + aq->rq.flow_tagw = 32; /* Copy full 32bit flow_tag to CQE header */ + aq->rq.qint_idx = 0; + aq->rq.lpb_drop_ena = 1; /* Enable RED dropping for AURA */ + aq->rq.xqe_drop_ena = 1; /* Enable RED dropping for CQ/SSO */ + aq->rq.xqe_pass = RQ_PASS_LVL_CQ(pfvf->hw.rq_skid, qset->rqe_cnt); + aq->rq.xqe_drop = RQ_DROP_LVL_CQ(pfvf->hw.rq_skid, qset->rqe_cnt); + aq->rq.lpb_aura_pass = RQ_PASS_LVL_AURA; + aq->rq.lpb_aura_drop = RQ_DROP_LVL_AURA; + + /* Fill AQ info */ + aq->qidx = qidx; + aq->ctype = NIX_AQ_CTYPE_RQ; + aq->op = NIX_AQ_INSTOP_INIT; + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +static int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) +{ + struct otx2_qset *qset = &pfvf->qset; + struct otx2_snd_queue *sq; + struct nix_aq_enq_req *aq; + struct otx2_pool *pool; + int err; + + pool = &pfvf->qset.pool[sqb_aura]; + sq = &qset->sq[qidx]; + sq->sqe_size = NIX_SQESZ_W16 ? 64 : 128; + sq->sqe_cnt = qset->sqe_cnt; + + err = qmem_alloc(pfvf->dev, &sq->sqe, 1, sq->sqe_size); + if (err) + return err; + + err = qmem_alloc(pfvf->dev, &sq->tso_hdrs, qset->sqe_cnt, + TSO_HEADER_SIZE); + if (err) + return err; + + sq->sqe_base = sq->sqe->base; + sq->sg = kcalloc(qset->sqe_cnt, sizeof(struct sg_list), GFP_KERNEL); + if (!sq->sg) + return -ENOMEM; + + if (pfvf->ptp) { + err = qmem_alloc(pfvf->dev, &sq->timestamps, qset->sqe_cnt, + sizeof(*sq->timestamps)); + if (err) + return err; + } + + sq->head = 0; + sq->sqe_per_sqb = (pfvf->hw.sqb_size / sq->sqe_size) - 1; + sq->num_sqbs = (qset->sqe_cnt + sq->sqe_per_sqb) / sq->sqe_per_sqb; + /* Set SQE threshold to 10% of total SQEs */ + sq->sqe_thresh = ((sq->num_sqbs * sq->sqe_per_sqb) * 10) / 100; + sq->aura_id = sqb_aura; + sq->aura_fc_addr = pool->fc_addr->base; + sq->lmt_addr = (__force u64 *)(pfvf->reg_base + LMT_LF_LMTLINEX(qidx)); + sq->io_addr = (__force u64)otx2_get_regaddr(pfvf, NIX_LF_OP_SENDX(0)); + + sq->stats.bytes = 0; + sq->stats.pkts = 0; + + /* Get memory to put this msg */ + aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); + if (!aq) + return -ENOMEM; + + aq->sq.cq = pfvf->hw.rx_queues + qidx; + aq->sq.max_sqe_size = NIX_MAXSQESZ_W16; /* 128 byte */ + aq->sq.cq_ena = 1; + aq->sq.ena = 1; + /* Only one SMQ is allocated, map all SQ's to that SMQ */ + aq->sq.smq = pfvf->hw.txschq_list[NIX_TXSCH_LVL_SMQ][0]; + aq->sq.smq_rr_quantum = OTX2_MAX_MTU; + aq->sq.default_chan = pfvf->hw.tx_chan_base; + aq->sq.sqe_stype = NIX_STYPE_STF; /* Cache SQB */ + aq->sq.sqb_aura = sqb_aura; + aq->sq.sq_int_ena = NIX_SQINT_BITS; + aq->sq.qint_idx = 0; + /* Due pipelining impact minimum 2000 unused SQ CQE's + * need to maintain to avoid CQ overflow. + */ + aq->sq.cq_limit = ((SEND_CQ_SKID * 256) / (sq->sqe_cnt)); + + /* Fill AQ info */ + aq->qidx = qidx; + aq->ctype = NIX_AQ_CTYPE_SQ; + aq->op = NIX_AQ_INSTOP_INIT; + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) +{ + struct otx2_qset *qset = &pfvf->qset; + struct nix_aq_enq_req *aq; + struct otx2_cq_queue *cq; + int err, pool_id; + + cq = &qset->cq[qidx]; + cq->cq_idx = qidx; + if (qidx < pfvf->hw.rx_queues) { + cq->cq_type = CQ_RX; + cq->cint_idx = qidx; + cq->cqe_cnt = qset->rqe_cnt; + } else { + cq->cq_type = CQ_TX; + cq->cint_idx = qidx - pfvf->hw.rx_queues; + cq->cqe_cnt = qset->sqe_cnt; + } + cq->cqe_size = pfvf->qset.xqe_size; + + /* Allocate memory for CQEs */ + err = qmem_alloc(pfvf->dev, &cq->cqe, cq->cqe_cnt, cq->cqe_size); + if (err) + return err; + + /* Save CQE CPU base for faster reference */ + cq->cqe_base = cq->cqe->base; + /* In case where all RQs auras point to single pool, + * all CQs receive buffer pool also point to same pool. + */ + pool_id = ((cq->cq_type == CQ_RX) && + (pfvf->hw.rqpool_cnt != pfvf->hw.rx_queues)) ? 0 : qidx; + cq->rbpool = &qset->pool[pool_id]; + cq->refill_task_sched = false; + + /* Get memory to put this msg */ + aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); + if (!aq) + return -ENOMEM; + + aq->cq.ena = 1; + aq->cq.qsize = Q_SIZE(cq->cqe_cnt, 4); + aq->cq.caching = 1; + aq->cq.base = cq->cqe->iova; + aq->cq.cint_idx = cq->cint_idx; + aq->cq.cq_err_int_ena = NIX_CQERRINT_BITS; + aq->cq.qint_idx = 0; + aq->cq.avg_level = 255; + + if (qidx < pfvf->hw.rx_queues) { + aq->cq.drop = RQ_DROP_LVL_CQ(pfvf->hw.rq_skid, cq->cqe_cnt); + aq->cq.drop_ena = 1; + + /* Enable receive CQ backpressure */ + aq->cq.bp_ena = 1; + aq->cq.bpid = pfvf->bpid[0]; + + /* Set backpressure level is same as cq pass level */ + aq->cq.bp = RQ_PASS_LVL_CQ(pfvf->hw.rq_skid, qset->rqe_cnt); + } + + /* Fill AQ info */ + aq->qidx = qidx; + aq->ctype = NIX_AQ_CTYPE_CQ; + aq->op = NIX_AQ_INSTOP_INIT; + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +static void otx2_pool_refill_task(struct work_struct *work) +{ + struct otx2_cq_queue *cq; + struct otx2_pool *rbpool; + struct refill_work *wrk; + int qidx, free_ptrs = 0; + struct otx2_nic *pfvf; + s64 bufptr; + + wrk = container_of(work, struct refill_work, pool_refill_work.work); + pfvf = wrk->pf; + qidx = wrk - pfvf->refill_wrk; + cq = &pfvf->qset.cq[qidx]; + rbpool = cq->rbpool; + free_ptrs = cq->pool_ptrs; + + while (cq->pool_ptrs) { + bufptr = otx2_alloc_rbuf(pfvf, rbpool, GFP_KERNEL); + if (bufptr <= 0) { + /* Schedule a WQ if we fails to free atleast half of the + * pointers else enable napi for this RQ. + */ + if (!((free_ptrs - cq->pool_ptrs) > free_ptrs / 2)) { + struct delayed_work *dwork; + + dwork = &wrk->pool_refill_work; + schedule_delayed_work(dwork, + msecs_to_jiffies(100)); + } else { + cq->refill_task_sched = false; + } + return; + } + otx2_aura_freeptr(pfvf, qidx, bufptr + OTX2_HEAD_ROOM); + cq->pool_ptrs--; + } + cq->refill_task_sched = false; +} + +int otx2_config_nix_queues(struct otx2_nic *pfvf) +{ + int qidx, err; + + /* Initialize RX queues */ + for (qidx = 0; qidx < pfvf->hw.rx_queues; qidx++) { + u16 lpb_aura = otx2_get_pool_idx(pfvf, AURA_NIX_RQ, qidx); + + err = otx2_rq_init(pfvf, qidx, lpb_aura); + if (err) + return err; + } + + /* Initialize TX queues */ + for (qidx = 0; qidx < pfvf->hw.tx_queues; qidx++) { + u16 sqb_aura = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx); + + err = otx2_sq_init(pfvf, qidx, sqb_aura); + if (err) + return err; + } + + /* Initialize completion queues */ + for (qidx = 0; qidx < pfvf->qset.cq_cnt; qidx++) { + err = otx2_cq_init(pfvf, qidx); + if (err) + return err; + } + + /* Initialize work queue for receive buffer refill */ + + pfvf->refill_wrk = devm_kcalloc(pfvf->dev, pfvf->qset.cq_cnt, + sizeof(struct refill_work), GFP_KERNEL); + if (!pfvf->refill_wrk) + return -ENOMEM; + + for (qidx = 0; qidx < pfvf->qset.cq_cnt; qidx++) { + pfvf->refill_wrk[qidx].pf = pfvf; + INIT_DELAYED_WORK(&pfvf->refill_wrk[qidx].pool_refill_work, + otx2_pool_refill_task); + } + return 0; +} + +int otx2_config_nix(struct otx2_nic *pfvf) +{ + struct nix_lf_alloc_req *nixlf; + struct nix_lf_alloc_rsp *rsp; + int err; + + pfvf->qset.xqe_size = NIX_XQESZ_W16 ? 128 : 512; + + /* Get memory to put this msg */ + nixlf = otx2_mbox_alloc_msg_nix_lf_alloc(&pfvf->mbox); + if (!nixlf) + return -ENOMEM; + + /* Set RQ/SQ/CQ counts */ + nixlf->rq_cnt = pfvf->hw.rx_queues; + nixlf->sq_cnt = pfvf->hw.tx_queues; + nixlf->cq_cnt = pfvf->qset.cq_cnt; + nixlf->rss_sz = MAX_RSS_INDIR_TBL_SIZE; + nixlf->rss_grps = 1; /* Single RSS indir table supported, for now */ + nixlf->xqe_sz = NIX_XQESZ_W16; + /* We don't know absolute NPA LF idx attached. + * AF will replace 'RVU_DEFAULT_PF_FUNC' with + * NPA LF attached to this RVU PF/VF. + */ + nixlf->npa_func = RVU_DEFAULT_PF_FUNC; + /* Disable alignment pad, enable L2 length check, + * enable L4 TCP/UDP checksum verification. + */ + nixlf->rx_cfg = BIT_ULL(33) | BIT_ULL(35) | BIT_ULL(37); + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + return err; + + rsp = (struct nix_lf_alloc_rsp *)otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, + &nixlf->hdr); + if (IS_ERR(rsp)) + return PTR_ERR(rsp); + + if (rsp->qints < 1) + return -ENXIO; + + return rsp->hdr.rc; +} + +void otx2_sq_free_sqbs(struct otx2_nic *pfvf) +{ + struct otx2_qset *qset = &pfvf->qset; + struct otx2_hw *hw = &pfvf->hw; + struct otx2_snd_queue *sq; + int sqb, qidx; + u64 iova, pa; + + for (qidx = 0; qidx < hw->tx_queues; qidx++) { + sq = &qset->sq[qidx]; + if (!sq->sqb_ptrs) + continue; + for (sqb = 0; sqb < sq->sqb_count; sqb++) { + if (!sq->sqb_ptrs[sqb]) + continue; + iova = sq->sqb_ptrs[sqb]; + pa = otx2_iova_to_phys(pfvf->iommu_domain, iova); + dma_unmap_page_attrs(pfvf->dev, iova, hw->sqb_size, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + put_page(virt_to_page(phys_to_virt(pa))); + } + sq->sqb_count = 0; + } +} + +void otx2_free_aura_ptr(struct otx2_nic *pfvf, int type) +{ + int pool_id, pool_start = 0, pool_end = 0, size = 0; + u64 iova, pa; + + if (type == AURA_NIX_SQ) { + pool_start = otx2_get_pool_idx(pfvf, type, 0); + pool_end = pool_start + pfvf->hw.sqpool_cnt; + size = pfvf->hw.sqb_size; + } + if (type == AURA_NIX_RQ) { + pool_start = otx2_get_pool_idx(pfvf, type, 0); + pool_end = pfvf->hw.rqpool_cnt; + size = RCV_FRAG_LEN; + } + + /* Free SQB and RQB pointers from the aura pool */ + for (pool_id = pool_start; pool_id < pool_end; pool_id++) { + iova = otx2_aura_allocptr(pfvf, pool_id); + while (iova) { + if (type == AURA_NIX_RQ) + iova -= OTX2_HEAD_ROOM; + + pa = otx2_iova_to_phys(pfvf->iommu_domain, iova); + dma_unmap_page_attrs(pfvf->dev, iova, size, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + put_page(virt_to_page(phys_to_virt(pa))); + iova = otx2_aura_allocptr(pfvf, pool_id); + } + } +} + +void otx2_aura_pool_free(struct otx2_nic *pfvf) +{ + struct otx2_pool *pool; + int pool_id; + + if (!pfvf->qset.pool) + return; + + for (pool_id = 0; pool_id < pfvf->hw.pool_cnt; pool_id++) { + pool = &pfvf->qset.pool[pool_id]; + qmem_free(pfvf->dev, pool->stack); + qmem_free(pfvf->dev, pool->fc_addr); + } + devm_kfree(pfvf->dev, pfvf->qset.pool); +} + +static int otx2_aura_init(struct otx2_nic *pfvf, int aura_id, + int pool_id, int numptrs) +{ + struct npa_aq_enq_req *aq; + struct otx2_pool *pool; + int err; + + pool = &pfvf->qset.pool[pool_id]; + + /* Allocate memory for HW to update Aura count. + * Alloc one cache line, so that it fits all FC_STYPE modes. + */ + if (!pool->fc_addr) { + err = qmem_alloc(pfvf->dev, &pool->fc_addr, 1, OTX2_ALIGN); + if (err) + return err; + } + + /* Initialize this aura's context via AF */ + aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!aq) { + /* Shared mbox memory buffer is full, flush it and retry */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + return err; + aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!aq) + return -ENOMEM; + } + + aq->aura_id = aura_id; + /* Will be filled by AF with correct pool context address */ + aq->aura.pool_addr = pool_id; + aq->aura.pool_caching = 1; + aq->aura.shift = ilog2(numptrs) - 8; + aq->aura.count = numptrs; + aq->aura.limit = numptrs; + aq->aura.avg_level = 255; + aq->aura.ena = 1; + aq->aura.fc_ena = 1; + aq->aura.fc_addr = pool->fc_addr->iova; + aq->aura.fc_hyst_bits = 0; /* Store count on all updates */ + + /* Enable backpressure for RQ aura */ + if (aura_id < pfvf->hw.rqpool_cnt) { + aq->aura.bp_ena = 0; + aq->aura.nix0_bpid = pfvf->bpid[0]; + /* Set backpressure level is same as RQ aura pass level */ + aq->aura.bp = RQ_PASS_LVL_AURA; + } + + /* Fill AQ info */ + aq->ctype = NPA_AQ_CTYPE_AURA; + aq->op = NPA_AQ_INSTOP_INIT; + + return 0; +} + +static int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id, + int stack_pages, int numptrs, int buf_size) +{ + struct npa_aq_enq_req *aq; + struct otx2_pool *pool; + int err; + + pool = &pfvf->qset.pool[pool_id]; + /* Alloc memory for stack which is used to store buffer pointers */ + err = qmem_alloc(pfvf->dev, &pool->stack, + stack_pages, pfvf->hw.stack_pg_bytes); + if (err) + return err; + + pool->rbsize = buf_size; + + /* Initialize this pool's context via AF */ + aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!aq) { + /* Shared mbox memory buffer is full, flush it and retry */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + qmem_free(pfvf->dev, pool->stack); + return err; + } + aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!aq) { + qmem_free(pfvf->dev, pool->stack); + return -ENOMEM; + } + } + + aq->aura_id = pool_id; + aq->pool.stack_base = pool->stack->iova; + aq->pool.stack_caching = 1; + aq->pool.ena = 1; + aq->pool.buf_size = buf_size / 128; + aq->pool.stack_max_pages = stack_pages; + aq->pool.shift = ilog2(numptrs) - 8; + aq->pool.ptr_start = 0; + aq->pool.ptr_end = ~0ULL; + + /* Fill AQ info */ + aq->ctype = NPA_AQ_CTYPE_POOL; + aq->op = NPA_AQ_INSTOP_INIT; + + return 0; +} + +int otx2_sq_aura_pool_init(struct otx2_nic *pfvf) +{ + int qidx, pool_id, stack_pages, num_sqbs; + struct otx2_qset *qset = &pfvf->qset; + struct otx2_hw *hw = &pfvf->hw; + struct otx2_snd_queue *sq; + struct otx2_pool *pool; + int err, ptr; + s64 bufptr; + + /* Calculate number of SQBs needed. + * + * For a 128byte SQE, and 4K size SQB, 31 SQEs will fit in one SQB. + * Last SQE is used for pointing to next SQB. + */ + num_sqbs = (hw->sqb_size / 128) - 1; + num_sqbs = (qset->sqe_cnt + num_sqbs) / num_sqbs; + + /* Get no of stack pages needed */ + stack_pages = + (num_sqbs + hw->stack_pg_ptrs - 1) / hw->stack_pg_ptrs; + + for (qidx = 0; qidx < hw->tx_queues; qidx++) { + pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx); + /* Initialize aura context */ + err = otx2_aura_init(pfvf, pool_id, pool_id, num_sqbs); + if (err) + goto fail; + + /* Initialize pool context */ + err = otx2_pool_init(pfvf, pool_id, stack_pages, + num_sqbs, hw->sqb_size); + if (err) + goto fail; + } + + /* Flush accumulated messages */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + goto fail; + + /* Allocate pointers and free them to aura/pool */ + for (qidx = 0; qidx < hw->tx_queues; qidx++) { + pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx); + pool = &pfvf->qset.pool[pool_id]; + + sq = &qset->sq[qidx]; + sq->sqb_count = 0; + sq->sqb_ptrs = kcalloc(num_sqbs, sizeof(u64 *), GFP_KERNEL); + if (!sq->sqb_ptrs) + return -ENOMEM; + + for (ptr = 0; ptr < num_sqbs; ptr++) { + bufptr = otx2_alloc_rbuf(pfvf, pool, GFP_KERNEL); + if (bufptr <= 0) + return bufptr; + otx2_aura_freeptr(pfvf, pool_id, bufptr); + sq->sqb_ptrs[sq->sqb_count++] = (u64)bufptr; + } + otx2_get_page(pool); + } + + return 0; +fail: + otx2_mbox_reset(&pfvf->mbox.mbox, 0); + otx2_aura_pool_free(pfvf); + return err; +} + +int otx2_rq_aura_pool_init(struct otx2_nic *pfvf) +{ + struct otx2_hw *hw = &pfvf->hw; + int stack_pages, pool_id, rq; + struct otx2_pool *pool; + int err, ptr, num_ptrs; + s64 bufptr; + + num_ptrs = pfvf->qset.rqe_cnt; + + stack_pages = + (num_ptrs + hw->stack_pg_ptrs - 1) / hw->stack_pg_ptrs; + + for (rq = 0; rq < hw->rx_queues; rq++) { + pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_RQ, rq); + /* Initialize aura context */ + err = otx2_aura_init(pfvf, pool_id, pool_id, num_ptrs); + if (err) + goto fail; + } + for (pool_id = 0; pool_id < hw->rqpool_cnt; pool_id++) { + err = otx2_pool_init(pfvf, pool_id, stack_pages, + num_ptrs, RCV_FRAG_LEN); + if (err) + goto fail; + } + + /* Flush accumulated messages */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + goto fail; + + /* Allocate pointers and free them to aura/pool */ + for (pool_id = 0; pool_id < hw->rqpool_cnt; pool_id++) { + pool = &pfvf->qset.pool[pool_id]; + for (ptr = 0; ptr < num_ptrs; ptr++) { + bufptr = otx2_alloc_rbuf(pfvf, pool, GFP_KERNEL); + if (bufptr <= 0) + return bufptr; + otx2_aura_freeptr(pfvf, pool_id, + bufptr + OTX2_HEAD_ROOM); + } + otx2_get_page(pool); + } + + return 0; +fail: + otx2_mbox_reset(&pfvf->mbox.mbox, 0); + otx2_aura_pool_free(pfvf); + return err; +} + +int otx2_config_npa(struct otx2_nic *pfvf) +{ + struct otx2_qset *qset = &pfvf->qset; + struct npa_lf_alloc_req *npalf; + struct otx2_hw *hw = &pfvf->hw; + int aura_cnt, err; + + /* Pool - Stack of free buffer pointers + * Aura - Alloc/frees pointers from/to pool for NIX DMA. + */ + + if (!hw->pool_cnt) + return -EINVAL; + + qset->pool = devm_kzalloc(pfvf->dev, sizeof(struct otx2_pool) * + hw->pool_cnt, GFP_KERNEL); + if (!qset->pool) + return -ENOMEM; + + /* Get memory to put this msg */ + npalf = otx2_mbox_alloc_msg_npa_lf_alloc(&pfvf->mbox); + if (!npalf) + return -ENOMEM; + + /* Set aura and pool counts */ + npalf->nr_pools = hw->pool_cnt; + aura_cnt = ilog2(roundup_pow_of_two(hw->pool_cnt)); + npalf->aura_sz = (aura_cnt >= ilog2(128)) ? (aura_cnt - 6) : 1; + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + return err; + return 0; +} + +int otx2_detach_resources(struct mbox *mbox) +{ + struct rsrc_detach *detach; + + otx2_mbox_lock(mbox); + detach = otx2_mbox_alloc_msg_detach_resources(mbox); + if (!detach) { + otx2_mbox_unlock(mbox); + return -ENOMEM; + } + + /* detach all */ + detach->partial = false; + + /* Send detach request to AF */ + otx2_mbox_msg_send(&mbox->mbox, 0); + otx2_mbox_unlock(mbox); + return 0; +} +EXPORT_SYMBOL(otx2_detach_resources); + +int otx2_attach_npa_nix(struct otx2_nic *pfvf) +{ + struct rsrc_attach *attach; + struct msg_req *msix; + int err; + + otx2_mbox_lock(&pfvf->mbox); + /* Get memory to put this msg */ + attach = otx2_mbox_alloc_msg_attach_resources(&pfvf->mbox); + if (!attach) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + attach->npalf = true; + attach->nixlf = true; + + /* Send attach request to AF */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + otx2_mbox_unlock(&pfvf->mbox); + return err; + } + + pfvf->nix_blkaddr = BLKADDR_NIX0; + + /* If the platform has two NIX blocks then LF may be + * allocated from NIX1. + */ + if (otx2_read64(pfvf, RVU_PF_BLOCK_ADDRX_DISC(BLKADDR_NIX1)) & 0x1FFULL) + pfvf->nix_blkaddr = BLKADDR_NIX1; + + /* Get NPA and NIX MSIX vector offsets */ + msix = otx2_mbox_alloc_msg_msix_offset(&pfvf->mbox); + if (!msix) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + otx2_mbox_unlock(&pfvf->mbox); + return err; + } + otx2_mbox_unlock(&pfvf->mbox); + + if (pfvf->hw.npa_msixoff == MSIX_VECTOR_INVALID || + pfvf->hw.nix_msixoff == MSIX_VECTOR_INVALID) { + dev_err(pfvf->dev, + "RVUPF: Invalid MSIX vector offset for NPA/NIX\n"); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(otx2_attach_npa_nix); + +void otx2_ctx_disable(struct mbox *mbox, int type, bool npa) +{ + struct hwctx_disable_req *req; + + otx2_mbox_lock(mbox); + /* Request AQ to disable this context */ + if (npa) + req = otx2_mbox_alloc_msg_npa_hwctx_disable(mbox); + else + req = otx2_mbox_alloc_msg_nix_hwctx_disable(mbox); + + if (!req) { + otx2_mbox_unlock(mbox); + return; + } + + req->ctype = type; + + WARN_ON(otx2_sync_mbox_msg(mbox)); + otx2_mbox_unlock(mbox); +} + +int otx2_nix_config_bp(struct otx2_nic *pfvf, bool enable) +{ + struct nix_bp_cfg_req *req; + + if (enable) + req = otx2_mbox_alloc_msg_nix_bp_enable(&pfvf->mbox); + else + req = otx2_mbox_alloc_msg_nix_bp_disable(&pfvf->mbox); + + if (!req) + return -ENOMEM; + + req->chan_base = 0; + req->chan_cnt = 1; + req->bpid_per_chan = 0; + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +static inline void otx2_nix_rq_op_stats(struct queue_stats *stats, + struct otx2_nic *pfvf, int qidx) +{ + u64 incr = (u64)qidx << 32; + u64 *ptr; + + ptr = (u64 *)otx2_get_regaddr(pfvf, NIX_LF_RQ_OP_OCTS); + stats->bytes = otx2_atomic64_add(incr, ptr); + + ptr = (u64 *)otx2_get_regaddr(pfvf, NIX_LF_RQ_OP_PKTS); + stats->pkts = otx2_atomic64_add(incr, ptr); +} + +static inline void otx2_nix_sq_op_stats(struct queue_stats *stats, + struct otx2_nic *pfvf, int qidx) +{ + u64 incr = (u64)qidx << 32; + u64 *ptr; + + ptr = (u64 *)otx2_get_regaddr(pfvf, NIX_LF_SQ_OP_OCTS); + stats->bytes = otx2_atomic64_add(incr, ptr); + + ptr = (u64 *)otx2_get_regaddr(pfvf, NIX_LF_SQ_OP_PKTS); + stats->pkts = otx2_atomic64_add(incr, ptr); +} + +/* Mbox message handlers */ +void mbox_handler_cgx_stats(struct otx2_nic *pfvf, + struct cgx_stats_rsp *rsp) +{ + int id; + + for (id = 0; id < CGX_RX_STATS_COUNT; id++) + pfvf->hw.cgx_rx_stats[id] = rsp->rx_stats[id]; + for (id = 0; id < CGX_TX_STATS_COUNT; id++) + pfvf->hw.cgx_tx_stats[id] = rsp->tx_stats[id]; +} + +void mbox_handler_cgx_fec_stats(struct otx2_nic *pfvf, + struct cgx_fec_stats_rsp *rsp) +{ + pfvf->hw.cgx_fec_corr_blks += rsp->fec_corr_blks; + pfvf->hw.cgx_fec_uncorr_blks += rsp->fec_uncorr_blks; +} + +void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, + struct nix_txsch_alloc_rsp *rsp) +{ + int lvl, schq; + + /* Setup transmit scheduler list */ + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) + for (schq = 0; schq < rsp->schq[lvl]; schq++) + pf->hw.txschq_list[lvl][schq] = + rsp->schq_list[lvl][schq]; +} +EXPORT_SYMBOL(mbox_handler_nix_txsch_alloc); + +void mbox_handler_npa_lf_alloc(struct otx2_nic *pfvf, + struct npa_lf_alloc_rsp *rsp) +{ + pfvf->hw.stack_pg_ptrs = rsp->stack_pg_ptrs; + pfvf->hw.stack_pg_bytes = rsp->stack_pg_bytes; +} +EXPORT_SYMBOL(mbox_handler_npa_lf_alloc); + +void mbox_handler_nix_lf_alloc(struct otx2_nic *pfvf, + struct nix_lf_alloc_rsp *rsp) +{ + pfvf->hw.sqb_size = rsp->sqb_size; + pfvf->hw.rx_chan_base = rsp->rx_chan_base; + pfvf->hw.tx_chan_base = rsp->tx_chan_base; + pfvf->hw.lso_tsov4_idx = rsp->lso_tsov4_idx; + pfvf->hw.lso_tsov6_idx = rsp->lso_tsov6_idx; +} +EXPORT_SYMBOL(mbox_handler_nix_lf_alloc); + +void mbox_handler_msix_offset(struct otx2_nic *pfvf, + struct msix_offset_rsp *rsp) +{ + pfvf->hw.npa_msixoff = rsp->npa_msixoff; + pfvf->hw.nix_msixoff = rsp->nix_msixoff; +} +EXPORT_SYMBOL(mbox_handler_msix_offset); + +void mbox_handler_nix_bp_enable(struct otx2_nic *pfvf, + struct nix_bp_cfg_rsp *rsp) +{ + int chan, chan_id; + + for (chan = 0; chan < rsp->chan_cnt; chan++) { + chan_id = ((rsp->chan_bpid[chan] >> 10) & 0x7F); + pfvf->bpid[chan_id] = rsp->chan_bpid[chan] & 0x3FF; + } +} +EXPORT_SYMBOL(mbox_handler_nix_bp_enable); + +void otx2_free_cints(struct otx2_nic *pfvf, int n) +{ + struct otx2_qset *qset = &pfvf->qset; + struct otx2_hw *hw = &pfvf->hw; + int irq, qidx; + + for (qidx = 0, irq = hw->nix_msixoff + NIX_LF_CINT_VEC_START; + qidx < n; + qidx++, irq++) { + int vector = pci_irq_vector(pfvf->pdev, irq); + + irq_set_affinity_hint(vector, NULL); + free_cpumask_var(hw->affinity_mask[irq]); + free_irq(vector, &qset->napi[qidx]); + } +} + +void otx2_set_cints_affinity(struct otx2_nic *pfvf) +{ + struct otx2_hw *hw = &pfvf->hw; + int vec, cpu, irq, cint; + + vec = hw->nix_msixoff + NIX_LF_CINT_VEC_START; + cpu = cpumask_first(cpu_online_mask); + + /* CQ interrupts */ + for (cint = 0; cint < pfvf->hw.cint_cnt; cint++, vec++) { + if (!alloc_cpumask_var(&hw->affinity_mask[vec], GFP_KERNEL)) + return; + + cpumask_set_cpu(cpu, hw->affinity_mask[vec]); + + irq = pci_irq_vector(pfvf->pdev, vec); + irq_set_affinity_hint(irq, hw->affinity_mask[vec]); + + cpu = cpumask_next(cpu, cpu_online_mask); + if (unlikely(cpu >= nr_cpu_ids)) + cpu = 0; + } +} + +#define M(_name, _id, _fn_name, _req_type, _rsp_type) \ +int __weak \ +otx2_mbox_up_handler_ ## _fn_name(struct otx2_nic *pfvf, \ + struct _req_type *req, \ + struct _rsp_type *rsp) \ +{ \ + /* Nothing to do here */ \ + return 0; \ +} \ +EXPORT_SYMBOL(otx2_mbox_up_handler_ ## _fn_name); +MBOX_UP_CGX_MESSAGES +#undef M diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h new file mode 100644 index 000000000000..f5c1e792f26e --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -0,0 +1,730 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ + +#ifndef OTX2_COMMON_H +#define OTX2_COMMON_H + +#include <linux/pci.h> +#include <linux/ptp_clock_kernel.h> +#include <linux/timecounter.h> + +#include <mbox.h> +#include "otx2_reg.h" +#include "otx2_txrx.h" + +/* PCI device IDs */ +#define PCI_DEVID_OCTEONTX2_RVU_PF 0xA063 +#define PCI_DEVID_OCTEONTX2_RVU_VF 0xA064 +#define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8 + +#define PCI_SUBSYS_DEVID_96XX_RVU_PFVF 0xB200 +#define PCI_SUBSYS_DEVID_95XX_RVU_PFVF 0xB200 + +/* PCI BAR nos */ +#define PCI_CFG_REG_BAR_NUM 2 +#define PCI_MBOX_BAR_NUM 4 + +#define NAME_SIZE 32 + +enum arua_mapped_qtypes { + AURA_NIX_RQ, + AURA_NIX_SQ, +}; + +/* NIX LF interrupts range*/ +#define NIX_LF_QINT_VEC_START 0x00 +#define NIX_LF_CINT_VEC_START 0x40 +#define NIX_LF_GINT_VEC 0x80 +#define NIX_LF_ERR_VEC 0x81 +#define NIX_LF_POISON_VEC 0x82 + +/* RSS configuration */ +struct otx2_rss_info { + u8 enable; + u32 flowkey_cfg; + u16 rss_size; + u8 ind_tbl[MAX_RSS_INDIR_TBL_SIZE]; +#define RSS_HASH_KEY_SIZE 44 /* 352 bit key */ + u8 key[RSS_HASH_KEY_SIZE]; +}; + +/* NIX (or NPC) RX errors */ +enum otx2_errlvl { + NPC_ERRLVL_RE, + NPC_ERRLVL_LID_LA, + NPC_ERRLVL_LID_LB, + NPC_ERRLVL_LID_LC, + NPC_ERRLVL_LID_LD, + NPC_ERRLVL_LID_LE, + NPC_ERRLVL_LID_LF, + NPC_ERRLVL_LID_LG, + NPC_ERRLVL_LID_LH, + NPC_ERRLVL_NIX = 0x0F, +}; + +enum otx2_errcodes_re { + /* NPC_ERRLVL_RE errcodes */ + ERRCODE_FCS = 0x7, + ERRCODE_FCS_RCV = 0x8, + ERRCODE_UNDERSIZE = 0x10, + ERRCODE_OVERSIZE = 0x11, + ERRCODE_OL2_LEN_MISMATCH = 0x12, + /* NPC_ERRLVL_NIX errcodes */ + ERRCODE_OL3_LEN = 0x10, + ERRCODE_OL4_LEN = 0x11, + ERRCODE_OL4_CSUM = 0x12, + ERRCODE_IL3_LEN = 0x20, + ERRCODE_IL4_LEN = 0x21, + ERRCODE_IL4_CSUM = 0x22, +}; + +/* NIX TX stats */ +enum nix_stat_lf_tx { + TX_UCAST = 0x0, + TX_BCAST = 0x1, + TX_MCAST = 0x2, + TX_DROP = 0x3, + TX_OCTS = 0x4, + TX_STATS_ENUM_LAST, +}; + +/* NIX RX stats */ +enum nix_stat_lf_rx { + RX_OCTS = 0x0, + RX_UCAST = 0x1, + RX_BCAST = 0x2, + RX_MCAST = 0x3, + RX_DROP = 0x4, + RX_DROP_OCTS = 0x5, + RX_FCS = 0x6, + RX_ERR = 0x7, + RX_DRP_BCAST = 0x8, + RX_DRP_MCAST = 0x9, + RX_DRP_L3BCAST = 0xa, + RX_DRP_L3MCAST = 0xb, + RX_STATS_ENUM_LAST, +}; + +struct otx2_dev_stats { + u64 rx_bytes; + u64 rx_frames; + u64 rx_ucast_frames; + u64 rx_bcast_frames; + u64 rx_mcast_frames; + u64 rx_drops; + + u64 tx_bytes; + u64 tx_frames; + u64 tx_ucast_frames; + u64 tx_bcast_frames; + u64 tx_mcast_frames; + u64 tx_drops; +}; + +/* Driver counted stats */ +struct otx2_drv_stats { + atomic_t rx_fcs_errs; + atomic_t rx_oversize_errs; + atomic_t rx_undersize_errs; + atomic_t rx_csum_errs; + atomic_t rx_len_errs; + atomic_t rx_other_errs; +}; + +struct mbox { + struct otx2_mbox mbox; + struct work_struct mbox_wrk; + struct otx2_mbox mbox_up; + struct work_struct mbox_up_wrk; + struct otx2_nic *pfvf; + void *bbuf_base; /* Bounce buffer for mbox memory */ + atomic_t lock; /* serialize mailbox access */ + int num_msgs; /*mbox number of messages*/ + int up_num_msgs;/* mbox_up number of messages*/ +}; + +struct otx2_hw { + struct pci_dev *pdev; + struct otx2_rss_info rss_info; + u16 rx_queues; + u16 tx_queues; + u16 max_queues; + u16 pool_cnt; + u16 rqpool_cnt; + u16 sqpool_cnt; + + /* NPA */ + u32 stack_pg_ptrs; /* No of ptrs per stack page */ + u32 stack_pg_bytes; /* Size of stack page */ + u16 sqb_size; + + /* NIX */ + u16 txschq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; + + /* HW settings, coalescing etc */ + u16 rx_chan_base; + u16 tx_chan_base; + u16 cq_qcount_wait; + u16 cq_ecount_wait; + u16 rq_skid; + u8 cq_time_wait; + + /* For TSO segmentation */ + u8 lso_tsov4_idx; + u8 lso_tsov6_idx; + u8 hw_tso; + + /* MSI-X*/ + u8 cint_cnt; /* CQ interrupt count */ + u16 npa_msixoff; /* Offset of NPA vectors */ + u16 nix_msixoff; /* Offset of NIX vectors */ + char *irq_name; + cpumask_var_t *affinity_mask; + + /* Stats */ + struct otx2_dev_stats dev_stats; + struct otx2_drv_stats drv_stats; + u64 cgx_rx_stats[CGX_RX_STATS_COUNT]; + u64 cgx_tx_stats[CGX_TX_STATS_COUNT]; + u64 cgx_fec_corr_blks; + u64 cgx_fec_uncorr_blks; +}; + +struct otx2_ptp { + struct ptp_clock_info ptp_info; + struct ptp_clock *ptp_clock; + struct otx2_nic *nic; + + struct cyclecounter cycle_counter; + struct timecounter time_counter; + bool ptp_en; +}; + +struct otx2_vf_config { + struct otx2_nic *pf; + struct delayed_work link_event_work; + struct delayed_work ptp_info_work; + bool intf_down; /* interface was either configured or not */ + u8 mac[ETH_ALEN]; + u16 vlan; +}; + +struct flr_work { + struct work_struct work; + struct otx2_nic *pf; +}; + +struct refill_work { + struct delayed_work pool_refill_work; + struct otx2_nic *pf; +}; + +struct otx2_mac_table { + u8 addr[ETH_ALEN]; + u16 mcam_entry; + bool inuse; +}; + +struct otx2_nic { + void __iomem *reg_base; + struct net_device *netdev; + void *iommu_domain; + u16 xtra_hdr; + u16 max_frs; + +#define OTX2_FLAG_RX_TSTAMP_ENABLED BIT_ULL(0) +#define OTX2_FLAG_TX_TSTAMP_ENABLED BIT_ULL(1) +#define OTX2_FLAG_INTF_DOWN BIT_ULL(2) +#define OTX2_FLAG_MCAM_ENTRIES_ALLOC BIT_ULL(3) +#define OTX2_FLAG_NTUPLE_SUPPORT BIT_ULL(4) +#define OTX2_FLAG_UCAST_FLTR_SUPPORT BIT_ULL(5) +#define OTX2_FLAG_RX_VLAN_SUPPORT BIT_ULL(6) + u64 flags; + + struct otx2_qset qset; + struct otx2_hw hw; + struct pci_dev *pdev; + struct device *dev; + + /* Mbox */ + struct mbox mbox; + struct mbox *mbox_pfvf; + struct workqueue_struct *mbox_wq; + struct workqueue_struct *mbox_pfvf_wq; + + u8 total_vfs; + u16 pcifunc; /* RVU PF_FUNC */ + u16 bpid[NIX_MAX_BPID_CHAN]; + struct otx2_ptp *ptp; + struct otx2_vf_config *vf_configs; + struct cgx_link_user_info linfo; + + /* NPC MCAM */ + u32 nr_flows; + u32 ntuple_max_flows; + u16 entry_list[NPC_MAX_NONCONTIG_ENTRIES]; + struct list_head flows; + struct otx2_mac_table *mac_table; + + u64 reset_count; + struct work_struct reset_task; + struct workqueue_struct *flr_wq; + struct flr_work *flr_wrk; + struct refill_work *refill_wrk; + struct work_struct otx2_rx_mode_work; + struct workqueue_struct *otx2_ndo_wq; + + /* Ethtool stuff */ + u32 msg_enable; + +#define OTX2_PRIV_FLAG_PAM4 BIT(0) +#define OTX2_PRIV_FLAG_EDSA_HDR BIT(1) +#define OTX2_PRIV_FLAG_HIGIG2_HDR BIT(2) +#define OTX2_IS_EDSA_ENABLED(flags) ((flags) & \ + OTX2_PRIV_FLAG_EDSA_HDR) +#define OTX2_IS_HIGIG2_ENABLED(flags) ((flags) & \ + OTX2_PRIV_FLAG_HIGIG2_HDR) + u32 ethtool_flags; + + /* extended DSA and EDSA header lengths are 8/16 bytes + * so take max length 16 bytes here + */ +#define OTX2_EDSA_HDR_LEN 16 +#define OTX2_HIGIG2_HDR_LEN 16 + u32 addl_mtu; + /* Block address of NIX either BLKADDR_NIX0 or BLKADDR_NIX1 */ + int nix_blkaddr; +}; + +static inline bool is_otx2_lbkvf(struct pci_dev *pdev) +{ + return pdev->device == PCI_DEVID_OCTEONTX2_RVU_AFVF; +} + +static inline bool is_96xx_A0(struct pci_dev *pdev) +{ + return (pdev->revision == 0x00) && + (pdev->subsystem_device == PCI_SUBSYS_DEVID_96XX_RVU_PFVF); +} + +static inline bool is_95xx_A0(struct pci_dev *pdev) +{ + return ((pdev->revision == 0x10) || (pdev->revision == 0x11)) && + (pdev->subsystem_device == PCI_SUBSYS_DEVID_95XX_RVU_PFVF); +} + +static inline void otx2_setup_dev_hw_settings(struct otx2_nic *pfvf) +{ + struct otx2_hw *hw = &pfvf->hw; + + pfvf->hw.cq_time_wait = CQ_TIMER_THRESH_DEFAULT; + pfvf->hw.cq_ecount_wait = CQ_CQE_THRESH_DEFAULT; + pfvf->hw.cq_qcount_wait = CQ_QCOUNT_DEFAULT; + + hw->hw_tso = true; + + if (is_96xx_A0(pfvf->pdev) || is_95xx_A0(pfvf->pdev)) { + hw->hw_tso = false; + /* Due to HW issue previous silicons required minimum 600 + * unused CQE to avoid CQ overflow. + */ + pfvf->hw.rq_skid = 600; + pfvf->qset.rqe_cnt = Q_COUNT(Q_SIZE_1K); + } + if (is_96xx_A0(pfvf->pdev)) + pfvf->hw.cq_qcount_wait = 0x0; +} + +static inline void __iomem *otx2_get_regaddr(struct otx2_nic *nic, u64 offset) +{ + u64 blkaddr; + + switch ((offset >> RVU_FUNC_BLKADDR_SHIFT) & RVU_FUNC_BLKADDR_MASK) { + case BLKTYPE_NIX: + blkaddr = nic->nix_blkaddr; + break; + case BLKTYPE_NPA: + blkaddr = BLKADDR_NPA; + break; + default: + blkaddr = BLKADDR_RVUM; + break; + }; + + offset &= ~(RVU_FUNC_BLKADDR_MASK << RVU_FUNC_BLKADDR_SHIFT); + offset |= (blkaddr << RVU_FUNC_BLKADDR_SHIFT); + + return nic->reg_base + offset; +} + +/* Register read/write APIs */ +static inline void otx2_write64(struct otx2_nic *nic, u64 offset, u64 val) +{ + void __iomem *addr = otx2_get_regaddr(nic, offset); + + writeq(val, addr); +} + +static inline u64 otx2_read64(struct otx2_nic *nic, u64 offset) +{ + void __iomem *addr = otx2_get_regaddr(nic, offset); + + return readq(addr); +} + +/* Mbox bounce buffer APIs */ +static inline int otx2_mbox_bbuf_init(struct mbox *mbox, struct pci_dev *pdev) +{ + struct otx2_mbox_dev *mdev; + struct otx2_mbox *otx2_mbox; + + mbox->bbuf_base = devm_kmalloc(&pdev->dev, MBOX_SIZE, GFP_KERNEL); + if (!mbox->bbuf_base) + return -ENOMEM; + + /* Overwrite mbox mbase to point to bounce buffer, so that PF/VF + * prepare all mbox messages in bounce buffer instead of directly + * in hw mbox memory. + */ + otx2_mbox = &mbox->mbox; + mdev = &otx2_mbox->dev[0]; + mdev->mbase = mbox->bbuf_base; + + otx2_mbox = &mbox->mbox_up; + mdev = &otx2_mbox->dev[0]; + mdev->mbase = mbox->bbuf_base; + return 0; +} + +static inline void otx2_sync_mbox_bbuf(struct otx2_mbox *mbox, int devid) +{ + u16 msgs_offset = ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN); + void *hw_mbase = mbox->hwbase + (devid * MBOX_SIZE); + struct otx2_mbox_dev *mdev = &mbox->dev[devid]; + struct mbox_hdr *hdr; + u64 msg_size; + + if (mdev->mbase == hw_mbase) + return; + + hdr = hw_mbase + mbox->rx_start; + msg_size = hdr->msg_size; + + if (msg_size > mbox->rx_size - msgs_offset) + msg_size = mbox->rx_size - msgs_offset; + + /* Copy mbox messages from mbox memory to bounce buffer */ + memcpy(mdev->mbase + mbox->rx_start, + hw_mbase + mbox->rx_start, msg_size + msgs_offset); +} + +static inline void otx2_mbox_lock_init(struct mbox *mbox) +{ + atomic_set(&mbox->lock, 0); +} + +static inline void otx2_mbox_lock(struct mbox *mbox) +{ + while (!(atomic_add_return(1, &mbox->lock) == 1)) + cpu_relax(); +} + +static inline void otx2_mbox_unlock(struct mbox *mbox) +{ + atomic_set(&mbox->lock, 0); +} + +/* With the absence of API for 128-bit IO memory access for arm64, + * implement required operations at place. + */ +#ifdef __BIG_ENDIAN +#define otx2_high(high, low) (low) +#define otx2_low(high, low) (high) +#else +#define otx2_high(high, low) (high) +#define otx2_low(high, low) (low) +#endif + +#if defined(CONFIG_ARM64) +static inline void otx2_write128(u64 lo, u64 hi, void __iomem *addr) +{ + __asm__ volatile("stp %x[x0], %x[x1], [%x[p1],#0]!" + ::[x0]"r"(lo), [x1]"r"(hi), [p1]"r"(addr)); +} + +static inline __uint128_t otx2_read128(const void __iomem *addr) +{ + __uint128_t *__addr = (__force __uint128_t *)addr; + u64 h, l; + + __asm__ volatile("ldp %x[x0], %x[x1], %x[p1]" + : [x0]"=r"(l), [x1]"=r"(h) + : [p1]"Ump"(*__addr)); + + return (__uint128_t)le64_to_cpu(otx2_low(h, l)) | + (((__uint128_t)le64_to_cpu(otx2_high(h, l))) << 64); +} + +static inline u64 otx2_atomic64_add(u64 incr, u64 *ptr) +{ + u64 result; + + __asm__ volatile(".cpu generic+lse\n" + "ldadd %x[i], %x[r], [%[b]]" + : [r]"=r"(result), "+m"(*ptr) + : [i]"r"(incr), [b]"r"(ptr) + : "memory"); + return result; +} + +static inline u64 otx2_lmt_flush(uint64_t addr) +{ + u64 result = 0; + + __asm__ volatile(".cpu generic+lse\n" + "ldeor xzr,%x[rf],[%[rs]]" + : [rf]"=r"(result) + : [rs]"r"(addr)); + return result; +} +#else +#define otx2_write128(lo, hi, addr) +#define otx2_read128(addr) ({ 0; }) +#define otx2_atomic64_add(incr, ptr) ({ 0; }) +#define otx2_lmt_flush(addr) ({ 0; }) +#endif + +/* Alloc pointer from pool/aura */ +static inline u64 otx2_aura_allocptr(struct otx2_nic *pfvf, int aura) +{ + u64 *ptr = (u64 *)otx2_get_regaddr(pfvf, + NPA_LF_AURA_OP_ALLOCX(0)); + u64 incr = (u64)aura | BIT_ULL(63); + + return otx2_atomic64_add(incr, ptr); +} + +/* Free pointer to a pool/aura */ +static inline void otx2_aura_freeptr(struct otx2_nic *pfvf, + int aura, s64 buf) +{ + otx2_write128((u64)buf, (u64)aura | BIT_ULL(63), + otx2_get_regaddr(pfvf, NPA_LF_AURA_OP_FREE0)); +} + +/* Update page ref count */ +static inline void otx2_get_page(struct otx2_pool *pool) +{ + if (!pool->page) + return; + + if (pool->pageref) + page_ref_add(pool->page, pool->pageref); + pool->pageref = 0; + pool->page = NULL; +} + +static inline int otx2_get_pool_idx(struct otx2_nic *pfvf, int type, int idx) +{ + if (type == AURA_NIX_SQ) + return pfvf->hw.rqpool_cnt + idx; + + /* AURA_NIX_RQ */ + return idx; +} + +/* Mbox APIs */ +static inline int otx2_sync_mbox_msg(struct mbox *mbox) +{ + int err; + + if (!otx2_mbox_nonempty(&mbox->mbox, 0)) + return 0; + otx2_mbox_msg_send(&mbox->mbox, 0); + err = otx2_mbox_wait_for_rsp(&mbox->mbox, 0); + if (err) + return err; + + return otx2_mbox_check_rsp_msgs(&mbox->mbox, 0); +} + +static inline int otx2_sync_mbox_up_msg(struct mbox *mbox, int devid) +{ + int err; + + if (!otx2_mbox_nonempty(&mbox->mbox_up, devid)) + return 0; + otx2_mbox_msg_send(&mbox->mbox_up, devid); + err = otx2_mbox_wait_for_rsp(&mbox->mbox_up, devid); + if (err) + return err; + + return otx2_mbox_check_rsp_msgs(&mbox->mbox_up, devid); +} + +/* Use this API to send mbox msgs in atomic context + * where sleeping is not allowed + */ +static inline int otx2_sync_mbox_msg_busy_poll(struct mbox *mbox) +{ + int err; + + if (!otx2_mbox_nonempty(&mbox->mbox, 0)) + return 0; + otx2_mbox_msg_send(&mbox->mbox, 0); + err = otx2_mbox_busy_poll_for_rsp(&mbox->mbox, 0); + if (err) + return err; + + return otx2_mbox_check_rsp_msgs(&mbox->mbox, 0); +} + +#define M(_name, _id, _fn_name, _req_type, _rsp_type) \ +static struct _req_type __maybe_unused \ +*otx2_mbox_alloc_msg_ ## _fn_name(struct mbox *mbox) \ +{ \ + struct _req_type *req; \ + \ + req = (struct _req_type *)otx2_mbox_alloc_msg_rsp( \ + &mbox->mbox, 0, sizeof(struct _req_type), \ + sizeof(struct _rsp_type)); \ + if (!req) \ + return NULL; \ + req->hdr.sig = OTX2_MBOX_REQ_SIG; \ + req->hdr.id = _id; \ + return req; \ +} + +MBOX_MESSAGES +#undef M + +#define M(_name, _id, _fn_name, _req_type, _rsp_type) \ +int \ +otx2_mbox_up_handler_ ## _fn_name(struct otx2_nic *pfvf, \ + struct _req_type *req, \ + struct _rsp_type *rsp); \ + +MBOX_UP_CGX_MESSAGES +#undef M + +/* Time to wait before watchdog kicks off. + * Due to PSE deadlock errata, XOFF on TL2 transmission + * queues takes more time than default watchdog timeout. + * Hence setting this value higher. + */ +#define OTX2_TX_TIMEOUT (100000 * HZ) + +#define RVU_PFVF_PF_SHIFT 10 +#define RVU_PFVF_PF_MASK 0x3F +#define RVU_PFVF_FUNC_SHIFT 0 +#define RVU_PFVF_FUNC_MASK 0x3FF + +static inline int rvu_get_pf(u16 pcifunc) +{ + return (pcifunc >> RVU_PFVF_PF_SHIFT) & RVU_PFVF_PF_MASK; +} + +/* MSI-X APIs */ +void otx2_free_cints(struct otx2_nic *pfvf, int n); +void otx2_set_cints_affinity(struct otx2_nic *pfvf); + +int otx2_hw_set_mac_addr(struct otx2_nic *pfvf, struct net_device *netdev); +int otx2_set_mac_address(struct net_device *netdev, void *p); +int otx2_hw_set_mtu(struct otx2_nic *pfvf, int mtu); +void otx2_tx_timeout(struct net_device *netdev); +void otx2_get_mac_from_af(struct net_device *netdev); +void otx2_config_irq_coalescing(struct otx2_nic *pfvf, int qidx); + +/* RVU block related APIs */ +int otx2_attach_npa_nix(struct otx2_nic *pfvf); +int otx2_detach_resources(struct mbox *mbox); +int otx2_config_npa(struct otx2_nic *pfvf); +int otx2_sq_aura_pool_init(struct otx2_nic *pfvf); +int otx2_rq_aura_pool_init(struct otx2_nic *pfvf); +void otx2_aura_pool_free(struct otx2_nic *pfvf); +void otx2_free_aura_ptr(struct otx2_nic *pfvf, int type); +void otx2_sq_free_sqbs(struct otx2_nic *pfvf); +int otx2_config_nix(struct otx2_nic *pfvf); +int otx2_config_nix_queues(struct otx2_nic *pfvf); +int otx2_txschq_config(struct otx2_nic *pfvf, int lvl); +int otx2_txsch_alloc(struct otx2_nic *pfvf); +int otx2_txschq_stop(struct otx2_nic *pfvf); +dma_addr_t otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, + gfp_t gfp); +int otx2_rxtx_enable(struct otx2_nic *pfvf, bool enable); +void otx2_ctx_disable(struct mbox *mbox, int type, bool npa); +int otx2_nix_config_bp(struct otx2_nic *pfvf, bool enable); +void otx2_cleanup_rx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); +void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); + +/* RSS configuration APIs*/ +int otx2_rss_init(struct otx2_nic *pfvf); +int otx2_set_flowkey_cfg(struct otx2_nic *pfvf); +void otx2_set_rss_key(struct otx2_nic *pfvf); +int otx2_set_rss_table(struct otx2_nic *pfvf); + +/* Mbox handlers */ +void mbox_handler_msix_offset(struct otx2_nic *pfvf, + struct msix_offset_rsp *rsp); +void mbox_handler_npa_lf_alloc(struct otx2_nic *pfvf, + struct npa_lf_alloc_rsp *rsp); +void mbox_handler_nix_lf_alloc(struct otx2_nic *pfvf, + struct nix_lf_alloc_rsp *rsp); +void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, + struct nix_txsch_alloc_rsp *rsp); +void mbox_handler_cgx_stats(struct otx2_nic *pfvf, + struct cgx_stats_rsp *rsp); +void mbox_handler_cgx_fec_stats(struct otx2_nic *pfvf, + struct cgx_fec_stats_rsp *rsp); +void otx2_set_fec_stats_count(struct otx2_nic *pfvf); +void mbox_handler_nix_bp_enable(struct otx2_nic *pfvf, + struct nix_bp_cfg_rsp *rsp); + +/* Device stats APIs */ +void otx2_get_dev_stats(struct otx2_nic *pfvf); +void otx2_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats); +void otx2_update_lmac_stats(struct otx2_nic *pfvf); +void otx2_update_lmac_fec_stats(struct otx2_nic *pfvf); +int otx2_update_rq_stats(struct otx2_nic *pfvf, int qidx); +int otx2_update_sq_stats(struct otx2_nic *pfvf, int qidx); +void otx2_set_ethtool_ops(struct net_device *netdev); +void otx2vf_set_ethtool_ops(struct net_device *netdev); + +int otx2_open(struct net_device *netdev); +int otx2_stop(struct net_device *netdev); +int otx2vf_open(struct net_device *netdev); +int otx2vf_stop(struct net_device *netdev); +int otx2_set_real_num_queues(struct net_device *netdev, + int tx_queues, int rx_queues); +int otx2_set_npc_parse_mode(struct otx2_nic *pfvf); + +/* MCAM filter related APIs */ +void otx2_do_set_rx_mode(struct work_struct *work); +int otx2_add_macfilter(struct net_device *netdev, const u8 *mac); +int otx2_mcam_flow_init(struct otx2_nic *pf); +int otx2_del_macfilter(struct net_device *netdev, const u8 *mac); +void otx2_mcam_flow_del(struct otx2_nic *pf); +int otx2_destroy_ntuple_flows(struct otx2_nic *pf); +int otx2_destroy_mcam_flows(struct otx2_nic *pfvf); +int otx2_get_flow(struct otx2_nic *pfvf, + struct ethtool_rxnfc *nfc, u32 location); +int otx2_get_all_flows(struct otx2_nic *pfvf, + struct ethtool_rxnfc *nfc, u32 *rule_locs); +int otx2_add_flow(struct otx2_nic *pfvf, + struct ethtool_rx_flow_spec *fsp); +int otx2_remove_flow(struct otx2_nic *pfvf, u32 location); +int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp, + struct npc_install_flow_req *req); +int otx2_enable_rxvlan(struct otx2_nic *pf, bool enable); +int otx2smqvf_probe(struct otx2_nic *vf); +int otx2smqvf_remove(struct otx2_nic *vf); + +#endif /* OTX2_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c new file mode 100644 index 000000000000..e2cb70a79dbe --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -0,0 +1,1565 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/pci.h> +#include <linux/net_tstamp.h> +#include <linux/ethtool.h> +#include <linux/stddef.h> +#include <linux/etherdevice.h> +#include <linux/log2.h> + +#include "otx2_common.h" +#include "otx2_ptp.h" + +#define DRV_NAME "octeontx2-nicpf" +#define DRV_VERSION "1.0" +#define DRV_VF_NAME "octeontx2-nicvf" +#define DRV_VF_VERSION "1.0" + +#define OTX2_DEFAULT_ACTION 0x1 + +static struct cgx_fw_data *otx2_get_fwdata(struct otx2_nic *pfvf); + +static const char otx2_priv_flags_strings[][ETH_GSTRING_LEN] = { + "pam4", + "edsa", + "higig2", +}; + +struct otx2_stat { + char name[ETH_GSTRING_LEN]; + unsigned int index; +}; + +/* HW device stats */ +#define OTX2_DEV_STAT(stat) { \ + .name = #stat, \ + .index = offsetof(struct otx2_dev_stats, stat) / sizeof(u64), \ +} + +#define OTX2_ETHTOOL_SUPPORTED_MODES 0x638CE23 //110001110001100111000100011 +#define OTX2_ETHTOOL_ALL_MODES (ULLONG_MAX) + +static const struct otx2_stat otx2_dev_stats[] = { + OTX2_DEV_STAT(rx_bytes), + OTX2_DEV_STAT(rx_frames), + OTX2_DEV_STAT(rx_ucast_frames), + OTX2_DEV_STAT(rx_bcast_frames), + OTX2_DEV_STAT(rx_mcast_frames), + OTX2_DEV_STAT(rx_drops), + + OTX2_DEV_STAT(tx_bytes), + OTX2_DEV_STAT(tx_frames), + OTX2_DEV_STAT(tx_ucast_frames), + OTX2_DEV_STAT(tx_bcast_frames), + OTX2_DEV_STAT(tx_mcast_frames), + OTX2_DEV_STAT(tx_drops), +}; + +/* Driver level stats */ +#define OTX2_DRV_STAT(stat) { \ + .name = #stat, \ + .index = offsetof(struct otx2_drv_stats, stat) / sizeof(atomic_t), \ +} + +static const struct otx2_stat otx2_drv_stats[] = { + OTX2_DRV_STAT(rx_fcs_errs), + OTX2_DRV_STAT(rx_oversize_errs), + OTX2_DRV_STAT(rx_undersize_errs), + OTX2_DRV_STAT(rx_csum_errs), + OTX2_DRV_STAT(rx_len_errs), + OTX2_DRV_STAT(rx_other_errs), +}; + +static const struct otx2_stat otx2_queue_stats[] = { + { "bytes", 0 }, + { "frames", 1 }, +}; + +static const unsigned int otx2_n_dev_stats = ARRAY_SIZE(otx2_dev_stats); +static const unsigned int otx2_n_drv_stats = ARRAY_SIZE(otx2_drv_stats); +static const unsigned int otx2_n_queue_stats = ARRAY_SIZE(otx2_queue_stats); + +int __weak otx2vf_open(struct net_device *netdev) +{ + return 0; +} + +int __weak otx2vf_stop(struct net_device *netdev) +{ + return 0; +} + +static void otx2_dev_open(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + if (pfvf->pcifunc & RVU_PFVF_FUNC_MASK) + otx2vf_open(netdev); + else + otx2_open(netdev); +} + +static void otx2_dev_stop(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + if (pfvf->pcifunc & RVU_PFVF_FUNC_MASK) + otx2vf_stop(netdev); + else + otx2_stop(netdev); +} + +static void otx2_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(pfvf->pdev), sizeof(info->bus_info)); +} + +static void otx2_get_qset_strings(struct otx2_nic *pfvf, u8 **data, int qset) +{ + int start_qidx = qset * pfvf->hw.rx_queues; + int qidx, stats; + + for (qidx = 0; qidx < pfvf->hw.rx_queues; qidx++) { + for (stats = 0; stats < otx2_n_queue_stats; stats++) { + sprintf(*data, "rxq%d: %s", qidx + start_qidx, + otx2_queue_stats[stats].name); + *data += ETH_GSTRING_LEN; + } + } + for (qidx = 0; qidx < pfvf->hw.tx_queues; qidx++) { + for (stats = 0; stats < otx2_n_queue_stats; stats++) { + sprintf(*data, "txq%d: %s", qidx + start_qidx, + otx2_queue_stats[stats].name); + *data += ETH_GSTRING_LEN; + } + } +} + +static void otx2_get_strings(struct net_device *netdev, u32 sset, u8 *data) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + int stats; + + if (sset == ETH_SS_PRIV_FLAGS) { + memcpy(data, otx2_priv_flags_strings, + ARRAY_SIZE(otx2_priv_flags_strings) * ETH_GSTRING_LEN); + return; + } + + if (sset != ETH_SS_STATS) + return; + + for (stats = 0; stats < otx2_n_dev_stats; stats++) { + memcpy(data, otx2_dev_stats[stats].name, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + + for (stats = 0; stats < otx2_n_drv_stats; stats++) { + memcpy(data, otx2_drv_stats[stats].name, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + + otx2_get_qset_strings(pfvf, &data, 0); + + for (stats = 0; stats < CGX_RX_STATS_COUNT; stats++) { + sprintf(data, "cgx_rxstat%d: ", stats); + data += ETH_GSTRING_LEN; + } + + for (stats = 0; stats < CGX_TX_STATS_COUNT; stats++) { + sprintf(data, "cgx_txstat%d: ", stats); + data += ETH_GSTRING_LEN; + } + + strcpy(data, "reset_count"); + data += ETH_GSTRING_LEN; + if (pfvf->linfo.fec) { + sprintf(data, "Fec Corrected Errors: "); + data += ETH_GSTRING_LEN; + sprintf(data, "Fec Uncorrected Errors: "); + data += ETH_GSTRING_LEN; + } +} + +static void otx2_get_qset_stats(struct otx2_nic *pfvf, + struct ethtool_stats *stats, u64 **data) +{ + int stat, qidx; + + if (!pfvf) + return; + for (qidx = 0; qidx < pfvf->hw.rx_queues; qidx++) { + if (!otx2_update_rq_stats(pfvf, qidx)) { + for (stat = 0; stat < otx2_n_queue_stats; stat++) + *((*data)++) = 0; + continue; + } + for (stat = 0; stat < otx2_n_queue_stats; stat++) + *((*data)++) = ((u64 *)&pfvf->qset.rq[qidx].stats) + [otx2_queue_stats[stat].index]; + } + + for (qidx = 0; qidx < pfvf->hw.tx_queues; qidx++) { + if (!otx2_update_sq_stats(pfvf, qidx)) { + for (stat = 0; stat < otx2_n_queue_stats; stat++) + *((*data)++) = 0; + continue; + } + for (stat = 0; stat < otx2_n_queue_stats; stat++) + *((*data)++) = ((u64 *)&pfvf->qset.sq[qidx].stats) + [otx2_queue_stats[stat].index]; + } +} + +static int otx2_get_phy_fec_stats(struct otx2_nic *pfvf) +{ + struct msg_req *req; + int rc = -EAGAIN; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_cgx_get_phy_fec_stats(&pfvf->mbox); + if (!req) + goto end; + + if (!otx2_sync_mbox_msg(&pfvf->mbox)) + rc = 0; +end: + otx2_mbox_unlock(&pfvf->mbox); + return rc; +} + +/* Get device and per queue statistics */ +static void otx2_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + u64 fec_corr_blks, fec_uncorr_blks; + struct cgx_fw_data *rsp; + int stat; + + otx2_get_dev_stats(pfvf); + for (stat = 0; stat < otx2_n_dev_stats; stat++) + *(data++) = ((u64 *)&pfvf->hw.dev_stats) + [otx2_dev_stats[stat].index]; + + for (stat = 0; stat < otx2_n_drv_stats; stat++) + *(data++) = atomic_read(&((atomic_t *)&pfvf->hw.drv_stats) + [otx2_drv_stats[stat].index]); + + otx2_get_qset_stats(pfvf, stats, &data); + otx2_update_lmac_stats(pfvf); + for (stat = 0; stat < CGX_RX_STATS_COUNT; stat++) + *(data++) = pfvf->hw.cgx_rx_stats[stat]; + for (stat = 0; stat < CGX_TX_STATS_COUNT; stat++) + *(data++) = pfvf->hw.cgx_tx_stats[stat]; + *(data++) = pfvf->reset_count; + + if (pfvf->linfo.fec == OTX2_FEC_NONE) + return; + + fec_corr_blks = pfvf->hw.cgx_fec_corr_blks; + fec_uncorr_blks = pfvf->hw.cgx_fec_uncorr_blks; + + rsp = otx2_get_fwdata(pfvf); + if (!IS_ERR(rsp) && rsp->fwdata.phy.misc.has_fec_stats && + !otx2_get_phy_fec_stats(pfvf)) { + /* Fetch fwdata again because it's been recently populated with + * latest PHY FEC stats. + */ + rsp = otx2_get_fwdata(pfvf); + if (!IS_ERR(rsp)) { + struct fec_stats_s *p = &rsp->fwdata.phy.fec_stats; + + if (pfvf->linfo.fec == OTX2_FEC_BASER) { + fec_corr_blks = p->brfec_corr_blks; + fec_uncorr_blks = p->brfec_uncorr_blks; + } else { + fec_corr_blks = p->rsfec_corr_cws; + fec_uncorr_blks = p->rsfec_uncorr_cws; + } + } + } + + *(data++) = fec_corr_blks; + *(data++) = fec_uncorr_blks; +} + +static int otx2_get_sset_count(struct net_device *netdev, int sset) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + int qstats_count, fec_stats_count = 0; + bool if_up = netif_running(netdev); + + if (sset == ETH_SS_PRIV_FLAGS) + return ARRAY_SIZE(otx2_priv_flags_strings); + + if (sset != ETH_SS_STATS) + return -EINVAL; + + qstats_count = otx2_n_queue_stats * + (pfvf->hw.rx_queues + pfvf->hw.tx_queues); + + if (!if_up || !pfvf->linfo.fec) { + return otx2_n_dev_stats + otx2_n_drv_stats + qstats_count + + CGX_RX_STATS_COUNT + CGX_TX_STATS_COUNT + 1; + } + fec_stats_count = 2; + otx2_update_lmac_fec_stats(pfvf); + return otx2_n_dev_stats + otx2_n_drv_stats + qstats_count + + CGX_RX_STATS_COUNT + CGX_TX_STATS_COUNT + 1 + + fec_stats_count; +} + +/* Get no of queues device supports and current queue count */ +static void otx2_get_channels(struct net_device *dev, + struct ethtool_channels *channel) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + + memset(channel, 0, sizeof(*channel)); + channel->max_rx = pfvf->hw.max_queues; + channel->max_tx = pfvf->hw.max_queues; + + channel->rx_count = pfvf->hw.rx_queues; + channel->tx_count = pfvf->hw.tx_queues; +} + +/* Set no of Tx, Rx queues to be used */ +static int otx2_set_channels(struct net_device *dev, + struct ethtool_channels *channel) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + bool if_up = netif_running(dev); + int err = 0; + + if (!channel->rx_count || !channel->tx_count) + return -EINVAL; + if (channel->rx_count > pfvf->hw.max_queues) + return -EINVAL; + if (channel->tx_count > pfvf->hw.max_queues) + return -EINVAL; + + if (if_up) + otx2_dev_stop(dev); + + pfvf->hw.rx_queues = channel->rx_count; + pfvf->hw.tx_queues = channel->tx_count; + err = otx2_set_real_num_queues(dev, pfvf->hw.tx_queues, + pfvf->hw.rx_queues); + pfvf->qset.cq_cnt = pfvf->hw.tx_queues + pfvf->hw.rx_queues; + if (err) + return err; + + if (if_up) + otx2_dev_open(dev); + + netdev_info(dev, "Setting num Tx rings to %d, Rx rings to %d success\n", + pfvf->hw.tx_queues, pfvf->hw.rx_queues); + + return err; +} + +static void otx2_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct cgx_pause_frm_cfg *req, *rsp; + + req = otx2_mbox_alloc_msg_cgx_cfg_pause_frm(&pfvf->mbox); + if (!req) + return; + + if (!otx2_sync_mbox_msg(&pfvf->mbox)) { + rsp = (struct cgx_pause_frm_cfg *) + otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr); + pause->rx_pause = rsp->rx_pause; + pause->tx_pause = rsp->tx_pause; + } +} + +static int otx2_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct cgx_pause_frm_cfg *req; + + if (pause->autoneg) + return -EOPNOTSUPP; + + req = otx2_mbox_alloc_msg_cgx_cfg_pause_frm(&pfvf->mbox); + if (!req) + return -EAGAIN; + + req->set = 1; + req->rx_pause = pause->rx_pause; + req->tx_pause = pause->tx_pause; + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +static void otx2_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct otx2_qset *qs = &pfvf->qset; + + ring->rx_max_pending = Q_COUNT(Q_SIZE_MAX); + ring->rx_pending = qs->rqe_cnt ? qs->rqe_cnt : Q_COUNT(Q_SIZE_256); + ring->tx_max_pending = Q_COUNT(Q_SIZE_MAX); + ring->tx_pending = qs->sqe_cnt ? qs->sqe_cnt : Q_COUNT(Q_SIZE_4K); +} + +static int otx2_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + bool if_up = netif_running(netdev); + struct otx2_qset *qs = &pfvf->qset; + u32 rx_count, tx_count; + + if (ring->rx_mini_pending || ring->rx_jumbo_pending) + return -EINVAL; + + /* Permitted lengths are 16 64 256 1K 4K 16K 64K 256K 1M */ + rx_count = clamp_t(u32, ring->rx_pending, + Q_COUNT(Q_SIZE_MIN), Q_COUNT(Q_SIZE_MAX)); + /* On some silicon variants a skid or reserved CQEs are + * needed to avoid CQ overflow. + */ + if (rx_count < pfvf->hw.rq_skid) + rx_count = pfvf->hw.rq_skid; + rx_count = Q_COUNT(Q_SIZE(rx_count, 3)); + + /* Due pipelining impact minimum 2000 unused SQ CQE's + * need to maintain to avoid CQ overflow, hence the + * minimum 4K size. + */ + tx_count = clamp_t(u32, ring->tx_pending, + Q_COUNT(Q_SIZE_4K), Q_COUNT(Q_SIZE_MAX)); + tx_count = Q_COUNT(Q_SIZE(tx_count, 3)); + + if (tx_count == qs->sqe_cnt && rx_count == qs->rqe_cnt) + return 0; + + if (if_up) + otx2_dev_stop(netdev); + + /* Assigned to the nearest possible exponent. */ + qs->sqe_cnt = tx_count; + qs->rqe_cnt = rx_count; + + if (if_up) + otx2_dev_open(netdev); + return 0; +} + +static int otx2_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *cmd) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct otx2_hw *hw = &pfvf->hw; + + cmd->rx_coalesce_usecs = hw->cq_time_wait; + cmd->rx_max_coalesced_frames = hw->cq_ecount_wait; + cmd->tx_coalesce_usecs = hw->cq_time_wait; + cmd->tx_max_coalesced_frames = hw->cq_ecount_wait; + + return 0; +} + +static int otx2_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct otx2_hw *hw = &pfvf->hw; + int qidx; + + if (ec->use_adaptive_rx_coalesce || ec->use_adaptive_tx_coalesce || + ec->rx_coalesce_usecs_irq || ec->rx_max_coalesced_frames_irq || + ec->tx_coalesce_usecs_irq || ec->tx_max_coalesced_frames_irq || + ec->stats_block_coalesce_usecs || ec->pkt_rate_low || + ec->rx_coalesce_usecs_low || ec->rx_max_coalesced_frames_low || + ec->tx_coalesce_usecs_low || ec->tx_max_coalesced_frames_low || + ec->pkt_rate_high || ec->rx_coalesce_usecs_high || + ec->rx_max_coalesced_frames_high || ec->tx_coalesce_usecs_high || + ec->tx_max_coalesced_frames_high || ec->rate_sample_interval) + return -EOPNOTSUPP; + + if (!ec->rx_max_coalesced_frames || !ec->tx_max_coalesced_frames) + return 0; + + /* 'cq_time_wait' is 8bit and is in multiple of 100ns, + * so clamp the user given value to the range of 1 to 25usec. + */ + ec->rx_coalesce_usecs = clamp_t(u32, ec->rx_coalesce_usecs, + 1, CQ_TIMER_THRESH_MAX); + ec->tx_coalesce_usecs = clamp_t(u32, ec->tx_coalesce_usecs, + 1, CQ_TIMER_THRESH_MAX); + + /* Rx and Tx are mapped to same CQ, check which one + * is changed, if both then choose the min. + */ + if (hw->cq_time_wait == ec->rx_coalesce_usecs) + hw->cq_time_wait = ec->tx_coalesce_usecs; + else if (hw->cq_time_wait == ec->tx_coalesce_usecs) + hw->cq_time_wait = ec->rx_coalesce_usecs; + else + hw->cq_time_wait = min_t(u8, ec->rx_coalesce_usecs, + ec->tx_coalesce_usecs); + + /* Max ecount_wait supported is 16bit, + * so clamp the user given value to the range of 1 to 64k. + */ + ec->rx_max_coalesced_frames = clamp_t(u32, ec->rx_max_coalesced_frames, + 1, U16_MAX); + ec->tx_max_coalesced_frames = clamp_t(u32, ec->tx_max_coalesced_frames, + 1, U16_MAX); + + /* Rx and Tx are mapped to same CQ, check which one + * is changed, if both then choose the min. + */ + if (hw->cq_ecount_wait == ec->rx_max_coalesced_frames) + hw->cq_ecount_wait = ec->tx_max_coalesced_frames; + else if (hw->cq_ecount_wait == ec->tx_max_coalesced_frames) + hw->cq_ecount_wait = ec->rx_max_coalesced_frames; + else + hw->cq_ecount_wait = min_t(u16, ec->rx_max_coalesced_frames, + ec->tx_max_coalesced_frames); + + if (netif_running(netdev)) { + for (qidx = 0; qidx < pfvf->hw.cint_cnt; qidx++) + otx2_config_irq_coalescing(pfvf, qidx); + } + + return 0; +} + +static int otx2_get_rss_hash_opts(struct otx2_nic *pfvf, + struct ethtool_rxnfc *nfc) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + + if (!(rss->flowkey_cfg & + (NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6))) + return 0; + + /* Mimimum is IPv4 and IPv6, SIP/DIP */ + nfc->data = RXH_IP_SRC | RXH_IP_DST; + + switch (nfc->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + if (rss->flowkey_cfg & NIX_FLOW_KEY_TYPE_TCP) + nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; + case UDP_V4_FLOW: + case UDP_V6_FLOW: + if (rss->flowkey_cfg & NIX_FLOW_KEY_TYPE_UDP) + nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; + case SCTP_V4_FLOW: + case SCTP_V6_FLOW: + if (rss->flowkey_cfg & NIX_FLOW_KEY_TYPE_SCTP) + nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; + case AH_ESP_V4_FLOW: + case AH_V4_FLOW: + case ESP_V4_FLOW: + case IPV4_FLOW: + case AH_ESP_V6_FLOW: + case AH_V6_FLOW: + case ESP_V6_FLOW: + case IPV6_FLOW: + break; + default: + return -EINVAL; + } + return 0; +} + +static int otx2_set_rss_hash_opts(struct otx2_nic *pfvf, + struct ethtool_rxnfc *nfc) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + u32 rss_cfg = rss->flowkey_cfg; + u32 rxh_l4 = RXH_L4_B_0_1 | RXH_L4_B_2_3; + + if (!rss->enable) + netdev_err(pfvf->netdev, "RSS is disabled, cmd ignored\n"); + + /* Mimimum is IPv4 and IPv6, SIP/DIP */ + if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) + return -EINVAL; + + switch (nfc->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + /* Different config for v4 and v6 is not supported. + * Both of them have to be either 4-tuple or 2-tuple. + */ + if ((nfc->data & rxh_l4) == rxh_l4) + rss_cfg |= NIX_FLOW_KEY_TYPE_TCP; + else + rss_cfg &= ~NIX_FLOW_KEY_TYPE_TCP; + break; + case UDP_V4_FLOW: + case UDP_V6_FLOW: + if ((nfc->data & rxh_l4) == rxh_l4) + rss_cfg |= NIX_FLOW_KEY_TYPE_UDP; + else + rss_cfg &= ~NIX_FLOW_KEY_TYPE_UDP; + break; + case SCTP_V4_FLOW: + case SCTP_V6_FLOW: + if ((nfc->data & rxh_l4) == rxh_l4) + rss_cfg |= NIX_FLOW_KEY_TYPE_SCTP; + else + rss_cfg &= ~NIX_FLOW_KEY_TYPE_SCTP; + break; + case AH_ESP_V4_FLOW: + case AH_V4_FLOW: + case ESP_V4_FLOW: + case IPV4_FLOW: + case AH_ESP_V6_FLOW: + case AH_V6_FLOW: + case ESP_V6_FLOW: + case IPV6_FLOW: + rss_cfg = NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6; + break; + default: + return -EINVAL; + } + + rss->flowkey_cfg = rss_cfg; + otx2_set_flowkey_cfg(pfvf); + return 0; +} + +static int otx2_get_rxnfc(struct net_device *dev, + struct ethtool_rxnfc *nfc, u32 *rules) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + switch (nfc->cmd) { + case ETHTOOL_GRXRINGS: + nfc->data = pfvf->hw.rx_queues; + ret = 0; + break; + case ETHTOOL_GRXCLSRLCNT: + nfc->rule_cnt = pfvf->nr_flows; + ret = 0; + break; + case ETHTOOL_GRXCLSRULE: + ret = otx2_get_flow(pfvf, nfc, nfc->fs.location); + break; + case ETHTOOL_GRXCLSRLALL: + ret = otx2_get_all_flows(pfvf, nfc, rules); + break; + case ETHTOOL_GRXFH: + return otx2_get_rss_hash_opts(pfvf, nfc); + default: + break; + } + return ret; +} + +int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp, + struct npc_install_flow_req *req) +{ + struct ethtool_tcpip4_spec *l4_mask = &fsp->m_u.tcp_ip4_spec; + struct ethtool_tcpip4_spec *l4_hdr = &fsp->h_u.tcp_ip4_spec; + struct ethhdr *eth_mask = &fsp->m_u.ether_spec; + struct ethhdr *eth_hdr = &fsp->h_u.ether_spec; + struct flow_msg *pmask = &req->mask; + struct flow_msg *pkt = &req->packet; + u32 flow_type; + + flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); + switch (flow_type) { + /* bits not set in mask are don't care */ + case ETHER_FLOW: + if (!is_zero_ether_addr(eth_mask->h_source)) { + ether_addr_copy(pkt->smac, eth_hdr->h_source); + ether_addr_copy(pmask->smac, eth_mask->h_source); + req->features |= BIT_ULL(NPC_SMAC); + } + if (!is_zero_ether_addr(eth_mask->h_dest)) { + ether_addr_copy(pkt->dmac, eth_hdr->h_dest); + ether_addr_copy(pmask->dmac, eth_mask->h_dest); + req->features |= BIT_ULL(NPC_DMAC); + } + if (eth_mask->h_proto) { + memcpy(&pkt->etype, ð_hdr->h_proto, + sizeof(pkt->etype)); + memcpy(&pmask->etype, ð_mask->h_proto, + sizeof(pmask->etype)); + req->features |= BIT_ULL(NPC_ETYPE); + } + break; + case TCP_V4_FLOW: + case UDP_V4_FLOW: + if (l4_mask->ip4src) { + memcpy(&pkt->ip4src, &l4_hdr->ip4src, + sizeof(pkt->ip4src)); + memcpy(&pmask->ip4src, &l4_mask->ip4src, + sizeof(pmask->ip4src)); + req->features |= BIT_ULL(NPC_SIP_IPV4); + } + if (l4_mask->ip4dst) { + memcpy(&pkt->ip4dst, &l4_hdr->ip4dst, + sizeof(pkt->ip4dst)); + memcpy(&pmask->ip4dst, &l4_mask->ip4dst, + sizeof(pmask->ip4dst)); + req->features |= BIT_ULL(NPC_DIP_IPV4); + } + if (l4_mask->psrc) { + memcpy(&pkt->sport, &l4_hdr->psrc, sizeof(pkt->sport)); + memcpy(&pmask->sport, &l4_mask->psrc, + sizeof(pmask->sport)); + if (flow_type == UDP_V4_FLOW) + req->features |= BIT_ULL(NPC_SPORT_UDP); + else + req->features |= BIT_ULL(NPC_SPORT_TCP); + } + if (l4_mask->pdst) { + memcpy(&pkt->dport, &l4_hdr->pdst, sizeof(pkt->dport)); + memcpy(&pmask->dport, &l4_mask->pdst, + sizeof(pmask->dport)); + if (flow_type == UDP_V4_FLOW) + req->features |= BIT_ULL(NPC_DPORT_UDP); + else + req->features |= BIT_ULL(NPC_DPORT_TCP); + } + break; + default: + return -ENOTSUPP; + } + if (fsp->flow_type & FLOW_EXT) { + if (fsp->m_ext.vlan_etype) + return -EINVAL; + if (fsp->m_ext.vlan_tci) { + if (fsp->m_ext.vlan_tci != cpu_to_be16(VLAN_VID_MASK)) + return -EINVAL; + if (be16_to_cpu(fsp->h_ext.vlan_tci) >= VLAN_N_VID) + return -EINVAL; + memcpy(&pkt->vlan_tci, &fsp->h_ext.vlan_tci, + sizeof(pkt->vlan_tci)); + memcpy(&pmask->vlan_tci, &fsp->m_ext.vlan_tci, + sizeof(pmask->vlan_tci)); + req->features |= BIT_ULL(NPC_OUTER_VID); + } + /* Not Drop/Direct to queue but use action in default entry */ + if (fsp->m_ext.data[1] && + fsp->h_ext.data[1] == cpu_to_be32(OTX2_DEFAULT_ACTION)) + req->op = NIX_RX_ACTION_DEFAULT; + } + if (fsp->flow_type & FLOW_MAC_EXT && + !is_zero_ether_addr(fsp->m_ext.h_dest)) { + ether_addr_copy(pkt->dmac, fsp->h_ext.h_dest); + ether_addr_copy(pmask->dmac, fsp->m_ext.h_dest); + req->features |= BIT_ULL(NPC_DMAC); + } + + if (!req->features) + return -ENOTSUPP; + + return 0; +} + +static int otx2_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *nfc) +{ + bool ntuple = !!(dev->features & NETIF_F_NTUPLE); + struct otx2_nic *pfvf = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + switch (nfc->cmd) { + case ETHTOOL_SRXFH: + ret = otx2_set_rss_hash_opts(pfvf, nfc); + break; + case ETHTOOL_SRXCLSRLINS: + if (netif_running(dev) && ntuple) + ret = otx2_add_flow(pfvf, &nfc->fs); + break; + case ETHTOOL_SRXCLSRLDEL: + if (netif_running(dev) && ntuple) + ret = otx2_remove_flow(pfvf, nfc->fs.location); + break; + default: + break; + } + + return ret; +} + +static int otx2vf_get_rxnfc(struct net_device *dev, + struct ethtool_rxnfc *nfc, u32 *rules) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + switch (nfc->cmd) { + case ETHTOOL_GRXRINGS: + nfc->data = pfvf->hw.rx_queues; + ret = 0; + break; + case ETHTOOL_GRXFH: + return otx2_get_rss_hash_opts(pfvf, nfc); + default: + break; + } + return ret; +} + +static int otx2vf_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *nfc) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + switch (nfc->cmd) { + case ETHTOOL_SRXFH: + ret = otx2_set_rss_hash_opts(pfvf, nfc); + break; + default: + break; + } + + return ret; +} + +static u32 otx2_get_rxfh_key_size(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + + return sizeof(rss->key); +} + +static u32 otx2_get_rxfh_indir_size(struct net_device *dev) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + + return pfvf->hw.rss_info.rss_size; +} + +/* Get RSS configuration*/ +static int otx2_get_rxfh(struct net_device *dev, u32 *indir, + u8 *hkey, u8 *hfunc) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + int idx; + + if (indir) { + for (idx = 0; idx < rss->rss_size; idx++) + indir[idx] = rss->ind_tbl[idx]; + } + + if (hkey) + memcpy(hkey, rss->key, sizeof(rss->key)); + + if (hfunc) + *hfunc = ETH_RSS_HASH_TOP; + + return 0; +} + +/* Configure RSS table and hash key*/ +static int otx2_set_rxfh(struct net_device *dev, const u32 *indir, + const u8 *hkey, const u8 hfunc) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + int idx; + + if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) + return -EOPNOTSUPP; + + if (!rss->enable) { + netdev_err(dev, "RSS is disabled, cannot change settings\n"); + return -EIO; + } + + if (indir) { + for (idx = 0; idx < rss->rss_size; idx++) + rss->ind_tbl[idx] = indir[idx]; + } + + if (hkey) { + memcpy(rss->key, hkey, sizeof(rss->key)); + otx2_set_rss_key(pfvf); + } + + otx2_set_rss_table(pfvf); + return 0; +} + +static int otx2_get_ts_info(struct net_device *netdev, + struct ethtool_ts_info *info) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + if (!pfvf->ptp) + return ethtool_op_get_ts_info(netdev, info); + + info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + info->phc_index = otx2_ptp_clock_index(pfvf); + + info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON); + + info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_ALL); + + return 0; +} + +static u32 otx2_get_msglevel(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + return pfvf->msg_enable; +} + +static void otx2_set_msglevel(struct net_device *netdev, u32 val) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + pfvf->msg_enable = val; +} + +static void otx2_get_fec_info(u64 index, int mode, struct ethtool_link_ksettings + *link_ksettings) +{ + switch (index) { + case OTX2_FEC_NONE: + if (mode) + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, + FEC_NONE); + else + ethtool_link_ksettings_add_link_mode(link_ksettings, + supported, + FEC_NONE); + break; + case OTX2_FEC_BASER: + if (mode) + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, + FEC_BASER); + else + ethtool_link_ksettings_add_link_mode(link_ksettings, + supported, + FEC_BASER); + break; + case OTX2_FEC_RS: + if (mode) + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, + FEC_RS); + else + ethtool_link_ksettings_add_link_mode(link_ksettings, + supported, + FEC_RS); + break; + case OTX2_FEC_BASER | OTX2_FEC_RS: + if (mode) { + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, + FEC_BASER); + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, + FEC_RS); + } else { + ethtool_link_ksettings_add_link_mode(link_ksettings, + supported, + FEC_BASER); + ethtool_link_ksettings_add_link_mode(link_ksettings, + supported, + FEC_RS); + } + + break; + } +} + +static void otx2_get_link_mode_info(u64 index, int mode, + struct ethtool_link_ksettings + *link_ksettings) +{ + u64 ethtool_link_mode = 0; + int bit_position = 0; + u64 link_modes = 0; + + int cgx_link_mode[29] = {0, + ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, + ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT, + ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, + ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, + ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, + ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, + ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, + ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, + ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, + ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, + ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, + ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, + ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, + ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, + ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, + ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, + ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, + ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, + ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, + ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, + ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, + ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT + }; + link_modes = index & OTX2_ETHTOOL_SUPPORTED_MODES; + + for (bit_position = 0; link_modes; bit_position++, link_modes >>= 1) { + if (!(link_modes & 1)) + continue; + + if (bit_position == 0) + ethtool_link_mode = 0x3F; + + ethtool_link_mode |= 1ULL << cgx_link_mode[bit_position]; + if (mode) + *link_ksettings->link_modes.advertising |= + ethtool_link_mode; + else + *link_ksettings->link_modes.supported |= + ethtool_link_mode; + } +} + +static struct cgx_fw_data *otx2_get_fwdata(struct otx2_nic *pfvf) +{ + struct cgx_fw_data *rsp = NULL; + struct msg_req *req; + int err = 0; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_cgx_get_aux_link_info(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return ERR_PTR(-ENOMEM); + } + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (!err) { + rsp = (struct cgx_fw_data *) + otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr); + } else { + rsp = ERR_PTR(err); + } + + otx2_mbox_unlock(&pfvf->mbox); + return rsp; +} + +static int otx2_get_module_info(struct net_device *netdev, + struct ethtool_modinfo *modinfo) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct cgx_fw_data *rsp; + + rsp = otx2_get_fwdata(pfvf); + if (IS_ERR(rsp)) + return PTR_ERR(rsp); + + modinfo->type = rsp->fwdata.sfp_eeprom.sff_id; + modinfo->eeprom_len = SFP_EEPROM_SIZE; + return 0; +} + +static int otx2_get_module_eeprom(struct net_device *netdev, + struct ethtool_eeprom *ee, + u8 *data) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct cgx_fw_data *rsp; + + rsp = otx2_get_fwdata(pfvf); + if (IS_ERR(rsp)) + return PTR_ERR(rsp); + + memcpy(data, &rsp->fwdata.sfp_eeprom.buf, ee->len); + + return 0; +} + +static int otx2_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct cgx_fw_data *rsp = NULL; + u32 supported = 0; + + cmd->base.duplex = pfvf->linfo.full_duplex; + cmd->base.speed = pfvf->linfo.speed; + cmd->base.autoneg = pfvf->linfo.an; + cmd->base.port = pfvf->linfo.port; + + rsp = otx2_get_fwdata(pfvf); + if (IS_ERR(rsp)) + return PTR_ERR(rsp); + + if (rsp->fwdata.supported_an) + supported |= SUPPORTED_Autoneg; + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + otx2_get_link_mode_info(rsp->fwdata.advertised_link_modes, 1, cmd); + otx2_get_fec_info(rsp->fwdata.advertised_fec, 1, cmd); + + otx2_get_link_mode_info(rsp->fwdata.supported_link_modes, 0, cmd); + otx2_get_fec_info(rsp->fwdata.supported_fec, 0, cmd); + + return 0; +} + +static int otx2_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) +{ + unsigned long advertising = 0; + struct otx2_nic *pfvf = netdev_priv(netdev); + struct cgx_set_link_mode_req *req; + struct cgx_set_link_mode_rsp *rsp; + int err = 0; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_cgx_set_link_mode(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -EAGAIN; + } + + advertising = (*cmd->link_modes.advertising) & (OTX2_ETHTOOL_ALL_MODES); + if (!(advertising & (advertising - 1)) && + (advertising <= BIT_ULL(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT))) { + req->args.mode = advertising; + } else { + otx2_mbox_unlock(&pfvf->mbox); + return -EINVAL; + } + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (!err) { + rsp = (struct cgx_set_link_mode_rsp *) + otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr); + if (rsp->status) + err = rsp->status; + } + otx2_mbox_unlock(&pfvf->mbox); + return err; +} + +static u32 otx2_get_link(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + if (is_otx2_lbkvf(pfvf->pdev)) + return 1; + return pfvf->linfo.link_up; +} + +static int otx2_get_fecparam(struct net_device *netdev, + struct ethtool_fecparam *fecparam) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct cgx_fw_data *rsp; + int fec[] = { + ETHTOOL_FEC_OFF, + ETHTOOL_FEC_BASER, + ETHTOOL_FEC_RS, + ETHTOOL_FEC_BASER | ETHTOOL_FEC_RS}; +#define FEC_MAX_INDEX 3 + if (pfvf->linfo.fec < FEC_MAX_INDEX) + fecparam->active_fec = fec[pfvf->linfo.fec]; + + rsp = otx2_get_fwdata(pfvf); + if (IS_ERR(rsp)) + return PTR_ERR(rsp); + + if (rsp->fwdata.supported_fec <= FEC_MAX_INDEX) { + if (!rsp->fwdata.supported_fec) + fecparam->fec = ETHTOOL_FEC_NONE; + else + fecparam->fec = fec[rsp->fwdata.supported_fec]; + } + return 0; +} + +static int otx2_set_fecparam(struct net_device *netdev, + struct ethtool_fecparam *fecparam) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct fec_mode *req, *rsp; + int err = 0, fec = 0; + + switch (fecparam->fec) { + case ETHTOOL_FEC_OFF: + fec = OTX2_FEC_NONE; + break; + case ETHTOOL_FEC_RS: + fec = OTX2_FEC_RS; + break; + case ETHTOOL_FEC_BASER: + fec = OTX2_FEC_BASER; + break; + default: + fec = OTX2_FEC_NONE; + break; + } + + if (fec == pfvf->linfo.fec) + return 0; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_cgx_set_fec_param(&pfvf->mbox); + if (!req) { + err = -EAGAIN; + goto end; + } + req->fec = fec; + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + goto end; + + rsp = (struct fec_mode *)otx2_mbox_get_rsp(&pfvf->mbox.mbox, + 0, &req->hdr); + if (rsp->fec >= 0) { + pfvf->linfo.fec = rsp->fec; + pfvf->hw.cgx_fec_corr_blks = 0; + pfvf->hw.cgx_fec_uncorr_blks = 0; + + } else { + err = rsp->fec; + } + +end: otx2_mbox_unlock(&pfvf->mbox); + return err; +} + +static u32 otx2_get_priv_flags(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct cgx_fw_data *rsp; + + rsp = otx2_get_fwdata(pfvf); + + if (IS_ERR(rsp)) { + pfvf->ethtool_flags &= ~OTX2_PRIV_FLAG_PAM4; + } else { + if (rsp->fwdata.phy.misc.mod_type) + pfvf->ethtool_flags |= OTX2_PRIV_FLAG_PAM4; + else + pfvf->ethtool_flags &= ~OTX2_PRIV_FLAG_PAM4; + } + + return pfvf->ethtool_flags; +} + +static int otx2_set_phy_mod_type(struct net_device *netdev, bool enable) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct cgx_phy_mod_type *req; + struct cgx_fw_data *fwd; + int rc = -EAGAIN; + + fwd = otx2_get_fwdata(pfvf); + if (IS_ERR(fwd)) + return -EAGAIN; + + /* ret here if phy does not support this feature */ + if (!fwd->fwdata.phy.misc.can_change_mod_type) + return -EOPNOTSUPP; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_cgx_set_phy_mod_type(&pfvf->mbox); + if (!req) + goto end; + + req->mod = enable; + + if (!otx2_sync_mbox_msg(&pfvf->mbox)) + rc = 0; +end: + otx2_mbox_unlock(&pfvf->mbox); + return rc; +} + +int otx2_set_npc_parse_mode(struct otx2_nic *pfvf) +{ + struct npc_set_pkind *req; + int rc = -EAGAIN; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_npc_set_pkind(&pfvf->mbox); + if (!req) + goto end; + + if (OTX2_IS_HIGIG2_ENABLED(pfvf->ethtool_flags)) + req->mode = OTX2_PRIV_FLAGS_HIGIG; + else if (OTX2_IS_EDSA_ENABLED(pfvf->ethtool_flags)) + req->mode = OTX2_PRIV_FLAGS_EDSA; + else + req->mode = OTX2_PRIV_FLAGS_DEFAULT; + + req->dir = PKIND_RX; + + /* req AF to change pkind on both the dir */ + if (req->mode == OTX2_PRIV_FLAGS_HIGIG) + req->dir |= PKIND_TX; + + if (!otx2_sync_mbox_msg(&pfvf->mbox)) + rc = 0; +end: + otx2_mbox_unlock(&pfvf->mbox); + return rc; +} + +static int otx2_enable_addl_header(struct net_device *netdev, int bitpos, + u32 len, bool enable) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + bool if_up = netif_running(netdev); + + if (enable) { + pfvf->ethtool_flags |= BIT(bitpos); + } else { + pfvf->ethtool_flags &= ~BIT(bitpos); + len = 0; + } + + if (if_up) + otx2_stop(netdev); + + /* Update max FRS so that additional hdrs are considered */ + pfvf->addl_mtu = len; + + /* Incase HIGIG2 mode is set packet will have 16 bytes of + * extra header at start of packet which stack does not need. + */ + if (OTX2_IS_HIGIG2_ENABLED(pfvf->ethtool_flags)) + pfvf->xtra_hdr = 16; + else + pfvf->xtra_hdr = 0; + + /* NPC parse mode will be updated here */ + if (if_up) + otx2_open(netdev); + + return 0; +} + +static int otx2_set_priv_flags(struct net_device *netdev, u32 new_flags) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + bool enable = false; + int bitnr, rc = 0; + u32 chg_flags; + + /* Get latest PAM4 settings */ + otx2_get_priv_flags(netdev); + + chg_flags = new_flags ^ pfvf->ethtool_flags; + if (!chg_flags) + return 0; + + /* Some are mutually exclusive, so allow only change at a time */ + if (hweight32(chg_flags) != 1) + return -EINVAL; + + bitnr = ffs(chg_flags) - 1; + if (new_flags & BIT(bitnr)) + enable = true; + + switch (BIT(bitnr)) { + case OTX2_PRIV_FLAG_PAM4: + rc = otx2_set_phy_mod_type(netdev, enable); + break; + case OTX2_PRIV_FLAG_EDSA_HDR: + /* HIGIG & EDSA are mutual exclusive */ + if (enable && OTX2_IS_HIGIG2_ENABLED(pfvf->ethtool_flags)) + return -EINVAL; + return otx2_enable_addl_header(netdev, bitnr, + OTX2_EDSA_HDR_LEN, enable); + break; + case OTX2_PRIV_FLAG_HIGIG2_HDR: + if (enable && OTX2_IS_EDSA_ENABLED(pfvf->ethtool_flags)) + return -EINVAL; + return otx2_enable_addl_header(netdev, bitnr, + OTX2_HIGIG2_HDR_LEN, enable); + break; + default: + break; + } + + /* save the change */ + if (!rc) { + if (enable) + pfvf->ethtool_flags |= BIT(bitnr); + else + pfvf->ethtool_flags &= ~BIT(bitnr); + } + + return rc; +} + +static struct ethtool_ops otx2_ethtool_ops = { + .get_link = otx2_get_link, + .get_drvinfo = otx2_get_drvinfo, + .get_strings = otx2_get_strings, + .get_ethtool_stats = otx2_get_ethtool_stats, + .get_sset_count = otx2_get_sset_count, + .set_channels = otx2_set_channels, + .get_channels = otx2_get_channels, + .get_ringparam = otx2_get_ringparam, + .set_ringparam = otx2_set_ringparam, + .get_coalesce = otx2_get_coalesce, + .set_coalesce = otx2_set_coalesce, + .get_rxnfc = otx2_get_rxnfc, + .set_rxnfc = otx2_set_rxnfc, + .get_rxfh_key_size = otx2_get_rxfh_key_size, + .get_rxfh_indir_size = otx2_get_rxfh_indir_size, + .get_rxfh = otx2_get_rxfh, + .set_rxfh = otx2_set_rxfh, + .get_ts_info = otx2_get_ts_info, + .get_msglevel = otx2_get_msglevel, + .set_msglevel = otx2_set_msglevel, + .get_link_ksettings = otx2_get_link_ksettings, + .set_link_ksettings = otx2_set_link_ksettings, + .get_pauseparam = otx2_get_pauseparam, + .set_pauseparam = otx2_set_pauseparam, + .get_fecparam = otx2_get_fecparam, + .set_fecparam = otx2_set_fecparam, + .get_module_info = otx2_get_module_info, + .get_module_eeprom = otx2_get_module_eeprom, + .get_priv_flags = otx2_get_priv_flags, + .set_priv_flags = otx2_set_priv_flags, +}; + +void otx2_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &otx2_ethtool_ops; +} + +/* VF's ethtool APIs */ +static void otx2vf_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + struct otx2_nic *vf = netdev_priv(netdev); + + strlcpy(info->driver, DRV_VF_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VF_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(vf->pdev), sizeof(info->bus_info)); +} + +static void otx2vf_get_strings(struct net_device *netdev, u32 sset, u8 *data) +{ + struct otx2_nic *vf = netdev_priv(netdev); + int stats; + + if (sset != ETH_SS_STATS) + return; + + for (stats = 0; stats < otx2_n_dev_stats; stats++) { + memcpy(data, otx2_dev_stats[stats].name, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + + otx2_get_qset_strings(vf, &data, 0); +} + +static void otx2vf_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct otx2_nic *vf = netdev_priv(netdev); + int stat; + + otx2_get_dev_stats(vf); + + for (stat = 0; stat < otx2_n_dev_stats; stat++) { + *data = ((u64 *)&vf->hw.dev_stats)[otx2_dev_stats[stat].index]; + data++; + } + + otx2_get_qset_stats(vf, stats, &data); +} + +static int otx2vf_get_sset_count(struct net_device *netdev, int sset) +{ + struct otx2_nic *vf = netdev_priv(netdev); + + if (sset != ETH_SS_STATS) + return -EINVAL; + + return otx2_n_dev_stats + + otx2_n_queue_stats * (vf->hw.rx_queues + vf->hw.tx_queues); +} + +static int otx2vf_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + if (is_otx2_lbkvf(pfvf->pdev)) { + cmd->base.port = PORT_OTHER; + cmd->base.duplex = DUPLEX_FULL; + cmd->base.speed = SPEED_100000; + } else { + return otx2_get_link_ksettings(netdev, cmd); + } + return 0; +} +static const struct ethtool_ops otx2vf_ethtool_ops = { + .get_link = otx2_get_link, + .get_drvinfo = otx2vf_get_drvinfo, + .get_strings = otx2vf_get_strings, + .get_ethtool_stats = otx2vf_get_ethtool_stats, + .get_sset_count = otx2vf_get_sset_count, + .set_channels = otx2_set_channels, + .get_channels = otx2_get_channels, + .get_rxnfc = otx2vf_get_rxnfc, + .set_rxnfc = otx2vf_set_rxnfc, + .get_rxfh_key_size = otx2_get_rxfh_key_size, + .get_rxfh_indir_size = otx2_get_rxfh_indir_size, + .get_rxfh = otx2_get_rxfh, + .set_rxfh = otx2_set_rxfh, + .get_ringparam = otx2_get_ringparam, + .set_ringparam = otx2_set_ringparam, + .get_coalesce = otx2_get_coalesce, + .set_coalesce = otx2_set_coalesce, + .get_pauseparam = otx2_get_pauseparam, + .set_pauseparam = otx2_set_pauseparam, + .get_link_ksettings = otx2vf_get_link_ksettings, +}; + +void otx2vf_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &otx2vf_ethtool_ops; +} +EXPORT_SYMBOL(otx2vf_set_ethtool_ops); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c new file mode 100644 index 000000000000..96a4dbba25a3 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Physcial Function ethernet driver + * + * Copyright (C) 2019 Marvell International Ltd. + * + * 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 "otx2_common.h" + +/* helper macros to support mcam flows */ +#define OTX2_MAX_NTUPLE_FLOWS 32 +#define OTX2_MAX_UNICAST_FLOWS 8 +#define OTX2_MAX_VLAN_FLOWS 1 + +enum mcam_offset { + NTUPLE_OFFSET = 0, + UNICAST_OFFSET = NTUPLE_OFFSET + OTX2_MAX_NTUPLE_FLOWS, + VLAN_OFFSET = UNICAST_OFFSET + OTX2_MAX_UNICAST_FLOWS, + OTX2_MCAM_COUNT = VLAN_OFFSET + OTX2_MAX_VLAN_FLOWS, +}; + +struct otx2_flow { + struct ethtool_rx_flow_spec flow_spec; + struct list_head list; + u32 location; + u16 entry; + bool is_vf; + int vf; +}; + +int otx2_mcam_flow_init(struct otx2_nic *pf) +{ + INIT_LIST_HEAD(&pf->flows); + + pf->ntuple_max_flows = OTX2_MAX_NTUPLE_FLOWS; + + pf->flags |= (OTX2_FLAG_NTUPLE_SUPPORT | + OTX2_FLAG_UCAST_FLTR_SUPPORT | OTX2_FLAG_RX_VLAN_SUPPORT); + + pf->mac_table = devm_kzalloc(pf->dev, sizeof(struct otx2_mac_table) + * OTX2_MAX_UNICAST_FLOWS, GFP_KERNEL); + + if (!pf->mac_table) + return -ENOMEM; + + /* register work queue for ndo callbacks */ + pf->otx2_ndo_wq = create_singlethread_workqueue("otx2_ndo_work_queue"); + if (!pf->otx2_ndo_wq) + return -ENOMEM; + INIT_WORK(&pf->otx2_rx_mode_work, otx2_do_set_rx_mode); + return 0; +} + +void otx2_mcam_flow_del(struct otx2_nic *pf) +{ + otx2_destroy_mcam_flows(pf); + if (pf->otx2_ndo_wq) { + flush_workqueue(pf->otx2_ndo_wq); + destroy_workqueue(pf->otx2_ndo_wq); + } +} + +static int otx2_alloc_mcam_entries(struct otx2_nic *pfvf) +{ + netdev_features_t wanted = NETIF_F_HW_VLAN_STAG_RX | + NETIF_F_HW_VLAN_CTAG_RX; + struct npc_mcam_alloc_entry_req *req; + struct npc_mcam_alloc_entry_rsp *rsp; + int i; + + otx2_mbox_lock(&pfvf->mbox); + if (pfvf->flags & OTX2_FLAG_MCAM_ENTRIES_ALLOC) { + otx2_mbox_unlock(&pfvf->mbox); + return 0; + } + + req = otx2_mbox_alloc_msg_npc_mcam_alloc_entry(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + req->contig = false; + req->count = OTX2_MCAM_COUNT; + + /* Send message to AF */ + if (otx2_sync_mbox_msg(&pfvf->mbox)) { + otx2_mbox_unlock(&pfvf->mbox); + return -EINVAL; + } + + rsp = (struct npc_mcam_alloc_entry_rsp *)otx2_mbox_get_rsp + (&pfvf->mbox.mbox, 0, &req->hdr); + + if (rsp->count != req->count) { + netdev_info(pfvf->netdev, "number of rules truncated to %d\n", + rsp->count); + netdev_info(pfvf->netdev, + "Disabling RX VLAN offload due to non-availability of MCAM space\n"); + /* support only ntuples here */ + pfvf->ntuple_max_flows = rsp->count; + pfvf->netdev->priv_flags &= ~IFF_UNICAST_FLT; + pfvf->flags &= ~OTX2_FLAG_UCAST_FLTR_SUPPORT; + pfvf->flags &= ~OTX2_FLAG_RX_VLAN_SUPPORT; + pfvf->netdev->features &= ~wanted; + pfvf->netdev->hw_features &= ~wanted; + } + + for (i = 0; i < rsp->count; i++) + pfvf->entry_list[i] = rsp->entry_list[i]; + + pfvf->flags |= OTX2_FLAG_MCAM_ENTRIES_ALLOC; + otx2_mbox_unlock(&pfvf->mbox); + + return 0; +} + +/* On success adds mcam entry + * On failure enable promisous mode + */ +static int otx2_do_add_macfilter(struct otx2_nic *pf, const u8 *mac) +{ + struct npc_install_flow_req *req; + int err, i; + + if (!(pf->flags & OTX2_FLAG_MCAM_ENTRIES_ALLOC)) { + err = otx2_alloc_mcam_entries(pf); + if (err) + return err; + } + + if (!(pf->flags & OTX2_FLAG_UCAST_FLTR_SUPPORT)) + return -ENOMEM; + + /* dont have free mcam entries or uc list is greater than alloted */ + if (netdev_uc_count(pf->netdev) > OTX2_MAX_UNICAST_FLOWS) + return -ENOMEM; + + otx2_mbox_lock(&pf->mbox); + req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox); + if (!req) { + otx2_mbox_unlock(&pf->mbox); + return -ENOMEM; + } + + /* unicast offset starts with 32 0..31 for ntuple */ + for (i = 0; i < OTX2_MAX_UNICAST_FLOWS; i++) { + if (pf->mac_table[i].inuse) + continue; + ether_addr_copy(pf->mac_table[i].addr, mac); + pf->mac_table[i].inuse = true; + pf->mac_table[i].mcam_entry = + pf->entry_list[i + UNICAST_OFFSET]; + req->entry = pf->mac_table[i].mcam_entry; + break; + } + + ether_addr_copy(req->packet.dmac, mac); + u64_to_ether_addr(0xffffffffffffull, req->mask.dmac); + req->features = BIT_ULL(NPC_DMAC); + req->channel = pf->hw.rx_chan_base; + req->intf = NIX_INTF_RX; + req->op = NIX_RX_ACTION_DEFAULT; + req->set_cntr = 1; + + err = otx2_sync_mbox_msg(&pf->mbox); + otx2_mbox_unlock(&pf->mbox); + + return err; +} + +int otx2_add_macfilter(struct net_device *netdev, const u8 *mac) +{ + struct otx2_nic *pf = netdev_priv(netdev); + int err; + + err = otx2_do_add_macfilter(pf, mac); + if (err) { + netdev->flags |= IFF_PROMISC; + return err; + } + return 0; +} + +static bool otx2_get_mcamentry_for_mac(struct otx2_nic *pf, const u8 *mac, + int *mcam_entry) +{ + int i; + + for (i = 0; i < OTX2_MAX_UNICAST_FLOWS; i++) { + if (!pf->mac_table[i].inuse) + continue; + + if (ether_addr_equal(pf->mac_table[i].addr, mac)) { + *mcam_entry = pf->mac_table[i].mcam_entry; + pf->mac_table[i].inuse = false; + return true; + } + } + return false; +} + +int otx2_del_macfilter(struct net_device *netdev, const u8 *mac) +{ + struct otx2_nic *pf = netdev_priv(netdev); + struct npc_delete_flow_req *req; + int err, mcam_entry; + + /* check does mcam entry exists for given mac */ + if (!otx2_get_mcamentry_for_mac(pf, mac, &mcam_entry)) + return 0; + + otx2_mbox_lock(&pf->mbox); + req = otx2_mbox_alloc_msg_npc_delete_flow(&pf->mbox); + if (!req) { + otx2_mbox_unlock(&pf->mbox); + return -ENOMEM; + } + req->entry = mcam_entry; + /* Send message to AF */ + err = otx2_sync_mbox_msg(&pf->mbox); + otx2_mbox_unlock(&pf->mbox); + + return err; +} + +static struct otx2_flow *otx2_find_flow(struct otx2_nic *pfvf, u32 location) +{ + struct otx2_flow *iter; + + list_for_each_entry(iter, &pfvf->flows, list) { + if (iter->location == location) + return iter; + } + + return NULL; +} + +static void otx2_add_flow_to_list(struct otx2_nic *pfvf, struct otx2_flow *flow) +{ + struct list_head *head = &pfvf->flows; + struct otx2_flow *iter; + + list_for_each_entry(iter, &pfvf->flows, list) { + if (iter->location > flow->location) + break; + head = &iter->list; + } + + list_add(&flow->list, head); +} + +int otx2_get_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc, + u32 location) +{ + struct otx2_flow *iter; + + if (location >= pfvf->ntuple_max_flows) + return -EINVAL; + + list_for_each_entry(iter, &pfvf->flows, list) { + if (iter->location == location) { + nfc->fs = iter->flow_spec; + return 0; + } + } + + return -ENOENT; +} + +int otx2_get_all_flows(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc, + u32 *rule_locs) +{ + u32 location = 0; + int idx = 0; + int err = 0; + + nfc->data = pfvf->ntuple_max_flows; + while ((!err || err == -ENOENT) && idx < nfc->rule_cnt) { + err = otx2_get_flow(pfvf, nfc, location); + if (!err) + rule_locs[idx++] = location; + location++; + } + + return err; +} + +static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow) +{ + u64 ring_cookie = flow->flow_spec.ring_cookie; + struct npc_install_flow_req *req; + int err, vf = 0; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_npc_install_flow(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + err = otx2_prepare_flow_request(&flow->flow_spec, req); + if (err) { + /* free the allocated msg above */ + otx2_mbox_reset(&pfvf->mbox.mbox, 0); + otx2_mbox_unlock(&pfvf->mbox); + return err; + } + + req->entry = flow->entry; + req->intf = NIX_INTF_RX; + req->set_cntr = 1; + req->channel = pfvf->hw.rx_chan_base; + if (ring_cookie == RX_CLS_FLOW_DISC) { + req->op = NIX_RX_ACTIONOP_DROP; + } else { + /* change to unicast only if action of default entry is not + * requested by user + */ + if (req->op != NIX_RX_ACTION_DEFAULT) + req->op = NIX_RX_ACTIONOP_UCAST; + req->index = ethtool_get_flow_spec_ring(ring_cookie); + vf = ethtool_get_flow_spec_ring_vf(ring_cookie); + if (vf > pci_num_vf(pfvf->pdev)) { + otx2_mbox_unlock(&pfvf->mbox); + return -EINVAL; + } + } + + /* ethtool ring_cookie has (VF + 1) for VF */ + if (vf) { + req->vf = vf; + flow->is_vf = true; + flow->vf = vf; + } + + /* Send message to AF */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} + +int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rx_flow_spec *fsp) +{ + u32 ring = ethtool_get_flow_spec_ring(fsp->ring_cookie); + struct otx2_flow *flow; + bool new = false; + int err; + + if (ring >= pfvf->hw.rx_queues && fsp->ring_cookie != RX_CLS_FLOW_DISC) + return -EINVAL; + + if (!(pfvf->flags & OTX2_FLAG_MCAM_ENTRIES_ALLOC)) { + err = otx2_alloc_mcam_entries(pfvf); + if (err) + return err; + } + + if (fsp->location >= pfvf->ntuple_max_flows) + return -EINVAL; + + flow = otx2_find_flow(pfvf, fsp->location); + if (!flow) { + flow = kzalloc(sizeof(*flow), GFP_ATOMIC); + if (!flow) + return -ENOMEM; + flow->location = fsp->location; + flow->entry = pfvf->entry_list[flow->location]; + new = true; + } + /* struct copy */ + flow->flow_spec = *fsp; + + err = otx2_add_flow_msg(pfvf, flow); + if (err) { + if (new) + kfree(flow); + return err; + } + + /* add the new flow installed to list */ + if (new) { + otx2_add_flow_to_list(pfvf, flow); + pfvf->nr_flows++; + } + + return 0; +} + +static int otx2_remove_flow_msg(struct otx2_nic *pfvf, u16 entry, bool all) +{ + struct npc_delete_flow_req *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_npc_delete_flow(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + req->entry = entry; + if (all) + req->all = 1; + + /* Send message to AF */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} + +int otx2_remove_flow(struct otx2_nic *pfvf, u32 location) +{ + struct otx2_flow *flow; + int err; + + if (location >= pfvf->ntuple_max_flows) + return -EINVAL; + + flow = otx2_find_flow(pfvf, location); + if (!flow) + return -ENOENT; + + err = otx2_remove_flow_msg(pfvf, flow->entry, false); + if (err) + return err; + + list_del(&flow->list); + kfree(flow); + pfvf->nr_flows--; + + return 0; +} + +int otx2_destroy_ntuple_flows(struct otx2_nic *pfvf) +{ + struct npc_delete_flow_req *req; + struct otx2_flow *iter, *tmp; + int err; + + if (!(pfvf->flags & OTX2_FLAG_MCAM_ENTRIES_ALLOC)) + return 0; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_npc_delete_flow(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + req->start = pfvf->entry_list[NTUPLE_OFFSET]; + req->end = pfvf->entry_list[NTUPLE_OFFSET + + pfvf->ntuple_max_flows - 1]; + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + + list_for_each_entry_safe(iter, tmp, &pfvf->flows, list) { + list_del(&iter->list); + kfree(iter); + pfvf->nr_flows--; + } + return err; +} + +int otx2_destroy_mcam_flows(struct otx2_nic *pfvf) +{ + struct npc_mcam_free_entry_req *req; + struct otx2_flow *iter, *tmp; + int err; + + if (!(pfvf->flags & OTX2_FLAG_MCAM_ENTRIES_ALLOC)) + return 0; + + /* remove all flows */ + err = otx2_remove_flow_msg(pfvf, 0, true); + if (err) + return err; + + list_for_each_entry_safe(iter, tmp, &pfvf->flows, list) { + list_del(&iter->list); + kfree(iter); + pfvf->nr_flows--; + } + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_npc_mcam_free_entry(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + req->all = 1; + /* Send message to AF to free MCAM entries */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + otx2_mbox_unlock(&pfvf->mbox); + return err; + } + + pfvf->flags &= ~OTX2_FLAG_MCAM_ENTRIES_ALLOC; + otx2_mbox_unlock(&pfvf->mbox); + + return 0; +} + +static int otx2_install_rxvlan_offload_flow(struct otx2_nic *pfvf) +{ + struct npc_install_flow_req *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_npc_install_flow(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + req->entry = pfvf->entry_list[VLAN_OFFSET]; + req->intf = NIX_INTF_RX; + ether_addr_copy(req->packet.dmac, pfvf->netdev->dev_addr); + u64_to_ether_addr(0xffffffffffffull, req->mask.dmac); + req->channel = pfvf->hw.rx_chan_base; + req->op = NIX_RX_ACTION_DEFAULT; + req->features = BIT_ULL(NPC_OUTER_VID) | BIT_ULL(NPC_DMAC); + req->vtag0_valid = true; + req->vtag0_type = NIX_AF_LFX_RX_VTAG_TYPE0; + + /* Send message to AF */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} + +static int otx2_delete_rxvlan_offload_flow(struct otx2_nic *pfvf) +{ + struct npc_delete_flow_req *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_npc_delete_flow(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + req->entry = pfvf->entry_list[VLAN_OFFSET]; + /* Send message to AF */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} + +int otx2_enable_rxvlan(struct otx2_nic *pf, bool enable) +{ + struct nix_vtag_config *req; + struct mbox_msghdr *rsp_hdr; + int err; + + if (!(pf->flags & OTX2_FLAG_MCAM_ENTRIES_ALLOC)) { + err = otx2_alloc_mcam_entries(pf); + if (err) + return err; + } + + /* Dont have enough mcam entries */ + if (!(pf->flags & OTX2_FLAG_RX_VLAN_SUPPORT)) + return -ENOMEM; + + if (enable) { + err = otx2_install_rxvlan_offload_flow(pf); + if (err) + return err; + } else { + err = otx2_delete_rxvlan_offload_flow(pf); + if (err) + return err; + } + + otx2_mbox_lock(&pf->mbox); + req = otx2_mbox_alloc_msg_nix_vtag_cfg(&pf->mbox); + if (!req) { + otx2_mbox_unlock(&pf->mbox); + return -ENOMEM; + } + + /* config strip, capture and size */ + req->vtag_size = VTAGSIZE_T4; + req->cfg_type = 1; /* rx vlan cfg */ + req->rx.vtag_type = NIX_AF_LFX_RX_VTAG_TYPE0; + req->rx.strip_vtag = enable; + req->rx.capture_vtag = enable; + + err = otx2_sync_mbox_msg(&pf->mbox); + if (err) { + otx2_mbox_unlock(&pf->mbox); + return err; + } + + rsp_hdr = otx2_mbox_get_rsp(&pf->mbox.mbox, 0, &req->hdr); + if (IS_ERR(rsp_hdr)) { + otx2_mbox_unlock(&pf->mbox); + return PTR_ERR(rsp_hdr); + } + + otx2_mbox_unlock(&pf->mbox); + return rsp_hdr->rc; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c new file mode 100644 index 000000000000..ea37e652939f --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -0,0 +1,2553 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Physcial Function ethernet driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/interrupt.h> +#include <linux/pci.h> +#include <linux/etherdevice.h> +#include <linux/of.h> +#include <linux/if_vlan.h> +#include <net/ip.h> +#include <linux/iommu.h> + +#include "otx2_reg.h" +#include "otx2_common.h" +#include "otx2_txrx.h" +#include "otx2_struct.h" +#include "otx2_ptp.h" + +#define DRV_NAME "octeontx2-nicpf" +#define DRV_STRING "Marvell OcteonTX2 NIC Physical Function Driver" +#define DRV_VERSION "1.0" + +/* Supported devices */ +static const struct pci_device_id otx2_pf_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_PF) }, + { 0, } /* end of table */ +}; + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION(DRV_STRING); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); +MODULE_DEVICE_TABLE(pci, otx2_pf_id_table); + +enum { + TYPE_PFAF, + TYPE_PFVF, +}; + +static int otx2_change_mtu(struct net_device *netdev, int new_mtu) +{ + bool if_up = netif_running(netdev); + int err = 0; + + if (if_up) + otx2_stop(netdev); + + netdev_info(netdev, "Changing MTU from %d to %d\n", + netdev->mtu, new_mtu); + netdev->mtu = new_mtu; + + if (if_up) + err = otx2_open(netdev); + + return err; +} + +static void otx2_disable_flr_me_intr(struct otx2_nic *pf) +{ + int irq, vfs = pf->total_vfs; + + /* Disable VFs ME interrupts */ + otx2_write64(pf, RVU_PF_VFME_INT_ENA_W1CX(0), INTR_MASK(vfs)); + irq = pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_VFME0); + free_irq(irq, pf); + + /* Disable VFs FLR interrupts */ + otx2_write64(pf, RVU_PF_VFFLR_INT_ENA_W1CX(0), INTR_MASK(vfs)); + irq = pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_VFFLR0); + free_irq(irq, pf); + + if (vfs <= 64) + return; + + otx2_write64(pf, RVU_PF_VFME_INT_ENA_W1CX(1), INTR_MASK(vfs - 64)); + irq = pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_VFME1); + free_irq(irq, pf); + + otx2_write64(pf, RVU_PF_VFFLR_INT_ENA_W1CX(1), INTR_MASK(vfs - 64)); + irq = pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_VFFLR1); + free_irq(irq, pf); +} + +static void otx2_flr_wq_destroy(struct otx2_nic *pf) +{ + if (!pf->flr_wq) + return; + flush_workqueue(pf->flr_wq); + destroy_workqueue(pf->flr_wq); + pf->flr_wq = NULL; +} + +static void otx2_flr_handler(struct work_struct *work) +{ + struct flr_work *flrwork = container_of(work, struct flr_work, work); + struct otx2_nic *pf = flrwork->pf; + struct msg_req *req; + struct msg_rsp *rsp; + int vf, reg = 0; + + vf = flrwork - pf->flr_wrk; + + + otx2_mbox_lock(&pf->mbox); + req = otx2_mbox_alloc_msg_vf_flr(&pf->mbox); + if (!req) { + otx2_mbox_unlock(&pf->mbox); + return; + } + req->hdr.pcifunc &= RVU_PFVF_FUNC_MASK; + req->hdr.pcifunc |= (vf + 1) & RVU_PFVF_FUNC_MASK; + + if (!otx2_sync_mbox_msg(&pf->mbox)) { + if (vf >= 64) { + reg = 1; + vf = vf - 64; + } + rsp = (struct msg_rsp *) + otx2_mbox_get_rsp(&pf->mbox.mbox, 0, &req->hdr); + otx2_mbox_unlock(&pf->mbox); + if (rsp->hdr.rc) + return; + /* clear transcation pending bit */ + otx2_write64(pf, RVU_PF_VFTRPENDX(reg), BIT_ULL(vf)); + otx2_write64(pf, RVU_PF_VFFLR_INT_ENA_W1SX(reg), BIT_ULL(vf)); + } + + otx2_mbox_unlock(&pf->mbox); +} + +static irqreturn_t otx2_pf_flr_intr_handler(int irq, void *pf_irq) +{ + struct otx2_nic *pf = (struct otx2_nic *)pf_irq; + int reg, dev, vf, start_vf, num_reg = 1; + u64 intr; + + if (pf->total_vfs > 64) + num_reg = 2; + + for (reg = 0; reg < num_reg; reg++) { + intr = otx2_read64(pf, RVU_PF_VFFLR_INTX(reg)); + if (!intr) + continue; + start_vf = 64 * reg; + for (vf = 0; vf < 64; vf++) { + if (!(intr & BIT_ULL(vf))) + continue; + dev = vf + start_vf; + queue_work(pf->flr_wq, &pf->flr_wrk[dev].work); + /* Clear interrupt */ + otx2_write64(pf, RVU_PF_VFFLR_INTX(reg), BIT_ULL(vf)); + /* Disable the interrupt */ + otx2_write64(pf, RVU_PF_VFFLR_INT_ENA_W1CX(reg), + BIT_ULL(vf)); + } + } + return IRQ_HANDLED; +} + +static irqreturn_t otx2_pf_me_intr_handler(int irq, void *pf_irq) +{ + struct otx2_nic *pf = (struct otx2_nic *)pf_irq; + int vf, reg, num_reg = 1; + u64 intr; + + if (pf->total_vfs > 64) + num_reg = 2; + + for (reg = 0; reg < num_reg; reg++) { + intr = otx2_read64(pf, RVU_PF_VFME_INTX(reg)); + if (!intr) + continue; + for (vf = 0; vf < 64; vf++) { + if (!(intr & BIT_ULL(vf))) + continue; + /* clear trpend bit */ + otx2_write64(pf, RVU_PF_VFTRPENDX(reg), BIT_ULL(vf)); + /* clear interrupt */ + otx2_write64(pf, RVU_PF_VFME_INTX(reg), BIT_ULL(vf)); + } + } + return IRQ_HANDLED; +} + +static int otx2_register_flr_me_intr(struct otx2_nic *pf) +{ + struct otx2_hw *hw = &pf->hw; + int vfs = pf->total_vfs; + char *irq_name; + int ret; + + /* Register ME interrupt handler*/ + irq_name = &hw->irq_name[RVU_PF_INT_VEC_VFME0 * NAME_SIZE]; + snprintf(irq_name, NAME_SIZE, "RVUPF%d_ME0", rvu_get_pf(pf->pcifunc)); + ret = request_irq(pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_VFME0), + otx2_pf_me_intr_handler, 0, irq_name, pf); + if (ret) { + dev_err(pf->dev, + "RVUPF: IRQ registration failed for ME\n"); + } + + /* Register FLR interrupt handler */ + irq_name = &hw->irq_name[RVU_PF_INT_VEC_VFFLR0 * NAME_SIZE]; + snprintf(irq_name, NAME_SIZE, "RVUPF%d_FLR0", rvu_get_pf(pf->pcifunc)); + ret = request_irq(pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_VFFLR0), + otx2_pf_flr_intr_handler, 0, irq_name, pf); + if (ret) { + dev_err(pf->dev, + "RVUPF: IRQ registration failed for FLR\n"); + return ret; + } + + if (pf->total_vfs > 64) { + irq_name = &hw->irq_name[RVU_PF_INT_VEC_VFME1 * NAME_SIZE]; + snprintf(irq_name, NAME_SIZE, "RVUPF%d_ME1", + rvu_get_pf(pf->pcifunc)); + ret = request_irq(pci_irq_vector + (pf->pdev, RVU_PF_INT_VEC_VFME1), + otx2_pf_me_intr_handler, 0, irq_name, pf); + if (ret) { + dev_err(pf->dev, + "RVUPF: IRQ registration failed for ME1\n"); + } + irq_name = &hw->irq_name[RVU_PF_INT_VEC_VFFLR1 * NAME_SIZE]; + snprintf(irq_name, NAME_SIZE, "RVUPF%d_FLR1", + rvu_get_pf(pf->pcifunc)); + ret = request_irq(pci_irq_vector + (pf->pdev, RVU_PF_INT_VEC_VFFLR1), + otx2_pf_flr_intr_handler, 0, irq_name, pf); + if (ret) { + dev_err(pf->dev, + "RVUPF: IRQ registration failed for FLR1\n"); + return ret; + } + } + + /* Enable ME interrupt for all VFs*/ + otx2_write64(pf, RVU_PF_VFME_INTX(0), INTR_MASK(vfs)); + otx2_write64(pf, RVU_PF_VFME_INT_ENA_W1SX(0), INTR_MASK(vfs)); + + /* Enable FLR interrupt for all VFs*/ + otx2_write64(pf, RVU_PF_VFFLR_INTX(0), INTR_MASK(vfs)); + otx2_write64(pf, RVU_PF_VFFLR_INT_ENA_W1SX(0), INTR_MASK(vfs)); + + if (pf->total_vfs > 64) { + vfs = pf->total_vfs - 64 - 1; + + otx2_write64(pf, RVU_PF_VFME_INTX(1), INTR_MASK(vfs)); + otx2_write64(pf, RVU_PF_VFME_INT_ENA_W1SX(1), INTR_MASK(vfs)); + + otx2_write64(pf, RVU_PF_VFFLR_INTX(1), INTR_MASK(vfs)); + otx2_write64(pf, RVU_PF_VFFLR_INT_ENA_W1SX(1), INTR_MASK(vfs)); + } + return 0; +} + +static int otx2_pf_flr_init(struct otx2_nic *pf, int num_vfs) +{ + int vf; + + pf->flr_wq = alloc_workqueue("otx2_pf_flr_wq", WQ_UNBOUND | WQ_HIGHPRI + | WQ_MEM_RECLAIM, 1); + if (!pf->flr_wq) + return -ENOMEM; + + pf->flr_wrk = devm_kcalloc(pf->dev, num_vfs, + sizeof(struct flr_work), GFP_KERNEL); + if (!pf->flr_wrk) { + destroy_workqueue(pf->flr_wq); + return -ENOMEM; + } + + for (vf = 0; vf < num_vfs; vf++) { + pf->flr_wrk[vf].pf = pf; + INIT_WORK(&pf->flr_wrk[vf].work, otx2_flr_handler); + } + + return 0; +} + +static void otx2_queue_work(struct mbox *mw, struct workqueue_struct *mbox_wq, + int first, int mdevs, u64 intr, int type) +{ + struct otx2_mbox_dev *mdev; + struct otx2_mbox *mbox; + struct mbox_hdr *hdr; + int i; + + for (i = first; i < mdevs; i++) { + /* start from 0 */ + if (!(intr & BIT_ULL(i - first))) + continue; + + mbox = &mw->mbox; + mdev = &mbox->dev[i]; + if (type == TYPE_PFAF) + otx2_sync_mbox_bbuf(mbox, i); + hdr = mdev->mbase + mbox->rx_start; + /* The hdr->num_msgs is set to zero immediately in the interrupt + * handler to ensure that it holds a correct value next time + * when the interrupt handler is called. + * pf->mbox.num_msgs holds the data for use in pfaf_mbox_handler + * pf>mbox.up_num_msgs holds the data for use in + * pfaf_mbox_up_handler. + */ + if (hdr->num_msgs) { + mw[i].num_msgs = hdr->num_msgs; + hdr->num_msgs = 0; + if (type == TYPE_PFAF) + memset(mbox->hwbase + mbox->rx_start, 0, + ALIGN(sizeof(struct mbox_hdr), + sizeof(u64))); + + queue_work(mbox_wq, &mw[i].mbox_wrk); + } + + mbox = &mw->mbox_up; + mdev = &mbox->dev[i]; + if (type == TYPE_PFAF) + otx2_sync_mbox_bbuf(mbox, i); + hdr = mdev->mbase + mbox->rx_start; + if (hdr->num_msgs) { + mw[i].up_num_msgs = hdr->num_msgs; + hdr->num_msgs = 0; + if (type == TYPE_PFAF) + memset(mbox->hwbase + mbox->rx_start, 0, + ALIGN(sizeof(struct mbox_hdr), + sizeof(u64))); + + queue_work(mbox_wq, &mw[i].mbox_up_wrk); + } + } +} + +static int otx2_config_hw_tx_tstamp(struct otx2_nic *pfvf, bool enable); +static int otx2_config_hw_rx_tstamp(struct otx2_nic *pfvf, bool enable); + +static void otx2_forward_msg_pfvf(struct otx2_mbox_dev *mdev, + struct otx2_mbox *pfvf_mbox, void *bbuf_base, + int devid) +{ + struct otx2_mbox_dev *src_mdev = mdev; + int offset; + + /* Msgs are already copied, trigger VF's mbox irq */ + smp_wmb(); + + offset = pfvf_mbox->trigger | (devid << pfvf_mbox->tr_shift); + writeq(1, (void __iomem *)pfvf_mbox->reg_base + offset); + + /* Restore VF's mbox bounce buffer region address */ + src_mdev->mbase = bbuf_base; +} + +static int otx2_forward_vf_mbox_msgs(struct otx2_nic *pf, + struct otx2_mbox *src_mbox, + int dir, int vf, int num_msgs) +{ + struct otx2_mbox_dev *src_mdev, *dst_mdev; + struct mbox_hdr *mbox_hdr; + struct mbox_hdr *req_hdr; + struct mbox *dst_mbox; + int dst_size, err; + + if (dir == MBOX_DIR_PFAF) { + /* Set VF's mailbox memory as PF's bounce buffer memory, so + * that explicit copying of VF's msgs to PF=>AF mbox region + * and AF=>PF responses to VF's mbox region can be avoided. + */ + src_mdev = &src_mbox->dev[vf]; + mbox_hdr = src_mbox->hwbase + + src_mbox->rx_start + (vf * MBOX_SIZE); + req_hdr = (struct mbox_hdr *)(src_mdev->mbase + + src_mbox->rx_start); + req_hdr->num_msgs = num_msgs; + dst_mbox = &pf->mbox; + dst_size = dst_mbox->mbox.tx_size - + ALIGN(sizeof(*mbox_hdr), MBOX_MSG_ALIGN); + /* Check if msgs fit into destination area */ + if (mbox_hdr->msg_size > dst_size) + return -EINVAL; + + dst_mdev = &dst_mbox->mbox.dev[0]; + + otx2_mbox_lock(&pf->mbox); + dst_mdev->mbase = src_mdev->mbase; + dst_mdev->msg_size = mbox_hdr->msg_size; + dst_mdev->num_msgs = mbox_hdr->num_msgs; + err = otx2_sync_mbox_msg(dst_mbox); + if (err) { + dev_warn(pf->dev, + "AF not responding to VF%d messages\n", vf); + /* restore PF mbase and exit */ + dst_mdev->mbase = pf->mbox.bbuf_base; + otx2_mbox_unlock(&pf->mbox); + return err; + } + /* At this point, all the VF messages sent to AF are acked + * with proper responses and responses are copied to VF + * mailbox hence raise interrupt to VF. + */ + req_hdr = (struct mbox_hdr *)(dst_mdev->mbase + + dst_mbox->mbox.rx_start); + req_hdr->num_msgs = num_msgs; + + otx2_forward_msg_pfvf(dst_mdev, &pf->mbox_pfvf[0].mbox, + pf->mbox.bbuf_base, vf); + otx2_mbox_unlock(&pf->mbox); + } else if (dir == MBOX_DIR_PFVF_UP) { + src_mdev = &src_mbox->dev[0]; + mbox_hdr = src_mbox->hwbase + src_mbox->rx_start; + req_hdr = (struct mbox_hdr *)(src_mdev->mbase + + src_mbox->rx_start); + req_hdr->num_msgs = num_msgs; + + dst_mbox = &pf->mbox_pfvf[0]; + dst_size = dst_mbox->mbox_up.tx_size - + ALIGN(sizeof(*mbox_hdr), MBOX_MSG_ALIGN); + /* Check if msgs fit into destination area */ + if (mbox_hdr->msg_size > dst_size) + return -EINVAL; + + dst_mdev = &dst_mbox->mbox_up.dev[vf]; + dst_mdev->mbase = src_mdev->mbase; + dst_mdev->msg_size = mbox_hdr->msg_size; + dst_mdev->num_msgs = mbox_hdr->num_msgs; + err = otx2_sync_mbox_up_msg(dst_mbox, vf); + if (err) { + dev_warn(pf->dev, + "VF%d is not responding to mailbox\n", vf); + return err; + } + } else if (dir == MBOX_DIR_VFPF_UP) { + req_hdr = (struct mbox_hdr *)(src_mbox->dev[0].mbase + + src_mbox->rx_start); + req_hdr->num_msgs = num_msgs; + otx2_forward_msg_pfvf(&pf->mbox_pfvf->mbox_up.dev[vf], + &pf->mbox.mbox_up, + pf->mbox_pfvf[vf].bbuf_base, + 0); + } + + return 0; +} + +static void otx2_pfvf_mbox_handler(struct work_struct *work) +{ + struct mbox_msghdr *msg = NULL; + int offset, vf_idx, id, err; + struct otx2_mbox_dev *mdev; + struct mbox_hdr *req_hdr; + struct otx2_mbox *mbox; + struct mbox *vf_mbox; + struct otx2_nic *pf; + + vf_mbox = container_of(work, struct mbox, mbox_wrk); + pf = vf_mbox->pfvf; + vf_idx = vf_mbox - pf->mbox_pfvf; + + mbox = &pf->mbox_pfvf[0].mbox; + mdev = &mbox->dev[vf_idx]; + req_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + + offset = ALIGN(sizeof(*req_hdr), MBOX_MSG_ALIGN); + + for (id = 0; id < vf_mbox->num_msgs; id++) { + msg = (struct mbox_msghdr *)(mdev->mbase + mbox->rx_start + + offset); + + if (msg->sig != OTX2_MBOX_REQ_SIG) + goto inval_msg; + + /* Set VF's number in each of the msg */ + msg->pcifunc &= RVU_PFVF_FUNC_MASK; + msg->pcifunc |= (vf_idx + 1) & RVU_PFVF_FUNC_MASK; + offset = msg->next_msgoff; + } + err = otx2_forward_vf_mbox_msgs(pf, mbox, MBOX_DIR_PFAF, vf_idx, + vf_mbox->num_msgs); + if (err) + goto inval_msg; + return; + +inval_msg: + otx2_reply_invalid_msg(mbox, vf_idx, 0, msg->id); + otx2_mbox_msg_send(mbox, vf_idx); +} + +static void otx2_pfvf_mbox_up_handler(struct work_struct *work) +{ + struct mbox *vf_mbox = container_of(work, struct mbox, mbox_up_wrk); + struct otx2_nic *pf = vf_mbox->pfvf; + struct otx2_mbox_dev *mdev; + int offset, id, vf_idx = 0; + struct mbox_hdr *rsp_hdr; + struct mbox_msghdr *msg; + struct otx2_mbox *mbox; + + vf_idx = vf_mbox - pf->mbox_pfvf; + mbox = &pf->mbox_pfvf[0].mbox_up; + mdev = &mbox->dev[vf_idx]; + + rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); + + for (id = 0; id < vf_mbox->up_num_msgs; id++) { + msg = mdev->mbase + offset; + + if (msg->id >= MBOX_MSG_MAX) { + dev_err(pf->dev, + "Mbox msg with unknown ID 0x%x\n", msg->id); + goto end; + } + + if (msg->sig != OTX2_MBOX_RSP_SIG) { + dev_err(pf->dev, + "Mbox msg with wrong signature %x, ID 0x%x\n", + msg->sig, msg->id); + goto end; + } + + switch (msg->id) { + case MBOX_MSG_CGX_LINK_EVENT: + break; + default: + if (msg->rc) + dev_err(pf->dev, + "Mbox msg response has err %d, ID 0x%x\n", + msg->rc, msg->id); + break; + } + +end: + offset = mbox->rx_start + msg->next_msgoff; + mdev->msgs_acked++; + } + + otx2_mbox_reset(mbox, vf_idx); +} + +static irqreturn_t otx2_pfvf_mbox_intr_handler(int irq, void *pf_irq) +{ + struct otx2_nic *pf = (struct otx2_nic *)(pf_irq); + int vfs = pf->total_vfs; + struct mbox *mbox; + u64 intr; + + mbox = pf->mbox_pfvf; + /* Handle VF interrupts */ + if (vfs > 64) { + intr = otx2_read64(pf, RVU_PF_VFPF_MBOX_INTX(1)); + otx2_write64(pf, RVU_PF_VFPF_MBOX_INTX(1), intr); + otx2_queue_work(mbox, pf->mbox_pfvf_wq, 64, vfs, intr, + TYPE_PFVF); + vfs -= 64; + } + + intr = otx2_read64(pf, RVU_PF_VFPF_MBOX_INTX(0)); + otx2_write64(pf, RVU_PF_VFPF_MBOX_INTX(0), intr); + + otx2_queue_work(mbox, pf->mbox_pfvf_wq, 0, vfs, intr, TYPE_PFVF); + + return IRQ_HANDLED; +} + +static int otx2_pfvf_mbox_init(struct otx2_nic *pf, int numvfs) +{ + void __iomem *hwbase; + struct mbox *mbox; + int err, vf; + u64 base; + + if (!numvfs) + return -EINVAL; + + pf->mbox_pfvf = devm_kcalloc(&pf->pdev->dev, numvfs, + sizeof(struct mbox), GFP_KERNEL); + if (!pf->mbox_pfvf) + return -ENOMEM; + + pf->mbox_pfvf_wq = alloc_workqueue("otx2_pfvf_mailbox", + WQ_UNBOUND | WQ_HIGHPRI | + WQ_MEM_RECLAIM, 1); + if (!pf->mbox_pfvf_wq) + return -ENOMEM; + + base = readq((void __iomem *)((u64)pf->reg_base + RVU_PF_VF_BAR4_ADDR)); + hwbase = ioremap_wc(base, MBOX_SIZE * pf->total_vfs); + + if (!hwbase) { + err = -ENOMEM; + goto free_wq; + } + + mbox = &pf->mbox_pfvf[0]; + err = otx2_mbox_init(&mbox->mbox, hwbase, pf->pdev, pf->reg_base, + MBOX_DIR_PFVF, numvfs); + if (err) + goto free_iomem; + + err = otx2_mbox_init(&mbox->mbox_up, hwbase, pf->pdev, pf->reg_base, + MBOX_DIR_PFVF_UP, numvfs); + if (err) + goto free_iomem; + + for (vf = 0; vf < numvfs; vf++) { + mbox->pfvf = pf; + INIT_WORK(&mbox->mbox_wrk, otx2_pfvf_mbox_handler); + INIT_WORK(&mbox->mbox_up_wrk, otx2_pfvf_mbox_up_handler); + mbox++; + } + + return 0; + +free_iomem: + if (hwbase) + iounmap(hwbase); +free_wq: + destroy_workqueue(pf->mbox_pfvf_wq); + return err; +} + +static void otx2_pfvf_mbox_destroy(struct otx2_nic *pf) +{ + struct mbox *mbox = &pf->mbox_pfvf[0]; + + if (!mbox) + return; + + if (pf->mbox_pfvf_wq) { + flush_workqueue(pf->mbox_pfvf_wq); + destroy_workqueue(pf->mbox_pfvf_wq); + pf->mbox_pfvf_wq = NULL; + } + + if (mbox->mbox.hwbase) + iounmap(mbox->mbox.hwbase); + + otx2_mbox_destroy(&mbox->mbox); +} + +static void otx2_enable_pfvf_mbox_intr(struct otx2_nic *pf) +{ + int bits; + + /* Clear PF <=> VF mailbox IRQ */ + otx2_write64(pf, RVU_PF_VFPF_MBOX_INTX(0), ~0ull); + otx2_write64(pf, RVU_PF_VFPF_MBOX_INTX(1), ~0ull); + + /* Enable PF <=> VF mailbox IRQ */ + bits = ((pf->total_vfs - 1) % 64); + otx2_write64(pf, RVU_PF_VFPF_MBOX_INT_ENA_W1SX(0), + GENMASK_ULL(bits, 0)); + + if (pf->total_vfs > 64) { + bits = pf->total_vfs - 64 - 1; + otx2_write64(pf, RVU_PF_VFPF_MBOX_INT_ENA_W1SX(1), + GENMASK_ULL(bits, 0)); + } +} + +static void otx2_disable_pfvf_mbox_intr(struct otx2_nic *pf) +{ + int vector; + + /* Disable PF <=> VF mailbox IRQ */ + otx2_write64(pf, RVU_PF_VFPF_MBOX_INT_ENA_W1CX(0), ~0ull); + otx2_write64(pf, RVU_PF_VFPF_MBOX_INT_ENA_W1CX(1), ~0ull); + + otx2_write64(pf, RVU_PF_VFPF_MBOX_INTX(0), ~0ull); + vector = pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_VFPF_MBOX0); + free_irq(vector, pf); + + if (pf->total_vfs > 64) { + otx2_write64(pf, RVU_PF_VFPF_MBOX_INTX(1), ~0ull); + vector = pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_VFPF_MBOX1); + free_irq(vector, pf); + } +} + +static int otx2_register_pfvf_mbox_intr(struct otx2_nic *pf) +{ + struct otx2_hw *hw = &pf->hw; + char *irq_name; + int err; + + /* Register MBOX0 interrupt handler */ + irq_name = &hw->irq_name[RVU_PF_INT_VEC_VFPF_MBOX0 * NAME_SIZE]; + if (pf->pcifunc) + snprintf(irq_name, NAME_SIZE, + "RVUPF%d_VF Mbox0", rvu_get_pf(pf->pcifunc)); + else + snprintf(irq_name, NAME_SIZE, "RVUPF_VF Mbox0"); + err = request_irq(pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_VFPF_MBOX0), + otx2_pfvf_mbox_intr_handler, 0, irq_name, pf); + if (err) { + dev_err(pf->dev, + "RVUPF: IRQ registration failed for PFAF mbox0 irq\n"); + return err; + } + + if (pf->total_vfs > 64) { + /* Register MBOX1 interrupt handler */ + irq_name = &hw->irq_name[RVU_PF_INT_VEC_VFPF_MBOX1 * NAME_SIZE]; + if (pf->pcifunc) + snprintf(irq_name, NAME_SIZE, + "RVUPF%d_VF Mbox1", rvu_get_pf(pf->pcifunc)); + else + snprintf(irq_name, NAME_SIZE, "RVUPF_VF Mbox1"); + err = request_irq(pci_irq_vector(pf->pdev, + RVU_PF_INT_VEC_VFPF_MBOX1), + otx2_pfvf_mbox_intr_handler, + 0, irq_name, pf); + if (err) { + dev_err(pf->dev, + "RVUPF: IRQ registration failed for PFAF mbox1 irq\n"); + return err; + } + } + + otx2_enable_pfvf_mbox_intr(pf); + + return 0; +} + +static void otx2_process_pfaf_mbox_msg(struct otx2_nic *pf, + struct mbox_msghdr *msg) +{ + int devid; + + if (msg->id >= MBOX_MSG_MAX) { + dev_err(pf->dev, + "Mbox msg with unknown ID 0x%x\n", msg->id); + return; + } + + if (msg->sig != OTX2_MBOX_RSP_SIG) { + dev_err(pf->dev, + "Mbox msg with wrong signature %x, ID 0x%x\n", + msg->sig, msg->id); + return; + } + + /* message response heading VF */ + devid = msg->pcifunc & RVU_PFVF_FUNC_MASK; + if (devid) { + struct otx2_vf_config *config = &pf->vf_configs[devid - 1]; + struct delayed_work *dwork; + + switch (msg->id) { + case MBOX_MSG_NIX_LF_START_RX: + config->intf_down = false; + dwork = &config->link_event_work; + schedule_delayed_work(dwork, msecs_to_jiffies(100)); + break; + case MBOX_MSG_NIX_LF_STOP_RX: + config->intf_down = true; + break; + } + + return; + } + + switch (msg->id) { + case MBOX_MSG_READY: + pf->pcifunc = msg->pcifunc; + break; + case MBOX_MSG_MSIX_OFFSET: + mbox_handler_msix_offset(pf, (struct msix_offset_rsp *)msg); + break; + case MBOX_MSG_NPA_LF_ALLOC: + mbox_handler_npa_lf_alloc(pf, (struct npa_lf_alloc_rsp *)msg); + break; + case MBOX_MSG_NIX_LF_ALLOC: + mbox_handler_nix_lf_alloc(pf, (struct nix_lf_alloc_rsp *)msg); + break; + case MBOX_MSG_NIX_TXSCH_ALLOC: + mbox_handler_nix_txsch_alloc(pf, + (struct nix_txsch_alloc_rsp *)msg); + break; + case MBOX_MSG_NIX_BP_ENABLE: + mbox_handler_nix_bp_enable(pf, (struct nix_bp_cfg_rsp *)msg); + break; + case MBOX_MSG_CGX_STATS: + mbox_handler_cgx_stats(pf, (struct cgx_stats_rsp *)msg); + break; + case MBOX_MSG_CGX_FEC_STATS: + mbox_handler_cgx_fec_stats(pf, (struct cgx_fec_stats_rsp *)msg); + break; + default: + if (msg->rc) + dev_err(pf->dev, + "Mbox msg response has err %d, ID 0x%x\n", + msg->rc, msg->id); + break; + } +} + +static void otx2_pfaf_mbox_handler(struct work_struct *work) +{ + struct otx2_mbox_dev *mdev; + struct mbox_hdr *rsp_hdr; + struct mbox_msghdr *msg; + struct otx2_mbox *mbox; + struct mbox *af_mbox; + struct otx2_nic *pf; + int offset, id; + + af_mbox = container_of(work, struct mbox, mbox_wrk); + mbox = &af_mbox->mbox; + mdev = &mbox->dev[0]; + rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + + offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); + pf = af_mbox->pfvf; + + for (id = 0; id < af_mbox->num_msgs; id++) { + msg = (struct mbox_msghdr *)(mdev->mbase + offset); + otx2_process_pfaf_mbox_msg(pf, msg); + offset = mbox->rx_start + msg->next_msgoff; + mdev->msgs_acked++; + } + + otx2_mbox_reset(mbox, 0); + + /* Clear the IRQ */ + smp_wmb(); + otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); +} + +static void otx2_handle_link_event(struct otx2_nic *pf) +{ + struct cgx_link_user_info *linfo = &pf->linfo; + struct net_device *netdev = pf->netdev; + + pr_info("%s NIC Link is %s %d Mbps %s duplex\n", netdev->name, + linfo->link_up ? "UP" : "DOWN", linfo->speed, + linfo->full_duplex ? "Full" : "Half"); + if (linfo->link_up) { + netif_carrier_on(netdev); + netif_tx_start_all_queues(netdev); + } else { + netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); + } +} + +int otx2_mbox_up_handler_cgx_link_event(struct otx2_nic *pf, + struct cgx_link_info_msg *msg, + struct msg_rsp *rsp) +{ + int i; + + pf->linfo = msg->link_info; + + /* notify VFs about link event */ + for (i = 0; i < pci_num_vf(pf->pdev); i++) { + struct otx2_vf_config *config = &pf->vf_configs[i]; + struct delayed_work *dwork = &config->link_event_work; + + if (config->intf_down) + continue; + + schedule_delayed_work(dwork, msecs_to_jiffies(100)); + } + + /* interface has not been fully configured yet */ + if (pf->flags & OTX2_FLAG_INTF_DOWN) + return 0; + + otx2_handle_link_event(pf); + return 0; +} + +int otx2_mbox_up_handler_cgx_ptp_rx_info(struct otx2_nic *pf, + struct cgx_ptp_rx_info_msg *msg, + struct msg_rsp *rsp) +{ + int i; + + pf->ptp->ptp_en = msg->ptp_en; + + /* notify VFs about ptp event */ + for (i = 0; i < pci_num_vf(pf->pdev); i++) { + struct otx2_vf_config *config = &pf->vf_configs[i]; + struct delayed_work *dwork = &config->ptp_info_work; + + if (config->intf_down) + continue; + + schedule_delayed_work(dwork, msecs_to_jiffies(100)); + } + return 0; +} + +static int otx2_process_mbox_msg_up(struct otx2_nic *pf, + struct mbox_msghdr *req) +{ + /* Check if valid, if not reply with a invalid msg */ + if (req->sig != OTX2_MBOX_REQ_SIG) { + otx2_reply_invalid_msg(&pf->mbox.mbox_up, 0, 0, req->id); + return -ENODEV; + } + + switch (req->id) { +#define M(_name, _id, _fn_name, _req_type, _rsp_type) \ + case _id: { \ + struct _rsp_type *rsp; \ + int err; \ + \ + rsp = (struct _rsp_type *)otx2_mbox_alloc_msg( \ + &pf->mbox.mbox_up, 0, \ + sizeof(struct _rsp_type)); \ + if (!rsp) \ + return -ENOMEM; \ + \ + rsp->hdr.id = _id; \ + rsp->hdr.sig = OTX2_MBOX_RSP_SIG; \ + rsp->hdr.pcifunc = 0; \ + rsp->hdr.rc = 0; \ + \ + err = otx2_mbox_up_handler_ ## _fn_name( \ + pf, (struct _req_type *)req, rsp); \ + return err; \ + } +MBOX_UP_CGX_MESSAGES +#undef M + break; + default: + otx2_reply_invalid_msg(&pf->mbox.mbox_up, 0, 0, req->id); + return -ENODEV; + } + return 0; +} + +static void otx2_pfaf_mbox_up_handler(struct work_struct *work) +{ + struct mbox *af_mbox = container_of(work, struct mbox, mbox_up_wrk); + struct otx2_mbox *mbox = &af_mbox->mbox_up; + struct otx2_mbox_dev *mdev = &mbox->dev[0]; + struct otx2_nic *pf = af_mbox->pfvf; + int offset, id, devid = 0; + struct mbox_hdr *rsp_hdr; + struct mbox_msghdr *msg; + + rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + + offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); + + for (id = 0; id < af_mbox->up_num_msgs; id++) { + msg = (struct mbox_msghdr *)(mdev->mbase + offset); + + devid = msg->pcifunc & RVU_PFVF_FUNC_MASK; + /* Skip processing VF's messages */ + if (!devid) + otx2_process_mbox_msg_up(pf, msg); + offset = mbox->rx_start + msg->next_msgoff; + } + if (devid) { + otx2_forward_vf_mbox_msgs(pf, &pf->mbox.mbox_up, + MBOX_DIR_PFVF_UP, devid - 1, + af_mbox->up_num_msgs); + return; + } + + otx2_mbox_msg_send(mbox, 0); +} + +static irqreturn_t otx2_pfaf_mbox_intr_handler(int irq, void *pf_irq) +{ + struct otx2_nic *pf = (struct otx2_nic *)pf_irq; + struct mbox *mbox; + + /* Clear the IRQ */ + otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); + + mbox = &pf->mbox; + otx2_queue_work(mbox, pf->mbox_wq, 0, 1, 1, TYPE_PFAF); + + return IRQ_HANDLED; +} + +static void otx2_disable_mbox_intr(struct otx2_nic *pf) +{ + int vector = pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_AFPF_MBOX); + + /* Disable AF => PF mailbox IRQ */ + otx2_write64(pf, RVU_PF_INT_ENA_W1C, BIT_ULL(0)); + free_irq(vector, pf); +} + +static int otx2_register_mbox_intr(struct otx2_nic *pf, bool probe_af) +{ + struct otx2_hw *hw = &pf->hw; + struct msg_req *req; + char *irq_name; + int err; + + /* Register mailbox interrupt handler */ + irq_name = &hw->irq_name[RVU_PF_INT_VEC_AFPF_MBOX * NAME_SIZE]; + snprintf(irq_name, NAME_SIZE, "RVUPFAF Mbox"); + err = request_irq(pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_AFPF_MBOX), + otx2_pfaf_mbox_intr_handler, 0, irq_name, pf); + if (err) { + dev_err(pf->dev, + "RVUPF: IRQ registration failed for PFAF mbox irq\n"); + return err; + } + + /* Enable mailbox interrupt for msgs coming from AF. + * First clear to avoid spurious interrupts, if any. + */ + otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); + otx2_write64(pf, RVU_PF_INT_ENA_W1S, BIT_ULL(0)); + + if (!probe_af) + return 0; + + /* Check mailbox communication with AF */ + req = otx2_mbox_alloc_msg_ready(&pf->mbox); + if (!req) { + otx2_disable_mbox_intr(pf); + return -ENOMEM; + } + err = otx2_sync_mbox_msg(&pf->mbox); + if (err) { + dev_warn(pf->dev, + "AF not responding to mailbox, deferring probe\n"); + otx2_disable_mbox_intr(pf); + return -EPROBE_DEFER; + } + + return 0; +} + +static void otx2_pfaf_mbox_destroy(struct otx2_nic *pf) +{ + struct mbox *mbox = &pf->mbox; + + if (pf->mbox_wq) { + flush_workqueue(pf->mbox_wq); + destroy_workqueue(pf->mbox_wq); + pf->mbox_wq = NULL; + } + + if (mbox->mbox.hwbase) + iounmap((void __iomem *)mbox->mbox.hwbase); + + otx2_mbox_destroy(&mbox->mbox); + otx2_mbox_destroy(&mbox->mbox_up); +} + +static int otx2_pfaf_mbox_init(struct otx2_nic *pf) +{ + struct mbox *mbox = &pf->mbox; + void __iomem *hwbase; + int err; + + mbox->pfvf = pf; + pf->mbox_wq = alloc_workqueue("otx2_pfaf_mailbox", + WQ_UNBOUND | WQ_HIGHPRI | + WQ_MEM_RECLAIM, 1); + if (!pf->mbox_wq) + return -ENOMEM; + + /* Mailbox is a reserved memory (in RAM) region shared between + * admin function (i.e AF) and this PF, shouldn't be mapped as + * device memory to allow unaligned accesses. + */ + hwbase = ioremap_wc(pci_resource_start(pf->pdev, PCI_MBOX_BAR_NUM), + pci_resource_len(pf->pdev, PCI_MBOX_BAR_NUM)); + if (!hwbase) { + dev_err(pf->dev, "Unable to map PFAF mailbox region\n"); + err = -ENOMEM; + goto exit; + } + + err = otx2_mbox_init(&mbox->mbox, hwbase, pf->pdev, pf->reg_base, + MBOX_DIR_PFAF, 1); + if (err) + goto exit; + + err = otx2_mbox_init(&mbox->mbox_up, hwbase, pf->pdev, pf->reg_base, + MBOX_DIR_PFAF_UP, 1); + if (err) + goto exit; + + err = otx2_mbox_bbuf_init(mbox, pf->pdev); + if (err) + goto exit; + + INIT_WORK(&mbox->mbox_wrk, otx2_pfaf_mbox_handler); + INIT_WORK(&mbox->mbox_up_wrk, otx2_pfaf_mbox_up_handler); + otx2_mbox_lock_init(&pf->mbox); + + return 0; +exit: + destroy_workqueue(pf->mbox_wq); + return err; +} + +static int otx2_cgx_config_linkevents(struct otx2_nic *pf, bool enable) +{ + struct msg_req *msg; + int err; + + otx2_mbox_lock(&pf->mbox); + if (enable) + msg = otx2_mbox_alloc_msg_cgx_start_linkevents(&pf->mbox); + else + msg = otx2_mbox_alloc_msg_cgx_stop_linkevents(&pf->mbox); + + if (!msg) { + otx2_mbox_unlock(&pf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pf->mbox); + otx2_mbox_unlock(&pf->mbox); + return err; +} + +static int otx2_cgx_config_loopback(struct otx2_nic *pf, bool enable) +{ + struct msg_req *msg; + int err; + + otx2_mbox_lock(&pf->mbox); + if (enable) + msg = otx2_mbox_alloc_msg_cgx_intlbk_enable(&pf->mbox); + else + msg = otx2_mbox_alloc_msg_cgx_intlbk_disable(&pf->mbox); + + if (!msg) { + otx2_mbox_unlock(&pf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pf->mbox); + otx2_mbox_unlock(&pf->mbox); + return err; +} + +int otx2_set_real_num_queues(struct net_device *netdev, + int tx_queues, int rx_queues) +{ + int err; + + err = netif_set_real_num_tx_queues(netdev, tx_queues); + if (err) { + netdev_err(netdev, + "Failed to set no of Tx queues: %d\n", tx_queues); + return err; + } + + err = netif_set_real_num_rx_queues(netdev, rx_queues); + if (err) + netdev_err(netdev, + "Failed to set no of Rx queues: %d\n", rx_queues); + return err; +} +EXPORT_SYMBOL(otx2_set_real_num_queues); + +static irqreturn_t otx2_q_intr_handler(int irq, void *data) +{ + struct otx2_nic *pf = data; + u64 val, *ptr; + u64 qidx = 0; + + /* CQ */ + for (qidx = 0; qidx < pf->qset.cq_cnt; qidx++) { + ptr = otx2_get_regaddr(pf, NIX_LF_CQ_OP_INT); + val = otx2_atomic64_add((qidx << 44), ptr); + + otx2_write64(pf, NIX_LF_CQ_OP_INT, (qidx << 44) | + (val & NIX_CQERRINT_BITS)); + if (!(val & (NIX_CQERRINT_BITS | BIT_ULL(42)))) + continue; + + if (val & BIT_ULL(42)) { + netdev_err(pf->netdev, "CQ%lld: error reading NIX_LF_CQ_OP_INT, NIX_LF_ERR_INT 0x%llx\n", + qidx, otx2_read64(pf, NIX_LF_ERR_INT)); + } else { + if (val & BIT_ULL(NIX_CQERRINT_DOOR_ERR)) + netdev_err(pf->netdev, "CQ%lld: Doorbell error", + qidx); + if (val & BIT_ULL(NIX_CQERRINT_CQE_FAULT)) + netdev_err(pf->netdev, "CQ%lld: Memory fault on CQE write to LLC/DRAM", + qidx); + } + + schedule_work(&pf->reset_task); + } + + /* SQ */ + for (qidx = 0; qidx < pf->hw.tx_queues; qidx++) { + ptr = otx2_get_regaddr(pf, NIX_LF_SQ_OP_INT); + val = otx2_atomic64_add((qidx << 44), ptr); + otx2_write64(pf, NIX_LF_SQ_OP_INT, (qidx << 44) | + (val & NIX_SQINT_BITS)); + + if (!(val & (NIX_SQINT_BITS | BIT_ULL(42)))) + continue; + + if (val & BIT_ULL(42)) { + netdev_err(pf->netdev, "SQ%lld: error reading NIX_LF_SQ_OP_INT, NIX_LF_ERR_INT 0x%llx\n", + qidx, otx2_read64(pf, NIX_LF_ERR_INT)); + } else { + if (val & BIT_ULL(NIX_SQINT_LMT_ERR)) { + netdev_err(pf->netdev, "SQ%lld: LMT store error NIX_LF_SQ_OP_ERR_DBG:0x%llx", + qidx, + otx2_read64(pf, + NIX_LF_SQ_OP_ERR_DBG)); + otx2_write64(pf, NIX_LF_SQ_OP_ERR_DBG, + BIT_ULL(44)); + } + if (val & BIT_ULL(NIX_SQINT_MNQ_ERR)) { + netdev_err(pf->netdev, "SQ%lld: Meta-descriptor enqueue error NIX_LF_MNQ_ERR_DGB:0x%llx\n", + qidx, + otx2_read64(pf, NIX_LF_MNQ_ERR_DBG)); + otx2_write64(pf, NIX_LF_MNQ_ERR_DBG, + BIT_ULL(44)); + } + if (val & BIT_ULL(NIX_SQINT_SEND_ERR)) { + netdev_err(pf->netdev, "SQ%lld: Send error, NIX_LF_SEND_ERR_DBG 0x%llx", + qidx, + otx2_read64(pf, + NIX_LF_SEND_ERR_DBG)); + otx2_write64(pf, NIX_LF_SEND_ERR_DBG, + BIT_ULL(44)); + } + if (val & BIT_ULL(NIX_SQINT_SQB_ALLOC_FAIL)) + netdev_err(pf->netdev, "SQ%lld: SQB allocation failed", + qidx); + } + + schedule_work(&pf->reset_task); + } + + return IRQ_HANDLED; +} + +static irqreturn_t otx2_cq_intr_handler(int irq, void *cq_irq) +{ + struct otx2_cq_poll *cq_poll = (struct otx2_cq_poll *)cq_irq; + struct otx2_nic *pf = (struct otx2_nic *)cq_poll->dev; + int qidx = cq_poll->cint_idx; + + /* Disable interrupts. + * + * Completion interrupts behave in a level-triggered interrupt + * fashion, and hence have to be cleared only after it is serviced. + */ + otx2_write64(pf, NIX_LF_CINTX_ENA_W1C(qidx), BIT_ULL(0)); + + /* Schedule NAPI */ + napi_schedule_irqoff(&cq_poll->napi); + + return IRQ_HANDLED; +} + +static void otx2_disable_napi(struct otx2_nic *pf) +{ + struct otx2_qset *qset = &pf->qset; + struct otx2_cq_poll *cq_poll; + int qidx; + + for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { + cq_poll = &qset->napi[qidx]; + napi_disable(&cq_poll->napi); + netif_napi_del(&cq_poll->napi); + } +} + +static void otx2_free_cq_res(struct otx2_nic *pf) +{ + struct otx2_qset *qset = &pf->qset; + struct mbox *mbox = &pf->mbox; + struct otx2_cq_queue *cq; + int qidx; + + /* Disable CQs*/ + otx2_ctx_disable(mbox, NIX_AQ_CTYPE_CQ, false); + for (qidx = 0; qidx < qset->cq_cnt; qidx++) { + cq = &qset->cq[qidx]; + qmem_free(pf->dev, cq->cqe); + } +} + +static void otx2_free_sq_res(struct otx2_nic *pf) +{ + struct otx2_qset *qset = &pf->qset; + struct mbox *mbox = &pf->mbox; + struct otx2_snd_queue *sq; + int qidx; + + /* Disable SQs */ + otx2_ctx_disable(mbox, NIX_AQ_CTYPE_SQ, false); + /* Free SQB pointers */ + otx2_sq_free_sqbs(pf); + for (qidx = 0; qidx < pf->hw.tx_queues; qidx++) { + sq = &qset->sq[qidx]; + qmem_free(pf->dev, sq->sqe); + qmem_free(pf->dev, sq->tso_hdrs); + kfree(sq->sg); + kfree(sq->sqb_ptrs); + qmem_free(pf->dev, sq->timestamps); + } +} + +static int otx2_init_hw_resources(struct otx2_nic *pf) +{ + struct nix_lf_free_req *free_req; + struct mbox *mbox = &pf->mbox; + struct otx2_hw *hw = &pf->hw; + struct msg_req *req; + int err = 0, lvl; + + /* Set required NPA LF's pool counts + * Auras and Pools are used in a 1:1 mapping, + * so, aura count = pool count. + */ + hw->rqpool_cnt = hw->rx_queues; + hw->sqpool_cnt = hw->tx_queues; + hw->pool_cnt = hw->rqpool_cnt + hw->sqpool_cnt; + + otx2_mbox_lock(mbox); + /* NPA init */ + err = otx2_config_npa(pf); + if (err) + goto exit; + + /* NIX init */ + err = otx2_config_nix(pf); + if (err) + goto err_free_npa_lf; + + /* Enable backpressure */ + otx2_nix_config_bp(pf, true); + + /* Init Auras and pools used by NIX RQ, for free buffer ptrs */ + err = otx2_rq_aura_pool_init(pf); + if (err) { + otx2_mbox_unlock(mbox); + goto err_free_nix_lf; + } + /* Init Auras and pools used by NIX SQ, for queueing SQEs */ + err = otx2_sq_aura_pool_init(pf); + if (err) { + otx2_mbox_unlock(mbox); + goto err_free_rq_ptrs; + } + + err = otx2_txsch_alloc(pf); + if (err) { + otx2_mbox_unlock(mbox); + goto err_free_sq_ptrs; + } + + err = otx2_config_nix_queues(pf); + if (err) { + otx2_mbox_unlock(mbox); + goto err_free_txsch; + } + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + err = otx2_txschq_config(pf, lvl); + if (err) { + otx2_mbox_unlock(mbox); + goto err_free_nix_queues; + } + } + otx2_mbox_unlock(mbox); + return err; + +err_free_nix_queues: + otx2_free_sq_res(pf); + otx2_free_cq_res(pf); + otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false); +err_free_txsch: + otx2_txschq_stop(pf); +err_free_sq_ptrs: + otx2_sq_free_sqbs(pf); +err_free_rq_ptrs: + otx2_free_aura_ptr(pf, AURA_NIX_RQ); + otx2_ctx_disable(mbox, NPA_AQ_CTYPE_POOL, true); + otx2_ctx_disable(mbox, NPA_AQ_CTYPE_AURA, true); + otx2_aura_pool_free(pf); +err_free_nix_lf: + otx2_mbox_lock(mbox); + free_req = otx2_mbox_alloc_msg_nix_lf_free(mbox); + if (free_req) { + free_req->flags = NIX_LF_DISABLE_FLOWS; + WARN_ON(otx2_sync_mbox_msg(mbox)); + } +err_free_npa_lf: + /* Reset NPA LF */ + req = otx2_mbox_alloc_msg_npa_lf_free(mbox); + if (req) + WARN_ON(otx2_sync_mbox_msg(mbox)); +exit: + otx2_mbox_unlock(mbox); + return err; +} + +static void otx2_free_hw_resources(struct otx2_nic *pf) +{ + struct otx2_qset *qset = &pf->qset; + struct nix_lf_free_req *free_req; + struct mbox *mbox = &pf->mbox; + struct otx2_cq_queue *cq; + struct msg_req *req; + int qidx, err; + + /* Stop transmission */ + err = otx2_txschq_stop(pf); + if (err) + dev_err(pf->dev, "RVUPF: Failed to stop/free TX schedulers\n"); + + otx2_mbox_lock(mbox); + /* Disable backpressure */ + if (!(pf->pcifunc & RVU_PFVF_FUNC_MASK)) + otx2_nix_config_bp(pf, false); + otx2_mbox_unlock(mbox); + + otx2_free_sq_res(pf); + + /* Disable RQs */ + otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false); + + /*Dequeue all CQEs */ + for (qidx = 0; qidx < qset->cq_cnt; qidx++) { + cq = &qset->cq[qidx]; + if (cq->cq_type == CQ_RX) + otx2_cleanup_rx_cqes(pf, cq); + else + otx2_cleanup_tx_cqes(pf, cq); + } + + /* Free RQ buffer pointers*/ + otx2_free_aura_ptr(pf, AURA_NIX_RQ); + + otx2_free_cq_res(pf); + + otx2_mbox_lock(mbox); + /* Reset NIX LF */ + free_req = otx2_mbox_alloc_msg_nix_lf_free(mbox); + if (free_req) { + free_req->flags = NIX_LF_DISABLE_FLOWS; + WARN_ON(otx2_sync_mbox_msg(mbox)); + } + otx2_mbox_unlock(mbox); + + /* Disable NPA Pool and Aura hw context */ + otx2_ctx_disable(mbox, NPA_AQ_CTYPE_POOL, true); + otx2_ctx_disable(mbox, NPA_AQ_CTYPE_AURA, true); + otx2_aura_pool_free(pf); + + otx2_mbox_lock(mbox); + /* Reset NPA LF */ + req = otx2_mbox_alloc_msg_npa_lf_free(mbox); + if (req) + WARN_ON(otx2_sync_mbox_msg(mbox)); + otx2_mbox_unlock(mbox); +} + +static netdev_tx_t otx2_xmit(struct sk_buff *skb, struct net_device *netdev) + +{ + struct otx2_nic *pf = netdev_priv(netdev); + struct otx2_snd_queue *sq; + int qidx = skb_get_queue_mapping(skb); + struct netdev_queue *txq = netdev_get_tx_queue(netdev, qidx); + + /* Check for minimum and maximum packet length */ + if (skb->len <= ETH_HLEN || + (!skb_shinfo(skb)->gso_size && skb->len > pf->max_frs)) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + sq = &pf->qset.sq[qidx]; + + if (netif_tx_queue_stopped(txq)) { + dev_kfree_skb(skb); + } else if (!otx2_sq_append_skb(netdev, sq, skb, qidx)) { + netif_tx_stop_queue(txq); + + /* Check again, incase SQBs got freed up */ + smp_mb(); + if (((sq->num_sqbs - *sq->aura_fc_addr) * sq->sqe_per_sqb) + > sq->sqe_thresh) + netif_tx_wake_queue(txq); + + return NETDEV_TX_BUSY; + } + + return NETDEV_TX_OK; +} + +int otx2_open(struct net_device *netdev) +{ + struct otx2_nic *pf = netdev_priv(netdev); + struct otx2_cq_poll *cq_poll = NULL; + struct otx2_qset *qset = &pf->qset; + int err = 0, qidx, vec; + char *irq_name; + + netif_carrier_off(netdev); + + pf->qset.cq_cnt = pf->hw.rx_queues + pf->hw.tx_queues; + /* RQ and SQs are mapped to different CQs, + * so find out max CQ IRQs (i.e CINTs) needed. + */ + pf->hw.cint_cnt = max(pf->hw.rx_queues, pf->hw.tx_queues); + qset->napi = kcalloc(pf->hw.cint_cnt, sizeof(*cq_poll), GFP_KERNEL); + if (!qset->napi) + return -ENOMEM; + + /* CQ size of RQ */ + qset->rqe_cnt = qset->rqe_cnt ? qset->rqe_cnt : Q_COUNT(Q_SIZE_256); + /* CQ size of SQ */ + qset->sqe_cnt = qset->sqe_cnt ? qset->sqe_cnt : Q_COUNT(Q_SIZE_4K); + + err = -ENOMEM; + qset->cq = kcalloc(pf->qset.cq_cnt, + sizeof(struct otx2_cq_queue), GFP_KERNEL); + if (!qset->cq) + goto err_free_mem; + + qset->sq = kcalloc(pf->hw.tx_queues, + sizeof(struct otx2_snd_queue), GFP_KERNEL); + if (!qset->sq) + goto err_free_mem; + + qset->rq = kcalloc(pf->hw.rx_queues, + sizeof(struct otx2_rcv_queue), GFP_KERNEL); + if (!qset->rq) + goto err_free_mem; + + err = otx2_init_hw_resources(pf); + if (err) + goto err_free_mem; + + /* Register NAPI handler */ + for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { + cq_poll = &qset->napi[qidx]; + cq_poll->cint_idx = qidx; + /* RQ0 & SQ0 are mapped to CINT0 and so on.. + * 'cq_ids[0]' points to RQ's CQ and + * 'cq_ids[1]' points to SQ's CQ and + */ + cq_poll->cq_ids[CQ_RX] = + (qidx < pf->hw.rx_queues) ? qidx : CINT_INVALID_CQ; + cq_poll->cq_ids[CQ_TX] = (qidx < pf->hw.tx_queues) ? + qidx + pf->hw.rx_queues : CINT_INVALID_CQ; + cq_poll->dev = (void *)pf; + netif_napi_add(netdev, &cq_poll->napi, + otx2_napi_handler, NAPI_POLL_WEIGHT); + napi_enable(&cq_poll->napi); + } + + /* Set default MTU in HW */ + err = otx2_hw_set_mtu(pf, netdev->mtu); + if (err) + goto err_disable_napi; + + /* Initialize RSS */ + err = otx2_rss_init(pf); + if (err) + goto err_disable_napi; + + /* Register Queue IRQ handlers */ + vec = pf->hw.nix_msixoff + NIX_LF_QINT_VEC_START; + irq_name = &pf->hw.irq_name[vec * NAME_SIZE]; + + snprintf(irq_name, NAME_SIZE, "%s-qerr", pf->netdev->name); + + err = request_irq(pci_irq_vector(pf->pdev, vec), + otx2_q_intr_handler, 0, irq_name, pf); + if (err) { + dev_err(pf->dev, + "RVUPF%d: IRQ registration failed for QERR\n", + rvu_get_pf(pf->pcifunc)); + goto err_disable_napi; + } + + /* Enable QINT IRQ */ + otx2_write64(pf, NIX_LF_QINTX_ENA_W1S(0), BIT_ULL(0)); + + /* Register CQ IRQ handlers */ + vec = pf->hw.nix_msixoff + NIX_LF_CINT_VEC_START; + for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { + irq_name = &pf->hw.irq_name[vec * NAME_SIZE]; + + snprintf(irq_name, NAME_SIZE, "%s-rxtx-%d", pf->netdev->name, + qidx); + + err = request_irq(pci_irq_vector(pf->pdev, vec), + otx2_cq_intr_handler, 0, irq_name, + &qset->napi[qidx]); + if (err) { + dev_err(pf->dev, + "RVUPF%d: IRQ registration failed for CQ%d\n", + rvu_get_pf(pf->pcifunc), qidx); + goto err_free_cints; + } + vec++; + + otx2_config_irq_coalescing(pf, qidx); + + /* Enable CQ IRQ */ + otx2_write64(pf, NIX_LF_CINTX_INT(qidx), BIT_ULL(0)); + otx2_write64(pf, NIX_LF_CINTX_ENA_W1S(qidx), BIT_ULL(0)); + } + + otx2_set_cints_affinity(pf); + + pf->flags &= ~OTX2_FLAG_INTF_DOWN; + /* 'intf_down' may be checked on any cpu */ + smp_wmb(); + + /* we have already received link status notification */ + if (pf->linfo.link_up && !(pf->pcifunc & RVU_PFVF_FUNC_MASK)) + otx2_handle_link_event(pf); + + if (pf->flags & OTX2_FLAG_RX_VLAN_SUPPORT) + otx2_enable_rxvlan(pf, true); + + /* When reinitializing enable time stamping if it was enabled before */ + if (pf->flags & OTX2_FLAG_TX_TSTAMP_ENABLED) { + pf->flags &= ~OTX2_FLAG_TX_TSTAMP_ENABLED; + otx2_config_hw_tx_tstamp(pf, true); + } + if (pf->flags & OTX2_FLAG_RX_TSTAMP_ENABLED) { + pf->flags &= ~OTX2_FLAG_RX_TSTAMP_ENABLED; + otx2_config_hw_rx_tstamp(pf, true); + } + + /* Set NPC parsing mode, skip LBKs */ + if (!is_otx2_lbkvf(pf->pdev)) { + err = otx2_set_npc_parse_mode(pf); + if (err) + goto err_free_cints; + } + + err = otx2_rxtx_enable(pf, true); + if (err) + goto err_free_cints; + + return 0; + +err_free_cints: + otx2_free_cints(pf, qidx); + vec = pci_irq_vector(pf->pdev, + pf->hw.nix_msixoff + NIX_LF_QINT_VEC_START); + otx2_write64(pf, NIX_LF_QINTX_ENA_W1C(0), BIT_ULL(0)); + synchronize_irq(vec); + free_irq(vec, pf); +err_disable_napi: + otx2_disable_napi(pf); + otx2_free_hw_resources(pf); +err_free_mem: + kfree(qset->sq); + kfree(qset->cq); + kfree(qset->napi); + return err; +} +EXPORT_SYMBOL(otx2_open); + +int otx2_stop(struct net_device *netdev) +{ + struct otx2_nic *pf = netdev_priv(netdev); + struct otx2_cq_poll *cq_poll = NULL; + struct otx2_qset *qset = &pf->qset; + int qidx, vec, wrk; + + netif_carrier_off(netdev); + netif_tx_stop_all_queues(netdev); + + pf->flags |= OTX2_FLAG_INTF_DOWN; + /* 'intf_down' may be checked on any cpu */ + smp_wmb(); + + /* First stop packet Rx/Tx */ + otx2_rxtx_enable(pf, false); + + /* Cleanup Queue IRQ */ + vec = pci_irq_vector(pf->pdev, + pf->hw.nix_msixoff + NIX_LF_QINT_VEC_START); + otx2_write64(pf, NIX_LF_QINTX_ENA_W1C(0), BIT_ULL(0)); + synchronize_irq(vec); + free_irq(vec, pf); + + /* Cleanup CQ NAPI and IRQ */ + vec = pf->hw.nix_msixoff + NIX_LF_CINT_VEC_START; + for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { + /* Disable interrupt */ + otx2_write64(pf, NIX_LF_CINTX_ENA_W1C(qidx), BIT_ULL(0)); + + synchronize_irq(pci_irq_vector(pf->pdev, vec)); + + cq_poll = &qset->napi[qidx]; + napi_synchronize(&cq_poll->napi); + vec++; + } + + netif_tx_disable(netdev); + + otx2_free_hw_resources(pf); + otx2_free_cints(pf, pf->hw.cint_cnt); + otx2_disable_napi(pf); + + for (qidx = 0; qidx < netdev->num_tx_queues; qidx++) + netdev_tx_reset_queue(netdev_get_tx_queue(netdev, qidx)); + + for (wrk = 0; wrk < pf->qset.cq_cnt; wrk++) + cancel_delayed_work_sync(&pf->refill_wrk[wrk].pool_refill_work); + devm_kfree(pf->dev, pf->refill_wrk); + + kfree(qset->sq); + kfree(qset->cq); + kfree(qset->rq); + kfree(qset->napi); + /* Do not clear RQ/SQ ringsize settings */ + memset((void *)qset + offsetof(struct otx2_qset, sqe_cnt), 0, + sizeof(*qset) - offsetof(struct otx2_qset, sqe_cnt)); + return 0; +} +EXPORT_SYMBOL(otx2_stop); + +static netdev_features_t otx2_fix_features(struct net_device *dev, + netdev_features_t features) +{ + if (features & NETIF_F_HW_VLAN_CTAG_RX) + features |= NETIF_F_HW_VLAN_STAG_RX; + else + features &= ~NETIF_F_HW_VLAN_STAG_RX; + + return features; +} + +static void otx2_set_rx_mode(struct net_device *netdev) +{ + struct otx2_nic *pf = netdev_priv(netdev); + + queue_work(pf->otx2_ndo_wq, &pf->otx2_rx_mode_work); +} + +void otx2_do_set_rx_mode(struct work_struct *work) +{ + struct otx2_nic *pf = container_of(work, struct otx2_nic, + otx2_rx_mode_work); + struct net_device *netdev = pf->netdev; + struct nix_rx_mode *req; + + if (!(netdev->flags & IFF_UP)) + return; + + /* Write unicast address to mcam entries or del from mcam */ + if (netdev->priv_flags & IFF_UNICAST_FLT) + __dev_uc_sync(netdev, otx2_add_macfilter, otx2_del_macfilter); + + otx2_mbox_lock(&pf->mbox); + req = otx2_mbox_alloc_msg_nix_set_rx_mode(&pf->mbox); + if (!req) { + otx2_mbox_unlock(&pf->mbox); + return; + } + + req->mode = NIX_RX_MODE_UCAST; + + if (netdev->flags & IFF_PROMISC) + req->mode |= NIX_RX_MODE_PROMISC; + else if (netdev->flags & (IFF_ALLMULTI | IFF_MULTICAST)) + req->mode |= NIX_RX_MODE_ALLMULTI; + + otx2_sync_mbox_msg(&pf->mbox); + otx2_mbox_unlock(&pf->mbox); +} + +static void otx2_reset_task(struct work_struct *work) +{ + struct otx2_nic *pf = container_of(work, struct otx2_nic, reset_task); + + if (!netif_running(pf->netdev)) + return; + + otx2_stop(pf->netdev); + pf->reset_count++; + otx2_open(pf->netdev); + netif_trans_update(pf->netdev); +} + +static int otx2_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct otx2_nic *pf = netdev_priv(netdev); + netdev_features_t changed = features ^ netdev->features; + bool ntuple = !!(features & NETIF_F_NTUPLE); + + if ((changed & NETIF_F_LOOPBACK) && netif_running(netdev)) + return otx2_cgx_config_loopback(pf, + features & NETIF_F_LOOPBACK); + + if ((changed & NETIF_F_HW_VLAN_CTAG_RX) && netif_running(netdev)) + return otx2_enable_rxvlan(pf, + features & NETIF_F_HW_VLAN_CTAG_RX); + + if ((changed & NETIF_F_NTUPLE) && !ntuple) + otx2_destroy_ntuple_flows(pf); + + return 0; +} + +static int otx2_config_hw_rx_tstamp(struct otx2_nic *pfvf, bool enable) +{ + struct msg_req *req; + int err; + + if (pfvf->flags & OTX2_FLAG_RX_TSTAMP_ENABLED && enable) + return 0; + + otx2_mbox_lock(&pfvf->mbox); + if (enable) + req = otx2_mbox_alloc_msg_cgx_ptp_rx_enable(&pfvf->mbox); + else + req = otx2_mbox_alloc_msg_cgx_ptp_rx_disable(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + otx2_mbox_unlock(&pfvf->mbox); + return err; + } + + otx2_mbox_unlock(&pfvf->mbox); + if (enable) + pfvf->flags |= OTX2_FLAG_RX_TSTAMP_ENABLED; + else + pfvf->flags &= ~OTX2_FLAG_RX_TSTAMP_ENABLED; + return 0; +} + +static int otx2_config_hw_tx_tstamp(struct otx2_nic *pfvf, bool enable) +{ + struct msg_req *req; + int err; + + if (pfvf->flags & OTX2_FLAG_TX_TSTAMP_ENABLED && enable) + return 0; + + otx2_mbox_lock(&pfvf->mbox); + if (enable) + req = otx2_mbox_alloc_msg_nix_lf_ptp_tx_enable(&pfvf->mbox); + else + req = otx2_mbox_alloc_msg_nix_lf_ptp_tx_disable(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + otx2_mbox_unlock(&pfvf->mbox); + return err; + } + + otx2_mbox_unlock(&pfvf->mbox); + if (enable) + pfvf->flags |= OTX2_FLAG_TX_TSTAMP_ENABLED; + else + pfvf->flags &= ~OTX2_FLAG_TX_TSTAMP_ENABLED; + return 0; +} + +static int otx2_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct hwtstamp_config config; + + if (!pfvf->ptp) + return -ENODEV; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + /* reserved for future extensions */ + if (config.flags) + return -EINVAL; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + otx2_config_hw_tx_tstamp(pfvf, false); + break; + case HWTSTAMP_TX_ON: + otx2_config_hw_tx_tstamp(pfvf, true); + break; + default: + return -ERANGE; + } + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + otx2_config_hw_rx_tstamp(pfvf, false); + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_SOME: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + otx2_config_hw_rx_tstamp(pfvf, true); + config.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + return -ERANGE; + } + + if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) + return -EFAULT; + + return 0; +} + +static int otx2_ioctl(struct net_device *netdev, struct ifreq *req, int cmd) +{ + switch (cmd) { + case SIOCSHWTSTAMP: + return otx2_config_hwtstamp(netdev, req); + default: + return -EOPNOTSUPP; + } +} + +static int otx2_do_set_vf_mac(struct otx2_nic *pf, int vf, const u8 *mac) +{ + struct npc_install_flow_req *req; + int err; + + otx2_mbox_lock(&pf->mbox); + req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox); + if (!req) { + otx2_mbox_unlock(&pf->mbox); + return -ENOMEM; + } + + ether_addr_copy(req->packet.dmac, mac); + u64_to_ether_addr(0xffffffffffffull, req->mask.dmac); + req->features = BIT_ULL(NPC_DMAC); + req->channel = pf->hw.rx_chan_base; + req->intf = NIX_INTF_RX; + req->default_rule = 1; + req->append = 1; + req->vf = vf + 1; + req->op = NIX_RX_ACTION_DEFAULT; + + err = otx2_sync_mbox_msg(&pf->mbox); + otx2_mbox_unlock(&pf->mbox); + + return err; +} + +static int otx2_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) +{ + struct otx2_nic *pf = netdev_priv(netdev); + struct pci_dev *pdev = pf->pdev; + struct otx2_vf_config *config; + + if (!netif_running(netdev)) + return -EAGAIN; + + if (vf >= pci_num_vf(pdev)) + return -EINVAL; + + if (!is_valid_ether_addr(mac)) + return -EINVAL; + + config = &pf->vf_configs[vf]; + ether_addr_copy(config->mac, mac); + + return otx2_do_set_vf_mac(pf, vf, mac); +} + +static int otx2_do_set_vf_vlan(struct otx2_nic *pf, int vf, u16 vlan, u8 qos) +{ + struct npc_install_flow_req *req; + int err; + + otx2_mbox_lock(&pf->mbox); + req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox); + if (!req) { + otx2_mbox_unlock(&pf->mbox); + return -ENOMEM; + } + + req->packet.vlan_tci = htons(vlan); + req->mask.vlan_tci = htons(VLAN_VID_MASK); + req->features = BIT_ULL(NPC_OUTER_VID); + req->channel = pf->hw.rx_chan_base; + req->intf = NIX_INTF_RX; + req->default_rule = 1; + req->append = 1; + req->vf = vf + 1; + req->op = NIX_RX_ACTION_DEFAULT; + + err = otx2_sync_mbox_msg(&pf->mbox); + otx2_mbox_unlock(&pf->mbox); + + return err; +} + +static int otx2_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, + __be16 proto) +{ + struct otx2_nic *pf = netdev_priv(netdev); + struct pci_dev *pdev = pf->pdev; + struct otx2_vf_config *config; + + if (!netif_running(netdev)) + return -EAGAIN; + + if (vf >= pci_num_vf(pdev)) + return -EINVAL; + + /* qos is currently unsupported */ + if (vlan >= VLAN_N_VID || qos) + return -EINVAL; + + if (proto != htons(ETH_P_8021Q)) + return -EPROTONOSUPPORT; + + config = &pf->vf_configs[vf]; + config->vlan = vlan; + + return otx2_do_set_vf_vlan(pf, vf, vlan, qos); +} + +static int otx2_get_vf_config(struct net_device *netdev, int vf, + struct ifla_vf_info *ivi) +{ + struct otx2_nic *pf = netdev_priv(netdev); + struct pci_dev *pdev = pf->pdev; + struct otx2_vf_config *config; + + if (vf >= pci_num_vf(pdev)) + return -EINVAL; + + config = &pf->vf_configs[vf]; + ivi->vf = vf; + ether_addr_copy(ivi->mac, config->mac); + ivi->vlan = config->vlan; + + return 0; +} + +static netdev_features_t +otx2_features_check(struct sk_buff *skb, struct net_device *dev, + netdev_features_t features) +{ + return features; +} + +static const struct net_device_ops otx2_netdev_ops = { + .ndo_open = otx2_open, + .ndo_stop = otx2_stop, + .ndo_start_xmit = otx2_xmit, + .ndo_fix_features = otx2_fix_features, + .ndo_set_mac_address = otx2_set_mac_address, + .ndo_change_mtu = otx2_change_mtu, + .ndo_set_rx_mode = otx2_set_rx_mode, + .ndo_set_features = otx2_set_features, + .ndo_tx_timeout = otx2_tx_timeout, + .ndo_get_stats64 = otx2_get_stats64, + .ndo_do_ioctl = otx2_ioctl, + .ndo_set_vf_mac = otx2_set_vf_mac, + .ndo_set_vf_vlan = otx2_set_vf_vlan, + .ndo_get_vf_config = otx2_get_vf_config, + .ndo_features_check = otx2_features_check, +}; + +static int otx2_check_pf_usable(struct otx2_nic *nic) +{ + u64 rev; + + rev = otx2_read64(nic, RVU_PF_BLOCK_ADDRX_DISC(BLKADDR_RVUM)); + rev = (rev >> 12) & 0xFF; + /* Check if AF has setup revision for RVUM block, + * otherwise this driver probe should be deferred + * until AF driver comes up. + */ + if (!rev) { + dev_warn(nic->dev, + "AF is not initialized, deferring probe\n"); + return -EPROBE_DEFER; + } + return 0; +} + +static int otx2_realloc_msix_vectors(struct otx2_nic *pf) +{ + struct otx2_hw *hw = &pf->hw; + int num_vec, err; + + num_vec = hw->nix_msixoff; + num_vec += NIX_LF_CINT_VEC_START + hw->max_queues; + + otx2_disable_mbox_intr(pf); + pci_free_irq_vectors(hw->pdev); + pci_free_irq_vectors(hw->pdev); + err = pci_alloc_irq_vectors(hw->pdev, num_vec, num_vec, PCI_IRQ_MSIX); + if (err < 0) { + dev_err(pf->dev, "%s: Failed to realloc %d IRQ vectors\n", + __func__, num_vec); + return err; + } + + err = otx2_register_mbox_intr(pf, false); + if (err) + return err; + return 0; +} + +static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct net_device *netdev; + struct otx2_nic *pf; + struct otx2_hw *hw; + int err, qcount; + int num_vec; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device\n"); + return err; + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + dev_err(dev, "PCI request regions failed 0x%x\n", err); + return err; + } + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); + if (err) { + dev_err(dev, "Unable to set DMA mask\n"); + goto err_release_regions; + } + + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); + if (err) { + dev_err(dev, "Unable to set consistent DMA mask\n"); + goto err_release_regions; + } + + pci_set_master(pdev); + + /* Set number of queues */ + qcount = min_t(int, num_online_cpus(), OTX2_MAX_CQ_CNT); + + netdev = alloc_etherdev_mqs(sizeof(*pf), qcount, qcount); + if (!netdev) { + err = -ENOMEM; + goto err_release_regions; + } + + pci_set_drvdata(pdev, netdev); + SET_NETDEV_DEV(netdev, &pdev->dev); + pf = netdev_priv(netdev); + pf->netdev = netdev; + pf->pdev = pdev; + pf->dev = dev; + pf->total_vfs = pci_sriov_get_totalvfs(pdev); + pf->flags |= OTX2_FLAG_INTF_DOWN; + + hw = &pf->hw; + hw->pdev = pdev; + hw->rx_queues = qcount; + hw->tx_queues = qcount; + hw->max_queues = qcount; + + num_vec = pci_msix_vec_count(pdev); + hw->irq_name = devm_kmalloc_array(&hw->pdev->dev, num_vec, NAME_SIZE, + GFP_KERNEL); + if (!hw->irq_name) + goto err_free_netdev; + + hw->affinity_mask = devm_kcalloc(&hw->pdev->dev, num_vec, + sizeof(cpumask_var_t), GFP_KERNEL); + if (!hw->affinity_mask) + goto err_free_netdev; + + /* Map CSRs */ + pf->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); + if (!pf->reg_base) { + dev_err(dev, "Unable to map physical function CSRs, aborting\n"); + err = -ENOMEM; + goto err_free_netdev; + } + + err = otx2_check_pf_usable(pf); + if (err) + goto err_free_netdev; + + err = pci_alloc_irq_vectors(hw->pdev, RVU_PF_INT_VEC_CNT, + RVU_PF_INT_VEC_CNT, PCI_IRQ_MSIX); + if (err < 0) { + dev_err(dev, "%s: Failed to alloc %d IRQ vectors\n", + __func__, num_vec); + goto err_free_netdev; + } + + /* Init PF <=> AF mailbox stuff */ + err = otx2_pfaf_mbox_init(pf); + if (err) + goto err_free_irq_vectors; + + /* Register mailbox interrupt */ + err = otx2_register_mbox_intr(pf, true); + if (err) + goto err_mbox_destroy; + + /* Request AF to attach NPA and NIX LFs to this PF. + * NIX and NPA LFs are needed for this PF to function as a NIC. + */ + err = otx2_attach_npa_nix(pf); + if (err) + goto err_disable_mbox_intr; + + err = otx2_realloc_msix_vectors(pf); + if (err) + goto err_mbox_destroy; + + err = otx2_set_real_num_queues(netdev, hw->tx_queues, hw->rx_queues); + if (err) + goto err_detach_rsrc; + + otx2_setup_dev_hw_settings(pf); + + /* Don't check for error. Proceed without ptp */ + otx2_ptp_init(pf); + + /* Assign default mac address */ + otx2_get_mac_from_af(netdev); + + /* NPA's pool is a stack to which SW frees buffer pointers via Aura. + * HW allocates buffer pointer from stack and uses it for DMA'ing + * ingress packet. In some scenarios HW can free back allocated buffer + * pointers to pool. This makes it impossible for SW to maintain a + * parallel list where physical addresses of buffer pointers (IOVAs) + * given to HW can be saved for later reference. + * + * So the only way to convert Rx packet's buffer address is to use + * IOMMU's iova_to_phys() handler which translates the address by + * walking through the translation tables. + * + * So check if device is binded to IOMMU, otherwise translation is + * not needed. + */ + pf->iommu_domain = iommu_get_domain_for_dev(dev); + + netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | NETIF_F_RXHASH | + NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6); + netdev->features |= netdev->hw_features; + /* Support TSO on tag interface */ + netdev->vlan_features |= netdev->features; + + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_STAG_RX; + + netdev->features |= netdev->hw_features | NETIF_F_LLTX; + + netdev->hw_features |= NETIF_F_LOOPBACK | NETIF_F_NTUPLE | + NETIF_F_RXALL; + + netdev->priv_flags |= IFF_UNICAST_FLT; + + netdev->gso_max_segs = OTX2_MAX_GSO_SEGS; + netdev->watchdog_timeo = OTX2_TX_TIMEOUT; + + netdev->netdev_ops = &otx2_netdev_ops; + + /* MTU range: 64 - 9190 */ + netdev->min_mtu = OTX2_MIN_MTU; + netdev->max_mtu = OTX2_MAX_MTU; + + INIT_WORK(&pf->reset_task, otx2_reset_task); + + err = register_netdev(netdev); + if (err) { + dev_err(dev, "Failed to register netdevice\n"); + goto err_ptp_destroy; + } + + err = otx2_mcam_flow_init(pf); + if (err) + goto err_unreg_netdev; + + otx2_set_ethtool_ops(netdev); + + /* Enable link notifications */ + otx2_cgx_config_linkevents(pf, true); + + return 0; + +err_unreg_netdev: + unregister_netdev(netdev); +err_ptp_destroy: + otx2_ptp_destroy(pf); +err_detach_rsrc: + otx2_detach_resources(&pf->mbox); +err_disable_mbox_intr: + otx2_disable_mbox_intr(pf); +err_mbox_destroy: + otx2_pfaf_mbox_destroy(pf); + otx2_pfvf_mbox_destroy(pf); +err_free_irq_vectors: + pci_free_irq_vectors(hw->pdev); +err_free_netdev: + pci_set_drvdata(pdev, NULL); + free_netdev(netdev); +err_release_regions: + pci_release_regions(pdev); + return err; +} + +static void otx2_vf_link_event_task(struct work_struct *work) +{ + struct otx2_vf_config *config; + struct cgx_link_info_msg *req; + struct mbox_msghdr *msghdr; + struct otx2_nic *pf; + int vf_idx; + + config = container_of(work, struct otx2_vf_config, + link_event_work.work); + vf_idx = config - config->pf->vf_configs; + pf = config->pf; + + msghdr = otx2_mbox_alloc_msg_rsp(&pf->mbox_pfvf[0].mbox_up, vf_idx, + sizeof(*req), sizeof(struct msg_rsp)); + if (!msghdr) { + dev_err(pf->dev, "Failed to create VF%d link event\n", vf_idx); + return; + } + + req = (struct cgx_link_info_msg *)msghdr; + req->hdr.id = MBOX_MSG_CGX_LINK_EVENT; + req->hdr.sig = OTX2_MBOX_REQ_SIG; + memcpy(&req->link_info, &pf->linfo, sizeof(req->link_info)); + + otx2_sync_mbox_up_msg(&pf->mbox_pfvf[0], vf_idx); +} + +static void otx2_vf_ptp_info_task(struct work_struct *work) +{ + struct cgx_ptp_rx_info_msg *req; + struct otx2_vf_config *config; + struct mbox_msghdr *msghdr; + struct otx2_nic *pf; + int vf_idx; + + config = container_of(work, struct otx2_vf_config, + ptp_info_work.work); + vf_idx = config - config->pf->vf_configs; + pf = config->pf; + + msghdr = otx2_mbox_alloc_msg_rsp(&pf->mbox_pfvf[0].mbox_up, vf_idx, + sizeof(*req), sizeof(struct msg_rsp)); + if (!msghdr) { + dev_err(pf->dev, "Failed to create VF%d link event\n", vf_idx); + return; + } + + req = (struct cgx_ptp_rx_info_msg *)msghdr; + req->hdr.id = MBOX_MSG_CGX_PTP_RX_INFO; + req->hdr.sig = OTX2_MBOX_REQ_SIG; + req->ptp_en = pf->ptp->ptp_en; + + otx2_sync_mbox_up_msg(&pf->mbox_pfvf[0], vf_idx); +} + +static int otx2_sriov_enable(struct pci_dev *pdev, int numvfs) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct otx2_nic *pf = netdev_priv(netdev); + int ret, i; + + if (numvfs > pf->total_vfs) + numvfs = pf->total_vfs; + + /* Init PF <=> VF mailbox stuff */ + ret = otx2_pfvf_mbox_init(pf, numvfs); + if (ret) + return ret; + + ret = otx2_register_pfvf_mbox_intr(pf); + if (ret) + goto free_mbox; + + pf->vf_configs = kcalloc(numvfs, sizeof(struct otx2_vf_config), + GFP_KERNEL); + if (!pf->vf_configs) { + ret = -ENOMEM; + goto free_intr; + } + + for (i = 0; i < numvfs; i++) { + pf->vf_configs[i].pf = pf; + pf->vf_configs[i].intf_down = true; + INIT_DELAYED_WORK(&pf->vf_configs[i].link_event_work, + otx2_vf_link_event_task); + INIT_DELAYED_WORK(&pf->vf_configs[i].ptp_info_work, + otx2_vf_ptp_info_task); + } + + ret = otx2_pf_flr_init(pf, numvfs); + if (ret) + goto free_configs; + + ret = otx2_register_flr_me_intr(pf); + if (ret) + goto free_flr; + + ret = pci_enable_sriov(pdev, numvfs); + if (ret) + goto free_flr_intr; + + return numvfs; +free_flr_intr: + otx2_disable_flr_me_intr(pf); +free_flr: + otx2_flr_wq_destroy(pf); +free_configs: + kfree(pf->vf_configs); +free_intr: + otx2_disable_pfvf_mbox_intr(pf); +free_mbox: + otx2_pfvf_mbox_destroy(pf); + return ret; +} + +static int otx2_sriov_disable(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct otx2_nic *pf = netdev_priv(netdev); + int i; + + if (!pci_num_vf(pdev)) + return 0; + + pci_disable_sriov(pdev); + + for (i = 0; i < pci_num_vf(pdev); i++) { + cancel_delayed_work_sync(&pf->vf_configs[i].link_event_work); + cancel_delayed_work_sync(&pf->vf_configs[i].ptp_info_work); + } + kfree(pf->vf_configs); + + otx2_disable_flr_me_intr(pf); + otx2_flr_wq_destroy(pf); + otx2_disable_pfvf_mbox_intr(pf); + otx2_pfvf_mbox_destroy(pf); + + return 0; +} + +static int otx2_sriov_configure(struct pci_dev *pdev, int numvfs) +{ + if (numvfs == 0) + return otx2_sriov_disable(pdev); + else + return otx2_sriov_enable(pdev, numvfs); +} + +static void otx2_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct otx2_nic *pf; + + if (!netdev) + return; + + pf = netdev_priv(netdev); + + if (pf->flags & OTX2_FLAG_TX_TSTAMP_ENABLED) + otx2_config_hw_tx_tstamp(pf, false); + if (pf->flags & OTX2_FLAG_RX_TSTAMP_ENABLED) + otx2_config_hw_rx_tstamp(pf, false); + + /* Disable link notifications */ + otx2_cgx_config_linkevents(pf, false); + + unregister_netdev(netdev); + + otx2_sriov_disable(pf->pdev); + otx2_ptp_destroy(pf); + otx2_mcam_flow_del(pf); + + otx2_detach_resources(&pf->mbox); + otx2_disable_mbox_intr(pf); + otx2_pfaf_mbox_destroy(pf); + pci_free_irq_vectors(pf->pdev); + pci_set_drvdata(pdev, NULL); + free_netdev(netdev); + + pci_release_regions(pdev); +} + +static struct pci_driver otx2_pf_driver = { + .name = DRV_NAME, + .id_table = otx2_pf_id_table, + .probe = otx2_probe, + .shutdown = otx2_remove, + .remove = otx2_remove, + .sriov_configure = otx2_sriov_configure +}; + +static int __init otx2_rvupf_init_module(void) +{ + pr_info("%s: %s\n", DRV_NAME, DRV_STRING); + + return pci_register_driver(&otx2_pf_driver); +} + +static void __exit otx2_rvupf_cleanup_module(void) +{ + pci_unregister_driver(&otx2_pf_driver); +} + +module_init(otx2_rvupf_init_module); +module_exit(otx2_rvupf_cleanup_module); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c new file mode 100644 index 000000000000..01a6961afc93 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 PTP support for ethernet driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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 "otx2_common.h" +#include "otx2_ptp.h" + +static int otx2_ptp_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm) +{ + struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, + ptp_info); + struct ptp_req *req; + int err; + + if (!ptp->nic) + return -ENODEV; + + req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox); + if (!req) + return -ENOMEM; + + req->op = PTP_OP_ADJFINE; + req->scaled_ppm = scaled_ppm; + + err = otx2_sync_mbox_msg(&ptp->nic->mbox); + if (err) + return err; + + return 0; +} + +static u64 ptp_cc_read(const struct cyclecounter *cc) +{ + struct otx2_ptp *ptp = container_of(cc, struct otx2_ptp, cycle_counter); + struct ptp_req *req; + struct ptp_rsp *rsp; + int err; + + if (!ptp->nic) + return 0; + + req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox); + if (!req) + return 0; + + req->op = PTP_OP_GET_CLOCK; + + err = otx2_sync_mbox_msg(&ptp->nic->mbox); + if (err) + return 0; + + rsp = (struct ptp_rsp *)otx2_mbox_get_rsp(&ptp->nic->mbox.mbox, 0, + &req->hdr); + if (IS_ERR(rsp)) + return 0; + + return rsp->clk; +} + +static int otx2_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta) +{ + struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, + ptp_info); + + otx2_mbox_lock(&ptp->nic->mbox); + timecounter_adjtime(&ptp->time_counter, delta); + otx2_mbox_unlock(&ptp->nic->mbox); + + return 0; +} + +static int otx2_ptp_gettime(struct ptp_clock_info *ptp_info, + struct timespec64 *ts) +{ + struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, + ptp_info); + u64 nsec; + + otx2_mbox_lock(&ptp->nic->mbox); + nsec = timecounter_read(&ptp->time_counter); + otx2_mbox_unlock(&ptp->nic->mbox); + + *ts = ns_to_timespec64(nsec); + + return 0; +} + +static int otx2_ptp_settime(struct ptp_clock_info *ptp_info, + const struct timespec64 *ts) +{ + struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, + ptp_info); + u64 nsec; + + nsec = timespec64_to_ns(ts); + + otx2_mbox_lock(&ptp->nic->mbox); + timecounter_init(&ptp->time_counter, &ptp->cycle_counter, nsec); + otx2_mbox_unlock(&ptp->nic->mbox); + + return 0; +} + +static int otx2_ptp_enable(struct ptp_clock_info *ptp_info, + struct ptp_clock_request *rq, int on) +{ + return -EOPNOTSUPP; +} + +int otx2_ptp_init(struct otx2_nic *pfvf) +{ + struct otx2_ptp *ptp_ptr; + struct cyclecounter *cc; + struct ptp_req *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + /* check if PTP block is available */ + req = otx2_mbox_alloc_msg_ptp_op(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + req->op = PTP_OP_GET_CLOCK; + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + otx2_mbox_unlock(&pfvf->mbox); + return err; + } + otx2_mbox_unlock(&pfvf->mbox); + + ptp_ptr = kzalloc(sizeof(*ptp_ptr), GFP_KERNEL); + if (!ptp_ptr) { + err = -ENOMEM; + goto error; + } + + ptp_ptr->nic = pfvf; + + cc = &ptp_ptr->cycle_counter; + cc->read = ptp_cc_read; + cc->mask = CYCLECOUNTER_MASK(64); + cc->mult = 1; + cc->shift = 0; + + timecounter_init(&ptp_ptr->time_counter, &ptp_ptr->cycle_counter, + ktime_to_ns(ktime_get_real())); + + ptp_ptr->ptp_info = (struct ptp_clock_info) { + .owner = THIS_MODULE, + .name = "OcteonTX2 PTP", + .max_adj = 1000000000ull, + .n_ext_ts = 0, + .n_pins = 0, + .pps = 0, + .adjfine = otx2_ptp_adjfine, + .adjtime = otx2_ptp_adjtime, + .gettime64 = otx2_ptp_gettime, + .settime64 = otx2_ptp_settime, + .enable = otx2_ptp_enable, + }; + + ptp_ptr->ptp_clock = ptp_clock_register(&ptp_ptr->ptp_info, pfvf->dev); + if (IS_ERR(ptp_ptr->ptp_clock)) { + err = PTR_ERR(ptp_ptr->ptp_clock); + kfree(ptp_ptr); + goto error; + } + + pfvf->ptp = ptp_ptr; + +error: + return err; +} + +void otx2_ptp_destroy(struct otx2_nic *pfvf) +{ + struct otx2_ptp *ptp = pfvf->ptp; + + if (!ptp) + return; + + ptp_clock_unregister(ptp->ptp_clock); + kfree(ptp); + pfvf->ptp = NULL; +} + +int otx2_ptp_clock_index(struct otx2_nic *pfvf) +{ + if (!pfvf->ptp) + return -ENODEV; + + return ptp_clock_index(pfvf->ptp->ptp_clock); +} + +int otx2_ptp_tstamp2time(struct otx2_nic *pfvf, u64 tstamp, u64 *tsns) +{ + if (!pfvf->ptp) + return -ENODEV; + + *tsns = timecounter_cyc2time(&pfvf->ptp->time_counter, tstamp); + + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.h new file mode 100644 index 000000000000..9ddb82cec195 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Marvell OcteonTx2 PTP support for ethernet driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ + +#ifndef OTX2_PTP_H +#define OTX2_PTP_H + +int otx2_ptp_init(struct otx2_nic *pfvf); +void otx2_ptp_destroy(struct otx2_nic *pfvf); + +int otx2_ptp_clock_index(struct otx2_nic *pfvf); +int otx2_ptp_tstamp2time(struct otx2_nic *pfvf, u64 tstamp, u64 *tsns); + +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h new file mode 100644 index 000000000000..e84cf3cc67fd --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ + +#ifndef OTX2_REG_H +#define OTX2_REG_H + +#include <rvu_struct.h> + +/* RVU PF registers */ +#define RVU_PF_VFX_PFVF_MBOX0 (0x00000) +#define RVU_PF_VFX_PFVF_MBOX1 (0x00008) +#define RVU_PF_VFX_PFVF_MBOXX(a, b) (0x0 | (a) << 12 | (b) << 3) +#define RVU_PF_VF_BAR4_ADDR (0x10) +#define RVU_PF_BLOCK_ADDRX_DISC(a) (0x200 | (a) << 3) +#define RVU_PF_VFME_STATUSX(a) (0x800 | (a) << 3) +#define RVU_PF_VFTRPENDX(a) (0x820 | (a) << 3) +#define RVU_PF_VFTRPEND_W1SX(a) (0x840 | (a) << 3) +#define RVU_PF_VFPF_MBOX_INTX(a) (0x880 | (a) << 3) +#define RVU_PF_VFPF_MBOX_INT_W1SX(a) (0x8A0 | (a) << 3) +#define RVU_PF_VFPF_MBOX_INT_ENA_W1SX(a) (0x8C0 | (a) << 3) +#define RVU_PF_VFPF_MBOX_INT_ENA_W1CX(a) (0x8E0 | (a) << 3) +#define RVU_PF_VFFLR_INTX(a) (0x900 | (a) << 3) +#define RVU_PF_VFFLR_INT_W1SX(a) (0x920 | (a) << 3) +#define RVU_PF_VFFLR_INT_ENA_W1SX(a) (0x940 | (a) << 3) +#define RVU_PF_VFFLR_INT_ENA_W1CX(a) (0x960 | (a) << 3) +#define RVU_PF_VFME_INTX(a) (0x980 | (a) << 3) +#define RVU_PF_VFME_INT_W1SX(a) (0x9A0 | (a) << 3) +#define RVU_PF_VFME_INT_ENA_W1SX(a) (0x9C0 | (a) << 3) +#define RVU_PF_VFME_INT_ENA_W1CX(a) (0x9E0 | (a) << 3) +#define RVU_PF_PFAF_MBOX0 (0xC00) +#define RVU_PF_PFAF_MBOX1 (0xC08) +#define RVU_PF_PFAF_MBOXX(a) (0xC00 | (a) << 3) +#define RVU_PF_INT (0xc20) +#define RVU_PF_INT_W1S (0xc28) +#define RVU_PF_INT_ENA_W1S (0xc30) +#define RVU_PF_INT_ENA_W1C (0xc38) +#define RVU_PF_MSIX_VECX_ADDR(a) (0x000 | (a) << 4) +#define RVU_PF_MSIX_VECX_CTL(a) (0x008 | (a) << 4) +#define RVU_PF_MSIX_PBAX(a) (0xF0000 | (a) << 3) + +/* RVU VF registers */ +#define RVU_VF_VFPF_MBOX0 (0x00000) +#define RVU_VF_VFPF_MBOX1 (0x00008) +#define RVU_VF_VFPF_MBOXX(a) (0x00 | (a) << 3) +#define RVU_VF_INT (0x20) +#define RVU_VF_INT_W1S (0x28) +#define RVU_VF_INT_ENA_W1S (0x30) +#define RVU_VF_INT_ENA_W1C (0x38) +#define RVU_VF_BLOCK_ADDRX_DISC(a) (0x200 | (a) << 3) +#define RVU_VF_MSIX_VECX_ADDR(a) (0x000 | (a) << 4) +#define RVU_VF_MSIX_VECX_CTL(a) (0x008 | (a) << 4) +#define RVU_VF_MSIX_PBAX(a) (0xF0000 | (a) << 3) + +#define RVU_FUNC_BLKADDR_SHIFT 20 +#define RVU_FUNC_BLKADDR_MASK 0x1FULL + +/* NPA LF registers */ +#define NPA_LFBASE (BLKTYPE_NPA << RVU_FUNC_BLKADDR_SHIFT) +#define NPA_LF_AURA_OP_ALLOCX(a) (NPA_LFBASE | 0x10 | (a) << 3) +#define NPA_LF_AURA_OP_FREE0 (NPA_LFBASE | 0x20) +#define NPA_LF_AURA_OP_FREE1 (NPA_LFBASE | 0x28) +#define NPA_LF_AURA_OP_CNT (NPA_LFBASE | 0x30) +#define NPA_LF_AURA_OP_LIMIT (NPA_LFBASE | 0x50) +#define NPA_LF_AURA_OP_INT (NPA_LFBASE | 0x60) +#define NPA_LF_AURA_OP_THRESH (NPA_LFBASE | 0x70) +#define NPA_LF_POOL_OP_PC (NPA_LFBASE | 0x100) +#define NPA_LF_POOL_OP_AVAILABLE (NPA_LFBASE | 0x110) +#define NPA_LF_POOL_OP_PTR_START0 (NPA_LFBASE | 0x120) +#define NPA_LF_POOL_OP_PTR_START1 (NPA_LFBASE | 0x128) +#define NPA_LF_POOL_OP_PTR_END0 (NPA_LFBASE | 0x130) +#define NPA_LF_POOL_OP_PTR_END1 (NPA_LFBASE | 0x138) +#define NPA_LF_POOL_OP_INT (NPA_LFBASE | 0x160) +#define NPA_LF_POOL_OP_THRESH (NPA_LFBASE | 0x170) +#define NPA_LF_ERR_INT (NPA_LFBASE | 0x200) +#define NPA_LF_ERR_INT_W1S (NPA_LFBASE | 0x208) +#define NPA_LF_ERR_INT_ENA_W1C (NPA_LFBASE | 0x210) +#define NPA_LF_ERR_INT_ENA_W1S (NPA_LFBASE | 0x218) +#define NPA_LF_RAS (NPA_LFBASE | 0x220) +#define NPA_LF_RAS_W1S (NPA_LFBASE | 0x228) +#define NPA_LF_RAS_ENA_W1C (NPA_LFBASE | 0x230) +#define NPA_LF_RAS_ENA_W1S (NPA_LFBASE | 0x238) +#define NPA_LF_QINTX_CNT(a) (NPA_LFBASE | 0x300 | (a) << 12) +#define NPA_LF_QINTX_INT(a) (NPA_LFBASE | 0x310 | (a) << 12) +#define NPA_LF_QINTX_INT_W1S(a) (NPA_LFBASE | 0x318 | (a) << 12) +#define NPA_LF_QINTX_ENA_W1S(a) (NPA_LFBASE | 0x320 | (a) << 12) +#define NPA_LF_QINTX_ENA_W1C(a) (NPA_LFBASE | 0x330 | (a) << 12) + +/* NIX LF registers */ +#define NIX_LFBASE (BLKTYPE_NIX << RVU_FUNC_BLKADDR_SHIFT) +#define NIX_LF_RX_SECRETX(a) (NIX_LFBASE | 0x0 | (a) << 3) +#define NIX_LF_CFG (NIX_LFBASE | 0x100) +#define NIX_LF_GINT (NIX_LFBASE | 0x200) +#define NIX_LF_GINT_W1S (NIX_LFBASE | 0x208) +#define NIX_LF_GINT_ENA_W1C (NIX_LFBASE | 0x210) +#define NIX_LF_GINT_ENA_W1S (NIX_LFBASE | 0x218) +#define NIX_LF_ERR_INT (NIX_LFBASE | 0x220) +#define NIX_LF_ERR_INT_W1S (NIX_LFBASE | 0x228) +#define NIX_LF_ERR_INT_ENA_W1C (NIX_LFBASE | 0x230) +#define NIX_LF_ERR_INT_ENA_W1S (NIX_LFBASE | 0x238) +#define NIX_LF_RAS (NIX_LFBASE | 0x240) +#define NIX_LF_RAS_W1S (NIX_LFBASE | 0x248) +#define NIX_LF_RAS_ENA_W1C (NIX_LFBASE | 0x250) +#define NIX_LF_RAS_ENA_W1S (NIX_LFBASE | 0x258) +#define NIX_LF_SQ_OP_ERR_DBG (NIX_LFBASE | 0x260) +#define NIX_LF_MNQ_ERR_DBG (NIX_LFBASE | 0x270) +#define NIX_LF_SEND_ERR_DBG (NIX_LFBASE | 0x280) +#define NIX_LF_TX_STATX(a) (NIX_LFBASE | 0x300 | (a) << 3) +#define NIX_LF_RX_STATX(a) (NIX_LFBASE | 0x400 | (a) << 3) +#define NIX_LF_OP_SENDX(a) (NIX_LFBASE | 0x800 | (a) << 3) +#define NIX_LF_RQ_OP_INT (NIX_LFBASE | 0x900) +#define NIX_LF_RQ_OP_OCTS (NIX_LFBASE | 0x910) +#define NIX_LF_RQ_OP_PKTS (NIX_LFBASE | 0x920) +#define NIX_LF_OP_IPSEC_DYNO_CN (NIX_LFBASE | 0x980) +#define NIX_LF_SQ_OP_INT (NIX_LFBASE | 0xa00) +#define NIX_LF_SQ_OP_OCTS (NIX_LFBASE | 0xa10) +#define NIX_LF_SQ_OP_PKTS (NIX_LFBASE | 0xa20) +#define NIX_LF_SQ_OP_STATUS (NIX_LFBASE | 0xa30) +#define NIX_LF_CQ_OP_INT (NIX_LFBASE | 0xb00) +#define NIX_LF_CQ_OP_DOOR (NIX_LFBASE | 0xb30) +#define NIX_LF_CQ_OP_STATUS (NIX_LFBASE | 0xb40) +#define NIX_LF_QINTX_CNT(a) (NIX_LFBASE | 0xC00 | (a) << 12) +#define NIX_LF_QINTX_INT(a) (NIX_LFBASE | 0xC10 | (a) << 12) +#define NIX_LF_QINTX_INT_W1S(a) (NIX_LFBASE | 0xC18 | (a) << 12) +#define NIX_LF_QINTX_ENA_W1S(a) (NIX_LFBASE | 0xC20 | (a) << 12) +#define NIX_LF_QINTX_ENA_W1C(a) (NIX_LFBASE | 0xC30 | (a) << 12) +#define NIX_LF_CINTX_CNT(a) (NIX_LFBASE | 0xD00 | (a) << 12) +#define NIX_LF_CINTX_WAIT(a) (NIX_LFBASE | 0xD10 | (a) << 12) +#define NIX_LF_CINTX_INT(a) (NIX_LFBASE | 0xD20 | (a) << 12) +#define NIX_LF_CINTX_INT_W1S(a) (NIX_LFBASE | 0xD30 | (a) << 12) +#define NIX_LF_CINTX_ENA_W1S(a) (NIX_LFBASE | 0xD40 | (a) << 12) +#define NIX_LF_CINTX_ENA_W1C(a) (NIX_LFBASE | 0xD50 | (a) << 12) + +/* NIX AF transmit scheduler registers */ +#define NIX_AF_SMQX_CFG(a) (0x700 | (a) << 16) +#define NIX_AF_TL1X_SCHEDULE(a) (0xC00 | (a) << 16) +#define NIX_AF_TL1X_CIR(a) (0xC20 | (a) << 16) +#define NIX_AF_TL1X_TOPOLOGY(a) (0xC80 | (a) << 16) +#define NIX_AF_TL2X_PARENT(a) (0xE88 | (a) << 16) +#define NIX_AF_TL2X_SCHEDULE(a) (0xE00 | (a) << 16) +#define NIX_AF_TL3X_PARENT(a) (0x1088 | (a) << 16) +#define NIX_AF_TL4X_PARENT(a) (0x1288 | (a) << 16) +#define NIX_AF_MDQX_SCHEDULE(a) (0x1400 | (a) << 16) +#define NIX_AF_MDQX_PARENT(a) (0x1480 | (a) << 16) +#define NIX_AF_TL3_TL2X_LINKX_CFG(a, b) (0x1700 | (a) << 16 | (b) << 3) + +/* LMT LF registers */ +#define LMT_LFBASE BIT_ULL(RVU_FUNC_BLKADDR_SHIFT) +#define LMT_LF_LMTLINEX(a) (LMT_LFBASE | 0x000 | (a) << 12) +#define LMT_LF_LMTCANCEL (LMT_LFBASE | 0x400) + +#endif /* OTX2_REG_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_smqvf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_smqvf.c new file mode 100644 index 000000000000..4f5f73f621d4 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_smqvf.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Virtual Function ethernet driver + * + * Copyright (C) 2019 Marvell International Ltd. + * + * 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/etherdevice.h> +#include <linux/pci.h> + +#include "otx2_common.h" +#include "otx2_reg.h" +#include "otx2_struct.h" +#include "rvu_fixes.h" + +/* serialize device removal and xmit */ +DEFINE_MUTEX(remove_lock); + +static char pkt_data[64] = { 0x00, 0x0f, 0xb7, 0x11, 0xa6, 0x87, 0x02, 0xe0, + 0x28, 0xa5, 0xf6, 0x00, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x04, 0x11, + 0xee, 0x53, 0x50, 0x50, 0x50, 0x02, 0x14, 0x14, + 0x14, 0x02, 0x10, 0x00, 0x10, 0x01, 0x00, 0x1e, + 0x00, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76 }; + +static struct sk_buff *the_skb; +static struct otx2_nic *the_smqvf; +static u16 drop_entry = 0xFFFF; + +static bool is_otx2_smqvf(struct otx2_nic *vf) +{ + if (vf->pcifunc == RVU_SMQVF_PCIFUNC && + (is_96xx_A0(vf->pdev) || is_95xx_A0(vf->pdev))) + return true; + + return false; +} + +static void otx2_sqe_flush(struct otx2_snd_queue *sq, int size) +{ + u64 status; + + /* Packet data stores should finish before SQE is flushed to HW */ + dma_wmb(); + + do { + memcpy(sq->lmt_addr, sq->sqe_base, size); + status = otx2_lmt_flush(sq->io_addr); + } while (status == 0); + + sq->head++; + sq->head &= (sq->sqe_cnt - 1); +} + +static int otx2_ctx_update(struct otx2_nic *vf, u16 qidx) +{ + struct nix_aq_enq_req *sq_aq, *rq_aq, *cq_aq; + + /* Do not link CQ for SQ and disable RQ, CQ */ + sq_aq = otx2_mbox_alloc_msg_nix_aq_enq(&vf->mbox); + if (!sq_aq) + return -ENOMEM; + + sq_aq->sq.cq_ena = 0; + sq_aq->sq_mask.cq_ena = 1; + sq_aq->qidx = qidx; + sq_aq->ctype = NIX_AQ_CTYPE_SQ; + sq_aq->op = NIX_AQ_INSTOP_WRITE; + + rq_aq = otx2_mbox_alloc_msg_nix_aq_enq(&vf->mbox); + if (!rq_aq) + return -ENOMEM; + + rq_aq->rq.ena = 0; + rq_aq->rq_mask.ena = 1; + rq_aq->qidx = qidx; + rq_aq->ctype = NIX_AQ_CTYPE_RQ; + rq_aq->op = NIX_AQ_INSTOP_WRITE; + + cq_aq = otx2_mbox_alloc_msg_nix_aq_enq(&vf->mbox); + if (!cq_aq) + return -ENOMEM; + + cq_aq->cq.ena = 0; + cq_aq->cq_mask.ena = 1; + cq_aq->qidx = qidx; + cq_aq->ctype = NIX_AQ_CTYPE_CQ; + cq_aq->op = NIX_AQ_INSTOP_WRITE; + + return otx2_sync_mbox_msg(&vf->mbox); +} + +void otx2smqvf_xmit(void) +{ + struct otx2_snd_queue *sq; + int i, size; + + mutex_lock(&remove_lock); + + if (!the_smqvf) { + mutex_unlock(&remove_lock); + return; + } + + sq = &the_smqvf->qset.sq[0]; + /* Min. set of send descriptors required to send packets */ + size = sizeof(struct nix_sqe_hdr_s) + sizeof(struct nix_sqe_sg_s) + + sizeof(struct nix_sqe_ext_s) + sizeof(u64); + + for (i = 0; i < 256; i++) + otx2_sqe_flush(sq, size); + + mutex_unlock(&remove_lock); +} +EXPORT_SYMBOL(otx2smqvf_xmit); + +static int otx2smqvf_install_flow(struct otx2_nic *vf) +{ + struct npc_mcam_alloc_entry_req *alloc_req; + struct npc_mcam_free_entry_req *free_req; + struct npc_install_flow_req *install_req; + struct npc_mcam_alloc_entry_rsp *rsp; + struct msg_req *msg; + int err, qid; + size_t size; + void *data; + + size = SKB_DATA_ALIGN(64 + OTX2_ALIGN) + NET_SKB_PAD + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + + err = -ENOMEM; + + data = kzalloc(size, GFP_KERNEL); + if (!data) + return err; + + memcpy(data, &pkt_data, 64); + + the_skb = build_skb(data, 0); + the_skb->len = 64; + + for (qid = 0; qid < vf->hw.tx_queues; qid++) { + err = otx2_ctx_update(vf, qid); + /* If something wrong with Q0 then treat as error */ + if (err && !qid) + goto err_free_mem; + } + + otx2_mbox_lock(&vf->mbox); + + alloc_req = otx2_mbox_alloc_msg_npc_mcam_alloc_entry(&vf->mbox); + if (!alloc_req) { + otx2_mbox_unlock(&vf->mbox); + goto err_free_mem; + } + alloc_req->count = 1; + alloc_req->contig = true; + + /* Send message to AF */ + if (otx2_sync_mbox_msg(&vf->mbox)) { + err = -EINVAL; + otx2_mbox_unlock(&vf->mbox); + goto err_free_mem; + } + otx2_mbox_unlock(&vf->mbox); + + rsp = (struct npc_mcam_alloc_entry_rsp *)otx2_mbox_get_rsp + (&vf->mbox.mbox, 0, &alloc_req->hdr); + drop_entry = rsp->entry; + + otx2_mbox_lock(&vf->mbox); + + /* Send messages to drop Tx packets at NPC and stop Rx traffic */ + install_req = otx2_mbox_alloc_msg_npc_install_flow(&vf->mbox); + if (!install_req) { + err = -ENOMEM; + otx2_mbox_unlock(&vf->mbox); + goto err_free_entry; + } + + u64_to_ether_addr(0x0ull, install_req->mask.dmac); + install_req->entry = drop_entry; + install_req->features = BIT_ULL(NPC_DMAC); + install_req->intf = NIX_INTF_TX; + install_req->op = NIX_TX_ACTIONOP_DROP; + install_req->set_cntr = 1; + + msg = otx2_mbox_alloc_msg_nix_lf_stop_rx(&vf->mbox); + if (!msg) { + otx2_mbox_unlock(&vf->mbox); + goto err_free_entry; + } + + /* Send message to AF */ + if (otx2_sync_mbox_msg(&vf->mbox)) { + err = -EINVAL; + otx2_mbox_unlock(&vf->mbox); + goto err_free_entry; + } + otx2_mbox_unlock(&vf->mbox); + + otx2_sq_append_skb(vf->netdev, &vf->qset.sq[0], the_skb, 0); + + return 0; + +err_free_entry: + otx2_mbox_lock(&vf->mbox); + free_req = otx2_mbox_alloc_msg_npc_mcam_free_entry(&vf->mbox); + if (!free_req) { + dev_err(vf->dev, "Could not allocate msg for freeing entry\n"); + } else { + free_req->entry = drop_entry; + WARN_ON(otx2_sync_mbox_msg(&vf->mbox)); + } + otx2_mbox_unlock(&vf->mbox); +err_free_mem: + kfree_skb(the_skb); + drop_entry = 0xFFFF; + return err; +} + +int otx2smqvf_probe(struct otx2_nic *vf) +{ + int err; + + if (!is_otx2_smqvf(vf)) + return -EPERM; + + err = otx2_open(vf->netdev); + if (err) + return -EINVAL; + + /* Disable QINT interrupts because we do not use a CQ for SQ and + * drop TX packets intentionally + */ + otx2_write64(vf, NIX_LF_QINTX_ENA_W1C(0), BIT_ULL(0)); + + err = otx2smqvf_install_flow(vf); + if (err) { + otx2_stop(vf->netdev); + return -EINVAL; + } + + the_smqvf = vf; + + return 0; +} + +int otx2smqvf_remove(struct otx2_nic *vf) +{ + struct npc_mcam_free_entry_req *free_req; + struct npc_delete_flow_req *del_req; + + if (!is_otx2_smqvf(vf)) + return -EPERM; + + mutex_lock(&remove_lock); + kfree_skb(the_skb); + the_smqvf = NULL; + the_skb = NULL; + mutex_unlock(&remove_lock); + + otx2_mbox_lock(&vf->mbox); + del_req = otx2_mbox_alloc_msg_npc_delete_flow(&vf->mbox); + free_req = otx2_mbox_alloc_msg_npc_mcam_free_entry(&vf->mbox); + if (!del_req || !free_req) { + dev_err(vf->dev, "Could not allocate msg for freeing entry\n"); + } else { + del_req->entry = drop_entry; + free_req->entry = drop_entry; + WARN_ON(otx2_sync_mbox_msg(&vf->mbox)); + } + otx2_mbox_unlock(&vf->mbox); + + otx2_stop(vf->netdev); + drop_entry = 0xFFFF; + + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h new file mode 100644 index 000000000000..fe0f0fb45294 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h @@ -0,0 +1,425 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ + +#ifndef OTX2_STRUCT_H +#define OTX2_STRUCT_H + +/* NIX WQE/CQE size 128 byte or 512 byte */ +enum nix_cqesz_e { + NIX_XQESZ_W64 = 0x0, + NIX_XQESZ_W16 = 0x1, +}; + +enum nix_sqes_e { + NIX_SQESZ_W16 = 0x0, + NIX_SQESZ_W8 = 0x1, +}; + +enum nix_send_ldtype { + NIX_SEND_LDTYPE_LDD = 0x0, + NIX_SEND_LDTYPE_LDT = 0x1, + NIX_SEND_LDTYPE_LDWB = 0x2, +}; + +/* CSUM offload */ +enum nix_sendl3type { + NIX_SENDL3TYPE_NONE = 0x0, + NIX_SENDL3TYPE_IP4 = 0x2, + NIX_SENDL3TYPE_IP4_CKSUM = 0x3, + NIX_SENDL3TYPE_IP6 = 0x4, +}; + +enum nix_sendl4type { + NIX_SENDL4TYPE_NONE, + NIX_SENDL4TYPE_TCP_CKSUM, + NIX_SENDL4TYPE_SCTP_CKSUM, + NIX_SENDL4TYPE_UDP_CKSUM, +}; + +/* NIX wqe/cqe types */ +enum nix_xqe_type { + NIX_XQE_TYPE_INVALID = 0x0, + NIX_XQE_TYPE_RX = 0x1, + NIX_XQE_TYPE_RX_IPSECS = 0x2, + NIX_XQE_TYPE_RX_IPSECH = 0x3, + NIX_XQE_TYPE_RX_IPSECD = 0x4, + NIX_XQE_TYPE_SEND = 0x8, +}; + +/* NIX CQE/SQE subdescriptor types */ +enum nix_subdc { + NIX_SUBDC_NOP = 0x0, + NIX_SUBDC_EXT = 0x1, + NIX_SUBDC_CRC = 0x2, + NIX_SUBDC_IMM = 0x3, + NIX_SUBDC_SG = 0x4, + NIX_SUBDC_MEM = 0x5, + NIX_SUBDC_JUMP = 0x6, + NIX_SUBDC_WORK = 0x7, + NIX_SUBDC_SOD = 0xf, +}; + +/* Algorithm for nix_sqe_mem_s header (value of the `alg` field) */ +enum nix_sendmemalg { + NIX_SENDMEMALG_E_SET = 0x0, + NIX_SENDMEMALG_E_SETTSTMP = 0x1, + NIX_SENDMEMALG_E_SETRSLT = 0x2, + NIX_SENDMEMALG_E_ADD = 0x8, + NIX_SENDMEMALG_E_SUB = 0x9, + NIX_SENDMEMALG_E_ADDLEN = 0xa, + NIX_SENDMEMALG_E_SUBLEN = 0xb, + NIX_SENDMEMALG_E_ADDMBUF = 0xc, + NIX_SENDMEMALG_E_SUBMBUF = 0xd, + NIX_SENDMEMALG_E_ENUM_LAST = 0xe, +}; + +/* NIX CQE header structure */ +struct nix_cqe_hdr_s { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 cqe_type : 4; + u64 node : 2; + u64 reserved_52_57 : 6; + u64 q : 20; + u64 flow_tag : 32; +#else + u64 flow_tag : 32; + u64 q : 20; + u64 reserved_52_57 : 6; + u64 node : 2; + u64 cqe_type : 4; +#endif +}; + +/* NIX CQE RX parse structure */ +struct nix_rx_parse_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* W0 */ + u64 lhtype : 4; + u64 lgtype : 4; + u64 lftype : 4; + u64 letype : 4; + u64 ldtype : 4; + u64 lctype : 4; + u64 lbtype : 4; + u64 latype : 4; + u64 errcode : 8; + u64 errlev : 4; + u64 wqwd : 1; + u64 express : 1; + u64 rsvd_17 : 1; + u64 desc_sizem1 : 5; + u64 chan : 12; +#else + u64 chan : 12; + u64 desc_sizem1 : 5; + u64 rsvd_17 : 1; + u64 express : 1; + u64 wqwd : 1; + u64 errlev : 4; + u64 errcode : 8; + u64 latype : 4; + u64 lbtype : 4; + u64 lctype : 4; + u64 ldtype : 4; + u64 letype : 4; + u64 lftype : 4; + u64 lgtype : 4; + u64 lhtype : 4; +#endif +#if defined(__BIG_ENDIAN_BITFIELD) /* W1 */ + u64 vtag1_tci : 16; + u64 vtag0_tci : 16; + u64 rsvd_95_94 : 2; + u64 pkind : 6; + u64 vtag1_gone : 1; + u64 vtag1_valid : 1; + u64 vtag0_gone : 1; + u64 vtag0_valid : 1; + u64 l3b : 1; + u64 l3m : 1; + u64 l2b : 1; + u64 l2m : 1; + u64 pkt_lenm1 : 16; +#else + u64 pkt_lenm1 : 16; + u64 l2m : 1; + u64 l2b : 1; + u64 l3m : 1; + u64 l3b : 1; + u64 vtag0_valid : 1; + u64 vtag0_gone : 1; + u64 vtag1_valid : 1; + u64 vtag1_gone : 1; + u64 pkind : 6; + u64 rsvd_95_94 : 2; + u64 vtag0_tci : 16; + u64 vtag1_tci : 16; +#endif +#if defined(__BIG_ENDIAN_BITFIELD) /* W2 */ + u64 lhflags : 8; + u64 lgflags : 8; + u64 lfflags : 8; + u64 leflags : 8; + u64 ldflags : 8; + u64 lcflags : 8; + u64 lbflags : 8; + u64 laflags : 8; +#else + u64 laflags : 8; + u64 lbflags : 8; + u64 lcflags : 8; + u64 ldflags : 8; + u64 leflags : 8; + u64 lfflags : 8; + u64 lgflags : 8; + u64 lhflags : 8; +#endif +#if defined(__BIG_ENDIAN_BITFIELD) /* W3 */ + u64 match_id : 16; + u64 pb_aura : 20; + u64 wqe_aura : 20; + u64 eoh_ptr : 8; +#else + u64 eoh_ptr : 8; + u64 wqe_aura : 20; + u64 pb_aura : 20; + u64 match_id : 16; +#endif +#if defined(__BIG_ENDIAN_BITFIELD) /* W4 */ + u64 lhptr : 8; + u64 lgptr : 8; + u64 lfptr : 8; + u64 leptr : 8; + u64 ldptr : 8; + u64 lcptr : 8; + u64 lbptr : 8; + u64 laptr : 8; +#else + u64 laptr : 8; + u64 lbptr : 8; + u64 lcptr : 8; + u64 ldptr : 8; + u64 leptr : 8; + u64 lfptr : 8; + u64 lgptr : 8; + u64 lhptr : 8; +#endif +#if defined(__BIG_ENDIAN_BITFIELD) /* W5 */ + u64 rsvd_383_341 : 43; + u64 flow_key_alg : 5; + u64 vtag1_ptr : 8; + u64 vtag0_ptr : 8; +#else + u64 vtag0_ptr : 8; + u64 vtag1_ptr : 8; + u64 flow_key_alg : 5; + u64 rsvd_383_341 : 43; +#endif + u64 rsvd_447_384; /* W6 */ +}; + +/* NIX CQE RX scatter/gather subdescriptor structure */ +struct nix_rx_sg_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* W0 */ + u64 subdc : 4; + u64 rsvd_59_50 : 10; + u64 segs : 2; + u64 seg3_size : 16; + u64 seg2_size : 16; + u64 seg1_size : 16; +#else + u64 seg1_size : 16; + u64 seg2_size : 16; + u64 seg3_size : 16; + u64 segs : 2; + u64 rsvd_59_50 : 10; + u64 subdc : 4; +#endif +}; + +struct nix_send_comp_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* W0 */ + u64 rsvd_24_63 : 40; + u64 sqe_id : 16; + u64 status : 8; +#else + u64 status : 8; + u64 sqe_id : 16; + u64 rsvd_24_63 : 40; +#endif +}; + +/* NIX SQE header structure */ +struct nix_sqe_hdr_s { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 sq : 20; /* W0 */ + u64 pnc : 1; + u64 sizem1 : 3; + u64 aura : 20; + u64 df : 1; + u64 reserved_18 : 1; + u64 total : 18; +#else + u64 total : 18; + u64 reserved_18 : 1; + u64 df : 1; + u64 aura : 20; + u64 sizem1 : 3; + u64 pnc : 1; + u64 sq : 20; +#endif +#if defined(__BIG_ENDIAN_BITFIELD) /* W1 */ + u64 sqe_id :16; + u64 il4type :4; + u64 il3type :4; + u64 ol4type :4; + u64 ol3type :4; + u64 il4ptr :8; + u64 il3ptr :8; + u64 ol4ptr :8; + u64 ol3ptr :8; +#else + u64 ol3ptr :8; + u64 ol4ptr :8; + u64 il3ptr :8; + u64 il4ptr :8; + u64 ol3type :4; + u64 ol4type :4; + u64 il3type :4; + u64 il4type :4; + u64 sqe_id :16; + +#endif +}; + +/* NIX send extended header subdescriptor structure */ +struct nix_sqe_ext_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* W0 */ + u64 subdc : 4; + u64 mark_en : 1; + u64 markform : 7; + u64 markptr : 8; + u64 shp_ra : 2; + u64 shp_dis : 1; + u64 shp_chg : 9; + u64 rsvd_31_29 : 3; + u64 lso_format : 5; + u64 lso_sb : 8; + u64 tstmp : 1; + u64 lso : 1; + u64 lso_mps : 14; +#else + u64 lso_mps : 14; + u64 lso : 1; + u64 tstmp : 1; + u64 lso_sb : 8; + u64 lso_format : 5; + u64 rsvd_31_29 : 3; + u64 shp_chg : 9; + u64 shp_dis : 1; + u64 shp_ra : 2; + u64 markptr : 8; + u64 markform : 7; + u64 mark_en : 1; + u64 subdc : 4; +#endif +#if defined(__BIG_ENDIAN_BITFIELD) /* W1 */ + u64 rsvd_127_114 : 14; + u64 vlan1_ins_ena : 1; + u64 vlan0_ins_ena : 1; + u64 vlan1_ins_tci : 16; + u64 vlan1_ins_ptr : 8; + u64 vlan0_ins_tci : 16; + u64 vlan0_ins_ptr : 8; +#else + u64 vlan0_ins_ptr : 8; + u64 vlan0_ins_tci : 16; + u64 vlan1_ins_ptr : 8; + u64 vlan1_ins_tci : 16; + u64 vlan0_ins_ena : 1; + u64 vlan1_ins_ena : 1; + u64 rsvd_127_114 : 14; +#endif +}; + +struct nix_sqe_sg_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* W0 */ + u64 subdc : 4; + u64 ld_type : 2; + u64 i3 : 1; + u64 i2 : 1; + u64 i1 : 1; + u64 rsvd_54_50 : 5; + u64 segs : 2; + u64 seg3_size : 16; + u64 seg2_size : 16; + u64 seg1_size : 16; +#else + u64 seg1_size : 16; + u64 seg2_size : 16; + u64 seg3_size : 16; + u64 segs : 2; + u64 rsvd_54_50 : 5; + u64 i1 : 1; + u64 i2 : 1; + u64 i3 : 1; + u64 ld_type : 2; + u64 subdc : 4; +#endif +}; + +/* NIX send memory subdescriptor structure */ +struct nix_sqe_mem_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* W0 */ + u64 subdc : 4; + u64 alg : 4; + u64 dsz : 2; + u64 wmem : 1; + u64 rsvd_52_16 : 37; + u64 offset : 16; +#else + u64 offset : 16; + u64 rsvd_52_16 : 37; + u64 wmem : 1; + u64 dsz : 2; + u64 alg : 4; + u64 subdc : 4; +#endif + u64 addr; +}; + +enum nix_cqerrint_e { + NIX_CQERRINT_DOOR_ERR = 0, + NIX_CQERRINT_WR_FULL = 1, + NIX_CQERRINT_CQE_FAULT = 2, +}; + +#define NIX_CQERRINT_BITS (BIT_ULL(NIX_CQERRINT_DOOR_ERR) | \ + BIT_ULL(NIX_CQERRINT_CQE_FAULT)) + +enum nix_rqint_e { + NIX_RQINT_DROP = 0, + NIX_RQINT_RED = 1, +}; + +#define NIX_RQINT_BITS (BIT_ULL(NIX_RQINT_DROP) | BIT_ULL(NIX_RQINT_RED)) + +enum nix_sqint_e { + NIX_SQINT_LMT_ERR = 0, + NIX_SQINT_MNQ_ERR = 1, + NIX_SQINT_SEND_ERR = 2, + NIX_SQINT_SQB_ALLOC_FAIL = 3, +}; + +#define NIX_SQINT_BITS (BIT_ULL(NIX_SQINT_LMT_ERR) | \ + BIT_ULL(NIX_SQINT_MNQ_ERR) | \ + BIT_ULL(NIX_SQINT_SEND_ERR) | \ + BIT_ULL(NIX_SQINT_SQB_ALLOC_FAIL)) + +#endif /* OTX2_STRUCT_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c new file mode 100644 index 000000000000..179d90a761c5 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -0,0 +1,1058 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/etherdevice.h> +#include <net/ip.h> +#include <net/tso.h> + +#include "otx2_reg.h" +#include "otx2_common.h" +#include "otx2_struct.h" +#include "otx2_txrx.h" +#include "otx2_ptp.h" + +#define CQE_ADDR(CQ, idx) ((CQ)->cqe_base + ((CQ)->cqe_size * (idx))) + +static inline struct nix_cqe_hdr_s *otx2_get_next_cqe(struct otx2_cq_queue *cq) +{ + struct nix_cqe_hdr_s *cqe_hdr; + + cqe_hdr = (struct nix_cqe_hdr_s *)CQE_ADDR(cq, cq->cq_head); + if (cqe_hdr->cqe_type == NIX_XQE_TYPE_INVALID) + return NULL; + + cq->cq_head++; + cq->cq_head &= (cq->cqe_cnt - 1); + + return cqe_hdr; +} + +static inline unsigned int frag_num(unsigned int i) +{ +#ifdef __BIG_ENDIAN + return (i & ~3) + 3 - (i & 3); +#else + return i; +#endif +} + +static dma_addr_t otx2_dma_map_skb_frag(struct otx2_nic *pfvf, + struct sk_buff *skb, int seg, int *len) +{ + const struct skb_frag_struct *frag; + struct page *page; + int offset; + + /* First segment is always skb->data */ + if (!seg) { + page = virt_to_page(skb->data); + offset = offset_in_page(skb->data); + *len = skb_headlen(skb); + } else { + frag = &skb_shinfo(skb)->frags[seg - 1]; + page = skb_frag_page(frag); + offset = frag->page_offset; + *len = skb_frag_size(frag); + } + return dma_map_page_attrs(pfvf->dev, page, offset, *len, + DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); +} + +static void otx2_dma_unmap_skb_frags(struct otx2_nic *pfvf, struct sg_list *sg) +{ + int seg; + + for (seg = 0; seg < sg->num_segs; seg++) { + dma_unmap_page_attrs(pfvf->dev, sg->dma_addr[seg], + sg->size[seg], DMA_TO_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + } + sg->num_segs = 0; +} + +static void otx2_snd_pkt_handler(struct otx2_nic *pfvf, + struct otx2_cq_queue *cq, + struct otx2_snd_queue *sq, + struct nix_cqe_hdr_s *cqe_hdr, + int budget, int *tx_pkts, int *tx_bytes) +{ + struct nix_send_comp_s *snd_comp; + struct sk_buff *skb = NULL; + struct sg_list *sg; + + snd_comp = (struct nix_send_comp_s *) + ((void *)cqe_hdr + sizeof(*cqe_hdr)); + if (unlikely(snd_comp->status)) { + /* tx packet error handling*/ + if (netif_msg_tx_err(pfvf)) { + netdev_info(pfvf->netdev, + "TX%d: Error in send CQ status:%x\n", + cq->cint_idx, snd_comp->status); + } + } + + /* Barrier, so that update to sq by other cpus is visible */ + smp_mb(); + sg = &sq->sg[snd_comp->sqe_id]; + + skb = (struct sk_buff *)sg->skb; + if (unlikely(!skb)) + return; + + if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) { + u64 timestamp = ((u64 *)sq->timestamps->base)[snd_comp->sqe_id]; + + if (timestamp != 1) { + u64 tsns; + int err; + + err = otx2_ptp_tstamp2time(pfvf, timestamp, &tsns); + if (!err) { + struct skb_shared_hwtstamps ts; + + memset(&ts, 0, sizeof(ts)); + ts.hwtstamp = ns_to_ktime(tsns); + skb_tstamp_tx(skb, &ts); + } + } + } + + *tx_bytes += skb->len; + (*tx_pkts)++; + otx2_dma_unmap_skb_frags(pfvf, sg); + napi_consume_skb(skb, budget); + sg->skb = (u64)NULL; +} + +static inline void otx2_set_taginfo(struct nix_rx_parse_s *parse, + struct sk_buff *skb) +{ + /* Check if VLAN is present, captured and stripped from packet */ + if (parse->vtag0_valid && parse->vtag0_gone) { + skb_frag_t *frag0 = &skb_shinfo(skb)->frags[0]; + + /* Is the tag captured STAG or CTAG ? */ + if (((struct ethhdr *)skb_frag_address(frag0))->h_proto == + htons(ETH_P_8021Q)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021AD), + parse->vtag0_tci); + else + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + parse->vtag0_tci); + } +} + +static inline void otx2_set_rxhash(struct otx2_nic *pfvf, + struct nix_cqe_hdr_s *cqe_hdr, + struct sk_buff *skb) +{ + enum pkt_hash_types hash_type = PKT_HASH_TYPE_NONE; + struct otx2_rss_info *rss; + u32 hash = 0; + + if (!(pfvf->netdev->features & NETIF_F_RXHASH)) + return; + + rss = &pfvf->hw.rss_info; + if (rss->flowkey_cfg) { + if (rss->flowkey_cfg & + ~(NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6)) + hash_type = PKT_HASH_TYPE_L4; + else + hash_type = PKT_HASH_TYPE_L3; + hash = cqe_hdr->flow_tag; + } + skb_set_hash(skb, hash, hash_type); +} + +static inline void otx2_set_rxtstamp(struct otx2_nic *pfvf, + struct sk_buff *skb, void *data) +{ + u64 tsns; + int err; + + if (!(pfvf->flags & OTX2_FLAG_RX_TSTAMP_ENABLED)) + return; + + /* The first 8 bytes is the timestamp */ + err = otx2_ptp_tstamp2time(pfvf, be64_to_cpu(*(u64 *)data), &tsns); + if (err) + return; + + skb_hwtstamps(skb)->hwtstamp = ns_to_ktime(tsns); +} + +static void otx2_skb_add_frag(struct otx2_nic *pfvf, struct sk_buff *skb, + u64 iova, int len, struct nix_rx_parse_s *parse) +{ + struct page *page; + int off = 0; + void *va; + + va = phys_to_virt(otx2_iova_to_phys(pfvf->iommu_domain, iova)); + + if (likely(!skb_shinfo(skb)->nr_frags)) { + /* Check if data starts at some nonzero offset + * from the start of the buffer. For now the + * only possible offset is 8 bytes in the case + * where packet is prepended by a timestamp. + */ + if (parse->laptr) { + otx2_set_rxtstamp(pfvf, skb, va); + off = 8; + } + off += pfvf->xtra_hdr; + } + + page = virt_to_page(va); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, + va - page_address(page) + off, len - off, RCV_FRAG_LEN); + + dma_unmap_page_attrs(pfvf->dev, iova - OTX2_HEAD_ROOM, RCV_FRAG_LEN, + DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); +} + +static inline bool otx2_check_rcv_errors(struct otx2_nic *pfvf, + struct nix_rx_parse_s *parse, int qidx) +{ + struct otx2_drv_stats *stats = &pfvf->hw.drv_stats; + struct nix_rx_sg_s *sg; + void *start, *end; + u64 *iova; + int seg; + + if (netif_msg_rx_err(pfvf)) + netdev_err(pfvf->netdev, + "RQ%d: Error pkt with errlev:0x%x errcode:0x%x\n", + qidx, parse->errlev, parse->errcode); + + if (parse->errlev == NPC_ERRLVL_RE) { + switch (parse->errcode) { + case ERRCODE_FCS: + case ERRCODE_FCS_RCV: + atomic_inc(&stats->rx_fcs_errs); + break; + case ERRCODE_UNDERSIZE: + atomic_inc(&stats->rx_undersize_errs); + break; + case ERRCODE_OVERSIZE: + atomic_inc(&stats->rx_oversize_errs); + break; + case ERRCODE_OL2_LEN_MISMATCH: + atomic_inc(&stats->rx_len_errs); + break; + default: + atomic_inc(&stats->rx_other_errs); + break; + } + } else if (parse->errlev == NPC_ERRLVL_NIX) { + switch (parse->errcode) { + case ERRCODE_OL3_LEN: + case ERRCODE_OL4_LEN: + case ERRCODE_IL3_LEN: + case ERRCODE_IL4_LEN: + atomic_inc(&stats->rx_len_errs); + break; + case ERRCODE_OL4_CSUM: + case ERRCODE_IL4_CSUM: + atomic_inc(&stats->rx_csum_errs); + break; + default: + atomic_inc(&stats->rx_other_errs); + break; + } + } else { + atomic_inc(&stats->rx_other_errs); + /* For now ignore all the NPC parser errors and + * pass the packets to stack. + */ + return false; + } + + start = (void *)parse + sizeof(*parse); + end = start + ((parse->desc_sizem1 + 1) * 16); + while ((start + sizeof(*sg)) < end) { + sg = (struct nix_rx_sg_s *)start; + iova = (void *)sg + sizeof(*sg); + + /* If RXALL is enabled pass on packets to stack */ + if (sg->segs && pfvf->netdev->features & NETIF_F_RXALL) + return false; + + for (seg = 0; seg < sg->segs; seg++) { + otx2_aura_freeptr(pfvf, qidx, *iova & ~0x07ULL); + iova++; + } + if (sg->segs == 1) + start += sizeof(*sg) + sizeof(u64); + else + start += sizeof(*sg) + (3 * sizeof(u64)); + } + return true; +} + +static void otx2_rcv_pkt_handler(struct otx2_nic *pfvf, + struct napi_struct *napi, + struct otx2_cq_queue *cq, + struct nix_cqe_hdr_s *cqe_hdr) +{ + struct nix_rx_parse_s *parse; + struct sk_buff *skb = NULL; + struct nix_rx_sg_s *sg; + void *start, *end; + int seg, len; + u16 *sg_lens; + u64 *iova; + + /* CQE_HDR_S for a Rx pkt is always followed by RX_PARSE_S */ + parse = (struct nix_rx_parse_s *)((void *)cqe_hdr + sizeof(*cqe_hdr)); + if (unlikely(parse->errlev || parse->errcode)) { + if (otx2_check_rcv_errors(pfvf, parse, cq->cq_idx)) + return; + } + + start = (void *)parse + sizeof(*parse); + end = start + ((parse->desc_sizem1 + 1) * 16); + + skb = napi_get_frags(napi); + if (unlikely(!skb)) + return; + + /* Run through the each NIX_RX_SG_S subdc and frame the skb */ + while ((start + sizeof(*sg)) < end) { + sg = (struct nix_rx_sg_s *)start; + sg_lens = (void *)sg; + iova = (void *)sg + sizeof(*sg); + + for (seg = 0; seg < sg->segs; seg++) { + len = sg_lens[frag_num(seg)]; + otx2_skb_add_frag(pfvf, skb, *iova, len, parse); + iova++; + } + cq->pool_ptrs += sg->segs; + + /* When SEGS = 1, only one IOVA is followed by NIX_RX_SG_S. + * When SEGS >= 2, three IOVAs will follow NIX_RX_SG_S, + * irrespective of whether 2 SEGS are valid or all 3. + */ + if (sg->segs == 1) + start += sizeof(*sg) + sizeof(u64); + else + start += sizeof(*sg) + (3 * sizeof(u64)); + } + + otx2_set_rxhash(pfvf, cqe_hdr, skb); + + skb_record_rx_queue(skb, cq->cq_idx); + if (pfvf->netdev->features & NETIF_F_RXCSUM) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + otx2_set_taginfo(parse, skb); + + napi_gro_frags(napi); +} + +static inline int otx2_rx_napi_handler(struct otx2_nic *pfvf, + struct napi_struct *napi, + struct otx2_cq_queue *cq, int budget) +{ + struct otx2_pool *rbpool = cq->rbpool; + struct nix_cqe_hdr_s *cqe_hdr; + int processed_cqe = 0; + s64 bufptr; + + /* Make sure HW writes to CQ are done */ + dma_rmb(); + while (likely(processed_cqe < budget)) { + cqe_hdr = otx2_get_next_cqe(cq); + if (unlikely(!cqe_hdr)) { + if (!processed_cqe) + return 0; + break; + } + otx2_rcv_pkt_handler(pfvf, napi, cq, cqe_hdr); + + cqe_hdr->cqe_type = NIX_XQE_TYPE_INVALID; + processed_cqe++; + } + + /* Free CQEs to HW */ + otx2_write64(pfvf, NIX_LF_CQ_OP_DOOR, + ((u64)cq->cq_idx << 32) | processed_cqe); + + if (unlikely(!cq->pool_ptrs)) + return 0; + + /* Refill pool with new buffers */ + while (cq->pool_ptrs) { + bufptr = otx2_alloc_rbuf(pfvf, rbpool, GFP_ATOMIC); + if (unlikely(bufptr <= 0)) { + struct refill_work *work; + struct delayed_work *dwork; + + work = &pfvf->refill_wrk[cq->cq_idx]; + dwork = &work->pool_refill_work; + /* Schedule a task if no other task is running */ + if (!cq->refill_task_sched) { + cq->refill_task_sched = true; + schedule_delayed_work(dwork, + msecs_to_jiffies(100)); + } + break; + } + otx2_aura_freeptr(pfvf, cq->cq_idx, bufptr + OTX2_HEAD_ROOM); + cq->pool_ptrs--; + } + otx2_get_page(rbpool); + + return processed_cqe; +} + +static inline int otx2_tx_napi_handler(struct otx2_nic *pfvf, + struct otx2_cq_queue *cq, int budget) +{ + struct nix_cqe_hdr_s *cqe_hdr; + int tx_pkts = 0, tx_bytes = 0; + struct otx2_snd_queue *sq; + struct netdev_queue *txq; + int processed_cqe = 0; + + sq = &pfvf->qset.sq[cq->cint_idx]; + + /* Make sure HW writes to CQ are done */ + dma_rmb(); + while (likely(processed_cqe < budget)) { + cqe_hdr = otx2_get_next_cqe(cq); + if (unlikely(!cqe_hdr)) { + if (!processed_cqe) + return 0; + break; + } + otx2_snd_pkt_handler(pfvf, cq, sq, cqe_hdr, budget, + &tx_pkts, &tx_bytes); + + cqe_hdr->cqe_type = NIX_XQE_TYPE_INVALID; + processed_cqe++; + } + + /* Free CQEs to HW */ + otx2_write64(pfvf, NIX_LF_CQ_OP_DOOR, + ((u64)cq->cq_idx << 32) | processed_cqe); + + if (likely(tx_pkts)) { + txq = netdev_get_tx_queue(pfvf->netdev, cq->cint_idx); + netdev_tx_completed_queue(txq, tx_pkts, tx_bytes); + /* Check if queue was stopped earlier due to ring full */ + smp_mb(); + if (netif_tx_queue_stopped(txq) && + netif_carrier_ok(pfvf->netdev)) + netif_tx_wake_queue(txq); + } + return 0; +} + +int otx2_napi_handler(struct napi_struct *napi, int budget) +{ + struct otx2_cq_poll *cq_poll; + int workdone = 0, cq_idx, i; + struct otx2_cq_queue *cq; + struct otx2_qset *qset; + struct otx2_nic *pfvf; + + cq_poll = container_of(napi, struct otx2_cq_poll, napi); + pfvf = (struct otx2_nic *)cq_poll->dev; + qset = &pfvf->qset; + + for (i = CQS_PER_CINT - 1; i >= 0; i--) { + cq_idx = cq_poll->cq_ids[i]; + if (unlikely(cq_idx == CINT_INVALID_CQ)) + continue; + cq = &qset->cq[cq_idx]; + if (cq->cq_type == CQ_RX) { + /* If the RQ refill WQ task is running, skip napi + * scheduler for this queue. + */ + if (cq->refill_task_sched) + continue; + workdone += otx2_rx_napi_handler(pfvf, napi, + cq, budget); + } else { + workdone += otx2_tx_napi_handler(pfvf, cq, budget); + } + } + + /* Clear the IRQ */ + otx2_write64(pfvf, NIX_LF_CINTX_INT(cq_poll->cint_idx), BIT_ULL(0)); + + if (workdone < budget && napi_complete_done(napi, workdone)) { + /* If interface is going down, don't re-enable IRQ */ + if (pfvf->flags & OTX2_FLAG_INTF_DOWN) + return workdone; + + /* Re-enable interrupts */ + otx2_write64(pfvf, NIX_LF_CINTX_ENA_W1S(cq_poll->cint_idx), + BIT_ULL(0)); + } + return workdone; +} + +static inline void otx2_sqe_flush(struct otx2_snd_queue *sq, int size) +{ + u64 status; + + /* Packet data stores should finish before SQE is flushed to HW */ + dma_wmb(); + + do { + memcpy(sq->lmt_addr, sq->sqe_base, size); + status = otx2_lmt_flush(sq->io_addr); + } while (status == 0); + + sq->head++; + sq->head &= (sq->sqe_cnt - 1); +} + +#define MAX_SEGS_PER_SG 3 +/* Add SQE scatter/gather subdescriptor structure */ +static bool otx2_sqe_add_sg(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, + struct sk_buff *skb, int num_segs, int *offset) +{ + struct nix_sqe_sg_s *sg = NULL; + u64 dma_addr, *iova = NULL; + u16 *sg_lens = NULL; + int seg, len; + + sq->sg[sq->head].num_segs = 0; + + for (seg = 0; seg < num_segs; seg++) { + if ((seg % MAX_SEGS_PER_SG) == 0) { + sg = (struct nix_sqe_sg_s *)(sq->sqe_base + *offset); + sg->ld_type = NIX_SEND_LDTYPE_LDD; + sg->subdc = NIX_SUBDC_SG; + sg->segs = 0; + sg_lens = (void *)sg; + iova = (void *)sg + sizeof(*sg); + /* Next subdc always starts at a 16byte boundary. + * So if sg->segs is whether 2 or 3, offset += 16bytes. + */ + if ((num_segs - seg) >= (MAX_SEGS_PER_SG - 1)) + *offset += sizeof(*sg) + (3 * sizeof(u64)); + else + *offset += sizeof(*sg) + sizeof(u64); + } + dma_addr = otx2_dma_map_skb_frag(pfvf, skb, seg, &len); + if (dma_mapping_error(pfvf->dev, dma_addr)) + return false; + + sg_lens[frag_num(seg % MAX_SEGS_PER_SG)] = len; + sg->segs++; + *iova++ = dma_addr; + + /* Save DMA mapping info for later unmapping */ + sq->sg[sq->head].dma_addr[seg] = dma_addr; + sq->sg[sq->head].size[seg] = len; + sq->sg[sq->head].num_segs++; + } + + sq->sg[sq->head].skb = (u64)skb; + return true; +} + +/* Add SQE extended header subdescriptor */ +static void otx2_sqe_add_ext(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, + struct sk_buff *skb, int *offset) +{ + struct nix_sqe_ext_s *ext; + + ext = (struct nix_sqe_ext_s *)(sq->sqe_base + *offset); + ext->subdc = NIX_SUBDC_EXT; + if (skb_shinfo(skb)->gso_size) { + ext->lso = 1; + ext->lso_sb = skb_transport_offset(skb) + tcp_hdrlen(skb); + ext->lso_mps = skb_shinfo(skb)->gso_size; + + /* Only TSOv4 and TSOv6 GSO offloads are supported */ + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { + ext->lso_format = pfvf->hw.lso_tsov4_idx; + + /* HW adds payload size to 'ip_hdr->tot_len' while + * sending TSO segment, hence set payload length + * in IP header of the packet to just header length. + */ + ip_hdr(skb)->tot_len = + htons(ext->lso_sb - skb_network_offset(skb)); + } else { + ext->lso_format = pfvf->hw.lso_tsov6_idx; + ipv6_hdr(skb)->payload_len = + htons(ext->lso_sb - skb_network_offset(skb)); + } + } else if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { + ext->tstmp = 1; + } + +#define OTX2_VLAN_PTR_OFFSET (ETH_HLEN - ETH_TLEN) + if (skb_vlan_tag_present(skb)) { + if (skb->vlan_proto == htons(ETH_P_8021Q)) { + ext->vlan1_ins_ena = 1; + ext->vlan1_ins_ptr = OTX2_VLAN_PTR_OFFSET; + ext->vlan1_ins_tci = skb_vlan_tag_get(skb); + } else if (skb->vlan_proto == htons(ETH_P_8021AD)) { + ext->vlan0_ins_ena = 1; + ext->vlan0_ins_ptr = OTX2_VLAN_PTR_OFFSET; + ext->vlan0_ins_tci = skb_vlan_tag_get(skb); + } + } + + *offset += sizeof(*ext); +} + +static void otx2_sqe_add_mem(struct otx2_snd_queue *sq, int *offset, + int alg, u64 iova) +{ + struct nix_sqe_mem_s *mem; + + mem = (struct nix_sqe_mem_s *)(sq->sqe_base + *offset); + mem->subdc = NIX_SUBDC_MEM; + mem->alg = alg; + mem->wmem = 1; /* wait for the memory operation */ + mem->addr = iova; + + *offset += sizeof(*mem); +} + +/* Add SQE header subdescriptor structure */ +static void otx2_sqe_add_hdr(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, + struct nix_sqe_hdr_s *sqe_hdr, + struct sk_buff *skb, u16 qidx) +{ + int proto = 0; + + /* Check if SQE was framed before, if yes then no need to + * set these constants again anf again. + */ + if (!sqe_hdr->total) { + /* Don't free Tx buffers to Aura */ + sqe_hdr->df = 1; + sqe_hdr->aura = sq->aura_id; + /* Post a CQE Tx after pkt transmission */ + sqe_hdr->pnc = 1; + sqe_hdr->sq = qidx; + } + sqe_hdr->total = skb->len; + /* Set SQE identifier which will be used later for freeing SKB */ + sqe_hdr->sqe_id = sq->head; + + /* Offload TCP/UDP checksum to HW */ + if (skb->ip_summed == CHECKSUM_PARTIAL) { + sqe_hdr->ol3ptr = skb_network_offset(skb); + sqe_hdr->ol4ptr = skb_transport_offset(skb); + /* get vlan protocol Ethertype */ + if (eth_type_vlan(skb->protocol)) + skb->protocol = vlan_get_protocol(skb); + + if (skb->protocol == htons(ETH_P_IP)) { + proto = ip_hdr(skb)->protocol; + /* In case of TSO, HW needs this to be explicitly set. + * So set this always, instead of adding a check. + */ + sqe_hdr->ol3type = NIX_SENDL3TYPE_IP4_CKSUM; + } else if (skb->protocol == htons(ETH_P_IPV6)) { + proto = ipv6_hdr(skb)->nexthdr; + } + + if (proto == IPPROTO_TCP) + sqe_hdr->ol4type = NIX_SENDL4TYPE_TCP_CKSUM; + else if (proto == IPPROTO_UDP) + sqe_hdr->ol4type = NIX_SENDL4TYPE_UDP_CKSUM; + } +} + +static int otx2_dma_map_tso_skb(struct otx2_nic *pfvf, + struct otx2_snd_queue *sq, + struct sk_buff *skb, int sqe, int hdr_len) +{ + int num_segs = skb_shinfo(skb)->nr_frags + 1; + struct sg_list *sg = &sq->sg[sqe]; + u64 dma_addr; + int seg, len; + + sg->num_segs = 0; + + /* Get payload length at skb->data */ + len = skb_headlen(skb) - hdr_len; + + for (seg = 0; seg < num_segs; seg++) { + /* Skip skb->data, if there is no payload */ + if (!seg && !len) + continue; + dma_addr = otx2_dma_map_skb_frag(pfvf, skb, seg, &len); + if (dma_mapping_error(pfvf->dev, dma_addr)) + goto unmap; + + /* Save DMA mapping info for later unmapping */ + sg->dma_addr[sg->num_segs] = dma_addr; + sg->size[sg->num_segs] = len; + sg->num_segs++; + } + return 0; +unmap: + otx2_dma_unmap_skb_frags(pfvf, sg); + return -EINVAL; +} + +static u64 otx2_tso_frag_dma_addr(struct otx2_snd_queue *sq, + struct sk_buff *skb, int seg, + u64 seg_addr, int hdr_len, int sqe) +{ + const struct skb_frag_struct *frag; + struct sg_list *sg = &sq->sg[sqe]; + int offset; + + if (seg < 0) + return sg->dma_addr[0] + (seg_addr - (u64)skb->data); + + frag = &skb_shinfo(skb)->frags[seg]; + offset = (u64)(page_address(frag->page.p) + frag->page_offset); + offset = seg_addr - offset; + if (skb_headlen(skb) - hdr_len) + seg++; + return sg->dma_addr[seg] + offset; +} + +static void otx2_sqe_tso_add_sg(struct otx2_snd_queue *sq, + struct sg_list *list, int *offset) +{ + struct nix_sqe_sg_s *sg = NULL; + u16 *sg_lens = NULL; + u64 *iova = NULL; + int seg; + + /* Add SG descriptors with buffer addresses */ + for (seg = 0; seg < list->num_segs; seg++) { + if ((seg % MAX_SEGS_PER_SG) == 0) { + sg = (struct nix_sqe_sg_s *)(sq->sqe_base + *offset); + sg->ld_type = NIX_SEND_LDTYPE_LDD; + sg->subdc = NIX_SUBDC_SG; + sg->segs = 0; + sg_lens = (void *)sg; + iova = (void *)sg + sizeof(*sg); + /* Next subdc always starts at a 16byte boundary. + * So if sg->segs is whether 2 or 3, offset += 16bytes. + */ + if ((list->num_segs - seg) >= (MAX_SEGS_PER_SG - 1)) + *offset += sizeof(*sg) + (3 * sizeof(u64)); + else + *offset += sizeof(*sg) + sizeof(u64); + } + sg_lens[frag_num(seg % MAX_SEGS_PER_SG)] = list->size[seg]; + *iova++ = list->dma_addr[seg]; + sg->segs++; + } +} + +static void otx2_sq_append_tso(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, + struct sk_buff *skb, u16 qidx) +{ + struct netdev_queue *txq = netdev_get_tx_queue(pfvf->netdev, qidx); + int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + int tcp_data, seg_len, pkt_len, offset; + struct nix_sqe_hdr_s *sqe_hdr; + int first_sqe = sq->head; + struct sg_list list; + struct tso_t tso; + + /* Map SKB's fragments to DMA. + * It's done here to avoid mapping for every TSO segment's packet. + */ + if (otx2_dma_map_tso_skb(pfvf, sq, skb, first_sqe, hdr_len)) { + dev_kfree_skb_any(skb); + return; + } + + netdev_tx_sent_queue(txq, skb->len); + + tso_start(skb, &tso); + tcp_data = skb->len - hdr_len; + while (tcp_data > 0) { + char *hdr; + + seg_len = min_t(int, skb_shinfo(skb)->gso_size, tcp_data); + tcp_data -= seg_len; + + /* Set SQE's SEND_HDR */ + memset(sq->sqe_base, 0, sq->sqe_size); + sqe_hdr = (struct nix_sqe_hdr_s *)(sq->sqe_base); + otx2_sqe_add_hdr(pfvf, sq, sqe_hdr, skb, qidx); + offset = sizeof(*sqe_hdr); + + /* Add TSO segment's pkt header */ + hdr = sq->tso_hdrs->base + (sq->head * TSO_HEADER_SIZE); + tso_build_hdr(skb, hdr, &tso, seg_len, tcp_data == 0); + list.dma_addr[0] = + sq->tso_hdrs->iova + (sq->head * TSO_HEADER_SIZE); + list.size[0] = hdr_len; + list.num_segs = 1; + + /* Add TSO segment's payload data fragments */ + pkt_len = hdr_len; + while (seg_len > 0) { + int size; + + size = min_t(int, tso.size, seg_len); + + list.size[list.num_segs] = size; + list.dma_addr[list.num_segs] = + otx2_tso_frag_dma_addr(sq, skb, + tso.next_frag_idx - 1, + (u64)tso.data, hdr_len, + first_sqe); + list.num_segs++; + pkt_len += size; + seg_len -= size; + tso_build_data(skb, &tso, size); + } + sqe_hdr->total = pkt_len; + otx2_sqe_tso_add_sg(sq, &list, &offset); + + /* DMA mappings and skb needs to be freed only after last + * TSO segment is transmitted out. So set 'PNC' only for + * last segment. Also point last segment's sqe_id to first + * segment's SQE index where skb address and DMA mappings + * are saved. + */ + if (!tcp_data) { + sqe_hdr->pnc = 1; + sqe_hdr->sqe_id = first_sqe; + sq->sg[first_sqe].skb = (u64)skb; + } else { + sqe_hdr->pnc = 0; + } + + sqe_hdr->sizem1 = (offset / 16) - 1; + + /* Flush SQE to HW */ + otx2_sqe_flush(sq, offset); + } +} + +static inline bool is_hw_tso_supported(struct otx2_nic *pfvf, + struct sk_buff *skb) +{ + int payload_len, last_seg_size; + + if (!pfvf->hw.hw_tso) + return false; + + /* HW has an issue due to which when the payload of the last LSO + * segment is shorter than 16 bytes, some header fields may not + * be correctly modified, hence don't offload such TSO segments. + */ + payload_len = skb->len - (skb_transport_offset(skb) + tcp_hdrlen(skb)); + last_seg_size = payload_len % skb_shinfo(skb)->gso_size; + if (last_seg_size && last_seg_size < 16) + return false; + return true; +} + +static int otx2_get_sqe_count(struct otx2_nic *pfvf, struct sk_buff *skb) +{ + if (!skb_shinfo(skb)->gso_size) + return 1; + + /* HW TSO */ + if (is_hw_tso_supported(pfvf, skb)) + return 1; + + /* SW TSO */ + return skb_shinfo(skb)->gso_segs; +} + +static inline void otx2_set_txtstamp(struct otx2_nic *pfvf, struct sk_buff *skb, + struct otx2_snd_queue *sq, int *offset) +{ + u64 iova; + + if (!skb_shinfo(skb)->gso_size && + skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + iova = sq->timestamps->iova + (sq->head * sizeof(u64)); + otx2_sqe_add_mem(sq, offset, NIX_SENDMEMALG_E_SETTSTMP, iova); + } else { + skb_tx_timestamp(skb); + } +} + +bool otx2_sq_append_skb(struct net_device *netdev, struct otx2_snd_queue *sq, + struct sk_buff *skb, u16 qidx) +{ + struct netdev_queue *txq = netdev_get_tx_queue(netdev, qidx); + struct otx2_nic *pfvf = netdev_priv(netdev); + int offset, num_segs, free_sqe; + struct nix_sqe_hdr_s *sqe_hdr; + + /* Check if there is room for new SQE. + * 'Num of SQBs freed to SQ's pool - SQ's Aura count' + * will give free SQE count. + */ + free_sqe = (sq->num_sqbs - *sq->aura_fc_addr) * sq->sqe_per_sqb; + + if (free_sqe < sq->sqe_thresh || + free_sqe < otx2_get_sqe_count(pfvf, skb)) + return false; + + num_segs = skb_shinfo(skb)->nr_frags + 1; + + /* If SKB doesn't fit in a single SQE, linearize it. + * TODO: Consider adding JUMP descriptor instead. + */ + if (unlikely(num_segs > OTX2_MAX_FRAGS_IN_SQE)) { + if (__skb_linearize(skb)) { + dev_kfree_skb_any(skb); + return true; + } + num_segs = skb_shinfo(skb)->nr_frags + 1; + } + + if (skb_shinfo(skb)->gso_size && !is_hw_tso_supported(pfvf, skb)) { + /* Insert vlan tag before giving pkt to tso */ + if (skb_vlan_tag_present(skb)) + skb = __vlan_hwaccel_push_inside(skb); + otx2_sq_append_tso(pfvf, sq, skb, qidx); + return true; + } + + /* Set SQE's SEND_HDR. + * Do not clear the first 64bit as it contains constant info. + */ + memset(sq->sqe_base + 8, 0, sq->sqe_size - 8); + sqe_hdr = (struct nix_sqe_hdr_s *)(sq->sqe_base); + otx2_sqe_add_hdr(pfvf, sq, sqe_hdr, skb, qidx); + offset = sizeof(*sqe_hdr); + + /* Add extended header if needed */ + otx2_sqe_add_ext(pfvf, sq, skb, &offset); + + /* Add SG subdesc with data frags */ + if (!otx2_sqe_add_sg(pfvf, sq, skb, num_segs, &offset)) { + otx2_dma_unmap_skb_frags(pfvf, &sq->sg[sq->head]); + return false; + } + + otx2_set_txtstamp(pfvf, skb, sq, &offset); + + sqe_hdr->sizem1 = (offset / 16) - 1; + + netdev_tx_sent_queue(txq, skb->len); + + /* Flush SQE to HW */ + otx2_sqe_flush(sq, offset); + + return true; +} +EXPORT_SYMBOL(otx2_sq_append_skb); + +void otx2_cleanup_rx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq) +{ + struct nix_cqe_hdr_s *cqe_hdr; + struct nix_rx_parse_s *parse; + struct nix_rx_sg_s *sg; + int processed_cqe = 0; + void *start, *end; + u64 *iova, pa; + int seg; + + /* Make sure HW writes to CQ are done */ + dma_rmb(); + while ((cqe_hdr = otx2_get_next_cqe(cq))) { + parse = (struct nix_rx_parse_s *) + ((void *)cqe_hdr + sizeof(*cqe_hdr)); + start = (void *)parse + sizeof(*parse); + end = start + ((parse->desc_sizem1 + 1) * 16); + while ((start + sizeof(*sg)) < end) { + sg = (struct nix_rx_sg_s *)start; + iova = (void *)sg + sizeof(*sg); + for (seg = 0; seg < sg->segs; seg++) { + /* Free IOVA */ + *iova -= OTX2_HEAD_ROOM; + pa = otx2_iova_to_phys(pfvf->iommu_domain, + *iova); + dma_unmap_page_attrs(pfvf->dev, *iova, + RCV_FRAG_LEN, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + put_page(virt_to_page(phys_to_virt(pa))); + iova++; + } + start += sizeof(*sg); + start += (sg->segs == 1) ? + sizeof(u64) : 3 * sizeof(u64); + } + cqe_hdr->cqe_type = NIX_XQE_TYPE_INVALID; + processed_cqe++; + } + + /* Free CQEs to HW */ + otx2_write64(pfvf, NIX_LF_CQ_OP_DOOR, + ((u64)cq->cq_idx << 32) | processed_cqe); +} + +void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq) +{ + struct nix_send_comp_s *snd_comp; + struct nix_cqe_hdr_s *cqe_hdr; + struct sk_buff *skb = NULL; + struct otx2_snd_queue *sq; + int processed_cqe = 0; + struct sg_list *sg; + + sq = &pfvf->qset.sq[cq->cint_idx]; + + /* Make sure HW writes to CQ are done */ + dma_rmb(); + while ((cqe_hdr = otx2_get_next_cqe(cq))) { + snd_comp = (struct nix_send_comp_s *) + ((void *)cqe_hdr + sizeof(*cqe_hdr)); + sg = &sq->sg[snd_comp->sqe_id]; + skb = (struct sk_buff *)sg->skb; + if (skb) { + otx2_dma_unmap_skb_frags(pfvf, sg); + dev_kfree_skb_any(skb); + sg->skb = (u64)NULL; + } + + cqe_hdr->cqe_type = NIX_XQE_TYPE_INVALID; + processed_cqe++; + } + + /* Free CQEs to HW */ + otx2_write64(pfvf, NIX_LF_CQ_OP_DOOR, + ((u64)cq->cq_idx << 32) | processed_cqe); +} + +int otx2_rxtx_enable(struct otx2_nic *pfvf, bool enable) +{ + struct msg_req *msg; + int err; + + otx2_mbox_lock(&pfvf->mbox); + if (enable) + msg = otx2_mbox_alloc_msg_nix_lf_start_rx(&pfvf->mbox); + else + msg = otx2_mbox_alloc_msg_nix_lf_stop_rx(&pfvf->mbox); + + if (!msg) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h new file mode 100644 index 000000000000..b898693ac08d --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ + +#ifndef OTX2_TXRX_H +#define OTX2_TXRX_H + +#include <linux/etherdevice.h> +#include <linux/iommu.h> +#include <linux/if_vlan.h> + +#define LBK_CHAN_BASE 0x000 +#define SDP_CHAN_BASE 0x700 +#define CGX_CHAN_BASE 0x800 + +#define DMA_BUFFER_LEN 1536 /* In multiples of 128bytes */ +#define OTX2_DATA_ALIGN(X) ALIGN(X, OTX2_ALIGN) +#define RCV_FRAG_LEN1 \ + ((OTX2_DATA_ALIGN(DMA_BUFFER_LEN + NET_SKB_PAD)) + \ + (OTX2_DATA_ALIGN(sizeof(struct skb_shared_info)))) + +/* Prefer 2048 byte buffers for better last level cache + * utilization or data distribution across regions. + */ +#define RCV_FRAG_LEN ((RCV_FRAG_LEN1 < 2048) ? 2048 : RCV_FRAG_LEN1) + +#define OTX2_HEAD_ROOM OTX2_ALIGN + +#define OTX2_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN) +#define OTX2_MIN_MTU 64 +#define OTX2_MAX_MTU (9212 - OTX2_ETH_HLEN) + +#define OTX2_MAX_GSO_SEGS 255 +#define OTX2_MAX_FRAGS_IN_SQE 9 + +/* IRQ triggered when NIX_LF_CINTX_CNT[ECOUNT] + * is equal to this value. + */ +#define CQ_CQE_THRESH_DEFAULT 10 + +/* IRQ triggered when NIX_LF_CINTX_CNT[ECOUNT] + * is nonzero and this much time elapses after that. + */ +#define CQ_TIMER_THRESH_DEFAULT 1 /* 1 usec */ +#define CQ_TIMER_THRESH_MAX 25 /* 25 usec */ +#define CQ_QCOUNT_DEFAULT 1 + +struct queue_stats { + u64 bytes; + u64 pkts; +}; + +struct otx2_rcv_queue { + struct queue_stats stats; +}; + +struct sg_list { + u16 num_segs; + u64 skb; + u64 size[OTX2_MAX_FRAGS_IN_SQE]; + u64 dma_addr[OTX2_MAX_FRAGS_IN_SQE]; +}; + +struct otx2_snd_queue { + u8 aura_id; + u16 head; + u16 sqe_size; + u32 sqe_cnt; + u16 num_sqbs; + u16 sqe_thresh; + u8 sqe_per_sqb; + u64 io_addr; + u64 *aura_fc_addr; + u64 *lmt_addr; + void *sqe_base; + struct qmem *sqe; + struct qmem *tso_hdrs; + struct sg_list *sg; + struct qmem *timestamps; + struct queue_stats stats; + u16 sqb_count; + u64 *sqb_ptrs; +} ____cacheline_aligned_in_smp; + +enum cq_type { + CQ_RX, + CQ_TX, + CQS_PER_CINT = 2, /* RQ + SQ */ +}; + +struct otx2_cq_poll { + void *dev; +#define CINT_INVALID_CQ 255 + u8 cint_idx; + u8 cq_ids[CQS_PER_CINT]; + struct napi_struct napi; +}; + +struct otx2_pool { + struct qmem *stack; + struct qmem *fc_addr; + u16 rbsize; + u32 page_offset; + u16 pageref; + struct page *page; +}; + +#define CQ_OP_ERROR BIT_ULL(63) +#define CQ_CQ_ERROR BIT_ULL(46) + +struct otx2_cq_queue { + u8 cq_idx; + u8 cq_type; + u8 cint_idx; /* CQ interrupt id */ + u8 refill_task_sched; + u16 cqe_size; + u16 pool_ptrs; + u32 cqe_cnt; + u32 cq_head; + void *cqe_base; + struct qmem *cqe; + struct otx2_pool *rbpool; +} ____cacheline_aligned_in_smp; + +struct otx2_qset { + u32 rqe_cnt; + u32 sqe_cnt; /* Keep these two at top */ +#define OTX2_MAX_CQ_CNT 64 + u16 cq_cnt; + u16 xqe_size; + struct otx2_pool *pool; + struct otx2_cq_poll *napi; + struct otx2_cq_queue *cq; + struct otx2_snd_queue *sq; + struct otx2_rcv_queue *rq; +}; + +/* Translate IOVA to physical address */ +static inline u64 otx2_iova_to_phys(void *iommu_domain, dma_addr_t dma_addr) +{ + /* Translation is installed only when IOMMU is present */ + if (likely(iommu_domain)) + return iommu_iova_to_phys(iommu_domain, dma_addr); + return dma_addr; +} + +int otx2_napi_handler(struct napi_struct *napi, int budget); +bool otx2_sq_append_skb(struct net_device *netdev, struct otx2_snd_queue *sq, + struct sk_buff *skb, u16 qidx); +#endif /* OTX2_TXRX_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c new file mode 100644 index 000000000000..129c693e62aa --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Virtual Function ethernet driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/etherdevice.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include "otx2_common.h" +#include "otx2_reg.h" + +#define DRV_NAME "octeontx2-nicvf" +#define DRV_STRING "Marvell OcteonTX2 NIC Virtual Function Driver" +#define DRV_VERSION "1.0" + +static const struct pci_device_id otx2_vf_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_AFVF) }, + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_VF) }, + { } +}; + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION(DRV_STRING); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); +MODULE_DEVICE_TABLE(pci, otx2_vf_id_table); + +/** + * RVU VF Interrupt Vector Enumeration + */ +enum { + RVU_VF_INT_VEC_MBOX = 0x0, +}; + +static int otx2vf_change_mtu(struct net_device *netdev, int new_mtu) +{ + bool if_up = netif_running(netdev); + int err = 0; + + if (if_up) + otx2vf_stop(netdev); + + netdev_info(netdev, "Changing MTU from %d to %d\n", + netdev->mtu, new_mtu); + netdev->mtu = new_mtu; + + if (if_up) + err = otx2vf_open(netdev); + + return err; +} + +static void otx2vf_process_vfaf_mbox_msg(struct otx2_nic *vf, + struct mbox_msghdr *msg) +{ + if (msg->id >= MBOX_MSG_MAX) { + dev_err(vf->dev, + "Mbox msg with unknown ID %d\n", msg->id); + return; + } + + if (msg->sig != OTX2_MBOX_RSP_SIG) { + dev_err(vf->dev, + "Mbox msg with wrong signature %x, ID %d\n", + msg->sig, msg->id); + return; + } + + if (msg->rc == MBOX_MSG_INVALID) { + dev_err(vf->dev, + "PF/AF says the sent msg(s) %d were invalid\n", + msg->id); + return; + } + + switch (msg->id) { + case MBOX_MSG_READY: + vf->pcifunc = msg->pcifunc; + break; + case MBOX_MSG_MSIX_OFFSET: + mbox_handler_msix_offset(vf, (struct msix_offset_rsp *)msg); + break; + case MBOX_MSG_NPA_LF_ALLOC: + mbox_handler_npa_lf_alloc(vf, (struct npa_lf_alloc_rsp *)msg); + break; + case MBOX_MSG_NIX_LF_ALLOC: + mbox_handler_nix_lf_alloc(vf, (struct nix_lf_alloc_rsp *)msg); + break; + case MBOX_MSG_NIX_TXSCH_ALLOC: + mbox_handler_nix_txsch_alloc(vf, + (struct nix_txsch_alloc_rsp *)msg); + break; + case MBOX_MSG_NIX_BP_ENABLE: + mbox_handler_nix_bp_enable(vf, (struct nix_bp_cfg_rsp *)msg); + break; + default: + if (msg->rc) + dev_err(vf->dev, + "Mbox msg response has err %d, ID %d\n", + msg->rc, msg->id); + } +} + +static void otx2vf_vfaf_mbox_handler(struct work_struct *work) +{ + struct otx2_mbox_dev *mdev; + struct mbox_hdr *rsp_hdr; + struct mbox_msghdr *msg; + struct otx2_mbox *mbox; + struct mbox *af_mbox; + int offset, id; + + af_mbox = container_of(work, struct mbox, mbox_wrk); + mbox = &af_mbox->mbox; + mdev = &mbox->dev[0]; + rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + if (af_mbox->num_msgs == 0) + return; + offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); + + for (id = 0; id < af_mbox->num_msgs; id++) { + msg = (struct mbox_msghdr *)(mdev->mbase + offset); + otx2vf_process_vfaf_mbox_msg(af_mbox->pfvf, msg); + offset = mbox->rx_start + msg->next_msgoff; + mdev->msgs_acked++; + } + + otx2_mbox_reset(mbox, 0); + + /* Clear the IRQ */ + smp_wmb(); + otx2_write64(af_mbox->pfvf, RVU_VF_INT, BIT_ULL(0)); +} + +static int otx2vf_process_mbox_msg_up(struct otx2_nic *vf, + struct mbox_msghdr *req) +{ + /* Check if valid, if not reply with a invalid msg */ + if (req->sig != OTX2_MBOX_REQ_SIG) { + otx2_reply_invalid_msg(&vf->mbox.mbox_up, 0, 0, req->id); + return -ENODEV; + } + + switch (req->id) { +#define M(_name, _id, _fn_name, _req_type, _rsp_type) \ + case _id: { \ + struct _rsp_type *rsp; \ + int err; \ + \ + rsp = (struct _rsp_type *)otx2_mbox_alloc_msg( \ + &vf->mbox.mbox_up, 0, \ + sizeof(struct _rsp_type)); \ + if (!rsp) \ + return -ENOMEM; \ + \ + rsp->hdr.id = _id; \ + rsp->hdr.sig = OTX2_MBOX_RSP_SIG; \ + rsp->hdr.pcifunc = 0; \ + rsp->hdr.rc = 0; \ + \ + err = otx2_mbox_up_handler_ ## _fn_name( \ + vf, (struct _req_type *)req, rsp); \ + return err; \ + } +MBOX_UP_CGX_MESSAGES +#undef M + break; + default: + otx2_reply_invalid_msg(&vf->mbox.mbox_up, 0, 0, req->id); + return -ENODEV; + } + return 0; +} + +static void otx2vf_vfaf_mbox_up_handler(struct work_struct *work) +{ + struct otx2_mbox_dev *mdev; + struct mbox_hdr *rsp_hdr; + struct mbox_msghdr *msg; + struct otx2_mbox *mbox; + struct mbox *vf_mbox; + struct otx2_nic *vf; + int offset, id; + + vf_mbox = container_of(work, struct mbox, mbox_up_wrk); + vf = vf_mbox->pfvf; + mbox = &vf_mbox->mbox_up; + mdev = &mbox->dev[0]; + + rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + if (vf_mbox->up_num_msgs == 0) + return; + + offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); + + for (id = 0; id < vf_mbox->up_num_msgs; id++) { + msg = (struct mbox_msghdr *)(mdev->mbase + offset); + otx2vf_process_mbox_msg_up(vf, msg); + offset = mbox->rx_start + msg->next_msgoff; + } + + otx2_mbox_msg_send(mbox, 0); +} + +static irqreturn_t otx2vf_vfaf_mbox_intr_handler(int irq, void *vf_irq) +{ + struct otx2_nic *vf = (struct otx2_nic *)vf_irq; + struct otx2_mbox_dev *mdev; + struct otx2_mbox *mbox; + struct mbox_hdr *hdr; + + /* Read latest mbox data */ + smp_rmb(); + + /* Check for PF => VF response messages */ + mbox = &vf->mbox.mbox; + mdev = &mbox->dev[0]; + otx2_sync_mbox_bbuf(mbox, 0); + + hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + if (hdr->num_msgs) { + vf->mbox.num_msgs = hdr->num_msgs; + hdr->num_msgs = 0; + memset(mbox->hwbase + mbox->rx_start, 0, + ALIGN(sizeof(struct mbox_hdr), sizeof(u64))); + queue_work(vf->mbox_wq, &vf->mbox.mbox_wrk); + } + /* Check for PF => VF notification messages */ + mbox = &vf->mbox.mbox_up; + mdev = &mbox->dev[0]; + otx2_sync_mbox_bbuf(mbox, 0); + + hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + if (hdr->num_msgs) { + vf->mbox.up_num_msgs = hdr->num_msgs; + hdr->num_msgs = 0; + memset(mbox->hwbase + mbox->rx_start, 0, + ALIGN(sizeof(struct mbox_hdr), sizeof(u64))); + queue_work(vf->mbox_wq, &vf->mbox.mbox_up_wrk); + } + /* Clear the IRQ */ + otx2_write64(vf, RVU_VF_INT, BIT_ULL(0)); + + return IRQ_HANDLED; +} + +static void otx2vf_disable_mbox_intr(struct otx2_nic *vf) +{ + int vector = pci_irq_vector(vf->pdev, RVU_VF_INT_VEC_MBOX); + + /* Disable VF => PF mailbox IRQ */ + otx2_write64(vf, RVU_VF_INT_ENA_W1C, BIT_ULL(0)); + free_irq(vector, vf); +} + +static int otx2vf_register_mbox_intr(struct otx2_nic *vf, bool probe_pf) +{ + struct otx2_hw *hw = &vf->hw; + struct msg_req *req; + char *irq_name; + int err; + + /* Register mailbox interrupt handler */ + irq_name = &hw->irq_name[RVU_VF_INT_VEC_MBOX * NAME_SIZE]; + snprintf(irq_name, NAME_SIZE, "RVUVFAF Mbox"); + err = request_irq(pci_irq_vector(vf->pdev, RVU_VF_INT_VEC_MBOX), + otx2vf_vfaf_mbox_intr_handler, 0, irq_name, vf); + if (err) { + dev_err(vf->dev, + "RVUPF: IRQ registration failed for VFAF mbox irq\n"); + return err; + } + + /* Enable mailbox interrupt for msgs coming from PF. + * First clear to avoid spurious interrupts, if any. + */ + otx2_write64(vf, RVU_VF_INT, BIT_ULL(0)); + otx2_write64(vf, RVU_VF_INT_ENA_W1S, BIT_ULL(0)); + + if (!probe_pf) + return 0; + + /* Check mailbox communication with PF */ + req = otx2_mbox_alloc_msg_ready(&vf->mbox); + if (!req) { + otx2vf_disable_mbox_intr(vf); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&vf->mbox); + if (err) { + dev_warn(vf->dev, + "AF not responding to mailbox, deferring probe\n"); + otx2vf_disable_mbox_intr(vf); + return -EPROBE_DEFER; + } + return 0; +} + +static void otx2vf_vfaf_mbox_destroy(struct otx2_nic *vf) +{ + struct mbox *mbox = &vf->mbox; + + if (vf->mbox_wq) { + flush_workqueue(vf->mbox_wq); + destroy_workqueue(vf->mbox_wq); + vf->mbox_wq = NULL; + } + + if (mbox->mbox.hwbase) + iounmap((void __iomem *)mbox->mbox.hwbase); + + otx2_mbox_destroy(&mbox->mbox); + otx2_mbox_destroy(&mbox->mbox_up); +} + +static int otx2vf_vfaf_mbox_init(struct otx2_nic *vf) +{ + struct mbox *mbox = &vf->mbox; + void __iomem *hwbase; + int err; + + mbox->pfvf = vf; + vf->mbox_wq = alloc_workqueue("otx2_vfaf_mailbox", + WQ_UNBOUND | WQ_HIGHPRI | + WQ_MEM_RECLAIM, 1); + if (!vf->mbox_wq) + return -ENOMEM; + + /* Mailbox is a reserved memory (in RAM) region shared between + * admin function (i.e PF0) and this VF, shouldn't be mapped as + * device memory to allow unaligned accesses. + */ + hwbase = ioremap_wc(pci_resource_start(vf->pdev, PCI_MBOX_BAR_NUM), + pci_resource_len(vf->pdev, PCI_MBOX_BAR_NUM)); + if (!hwbase) { + dev_err(vf->dev, "Unable to map VFAF mailbox region\n"); + err = -ENOMEM; + goto exit; + } + + err = otx2_mbox_init(&mbox->mbox, hwbase, vf->pdev, vf->reg_base, + MBOX_DIR_VFPF, 1); + if (err) + goto exit; + + err = otx2_mbox_init(&mbox->mbox_up, hwbase, vf->pdev, vf->reg_base, + MBOX_DIR_VFPF_UP, 1); + if (err) + goto exit; + + err = otx2_mbox_bbuf_init(mbox, vf->pdev); + if (err) + goto exit; + + INIT_WORK(&mbox->mbox_wrk, otx2vf_vfaf_mbox_handler); + INIT_WORK(&mbox->mbox_up_wrk, otx2vf_vfaf_mbox_up_handler); + otx2_mbox_lock_init(&vf->mbox); + + return 0; +exit: + destroy_workqueue(vf->mbox_wq); + return err; +} + +int otx2vf_open(struct net_device *netdev) +{ + struct otx2_nic *vf; + int err; + + err = otx2_open(netdev); + if (err) + return err; + + /* LBKs do not receive link events so tell everyone we are up here */ + vf = netdev_priv(netdev); + if (is_otx2_lbkvf(vf->pdev)) { + pr_info("%s NIC Link is UP\n", netdev->name); + netif_carrier_on(netdev); + netif_tx_start_all_queues(netdev); + } + + return 0; +} +EXPORT_SYMBOL(otx2vf_open); + +int otx2vf_stop(struct net_device *netdev) +{ + return otx2_stop(netdev); +} +EXPORT_SYMBOL(otx2vf_stop); + +static netdev_tx_t otx2vf_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct otx2_nic *vf = netdev_priv(netdev); + int qidx = skb_get_queue_mapping(skb); + struct otx2_snd_queue *sq; + struct netdev_queue *txq; + + /* Check for minimum and maximum packet length */ + if (skb->len <= ETH_HLEN || + (!skb_shinfo(skb)->gso_size && skb->len > vf->max_frs)) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + sq = &vf->qset.sq[qidx]; + + txq = netdev_get_tx_queue(netdev, qidx); + if (!netif_tx_queue_stopped(txq) && + !otx2_sq_append_skb(netdev, sq, skb, qidx)) { + netif_tx_stop_queue(txq); + + /* Barrier, for stop_queue visible to be on other cpus */ + smp_mb(); + if ((sq->num_sqbs - *sq->aura_fc_addr) > 1) + netif_tx_start_queue(txq); + else + netdev_warn(netdev, + "%s: No free SQE/SQB, stopping SQ%d\n", + netdev->name, qidx); + + return NETDEV_TX_BUSY; + } + + return NETDEV_TX_OK; +} + +static void otx2vf_reset_task(struct work_struct *work) +{ + struct otx2_nic *vf = container_of(work, struct otx2_nic, reset_task); + + if (!netif_running(vf->netdev)) + return; + + otx2vf_stop(vf->netdev); + otx2vf_open(vf->netdev); + netif_trans_update(vf->netdev); +} + +static netdev_features_t +otx2_features_check(struct sk_buff *skb, struct net_device *dev, + netdev_features_t features) +{ + return features; +} + +static const struct net_device_ops otx2vf_netdev_ops = { + .ndo_open = otx2vf_open, + .ndo_stop = otx2vf_stop, + .ndo_start_xmit = otx2vf_xmit, + .ndo_set_mac_address = otx2_set_mac_address, + .ndo_change_mtu = otx2vf_change_mtu, + .ndo_get_stats64 = otx2_get_stats64, + .ndo_tx_timeout = otx2_tx_timeout, + .ndo_features_check = otx2_features_check, +}; + +static int otx2vf_realloc_msix_vectors(struct otx2_nic *vf) +{ + struct otx2_hw *hw = &vf->hw; + int num_vec, err; + + num_vec = hw->nix_msixoff; + num_vec += NIX_LF_CINT_VEC_START + hw->max_queues; + + otx2vf_disable_mbox_intr(vf); + pci_free_irq_vectors(hw->pdev); + pci_free_irq_vectors(hw->pdev); + err = pci_alloc_irq_vectors(hw->pdev, num_vec, num_vec, PCI_IRQ_MSIX); + if (err < 0) { + dev_err(vf->dev, "%s: Failed to realloc %d IRQ vectors\n", + __func__, num_vec); + return err; + } + + err = otx2vf_register_mbox_intr(vf, false); + if (err) + return err; + return 0; +} + +static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int num_vec = pci_msix_vec_count(pdev); + struct device *dev = &pdev->dev; + struct net_device *netdev; + struct otx2_nic *vf; + struct otx2_hw *hw; + int err, qcount; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device\n"); + return err; + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + dev_err(dev, "PCI request regions failed 0x%x\n", err); + return err; + } + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); + if (err) { + dev_err(dev, "Unable to set DMA mask\n"); + goto err_release_regions; + } + + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); + if (err) { + dev_err(dev, "Unable to set consistent DMA mask\n"); + goto err_release_regions; + } + + pci_set_master(pdev); + + qcount = num_online_cpus(); + netdev = alloc_etherdev_mqs(sizeof(*vf), qcount, qcount); + if (!netdev) { + err = -ENOMEM; + goto err_release_regions; + } + + pci_set_drvdata(pdev, netdev); + SET_NETDEV_DEV(netdev, &pdev->dev); + vf = netdev_priv(netdev); + vf->netdev = netdev; + vf->pdev = pdev; + vf->dev = dev; + vf->iommu_domain = iommu_get_domain_for_dev(dev); + vf->flags |= OTX2_FLAG_INTF_DOWN; + hw = &vf->hw; + hw->pdev = vf->pdev; + hw->rx_queues = qcount; + hw->tx_queues = qcount; + hw->max_queues = qcount; + + hw->irq_name = devm_kmalloc_array(&hw->pdev->dev, num_vec, NAME_SIZE, + GFP_KERNEL); + if (!hw->irq_name) + goto err_free_netdev; + + hw->affinity_mask = devm_kcalloc(&hw->pdev->dev, num_vec, + sizeof(cpumask_var_t), GFP_KERNEL); + if (!hw->affinity_mask) + goto err_free_netdev; + + err = pci_alloc_irq_vectors(hw->pdev, num_vec, num_vec, PCI_IRQ_MSIX); + if (err < 0) { + dev_err(dev, "%s: Failed to alloc %d IRQ vectors\n", + __func__, num_vec); + goto err_free_netdev; + } + + vf->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); + if (!vf->reg_base) { + dev_err(dev, "Unable to map physical function CSRs, aborting\n"); + err = -ENOMEM; + goto err_free_irq_vectors; + } + + /* Init VF <=> PF mailbox stuff */ + err = otx2vf_vfaf_mbox_init(vf); + if (err) + goto err_free_irq_vectors; + + /* Register mailbox interrupt */ + err = otx2vf_register_mbox_intr(vf, true); + if (err) + goto err_mbox_destroy; + + /* Request AF to attach NPA and LIX LFs to this AF */ + err = otx2_attach_npa_nix(vf); + if (err) + goto err_disable_mbox_intr; + + err = otx2vf_realloc_msix_vectors(vf); + if (err) + goto err_mbox_destroy; + + err = otx2_set_real_num_queues(netdev, qcount, qcount); + if (err) + goto err_detach_rsrc; + + otx2_setup_dev_hw_settings(vf); + + err = otx2smqvf_probe(vf); + if (!err) + return 0; + else if (err == -EINVAL) + goto err_detach_rsrc; + + /* Assign default mac address */ + otx2_get_mac_from_af(netdev); + + netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | NETIF_F_RXHASH | + NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6; + netdev->features = netdev->hw_features; + /* Support TSO on tag interface */ + netdev->vlan_features |= netdev->features; + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX; + netdev->features |= netdev->hw_features; + netdev->watchdog_timeo = OTX2_TX_TIMEOUT; + + netdev->netdev_ops = &otx2vf_netdev_ops; + + /* MTU range: 68 - 9190 */ + netdev->min_mtu = OTX2_MIN_MTU; + netdev->max_mtu = OTX2_MAX_MTU; + + INIT_WORK(&vf->reset_task, otx2vf_reset_task); + + if (is_otx2_lbkvf(vf->pdev)) { + int n; + + n = (vf->pcifunc >> RVU_PFVF_FUNC_SHIFT) & RVU_PFVF_FUNC_MASK; + /* Need to subtract 1 to get proper VF number */ + n -= 1; + snprintf(netdev->name, sizeof(netdev->name), "lbk%d", n); + } + + err = register_netdev(netdev); + if (err) { + dev_err(dev, "Failed to register netdevice\n"); + goto err_detach_rsrc; + } + + otx2vf_set_ethtool_ops(netdev); + + return 0; + +err_detach_rsrc: + otx2_detach_resources(&vf->mbox); +err_disable_mbox_intr: + otx2vf_disable_mbox_intr(vf); +err_mbox_destroy: + otx2vf_vfaf_mbox_destroy(vf); +err_free_irq_vectors: + pci_free_irq_vectors(hw->pdev); +err_free_netdev: + pci_set_drvdata(pdev, NULL); + free_netdev(netdev); +err_release_regions: + pci_release_regions(pdev); + return err; +} + +static void otx2vf_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct otx2_nic *vf; + + if (!netdev) + return; + + vf = netdev_priv(netdev); + + if (otx2smqvf_remove(vf)) + unregister_netdev(netdev); + + otx2vf_disable_mbox_intr(vf); + + otx2_detach_resources(&vf->mbox); + otx2vf_vfaf_mbox_destroy(vf); + pci_free_irq_vectors(vf->pdev); + pci_set_drvdata(pdev, NULL); + free_netdev(netdev); + + pci_release_regions(pdev); +} + +static struct pci_driver otx2vf_driver = { + .name = DRV_NAME, + .id_table = otx2_vf_id_table, + .probe = otx2vf_probe, + .remove = otx2vf_remove, + .shutdown = otx2vf_remove, +}; + +static int __init otx2vf_init_module(void) +{ + pr_info("%s: %s\n", DRV_NAME, DRV_STRING); + + return pci_register_driver(&otx2vf_driver); +} + +static void __exit otx2vf_cleanup_module(void) +{ + pci_unregister_driver(&otx2vf_driver); +} + +module_init(otx2vf_init_module); +module_exit(otx2vf_cleanup_module); diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 011c57cae4b0..6979e895ba1b 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -196,6 +196,14 @@ config PCI_HOST_THUNDER_PEM help Say Y here if you want PCIe support for CN88XX Cavium Thunder SoCs. +config PCI_HOST_OCTEONTX2_PEM + bool "Marvell OcteonTX2 PCIe controller to off-chip devices" + depends on ARM64 + depends on OF + select PCI_HOST_COMMON + help + Say Y here if you want PCIe support for CN9XXX Marvell OcteonTX2 SoCs. + config PCI_HOST_THUNDER_ECAM bool "Cavium Thunder ECAM controller to on-chip devices on pass-1.x silicon" depends on ARM64 || COMPILE_TEST diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index d56a507495c5..bb635ceb14b8 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -46,5 +46,6 @@ obj-y += dwc/ ifdef CONFIG_PCI obj-$(CONFIG_ARM64) += pci-thunder-ecam.o obj-$(CONFIG_ARM64) += pci-thunder-pem.o +obj-$(CONFIG_ARM64) += pci-octeontx2-pem.o obj-$(CONFIG_ARM64) += pci-xgene.o endif diff --git a/drivers/pci/controller/pci-octeontx2-pem.c b/drivers/pci/controller/pci-octeontx2-pem.c new file mode 100644 index 000000000000..1e14dc065322 --- /dev/null +++ b/drivers/pci/controller/pci-octeontx2-pem.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 PCIe host controller + * + * Copyright (C) 2019 Marvell International Ltd. + * + * 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/bitfield.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/pci-acpi.h> +#include <linux/pci-ecam.h> +#include <linux/platform_device.h> +#include "../pci.h" + +#if defined(CONFIG_PCI_HOST_OCTEONTX2_PEM) + +/* Bridge config space reads/writes done using + * these registers. + */ +#define PEM_CFG_WR 0x018 +#define PEM_CFG_RD 0x020 +#define PEM_IB_MERGE_TIMER_CTL 0x1C0 + +#define PCIERC_RAS_EINJ_EN 0x348 +#define PCIERC_RAS_EINJ_CTL6CMPP0 0x364 +#define PCIERC_RAS_EINJ_CTL6CMPV0 0x374 +#define PCIERC_RAS_EINJ_CTL6CHGP1 0x388 +#define PCIERC_RAS_EINJ_CTL6CHGV1 0x398 +#define PCIERC_RAS_EINJ_CTL6PE 0x3A4 +#define PCIERC_RASDP_EP_CTL 0x420 +#define PCIERC_RASDP_DE_ME 0x440 + +struct octeontx2_pem_pci { + u32 ea_entry[3]; + void __iomem *pem_reg_base; +}; + +static int octeontx2_pem_bridge_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct pci_config_window *cfg = bus->sysdata; + struct octeontx2_pem_pci *pem_pci; + u64 read_val; + + if (devfn != 0 || where >= 2048) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + pem_pci = (struct octeontx2_pem_pci *)cfg->priv; + + /* + * 32-bit accesses only. Write the address to the low order + * bits of PEM_CFG_RD, then trigger the read by reading back. + * The config data lands in the upper 32-bits of PEM_CFG_RD. + */ + read_val = where & ~3ull; + writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD); + read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); + read_val >>= 32; + + /* HW reset value at few config space locations are + * garbage, fix them. + */ + switch (where & ~3) { + case 0x00: /* DevID & VenID */ + read_val = 0xA02D177D; + break; + case 0x04: + read_val = 0x00100006; + break; + case 0x08: + read_val = 0x06040100; + break; + case 0x0c: + read_val = 0x00010000; + break; + case 0x18: + read_val = 0x00010100; + break; + case 0x40: + read_val &= 0xffff00ff; + read_val |= 0x00005000; /* In RC mode, point to EA capability */ + break; + case 0x5c: /* EA_ENTRY2 */ + read_val = pem_pci->ea_entry[0]; + break; + case 0x60: /* EA_ENTRY3 */ + read_val = pem_pci->ea_entry[1]; + break; + case 0x64: /* EA_ENTRY4 */ + read_val = pem_pci->ea_entry[2]; + break; + case 0x70: /* Express Cap */ + /* HW reset value is '0', set PME interrupt vector to 1 */ + if (!(read_val & (0x1f << 25))) + read_val |= (1u << 25); + break; + default: + break; + } + read_val >>= (8 * (where & 3)); + switch (size) { + case 1: + read_val &= 0xff; + break; + case 2: + read_val &= 0xffff; + break; + default: + break; + } + *val = read_val; + return PCIBIOS_SUCCESSFUL; +} + +static int octeontx2_pem_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct pci_config_window *cfg = bus->sysdata; + + if (bus->number < cfg->busr.start || + bus->number > cfg->busr.end) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * The first device on the bus is the PEM PCIe bridge. + * Special case its config access. + */ + if (bus->number == cfg->busr.start) + return octeontx2_pem_bridge_read(bus, devfn, where, size, val); + + return pci_generic_config_read(bus, devfn, where, size, val); +} + +/* + * Some of the w1c_bits below also include read-only or non-writable + * reserved bits, this makes the code simpler and is OK as the bits + * are not affected by writing zeros to them. + */ +static u32 octeontx2_pem_bridge_w1c_bits(u64 where_aligned) +{ + u32 w1c_bits = 0; + + switch (where_aligned) { + case 0x04: /* Command/Status */ + case 0x1c: /* Base and I/O Limit/Secondary Status */ + w1c_bits = 0xff000000; + break; + case 0x44: /* Power Management Control and Status */ + w1c_bits = 0xfffffe00; + break; + case 0x78: /* Device Control/Device Status */ + case 0x80: /* Link Control/Link Status */ + case 0x88: /* Slot Control/Slot Status */ + case 0x90: /* Root Status */ + case 0xa0: /* Link Control 2 Registers/Link Status 2 */ + w1c_bits = 0xffff0000; + break; + case 0x104: /* Uncorrectable Error Status */ + case 0x110: /* Correctable Error Status */ + case 0x130: /* Error Status */ + case 0x180: /* Lane error status */ + w1c_bits = 0xffffffff; + break; + default: + break; + } + return w1c_bits; +} + +/* Some bits must be written to one so they appear to be read-only. */ +static u32 octeontx2_pem_bridge_w1_bits(u64 where_aligned) +{ + u32 w1_bits; + + switch (where_aligned) { + case 0x1c: /* I/O Base / I/O Limit, Secondary Status */ + /* Force 32-bit I/O addressing. */ + w1_bits = 0x0101; + break; + case 0x24: /* Prefetchable Memory Base / Prefetchable Memory Limit */ + /* Force 64-bit addressing */ + w1_bits = 0x00010001; + break; + default: + w1_bits = 0; + break; + } + return w1_bits; +} + +static int octeontx2_pem_bridge_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct pci_config_window *cfg = bus->sysdata; + struct octeontx2_pem_pci *pem_pci; + u64 where_aligned = where & ~3ull; + u64 write_val, read_val; + u32 mask = 0; + + + if (devfn != 0 || where >= 2048) + return PCIBIOS_DEVICE_NOT_FOUND; + + pem_pci = (struct octeontx2_pem_pci *)cfg->priv; + + /* + * 32-bit accesses only. If the write is for a size smaller + * than 32-bits, we must first read the 32-bit value and merge + * in the desired bits and then write the whole 32-bits back + * out. + */ + switch (size) { + case 1: + writeq(where_aligned, pem_pci->pem_reg_base + PEM_CFG_RD); + read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); + read_val >>= 32; + mask = ~(0xff << (8 * (where & 3))); + read_val &= mask; + val = (val & 0xff) << (8 * (where & 3)); + val |= (u32)read_val; + break; + case 2: + writeq(where_aligned, pem_pci->pem_reg_base + PEM_CFG_RD); + read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); + read_val >>= 32; + mask = ~(0xffff << (8 * (where & 3))); + read_val &= mask; + val = (val & 0xffff) << (8 * (where & 3)); + val |= (u32)read_val; + break; + default: + break; + } + + /* + * By expanding the write width to 32 bits, we may + * inadvertently hit some W1C bits that were not intended to + * be written. Calculate the mask that must be applied to the + * data to be written to avoid these cases. + */ + if (mask) { + u32 w1c_bits = octeontx2_pem_bridge_w1c_bits(where); + + if (w1c_bits) { + mask &= w1c_bits; + val &= ~mask; + } + } + + /* + * Some bits must be read-only with value of one. Since the + * access method allows these to be cleared if a zero is + * written, force them to one before writing. + */ + val |= octeontx2_pem_bridge_w1_bits(where_aligned); + + /* + * Low order bits are the config address, the high order 32 + * bits are the data to be written. + */ + write_val = (((u64)val) << 32) | where_aligned; + writeq(write_val, pem_pci->pem_reg_base + PEM_CFG_WR); + return PCIBIOS_SUCCESSFUL; +} + +static void octeontx2_be_workaround_init(struct pci_bus *bus) +{ + u32 val; + + /* Ensure that PCIERC_RASDP_DE_ME.ERR_MODE is set to 0 */ + octeontx2_pem_bridge_read(bus, 0x00, + PCIERC_RASDP_DE_ME, 4, &val); + octeontx2_pem_bridge_write(bus, 0x00, + PCIERC_RASDP_DE_ME, 4, val & ~BIT(0)); + + /* Disable parity error correction */ + octeontx2_pem_bridge_read(bus, 0x00, + PCIERC_RASDP_EP_CTL, 4, &val); + octeontx2_pem_bridge_write(bus, 0x00, + PCIERC_RASDP_EP_CTL, 4, val | BIT(0)); + + /* Enable RAS to change header + * PCIERC_RAS_EINJ_EN.EINJ0_EN.set(0); + * PCIERC_RAS_EINJ_EN.EINJ1_EN.set(0); + * PCIERC_RAS_EINJ_EN.EINJ2_EN.set(0); + * PCIERC_RAS_EINJ_EN.EINJ3_EN.set(0); + * PCIERC_RAS_EINJ_EN.EINJ4_EN.set(0); + * PCIERC_RAS_EINJ_EN.EINJ5_EN.set(0); + * PCIERC_RAS_EINJ_EN.EINJ6_EN.set(1); + */ + octeontx2_pem_bridge_write(bus, 0x00, + PCIERC_RAS_EINJ_EN, 4, BIT(6)); + + /* Set up error injection count to 1 and + * set type to TLP and INV_CNTRL must be 0. + */ + octeontx2_pem_bridge_write(bus, 0x00, + PCIERC_RAS_EINJ_CTL6PE, 4, 1); + + /* Set up compare point to compare Fmt/Type field in TLP Header word 0 + * Where bits[31:0] = tlp_dw[7:0], tlp_dw[15:18], + * tlp_dw[23:16], tlp_dw[31:24]. + * + * PCIERC_RAS_EINJ_CTL6CMPP0.EINJ6_COM_PT_H0.set(32'hfe00_0000); + */ + octeontx2_pem_bridge_write(bus, 0x00, + PCIERC_RAS_EINJ_CTL6CMPP0, 4, 0xFE000000); + + /* Set up the value to compare against, + * look for Fmt/Type to indicate CfgRd/CfWr - both type 0 or 1. + * Where bits[31:0] = tlp_dw[7:0], tlp_dw[15:18], + * tlp_dw[23:16], tlp_dw[31:24] + */ + octeontx2_pem_bridge_write(bus, 0x00, + PCIERC_RAS_EINJ_CTL6CMPV0, 4, 0x44000000); + + /* Set up the bit position in TLP Header word 1 to replace + * (LBE is bits 7:4, FBE is bits 3:0). + * + * Where bits[31:0] = tlp_dw[7:0], tlp_dw[15:18], + * tlp_dw[23:16], tlp_dw[31:24]. + */ + octeontx2_pem_bridge_write(bus, 0x00, + PCIERC_RAS_EINJ_CTL6CHGP1, 4, 0xFF); +} + +static void octeontx2_be_workaround(struct pci_bus *bus, int where, + int size, u32 val) +{ + struct pci_host_bridge *rc; + u32 reg, be = 0; + + rc = pci_find_host_bridge(bus); + + /* Setup RAS to inject one error */ + octeontx2_be_workaround_init(rc->bus); + + /* Get byte-enable to inject into TLP */ + where &= 0x03; + switch (size) { + case 1: + be = 1 << where; + break; + case 2: + be = 3 << where; + break; + case 4: + be = 0xF; + } + + /* Set up the value you'd like to use for FBE (Cfg ops must have LBE==0) + * Where bits[31:0] = tlp_dw[7:0], tlp_dw[15:18], + * tlp_dw[23:16], tlp_dw[31:24]. + */ + octeontx2_pem_bridge_write(rc->bus, 0x00, + PCIERC_RAS_EINJ_CTL6CHGV1, 4, be); + + /* To be absolutely sure that the ECAM access does not get to + * the MAC prior to the PCIERC register updates that are setting + * up for that ECAM access, SW should read back one of the + * registers it wrote before launching the ECAM access. + */ + octeontx2_pem_bridge_read(rc->bus, 0x00, + PCIERC_RAS_EINJ_CTL6CHGV1, 4, ®); +} + +static int octeontx2_pem_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct pci_config_window *cfg = bus->sysdata; + + if (bus->number < cfg->busr.start || + bus->number > cfg->busr.end) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * The first device on the bus is the PEM PCIe bridge. + * Special case its config access. + */ + if (bus->number == cfg->busr.start) + return octeontx2_pem_bridge_write(bus, devfn, where, size, val); + + octeontx2_be_workaround(bus, where, size, val); + + return pci_generic_config_write(bus, devfn, where, size, val); +} + +static int octeontx2_pem_init(struct device *dev, struct pci_config_window *cfg, + struct resource *res_pem) +{ + struct octeontx2_pem_pci *pem_pci; + resource_size_t bar4_start; + u64 val; + + pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL); + if (!pem_pci) + return -ENOMEM; + + pem_pci->pem_reg_base = devm_ioremap(dev, res_pem->start, 0x10000); + if (!pem_pci->pem_reg_base) + return -ENOMEM; + + /* As per HW Errata 34726, an issue exists whereby inbound write + * merging may cause undefined operation. Hence disabling it. + * + * Need to revisit this for future silicon passes and versions. + */ + val = readq(pem_pci->pem_reg_base + PEM_IB_MERGE_TIMER_CTL); + val |= BIT_ULL(10); + writeq(val, pem_pci->pem_reg_base + PEM_IB_MERGE_TIMER_CTL); + + /* + * The MSI-X BAR for the PEM and AER interrupts is located at + * a fixed offset from the PEM register base. Generate a + * fragment of the synthesized Enhanced Allocation capability + * structure here for the BAR. + */ + bar4_start = res_pem->start + 0xf00000000; + pem_pci->ea_entry[0] = (u32)bar4_start | 2; + pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u; + pem_pci->ea_entry[2] = (u32)(bar4_start >> 32); + + cfg->priv = pem_pci; + return 0; +} + +static int octeontx2_pem_platform_init(struct pci_config_window *cfg) +{ + struct device *dev = cfg->parent; + struct platform_device *pdev; + struct resource *res_pem; + + if (!dev->of_node) + return -EINVAL; + + pdev = to_platform_device(dev); + + /* + * The second register range is the PEM bridge to the PCIe + * bus. It has a different config access method than those + * devices behind the bridge. + */ + res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res_pem) { + dev_err(dev, "missing \"reg[1]\"property\n"); + return -EINVAL; + } + + return octeontx2_pem_init(dev, cfg, res_pem); +} + +static struct pci_ecam_ops pci_octeontx2_pem_ops = { + .bus_shift = 20, + .init = octeontx2_pem_platform_init, + .pci_ops = { + .map_bus = pci_ecam_map_bus, + .read = octeontx2_pem_config_read, + .write = octeontx2_pem_config_write, + } +}; + +static const struct of_device_id octeontx2_pem_of_match[] = { + { .compatible = "marvell,pci-host-octeontx2-pem" }, + { }, +}; + +static int octeontx2_pem_probe(struct platform_device *pdev) +{ + return pci_host_common_probe(pdev, &pci_octeontx2_pem_ops); +} + +static struct platform_driver octeontx2_pem_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = octeontx2_pem_of_match, + .suppress_bind_attrs = true, + }, + .probe = octeontx2_pem_probe, +}; +builtin_platform_driver(octeontx2_pem_driver); + +#endif diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0e8e2c186f50..1910b3394d2c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1094,14 +1094,15 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus, * @sec: updated with secondary bus number from EA * @sub: updated with subordinate bus number from EA * - * If @dev is a bridge with EA capability, update @sec and @sub with - * fixed bus numbers from the capability and return true. Otherwise, - * return false. + * If @dev is a bridge with EA capability that specifies valid secondary + * and subordinate bus numbers, return true with the bus numbers in @sec + * and @sub. Otherwise return false. */ static bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub) { int ea, offset; u32 dw; + u8 ea_sec, ea_sub; if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) return false; @@ -1113,8 +1114,13 @@ static bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub) offset = ea + PCI_EA_FIRST_ENT; pci_read_config_dword(dev, offset, &dw); - *sec = dw & PCI_EA_SEC_BUS_MASK; - *sub = (dw & PCI_EA_SUB_BUS_MASK) >> PCI_EA_SUB_BUS_SHIFT; + ea_sec = dw & PCI_EA_SEC_BUS_MASK; + ea_sub = (dw & PCI_EA_SUB_BUS_MASK) >> PCI_EA_SUB_BUS_SHIFT; + if (ea_sec == 0 || ea_sub < ea_sec) + return false; + + *sec = ea_sec; + *sub = ea_sub; return true; } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 0f16acc323c6..5928eea7d4a3 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4249,9 +4249,7 @@ static bool pci_quirk_cavium_acs_match(struct pci_dev *dev) * bits of device ID are used to indicate which subdevice is used * within the SoC. */ - return (pci_is_pcie(dev) && - (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) && - ((dev->device & 0xf800) == 0xa000)); + return (pci_is_pcie(dev) && ((dev->device & 0xf800) == 0xa000)); } static int pci_quirk_cavium_acs(struct pci_dev *dev, u16 acs_flags) diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig index e4221a107dca..1d26e38ad463 100644 --- a/drivers/perf/Kconfig +++ b/drivers/perf/Kconfig @@ -55,7 +55,7 @@ config ARM_PMU_ACPI config ARM_SMMU_V3_PMU tristate "ARM SMMUv3 Performance Monitors Extension" - depends on ARM64 && ACPI && ARM_SMMU_V3 + depends on ARM64 && ARM_SMMU_V3 help Provides support for the ARM SMMUv3 Performance Monitor Counter Groups (PMCG), which provide monitoring of transactions passing diff --git a/drivers/perf/arm_smmuv3_pmu.c b/drivers/perf/arm_smmuv3_pmu.c index da71c741cb46..94f1e83bec2e 100644 --- a/drivers/perf/arm_smmuv3_pmu.c +++ b/drivers/perf/arm_smmuv3_pmu.c @@ -36,6 +36,7 @@ #include <linux/acpi.h> #include <linux/acpi_iort.h> +#include <linux/of.h> #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/cpuhotplug.h> @@ -280,8 +281,13 @@ static int smmu_pmu_apply_event_filter(struct smmu_pmu *smmu_pmu, /* Requested settings same as current global settings*/ if (span == smmu_pmu->global_filter_span && - sid == smmu_pmu->global_filter_sid) + sid == smmu_pmu->global_filter_sid) { + if (idx == 0) + smmu_pmu_set_event_filter(event, idx, span, sid); + else + smmu_pmu_set_event_filter(event, idx, 0, 0); return 0; + } if (!bitmap_empty(smmu_pmu->used_counters, num_ctrs)) return -EAGAIN; @@ -829,9 +835,16 @@ static void smmu_pmu_shutdown(struct platform_device *pdev) smmu_pmu_disable(&smmu_pmu->pmu); } +static const struct of_device_id smmu_pmu_of_match[] = { + { .compatible = "arm,smmu-pmu-v3", }, + { }, +}; +MODULE_DEVICE_TABLE(of, smmu_pmu_of_match); + static struct platform_driver smmu_pmu_driver = { .driver = { .name = "arm-smmu-v3-pmcg", + .of_match_table = of_match_ptr(smmu_pmu_of_match), }, .probe = smmu_pmu_probe, .remove = smmu_pmu_remove, diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 833e04a7835c..228a67ce294f 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -9,6 +9,7 @@ source "drivers/soc/bcm/Kconfig" source "drivers/soc/fsl/Kconfig" source "drivers/soc/imx/Kconfig" source "drivers/soc/ixp4xx/Kconfig" +source "drivers/soc/marvell/Kconfig" source "drivers/soc/mediatek/Kconfig" source "drivers/soc/qcom/Kconfig" source "drivers/soc/renesas/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 2ec355003524..b684e617e308 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_ARCH_IXP4XX) += ixp4xx/ obj-$(CONFIG_SOC_XWAY) += lantiq/ obj-y += mediatek/ obj-y += amlogic/ +obj-y += marvell/ obj-y += qcom/ obj-y += renesas/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ diff --git a/drivers/soc/marvell/Kconfig b/drivers/soc/marvell/Kconfig new file mode 100644 index 000000000000..67bd5ca92b18 --- /dev/null +++ b/drivers/soc/marvell/Kconfig @@ -0,0 +1,85 @@ +# +# MARVELL SoC drivers +# + +menu "Marvell SoC drivers" + +config OCTEONTX2_RM + tristate "OcteonTX2 RVU Resource Manager driver" + depends on OCTEONTX2_AF + ---help--- + This driver offers resource management interfaces for Marvell's + OcteonTX2 Resource Virtualization Unit SSO/TIM PFs which are used + for interfacing with non-NIC hardware offload units. + +config OCTEONTX2_RM_DOM_SYSFS + bool "OcteonTX2 RVU Resource Manager domain sysfs" + depends on OCTEONTX2_RM + ---help--- + Enable Application Domain sysfs which simplifies management of + SSO/TIM VFs and OcteonTX2 RVU based NIC devices by the system + administrator. This interface consists of the following files: + + I. /sys/bus/pci/drivers/octeontx2-rm/0*/create_domain + + Writing to this file will: + 1. Create a domain directory in /sys/bus/pci/drivers/octeontx2-rm/0* + with the domain name + 2. Reserve one of SSO/TIM VFs for this domain and set its limits + according to the specification passed in write string + 3. Create symlinks to all devices that will be part of the domain + in the directory created in point 1 + 4. Create domain_id file returning the ID assigned to this domain + (effectively the domain name) + 5. Create domain_in_use file which reports state of domain's + SSO/TIM device's in_use file to indicate when domain is used + by an application. + + The syntax for writing into this file is: + + name;param:val(;param:val)* + + * name - domain name + * param - parameter name, based on the parameter, its value 'val' + has to have a different format: + * sso, ssow, npa, tim, cpt - 'val' is an integer value of the + number of LFs to assign to the domain + * port - 'val' is in 'DDDD:BB:DD.F' format and specifies device + representing a port. + + There are the following rules when creating a domain: + + 1. Domain names must be unique + 2. Each domain must have at least 1 NPA and 1 SSOW LF + 3. One port may be only assigned to a single domain + + II. /sys/bus/pci/drivers/octeontx2-rm/0*/destroy_domain + + Writing domain name to this file will cause given domain to be + removed from the sysfs. This includes: + 1. Setting all limits of domain's SSO/TIM device to 0 + 2. Removing all sysfs structures for this domain + 3. Removing all ports in this application domain from the list of + ports in use. + + Removal of the domain is disabled while domain is in use, that + is while the 'in_use' flag of the domain's SSO/TIM device is set. + User/admin may query the status of this flag using the + 'domain_in_use' file in the domain's sysfs directory. + +config OCTEONTX2_DPI_PF + tristate "OcteonTX2 DPI-DMA PF driver" + depends on ARM64 && PCI + ---help--- + Select this option to enable DPI PF driver support. + DPI (DMA packet interface) provides DMA support for MAC. + This driver intializes dpi PF device and enables VF's for supporting + different types of DMA transfers. + +config MDIO_DEBUGFS + tristate "Stub driver for debugfs support for mdio commands " + depends on OCTEONTX2_AF + help + provides debugfs support to initiate mdio commands via smc call + to the atf. +endmenu diff --git a/drivers/soc/marvell/Makefile b/drivers/soc/marvell/Makefile new file mode 100644 index 000000000000..cdc062497815 --- /dev/null +++ b/drivers/soc/marvell/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-y += octeontx2-rm/ +obj-y += octeontx2-dpi/ +obj-$(CONFIG_MDIO_DEBUGFS) += mdio_debugfs.o diff --git a/drivers/soc/marvell/mdio_debugfs.c b/drivers/soc/marvell/mdio_debugfs.c new file mode 100644 index 000000000000..652108960499 --- /dev/null +++ b/drivers/soc/marvell/mdio_debugfs.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ + +#ifdef CONFIG_DEBUG_FS + +#include <linux/fs.h> +#include <linux/debugfs.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/string.h> +#include <linux/uaccess.h> +#include <linux/arm-smccc.h> + +#define OCTEONTX_MDIO_DBG_READ 0xc2000d01 +#define OCTEONTX_MDIO_DBG_WRITE 0xc2000d02 + +struct dentry *pfile; +static int parse_cmd_buffer_ctx(char *cmd_buf, size_t *count, + const char __user *buffer, + int *a, bool *write) +{ + int bytes_not_copied; + char *subtoken; + int ret, i; + + bytes_not_copied = copy_from_user(cmd_buf, buffer, *count); + if (bytes_not_copied) + return -EFAULT; + + cmd_buf[*count] = '\0'; + for (i = 0; i < 5; i++) { + subtoken = strsep(&cmd_buf, " "); + ret = subtoken ? kstrtoint(subtoken, 10, &a[i]) : -EINVAL; + if (ret < 0) + return ret; + } + if (cmd_buf) { + subtoken = strsep(&cmd_buf, " "); + ret = subtoken ? kstrtoint(subtoken, 10, &a[i]) : -EINVAL; + if (ret < 0) + return ret; + *write = true; + } + if (cmd_buf) + return -EINVAL; + return ret; +} + +static ssize_t dbg_mdio_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct arm_smccc_res res; + bool write = false; + char *cmd_buf; + int ret, a[6]; + + if ((*ppos != 0) || !count) + return -EINVAL; + + cmd_buf = kzalloc(count + 1, GFP_KERNEL); + if (!cmd_buf) + return count; + + ret = parse_cmd_buffer_ctx(cmd_buf, &count, buffer, a, &write); + if (ret < 0) { + pr_info("Usage: echo <cgxlmac> <mode> <addr> <devad> <reg> [value] > mdio_cmd\n"); + goto done; + } else { + if (write) + arm_smccc_smc(OCTEONTX_MDIO_DBG_WRITE, a[0], a[1], a[2], + a[3], a[4], a[5], 0, &res); + else + arm_smccc_smc(OCTEONTX_MDIO_DBG_READ, a[0], a[1], a[2], + a[3], a[4], 0, 0, &res); + pr_info("MDIO COMMAND RESULT\n"); + pr_info("===================\n"); + pr_info("res[0]:\t%ld\n", res.a0); + pr_info("res[1]:\t%ld\n", res.a1); + pr_info("res[2]:\t%ld\n", res.a2); + pr_info("res[3]:\t%ld\n", res.a3); + } +done: + kfree(cmd_buf); + return ret ? ret : count; +} + +static const struct file_operations dbg_mdio_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = dbg_mdio_write, +}; + +static int dbg_mdio_init(void) +{ + pfile = debugfs_create_file("mdio_cmd", 0644, NULL, NULL, + &dbg_mdio_fops); + if (!pfile) + goto create_failed; + return 0; +create_failed: + pr_err("Failed to create debugfs dir/file for mdio_cmd\n"); + debugfs_remove_recursive(pfile); + return 0; +} + +static void dbg_mdio_exit(void) +{ + debugfs_remove_recursive(pfile); +} +module_init(dbg_mdio_init); +module_exit(dbg_mdio_exit); +#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/soc/marvell/octeontx2-dpi/Makefile b/drivers/soc/marvell/octeontx2-dpi/Makefile new file mode 100644 index 000000000000..73640517593c --- /dev/null +++ b/drivers/soc/marvell/octeontx2-dpi/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Marvell's OcteonTX2 DPI PF driver +# + +obj-$(CONFIG_OCTEONTX2_DPI_PF) += octeontx2_dpi.o + +octeontx2_dpi-y := dpi.o diff --git a/drivers/soc/marvell/octeontx2-dpi/dpi.c b/drivers/soc/marvell/octeontx2-dpi/dpi.c new file mode 100644 index 000000000000..1be37f3b5d50 --- /dev/null +++ b/drivers/soc/marvell/octeontx2-dpi/dpi.c @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 DPI PF driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/pci.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/sysfs.h> + +#include "dpi.h" + +#define DPI_DRV_NAME "octeontx2-dpi" +#define DPI_DRV_STRING "Marvell OcteonTX2 DPI-DMA Driver" +#define DPI_DRV_VERSION "1.0" + +/* Supported devices */ +static const struct pci_device_id dpi_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_DPI_PF) }, + { 0, } /* end of table */ +}; +MODULE_DEVICE_TABLE(pci, dpi_id_table); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION(DPI_DRV_STRING); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DPI_DRV_VERSION); + +static void dpi_reg_write(struct dpipf *dpi, u64 offset, u64 val) +{ + writeq(val, dpi->reg_base + offset); +} + +static u64 dpi_reg_read(struct dpipf *dpi, u64 offset) +{ + return readq(dpi->reg_base + offset); +} + +static int dpi_dma_engine_get_num(void) +{ + return DPI_MAX_ENGINES; +} + +int dpi_queue_init(struct dpipf *dpi, struct dpipf_vf *dpivf, u8 vf) +{ + int engine = 0; + int queue = vf; + u64 reg = 0ULL; + u32 aura = dpivf->vf_config.aura; + u16 buf_size = dpivf->vf_config.csize; + u16 sso_pf_func = dpivf->vf_config.sso_pf_func; + u16 npa_pf_func = dpivf->vf_config.npa_pf_func; + + dpi_reg_write(dpi, DPI_DMAX_IBUFF_CSIZE(queue), + DPI_DMA_IBUFF_CSIZE_CSIZE((u64)(buf_size / 8))); + + /* IDs are already configured while crating the domains. + * No need to configure here. + */ + for (engine = 0; engine < dpi_dma_engine_get_num(); engine++) { + /* Dont configure the queus for PKT engines */ + if (engine >= 4) + break; + + reg = 0; + reg = dpi_reg_read(dpi, DPI_DMA_ENGX_EN(engine)); + reg |= DPI_DMA_ENG_EN_QEN(0x1 << queue); + dpi_reg_write(dpi, DPI_DMA_ENGX_EN(engine), reg); + } + + reg = dpi_reg_read(dpi, DPI_DMAX_IDS2(queue)); + reg |= DPI_DMA_IDS2_INST_AURA(aura); + dpi_reg_write(dpi, DPI_DMAX_IDS2(queue), reg); + + reg = dpi_reg_read(dpi, DPI_DMAX_IDS(queue)); + reg |= DPI_DMA_IDS_DMA_NPA_PF_FUNC(npa_pf_func); + reg |= DPI_DMA_IDS_DMA_SSO_PF_FUNC(sso_pf_func); + reg |= DPI_DMA_IDS_DMA_STRM(vf + 1); + reg |= DPI_DMA_IDS_INST_STRM(vf + 1); + dpi_reg_write(dpi, DPI_DMAX_IDS(queue), reg); + + return 0; +} + +int dpi_queue_fini(struct dpipf *dpi, struct dpipf_vf *dpivf, u8 vf) +{ + u64 reg = 0ULL; + int engine = 0; + int queue = vf; + u16 buf_size = dpivf->vf_config.csize; + + for (engine = 0; engine < dpi_dma_engine_get_num(); engine++) { + /* Dont configure the queus for PKT engines */ + if (engine >= 4) + break; + + reg = 0; + reg = dpi_reg_read(dpi, DPI_DMA_ENGX_EN(engine)); + reg &= DPI_DMA_ENG_EN_QEN((~(1 << queue))); + dpi_reg_write(dpi, DPI_DMA_ENGX_EN(engine), reg); + } + + dpi_reg_write(dpi, DPI_DMAX_QRST(queue), 0x1ULL); + /* TBD: below code required ? */ + dpi_reg_write(dpi, DPI_DMAX_IBUFF_CSIZE(queue), + DPI_DMA_IBUFF_CSIZE_CSIZE((u64)(buf_size))); + + /* Reset IDS and IDS2 registers */ + dpi_reg_write(dpi, DPI_DMAX_IDS2(queue), 0ULL); + dpi_reg_write(dpi, DPI_DMAX_IDS(queue), 0ULL); + + return 0; +} + +/** + * Global initialization of DPI + * + * @return Zero on success, negative on failure + */ +int dpi_init(struct dpipf *dpi) +{ + int engine = 0; + u64 reg = 0ULL; + + for (engine = 0; engine < dpi_dma_engine_get_num(); engine++) { + if (engine == 4 || engine == 5) + reg = DPI_ENG_BUF_BLKS(8); + else + reg = DPI_ENG_BUF_BLKS(4); + + dpi_reg_write(dpi, DPI_ENGX_BUF(engine), reg); + + /* Here qmap for the engines are set to 0. + * No dpi queues are mapped to engines. + * When a VF is initialised corresponding bit + * in the qmap will be set for all engines. + */ + dpi_reg_write(dpi, DPI_DMA_ENGX_EN(engine), 0x0ULL); + } + + reg = 0ULL; + reg = (DPI_DMA_CONTROL_ZBWCSEN | DPI_DMA_CONTROL_PKT_EN | + DPI_DMA_CONTROL_LDWB | DPI_DMA_CONTROL_O_MODE | + DPI_DMA_CONTROL_DMA_ENB(0xfULL)); + + dpi_reg_write(dpi, DPI_DMA_CONTROL, reg); + dpi_reg_write(dpi, DPI_CTL, DPI_CTL_EN); + + return 0; +} + +int dpi_fini(struct dpipf *dpi) +{ + int engine = 0; + u64 reg = 0ULL; + + for (engine = 0; engine < dpi_dma_engine_get_num(); engine++) { + + dpi_reg_write(dpi, DPI_ENGX_BUF(engine), reg); + dpi_reg_write(dpi, DPI_DMA_ENGX_EN(engine), 0x0ULL); + } + + reg = 0ULL; + dpi_reg_write(dpi, DPI_DMA_CONTROL, reg); + dpi_reg_write(dpi, DPI_CTL, ~DPI_CTL_EN); + + return 0; +} + +int dpi_queue_reset(struct dpipf *dpi, u16 queue) +{ + /* TODO: add support */ + return 0; +} + +static irqreturn_t dpi_pf_intr_handler (int irq, void *dpi_irq) +{ + u64 reg_val = 0; + int i = 0; + struct dpipf *dpi = (struct dpipf *)dpi_irq; + + dev_err(&dpi->pdev->dev, "intr received: %d\n", irq); + + /* extract MSIX vector number from irq number. */ + while (irq != pci_irq_vector(dpi->pdev, i)) { + i++; + if (i > dpi->num_vec) + break; + } + if (i < DPI_REQQX_INT_IDX) { + reg_val = dpi_reg_read(dpi, DPI_DMA_CCX_INT(i)); + dev_err(&dpi->pdev->dev, "DPI_CC%d_INT raised: 0x%016llx\n", + i, reg_val); + dpi_reg_write(dpi, DPI_DMA_CCX_INT(i), 0x1ULL); + } else if (i < DPI_SDP_FLR_RING_LINTX_IDX) { + reg_val = dpi_reg_read( + dpi, DPI_REQQX_INT(i - DPI_REQQX_INT_IDX)); + dev_err(&dpi->pdev->dev, + "DPI_REQQ_INT raised for q:%d: 0x%016llx\n", + (i - 0x40), reg_val); + + dpi_reg_write( + dpi, DPI_REQQX_INT(i - DPI_REQQX_INT_IDX), reg_val); + + if (reg_val & (0x71ULL)) + dpi_queue_reset(dpi, (i - DPI_REQQX_INT_IDX)); + } else if (i < DPI_SDP_IRE_LINTX_IDX) { + /* TODO: handle interrupt */ + dev_err(&dpi->pdev->dev, "DPI_SDP_FLR_RING_LINTX raised\n"); + + } else if (i < DPI_SDP_ORE_LINTX_IDX) { + /* TODO: handle interrupt */ + dev_err(&dpi->pdev->dev, "DPI_SDP_IRE_LINTX raised\n"); + + } else if (i < DPI_SDP_ORD_LINTX_IDX) { + /* TODO: handle interrupt */ + dev_err(&dpi->pdev->dev, "DPI_SDP_ORE_LINTX raised\n"); + + } else if (i < DPI_EPFX_PP_VF_LINTX_IDX) { + /* TODO: handle interrupt */ + dev_err(&dpi->pdev->dev, "DPI_SDP_ORD_LINTX raised\n"); + + } else if (i < DPI_EPFX_DMA_VF_LINTX_IDX) { + /* TODO: handle interrupt */ + dev_err(&dpi->pdev->dev, "DPI_EPFX_PP_VF_LINTX raised\n"); + + } else if (i < DPI_EPFX_MISC_LINTX_IDX) { + /* TODO: handle interrupt */ + dev_err(&dpi->pdev->dev, "DPI_EPFX_DMA_VF_LINTX raised\n"); + + } else if (i < DPI_PF_RAS_IDX) { + /* TODO: handle interrupt */ + dev_err(&dpi->pdev->dev, "DPI_EPFX_MISC_LINTX raised\n"); + + } else if (i == DPI_PF_RAS_IDX) { + reg_val = dpi_reg_read(dpi, DPI_PF_RAS); + dev_err(&dpi->pdev->dev, "DPI_PF_RAS raised: 0x%016llx\n", + reg_val); + dpi_reg_write(dpi, DPI_PF_RAS, reg_val); + } + return IRQ_HANDLED; +} + +static int dpi_irq_init(struct dpipf *dpi) +{ + int i, irq = 0; + int ret = 0; + + /* Clear All Interrupts */ + dpi_reg_write(dpi, DPI_PF_RAS, DPI_PF_RAS_INT); + + /* Clear All Enables */ + dpi_reg_write(dpi, DPI_PF_RAS_ENA_W1C, DPI_PF_RAS_INT); + + for (i = 0; i < DPI_MAX_REQQ_INT; i++) { + dpi_reg_write(dpi, DPI_REQQX_INT(i), DPI_REQQ_INT); + dpi_reg_write(dpi, DPI_REQQX_INT_ENA_W1C(i), DPI_REQQ_INT); + } + + for (i = 0; i < DPI_MAX_CC_INT; i++) { + dpi_reg_write(dpi, DPI_DMA_CCX_INT(i), DPI_DMA_CC_INT); + dpi_reg_write(dpi, DPI_DMA_CCX_INT_ENA_W1C(i), DPI_DMA_CC_INT); + } + + dpi->num_vec = pci_msix_vec_count(dpi->pdev); + /* Enable MSI-X */ + ret = pci_alloc_irq_vectors(dpi->pdev, dpi->num_vec, + dpi->num_vec, PCI_IRQ_MSIX); + if (ret < 0) { + dev_err(&dpi->pdev->dev, + "DPIPF: Request for %d msix vectors failed, ret %d\n", + dpi->num_vec, ret); + goto alloc_fail; + } + + for (irq = 0; irq < dpi->num_vec; irq++) { + ret = request_irq(pci_irq_vector(dpi->pdev, irq), + dpi_pf_intr_handler, 0, "DPIPF", dpi); + if (ret) { + dev_err(&dpi->pdev->dev, + "DPIPF: IRQ(%d) registration failed for DPIPF\n", + irq); + goto fail; + } + } + +#define ENABLE_DPI_INTERRUPTS 0 +#if ENABLE_DPI_INTERRUPTS + /*Enable All Interrupts */ + for (i = 0; i < DPI_MAX_REQQ_INT; i++) + dpi_reg_write(dpi, DPI_REQQX_INT_ENA_W1S(i), DPI_REQQ_INT); + + dpi_reg_write(dpi, DPI_PF_RAS_ENA_W1S, DPI_PF_RAS_INT); +#endif + return 0; +fail: + if (irq) { + for (i = 0; i <= irq; i++) + free_irq(pci_irq_vector(dpi->pdev, i), dpi); + } + pci_free_irq_vectors(dpi->pdev); +alloc_fail: + dpi->num_vec = 0; + return ret; +} + +static void dpi_irq_free(struct dpipf *dpi) +{ + int i = 0; + + /* Clear All Enables */ + dpi_reg_write(dpi, DPI_PF_RAS_ENA_W1C, DPI_PF_RAS_INT); + + for (i = 0; i < DPI_MAX_REQQ_INT; i++) { + dpi_reg_write(dpi, DPI_REQQX_INT(i), DPI_REQQ_INT); + dpi_reg_write(dpi, DPI_REQQX_INT_ENA_W1C(i), DPI_REQQ_INT); + } + + for (i = 0; i < DPI_MAX_CC_INT; i++) { + dpi_reg_write(dpi, DPI_DMA_CCX_INT(i), DPI_DMA_CC_INT); + dpi_reg_write(dpi, DPI_DMA_CCX_INT_ENA_W1C(i), DPI_DMA_CC_INT); + } + + for (i = 0; i < dpi->num_vec; i++) + free_irq(pci_irq_vector(dpi->pdev, i), dpi); + + pci_free_irq_vectors(dpi->pdev); + dpi->num_vec = 0; +} + +static int dpi_sriov_configure(struct pci_dev *pdev, int numvfs) +{ + struct dpipf *dpi = pci_get_drvdata(pdev); + int ret = 0; + + if (numvfs == 0) { + pci_disable_sriov(pdev); + dpi->total_vfs = 0; + } else { + ret = pci_enable_sriov(pdev, numvfs); + if (ret == 0) { + dpi->total_vfs = numvfs; + ret = numvfs; + } + } + + return ret; +} + +static ssize_t dpi_show_config(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct dpipf *dpi = pci_get_drvdata(pdev); + int vf_idx; + + for (vf_idx = 0; vf_idx < DPI_MAX_VFS; vf_idx++) { + struct dpipf_vf *dpivf = &dpi->vf[vf_idx]; + + if (!dpivf->setup_done) + continue; + sprintf(buf + strlen(buf), + "VF:%d command buffer size:%d aura:%d", + vf_idx, dpivf->vf_config.csize, dpivf->vf_config.aura); + sprintf(buf + strlen(buf), + "sso_pf_func:%x npa_pf_func:%x\n", + dpivf->vf_config.sso_pf_func, + dpivf->vf_config.npa_pf_func); + } + return strlen(buf); +} + +static ssize_t dpi_write_config(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + union dpi_mbox_message_t mbox_msg = {.u[0] = 0ULL, .u[1] = 0ULL}; + struct dpipf *dpi = pci_get_drvdata(pdev); + struct dpipf_vf *dpivf; + + memcpy(&mbox_msg, buf, count); + if (mbox_msg.s.vfid > DPI_MAX_VFS) { + dev_err(dev, "Invalid vfid:%d\n", mbox_msg.s.vfid); + return -1; + } + dpivf = &dpi->vf[mbox_msg.s.vfid]; + + switch (mbox_msg.s.cmd) { + case DPI_QUEUE_OPEN: + dpivf->vf_config.aura = mbox_msg.s.aura; + dpivf->vf_config.csize = mbox_msg.s.csize; + dpivf->vf_config.sso_pf_func = mbox_msg.s.sso_pf_func; + dpivf->vf_config.npa_pf_func = mbox_msg.s.npa_pf_func; + dpi_queue_init(dpi, dpivf, mbox_msg.s.vfid); + dpivf->setup_done = true; + break; + case DPI_QUEUE_CLOSE: + dpivf->vf_config.aura = 0; + dpivf->vf_config.csize = 0; + dpivf->vf_config.sso_pf_func = 0; + dpivf->vf_config.npa_pf_func = 0; + dpi_queue_fini(dpi, dpivf, mbox_msg.s.vfid); + dpivf->setup_done = false; + break; + default: + return -1; + } + + return sizeof(mbox_msg); +} + +static DEVICE_ATTR(dpi_device_config, 0660, + dpi_show_config, dpi_write_config); + +static int dpi_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct dpipf *dpi; + int err; + + dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL); + if (!dpi) + return -ENOMEM; + dpi->pdev = pdev; + + pci_set_drvdata(pdev, dpi); + + err = pci_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device\n"); + pci_set_drvdata(pdev, NULL); + return err; + } + + err = pci_request_regions(pdev, DPI_DRV_NAME); + if (err) { + dev_err(dev, "PCI request regions failed 0x%x\n", err); + goto err_disable_device; + } + + /* MAP configuration registers */ + dpi->reg_base = pcim_iomap(pdev, PCI_DPI_PF_CFG_BAR, 0); + if (!dpi->reg_base) { + dev_err(dev, "DPI: Cannot map CSR memory space, aborting\n"); + err = -ENOMEM; + goto err_release_regions; + } + + /* Initialize global PF registers */ + err = dpi_init(dpi); + if (err) { + dev_err(dev, "DPI: Failed to initialize dpi\n"); + goto err_release_regions; + } + + /* Register interrupts */ + err = dpi_irq_init(dpi); + if (err) { + dev_err(dev, "DPI: Failed to initialize irq vectors\n"); + goto err_dpi_fini; + } + + err = device_create_file(dev, &dev_attr_dpi_device_config); + if (err) { + dev_err(dev, "DPI: Failed to create sysfs entry for driver\n"); + goto err_free_irq; + } + + return 0; + +err_free_irq: + dpi_irq_free(dpi); +err_dpi_fini: + dpi_fini(dpi); +err_release_regions: + pci_release_regions(pdev); +err_disable_device: + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + devm_kfree(dev, dpi); + return err; +} + +static void dpi_remove(struct pci_dev *pdev) +{ + struct device *dev = &pdev->dev; + struct dpipf *dpi = pci_get_drvdata(pdev); + + device_remove_file(dev, &dev_attr_dpi_device_config); + dpi_irq_free(dpi); + dpi_fini(dpi); + dpi_sriov_configure(pdev, 0); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + devm_kfree(dev, dpi); +} + +static struct pci_driver dpi_driver = { + .name = DPI_DRV_NAME, + .id_table = dpi_id_table, + .probe = dpi_probe, + .remove = dpi_remove, + .sriov_configure = dpi_sriov_configure, +}; + +static int __init dpi_init_module(void) +{ + pr_info("%s: %s\n", DPI_DRV_NAME, DPI_DRV_STRING); + + return pci_register_driver(&dpi_driver); +} + +static void __exit dpi_cleanup_module(void) +{ + pci_unregister_driver(&dpi_driver); +} + +module_init(dpi_init_module); +module_exit(dpi_cleanup_module); diff --git a/drivers/soc/marvell/octeontx2-dpi/dpi.h b/drivers/soc/marvell/octeontx2-dpi/dpi.h new file mode 100644 index 000000000000..2d0cf04524b7 --- /dev/null +++ b/drivers/soc/marvell/octeontx2-dpi/dpi.h @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 DPI PF driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ + +#ifndef __DPI_H__ +#define __DPI_H__ + + /* PCI device IDs */ +#define PCI_DEVID_OCTEONTX2_DPI_PF 0xA080 +#define PCI_DEVID_OCTEONTX2_DPI_VF 0xA081 + +/* PCI BAR nos */ +#define PCI_DPI_PF_CFG_BAR 0 +#define PCI_DPI_PF_MSIX_BAR 4 +#define PCI_DPI_VF_CFG_BAR 0 +#define PCI_DPI_VF_MSIX_BAR 4 +#define DPI_VF_CFG_SIZE 0x100000 +#define DPI_VF_OFFSET(x) (0x20000000 | 0x100000 * (x)) + +/* MSI-X interrupts */ +#define DPI_VF_MSIX_COUNT 1 +#define DPI_MAX_REQQ_INT 8 +#define DPI_MAX_CC_INT 64 + +/* MSI-X interrupt vectors indexes */ +#define DPI_CCX_INT_IDX 0x0 +#define DPI_REQQX_INT_IDX 0x40 +#define DPI_SDP_FLR_RING_LINTX_IDX 0x48 +#define DPI_SDP_IRE_LINTX_IDX 0x4C +#define DPI_SDP_ORE_LINTX_IDX 0x50 +#define DPI_SDP_ORD_LINTX_IDX 0x54 +#define DPI_EPFX_PP_VF_LINTX_IDX 0x58 +#define DPI_EPFX_DMA_VF_LINTX_IDX 0x78 +#define DPI_EPFX_MISC_LINTX_IDX 0x98 +#define DPI_PF_RAS_IDX 0xA8 + +#define DPI_MAX_ENGINES 6 +#define DPI_MAX_VFS 8 + +/**************** Macros for register modification ************/ +#define DPI_DMA_IBUFF_CSIZE_CSIZE(x) ((x) & 0x1fff) +#define DPI_DMA_IBUFF_CSIZE_GET_CSIZE(x) ((x) & 0x1fff) + +#define DPI_DMA_IDS_INST_STRM(x) ((uint64_t)((x) & 0xff) << 40) +#define DPI_DMA_IDS_GET_INST_STRM(x) (((x) >> 40) & 0xff) + +#define DPI_DMA_IDS_DMA_STRM(x) ((uint64_t)((x) & 0xff) << 32) +#define DPI_DMA_IDS_GET_DMA_STRM(x) (((x) >> 32) & 0xff) + +#define DPI_DMA_IDS_DMA_NPA_PF_FUNC(x) ((uint64_t)((x) & 0xffff) << 16) +#define DPI_DMA_IDS_GET_DMA_NPA_PF_FUNC(x) (((x) >> 16) & 0xffff) + +#define DPI_DMA_IDS_DMA_SSO_PF_FUNC(x) ((uint64_t)((x) & 0xffff)) +#define DPI_DMA_IDS_GET_DMA_SSO_PF_FUNC(x) ((x) & 0xffff) + +#define DPI_DMA_IDS2_INST_AURA(x) ((uint64_t)((x) & 0xfffff)) +#define DPI_DMA_IDS2_GET_INST_AURA(x) ((x) & 0xfffff) + +#define DPI_ENG_BUF_BLKS(x) ((x) & 0x1fULL) +#define DPI_ENG_BUF_GET_BLKS(x) ((x) & 0x1fULL) + +#define DPI_ENG_BUF_BASE(x) (((x) & 0x3fULL) << 16) +#define DPI_ENG_BUF_GET_BASE(x) (((x) >> 16) & 0x3fULL) + +#define DPI_DMA_ENG_EN_QEN(x) ((x) & 0xffULL) +#define DPI_DMA_ENG_EN_GET_QEN(x) ((x) & 0xffULL) + +#define DPI_DMA_ENG_EN_MOLR(x) (((x) & 0x3ffULL) << 32) +#define DPI_DMA_ENG_EN_GET_MOLR(x) (((x) >> 32) & 0x3ffULL) + +#define DPI_DMA_CONTROL_DMA_ENB(x) (((x) & 0x3fULL) << 48) +#define DPI_DMA_CONTROL_GET_DMA_ENB(x) (((x) >> 48) & 0x3fULL) + +#define DPI_DMA_CONTROL_O_ES(x) (((x) & 0x3ULL) << 15) +#define DPI_DMA_CONTROL_GET_O_ES(x) (((x) >> 15) & 0x3ULL) + +#define DPI_DMA_CONTROL_O_MODE (0x1ULL << 14) +#define DPI_DMA_CONTROL_O_NS (0x1ULL << 17) +#define DPI_DMA_CONTROL_O_RO (0x1ULL << 18) +#define DPI_DMA_CONTROL_O_ADD1 (0x1ULL << 19) +#define DPI_DMA_CONTROL_LDWB (0x1ULL << 32) +#define DPI_DMA_CONTROL_NCB_TAG_DIS (0x1ULL << 34) +#define DPI_DMA_CONTROL_ZBWCSEN (0x1ULL << 39) +#define DPI_DMA_CONTROL_WQECSDIS (0x1ULL << 47) +#define DPI_DMA_CONTROL_UIO_DIS (0x1ULL << 55) +#define DPI_DMA_CONTROL_PKT_EN (0x1ULL << 56) +#define DPI_DMA_CONTROL_FFP_DIS (0x1ULL << 59) + +#define DPI_CTL_EN (0x1ULL) + +/******************** macros for Interrupts ************************/ +#define DPI_DMA_CC_INT (0x1ULL) + +#define DPI_REQQ_INT_INSTRFLT (0x1ULL) +#define DPI_REQQ_INT_RDFLT (0x1ULL << 1) +#define DPI_REQQ_INT_WRFLT (0x1ULL << 2) +#define DPI_REQQ_INT_CSFLT (0x1ULL << 3) +#define DPI_REQQ_INT_INST_DBO (0x1ULL << 4) +#define DPI_REQQ_INT_INST_ADDR_NULL (0x1ULL << 5) +#define DPI_REQQ_INT_INST_FILL_INVAL (0x1ULL << 6) +#define DPI_REQQ_INT_INSTR_PSN (0x1ULL << 7) + +#define DPI_REQQ_INT \ + (DPI_REQQ_INT_INSTRFLT | \ + DPI_REQQ_INT_RDFLT | \ + DPI_REQQ_INT_WRFLT | \ + DPI_REQQ_INT_CSFLT | \ + DPI_REQQ_INT_INST_DBO | \ + DPI_REQQ_INT_INST_ADDR_NULL | \ + DPI_REQQ_INT_INST_FILL_INVAL | \ + DPI_REQQ_INT_INSTR_PSN) + +#define DPI_PF_RAS_EBI_DAT_PSN (0x1ULL) +#define DPI_PF_RAS_NCB_DAT_PSN (0x1ULL << 1) +#define DPI_PF_RAS_NCB_CMD_PSN (0x1ULL << 2) +#define DPI_PF_RAS_INT \ + (DPI_PF_RAS_EBI_DAT_PSN | \ + DPI_PF_RAS_NCB_DAT_PSN | \ + DPI_PF_RAS_NCB_CMD_PSN) + + +/***************** Registers ******************/ +#define DPI_DMAX_IBUFF_CSIZE(x) (0x0ULL | ((x) << 11)) +#define DPI_DMAX_REQBANK0(x) (0x8ULL | ((x) << 11)) +#define DPI_DMAX_REQBANK1(x) (0x10ULL | ((x) << 11)) +#define DPI_DMAX_IDS(x) (0x18ULL | ((x) << 11)) +#define DPI_DMAX_IDS2(x) (0x20ULL | ((x) << 11)) +#define DPI_DMAX_IFLIGHT(x) (0x28ULL | ((x) << 11)) +#define DPI_DMAX_QRST(x) (0x30ULL | ((x) << 11)) +#define DPI_DMAX_ERR_RSP_STATUS(x) (0x38ULL | ((x) << 11)) + +#define DPI_CSCLK_ACTIVE_PC (0x4000ULL) +#define DPI_CTL (0x4010ULL) +#define DPI_DMA_CONTROL (0x4018ULL) +#define DPI_DMA_ENGX_EN(x) (0x4040ULL | ((x) << 3)) +#define DPI_REQ_ERR_RSP (0x4078ULL) +#define DPI_REQ_ERR_RSP_EN (0x4088ULL) +#define DPI_PKT_ERR_RSP (0x4098ULL) +#define DPI_NCB_CFG (0x40A0ULL) +#define DPI_BP_TEST0 (0x40B0ULL) +#define DPI_ENGX_BUF(x) (0x40C0ULL | ((x) << 3)) +#define DPI_EBUS_RECAL (0x40F0ULL) +#define DPI_EBUS_PORTX_CFG(x) (0x4100ULL | ((x) << 3)) +#define DPI_EBUS_PORTX_SCFG(x) (0x4180ULL | ((x) << 3)) +#define DPI_EBUS_PORTX_ERR_INFO(x) (0x4200ULL | ((x) << 3)) +#define DPI_EBUS_PORTX_ERR(x) (0x4280ULL | ((x) << 3)) +#define DPI_INFO_REG (0x4300ULL) +#define DPI_PF_RAS (0x4308ULL) +#define DPI_PF_RAS_W1S (0x4310ULL) +#define DPI_PF_RAS_ENA_W1C (0x4318ULL) +#define DPI_PF_RAS_ENA_W1S (0x4320ULL) +#define DPI_DMA_CCX_INT(x) (0x5000ULL | ((x) << 3)) +#define DPI_DMA_CCX_INT_W1S(x) (0x5400ULL | ((x) << 3)) +#define DPI_DMA_CCX_INT_ENA_W1C(x) (0x5800ULL | ((x) << 3)) +#define DPI_DMA_CCX_INT_ENA_W1S(x) (0x5C00ULL | ((x) << 3)) +#define DPI_DMA_CCX_CNT(x) (0x6000ULL | ((x) << 3)) +#define DPI_REQQX_INT(x) (0x6600ULL | ((x) << 3)) +#define DPI_REQQX_INT_W1S(x) (0x6640ULL | ((x) << 3)) +#define DPI_REQQX_INT_ENA_W1C(x) (0x6680ULL | ((x) << 3)) +#define DPI_REQQX_INT_ENA_W1S(x) (0x66C0ULL | ((x) << 3)) +#define DPI_EPFX_DMA_VF_LINTX(x, y) (0x6800ULL | ((x) << 5) |\ + ((y) << 4)) +#define DPI_EPFX_DMA_VF_LINTX_W1S(x, y) (0x6A00ULL | ((x) << 5) |\ + ((y) << 4)) +#define DPI_EPFX_DMA_VF_LINTX_ENA_W1C(x, y) (0x6C00ULL | ((x) << 5) |\ + ((y) << 4)) +#define DPI_EPFX_DMA_VF_LINTX_ENA_W1S(x, y) (0x6E00ULL | ((x) << 5) |\ + ((y) << 4)) +#define DPI_EPFX_MISC_LINT(x) (0x7000ULL | ((x) << 5)) +#define DPI_EPFX_MISC_LINT_W1S(x) (0x7008ULL | ((x) << 5)) +#define DPI_EPFX_MISC_LINT_ENA_W1C(x) (0x7010ULL | ((x) << 5)) +#define DPI_EPFX_MISC_LINT_ENA_W1S(x) (0x7018ULL | ((x) << 5)) +#define DPI_EPFX_PP_VF_LINTX(x, y) (0x7200ULL | ((x) << 5) |\ + ((y) << 4)) +#define DPI_EPFX_PP_VF_LINTX_W1S(x, y) (0x7400ULL | ((x) << 5) |\ + ((y) << 4)) +#define DPI_EPFX_PP_VF_LINTX_ENA_W1C(x, y) (0x7600ULL | ((x) << 5) |\ + ((y) << 4)) +#define DPI_EPFX_PP_VF_LINTX_ENA_W1S(x, y) (0x7800ULL | ((x) << 5) |\ + ((y) << 4)) +/* VF Registers: */ +#define DPI_VDMA_EN (0x0ULL) +#define DPI_VDMA_REQQ_CTL (0x8ULL) +#define DPI_VDMA_DBELL (0x10ULL) +#define DPI_VDMA_SADDR (0x18ULL) +#define DPI_VDMA_COUNTS (0x20ULL) +#define DPI_VDMA_NADDR (0x28ULL) +#define DPI_VDMA_IWBUSY (0x30ULL) +#define DPI_VDMA_CNT (0x38ULL) +#define DPI_VF_INT (0x100ULL) +#define DPI_VF_INT_W1S (0x108ULL) +#define DPI_VF_INT_ENA_W1C (0x110ULL) +#define DPI_VF_INT_ENA_W1S (0x118ULL) + +struct dpivf_config { + uint16_t csize; + uint32_t aura; + uint16_t sso_pf_func; + uint16_t npa_pf_func; +}; + +struct dpipf_vf { + uint8_t this_vfid; + bool setup_done; + struct dpivf_config vf_config; +}; + +struct dpipf { + void __iomem *reg_base; + struct pci_dev *pdev; + int num_vec; + struct msix_entry *msix_entries; + int total_vfs; + int vfs_in_use; + struct dpipf_vf vf[DPI_MAX_VFS]; +}; + +#define DPI_QUEUE_OPEN 0x1 +#define DPI_QUEUE_CLOSE 0x2 +#define DPI_REG_DUMP 0x3 +#define DPI_GET_REG_CFG 0x4 + +union dpi_mbox_message_t { + uint64_t u[2]; + struct dpi_mbox_message_s { + /* VF ID to configure */ + uint64_t vfid :4; + /* Command code */ + uint64_t cmd :4; + /* Command buffer size in 8-byte words */ + uint64_t csize :14; + /* aura of the command buffer */ + uint64_t aura :20; + /* SSO PF function */ + uint64_t sso_pf_func :16; + /* NPA PF function */ + uint64_t npa_pf_func :16; + } s; +}; +#endif diff --git a/drivers/soc/marvell/octeontx2-rm/Makefile b/drivers/soc/marvell/octeontx2-rm/Makefile new file mode 100644 index 000000000000..bab787b56b43 --- /dev/null +++ b/drivers/soc/marvell/octeontx2-rm/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Marvell's OcteonTX2 SSO/TIM RVU device driver +# + +obj-$(CONFIG_OCTEONTX2_RM) += octeontx2_rm.o + +octeontx2_rm-y := otx2_rm.o quota.o +octeontx2_rm-$(CONFIG_OCTEONTX2_RM_DOM_SYSFS) += domain_sysfs.o +ccflags-y += -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af +ccflags-y += -I$(srctree)/drivers/soc/marvell/octeontx2-dpi/ diff --git a/drivers/soc/marvell/octeontx2-rm/domain_sysfs.c b/drivers/soc/marvell/octeontx2-rm/domain_sysfs.c new file mode 100644 index 000000000000..9101edea8118 --- /dev/null +++ b/drivers/soc/marvell/octeontx2-rm/domain_sysfs.c @@ -0,0 +1,832 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OcteonTX2 RVU Resource Manager driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/sysfs.h> +#include "domain_sysfs.h" +#include "otx2_rm.h" +#include "dpi.h" + +#define DOMAIN_NAME_LEN 32 +#define PCI_SCAN_FMT "%04x:%02x:%02x.%02x" + +/* The format of DP is: DP(_name, _param_type, _scanf_fmt) */ +#define DOM_PARAM_SPEC \ +DP(ssow, int, "%d") \ +DP(sso, int, "%d") \ +DP(npa, int, "%d") \ +DP(cpt, int, "%d") \ +DP(tim, int, "%d") \ +DP(dpi, int, "%d") + +struct domain_params { + const char *name; +#define DP(_name, _type, _1) \ + _type _name; +DOM_PARAM_SPEC +#undef DP + const char *ports[RM_MAX_PORTS]; + u16 port_cnt; +}; + +struct domain { + char name[DOMAIN_NAME_LEN]; + struct kobj_attribute domain_id; + struct kobj_attribute domain_in_use; + /* List of all ports attached to the domain */ + struct rvu_port *ports; + struct kobject *kobj; + struct rvu_vf *rvf; + int port_count; + bool in_use; +}; + +struct rvu_port { + /* handle in global list of ports associated to all domains */ + struct list_head list; + struct pci_dev *pdev; + struct domain *domain; +}; + +struct dpi_vf { + struct pci_dev *pdev; + /* pointer to the kobject which owns this vf */ + struct kobject *domain_kobj; + int vf_id; + bool in_use; +}; + +struct dpi_info { + /* Total number of vfs available */ + uint8_t num_vfs; + /* Free vfs */ + uint8_t vfs_free; + /* Pointer to the vfs available */ + struct dpi_vf *dpi_vf; +}; + +struct domain_sysfs { + struct list_head list; + struct kobj_attribute create_domain; + struct kobj_attribute destroy_domain; + struct kobj_attribute pmccntr_el0; + /* List of all ports added to all domains. Used for validating if new + * domain creation doesn't want to take an already taken port. + */ + struct list_head ports; + struct rm_dev *rdev; + struct kobject *parent; + struct domain *domains; + size_t domains_len; + struct dpi_info dpi_info; +}; + +static DEFINE_MUTEX(domain_sysfs_lock); +static LIST_HEAD(domain_sysfs_list); + +static ssize_t +domain_id_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct domain *dom = container_of(attr, struct domain, domain_id); + + return snprintf(buf, PAGE_SIZE, "%s\n", dom->name); +} + +static ssize_t +domain_in_use_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct domain *dom = container_of(attr, struct domain, domain_in_use); + + return snprintf(buf, PAGE_SIZE, "%d\n", dom->rvf->in_use); +} + +static int do_destroy_domain(struct domain_sysfs *lsfs, struct domain *domain) +{ + struct device *dev = &lsfs->rdev->pdev->dev; + int i; + + if (domain->rvf->in_use) { + dev_err(dev, "Domain %s is in use.\n", domain->name); + return -EBUSY; + } + + sysfs_remove_file(domain->kobj, &domain->domain_id.attr); + domain->domain_id.attr.mode = 0; + sysfs_remove_file(domain->kobj, &domain->domain_in_use.attr); + domain->domain_in_use.attr.mode = 0; + for (i = 0; i < domain->port_count; i++) { + sysfs_remove_link(domain->kobj, + pci_name(domain->ports[i].pdev)); + } + + for (i = 0; i < lsfs->dpi_info.num_vfs; i++) { + struct dpi_vf *dpivf_ptr = NULL; + + dpivf_ptr = &lsfs->dpi_info.dpi_vf[i]; + /* Identify the devices belongs to this domain */ + if (dpivf_ptr->in_use && + dpivf_ptr->domain_kobj == domain->kobj) { + sysfs_remove_link(domain->kobj, + pci_name(dpivf_ptr->pdev)); + dpivf_ptr->in_use = false; + dpivf_ptr->domain_kobj = NULL; + lsfs->dpi_info.vfs_free++; + } + } + + sysfs_remove_link(domain->kobj, pci_name(domain->rvf->pdev)); + kobject_del(domain->kobj); + mutex_lock(&lsfs->rdev->lock); + // restore limits + lsfs->rdev->vf_limits.sso->a[domain->rvf->vf_id].val = 0; + lsfs->rdev->vf_limits.ssow->a[domain->rvf->vf_id].val = 0; + lsfs->rdev->vf_limits.npa->a[domain->rvf->vf_id].val = 0; + lsfs->rdev->vf_limits.cpt->a[domain->rvf->vf_id].val = 0; + lsfs->rdev->vf_limits.tim->a[domain->rvf->vf_id].val = 0; + mutex_unlock(&lsfs->rdev->lock); + + mutex_lock(&domain_sysfs_lock); + // FREE ALL allocated ports + for (i = 0; i < domain->port_count; i++) { + list_del(&domain->ports[i].list); + pci_dev_put(domain->ports[i].pdev); + } + kfree(domain->ports); + domain->ports = NULL; + domain->port_count = 0; + domain->in_use = false; + domain->name[0] = '\0'; + mutex_unlock(&domain_sysfs_lock); + + return 0; +} + +static int +do_create_domain(struct domain_sysfs *lsfs, struct domain_params *dparams) +{ + struct device *dev = &lsfs->rdev->pdev->dev; + struct domain *domain = NULL; + struct rvu_port *ports = NULL, *cur; + u32 dom, bus, slot, fn; + int old_sso, old_ssow, old_npa, old_cpt, old_tim, device; + int res = 0, i; + + /* Validate parameters */ + if (dparams == NULL) + return -EINVAL; + if (strnlen(dparams->name, DOMAIN_NAME_LEN) >= DOMAIN_NAME_LEN) { + dev_err(dev, "Domain name too long, max %d characters.\n", + DOMAIN_NAME_LEN); + return -EINVAL; + } + if (dparams->npa != 1) { + dev_err(dev, "Exactly 1 NPA resource required.\n"); + return -EINVAL; + } + if (dparams->ssow < 1) { + dev_err(dev, "At least 1 SSOW resource required.\n"); + return -EINVAL; + } + mutex_lock(&domain_sysfs_lock); + /* Find a free domain device */ + for (i = 0; i < lsfs->domains_len; i++) { + if (!strncmp(lsfs->domains[i].name, dparams->name, + DOMAIN_NAME_LEN)) { + dev_err(dev, "Domain %s exists already.\n", + dparams->name); + res = -EINVAL; + goto err_dom; + } + if (lsfs->domains[i].in_use == false && + lsfs->domains[i].rvf->in_use == false) { + if (domain == NULL) + domain = &lsfs->domains[i]; + } + } + if (domain == NULL) { + dev_err(dev, "No free device to create new domain.\n"); + res = -ENODEV; + goto err_dom; + } + strncpy(domain->name, dparams->name, DOMAIN_NAME_LEN - 1); + domain->in_use = true; + /* Verify ports are valid and supported. */ + if (dparams->port_cnt == 0) + goto skip_ports; + ports = kcalloc(dparams->port_cnt, sizeof(struct rvu_port), GFP_KERNEL); + if (ports == NULL) { + dev_err(dev, "Not enough memory.\n"); + res = -ENOMEM; + goto err_ports; + } + for (i = 0; i < dparams->port_cnt; i++) { + if (sscanf(dparams->ports[i], PCI_SCAN_FMT, &dom, &bus, &slot, + &fn) != 4) { + dev_err(dev, "Invalid port: %s.\n", dparams->ports[i]); + res = -EINVAL; + goto err_ports; + } + ports[i].pdev = + pci_get_domain_bus_and_slot(dom, bus, + PCI_DEVFN(slot, fn)); + if (ports[i].pdev == NULL) { + dev_err(dev, "Unknown port: %s.\n", dparams->ports[i]); + res = -ENODEV; + goto err_ports; + } + device = ports[i].pdev->device; + if (ports[i].pdev->vendor != PCI_VENDOR_ID_CAVIUM || + (device != PCI_DEVID_OCTEONTX2_RVU_PF && + device != PCI_DEVID_OCTEONTX2_PASS1_RVU_PF && + device != PCI_DEVID_OCTEONTX2_RVU_AFVF && + device != PCI_DEVID_OCTEONTX2_PASS1_RVU_AFVF && + device != PCI_DEVID_OCTEONTX2_RVU_VF && + device != PCI_DEVID_OCTEONTX2_PASS1_RVU_VF)) { + dev_err(dev, "Unsupported port: %s.\n", + dparams->ports[i]); + res = -EINVAL; + goto err_ports; + } + list_for_each_entry(cur, &lsfs->ports, list) { + if (cur->pdev != ports[i].pdev) + continue; + dev_err(dev, + "Port %s already assigned to domain %s.\n", + dparams->ports[i], cur->domain->name); + res = -EBUSY; + goto err_ports; + } + } + for (i = 0; i < dparams->port_cnt; i++) { + ports[i].domain = domain; + list_add(&ports[i].list, &lsfs->ports); + } + domain->ports = ports; + domain->port_count = dparams->port_cnt; +skip_ports: + mutex_unlock(&domain_sysfs_lock); + /* Check domain spec against limits for the parent RVU. */ + mutex_lock(&lsfs->rdev->lock); + old_sso = lsfs->rdev->vf_limits.sso->a[domain->rvf->vf_id].val; + old_ssow = lsfs->rdev->vf_limits.ssow->a[domain->rvf->vf_id].val; + old_npa = lsfs->rdev->vf_limits.npa->a[domain->rvf->vf_id].val; + old_cpt = lsfs->rdev->vf_limits.cpt->a[domain->rvf->vf_id].val; + old_tim = lsfs->rdev->vf_limits.tim->a[domain->rvf->vf_id].val; +#define CHECK_LIMITS(_ls, _val, _n, _idx) do { \ + if (quotas_get_sum(_ls) + _val - _ls->a[_idx].val > _ls->max_sum) { \ + dev_err(dev, \ + "Not enough "_n" LFs, currently used: %lld/%lld\n", \ + quotas_get_sum(_ls), _ls->max_sum); \ + res = -ENODEV; \ + goto err_limits; \ + } \ +} while (0) + CHECK_LIMITS(lsfs->rdev->vf_limits.sso, dparams->sso, "SSO", + domain->rvf->vf_id); + CHECK_LIMITS(lsfs->rdev->vf_limits.ssow, dparams->ssow, "SSOW", + domain->rvf->vf_id); + CHECK_LIMITS(lsfs->rdev->vf_limits.npa, dparams->npa, "NPA", + domain->rvf->vf_id); + CHECK_LIMITS(lsfs->rdev->vf_limits.cpt, dparams->cpt, "CPT", + domain->rvf->vf_id); + CHECK_LIMITS(lsfs->rdev->vf_limits.tim, dparams->tim, "TIM", + domain->rvf->vf_id); + if (dparams->dpi > lsfs->dpi_info.vfs_free) { + dev_err(dev, + "Not enough DPI VFS, currently used:%d/%d\n", + lsfs->dpi_info.num_vfs - + lsfs->dpi_info.vfs_free, + lsfs->dpi_info.num_vfs); + res = -ENODEV; + goto err_limits; + } + + /* Now that checks are done, update the limits */ + lsfs->rdev->vf_limits.sso->a[domain->rvf->vf_id].val = dparams->sso; + lsfs->rdev->vf_limits.ssow->a[domain->rvf->vf_id].val = dparams->ssow; + lsfs->rdev->vf_limits.npa->a[domain->rvf->vf_id].val = dparams->npa; + lsfs->rdev->vf_limits.cpt->a[domain->rvf->vf_id].val = dparams->cpt; + lsfs->rdev->vf_limits.tim->a[domain->rvf->vf_id].val = dparams->tim; + lsfs->dpi_info.vfs_free -= dparams->dpi; + mutex_unlock(&lsfs->rdev->lock); + + /* Set it up according to user spec */ + domain->kobj = kobject_create_and_add(dparams->name, lsfs->parent); + if (domain->kobj == NULL) { + dev_err(dev, "Failed to create domain directory.\n"); + res = -ENOMEM; + goto err_kobject_create; + } + res = sysfs_create_link(domain->kobj, &domain->rvf->pdev->dev.kobj, + pci_name(domain->rvf->pdev)); + if (res < 0) { + dev_err(dev, "Failed to create dev links for domain %s.\n", + domain->name); + res = -ENOMEM; + goto err_dom_dev_symlink; + } + for (i = 0; i < dparams->port_cnt; i++) { + res = sysfs_create_link(domain->kobj, &ports[i].pdev->dev.kobj, + pci_name(ports[i].pdev)); + if (res < 0) { + dev_err(dev, + "Failed to create dev links for domain %s.\n", + domain->name); + res = -ENOMEM; + goto err_dom_port_symlink; + } + } + /* Create symlinks for dpi vfs in domain */ + for (i = 0; i < dparams->dpi; i++) { + struct dpi_vf *dpivf_ptr = NULL; + int vf_idx; + + for (vf_idx = 0; vf_idx < lsfs->dpi_info.num_vfs; + vf_idx++) { + /* Find available dpi vfs and create symlinks */ + dpivf_ptr = &lsfs->dpi_info.dpi_vf[vf_idx]; + if (dpivf_ptr->in_use) + continue; + else + break; + } + res = sysfs_create_link(domain->kobj, + &dpivf_ptr->pdev->dev.kobj, + pci_name(dpivf_ptr->pdev)); + if (res < 0) { + dev_err(dev, + "Failed to create DPI dev links for domain %s\n", + domain->name); + res = -ENOMEM; + goto err_dpi_symlink; + } + dpivf_ptr->domain_kobj = domain->kobj; + dpivf_ptr->in_use = true; + } + + domain->domain_in_use.attr.mode = 0444; + domain->domain_in_use.attr.name = "domain_in_use"; + domain->domain_in_use.show = domain_in_use_show; + res = sysfs_create_file(domain->kobj, &domain->domain_in_use.attr); + if (res < 0) { + dev_err(dev, + "Failed to create domain_in_use file for domain %s.\n", + domain->name); + res = -ENOMEM; + goto err_dom_in_use; + } + + domain->domain_id.attr.mode = 0444; + domain->domain_id.attr.name = "domain_id"; + domain->domain_id.show = domain_id_show; + res = sysfs_create_file(domain->kobj, &domain->domain_id.attr); + if (res < 0) { + dev_err(dev, "Failed to create domain_id file for domain %s.\n", + domain->name); + res = -ENOMEM; + goto err_dom_id; + } + + return res; + +err_dom_id: + domain->domain_id.attr.mode = 0; + sysfs_remove_file(domain->kobj, &domain->domain_in_use.attr); +err_dom_in_use: + domain->domain_in_use.attr.mode = 0; +err_dpi_symlink: + for (i = 0; i < lsfs->dpi_info.num_vfs; i++) { + struct dpi_vf *dpivf_ptr = NULL; + + dpivf_ptr = &lsfs->dpi_info.dpi_vf[i]; + /* Identify the devices belongs to this domain */ + if (dpivf_ptr->in_use && + dpivf_ptr->domain_kobj == domain->kobj) { + sysfs_remove_link(domain->kobj, + pci_name(dpivf_ptr->pdev)); + dpivf_ptr->in_use = false; + dpivf_ptr->domain_kobj = NULL; + } + } +err_dom_port_symlink: + for (i = 0; i < dparams->port_cnt; i++) + sysfs_remove_link(domain->kobj, pci_name(ports[i].pdev)); + sysfs_remove_link(domain->kobj, pci_name(domain->rvf->pdev)); +err_dom_dev_symlink: + kobject_del(domain->kobj); +err_kobject_create: + mutex_lock(&lsfs->rdev->lock); +err_limits: + // restore limits + lsfs->rdev->vf_limits.sso->a[domain->rvf->vf_id].val = old_sso; + lsfs->rdev->vf_limits.ssow->a[domain->rvf->vf_id].val = old_ssow; + lsfs->rdev->vf_limits.npa->a[domain->rvf->vf_id].val = old_npa; + lsfs->rdev->vf_limits.cpt->a[domain->rvf->vf_id].val = old_cpt; + lsfs->rdev->vf_limits.tim->a[domain->rvf->vf_id].val = old_tim; + lsfs->dpi_info.vfs_free += dparams->dpi; + mutex_unlock(&lsfs->rdev->lock); + mutex_lock(&domain_sysfs_lock); +err_ports: + // FREE ALL allocated ports + for (i = 0; i < dparams->port_cnt; i++) { + if (ports[i].pdev == NULL) + break; + if (ports[i].domain != NULL) + list_del(&ports[i].list); + pci_dev_put(ports[i].pdev); + } + kfree(ports); + domain->ports = NULL; + domain->port_count = 0; + domain->in_use = false; + domain->name[0] = '\0'; +err_dom: + mutex_unlock(&domain_sysfs_lock); + return res; +} + +static ssize_t +destroy_domain_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct domain_sysfs *lsfs = + container_of(attr, struct domain_sysfs, destroy_domain); + struct device *dev = &lsfs->rdev->pdev->dev; + struct domain *domain = NULL; + char name[DOMAIN_NAME_LEN], *name_ptr; + int i, res; + + strncpy(name, buf, DOMAIN_NAME_LEN - 1); + name_ptr = strim(name); + if (strlen(name_ptr) == 0) { + dev_err(dev, "Empty domain name.\n"); + return -EINVAL; + } + + mutex_lock(&domain_sysfs_lock); + /* Find a free domain device */ + for (i = 0; i < lsfs->domains_len; i++) { + if (!strncmp(lsfs->domains[i].name, name_ptr, + DOMAIN_NAME_LEN)) { + domain = &lsfs->domains[i]; + break; + } + } + if (domain == NULL) { + dev_err(dev, "Domain '%s' doesn't exist.\n", name); + res = -EINVAL; + goto err_dom; + } + mutex_unlock(&domain_sysfs_lock); + + res = do_destroy_domain(lsfs, domain); + if (res == 0) + res = count; +err_dom: + mutex_unlock(&domain_sysfs_lock); + return res; +} + +static ssize_t +create_domain_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct domain_params *dparams = NULL; + struct domain_sysfs *lsfs = + container_of(attr, struct domain_sysfs, create_domain); + struct device *dev = &lsfs->rdev->pdev->dev; + int res = 0; + char *start; + char *end; + char *ptr = NULL; + const char *name; + char *errmsg = "Invalid domain specification format."; + + if (strlen(buf) == 0) { + dev_err(dev, "Empty domain spec.\n"); + return -EINVAL; + } + + dparams = kzalloc(sizeof(*dparams), GFP_KERNEL); + if (dparams == NULL) { + errmsg = "Not enough memory"; + res = -ENOMEM; + goto error; + } + + end = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (end == NULL) { + errmsg = "Not enough memory"; + res = -ENOMEM; + goto error; + } + + ptr = end; + memcpy(end, buf, count); + + name = strsep(&end, ";"); + if (end == NULL) { + res = -EINVAL; + goto error; + } + + dparams->name = name; + + for (;;) { + start = strsep(&end, ";"); + if (start == NULL) + break; + start = strim(start); + if (!*start) + continue; + + if (!strncmp(strim(start), "port", sizeof("port") - 1)) { + strsep(&start, ":"); + if (dparams->port_cnt >= RM_MAX_PORTS) + goto error; + dparams->ports[dparams->port_cnt++] = strim(start); + } + #define DP(_name, _1, _fmt) \ + else if (!strncmp(strim(start), #_name, \ + sizeof(#_name) - 1)) { \ + strsep(&start, ":"); \ + start = strim(start); \ + res = sscanf(start, _fmt, &dparams->_name); \ + if (res != 1) \ + goto error; \ + continue; \ + } + DOM_PARAM_SPEC + #undef DP + else { + res = -EINVAL; + goto error; + } + } + res = do_create_domain(lsfs, dparams); + if (res < 0) { + errmsg = "Failed to create application domain."; + goto error; + } else + res = count; +error: + if (res < 0) + dev_err(dev, "%s\n", errmsg); + kfree(ptr); + kfree(dparams); + return res; +} + +static int dpivf_sysfs_create(struct domain_sysfs *lsfs) +{ + struct dpi_info *dpi_info = &lsfs->dpi_info; + struct dpi_vf *dpivf_ptr = NULL; + struct pci_dev *pdev = lsfs->rdev->pdev; + struct pci_dev *vdev = NULL; + uint8_t vf_idx = 0; + + dpi_info->dpi_vf = kcalloc(DPI_MAX_VFS, + sizeof(struct dpi_vf), GFP_KERNEL); + if (dpi_info->dpi_vf == NULL) + return -ENOMEM; + + /* Get available DPI vfs */ + while ((vdev = pci_get_device(pdev->vendor, + PCI_DEVID_OCTEONTX2_DPI_VF, vdev))) { + if (!vdev->is_virtfn) + continue; + else { + dpivf_ptr = &dpi_info->dpi_vf[vf_idx]; + dpivf_ptr->pdev = vdev; + dpivf_ptr->vf_id = vf_idx; + dpivf_ptr->in_use = false; + vf_idx++; + } + } + dpi_info->num_vfs = vf_idx; + dpi_info->vfs_free = vf_idx; + return 0; +} + +static void dpivf_sysfs_destroy(struct domain_sysfs *lsfs) +{ + struct dpi_info *dpi_info = &lsfs->dpi_info; + struct dpi_vf *dpivf_ptr = NULL; + uint8_t vf_idx = 0; + + if (dpi_info->num_vfs == 0) + goto free_mem; + else { + for (vf_idx = 0; vf_idx < dpi_info->num_vfs; vf_idx++) { + dpivf_ptr = &dpi_info->dpi_vf[vf_idx]; + pci_dev_put(dpivf_ptr->pdev); + dpivf_ptr->pdev = NULL; + vf_idx++; + } + } + dpi_info->num_vfs = 0; + +free_mem: + kfree(dpi_info->dpi_vf); + dpi_info->dpi_vf = NULL; +} + + +static void enable_pmccntr_el0(void *data) +{ + u64 val; + /* Disable cycle counter overflow interrupt */ + asm volatile("mrs %0, pmintenset_el1" : "=r" (val)); + val &= ~BIT_ULL(31); + asm volatile("msr pmintenset_el1, %0" : : "r" (val)); + /* Enable cycle counter */ + asm volatile("mrs %0, pmcntenset_el0" : "=r" (val)); + val |= BIT_ULL(31); + asm volatile("msr pmcntenset_el0, %0" :: "r" (val)); + /* Enable user-mode access to cycle counters. */ + asm volatile("mrs %0, pmuserenr_el0" : "=r" (val)); + val |= BIT(2) | BIT(0); + asm volatile("msr pmuserenr_el0, %0" : : "r"(val)); + /* Start cycle counter */ + asm volatile("mrs %0, pmcr_el0" : "=r" (val)); + val |= BIT(0); + isb(); + asm volatile("msr pmcr_el0, %0" : : "r" (val)); + asm volatile("mrs %0, pmccfiltr_el0" : "=r" (val)); + val |= BIT(27); + asm volatile("msr pmccfiltr_el0, %0" : : "r" (val)); +} + +static void disable_pmccntr_el0(void *data) +{ + u64 val; + /* Disable cycle counter */ + asm volatile("mrs %0, pmcntenset_el0" : "=r" (val)); + val &= ~BIT_ULL(31); + asm volatile("msr pmcntenset_el0, %0" :: "r" (val)); + /* Disable user-mode access to counters. */ + asm volatile("mrs %0, pmuserenr_el0" : "=r" (val)); + val &= ~(BIT(2) | BIT(0)); + asm volatile("msr pmuserenr_el0, %0" : : "r"(val)); +} + +static ssize_t +enadis_pmccntr_el0_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct domain_sysfs *lsfs = container_of(attr, struct domain_sysfs, + pmccntr_el0); + struct device *dev = &lsfs->rdev->pdev->dev; + char tmp_buf[64]; + long enable = 0; + char *tmp_ptr; + ssize_t used; + + strlcpy(tmp_buf, buf, 64); + used = strlen(tmp_buf); + tmp_ptr = strim(tmp_buf); + if (kstrtol(tmp_ptr, 0, &enable)) { + dev_err(dev, "Invalid value, expected 1/0\n"); + return -EIO; + } + + if (enable) + on_each_cpu(enable_pmccntr_el0, NULL, 1); + else + on_each_cpu(disable_pmccntr_el0, NULL, 1); + + return count; +} + +static void check_pmccntr_el0(void *data) +{ + int *out = data; + u64 val; + + asm volatile("mrs %0, pmuserenr_el0" : "=r" (val)); + *out = *out & !!(val & (BIT(2) | BIT(0))); +} + +static ssize_t +enadis_pmccntr_el0_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int out = 1; + + on_each_cpu(check_pmccntr_el0, &out, 1); + + return snprintf(buf, PAGE_SIZE, "%d\n", out); +} + +int domain_sysfs_create(struct rm_dev *rm) +{ + struct domain_sysfs *lsfs; + int res = 0, i; + + if (rm == NULL || rm->num_vfs == 0) + return -EINVAL; + + lsfs = kzalloc(sizeof(*lsfs), GFP_KERNEL); + if (lsfs == NULL) { + res = -ENOMEM; + goto err_lsfs_alloc; + } + + INIT_LIST_HEAD(&lsfs->ports); + lsfs->rdev = rm; + lsfs->domains_len = rm->num_vfs; + lsfs->domains = + kcalloc(lsfs->domains_len, sizeof(struct domain), GFP_KERNEL); + if (lsfs->domains == NULL) + goto err_domains_alloc; + for (i = 0; i < lsfs->domains_len; i++) + lsfs->domains[i].rvf = &rm->vf_info[i]; + + lsfs->create_domain.attr.name = "create_domain"; + lsfs->create_domain.attr.mode = 0200; + lsfs->create_domain.store = create_domain_store; + res = sysfs_create_file(&rm->pdev->dev.kobj, &lsfs->create_domain.attr); + if (res) + goto err_create_domain; + + lsfs->destroy_domain.attr.name = "destroy_domain"; + lsfs->destroy_domain.attr.mode = 0200; + lsfs->destroy_domain.store = destroy_domain_store; + res = sysfs_create_file(&rm->pdev->dev.kobj, + &lsfs->destroy_domain.attr); + if (res) + goto err_destroy_domain; + + lsfs->pmccntr_el0.attr.name = "pmccntr_el0"; + lsfs->pmccntr_el0.attr.mode = 0644; + lsfs->pmccntr_el0.show = enadis_pmccntr_el0_show; + lsfs->pmccntr_el0.store = enadis_pmccntr_el0_store; + res = sysfs_create_file(&rm->pdev->dev.kobj, &lsfs->pmccntr_el0.attr); + if (res) + goto err_pmccntr_el0; + + lsfs->parent = &rm->pdev->dev.kobj; + + res = dpivf_sysfs_create(lsfs); + if (res) + goto err_dpivf_sysfs_create; + + mutex_lock(&domain_sysfs_lock); + list_add_tail(&lsfs->list, &domain_sysfs_list); + mutex_unlock(&domain_sysfs_lock); + + return 0; + +err_dpivf_sysfs_create: + sysfs_remove_file(&rm->pdev->dev.kobj, &lsfs->pmccntr_el0.attr); +err_pmccntr_el0: + sysfs_remove_file(&rm->pdev->dev.kobj, &lsfs->destroy_domain.attr); +err_destroy_domain: + sysfs_remove_file(&rm->pdev->dev.kobj, &lsfs->create_domain.attr); +err_create_domain: + kfree(lsfs->domains); +err_domains_alloc: + kfree(lsfs); +err_lsfs_alloc: + return res; +} + +void domain_sysfs_destroy(struct rm_dev *rm) +{ + struct list_head *pos, *n; + struct domain_sysfs *lsfs; + + if (rm == NULL) + return; + + mutex_lock(&domain_sysfs_lock); + list_for_each_safe(pos, n, &domain_sysfs_list) { + lsfs = container_of(pos, struct domain_sysfs, list); + if (lsfs->rdev == rm) { + list_del(pos); + break; + } + lsfs = NULL; + } + mutex_unlock(&domain_sysfs_lock); + + if (lsfs == NULL) + return; + + dpivf_sysfs_destroy(lsfs); + + if (lsfs->pmccntr_el0.attr.mode != 0) + sysfs_remove_file(lsfs->parent, &lsfs->pmccntr_el0.attr); + if (lsfs->destroy_domain.attr.mode != 0) + sysfs_remove_file(lsfs->parent, &lsfs->destroy_domain.attr); + if (lsfs->create_domain.attr.mode != 0) + sysfs_remove_file(lsfs->parent, &lsfs->create_domain.attr); + + kfree(lsfs->domains); + kfree(lsfs); +} diff --git a/drivers/soc/marvell/octeontx2-rm/domain_sysfs.h b/drivers/soc/marvell/octeontx2-rm/domain_sysfs.h new file mode 100644 index 000000000000..d28d5b8e8f38 --- /dev/null +++ b/drivers/soc/marvell/octeontx2-rm/domain_sysfs.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OcteonTX2 RVU Resource Manager driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ +#ifndef DOMAIN_SYSFS_H_ +#define DOMAIN_SYSFS_H_ + +#include "otx2_rm.h" + +int domain_sysfs_create(struct rm_dev *rm); +void domain_sysfs_destroy(struct rm_dev *rm); + +#endif /* DOMAIN_SYSFS_H_ */ diff --git a/drivers/soc/marvell/octeontx2-rm/otx2_rm.c b/drivers/soc/marvell/octeontx2-rm/otx2_rm.c new file mode 100644 index 000000000000..bf0e023abdda --- /dev/null +++ b/drivers/soc/marvell/octeontx2-rm/otx2_rm.c @@ -0,0 +1,1581 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OcteonTX2 RVU Resource Manager driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/delay.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/sysfs.h> + +#include "rvu_reg.h" +#include "rvu_struct.h" +#include "otx2_rm.h" + +#ifdef CONFIG_OCTEONTX2_RM_DOM_SYSFS +#include "domain_sysfs.h" +#endif + +#define DRV_NAME "octeontx2-rm" +#define DRV_VERSION "1.0" + +#define PCI_DEVID_OCTEONTX2_SSO_PF 0xA0F9 +#define PCI_DEVID_OCTEONTX2_SSO_VF 0xA0FA + +/* PCI BAR nos */ +#define PCI_AF_REG_BAR_NUM 0 +#define PCI_CFG_REG_BAR_NUM 2 +#define PCI_MBOX_BAR_NUM 4 + +/* Supported devices */ +static const struct pci_device_id rvu_rm_id_table[] = { + {PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_SSO_PF)}, + {0} /* end of table */ +}; + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell OcteonTX2 SSO/SSOW/TIM/NPA PF Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); +MODULE_DEVICE_TABLE(pci, rvu_rm_id_table); + +/* All PF devices found are stored here */ +static spinlock_t rm_lst_lock; +LIST_HEAD(rm_dev_lst_head); + +static void +rm_write64(struct rm_dev *rvu, u64 b, u64 s, u64 o, u64 v) +{ + writeq_relaxed(v, rvu->bar2 + ((b << 20) | (s << 12) | o)); +} + +static u64 rm_read64(struct rm_dev *rvu, u64 b, u64 s, u64 o) +{ + return readq_relaxed(rvu->bar2 + ((b << 20) | (s << 12) | o)); +} + +static void enable_af_mbox_int(struct pci_dev *pdev) +{ + struct rm_dev *rm; + + rm = pci_get_drvdata(pdev); + /* Clear interrupt if any */ + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_INT, 0x1ULL); + + /* Now Enable AF-PF interrupt */ + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_INT_ENA_W1S, 0x1ULL); +} + +static void disable_af_mbox_int(struct pci_dev *pdev) +{ + struct rm_dev *rm; + + rm = pci_get_drvdata(pdev); + /* Clear interrupt if any */ + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_INT, 0x1ULL); + + /* Now Disable AF-PF interrupt */ + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_INT_ENA_W1C, 0x1ULL); +} + +static int +forward_to_mbox(struct rm_dev *rm, struct otx2_mbox *mbox, int devid, + struct mbox_msghdr *req, int size, const char *mstr) +{ + struct mbox_msghdr *msg; + int res = 0; + + msg = otx2_mbox_alloc_msg(mbox, devid, size); + if (msg == NULL) + return -ENOMEM; + + memcpy((uint8_t *)msg + sizeof(struct mbox_msghdr), + (uint8_t *)req + sizeof(struct mbox_msghdr), size); + msg->id = req->id; + msg->pcifunc = req->pcifunc; + msg->sig = req->sig; + msg->ver = req->ver; + + otx2_mbox_msg_send(mbox, devid); + res = otx2_mbox_wait_for_rsp(mbox, devid); + if (res == -EIO) { + dev_err(&rm->pdev->dev, "RVU %s MBOX timeout.\n", mstr); + goto err; + } else if (res) { + dev_err(&rm->pdev->dev, + "RVU %s MBOX error: %d.\n", mstr, res); + res = -EFAULT; + goto err; + } + + return 0; +err: + return res; +} + +static int +handle_af_req(struct rm_dev *rm, struct rvu_vf *vf, struct mbox_msghdr *req, + int size) +{ + /* We expect a request here */ + if (req->sig != OTX2_MBOX_REQ_SIG) { + dev_err(&rm->pdev->dev, + "UP MBOX msg with wrong signature %x, ID 0x%x\n", + req->sig, req->id); + return -EINVAL; + } + + /* If handling notifs in PF is required,add a switch-case here. */ + return forward_to_mbox(rm, &rm->pfvf_mbox_up, vf->vf_id, req, size, + "VF"); +} + + +static void rm_afpf_mbox_handler_up(struct work_struct *work) +{ + struct rm_dev *rm = container_of(work, struct rm_dev, mbox_wrk_up); + struct otx2_mbox *mbox = &rm->afpf_mbox_up; + struct otx2_mbox_dev *mdev = mbox->dev; + struct rvu_vf *vf; + struct mbox_hdr *req_hdr; + struct mbox_msghdr *msg; + int offset, id, err; + + /* sync with mbox memory region */ + smp_rmb(); + + /* Process received mbox messages */ + req_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + offset = ALIGN(sizeof(*req_hdr), MBOX_MSG_ALIGN); + for (id = 0; id < req_hdr->num_msgs; id++) { + msg = (struct mbox_msghdr *)(mdev->mbase + mbox->rx_start + + offset); + + if ((msg->pcifunc >> RVU_PFVF_PF_SHIFT) != rm->pf || + (msg->pcifunc & RVU_PFVF_FUNC_MASK) <= rm->num_vfs) + err = -EINVAL; + else { + vf = &rm->vf_info[msg->pcifunc & RVU_PFVF_FUNC_MASK]; + err = handle_af_req(rm, vf, msg, + msg->next_msgoff - offset); + } + if (err) + otx2_reply_invalid_msg(mbox, 0, msg->pcifunc, msg->id); + offset = msg->next_msgoff; + } + + otx2_mbox_msg_send(mbox, 0); +} + +static void rm_afpf_mbox_handler(struct work_struct *work) +{ + struct rm_dev *rm; + struct mbox_hdr *rsp_hdr; + struct mbox_msghdr *msg, *fwd; + struct otx2_mbox *af_mbx, *vf_mbx; + struct free_rsrcs_rsp *rsp; + int offset, i, vf_id, size; + struct rvu_vf *vf; + + /* Read latest mbox data */ + smp_rmb(); + + rm = container_of(work, struct rm_dev, mbox_wrk); + af_mbx = &rm->afpf_mbox; + vf_mbx = &rm->pfvf_mbox; + rsp_hdr = (struct mbox_hdr *)(af_mbx->dev->mbase + af_mbx->rx_start); + if (rsp_hdr->num_msgs == 0) + return; + offset = ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN); + + for (i = 0; i < rsp_hdr->num_msgs; i++) { + msg = (struct mbox_msghdr *)(af_mbx->dev->mbase + + af_mbx->rx_start + offset); + size = msg->next_msgoff - offset; + + if (msg->id >= MBOX_MSG_MAX) { + dev_err(&rm->pdev->dev, + "MBOX msg with unknown ID 0x%x\n", msg->id); + goto end; + } + + if (msg->sig != OTX2_MBOX_RSP_SIG) { + dev_err(&rm->pdev->dev, + "MBOX msg with wrong signature %x, ID 0x%x\n", + msg->sig, msg->id); + goto end; + } + + vf_id = (msg->pcifunc & RVU_PFVF_FUNC_MASK); + if (vf_id > 0) { + if (vf_id > rm->num_vfs) { + dev_err(&rm->pdev->dev, + "MBOX msg to unknown VF: %d >= %d\n", + vf_id, rm->num_vfs); + goto end; + } + vf = &rm->vf_info[vf_id - 1]; + /* Ignore stale responses and VFs in FLR. */ + if (!vf->in_use || vf->got_flr) + goto end; + fwd = otx2_mbox_alloc_msg(vf_mbx, vf_id - 1, size); + if (!fwd) { + dev_err(&rm->pdev->dev, + "Forwarding to VF%d failed.\n", vf_id); + goto end; + } + memcpy((uint8_t *)fwd + sizeof(struct mbox_msghdr), + (uint8_t *)msg + sizeof(struct mbox_msghdr), + size); + fwd->id = msg->id; + fwd->pcifunc = msg->pcifunc; + fwd->sig = msg->sig; + fwd->ver = msg->ver; + fwd->rc = msg->rc; + } else { + if (msg->ver < OTX2_MBOX_VERSION) { + dev_err(&rm->pdev->dev, + "MBOX msg with version %04x != %04x\n", + msg->ver, OTX2_MBOX_VERSION); + goto end; + } + + switch (msg->id) { + case MBOX_MSG_READY: + rm->pf = (msg->pcifunc >> RVU_PFVF_PF_SHIFT) & + RVU_PFVF_PF_MASK; + break; + case MBOX_MSG_FREE_RSRC_CNT: + rsp = (struct free_rsrcs_rsp *)msg; + memcpy(&rm->limits, msg, sizeof(*rsp)); + break; + default: + dev_err(&rm->pdev->dev, + "Unsupported msg %d received.\n", + msg->id); + break; + } + } +end: + offset = msg->next_msgoff; + af_mbx->dev->msgs_acked++; + } + otx2_mbox_reset(af_mbx, 0); +} + +static int +reply_free_rsrc_cnt(struct rm_dev *rm, struct rvu_vf *vf, + struct mbox_msghdr *req, int size) +{ + struct free_rsrcs_rsp *rsp; + + rsp = (struct free_rsrcs_rsp *)otx2_mbox_alloc_msg(&rm->pfvf_mbox, + vf->vf_id, + sizeof(*rsp)); + if (rsp == NULL) + return -ENOMEM; + + rsp->hdr.id = MBOX_MSG_FREE_RSRC_CNT; + rsp->hdr.pcifunc = req->pcifunc; + rsp->hdr.sig = OTX2_MBOX_RSP_SIG; + mutex_lock(&rm->lock); + rsp->sso = rm->vf_limits.sso->a[vf->vf_id].val; + rsp->ssow = rm->vf_limits.ssow->a[vf->vf_id].val; + rsp->npa = rm->vf_limits.npa->a[vf->vf_id].val; + rsp->cpt = rm->vf_limits.cpt->a[vf->vf_id].val; + rsp->tim = rm->vf_limits.tim->a[vf->vf_id].val; + rsp->nix = 0; + mutex_unlock(&rm->lock); + return 0; +} + +static int +check_attach_rsrcs_req(struct rm_dev *rm, struct rvu_vf *vf, + struct mbox_msghdr *req, int size) +{ + struct rsrc_attach *rsrc_req; + + rsrc_req = (struct rsrc_attach *)req; + mutex_lock(&rm->lock); + if (rsrc_req->sso > rm->vf_limits.sso->a[vf->vf_id].val || + rsrc_req->ssow > rm->vf_limits.ssow->a[vf->vf_id].val || + rsrc_req->npalf > rm->vf_limits.npa->a[vf->vf_id].val || + rsrc_req->timlfs > rm->vf_limits.tim->a[vf->vf_id].val || + rsrc_req->cptlfs > rm->vf_limits.cpt->a[vf->vf_id].val || + rsrc_req->nixlf > 0) { + dev_err(&rm->pdev->dev, + "Invalid ATTACH_RESOURCES request from %s\n", + dev_name(&vf->pdev->dev)); + mutex_unlock(&rm->lock); + return -EINVAL; + } + mutex_unlock(&rm->lock); + return forward_to_mbox(rm, &rm->afpf_mbox, 0, req, size, "AF"); +} + +static int +handle_vf_req(struct rm_dev *rm, struct rvu_vf *vf, struct mbox_msghdr *req, + int size) +{ + int err = 0; + + /* Check if valid, if not reply with a invalid msg */ + if (req->sig != OTX2_MBOX_REQ_SIG) { + dev_err(&rm->pdev->dev, + "VF MBOX msg with wrong signature %x, ID 0x%x\n", + req->sig, req->id); + return -EINVAL; + } + + switch (req->id) { + case MBOX_MSG_READY: + if (req->ver < OTX2_MBOX_VERSION) { + dev_err(&rm->pdev->dev, + "VF MBOX msg with version %04x != %04x\n", + req->ver, OTX2_MBOX_VERSION); + return -EINVAL; + } + vf->in_use = true; + err = forward_to_mbox(rm, &rm->afpf_mbox, 0, req, size, "AF"); + break; + case MBOX_MSG_FREE_RSRC_CNT: + if (req->ver < OTX2_MBOX_VERSION) { + dev_err(&rm->pdev->dev, + "VF MBOX msg with version %04x != %04x\n", + req->ver, OTX2_MBOX_VERSION); + return -EINVAL; + } + err = reply_free_rsrc_cnt(rm, vf, req, size); + break; + case MBOX_MSG_ATTACH_RESOURCES: + if (req->ver < OTX2_MBOX_VERSION) { + dev_err(&rm->pdev->dev, + "VF MBOX msg with version %04x != %04x\n", + req->ver, OTX2_MBOX_VERSION); + return -EINVAL; + } + err = check_attach_rsrcs_req(rm, vf, req, size); + break; + default: + err = forward_to_mbox(rm, &rm->afpf_mbox, 0, req, size, "AF"); + break; + } + + return err; +} + +static int send_flr_msg(struct otx2_mbox *mbox, int dev_id, int pcifunc) +{ + struct msg_req *req; + + req = (struct msg_req *) + otx2_mbox_alloc_msg(mbox, dev_id, sizeof(*req)); + if (req == NULL) + return -ENOMEM; + + req->hdr.pcifunc = pcifunc; + req->hdr.id = MBOX_MSG_VF_FLR; + req->hdr.sig = OTX2_MBOX_REQ_SIG; + + otx2_mbox_msg_send(mbox, 0); + + return 0; +} + +static void rm_send_flr_msg(struct rm_dev *rm, struct rvu_vf *vf) +{ + int res, pcifunc; + + pcifunc = (vf->rm->pf << RVU_PFVF_PF_SHIFT) | + ((vf->vf_id + 1) & RVU_PFVF_FUNC_MASK); + + if (send_flr_msg(&rm->afpf_mbox, 0, pcifunc) != 0) { + dev_err(&rm->pdev->dev, "Sending FLR to AF failed\n"); + return; + } + + res = otx2_mbox_wait_for_rsp(&rm->afpf_mbox, 0); + if (res == -EIO) { + dev_err(&rm->pdev->dev, "RVU AF MBOX timeout.\n"); + } else if (res) { + dev_err(&rm->pdev->dev, + "RVU MBOX error: %d.\n", res); + } +} + +static void rm_send_flr_to_dpi(struct rm_dev *rm) +{ + /* TODO: DPI VF's needs to be handled */ +} + +static void rm_pfvf_flr_handler(struct work_struct *work) +{ + struct rvu_vf *vf = container_of(work, struct rvu_vf, pfvf_flr_work); + struct rm_dev *rm = vf->rm; + struct otx2_mbox *mbox = &rm->pfvf_mbox; + + rm_send_flr_to_dpi(rm); + rm_send_flr_msg(rm, vf); + + /* Disable interrupts from AF and wait for any pending + * responses to be handled for this VF and then reset the + * mailbox + */ + disable_af_mbox_int(rm->pdev); + flush_workqueue(rm->afpf_mbox_wq); + otx2_mbox_reset(mbox, vf->vf_id); + vf->in_use = false; + vf->got_flr = false; + enable_af_mbox_int(rm->pdev); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFTRPENDX(vf->vf_id / 64), + BIT_ULL(vf->intr_idx)); +} + +static void rm_pfvf_mbox_handler_up(struct work_struct *work) +{ + struct rm_dev *rm; + struct mbox_hdr *rsp_hdr; + struct mbox_msghdr *msg, *fwd; + struct otx2_mbox *af_mbx, *vf_mbx; + int offset, i, size; + struct rvu_vf *vf; + + /* Read latest mbox data */ + smp_rmb(); + + vf = container_of(work, struct rvu_vf, mbox_wrk_up); + rm = vf->rm; + af_mbx = &rm->afpf_mbox; + vf_mbx = &rm->pfvf_mbox; + rsp_hdr = (struct mbox_hdr *)(vf_mbx->dev[vf->vf_id].mbase + + vf_mbx->rx_start); + if (rsp_hdr->num_msgs == 0) + return; + offset = ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN); + + for (i = 0; i < rsp_hdr->num_msgs; i++) { + msg = (struct mbox_msghdr *)(vf_mbx->dev->mbase + + vf_mbx->rx_start + offset); + size = msg->next_msgoff - offset; + + if (msg->sig != OTX2_MBOX_RSP_SIG) { + dev_err(&rm->pdev->dev, + "UP MBOX msg with wrong signature %x, ID 0x%x\n", + msg->sig, msg->id); + goto end; + } + + /* override message value with actual values */ + msg->pcifunc = (rm->pf << RVU_PFVF_PF_SHIFT) | vf->vf_id; + + fwd = otx2_mbox_alloc_msg(af_mbx, 0, size); + if (!fwd) { + dev_err(&rm->pdev->dev, + "UP Forwarding from VF%d to AF failed.\n", + vf->vf_id); + goto end; + } + memcpy((uint8_t *)fwd + sizeof(struct mbox_msghdr), + (uint8_t *)msg + sizeof(struct mbox_msghdr), + size); + fwd->id = msg->id; + fwd->pcifunc = msg->pcifunc; + fwd->sig = msg->sig; + fwd->ver = msg->ver; + fwd->rc = msg->rc; +end: + offset = msg->next_msgoff; + vf_mbx->dev->msgs_acked++; + } + otx2_mbox_reset(vf_mbx, vf->vf_id); +} + +static void rm_pfvf_mbox_handler(struct work_struct *work) +{ + struct rvu_vf *vf = container_of(work, struct rvu_vf, mbox_wrk); + struct rm_dev *rm = vf->rm; + struct otx2_mbox *mbox = &rm->pfvf_mbox; + struct otx2_mbox_dev *mdev = &mbox->dev[vf->vf_id]; + struct mbox_hdr *req_hdr; + struct mbox_msghdr *msg; + int offset, id, err; + + /* sync with mbox memory region */ + smp_rmb(); + + /* Process received mbox messages */ + req_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + offset = ALIGN(sizeof(*req_hdr), MBOX_MSG_ALIGN); + for (id = 0; id < req_hdr->num_msgs; id++) { + msg = (struct mbox_msghdr *)(mdev->mbase + mbox->rx_start + + offset); + + /* Set which VF sent this message based on mbox IRQ */ + msg->pcifunc = ((u16)rm->pf << RVU_PFVF_PF_SHIFT) | + ((vf->vf_id + 1) & RVU_PFVF_FUNC_MASK); + err = handle_vf_req(rm, vf, msg, msg->next_msgoff - offset); + if (err) + otx2_reply_invalid_msg(mbox, vf->vf_id, msg->pcifunc, + msg->id); + offset = msg->next_msgoff; + } + /* Send mbox responses to VF */ + if (mdev->num_msgs) + otx2_mbox_msg_send(mbox, vf->vf_id); +} + +static irqreturn_t rm_af_pf_mbox_intr(int irq, void *arg) +{ + struct rm_dev *rm = (struct rm_dev *)arg; + struct mbox_hdr *hdr; + struct otx2_mbox *mbox; + struct otx2_mbox_dev *mdev; + + /* Read latest mbox data */ + smp_rmb(); + + mbox = &rm->afpf_mbox; + mdev = &mbox->dev[0]; + hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + /* Handle PF => AF channel response */ + if (hdr->num_msgs) + queue_work(rm->afpf_mbox_wq, &rm->mbox_wrk); + + mbox = &rm->afpf_mbox_up; + mdev = &mbox->dev[0]; + hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + /* Handle AF => PF request */ + if (hdr->num_msgs) + queue_work(rm->afpf_mbox_wq, &rm->mbox_wrk_up); + + /* Clear and ack the interrupt */ + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_INT, 0x1ULL); + + return IRQ_HANDLED; +} + +static void __handle_vf_flr(struct rm_dev *rm, struct rvu_vf *vf_ptr) +{ + if (vf_ptr->in_use) { + /* Using the same MBOX workqueue here, so that we can + * synchronize with other VF->PF messages being forwarded to + * AF + */ + vf_ptr->got_flr = true; + queue_work(rm->pfvf_mbox_wq, &vf_ptr->pfvf_flr_work); + } else + rm_write64(rm, BLKADDR_RVUM, 0, + RVU_PF_VFTRPENDX(vf_ptr->vf_id / 64), + BIT_ULL(vf_ptr->intr_idx)); +} + +static irqreturn_t rm_pf_vf_flr_intr(int irq, void *arg) +{ + struct rm_dev *rm = (struct rm_dev *)arg; + u64 intr; + struct rvu_vf *vf_ptr; + int vf, i; + + /* Check which VF FLR has been raised and process accordingly */ + for (i = 0; i < 2; i++) { + /* Read the interrupt bits */ + intr = rm_read64(rm, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INTX(i)); + + for (vf = i * 64; vf < rm->num_vfs; vf++) { + vf_ptr = &rm->vf_info[vf]; + if (intr & (1ULL << vf_ptr->intr_idx)) { + /* Clear the interrupts */ + rm_write64(rm, BLKADDR_RVUM, 0, + RVU_PF_VFFLR_INTX(i), + BIT_ULL(vf_ptr->intr_idx)); + __handle_vf_flr(rm, vf_ptr); + } + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t rm_pf_vf_mbox_intr(int irq, void *arg) +{ + struct rm_dev *rm = (struct rm_dev *)arg; + struct mbox_hdr *hdr; + struct otx2_mbox *mbox; + struct otx2_mbox_dev *mdev; + u64 intr; + struct rvu_vf *vf; + int i, vfi; + + /* Check which VF has raised an interrupt and schedule corresponding + * workq to process the MBOX + */ + for (i = 0; i < 2; i++) { + /* Read the interrupt bits */ + intr = rm_read64(rm, BLKADDR_RVUM, 0, RVU_PF_VFPF_MBOX_INTX(i)); + + for (vfi = i * 64; vfi < rm->num_vfs; vfi++) { + vf = &rm->vf_info[vfi]; + if ((intr & (1ULL << vf->intr_idx)) == 0) + continue; + mbox = &rm->pfvf_mbox; + mdev = &mbox->dev[vf->vf_id]; + hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + /* Handle VF => PF channel request */ + if (hdr->num_msgs) + queue_work(rm->pfvf_mbox_wq, &vf->mbox_wrk); + + mbox = &rm->pfvf_mbox_up; + mdev = &mbox->dev[vf->vf_id]; + hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + /* Handle PF => VF channel response */ + if (hdr->num_msgs) + queue_work(rm->pfvf_mbox_wq, &vf->mbox_wrk_up); + /* Clear the interrupt */ + rm_write64(rm, BLKADDR_RVUM, 0, + RVU_PF_VFPF_MBOX_INTX(i), + BIT_ULL(vf->intr_idx)); + } + } + + return IRQ_HANDLED; +} + +static int rm_register_flr_irq(struct pci_dev *pdev) +{ + struct rm_dev *rm; + int err, vec, i; + + rm = pci_get_drvdata(pdev); + + /* Register for VF FLR interrupts + * There are 2 vectors starting at index 0x0 + */ + for (vec = RVU_PF_INT_VEC_VFFLR0, i = 0; + vec + i <= RVU_PF_INT_VEC_VFFLR1; i++) { + sprintf(&rm->irq_names[(vec + i) * NAME_SIZE], + "PF%02d_VF_FLR_IRQ%d", pdev->devfn, i); + err = request_irq(pci_irq_vector(pdev, vec + i), + rm_pf_vf_flr_intr, 0, + &rm->irq_names[(vec + i) * NAME_SIZE], rm); + if (err) { + dev_err(&pdev->dev, + "request_irq() failed for PFVF FLR intr %d\n", + vec); + goto reg_fail; + } + rm->irq_allocated[vec + i] = true; + } + + return 0; + +reg_fail: + + return err; +} + +static void rm_free_flr_irq(struct pci_dev *pdev) +{ + (void) pdev; + /* Nothing here but will free workqueues */ +} + +static int rm_alloc_irqs(struct pci_dev *pdev) +{ + struct rm_dev *rm; + int err; + + rm = pci_get_drvdata(pdev); + + /* Get number of MSIX vector count and allocate vectors first */ + rm->msix_count = pci_msix_vec_count(pdev); + + err = pci_alloc_irq_vectors(pdev, rm->msix_count, rm->msix_count, + PCI_IRQ_MSIX); + + if (err < 0) { + dev_err(&pdev->dev, "pci_alloc_irq_vectors() failed %d\n", err); + return err; + } + + rm->irq_names = kmalloc_array(rm->msix_count, NAME_SIZE, GFP_KERNEL); + if (!rm->irq_names) { + err = -ENOMEM; + goto err_irq_names; + } + + rm->irq_allocated = kcalloc(rm->msix_count, sizeof(bool), GFP_KERNEL); + if (!rm->irq_allocated) { + err = -ENOMEM; + goto err_irq_allocated; + } + + return 0; + +err_irq_allocated: + kfree(rm->irq_names); + rm->irq_names = NULL; +err_irq_names: + pci_free_irq_vectors(pdev); + rm->msix_count = 0; + + return err; +} + +static void rm_free_irqs(struct pci_dev *pdev) +{ + struct rm_dev *rm; + int irq; + + rm = pci_get_drvdata(pdev); + for (irq = 0; irq < rm->msix_count; irq++) { + if (rm->irq_allocated[irq]) + free_irq(pci_irq_vector(rm->pdev, irq), rm); + } + + pci_free_irq_vectors(pdev); + + kfree(rm->irq_names); + kfree(rm->irq_allocated); +} + +static int rm_register_mbox_irq(struct pci_dev *pdev) +{ + struct rm_dev *rm; + int err, vec = RVU_PF_INT_VEC_VFPF_MBOX0, i; + + rm = pci_get_drvdata(pdev); + + /* Register PF-AF interrupt handler */ + sprintf(&rm->irq_names[RVU_PF_INT_VEC_AFPF_MBOX * NAME_SIZE], + "PF%02d_AF_MBOX_IRQ", pdev->devfn); + err = request_irq(pci_irq_vector(pdev, RVU_PF_INT_VEC_AFPF_MBOX), + rm_af_pf_mbox_intr, 0, + &rm->irq_names[RVU_PF_INT_VEC_AFPF_MBOX * NAME_SIZE], + rm); + if (err) { + dev_err(&pdev->dev, + "request_irq() failed for AF_PF MSIX vector\n"); + return err; + } + rm->irq_allocated[RVU_PF_INT_VEC_AFPF_MBOX] = true; + + err = otx2_mbox_init(&rm->afpf_mbox, rm->af_mbx_base, pdev, rm->bar2, + MBOX_DIR_PFAF, 1); + if (err) { + dev_err(&pdev->dev, "Failed to initialize PF/AF MBOX\n"); + goto error; + } + err = otx2_mbox_init(&rm->afpf_mbox_up, rm->af_mbx_base, pdev, rm->bar2, + MBOX_DIR_PFAF_UP, 1); + if (err) { + dev_err(&pdev->dev, "Failed to initialize PF/AF UP MBOX\n"); + goto error; + } + + /* Register for PF-VF mailbox interrupts + * There are 2 vectors starting at index 0x4 + */ + for (vec = RVU_PF_INT_VEC_VFPF_MBOX0, i = 0; + vec + i <= RVU_PF_INT_VEC_VFPF_MBOX1; i++) { + sprintf(&rm->irq_names[(vec + i) * NAME_SIZE], + "PF%02d_VF_MBOX_IRQ%d", pdev->devfn, i); + err = request_irq(pci_irq_vector(pdev, vec + i), + rm_pf_vf_mbox_intr, 0, + &rm->irq_names[(vec + i) * NAME_SIZE], rm); + if (err) { + dev_err(&pdev->dev, + "request_irq() failed for PFVF Mbox intr %d\n", + vec + i); + goto error; + } + rm->irq_allocated[vec + i] = true; + } + + rm->afpf_mbox_wq = alloc_workqueue( + "rm_pfaf_mailbox", WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM, 1); + if (!rm->afpf_mbox_wq) + goto error; + + INIT_WORK(&rm->mbox_wrk, rm_afpf_mbox_handler); + INIT_WORK(&rm->mbox_wrk_up, rm_afpf_mbox_handler_up); + + return err; + +error: + if (rm->afpf_mbox_up.dev != NULL) + otx2_mbox_destroy(&rm->afpf_mbox_up); + if (rm->afpf_mbox.dev != NULL) + otx2_mbox_destroy(&rm->afpf_mbox); + + return err; +} + +static int rm_get_pcifunc(struct rm_dev *rm) +{ + struct msg_req *ready_req; + int res = 0; + + ready_req = (struct msg_req *) + otx2_mbox_alloc_msg_rsp(&rm->afpf_mbox, 0, sizeof(ready_req), + sizeof(struct ready_msg_rsp)); + if (ready_req == NULL) { + dev_err(&rm->pdev->dev, "RVU MBOX failed to get message.\n"); + return -EFAULT; + } + + ready_req->hdr.id = MBOX_MSG_READY; + ready_req->hdr.sig = OTX2_MBOX_REQ_SIG; + otx2_mbox_msg_send(&rm->afpf_mbox, 0); + res = otx2_mbox_wait_for_rsp(&rm->afpf_mbox, 0); + if (res == -EIO) { + dev_err(&rm->pdev->dev, "RVU AF MBOX timeout.\n"); + } else if (res) { + dev_err(&rm->pdev->dev, "RVU MBOX error: %d.\n", res); + res = -EFAULT; + } + return res; +} + +static int rm_get_available_rsrcs(struct rm_dev *rm) +{ + struct mbox_msghdr *rsrc_req; + int res = 0; + + rsrc_req = otx2_mbox_alloc_msg(&rm->afpf_mbox, 0, sizeof(*rsrc_req)); + if (rsrc_req == NULL) { + dev_err(&rm->pdev->dev, "RVU MBOX failed to get message.\n"); + return -EFAULT; + } + rsrc_req->id = MBOX_MSG_FREE_RSRC_CNT; + rsrc_req->sig = OTX2_MBOX_REQ_SIG; + rsrc_req->pcifunc = RVU_PFFUNC(rm->pf, 0); + otx2_mbox_msg_send(&rm->afpf_mbox, 0); + res = otx2_mbox_wait_for_rsp(&rm->afpf_mbox, 0); + if (res == -EIO) { + dev_err(&rm->pdev->dev, "RVU AF MBOX timeout.\n"); + } else if (res) { + dev_err(&rm->pdev->dev, + "RVU MBOX error: %d.\n", res); + res = -EFAULT; + } + return res; +} + +static void rm_afpf_mbox_term(struct pci_dev *pdev) +{ + struct rm_dev *rm = pci_get_drvdata(pdev); + + flush_workqueue(rm->afpf_mbox_wq); + destroy_workqueue(rm->afpf_mbox_wq); + otx2_mbox_destroy(&rm->afpf_mbox); + otx2_mbox_destroy(&rm->afpf_mbox_up); +} + +static ssize_t vf_in_use_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct rvu_vf *vf = container_of(attr, struct rvu_vf, in_use_attr); + + return snprintf(buf, PAGE_SIZE, "%d\n", vf->in_use); +} + +static void vf_sysfs_destroy(struct pci_dev *pdev) +{ + struct rm_dev *rm; + struct rvu_vf *vf; + int i; + + rm = pci_get_drvdata(pdev); + + quotas_free(rm->vf_limits.sso); + quotas_free(rm->vf_limits.ssow); + quotas_free(rm->vf_limits.npa); + quotas_free(rm->vf_limits.cpt); + quotas_free(rm->vf_limits.tim); + rm->vf_limits.sso = NULL; + rm->vf_limits.ssow = NULL; + rm->vf_limits.npa = NULL; + rm->vf_limits.cpt = NULL; + rm->vf_limits.tim = NULL; + + for (i = 0; i < rm->num_vfs; i++) { + vf = &rm->vf_info[i]; + if (vf->limits_kobj == NULL) + continue; + if (vf->in_use_attr.attr.mode != 0) { + sysfs_remove_file(&vf->pdev->dev.kobj, + &vf->in_use_attr.attr); + vf->in_use_attr.attr.mode = 0; + } + kobject_del(vf->limits_kobj); + vf->limits_kobj = NULL; + pci_dev_put(vf->pdev); + vf->pdev = NULL; + } +} + +static int check_vf_in_use(void *arg, struct quota *quota, int new_val) +{ + struct rvu_vf *vf = arg; + + if (vf->in_use) { + dev_err(quota->dev, "Can't modify limits, device is in use.\n"); + return 1; + } + return 0; +} + +static struct quota_ops vf_limit_ops = { + .pre_store = check_vf_in_use, +}; + +static int vf_sysfs_create(struct pci_dev *pdev) +{ + struct rm_dev *rm; + struct pci_dev *vdev; + struct rvu_vf *vf; + int err, i; + + vdev = NULL; + vf = NULL; + rm = pci_get_drvdata(pdev); + err = 0; + i = 0; + + /* Create limit structures for all resource types */ + rm->vf_limits.sso = quotas_alloc(rm->num_vfs, rm->limits.sso, + rm->limits.sso, 0, &rm->lock, + &vf_limit_ops); + if (rm->vf_limits.sso == NULL) { + dev_err(&pdev->dev, + "Failed to allocate sso limits structures.\n"); + err = -EFAULT; + goto error; + } + rm->vf_limits.ssow = quotas_alloc(rm->num_vfs, rm->limits.ssow, + rm->limits.ssow, 0, &rm->lock, + &vf_limit_ops); + if (rm->vf_limits.ssow == NULL) { + dev_err(&pdev->dev, + "Failed to allocate ssow limits structures.\n"); + err = -EFAULT; + goto error; + } + /* AF currently reports only 0-1 for PF but there's more free LFs. + * Until we implement proper limits in AF, use max num_vfs in total. + */ + rm->vf_limits.npa = quotas_alloc(rm->num_vfs, 1, rm->num_vfs, 0, + &rm->lock, &vf_limit_ops); + if (rm->vf_limits.npa == NULL) { + dev_err(&pdev->dev, + "Failed to allocate npa limits structures.\n"); + err = -EFAULT; + goto error; + } + rm->vf_limits.cpt = quotas_alloc(rm->num_vfs, rm->limits.cpt, + rm->limits.cpt, 0, &rm->lock, + &vf_limit_ops); + if (rm->vf_limits.cpt == NULL) { + dev_err(&pdev->dev, + "Failed to allocate cpt limits structures.\n"); + err = -EFAULT; + goto error; + } + rm->vf_limits.tim = quotas_alloc(rm->num_vfs, rm->limits.tim, + rm->limits.tim, 0, &rm->lock, + &vf_limit_ops); + if (rm->vf_limits.tim == NULL) { + dev_err(&pdev->dev, + "Failed to allocate tim limits structures.\n"); + err = -EFAULT; + goto error; + } + + /* loop through all the VFs and create sysfs entries for them */ + while ((vdev = pci_get_device(pdev->vendor, PCI_DEVID_OCTEONTX2_SSO_VF, + vdev))) { + if (!vdev->is_virtfn || (vdev->physfn != pdev)) + continue; + vf = &rm->vf_info[i]; + vf->pdev = pci_dev_get(vdev); + vf->limits_kobj = kobject_create_and_add("limits", + &vdev->dev.kobj); + if (vf->limits_kobj == NULL) { + err = -ENOMEM; + goto error; + } + if (quota_sysfs_create("sso", vf->limits_kobj, &vdev->dev, + &rm->vf_limits.sso->a[i], vf) != 0) { + dev_err(&pdev->dev, + "Failed to create sso limits sysfs for %s\n", + pci_name(vdev)); + err = -EFAULT; + goto error; + } + if (quota_sysfs_create("ssow", vf->limits_kobj, &vdev->dev, + &rm->vf_limits.ssow->a[i], vf) != 0) { + dev_err(&pdev->dev, + "Failed to create ssow limits sysfs for %s\n", + pci_name(vdev)); + err = -EFAULT; + goto error; + } + if (quota_sysfs_create("npa", vf->limits_kobj, &vdev->dev, + &rm->vf_limits.npa->a[i], vf) != 0) { + dev_err(&pdev->dev, + "Failed to create npa limits sysfs for %s\n", + pci_name(vdev)); + err = -EFAULT; + goto error; + } + if (quota_sysfs_create("cpt", vf->limits_kobj, &vdev->dev, + &rm->vf_limits.cpt->a[i], vf) != 0) { + dev_err(&pdev->dev, + "Failed to create cpt limits sysfs for %s\n", + pci_name(vdev)); + err = -EFAULT; + goto error; + } + if (quota_sysfs_create("tim", vf->limits_kobj, &vdev->dev, + &rm->vf_limits.tim->a[i], vf) != 0) { + dev_err(&pdev->dev, + "Failed to create tim limits sysfs for %s\n", + pci_name(vdev)); + err = -EFAULT; + goto error; + } + + vf->in_use_attr.show = vf_in_use_show; + vf->in_use_attr.attr.name = "in_use"; + vf->in_use_attr.attr.mode = 0444; + sysfs_attr_init(&vf->in_use_attr.attr); + if (sysfs_create_file(&vdev->dev.kobj, &vf->in_use_attr.attr)) { + dev_err(&pdev->dev, + "Failed to create in_use sysfs entry for %s\n", + pci_name(vdev)); + err = -EFAULT; + goto error; + } + i++; + } + + return 0; +error: + vf_sysfs_destroy(pdev); + return err; +} + +static int rm_check_pf_usable(struct rm_dev *rm) +{ + u64 rev; + + rev = rm_read64(rm, BLKADDR_RVUM, 0, + RVU_PF_BLOCK_ADDRX_DISC(BLKADDR_RVUM)); + rev = (rev >> 12) & 0xFF; + /* Check if AF has setup revision for RVUM block, + * otherwise this driver probe should be deferred + * until AF driver comes up. + */ + if (!rev) { + dev_warn(&rm->pdev->dev, + "AF is not initialized, deferring probe\n"); + return -EPROBE_DEFER; + } + return 0; +} + +static int rm_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct rm_dev *rm; + int err; + + rm = devm_kzalloc(dev, sizeof(struct rm_dev), GFP_KERNEL); + if (rm == NULL) + return -ENOMEM; + + rm->pdev = pdev; + pci_set_drvdata(pdev, rm); + + mutex_init(&rm->lock); + + err = pci_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device\n"); + goto enable_failed; + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + dev_err(dev, "PCI request regions failed 0x%x\n", err); + goto map_failed; + } + + if (pci_sriov_get_totalvfs(pdev) <= 0) { + err = -ENODEV; + goto set_mask_failed; + } + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); + if (err) { + dev_err(dev, "Unable to set DMA mask\n"); + goto set_mask_failed; + } + + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); + if (err) { + dev_err(dev, "Unable to set DMA mask\n"); + goto set_mask_failed; + } + + pci_set_master(pdev); + + /* CSR Space mapping */ + rm->bar2 = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, + pci_resource_len(pdev, PCI_CFG_REG_BAR_NUM)); + if (!rm->bar2) { + dev_err(&pdev->dev, "Unable to map BAR2\n"); + err = -ENODEV; + goto set_mask_failed; + } + + err = rm_check_pf_usable(rm); + if (err) + goto pf_unusable; + + /* Map PF-AF mailbox memory */ + rm->af_mbx_base = ioremap_wc(pci_resource_start(pdev, PCI_MBOX_BAR_NUM), + pci_resource_len(pdev, PCI_MBOX_BAR_NUM)); + if (!rm->af_mbx_base) { + dev_err(&pdev->dev, "Unable to map BAR4\n"); + err = -ENODEV; + goto pf_unusable; + } + + /* Request IRQ for PF-VF mailbox here - TBD: check if this can be moved + * to sriov enable function + */ + if (rm_alloc_irqs(pdev)) { + dev_err(&pdev->dev, + "Unable to allocate MSIX Interrupt vectors\n"); + err = -ENODEV; + goto alloc_irqs_failed; + } + + if (rm_register_mbox_irq(pdev) != 0) { + dev_err(&pdev->dev, + "Unable to allocate MBOX Interrupt vectors\n"); + err = -ENODEV; + goto reg_mbox_irq_failed; + } + + if (rm_register_flr_irq(pdev) != 0) { + dev_err(&pdev->dev, + "Unable to allocate FLR Interrupt vectors\n"); + err = -ENODEV; + goto reg_flr_irq_failed; + } + + enable_af_mbox_int(pdev); + + if (rm_get_pcifunc(rm)) { + dev_err(&pdev->dev, + "Failed to retrieve pcifunc from AF\n"); + err = -ENODEV; + goto get_pcifunc_failed; + } + + /* Add to global list of PFs found */ + spin_lock(&rm_lst_lock); + list_add(&rm->list, &rm_dev_lst_head); + spin_unlock(&rm_lst_lock); + + return 0; + +get_pcifunc_failed: + disable_af_mbox_int(pdev); + rm_free_flr_irq(pdev); +reg_flr_irq_failed: + rm_afpf_mbox_term(pdev); +reg_mbox_irq_failed: + rm_free_irqs(pdev); +alloc_irqs_failed: + iounmap(rm->af_mbx_base); +pf_unusable: + pcim_iounmap(pdev, rm->bar2); +set_mask_failed: + pci_release_regions(pdev); +map_failed: + pci_disable_device(pdev); +enable_failed: + pci_set_drvdata(pdev, NULL); + devm_kfree(dev, rm); + return err; +} + +static void enable_vf_flr_int(struct pci_dev *pdev) +{ + struct rm_dev *rm; + int ena_bits; + + rm = pci_get_drvdata(pdev); + /* Clear any pending interrupts */ + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFTRPENDX(0), ~0x0ULL); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INTX(0), ~0x0ULL); + + if (rm->num_vfs > 64) { /* For VF 64 to 127(MAX) */ + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFTRPENDX(1), ~0x0ULL); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INTX(1), ~0x0ULL); + } + + /* Enable for first 64 VFs here - upto number of VFs enabled */ + ena_bits = ((rm->num_vfs - 1) % 64); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INT_ENA_W1SX(0), + GENMASK_ULL(ena_bits, 0)); + + if (rm->num_vfs > 64) { /* For VF 64 to 127(MAX) */ + /* Enable for VF interrupts for VFs 64 to 128 */ + ena_bits = rm->num_vfs - 64 - 1; + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INT_ENA_W1SX(1), + GENMASK_ULL(ena_bits, 0)); + } +} + +static void disable_vf_flr_int(struct pci_dev *pdev) +{ + struct rm_dev *rm; + int ena_bits; + u64 intr; + + rm = pci_get_drvdata(pdev); + /* clear any pending interrupt */ + + intr = rm_read64(rm, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INTX(0)); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INTX(0), intr); + intr = rm_read64(rm, BLKADDR_RVUM, 0, RVU_PF_VFTRPENDX(0)); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFTRPENDX(0), intr); + + if (rm->num_vfs > 64) { /* For VF 64 to 127(MAX) */ + intr = rm_read64(rm, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INTX(1)); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INTX(1), intr); + intr = rm_read64(rm, BLKADDR_RVUM, 0, RVU_PF_VFTRPENDX(1)); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFTRPENDX(1), intr); + } + + /* Disable for first 64 VFs here - upto number of VFs enabled */ + ena_bits = ((rm->num_vfs - 1) % 64); + + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INT_ENA_W1CX(0), + GENMASK_ULL(ena_bits, 0)); + + if (rm->num_vfs > 64) { /* For VF 64 to 127(MAX) */ + /* Enable for VF interrupts for VFs 64 to 128 */ + ena_bits = rm->num_vfs - 64 - 1; + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INT_ENA_W1CX(1), + GENMASK_ULL(ena_bits, 0)); + } +} + +static void enable_vf_mbox_int(struct pci_dev *pdev) +{ + struct rm_dev *rm; + int ena_bits; + + rm = pci_get_drvdata(pdev); + /* Clear any pending interrupts */ + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFPF_MBOX_INTX(0), ~0x0ULL); + + if (rm->num_vfs > 64) { /* For VF 64 to 127(MAX) */ + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFPF_MBOX_INTX(1), + ~0x0ULL); + } + + /* Enable for first 64 VFs here - upto number of VFs enabled */ + ena_bits = ((rm->num_vfs - 1) % 64); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFPF_MBOX_INT_ENA_W1SX(0), + GENMASK_ULL(ena_bits, 0)); + + if (rm->num_vfs > 64) { /* For VF 64 to 127(MAX) */ + /* Enable for VF interrupts for VFs 64 to 128 */ + ena_bits = rm->num_vfs - 64 - 1; + rm_write64(rm, BLKADDR_RVUM, 0, + RVU_PF_VFPF_MBOX_INT_ENA_W1SX(1), + GENMASK_ULL(ena_bits, 0)); + } +} + +static void disable_vf_mbox_int(struct pci_dev *pdev) +{ + struct rm_dev *rm; + int ena_bits; + u64 intr; + + rm = pci_get_drvdata(pdev); + /* clear any pending interrupt */ + + intr = rm_read64(rm, BLKADDR_RVUM, 0, RVU_PF_VFPF_MBOX_INTX(0)); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFPF_MBOX_INTX(0), intr); + + if (rm->num_vfs > 64) { /* For VF 64 to 127(MAX) */ + intr = rm_read64(rm, BLKADDR_RVUM, 0, RVU_PF_VFPF_MBOX_INTX(1)); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFPF_MBOX_INTX(1), intr); + } + + /* Disable for first 64 VFs here - upto number of VFs enabled */ + ena_bits = ((rm->num_vfs - 1) % 64); + rm_write64(rm, BLKADDR_RVUM, 0, RVU_PF_VFPF_MBOX_INT_ENA_W1CX(0), + GENMASK_ULL(ena_bits, 0)); + + if (rm->num_vfs > 64) { /* For VF 64 to 127(MAX) */ + /* Enable for VF interrupts for VFs 64 to 128 */ + ena_bits = rm->num_vfs - 64 - 1; + rm_write64(rm, BLKADDR_RVUM, 0, + RVU_PF_VFPF_MBOX_INT_ENA_W1CX(1), + GENMASK_ULL(ena_bits, 0)); + } +} + +static int __sriov_disable(struct pci_dev *pdev) +{ + struct rm_dev *rm; + + rm = pci_get_drvdata(pdev); + if (pci_vfs_assigned(pdev)) { + dev_err(&pdev->dev, "Disabing VFs while VFs are assigned\n"); + dev_err(&pdev->dev, "VFs will not be freed\n"); + return -EPERM; + } + + disable_vf_flr_int(pdev); + disable_vf_mbox_int(pdev); + +#ifdef CONFIG_OCTEONTX2_RM_DOM_SYSFS + domain_sysfs_destroy(rm); +#endif + vf_sysfs_destroy(pdev); + + if (rm->pfvf_mbox_wq) { + flush_workqueue(rm->pfvf_mbox_wq); + destroy_workqueue(rm->pfvf_mbox_wq); + rm->pfvf_mbox_wq = NULL; + } + if (rm->pfvf_mbx_base) { + iounmap(rm->pfvf_mbx_base); + rm->pfvf_mbx_base = NULL; + } + + otx2_mbox_destroy(&rm->pfvf_mbox); + otx2_mbox_destroy(&rm->pfvf_mbox_up); + + pci_disable_sriov(pdev); + + kfree(rm->vf_info); + rm->vf_info = NULL; + + return 0; +} + +static int __sriov_enable(struct pci_dev *pdev, int num_vfs) +{ + int curr_vfs, vf = 0; + int err; + struct rm_dev *rm; + struct rvu_vf *vf_ptr; + u64 pf_vf_mbox_base; + + curr_vfs = pci_num_vf(pdev); + if (!curr_vfs && !num_vfs) + return -EINVAL; + + if (curr_vfs) { + dev_err( + &pdev->dev, + "Virtual Functions are already enabled on this device\n"); + return -EINVAL; + } + if (num_vfs > RM_MAX_VFS) + num_vfs = RM_MAX_VFS; + + rm = pci_get_drvdata(pdev); + + if (rm_get_available_rsrcs(rm)) { + dev_err(&pdev->dev, "Failed to get resource limits.\n"); + return -EFAULT; + } + + rm->vf_info = kcalloc(num_vfs, sizeof(struct rvu_vf), GFP_KERNEL); + if (rm->vf_info == NULL) + return -ENOMEM; + + err = pci_enable_sriov(pdev, num_vfs); + if (err) { + dev_err(&pdev->dev, "Failed to enable to SRIOV VFs: %d\n", err); + goto err_enable_sriov; + } + + rm->num_vfs = num_vfs; + + /* Map PF-VF mailbox memory */ + pf_vf_mbox_base = (u64)rm->bar2 + RVU_PF_VF_BAR4_ADDR; + pf_vf_mbox_base = readq((void __iomem *)(unsigned long)pf_vf_mbox_base); + if (!pf_vf_mbox_base) { + dev_err(&pdev->dev, "PF-VF Mailbox address not configured\n"); + err = -ENOMEM; + goto err_mbox_mem_map; + } + rm->pfvf_mbx_base = ioremap_wc(pf_vf_mbox_base, MBOX_SIZE * num_vfs); + if (!rm->pfvf_mbx_base) { + dev_err(&pdev->dev, + "Mapping of PF-VF mailbox address failed\n"); + err = -ENOMEM; + goto err_mbox_mem_map; + } + err = otx2_mbox_init(&rm->pfvf_mbox, rm->pfvf_mbx_base, pdev, rm->bar2, + MBOX_DIR_PFVF, num_vfs); + if (err) { + dev_err(&pdev->dev, + "Failed to initialize PF/VF MBOX for %d VFs\n", + num_vfs); + goto err_mbox_init; + } + + err = otx2_mbox_init(&rm->pfvf_mbox_up, rm->pfvf_mbx_base, pdev, + rm->bar2, MBOX_DIR_PFVF_UP, num_vfs); + if (err) { + dev_err(&pdev->dev, + "Failed to initialize PF/VF MBOX UP for %d VFs\n", + num_vfs); + goto err_mbox_up_init; + } + + /* Allocate a single workqueue for VF/PF mailbox because access to + * AF/PF mailbox has to be synchronized. + */ + rm->pfvf_mbox_wq = + alloc_workqueue("rm_pfvf_mailbox", + WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM, 1); + if (rm->pfvf_mbox_wq == NULL) { + dev_err(&pdev->dev, + "Workqueue allocation failed for PF-VF MBOX\n"); + err = -ENOMEM; + goto err_workqueue_alloc; + } + + for (vf = 0; vf < num_vfs; vf++) { + vf_ptr = &rm->vf_info[vf]; + vf_ptr->vf_id = vf; + vf_ptr->rm = (void *)rm; + vf_ptr->intr_idx = vf % 64; + INIT_WORK(&vf_ptr->mbox_wrk, rm_pfvf_mbox_handler); + INIT_WORK(&vf_ptr->mbox_wrk_up, rm_pfvf_mbox_handler_up); + INIT_WORK(&vf_ptr->pfvf_flr_work, rm_pfvf_flr_handler); + } + + err = vf_sysfs_create(pdev); + if (err) { + dev_err(&pdev->dev, + "Failed to initialize VF sysfs entries. Err=%d\n", + err); + err = -EFAULT; + goto err_vf_sysfs_create; + } + +#ifdef CONFIG_OCTEONTX2_RM_DOM_SYSFS + err = domain_sysfs_create(rm); + if (err) { + dev_err(&pdev->dev, "Failed to create RM domain sysfs\n"); + err = -EFAULT; + goto err_domain_sysfs_create; + } +#endif + + enable_vf_mbox_int(pdev); + enable_vf_flr_int(pdev); + return num_vfs; + +#ifdef CONFIG_OCTEONTX2_RM_DOM_SYSFS +err_domain_sysfs_create: + vf_sysfs_destroy(pdev); +#endif +err_vf_sysfs_create: +err_workqueue_alloc: + destroy_workqueue(rm->pfvf_mbox_wq); + if (rm->pfvf_mbox_up.dev != NULL) + otx2_mbox_destroy(&rm->pfvf_mbox_up); +err_mbox_up_init: + if (rm->pfvf_mbox.dev != NULL) + otx2_mbox_destroy(&rm->pfvf_mbox); +err_mbox_init: + iounmap(rm->pfvf_mbx_base); +err_mbox_mem_map: + pci_disable_sriov(pdev); +err_enable_sriov: + kfree(rm->vf_info); + + return err; +} + +static int rm_sriov_configure(struct pci_dev *pdev, int num_vfs) +{ + if (num_vfs == 0) + return __sriov_disable(pdev); + else + return __sriov_enable(pdev, num_vfs); +} + +static void rm_remove(struct pci_dev *pdev) +{ + struct rm_dev *rm = pci_get_drvdata(pdev); + + spin_lock(&rm_lst_lock); + list_del(&rm->list); + spin_unlock(&rm_lst_lock); + + if (rm->num_vfs) + __sriov_disable(pdev); + + disable_af_mbox_int(pdev); + rm_free_flr_irq(pdev); + rm_afpf_mbox_term(pdev); + rm_free_irqs(pdev); + + if (rm->af_mbx_base) + iounmap(rm->af_mbx_base); + if (rm->bar2) + pcim_iounmap(pdev, rm->bar2); + + pci_release_regions(pdev); + pci_disable_device(pdev); + devm_kfree(&pdev->dev, rm); +} + +static struct pci_driver rm_driver = { + .name = DRV_NAME, + .id_table = rvu_rm_id_table, + .probe = rm_probe, + .remove = rm_remove, + .sriov_configure = rm_sriov_configure, +}; + +static int __init otx2_rm_init_module(void) +{ + pr_info("%s\n", DRV_NAME); + + spin_lock_init(&rm_lst_lock); + return pci_register_driver(&rm_driver); +} + +static void __exit otx2_rm_exit_module(void) +{ + pci_unregister_driver(&rm_driver); +} + +module_init(otx2_rm_init_module); +module_exit(otx2_rm_exit_module); diff --git a/drivers/soc/marvell/octeontx2-rm/otx2_rm.h b/drivers/soc/marvell/octeontx2-rm/otx2_rm.h new file mode 100644 index 000000000000..2375e84c1198 --- /dev/null +++ b/drivers/soc/marvell/octeontx2-rm/otx2_rm.h @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OcteonTX2 RVU Resource Manager driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ +#ifndef RM_H_ +#define RM_H_ + +#include <linux/device.h> +#include <linux/workqueue.h> +#include <linux/pci.h> +#include "mbox.h" +#include "quota.h" + +#define MAX_DOM_VFS 8 +#define RM_MAX_VFS 128 +/* 12 CGX PFs + max HWVFs - VFs used for domains */ +#define RM_MAX_PORTS (12 + 256 - MAX_DOM_VFS) +#define NAME_SIZE 32 + +#define RVU_PFVF_PF_SHIFT 10 +#define RVU_PFVF_PF_MASK 0x3F +#define RVU_PFVF_FUNC_SHIFT 0 +#define RVU_PFVF_FUNC_MASK 0x3FF + +#define RVU_PFFUNC(pf, func) \ + ((((pf) & RVU_PFVF_PF_MASK) << RVU_PFVF_PF_SHIFT) | \ + (((func) & RVU_PFVF_FUNC_MASK) << RVU_PFVF_FUNC_SHIFT)) + +/* PCI device IDs */ +#define PCI_DEVID_OCTEONTX2_RVU_PF 0xA063 +#define PCI_DEVID_OCTEONTX2_PASS1_RVU_PF 0x0063 /* Errata */ +#define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8 +#define PCI_DEVID_OCTEONTX2_PASS1_RVU_AFVF 0x00F8 +#define PCI_DEVID_OCTEONTX2_RVU_VF 0xA064 +#define PCI_DEVID_OCTEONTX2_PASS1_RVU_VF 0xA064 + +struct rm_dev; + +struct rvu_vf { + struct work_struct mbox_wrk; + struct work_struct mbox_wrk_up; + struct work_struct pfvf_flr_work; + struct device_attribute in_use_attr; + struct pci_dev *pdev; + struct kobject *limits_kobj; + /* pointer to PF struct this PF belongs to */ + struct rm_dev *rm; + int vf_id; + int intr_idx; /* vf_id%64 actually */ + bool in_use; + bool got_flr; +}; + +struct rvu_limits { + struct quotas *sso; + struct quotas *ssow; + struct quotas *npa; + struct quotas *tim; + struct quotas *cpt; +}; + +struct rm_dev { + struct list_head list; + struct mutex lock; + struct pci_dev *pdev; + void __iomem *bar2; + void __iomem *af_mbx_base; + void __iomem *pfvf_mbx_base; +#define RM_VF_ENABLED 0x1 + u32 flags; + u32 num_vfs; + bool *irq_allocated; + char *irq_names; + int msix_count; + int pf; + + struct otx2_mbox pfvf_mbox; /* MBOXes for VF => PF channel */ + struct otx2_mbox pfvf_mbox_up; /* MBOXes for PF => VF channel */ + struct otx2_mbox afpf_mbox; /* MBOX for PF => AF channel */ + struct otx2_mbox afpf_mbox_up; /* MBOX for AF => PF channel */ + struct work_struct mbox_wrk; + struct work_struct mbox_wrk_up; + struct workqueue_struct *afpf_mbox_wq; /* MBOX handler */ + struct workqueue_struct *pfvf_mbox_wq; /* VF MBOX handler */ + struct rvu_vf *vf_info; + struct free_rsrcs_rsp limits; /* Maximum limits for all VFs */ + struct rvu_limits vf_limits; /* Limits for each VF */ +}; + +#endif /* RM_H_ */ diff --git a/drivers/soc/marvell/octeontx2-rm/quota.c b/drivers/soc/marvell/octeontx2-rm/quota.c new file mode 100644 index 000000000000..361b903cf86c --- /dev/null +++ b/drivers/soc/marvell/octeontx2-rm/quota.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OcteonTX2 RVU Resource Manager driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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/pci.h> +#include <linux/sysfs.h> + +#include "quota.h" + +static ssize_t quota_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct quota *quota; + int val; + + quota = container_of(attr, struct quota, sysfs); + + if (quota->base->lock) + mutex_lock(quota->base->lock); + val = quota->val; + if (quota->base->lock) + mutex_unlock(quota->base->lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t quota_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct quota *quota; + struct quotas *base; + struct device *dev; + int old_val, new_val, res = 0; + u64 lf_sum; + + quota = container_of(attr, struct quota, sysfs); + dev = quota->dev; + base = quota->base; + + if (kstrtoint(buf, 0, &new_val)) { + dev_err(dev, "Invalid %s quota: %s\n", attr->attr.name, buf); + return -EIO; + } + if (new_val < 0) { + dev_err(dev, "Invalid %s quota: %d < 0\n", attr->attr.name, + new_val); + return -EIO; + } + + if (new_val > base->max) { + dev_err(dev, "Invalid %s quota: %d > %d\n", attr->attr.name, + new_val, base->max); + return -EIO; + } + + if (base->lock) + mutex_lock(base->lock); + old_val = quota->val; + + if (base->ops.pre_store) + res = base->ops.pre_store(quota->ops_arg, quota, new_val); + + if (res != 0) { + res = -EIO; + goto unlock; + } + + lf_sum = quotas_get_sum(quota->base); + + if (lf_sum + new_val - quota->val > base->max_sum) { + dev_err(dev, + "Not enough resources for %s quota. Used: %lld, Max: %lld\n", + attr->attr.name, lf_sum, base->max_sum); + res = -EIO; + goto unlock; + } + quota->val = new_val; + + if (base->ops.post_store) + base->ops.post_store(quota->ops_arg, quota, old_val); + + res = count; + +unlock: + if (base->lock) + mutex_unlock(base->lock); + return res; +} + + +struct quotas *quotas_alloc(u32 cnt, u32 max, u64 max_sum, + int init_val, struct mutex *lock, + struct quota_ops *ops) +{ + struct quotas *quotas; + u64 i; + + if (cnt == 0) + return NULL; + + quotas = kzalloc(sizeof(struct quotas) + cnt * sizeof(struct quota), + GFP_KERNEL); + if (quotas == NULL) + return NULL; + + for (i = 0; i < cnt; i++) { + quotas->a[i].base = quotas; + quotas->a[i].val = init_val; + } + + quotas->cnt = cnt; + quotas->max = max; + quotas->max_sum = max_sum; + if (ops) { + quotas->ops.pre_store = ops->pre_store; + quotas->ops.post_store = ops->post_store; + } + quotas->lock = lock; + + return quotas; +} + +void quotas_free(struct quotas *quotas) +{ + u64 i; + + if (quotas == NULL) + return; + WARN_ON(quotas->cnt == 0); + + for (i = 0; i < quotas->cnt; i++) + quota_sysfs_destroy("as->a[i]); + + kfree(quotas); +} + +int quota_sysfs_create(const char *name, struct kobject *parent, + struct device *log_dev, struct quota *quota, + void *ops_arg) +{ + int err; + + if (name == NULL || quota == NULL || log_dev == NULL) + return -EINVAL; + + quota->sysfs.show = quota_show; + quota->sysfs.store = quota_store; + quota->sysfs.attr.name = name; + quota->sysfs.attr.mode = 0644; + quota->parent = parent; + quota->dev = log_dev; + quota->ops_arg = ops_arg; + + sysfs_attr_init("a->sysfs.attr); + err = sysfs_create_file(quota->parent, "a->sysfs.attr); + if (err) { + dev_err(quota->dev, + "Failed to create '%s' quota sysfs for '%s'\n", + name, kobject_name(quota->parent)); + return -EFAULT; + } + + return 0; +} + +int quota_sysfs_destroy(struct quota *quota) +{ + if (quota == NULL) + return -EINVAL; + if (quota->sysfs.attr.mode != 0) { + sysfs_remove_file(quota->parent, "a->sysfs.attr); + quota->sysfs.attr.mode = 0; + } + return 0; +} + +u64 quotas_get_sum(struct quotas *quotas) +{ + u64 lf_sum = 0; + int i; + + for (i = 0; i < quotas->cnt; i++) + lf_sum += quotas->a[i].val; + + return lf_sum; +} + diff --git a/drivers/soc/marvell/octeontx2-rm/quota.h b/drivers/soc/marvell/octeontx2-rm/quota.h new file mode 100644 index 000000000000..84b12f952f4c --- /dev/null +++ b/drivers/soc/marvell/octeontx2-rm/quota.h @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OcteonTX2 RVU Resource Manager driver + * + * Copyright (C) 2018 Marvell International Ltd. + * + * 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. + */ +#ifndef QUOTA_H_ +#define QUOTA_H_ + +#include <linux/kobject.h> +#include <linux/mutex.h> + +struct quotas; + +struct quota { + struct kobj_attribute sysfs; + /* Device to scope logs to */ + struct device *dev; + /* Kobject of the sysfs file */ + struct kobject *parent; + /* Pointer to base structure */ + struct quotas *base; + /* Argument passed to the quota_ops when this quota is modified */ + void *ops_arg; + /* Value of the quota */ + int val; +}; + +struct quota_ops { + /** + * Called before sysfs store(). store() will proceed if returns 0. + * It is called with struct quotas::lock taken. + */ + int (*pre_store)(void *arg, struct quota *quota, int new_val); + /** called after sysfs store(). */ + void (*post_store)(void *arg, struct quota *quota, int old_val); +}; + +struct quotas { + struct quota_ops ops; + struct mutex *lock; /* lock taken for each sysfs operation */ + u32 cnt; /* number of elements in arr */ + u32 max; /* maximum value for a single quota */ + u64 max_sum; /* maximum sum of all quotas */ + struct quota a[0]; /* array of quota assignments */ +}; + +/** + * Allocate and setup quotas structure. + * + * @p cnt number of quotas to allocate + * @p max maximum value of a single quota + * @p max_sum maximum sum of all quotas + * @p init_val initial value set to all quotas + * @p ops callbacks for sysfs manipulation notifications + */ +struct quotas *quotas_alloc(u32 cnt, u32 max, u64 max_sum, + int init_val, struct mutex *lock, + struct quota_ops *ops); +/** + * Frees quota array and any sysfs entries associated with it. + */ +void quotas_free(struct quotas *quotas); + +/** + * Create a sysfs entry controling given quota entry. + * + * File created under parent will read the current value of the quota and + * write will take quotas lock and check if new value does not exceed + * configured maximum values. + * + * @return 0 if succeeded, negative error code otherwise. + */ +int quota_sysfs_create(const char *name, struct kobject *parent, + struct device *log_dev, struct quota *quota, + void *ops_arg); +/** + * Remove sysfs entry for a given quota if it was created. + */ +int quota_sysfs_destroy(struct quota *quota); + +/** + * Return current sum of values for current quota. + */ +u64 quotas_get_sum(struct quotas *quotas); + +#endif /* QUOTA_H_ */ diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 30a40280c157..d09d94a4f5a3 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -469,6 +469,15 @@ config SPI_OCTEON SPI host driver for the hardware found on some Cavium OCTEON SOCs. +config SPI_OCTEONTX2 + tristate "Marvell OcteonTX2 SPI controller" + depends on PCI && 64BIT && (ARM64 || COMPILE_TEST) + help + This driver supports the OcteonTX2 SPI controller in master + mode. It supports single, dual and quad mode transfers. + This controller hardware is found on some of Marvell + OcteonTX2 SoCs. + config SPI_OMAP_UWIRE tristate "OMAP1 MicroWire" depends on ARCH_OMAP1 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index f2f78d03dc28..e63c20791615 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_SPI_NXP_FLEXSPI) += spi-nxp-fspi.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o spi-octeon-objs := spi-cavium.o spi-cavium-octeon.o obj-$(CONFIG_SPI_OCTEON) += spi-octeon.o +obj-$(CONFIG_SPI_OCTEONTX2) += spi-octeontx2.o obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o diff --git a/drivers/spi/spi-cavium-thunderx.c b/drivers/spi/spi-cavium-thunderx.c index d12e149f1a41..ff8b71163569 100644 --- a/drivers/spi/spi-cavium-thunderx.c +++ b/drivers/spi/spi-cavium-thunderx.c @@ -16,6 +16,11 @@ #define SYS_FREQ_DEFAULT 700000000 /* 700 Mhz */ +#define PCI_DEVICE_ID_THUNDER_SPI 0xA00B +#define PCI_SUBSYS_DEVID_88XX_SPI 0xA10B +#define PCI_SUBSYS_DEVID_81XX_SPI 0xA20B +#define PCI_SUBSYS_DEVID_83XX_SPI 0xA30B + static int thunderx_spi_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -101,7 +106,18 @@ static void thunderx_spi_remove(struct pci_dev *pdev) } static const struct pci_device_id thunderx_spi_pci_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa00b) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, + PCI_DEVICE_ID_THUNDER_SPI, + PCI_VENDOR_ID_CAVIUM, + PCI_SUBSYS_DEVID_88XX_SPI) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, + PCI_DEVICE_ID_THUNDER_SPI, + PCI_VENDOR_ID_CAVIUM, + PCI_SUBSYS_DEVID_81XX_SPI) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, + PCI_DEVICE_ID_THUNDER_SPI, + PCI_VENDOR_ID_CAVIUM, + PCI_SUBSYS_DEVID_83XX_SPI) }, { 0, } }; diff --git a/drivers/spi/spi-octeontx2.c b/drivers/spi/spi-octeontx2.c new file mode 100644 index 000000000000..ead6c234f9e2 --- /dev/null +++ b/drivers/spi/spi-octeontx2.c @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Marvell OcteonTX2 SPI driver. + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include <linux/of.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/spi/spi.h> + +#include "spi-octeontx2.h" + +#define DRV_NAME "spi-octeontx2" + +#define TBI_FREQ 100000000 /* 100 Mhz */ +#define SYS_FREQ_DEFAULT 700000000 /* 700 Mhz */ + +static int tbi_clk_en = 1; +module_param(tbi_clk_en, uint, 0644); +MODULE_PARM_DESC(tbi_clk_en, + "Use Fixed Time Base 100MHz Reference Clock (0=Disable, 1=Enable [default])"); + +static int cfg_mode_delay = 30; +module_param(cfg_mode_delay, uint, 0644); +MODULE_PARM_DESC(cfg_mode_delay, + "Delay in micro-seconds for mode change in MPI CFG register (30 [default])"); + +static void octeontx2_spi_wait_ready(struct octeontx2_spi *p) +{ + union mpix_sts mpi_sts; + unsigned int loops = 0; + + do { + if (loops++) + __delay(500); + mpi_sts.u64 = readq(p->register_base + OCTEONTX2_SPI_STS(p)); + } while (mpi_sts.s.busy); +} + +static int octeontx2_spi_do_transfer(struct octeontx2_spi *p, + struct spi_message *msg, + struct spi_transfer *xfer, + bool last_xfer, + int cs) +{ + struct spi_device *spi = msg->spi; + union mpix_cfg mpi_cfg; + union mpix_xmit mpi_xmit; + unsigned int clkdiv, calc_spd; + int mode; + bool cpha, cpol; + const u8 *tx_buf; + u8 *rx_buf; + int len, rem; + int i; + void __iomem *wbuf_ptr = p->register_base + OCTEONTX2_SPI_WBUF(p); + + mode = spi->mode; + cpha = mode & SPI_CPHA; + cpol = mode & SPI_CPOL; + + clkdiv = p->sys_freq / (2 * xfer->speed_hz); + /* Perform check to not exceed requested speed */ + while (1) { + calc_spd = p->sys_freq / (2 * clkdiv); + if (calc_spd <= xfer->speed_hz) + break; + clkdiv += 1; + } + + if ((clkdiv > 8191) || (!tbi_clk_en && (clkdiv == 1))) { + dev_err(&spi->dev, + "can't support xfer->speed_hz %d for reference clock %d\n", + xfer->speed_hz, p->sys_freq); + return -EINVAL; + } + + mpi_cfg.u64 = 0; + + mpi_cfg.s.clkdiv = clkdiv; + mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0; + mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0; + mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0; + mpi_cfg.s.idlelo = cpha != cpol; + mpi_cfg.s.cslate = cpha ? 1 : 0; + mpi_cfg.s.tritx = 1; + mpi_cfg.s.enable = 1; + mpi_cfg.s.cs_sticky = 1; + mpi_cfg.s.legacy_dis = 1; + if (tbi_clk_en) + mpi_cfg.s.tb100_en = 1; + + /* Set x1 mode as default */ + mpi_cfg.s.iomode = 0; + /* Set x2 mode if either tx or rx request dual */ + if ((xfer->tx_nbits == SPI_NBITS_DUAL) || + (xfer->rx_nbits == SPI_NBITS_DUAL)) + mpi_cfg.s.iomode = 2; + /* Set x4 mode if either tx or rx request quad */ + if ((xfer->tx_nbits == SPI_NBITS_QUAD) || + (xfer->rx_nbits == SPI_NBITS_QUAD)) + mpi_cfg.s.iomode = 3; + + p->cs_enax |= (0xFull << 12); + mpi_cfg.u64 |= p->cs_enax; + + if (mpi_cfg.u64 != p->last_cfg) { + p->last_cfg = mpi_cfg.u64; + writeq(mpi_cfg.u64, p->register_base + OCTEONTX2_SPI_CFG(p)); + mpi_cfg.u64 = readq(p->register_base + OCTEONTX2_SPI_CFG(p)); + udelay(cfg_mode_delay); /* allow CS change to settle */ + } + tx_buf = xfer->tx_buf; + rx_buf = xfer->rx_buf; + len = xfer->len; + + while (len > OCTEONTX2_SPI_MAX_BYTES) { + if (tx_buf) { + /* 8 bytes per iteration */ + for (i = 0; i < OCTEONTX2_SPI_MAX_BYTES / 8; i++) { + u64 data = *(uint64_t *)tx_buf; + + tx_buf += 8; + writeq(data, wbuf_ptr + (8 * i)); + } + } + mpi_xmit.u64 = 0; + mpi_xmit.s.csid = cs; + mpi_xmit.s.leavecs = 1; + mpi_xmit.s.txnum = tx_buf ? OCTEONTX2_SPI_MAX_BYTES : 0; + mpi_xmit.s.totnum = OCTEONTX2_SPI_MAX_BYTES; + writeq(mpi_xmit.u64, p->register_base + OCTEONTX2_SPI_XMIT(p)); + + octeontx2_spi_wait_ready(p); + if (rx_buf) { + /* 8 bytes per iteration */ + for (i = 0; i < OCTEONTX2_SPI_MAX_BYTES / 8; i++) { + u64 v = readq(wbuf_ptr + (8 * i)); + *(uint64_t *)rx_buf = v; + rx_buf += 8; + } + } + len -= OCTEONTX2_SPI_MAX_BYTES; + } + + rem = len % 8; + + if (tx_buf) { + u64 data; + /* 8 bytes per iteration */ + for (i = 0; i < len / 8; i++) { + data = *(uint64_t *)tx_buf; + tx_buf += 8; + writeq(data, wbuf_ptr + (8 * i)); + } + /* remaining <8 bytes */ + if (rem) { + data = 0; + memcpy(&data, tx_buf, rem); + writeq(data, wbuf_ptr + (8 * i)); + } + } + + mpi_xmit.u64 = 0; + mpi_xmit.s.csid = cs; + if (last_xfer) + mpi_xmit.s.leavecs = xfer->cs_change; + else + mpi_xmit.s.leavecs = !xfer->cs_change; + mpi_xmit.s.txnum = tx_buf ? len : 0; + mpi_xmit.s.totnum = len; + writeq(mpi_xmit.u64, p->register_base + OCTEONTX2_SPI_XMIT(p)); + + octeontx2_spi_wait_ready(p); + if (rx_buf) { + u64 v; + /* 8 bytes per iteration */ + for (i = 0; i < len / 8; i++) { + v = readq(wbuf_ptr + (8 * i)); + *(uint64_t *)rx_buf = v; + rx_buf += 8; + } + /* remaining <8 bytes */ + if (rem) { + v = readq(wbuf_ptr + (8 * i)); + memcpy(rx_buf, &v, rem); + rx_buf += rem; + } + } + + if (xfer->delay_usecs) + udelay(xfer->delay_usecs); + + return xfer->len; +} + +int octeontx2_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct octeontx2_spi *p = spi_master_get_devdata(master); + unsigned int total_len = 0; + int status = 0; + struct spi_transfer *xfer; + int cs = msg->spi->chip_select; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + bool last_xfer = list_is_last(&xfer->transfer_list, + &msg->transfers); + int r = octeontx2_spi_do_transfer(p, msg, xfer, last_xfer, cs); + + if (r < 0) { + status = r; + goto err; + } + total_len += r; + } +err: + msg->status = status; + msg->actual_length = total_len; + spi_finalize_current_message(master); + return status; +} + +static int octeontx2_spi_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + struct spi_master *master; + struct octeontx2_spi *p; + int ret = -ENOENT; + + /* may need to hunt for devtree entry */ + if (!pdev->dev.of_node) { + struct device_node *np = of_find_node_by_name(NULL, "spi"); + + if (IS_ERR(np)) { + ret = PTR_ERR(np); + goto error; + } + pdev->dev.of_node = np; + of_node_put(np); + } + + master = spi_alloc_master(dev, sizeof(struct octeontx2_spi)); + if (!master) + return -ENOMEM; + + p = spi_master_get_devdata(master); + + ret = pcim_enable_device(pdev); + if (ret) + goto error_put; + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret) + goto error_disable; + + p->register_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!p->register_base) { + ret = -EINVAL; + goto error_disable; + } + + p->regs.config = 0x1000; + p->regs.status = 0x1008; + p->regs.xmit = 0x1018; + p->regs.wbuf = 0x1800; + p->last_cfg = 0x0; + + /* FIXME: need a proper clocksource object for SCLK */ + p->clk = devm_clk_get(dev, NULL); + if (IS_ERR(p->clk)) { + p->clk = devm_clk_get(dev, "sclk"); + p->sys_freq = 0; + } else { + ret = clk_prepare_enable(p->clk); + if (!ret) + p->sys_freq = clk_get_rate(p->clk); + } + + if (!p->sys_freq) + p->sys_freq = SYS_FREQ_DEFAULT; + if (tbi_clk_en) + p->sys_freq = TBI_FREQ; + dev_info(dev, "Reference clock is %u\n", p->sys_freq); + + master->num_chipselect = 4; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | + SPI_LSB_FIRST | SPI_3WIRE | + SPI_TX_DUAL | SPI_RX_DUAL | + SPI_TX_QUAD | SPI_RX_QUAD; + master->transfer_one_message = octeontx2_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->max_speed_hz = OCTEONTX2_SPI_MAX_CLOCK_HZ; + master->dev.of_node = pdev->dev.of_node; + + pci_set_drvdata(pdev, master); + + ret = devm_spi_register_master(dev, master); + if (ret) + goto error_disable; + + return 0; + +error_disable: + clk_disable_unprepare(p->clk); +error_put: + spi_master_put(master); +error: + return ret; +} + +static void octeontx2_spi_remove(struct pci_dev *pdev) +{ + struct spi_master *master = pci_get_drvdata(pdev); + struct octeontx2_spi *p; + + p = spi_master_get_devdata(master); + + clk_disable_unprepare(p->clk); + /* Put everything in a known state. */ + if (p) + writeq(0, p->register_base + OCTEONTX2_SPI_CFG(p)); + + pci_disable_device(pdev); + spi_master_put(master); +} + +static const struct pci_device_id octeontx2_spi_pci_id_table[] = { + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, + PCI_DEVID_OCTEONTX2_SPI, + PCI_VENDOR_ID_CAVIUM, + PCI_SUBSYS_DEVID_OTX2_96XX) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, + PCI_DEVID_OCTEONTX2_SPI, + PCI_VENDOR_ID_CAVIUM, + PCI_SUBSYS_DEVID_OTX2_95XX) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, + PCI_DEVID_OCTEONTX2_SPI, + PCI_VENDOR_ID_CAVIUM, + PCI_SUBSYS_DEVID_OTX2_LOKI) }, + + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, octeontx2_spi_pci_id_table); + +static struct pci_driver octeontx2_spi_driver = { + .name = DRV_NAME, + .id_table = octeontx2_spi_pci_id_table, + .probe = octeontx2_spi_probe, + .remove = octeontx2_spi_remove, +}; + +module_pci_driver(octeontx2_spi_driver); + +MODULE_DESCRIPTION("OcteonTX2 SPI bus driver"); +MODULE_AUTHOR("Marvell Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-octeontx2.h b/drivers/spi/spi-octeontx2.h new file mode 100644 index 000000000000..84dfae89a8f8 --- /dev/null +++ b/drivers/spi/spi-octeontx2.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __SPI_OCTEONTX2_H +#define __SPI_OCTEONTX2_H + +#include <linux/clk.h> + +#define PCI_DEVID_OCTEONTX2_SPI 0xA00B +#define PCI_SUBSYS_DEVID_OTX2_96XX 0xB200 +#define PCI_SUBSYS_DEVID_OTX2_95XX 0xB300 +#define PCI_SUBSYS_DEVID_OTX2_LOKI 0xB400 + +#define OCTEONTX2_SPI_MAX_BYTES 1024 +#define OCTEONTX2_SPI_MAX_CLOCK_HZ 25000000 + +struct octeontx2_spi_regs { + int config; + int status; + int xmit; + int wbuf; +}; + +struct octeontx2_spi { + void __iomem *register_base; + u64 last_cfg; + u64 cs_enax; + int sys_freq; + struct octeontx2_spi_regs regs; + struct clk *clk; +}; + +#define OCTEONTX2_SPI_CFG(x) ((x)->regs.config) +#define OCTEONTX2_SPI_STS(x) ((x)->regs.status) +#define OCTEONTX2_SPI_XMIT(x) ((x)->regs.xmit) +#define OCTEONTX2_SPI_WBUF(x) ((x)->regs.wbuf) + +int octeontx2_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg); + + +union mpix_cfg { + uint64_t u64; + struct mpix_cfg_s { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_50_63:14; + uint64_t tb100_en:1; + uint64_t reserved_48:1; + uint64_t cs_espi_en:4; + uint64_t reserved_36_43:8; + uint64_t iomode:2; + uint64_t reserved_32_33:2; + uint64_t legacy_dis:1; + uint64_t reserved_29_30:2; + uint64_t clkdiv:13; + uint64_t csena3:1; + uint64_t csena2:1; + uint64_t csena1:1; + uint64_t csena0:1; + uint64_t cslate:1; + uint64_t tritx:1; + uint64_t idleclks:2; + uint64_t cshi:1; + uint64_t reserved_6:1; + uint64_t cs_sticky:1; + uint64_t lsbfirst:1; + uint64_t wireor:1; + uint64_t clk_cont:1; + uint64_t idlelo:1; + uint64_t enable:1; +#else + uint64_t enable:1; + uint64_t idlelo:1; + uint64_t clk_cont:1; + uint64_t wireor:1; + uint64_t lsbfirst:1; + uint64_t cs_sticky:1; + uint64_t reserved_6:1; + uint64_t cshi:1; + uint64_t idleclks:2; + uint64_t tritx:1; + uint64_t cslate:1; + uint64_t csena0:1; + uint64_t csena1:1; + uint64_t csena2:1; + uint64_t csena3:1; + uint64_t clkdiv:13; + uint64_t reserved_29_30:2; + uint64_t legacy_dis:1; + uint64_t reserved_32_33:2; + uint64_t iomode:2; + uint64_t reserved_36_43:8; + uint64_t cs_espi_en:4; + uint64_t reserved_48:1; + uint64_t tb100_en:1; + uint64_t reserved_50_63:14; +#endif + } s; +}; + +union mpix_sts { + uint64_t u64; + struct mpix_sts_s { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_40_63:24; + uint64_t crc:8; + uint64_t reserved_27_31:5; + uint64_t crc_err:1; + uint64_t reserved_19_25:7; + uint64_t rxnum:11; + uint64_t reserved_2_7:6; + uint64_t mpi_intr:1; + uint64_t busy:1; +#else + uint64_t busy:1; + uint64_t mpi_intr:1; + uint64_t reserved_2_7:6; + uint64_t rxnum:11; + uint64_t reserved_19_25:7; + uint64_t crc_err:1; + uint64_t reserved_27_31:5; + uint64_t crc:8; + uint64_t reserved_40_63:24; +#endif + } s; +}; + +union mpix_xmit { + uint64_t u64; + struct mpix_xmit_s { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_63:1; + uint64_t csid:2; + uint64_t leavecs:1; + uint64_t reserved_31_59:29; + uint64_t txnum:11; + uint64_t reserved_11_19:9; + uint64_t totnum:11; +#else + uint64_t totnum:11; + uint64_t reserved_11_19:9; + uint64_t txnum:11; + uint64_t reserved_31_59:29; + uint64_t leavecs:1; + uint64_t csid:2; + uint64_t reserved_63:1; +#endif + } s; +}; +#endif /* __SPI_OCTEONTX2_H */ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 0c9bc231107f..5ef7e9ecb650 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -305,6 +305,12 @@ extern int oops_may_print(void); void do_exit(long error_code) __noreturn; void complete_and_exit(struct completion *, long) __noreturn; +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +struct task_struct; +int task_cleanup_handler_add(void (*handler)(struct task_struct *)); +int task_cleanup_handler_remove(void (*handler)(struct task_struct *)); +#endif + #ifdef CONFIG_ARCH_HAS_REFCOUNT void refcount_error_report(struct pt_regs *regs, const char *err); #else diff --git a/kernel/exit.c b/kernel/exit.c index a75b6a7f458a..235cf6825593 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -772,6 +772,68 @@ static void check_stack_usage(void) static inline void check_stack_usage(void) {} #endif +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR +struct task_cleanup_handler { + void (*handler)(struct task_struct *); + struct list_head list; +}; + +static DEFINE_MUTEX(task_cleanup_handlers_mutex); +static LIST_HEAD(task_cleanup_handlers); + +int task_cleanup_handler_add(void (*handler)(struct task_struct *)) +{ + struct task_cleanup_handler *newhandler; + + newhandler = (struct task_cleanup_handler *) + kmalloc(sizeof(struct task_cleanup_handler), GFP_KERNEL); + if (newhandler == NULL) + return -1; + newhandler->handler = handler; + mutex_lock(&task_cleanup_handlers_mutex); + list_add(&newhandler->list, &task_cleanup_handlers); + mutex_unlock(&task_cleanup_handlers_mutex); + return 0; +} +EXPORT_SYMBOL(task_cleanup_handler_add); + +int task_cleanup_handler_remove(void (*handler)(struct task_struct *)) +{ + struct list_head *pos, *tmppos; + struct task_cleanup_handler *curr_task_cleanup_handler; + int retval = -1; + + mutex_lock(&task_cleanup_handlers_mutex); + list_for_each_safe(pos, tmppos, &task_cleanup_handlers) { + curr_task_cleanup_handler + = list_entry(pos, struct task_cleanup_handler, list); + if (curr_task_cleanup_handler->handler == handler) { + list_del(pos); + kfree(curr_task_cleanup_handler); + retval = 0; + } + } + mutex_unlock(&task_cleanup_handlers_mutex); + return retval; +} +EXPORT_SYMBOL(task_cleanup_handler_remove); + +static void task_cleanup_handlers_call(struct task_struct *task) +{ + struct list_head *pos; + struct task_cleanup_handler *curr_task_cleanup_handler; + + mutex_lock(&task_cleanup_handlers_mutex); + list_for_each(pos, &task_cleanup_handlers) { + curr_task_cleanup_handler = + list_entry(pos, struct task_cleanup_handler, list); + if (curr_task_cleanup_handler->handler != NULL) + curr_task_cleanup_handler->handler(task); + } + mutex_unlock(&task_cleanup_handlers_mutex); +} +#endif + void __noreturn do_exit(long code) { struct task_struct *tsk = current; @@ -861,6 +923,10 @@ void __noreturn do_exit(long code) tsk->exit_code = code; taskstats_exit(tsk, group_dead); +#ifdef CONFIG_MRVL_OCTEONTX_EL0_INTR + task_cleanup_handlers_call(tsk); +#endif + exit_mm(); if (group_dead) |