diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0001-arch-arm-add-NXP-RT-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0001-arch-arm-add-NXP-RT-support.patch new file mode 100644 index 000000000..1affe022b --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0001-arch-arm-add-NXP-RT-support.patch @@ -0,0 +1,4297 @@ +From f30b008a0e7277dea1c5bc16d87dfa3e868d245a Mon Sep 17 00:00:00 2001 +From: Mike Engel +Date: Fri, 23 Feb 2024 08:39:38 +0100 +Subject: [PATCH 01/10] arch:arm: add NXP RT support + +Signed-off-by: Mike Engel +--- + arch/arm/Kconfig | 5 +- + arch/arm/boot/dts/Makefile | 8 +- + arch/arm/boot/dts/imx6ul-14x14-evk-ecat.dts | 11 + + .../boot/dts/imx6ull-14x14-evk-avb-mcr.dts | 59 + + arch/arm/boot/dts/imx6ull-14x14-evk-avb.dts | 29 + + arch/arm/boot/dts/imx6ull-14x14-evk-ecat.dts | 11 + + .../arm/boot/dts/imx6ull-14x14-evk-lpuart.dts | 25 + + arch/arm/boot/dts/ls1021a-iot-bm.dts | 259 ++ + arch/arm/boot/dts/ls1021a-iot.dts | 47 + + arch/arm/boot/dts/ls1021a.dtsi | 4 +- + arch/arm/configs/imx6ullevk-plc.config | 3333 +++++++++++++++++ + arch/arm/configs/imx_avb.config | 8 + + arch/arm/configs/imx_up.config | 2 + + arch/arm/configs/imx_v6_v7_defconfig | 3 +- + arch/arm/configs/imx_v7_defconfig | 9 +- + arch/arm/configs/lsdk.config | 20 + + arch/arm/configs/multi_v7_defconfig | 2 + + arch/arm/include/asm/thread_info.h | 6 +- + arch/arm/kernel/asm-offsets.c | 5 +- + arch/arm/kernel/entry-armv.S | 19 +- + arch/arm/kernel/signal.c | 3 +- + arch/arm/kernel/smp.c | 22 + + arch/arm/mach-imx/Kconfig | 10 + + arch/arm/mm/fault.c | 6 + + 24 files changed, 3891 insertions(+), 15 deletions(-) + create mode 100644 arch/arm/boot/dts/imx6ul-14x14-evk-ecat.dts + create mode 100644 arch/arm/boot/dts/imx6ull-14x14-evk-avb-mcr.dts + create mode 100644 arch/arm/boot/dts/imx6ull-14x14-evk-avb.dts + create mode 100644 arch/arm/boot/dts/imx6ull-14x14-evk-ecat.dts + create mode 100644 arch/arm/boot/dts/imx6ull-14x14-evk-lpuart.dts + create mode 100644 arch/arm/boot/dts/ls1021a-iot-bm.dts + create mode 100644 arch/arm/configs/imx6ullevk-plc.config + create mode 100644 arch/arm/configs/imx_avb.config + create mode 100644 arch/arm/configs/imx_up.config + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index edc39ef9d6c0..6432868439a7 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -33,6 +33,7 @@ config ARM + select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT if CPU_V7 + select ARCH_SUPPORTS_ATOMIC_RMW + select ARCH_SUPPORTS_HUGETLBFS if ARM_LPAE ++ select ARCH_SUPPORTS_RT if HAVE_POSIX_CPU_TIMERS_TASK_WORK + select ARCH_USE_BUILTIN_BSWAP + select ARCH_USE_CMPXCHG_LOCKREF + select ARCH_USE_MEMTEST +@@ -71,7 +72,7 @@ config ARM + select HARDIRQS_SW_RESEND + select HAVE_ARCH_AUDITSYSCALL if AEABI && !OABI_COMPAT + select HAVE_ARCH_BITREVERSE if (CPU_32v7M || CPU_32v7) && !CPU_32v6 +- select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL && !CPU_ENDIAN_BE32 && MMU ++ select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL && !CPU_ENDIAN_BE32 && MMU && !PREEMPT_RT + select HAVE_ARCH_KFENCE if MMU && !XIP_KERNEL + select HAVE_ARCH_KGDB if !CPU_ENDIAN_BE32 && MMU + select HAVE_ARCH_KASAN if MMU && !XIP_KERNEL +@@ -115,6 +116,8 @@ config ARM + select HAVE_PERF_EVENTS + select HAVE_PERF_REGS + select HAVE_PERF_USER_STACK_DUMP ++ select HAVE_POSIX_CPU_TIMERS_TASK_WORK if !KVM ++ select HAVE_PREEMPT_LAZY + select MMU_GATHER_RCU_TABLE_FREE if SMP && ARM_LPAE + select HAVE_REGS_AND_STACK_ACCESS_API + select HAVE_RSEQ +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 92f31aa19dbc..21e6828fcea0 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -761,6 +761,7 @@ dtb-$(CONFIG_SOC_IMX6SX) += \ + imx6sx-udoo-neo-full.dtb + dtb-$(CONFIG_SOC_IMX6UL) += \ + imx6ul-14x14-evk.dtb \ ++ imx6ul-14x14-evk-ecat.dtb \ + imx6ul-14x14-evk-csi.dtb \ + imx6ul-14x14-evk-emmc.dtb \ + imx6ul-14x14-evk-btwifi.dtb \ +@@ -803,6 +804,10 @@ dtb-$(CONFIG_SOC_IMX6UL) += \ + imx6ul-ccimx6ulstarter-wb.dtb \ + imx6ul-ccimx6ulstarter-wb-mfg.dtb \ + imx6ull-14x14-evk.dtb \ ++ imx6ull-14x14-evk-ecat.dtb \ ++ imx6ull-14x14-evk-lpuart.dtb \ ++ imx6ull-14x14-evk-avb.dtb \ ++ imx6ull-14x14-evk-avb-mcr.dtb \ + imx6ull-14x14-evk-emmc.dtb \ + imx6ull-14x14-evk-btwifi.dtb \ + imx6ull-14x14-evk-gpmi-weim.dtb \ +@@ -941,7 +946,8 @@ dtb-$(CONFIG_SOC_LS1021A) += \ + ls1021a-moxa-uc-8410a.dtb \ + ls1021a-qds.dtb \ + ls1021a-tsn.dtb \ +- ls1021a-twr.dtb ++ ls1021a-twr.dtb \ ++ ls1021a-iot.dtb + dtb-$(CONFIG_SOC_VF610) += \ + vf500-colibri-eval-v3.dtb \ + vf610-bk4.dtb \ +diff --git a/arch/arm/boot/dts/imx6ul-14x14-evk-ecat.dts b/arch/arm/boot/dts/imx6ul-14x14-evk-ecat.dts +new file mode 100644 +index 000000000000..2a99ab3960f1 +--- /dev/null ++++ b/arch/arm/boot/dts/imx6ul-14x14-evk-ecat.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx6ul-14x14-evk.dts" ++ ++ ++&fec1 { ++ compatible = "fsl,imx6ul-fec-ecat", "fsl,imx6q-fec-ecat"; ++}; +diff --git a/arch/arm/boot/dts/imx6ull-14x14-evk-avb-mcr.dts b/arch/arm/boot/dts/imx6ull-14x14-evk-avb-mcr.dts +new file mode 100644 +index 000000000000..e06167bd96bc +--- /dev/null ++++ b/arch/arm/boot/dts/imx6ull-14x14-evk-avb-mcr.dts +@@ -0,0 +1,59 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++// ++// Copyright 2022 NXP ++ ++#include "imx6ull-14x14-evk-avb.dts" ++ ++/****************************************************************************************** ++* This dts adds the media clock recovery capability for AVB to the imx6ull-14x14-evk board. ++* A hardware rework is necessary for this feature: ++* -connect SD1_DATA2 and GPIO1_IO05 pads (can be done by connecting R1728 and TP2120) ++* -connect JTAG_MOD and JTAG_TMS (SAI2_MCLK) pads (can be done by connecting R1023 and ++* JTAG pin number 7) ++* ++* Warning: this will prevent using SD1 Slot. ++*******************************************************************************************/ ++ ++&iomuxc { ++ pinctrl_avb: avbgrp { ++ fsl,pins = < ++ MX6UL_PAD_SD1_DATA2__GPT2_CAPTURE1 0x110b0 ++ MX6UL_PAD_GPIO1_IO05__ENET2_1588_EVENT0_OUT 0x88 ++ MX6UL_PAD_JTAG_MOD__GPT2_CLK 0x1f0b0 ++ >; ++ }; ++}; ++ ++/* AVB MCR timer */ ++&gpt2 { ++ compatible = "fsl,avb-gpt"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_avb>; ++ prescale = <1>; ++ ++ rec-channel = <1 0 0>; /* capture channel, eth port, ENET TC id */ ++ domain = <0>; ++ ++ clocks = <&clks IMX6UL_CLK_GPT2_BUS>, <&clks IMX6UL_CLK_GPT2_SERIAL>, ++ <&clks IMX6UL_CLK_SAI2>, <&clks IMX6UL_CLK_PLL4>; ++ clock-names = "ipg", "per", "clk_in", "audio_pll"; ++ ++ status = "okay"; ++}; ++ ++/* Pin conflict on GPIO1_IO05 */ ++&usdhc1 { ++ status = "disabled"; ++}; ++ ++/* Set PERCLK parent to IPG to make sure that GPT_CLK set above is lower ++ * than 1/4 of frequency of the peripheral clock ++ * (Keep previsouly set, from included dts files, clocks in the node) ++ */ ++&clks { ++ assigned-clocks = <&clks IMX6UL_CLK_PERCLK_SEL>, ++ <&clks IMX6UL_CLK_PLL3_PFD2>, ++ <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; ++ assigned-clock-parents = <&clks IMX6UL_CLK_IPG>; ++ assigned-clock-rates = <0>, <320000000>, <786432000>; ++}; +diff --git a/arch/arm/boot/dts/imx6ull-14x14-evk-avb.dts b/arch/arm/boot/dts/imx6ull-14x14-evk-avb.dts +new file mode 100644 +index 000000000000..09af5531b3e0 +--- /dev/null ++++ b/arch/arm/boot/dts/imx6ull-14x14-evk-avb.dts +@@ -0,0 +1,29 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++// ++// Copyright 2018 NXP ++ ++#include "imx6ull-14x14-evk.dts" ++ ++/* AVB HW timer*/ ++&epit1 { ++ compatible = "fsl,avb-epit"; ++ clocks = <&clks IMX6UL_CLK_EPIT1>; ++ clock-names = "epit1"; ++ ++ status = "okay"; ++}; ++ ++&fec1 { ++ fsl,rx-phy-delay-100-ns = <670>; ++ fsl,tx-phy-delay-100-ns = <670>; ++ fsl,rx-phy-delay-1000-ns = <0>; ++ fsl,tx-phy-delay-1000-ns = <0>; ++}; ++ ++&fec2 { ++ fsl,rx-phy-delay-100-ns = <670>; ++ fsl,tx-phy-delay-100-ns = <670>; ++ fsl,rx-phy-delay-1000-ns = <0>; ++ fsl,tx-phy-delay-1000-ns = <0>; ++}; ++ +diff --git a/arch/arm/boot/dts/imx6ull-14x14-evk-ecat.dts b/arch/arm/boot/dts/imx6ull-14x14-evk-ecat.dts +new file mode 100644 +index 000000000000..67df18babf56 +--- /dev/null ++++ b/arch/arm/boot/dts/imx6ull-14x14-evk-ecat.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx6ull-14x14-evk.dts" ++ ++ ++&fec1 { ++ compatible = "fsl,imx6ul-fec-ecat", "fsl,imx6q-fec-ecat"; ++}; +diff --git a/arch/arm/boot/dts/imx6ull-14x14-evk-lpuart.dts b/arch/arm/boot/dts/imx6ull-14x14-evk-lpuart.dts +new file mode 100644 +index 000000000000..d7a378c5349c +--- /dev/null ++++ b/arch/arm/boot/dts/imx6ull-14x14-evk-lpuart.dts +@@ -0,0 +1,25 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx6ull-14x14-evk.dts" ++ ++/* Unbind lpuart2 from Bluetooth, use it for lpuart test. */ ++&uart2 { ++ /delete-node/ bluetooth; ++}; ++ ++/* ++ * uart test port1. Note: don't use Bluetooth at the sametime. ++ * J1703: Pin1--RX Pin2--TX ++ */ ++&pinctrl_uart2 { ++ fsl,pins = < ++ MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX 0x1b0b1 ++ MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX 0x1b0b1 ++ MX6UL_PAD_UART3_RX_DATA__UART2_DCE_RTS 0x1b0b1 ++ MX6UL_PAD_UART3_TX_DATA__UART2_DCE_CTS 0x1b0b1 ++ >; ++}; ++ +diff --git a/arch/arm/boot/dts/ls1021a-iot-bm.dts b/arch/arm/boot/dts/ls1021a-iot-bm.dts +new file mode 100644 +index 000000000000..3a23b97f0081 +--- /dev/null ++++ b/arch/arm/boot/dts/ls1021a-iot-bm.dts +@@ -0,0 +1,259 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * DTS file for NXP Layerscape baremetal ++ * ++ * Copyright 2018-2023 NXP ++ * ++ * Author: Changming Huang ++ * ++ */ ++ ++/dts-v1/; ++#include "ls1021a.dtsi" ++ ++/ { ++ model = "LS1021A IOT Board"; ++ ++ sys_mclk: clock-mclk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24576000>; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_3p3v: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_2p5v: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "2P5V"; ++ regulator-min-microvolt = <2500000>; ++ regulator-max-microvolt = <2500000>; ++ regulator-always-on; ++ }; ++ }; ++ ++ sound { ++ compatible = "simple-audio-card"; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,widgets = ++ "Microphone", "Microphone Jack", ++ "Headphone", "Headphone Jack", ++ "Speaker", "Speaker Ext", ++ "Line", "Line In Jack"; ++ simple-audio-card,routing = ++ "MIC_IN", "Microphone Jack", ++ "Microphone Jack", "Mic Bias", ++ "LINE_IN", "Line In Jack", ++ "Headphone Jack", "HP_OUT", ++ "Speaker Ext", "LINE_OUT"; ++ ++ simple-audio-card,cpu { ++ sound-dai = <&sai2>; ++ frame-master; ++ bitclock-master; ++ }; ++ ++ simple-audio-card,codec { ++ sound-dai = <&codec>; ++ frame-master; ++ bitclock-master; ++ }; ++ }; ++ ++ firmware { ++ optee { ++ compatible = "linaro,optee-tz"; ++ method = "smc"; ++ }; ++ }; ++}; ++ ++&enet0 { ++ tbi-handle = <&tbi1>; ++ phy-handle = <&phy1>; ++ phy-connection-type = "sgmii"; ++ status = "okay"; ++}; ++ ++&enet1 { ++ tbi-handle = <&tbi1>; ++ phy-handle = <&phy3>; ++ phy-connection-type = "sgmii"; ++ status = "okay"; ++}; ++ ++&enet2 { ++ fixed-link = <0 1 1000 0 0>; ++ phy-connection-type = "rgmii-id"; ++ status = "okay"; ++}; ++ ++&can0{ ++ status = "disabled"; ++}; ++ ++&can1{ ++ status = "disabled"; ++}; ++ ++&can2{ ++ status = "disabled"; ++}; ++ ++&can3{ ++ status = "okay"; ++}; ++ ++&esdhc{ ++ status = "okay"; ++}; ++ ++&i2c0 { ++ status = "okay"; ++ ++ max1239@35 { ++ compatible = "maxim,max1239"; ++ reg = <0x35>; ++ #io-channel-cells = <1>; ++ }; ++ ++ codec: sgtl5000@2a { ++ #sound-dai-cells=<0x0>; ++ compatible = "fsl,sgtl5000"; ++ reg = <0x2a>; ++ VDDA-supply = <®_3p3v>; ++ VDDIO-supply = <®_2p5v>; ++ clocks = <&sys_mclk 1>; ++ }; ++ ++ pca9555: pca9555@23 { ++ compatible = "nxp,pca9555"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ reg = <0x23>; ++ }; ++ ++ ina220@44 { ++ compatible = "ti,ina220"; ++ reg = <0x44>; ++ shunt-resistor = <1000>; ++ }; ++ ++ ina220@45 { ++ compatible = "ti,ina220"; ++ reg = <0x45>; ++ shunt-resistor = <1000>; ++ }; ++ ++ lm75b@48 { ++ compatible = "nxp,lm75a"; ++ reg = <0x48>; ++ }; ++ ++ adt7461a@4c { ++ compatible = "adt7461a"; ++ reg = <0x4c>; ++ }; ++ ++ hdmi: sii9022a@39 { ++ compatible = "fsl,sii902x"; ++ reg = <0x39>; ++ interrupts = ; ++ }; ++}; ++ ++&i2c1 { ++ status = "disabled"; ++}; ++ ++&ifc { ++ status = "disabled"; ++}; ++ ++&lpuart0 { ++ status = "okay"; ++}; ++ ++&mdio0 { ++ phy0: ethernet-phy@0 { ++ reg = <0x0>; ++ }; ++ phy1: ethernet-phy@1 { ++ reg = <0x1>; ++ }; ++ phy2: ethernet-phy@2 { ++ reg = <0x2>; ++ }; ++ phy3: ethernet-phy@3 { ++ reg = <0x3>; ++ }; ++ tbi1: tbi-phy@1f { ++ reg = <0x1f>; ++ device_type = "tbi-phy"; ++ }; ++}; ++ ++&sai2 { ++ status = "okay"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&uart1 { ++ status = "okay"; ++}; ++ ++&dcu { ++ display = <&display>; ++ status = "okay"; ++ ++ display: display@0 { ++ bits-per-pixel = <24>; ++ ++ display-timings { ++ native-mode = <&timing0>; ++ ++ timing0: mode0 { ++ clock-frequency = <25000000>; ++ hactive = <640>; ++ vactive = <480>; ++ hback-porch = <80>; ++ hfront-porch = <80>; ++ vback-porch = <16>; ++ vfront-porch = <16>; ++ hsync-len = <12>; ++ vsync-len = <2>; ++ hsync-active = <1>; ++ vsync-active = <1>; ++ }; ++ }; ++ }; ++}; ++ ++&usb3 { ++ status = "disabled"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&pcie2 { ++ status = "disabled"; ++}; +diff --git a/arch/arm/boot/dts/ls1021a-iot.dts b/arch/arm/boot/dts/ls1021a-iot.dts +index ce8e26d7791f..fa65c467cfd0 100644 +--- a/arch/arm/boot/dts/ls1021a-iot.dts ++++ b/arch/arm/boot/dts/ls1021a-iot.dts +@@ -129,6 +129,26 @@ &esdhc{ + status = "okay"; + }; + ++&can0{ ++ status = "disabled"; ++}; ++ ++&can1{ ++ status = "disabled"; ++}; ++ ++&can2{ ++ status = "disabled"; ++}; ++ ++&can3{ ++ status = "okay"; ++}; ++ ++&esdhc{ ++ status = "okay"; ++}; ++ + &i2c0 { + status = "okay"; + +@@ -225,3 +245,30 @@ &uart0 { + &uart1 { + status = "okay"; + }; ++ ++&dcu { ++ display = <&display>; ++ status = "okay"; ++ ++ display: display@0 { ++ bits-per-pixel = <24>; ++ ++ display-timings { ++ native-mode = <&timing0>; ++ ++ timing0: mode0 { ++ clock-frequency = <25000000>; ++ hactive = <640>; ++ vactive = <480>; ++ hback-porch = <80>; ++ hfront-porch = <80>; ++ vback-porch = <16>; ++ vfront-porch = <16>; ++ hsync-len = <12>; ++ vsync-len = <2>; ++ hsync-active = <1>; ++ vsync-active = <1>; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi +index db945475918d..541abf48b90d 100644 +--- a/arch/arm/boot/dts/ls1021a.dtsi ++++ b/arch/arm/boot/dts/ls1021a.dtsi +@@ -815,7 +815,7 @@ usb3: usb@3100000 { + snps,host-vbus-glitches; + }; + +- pcie@3400000 { ++ pcie1: pcie@3400000 { + compatible = "fsl,ls1021a-pcie"; + reg = <0x00 0x03400000 0x0 0x00010000>, /* controller registers */ + <0x40 0x00000000 0x0 0x00002000>; /* configuration space */ +@@ -841,7 +841,7 @@ pcie@3400000 { + status = "disabled"; + }; + +- pcie@3500000 { ++ pcie2: pcie@3500000 { + compatible = "fsl,ls1021a-pcie"; + reg = <0x00 0x03500000 0x0 0x00010000>, /* controller registers */ + <0x48 0x00000000 0x0 0x00002000>; /* configuration space */ +diff --git a/arch/arm/configs/imx6ullevk-plc.config b/arch/arm/configs/imx6ullevk-plc.config +new file mode 100644 +index 000000000000..7a309a93b547 +--- /dev/null ++++ b/arch/arm/configs/imx6ullevk-plc.config +@@ -0,0 +1,3333 @@ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++# CONFIG_COMPILE_TEST is not set ++# CONFIG_WERROR is not set ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_BUILD_SALT="" ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++# CONFIG_KERNEL_GZIP is not set ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++CONFIG_KERNEL_LZO=y ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_INIT="" ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_POSIX_MQUEUE_SYSCTL=y ++# CONFIG_WATCH_QUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_USELIB is not set ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_GENERIC_IRQ_SHOW_LEVEL=y ++CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_GENERIC_IRQ_CHIP=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_IRQ_DOMAIN_HIERARCHY=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++# end of IRQ subsystem ++ ++CONFIG_GENERIC_IRQ_MULTI_HANDLER=y ++CONFIG_GENERIC_TIME_VSYSCALL=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK=y ++CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++# CONFIG_NO_HZ is not set ++CONFIG_HIGH_RES_TIMERS=y ++# end of Timers subsystem ++ ++CONFIG_BPF=y ++CONFIG_HAVE_EBPF_JIT=y ++ ++# ++# BPF subsystem ++# ++# CONFIG_BPF_SYSCALL is not set ++# CONFIG_BPF_JIT is not set ++# end of BPF subsystem ++ ++CONFIG_HAVE_PREEMPT_LAZY=y ++CONFIG_PREEMPT_LAZY=y ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_PREEMPT_RT=y ++CONFIG_PREEMPT_COUNT=y ++CONFIG_PREEMPTION=y ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_PSI is not set ++# end of CPU/Task time and stats accounting ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++CONFIG_PREEMPT_RCU=y ++# CONFIG_RCU_EXPERT is not set ++CONFIG_SRCU=y ++CONFIG_TREE_SRCU=y ++CONFIG_TASKS_RCU_GENERIC=y ++CONFIG_TASKS_RCU=y ++CONFIG_RCU_STALL_COMMON=y ++CONFIG_RCU_NEED_SEGCBLIST=y ++CONFIG_RCU_BOOST=y ++CONFIG_RCU_BOOST_DELAY=500 ++# end of RCU Subsystem ++ ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++# CONFIG_IKHEADERS is not set ++CONFIG_LOG_BUF_SHIFT=18 ++CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13 ++CONFIG_GENERIC_SCHED_CLOCK=y ++ ++# ++# Scheduler features ++# ++# end of Scheduler features ++ ++# CONFIG_CGROUPS is not set ++CONFIG_NAMESPACES=y ++# CONFIG_UTS_NS is not set ++# CONFIG_IPC_NS is not set ++# CONFIG_USER_NS is not set ++# CONFIG_PID_NS is not set ++# CONFIG_NET_NS is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++CONFIG_RELAY=y ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_BOOT_CONFIG is not set ++CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_LD_ORPHAN_WARN=y ++CONFIG_SYSCTL=y ++CONFIG_HAVE_UID16=y ++CONFIG_EXPERT=y ++CONFIG_UID16=y ++CONFIG_MULTIUSER=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++CONFIG_FHANDLE=y ++CONFIG_POSIX_TIMERS=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_FUTEX_PI=y ++CONFIG_HAVE_FUTEX_CMPXCHG=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_IO_URING=y ++CONFIG_ADVISE_SYSCALLS=y ++CONFIG_MEMBARRIER=y ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_KALLSYMS_BASE_RELATIVE=y ++# CONFIG_USERFAULTFD is not set ++CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y ++CONFIG_KCMP=y ++CONFIG_RSEQ=y ++# CONFIG_DEBUG_RSEQ is not set ++CONFIG_EMBEDDED=y ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++# CONFIG_PC104 is not set ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# end of Kernel Performance Events And Counters ++ ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++CONFIG_SLUB=y ++# CONFIG_SLAB_MERGE_DEFAULT is not set ++# CONFIG_SLAB_FREELIST_RANDOM is not set ++# CONFIG_SLAB_FREELIST_HARDENED is not set ++# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set ++# CONFIG_PROFILING is not set ++# end of General setup ++ ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_FIX_EARLYCON_MEM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_PGTABLE_LEVELS=2 ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MMAP_RND_BITS_MIN=8 ++CONFIG_ARCH_MMAP_RND_BITS_MAX=15 ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# end of Multiple platform selection ++ ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_ACTIONS is not set ++# CONFIG_ARCH_ALPINE is not set ++# CONFIG_ARCH_ARTPEC is not set ++# CONFIG_ARCH_ASPEED is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_DIGICOLOR is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_HISI is not set ++CONFIG_ARCH_MXC=y ++CONFIG_HAVE_IMX_ANATOP=y ++CONFIG_HAVE_IMX_GPC=y ++CONFIG_HAVE_IMX_GPCV2=y ++CONFIG_HAVE_IMX_MMDC=y ++CONFIG_HAVE_IMX_AMP=y ++CONFIG_HAVE_IMX_DDRC=y ++CONFIG_HAVE_IMX_BUSFREQ=y ++CONFIG_HAVE_IMX_MU=y ++CONFIG_HAVE_IMX_RPMSG=y ++CONFIG_HAVE_IMX_SRC=y ++ ++# ++# Cortex-A platforms ++# ++# CONFIG_SOC_IMX50 is not set ++# CONFIG_SOC_IMX51 is not set ++# CONFIG_SOC_IMX53 is not set ++CONFIG_SOC_IMX6=y ++CONFIG_SOC_IMX6Q=y ++CONFIG_SOC_IMX6SL=y ++CONFIG_SOC_IMX6SLL=y ++CONFIG_SOC_IMX6SX=y ++CONFIG_SOC_IMX6UL=y ++# CONFIG_SOC_LS1021A is not set ++# CONFIG_SOC_IMX6Q_BAREMETAL is not set ++ ++# ++# Cortex-A/Cortex-M asymmetric multiprocessing platforms ++# ++CONFIG_SOC_IMX7D_CA7=y ++CONFIG_SOC_IMX7D=y ++# CONFIG_SOC_IMX7ULP is not set ++# CONFIG_SOC_VF610 is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MEDIATEK is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MILBEAUT is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_MSTARV7 is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_NPCM is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# end of TI OMAP/AM/DM/DRA Family ++ ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_RDA is not set ++# CONFIG_ARCH_REALTEK is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_RENESAS is not set ++# CONFIG_ARCH_INTEL_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_STM32 is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_UNIPHIER is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_THUMB_CAPABLE=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++# CONFIG_SWP_EMULATE is not set ++# CONFIG_CPU_BIG_ENDIAN is not set ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_CPU_SPECTRE=y ++CONFIG_HARDEN_BRANCH_PREDICTOR=y ++CONFIG_HARDEN_BRANCH_HISTORY=y ++CONFIG_KUSER_HELPERS=y ++CONFIG_VDSO=y ++CONFIG_OUTER_CACHE=y ++CONFIG_OUTER_CACHE_SYNC=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++CONFIG_CACHE_L2X0=y ++# CONFIG_PL310_ERRATA_588369 is not set ++# CONFIG_PL310_ERRATA_727915 is not set ++# CONFIG_PL310_ERRATA_753970 is not set ++CONFIG_PL310_ERRATA_769419=y ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_ARM_HEAVY_MB=y ++CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y ++# CONFIG_DEBUG_ALIGN_RODATA is not set ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++CONFIG_ARM_ERRATA_754322=y ++CONFIG_ARM_ERRATA_775420=y ++# CONFIG_ARM_ERRATA_773022 is not set ++# CONFIG_ARM_ERRATA_818325_852422 is not set ++# CONFIG_ARM_ERRATA_821420 is not set ++# CONFIG_ARM_ERRATA_825619 is not set ++# CONFIG_ARM_ERRATA_857271 is not set ++# CONFIG_ARM_ERRATA_852421 is not set ++# CONFIG_ARM_ERRATA_852423 is not set ++# CONFIG_ARM_ERRATA_857272 is not set ++# end of System Type ++ ++# ++# Bus support ++# ++CONFIG_ARM_ERRATA_814220=y ++# end of Bus support ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++# CONFIG_SMP is not set ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_HAVE_ARM_TWD=y ++# CONFIG_VMSPLIT_3G is not set ++# CONFIG_VMSPLIT_3G_OPT is not set ++CONFIG_VMSPLIT_2G=y ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0x80000000 ++CONFIG_ARM_PSCI=y ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++CONFIG_SCHED_HRTICK=y ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_ARM_PATCH_IDIV=y ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++CONFIG_ARCH_SELECT_MEMORY_MODEL=y ++CONFIG_ARCH_FLATMEM_ENABLE=y ++CONFIG_ARCH_SPARSEMEM_ENABLE=y ++# CONFIG_HIGHMEM is not set ++# CONFIG_CPU_SW_DOMAIN_PAN is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_ARM_MODULE_PLTS=y ++CONFIG_FORCE_MAX_ZONEORDER=14 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_PARAVIRT is not set ++# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set ++# CONFIG_XEN is not set ++# end of Kernel Features ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++# CONFIG_ARM_APPENDED_DTB is not set ++CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" ++CONFIG_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_CMDLINE_EXTEND is not set ++# CONFIG_CMDLINE_FORCE is not set ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++# CONFIG_EFI is not set ++# end of Boot options ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++# CONFIG_CPU_FREQ_STAT is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++ ++# ++# CPU frequency scaling drivers ++# ++# CONFIG_CPUFREQ_DT is not set ++CONFIG_ARM_IMX6Q_CPUFREQ=y ++# end of CPU Frequency scaling ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# end of CPU Idle ++# end of CPU Power Management ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++# end of Floating point emulation ++ ++# ++# Power management options ++# ++# CONFIG_SUSPEND is not set ++# CONFIG_PM is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++# end of Power management options ++ ++CONFIG_ARM_CRYPTO=y ++CONFIG_CRYPTO_SHA1_ARM=y ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA1_ARM_CE is not set ++# CONFIG_CRYPTO_SHA2_ARM_CE is not set ++CONFIG_CRYPTO_SHA256_ARM=y ++CONFIG_CRYPTO_SHA512_ARM=y ++CONFIG_CRYPTO_BLAKE2S_ARM=y ++# CONFIG_CRYPTO_BLAKE2B_NEON is not set ++CONFIG_CRYPTO_AES_ARM=y ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_AES_ARM_CE is not set ++# CONFIG_CRYPTO_GHASH_ARM_CE is not set ++# CONFIG_CRYPTO_CRCT10DIF_ARM_CE is not set ++# CONFIG_CRYPTO_CRC32_ARM_CE is not set ++CONFIG_CRYPTO_CHACHA20_NEON=y ++CONFIG_CRYPTO_POLY1305_ARM=y ++# CONFIG_CRYPTO_NHPOLY1305_NEON is not set ++# CONFIG_CRYPTO_CURVE25519_NEON is not set ++CONFIG_AS_VFP_VMRS_FPINST=y ++ ++# ++# General architecture-dependent options ++# ++CONFIG_SET_FS=y ++# CONFIG_KPROBES is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_OPTPROBES=y ++CONFIG_HAVE_NMI=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_ARCH_HAS_FORTIFY_SOURCE=y ++CONFIG_ARCH_HAS_KEEPINITRD=y ++CONFIG_ARCH_HAS_SET_MEMORY=y ++CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y ++CONFIG_ARCH_32BIT_OFF_T=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_RSEQ=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_SECCOMP=y ++CONFIG_SECCOMP_FILTER=y ++# CONFIG_SECCOMP_CACHE_DEBUG is not set ++CONFIG_HAVE_STACKPROTECTOR=y ++CONFIG_STACKPROTECTOR=y ++# CONFIG_STACKPROTECTOR_STRONG is not set ++CONFIG_LTO_NONE=y ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_ARCH_HAS_ELF_RANDOMIZE=y ++CONFIG_HAVE_ARCH_MMAP_RND_BITS=y ++CONFIG_HAVE_EXIT_THREAD=y ++CONFIG_ARCH_MMAP_RND_BITS=8 ++CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++CONFIG_COMPAT_32BIT_TIME=y ++CONFIG_ARCH_SUPPORTS_RT=y ++CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y ++CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y ++CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y ++CONFIG_STRICT_KERNEL_RWX=y ++CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y ++# CONFIG_STRICT_MODULE_RWX is not set ++CONFIG_ARCH_HAS_PHYS_TO_DMA=y ++CONFIG_ARCH_WANT_LD_ORPHAN_WARN=y ++CONFIG_HAVE_ARCH_PFN_VALID=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y ++# end of GCOV-based kernel profiling ++ ++CONFIG_HAVE_GCC_PLUGINS=y ++# CONFIG_GCC_PLUGINS is not set ++# end of General architecture-dependent options ++ ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++# CONFIG_MODULE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++CONFIG_MODULE_COMPRESS_NONE=y ++# CONFIG_MODULE_COMPRESS_GZIP is not set ++# CONFIG_MODULE_COMPRESS_XZ is not set ++# CONFIG_MODULE_COMPRESS_ZSTD is not set ++# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set ++CONFIG_MODPROBE_PATH="/sbin/modprobe" ++# CONFIG_TRIM_UNUSED_KSYMS is not set ++CONFIG_BLOCK=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++# CONFIG_BLK_DEV_ZONED is not set ++# CONFIG_BLK_WBT is not set ++# CONFIG_BLK_SED_OPAL is not set ++# CONFIG_BLK_INLINE_ENCRYPTION is not set ++ ++# ++# Partition Types ++# ++# CONFIG_PARTITION_ADVANCED is not set ++CONFIG_MSDOS_PARTITION=y ++CONFIG_EFI_PARTITION=y ++# end of Partition Types ++ ++CONFIG_BLK_MQ_VIRTIO=y ++ ++# ++# IO Schedulers ++# ++CONFIG_MQ_IOSCHED_DEADLINE=y ++# CONFIG_MQ_IOSCHED_KYBER is not set ++# CONFIG_IOSCHED_BFQ is not set ++# end of IO Schedulers ++ ++CONFIG_ASN1=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE=y ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_ELF_FDPIC is not set ++CONFIG_ELFCORE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++CONFIG_ARCH_HAS_BINFMT_FLAT=y ++# CONFIG_BINFMT_FLAT is not set ++CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++# end of Executable file formats ++ ++# ++# Memory Management options ++# ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++# CONFIG_SPARSEMEM_MANUAL is not set ++CONFIG_FLATMEM=y ++CONFIG_ARCH_KEEP_MEMBLOCK=y ++CONFIG_MEMORY_ISOLATION=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_COMPACTION is not set ++# CONFIG_PAGE_REPORTING is not set ++CONFIG_MIGRATION=y ++CONFIG_CONTIG_ALLOC=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++# CONFIG_CMA_SYSFS is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_GENERIC_EARLY_IOREMAP=y ++# CONFIG_IDLE_PAGE_TRACKING is not set ++# CONFIG_PERCPU_STATS is not set ++ ++# ++# GUP_TEST needs to have DEBUG_FS enabled ++# ++ ++# ++# Data Access Monitoring ++# ++# CONFIG_DAMON is not set ++# end of Data Access Monitoring ++# end of Memory Management options ++ ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++CONFIG_UNIX_SCM=y ++CONFIG_AF_UNIX_OOB=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_TLS=y ++CONFIG_TLS_DEVICE=y ++# CONFIG_TLS_TOE is not set ++# CONFIG_XFRM_USER is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_IP_MROUTE is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_INET_RAW_DIAG is not set ++# CONFIG_INET_DIAG_DESTROY is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_MPTCP is not set ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_BPFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_NET_DSA is not set ++CONFIG_VLAN_8021Q=y ++# CONFIG_VLAN_8021Q_GVRP is not set ++# CONFIG_VLAN_8021Q_MVRP is not set ++# CONFIG_DECNET is not set ++CONFIG_LLC=y ++CONFIG_LLC2=y ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_MPLS is not set ++# CONFIG_NET_NSH is not set ++# CONFIG_HSR is not set ++# CONFIG_NET_SWITCHDEV is not set ++# CONFIG_NET_L3_MASTER_DEV is not set ++# CONFIG_QRTR is not set ++# CONFIG_NET_NCSI is not set ++CONFIG_SOCK_RX_QUEUE_MAPPING=y ++CONFIG_BQL=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# end of Network testing ++# end of Networking options ++ ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++# CONFIG_AF_KCM is not set ++CONFIG_STREAM_PARSER=y ++# CONFIG_MCTP is not set ++# CONFIG_WIRELESS is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++# CONFIG_PSAMPLE is not set ++# CONFIG_NET_IFE is not set ++# CONFIG_LWTUNNEL is not set ++CONFIG_SOCK_VALIDATE_XMIT=y ++CONFIG_NET_SELFTESTS=y ++CONFIG_NET_SOCK_MSG=y ++# CONFIG_FAILOVER is not set ++CONFIG_ETHTOOL_NETLINK=y ++ ++# ++# Device Drivers ++# ++CONFIG_HAVE_PCI=y ++# CONFIG_PCI is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++# CONFIG_STANDALONE is not set ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++ ++# ++# Firmware loader ++# ++CONFIG_FW_LOADER=y ++CONFIG_FW_LOADER_PAGED_BUF=y ++CONFIG_EXTRA_FIRMWARE="" ++CONFIG_FW_LOADER_USER_HELPER=y ++CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y ++# CONFIG_FW_LOADER_COMPRESS is not set ++# end of Firmware loader ++ ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set ++# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set ++CONFIG_GENERIC_CPU_AUTOPROBE=y ++CONFIG_GENERIC_CPU_VULNERABILITIES=y ++CONFIG_SOC_BUS=y ++CONFIG_REGMAP=y ++CONFIG_REGMAP_I2C=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_REGMAP_IRQ=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_DMA_FENCE_TRACE is not set ++# end of Generic Driver Options ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_IMX_WEIM=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_FSL_MC_BUS is not set ++# CONFIG_MHI_BUS is not set ++# end of Bus devices ++ ++# CONFIG_CONNECTOR is not set ++ ++# ++# Firmware Drivers ++# ++ ++# ++# ARM System Control and Management Interface Protocol ++# ++# CONFIG_ARM_SCMI_PROTOCOL is not set ++# end of ARM System Control and Management Interface Protocol ++ ++# CONFIG_ARM_SCPI_PROTOCOL is not set ++# CONFIG_FIRMWARE_MEMMAP is not set ++# CONFIG_FW_CFG_SYSFS is not set ++# CONFIG_TRUSTED_FOUNDATIONS is not set ++# CONFIG_GOOGLE_FIRMWARE is not set ++# CONFIG_IMX_DSP is not set ++# CONFIG_IMX_SCU is not set ++# CONFIG_IMX_SECO_MU is not set ++# CONFIG_IMX_EL_ENCLAVE is not set ++CONFIG_ARM_PSCI_FW=y ++CONFIG_HAVE_ARM_SMCCC=y ++CONFIG_HAVE_ARM_SMCCC_DISCOVERY=y ++CONFIG_ARM_SMCCC_SOC_ID=y ++ ++# ++# Tegra firmware driver ++# ++# end of Tegra firmware driver ++# end of Firmware Drivers ++ ++# CONFIG_GNSS is not set ++# CONFIG_MTD is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++# CONFIG_OF_UNITTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_KOBJ=y ++CONFIG_OF_DYNAMIC=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_OF_RESOLVE=y ++CONFIG_OF_OVERLAY=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_RAM is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_VIRTIO_BLK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# NVME Support ++# ++# CONFIG_NVME_FC is not set ++# CONFIG_NVME_TCP is not set ++# CONFIG_NVME_TARGET is not set ++# end of NVME Support ++ ++# ++# Misc devices ++# ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++CONFIG_SRAM=y ++CONFIG_SRAM_EXEC=y ++# CONFIG_XILINX_SDFEC is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++CONFIG_EEPROM_AT24=y ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_IDT_89HPESX is not set ++# CONFIG_EEPROM_EE1004 is not set ++# end of EEPROM support ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# end of Texas Instruments shared transport line discipline ++ ++# CONFIG_SENSORS_LIS3_I2C is not set ++# CONFIG_ALTERA_STAPL is not set ++# CONFIG_ECHO is not set ++# CONFIG_PVPANIC is not set ++# end of Misc devices ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++# CONFIG_SCSI is not set ++# end of SCSI device support ++ ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_WIREGUARD is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_IPVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_GENEVE is not set ++# CONFIG_BAREUDP is not set ++# CONFIG_GTP is not set ++# CONFIG_MACSEC is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_TUN is not set ++# CONFIG_TUN_VNET_CROSS_LE is not set ++# CONFIG_VETH is not set ++# CONFIG_VIRTIO_NET is not set ++# CONFIG_NLMON is not set ++CONFIG_ETHERNET=y ++# CONFIG_NET_VENDOR_ALACRITECH is not set ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_VENDOR_AMAZON is not set ++# CONFIG_NET_VENDOR_AQUANTIA is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CADENCE is not set ++# CONFIG_NET_VENDOR_CAVIUM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_NET_VENDOR_CORTINA is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_EZCHIP is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_FREESCALE=y ++CONFIG_FEC=y ++CONFIG_FEC_ECAT=y ++# CONFIG_FEC_UIO is not set ++# CONFIG_AVB_SUPPORT is not set ++# CONFIG_FSL_PQ_MDIO is not set ++# CONFIG_FSL_XGMAC_MDIO is not set ++# CONFIG_GIANFAR is not set ++ ++# ++# Frame Manager support ++# ++# end of Frame Manager support ++ ++# CONFIG_FSL_ENETC_IERB is not set ++# CONFIG_NET_VENDOR_GOOGLE is not set ++# CONFIG_NET_VENDOR_HISILICON is not set ++# CONFIG_NET_VENDOR_HUAWEI is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_LITEX is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MELLANOX is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_MICROSEMI is not set ++# CONFIG_NET_VENDOR_MICROSOFT is not set ++# CONFIG_NET_VENDOR_NI is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_NET_VENDOR_NETRONOME is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_PENSANDO is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_RENESAS is not set ++# CONFIG_NET_VENDOR_ROCKER is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SOLARFLARE is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_SOCIONEXT is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_SYNOPSYS is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++# CONFIG_NET_VENDOR_XILINX is not set ++CONFIG_PHYLIB=y ++CONFIG_SWPHY=y ++CONFIG_FIXED_PHY=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AMD_PHY is not set ++# CONFIG_ADIN_PHY is not set ++# CONFIG_AQUANTIA_PHY is not set ++CONFIG_AX88796B_PHY=y ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM54140_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM84881_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_CORTINA_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_INPHI_PHY is not set ++# CONFIG_INTEL_XWAY_PHY is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_MARVELL_10G_PHY is not set ++# CONFIG_MARVELL_88X2222_PHY is not set ++# CONFIG_MAXLINEAR_GPHY is not set ++# CONFIG_MEDIATEK_GE_PHY is not set ++CONFIG_MICREL_PHY=y ++CONFIG_MICROCHIP_PHY=y ++# CONFIG_MICROCHIP_T1_PHY is not set ++# CONFIG_MICROSEMI_PHY is not set ++# CONFIG_MOTORCOMM_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_NXP_C45_TJA11XX_PHY is not set ++# CONFIG_NXP_TJA11XX_PHY is not set ++CONFIG_AT803X_PHY=y ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_RENESAS_PHY is not set ++# CONFIG_ROCKCHIP_PHY is not set ++CONFIG_SMSC_PHY=y ++# CONFIG_STE10XP is not set ++# CONFIG_TERANETICS_PHY is not set ++# CONFIG_DP83822_PHY is not set ++# CONFIG_DP83TC811_PHY is not set ++# CONFIG_DP83848_PHY is not set ++# CONFIG_DP83867_PHY is not set ++# CONFIG_DP83869_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_XILINX_GMII2RGMII is not set ++CONFIG_MDIO_DEVICE=y ++CONFIG_MDIO_BUS=y ++CONFIG_FWNODE_MDIO=y ++CONFIG_OF_MDIO=y ++CONFIG_MDIO_DEVRES=y ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_MSCC_MIIM is not set ++# CONFIG_MDIO_IPQ4019 is not set ++# CONFIG_MDIO_IPQ8064 is not set ++ ++# ++# MDIO Multiplexers ++# ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MULTIPLEXER is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++ ++# ++# PCS device drivers ++# ++# CONFIG_PCS_XPCS is not set ++# end of PCS device drivers ++ ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++ ++# ++# Host-side USB support is needed for USB Network Adapter support ++# ++# CONFIG_WLAN is not set ++# CONFIG_WAN is not set ++ ++# ++# Wireless WAN ++# ++# CONFIG_WWAN is not set ++# end of Wireless WAN ++ ++# CONFIG_NET_FAILOVER is not set ++# CONFIG_IMX_SHMEM_NET is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++# CONFIG_INPUT_EVDEV is not set ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++CONFIG_KEYBOARD_SNVS_PWRKEY=y ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_TOUCHSCREEN_FTS is not set ++# CONFIG_INPUT_MISC is not set ++# CONFIG_RMI4_CORE is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++# end of Hardware I/O ports ++# end of Input device support ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++CONFIG_UNIX98_PTYS=y ++# CONFIG_LEGACY_PTYS is not set ++CONFIG_LDISC_AUTOLOAD=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++CONFIG_SERIAL_IMX=y ++CONFIG_SERIAL_IMX_CONSOLE=y ++CONFIG_SERIAL_IMX_EARLYCON=y ++# CONFIG_SERIAL_UARTLITE is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SIFIVE is not set ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_BCM63XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++CONFIG_SERIAL_FSL_LPUART=y ++CONFIG_SERIAL_FSL_LPUART_CONSOLE=y ++# CONFIG_SERIAL_FSL_LINFLEXUART is not set ++# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_SERIAL_SPRD is not set ++# end of Serial drivers ++ ++CONFIG_SERIAL_MCTRL_GPIO=y ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_NULL_TTY is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_RPMSG_TTY is not set ++CONFIG_SERIAL_DEV_BUS=y ++CONFIG_SERIAL_DEV_CTRL_TTYPORT=y ++# CONFIG_TTY_PRINTK is not set ++# CONFIG_VIRTIO_CONSOLE is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_IPMB_DEVICE_INTERFACE is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_HW_RANDOM_BA431 is not set ++# CONFIG_HW_RANDOM_VIRTIO is not set ++CONFIG_HW_RANDOM_IMX_RNGC=y ++CONFIG_HW_RANDOM_OPTEE=y ++# CONFIG_HW_RANDOM_CCTRNG is not set ++# CONFIG_HW_RANDOM_XIPHERA is not set ++CONFIG_HW_RANDOM_ARM_SMCCC_TRNG=y ++# CONFIG_DEVMEM is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_XILLYBUS is not set ++CONFIG_IMX_SEMA4=y ++CONFIG_RANDOM_TRUST_BOOTLOADER=y ++# end of Character devices ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set ++CONFIG_I2C_MUX_GPIO=y ++# CONFIG_I2C_MUX_GPMUX is not set ++# CONFIG_I2C_MUX_LTC4306 is not set ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PCA954x is not set ++# CONFIG_I2C_MUX_PINCTRL is not set ++# CONFIG_I2C_MUX_REG is not set ++# CONFIG_I2C_DEMUX_PINCTRL is not set ++# CONFIG_I2C_MUX_MLXCPLD is not set ++# end of Multiplexer I2C Chip support ++ ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++CONFIG_I2C_ALGOBIT=y ++CONFIG_I2C_ALGOPCF=y ++CONFIG_I2C_ALGOPCA=y ++# end of I2C Algorithms ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_EMEV2 is not set ++CONFIG_I2C_GPIO=y ++# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set ++CONFIG_I2C_IMX=y ++CONFIG_I2C_IMX_LPI2C=y ++CONFIG_I2C_IMX_FLEXIO=y ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_RPBUS is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_TAOS_EVM is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_VIRTIO is not set ++# end of I2C Hardware Bus support ++ ++# CONFIG_I2C_STUB is not set ++CONFIG_I2C_SLAVE=y ++# CONFIG_I2C_SLAVE_EEPROM is not set ++# CONFIG_I2C_SLAVE_TESTUNIT is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++# end of I2C support ++ ++# CONFIG_I3C is not set ++# CONFIG_SPI is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++CONFIG_PPS=y ++# CONFIG_PPS_DEBUG is not set ++ ++# ++# PPS clients support ++# ++# CONFIG_PPS_CLIENT_KTIMER is not set ++# CONFIG_PPS_CLIENT_LDISC is not set ++# CONFIG_PPS_CLIENT_GPIO is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++CONFIG_PTP_1588_CLOCK_OPTIONAL=y ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++# end of PTP clock support ++ ++CONFIG_PINCTRL=y ++CONFIG_GENERIC_PINCTRL_GROUPS=y ++CONFIG_PINMUX=y ++CONFIG_GENERIC_PINMUX_FUNCTIONS=y ++CONFIG_PINCONF=y ++CONFIG_GENERIC_PINCONF=y ++# CONFIG_DEBUG_PINCTRL is not set ++# CONFIG_PINCTRL_DA9062 is not set ++# CONFIG_PINCTRL_MCP23S08 is not set ++# CONFIG_PINCTRL_SINGLE is not set ++# CONFIG_PINCTRL_SX150X is not set ++# CONFIG_PINCTRL_STMFX is not set ++# CONFIG_PINCTRL_OCELOT is not set ++# CONFIG_PINCTRL_MICROCHIP_SGPIO is not set ++CONFIG_PINCTRL_IMX=y ++CONFIG_PINCTRL_IMX6Q=y ++CONFIG_PINCTRL_IMX6SL=y ++CONFIG_PINCTRL_IMX6SLL=y ++CONFIG_PINCTRL_IMX6SX=y ++CONFIG_PINCTRL_IMX6UL=y ++CONFIG_PINCTRL_IMX7D=y ++# CONFIG_PINCTRL_IMX8MM is not set ++# CONFIG_PINCTRL_IMX8MN is not set ++# CONFIG_PINCTRL_IMX8MP is not set ++# CONFIG_PINCTRL_IMX8MQ is not set ++# CONFIG_PINCTRL_IMX8ULP is not set ++# CONFIG_PINCTRL_IMX93 is not set ++# CONFIG_PINCTRL_S32V_CORE is not set ++ ++# ++# Renesas pinctrl drivers ++# ++# end of Renesas pinctrl drivers ++ ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIOLIB_FASTPATH_LIMIT=512 ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++# CONFIG_GPIO_SYSFS is not set ++CONFIG_GPIO_CDEV=y ++CONFIG_GPIO_CDEV_V1=y ++CONFIG_GPIO_GENERIC=y ++ ++# ++# Memory mapped GPIO drivers ++# ++# CONFIG_GPIO_74XX_MMIO is not set ++# CONFIG_GPIO_ALTERA is not set ++# CONFIG_GPIO_CADENCE is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_FTGPIO010 is not set ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_GRGPIO is not set ++# CONFIG_GPIO_HLWD is not set ++# CONFIG_GPIO_LOGICVC is not set ++# CONFIG_GPIO_MB86S7X is not set ++# CONFIG_GPIO_MPC8XXX is not set ++CONFIG_GPIO_MXC=y ++# CONFIG_GPIO_SAMA5D2_PIOBU is not set ++# CONFIG_GPIO_SIFIVE is not set ++# CONFIG_GPIO_SYSCON is not set ++CONFIG_GPIO_VF610=y ++CONFIG_GPIO_IMX_RPMSG=y ++# CONFIG_GPIO_XILINX is not set ++# CONFIG_GPIO_ZEVIO is not set ++# CONFIG_GPIO_AMD_FCH is not set ++# end of Memory mapped GPIO drivers ++ ++# ++# I2C GPIO expanders ++# ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++# CONFIG_GPIO_GW_PLD is not set ++# CONFIG_GPIO_MAX7300 is not set ++CONFIG_GPIO_MAX732X=y ++# CONFIG_GPIO_MAX732X_IRQ is not set ++CONFIG_GPIO_PCA953X=y ++# CONFIG_GPIO_PCA953X_IRQ is not set ++# CONFIG_GPIO_PCA9570 is not set ++CONFIG_GPIO_PCF857X=y ++# CONFIG_GPIO_TPIC2810 is not set ++# CONFIG_GPIO_TS4900 is not set ++# end of I2C GPIO expanders ++ ++# ++# MFD GPIO expanders ++# ++# CONFIG_GPIO_DA9052 is not set ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_GPIO_WM8994 is not set ++# end of MFD GPIO expanders ++ ++# ++# Virtual GPIO drivers ++# ++# CONFIG_GPIO_AGGREGATOR is not set ++# CONFIG_GPIO_MOCKUP is not set ++# CONFIG_GPIO_VIRTIO is not set ++# end of Virtual GPIO drivers ++ ++# CONFIG_W1 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMKONA is not set ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_REGULATOR is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++CONFIG_POWER_RESET_SYSCON=y ++CONFIG_POWER_RESET_SYSCON_POWEROFF=y ++# CONFIG_SYSCON_REBOOT_MODE is not set ++# CONFIG_NVMEM_REBOOT_MODE is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_POWER_SUPPLY_HWMON is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_CHARGER_ADP5061 is not set ++# CONFIG_BATTERY_CW2015 is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_CHARGER_SBS is not set ++# CONFIG_MANAGER_SBS is not set ++# CONFIG_BATTERY_BQ27XXX is not set ++# CONFIG_BATTERY_DA9052 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_MANAGER is not set ++# CONFIG_CHARGER_LT3651 is not set ++# CONFIG_CHARGER_LTC4162L is not set ++# CONFIG_CHARGER_DETECTOR_MAX14656 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24257 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_BQ2515X is not set ++# CONFIG_CHARGER_BQ25890 is not set ++# CONFIG_CHARGER_BQ25980 is not set ++# CONFIG_CHARGER_BQ256XX is not set ++# CONFIG_CHARGER_SMB347 is not set ++# CONFIG_BATTERY_GAUGE_LTC2941 is not set ++# CONFIG_BATTERY_GOLDFISH is not set ++# CONFIG_BATTERY_RT5033 is not set ++# CONFIG_CHARGER_RT9455 is not set ++# CONFIG_CHARGER_UCS1002 is not set ++# CONFIG_CHARGER_BD99954 is not set ++CONFIG_HWMON=y ++# CONFIG_HWMON_DEBUG_CHIP is not set ++ ++# ++# Native drivers ++# ++# CONFIG_SENSORS_AD7414 is not set ++# CONFIG_SENSORS_AD7418 is not set ++# CONFIG_SENSORS_ADM1021 is not set ++# CONFIG_SENSORS_ADM1025 is not set ++# CONFIG_SENSORS_ADM1026 is not set ++# CONFIG_SENSORS_ADM1029 is not set ++# CONFIG_SENSORS_ADM1031 is not set ++# CONFIG_SENSORS_ADM1177 is not set ++# CONFIG_SENSORS_ADM9240 is not set ++# CONFIG_SENSORS_ADT7410 is not set ++# CONFIG_SENSORS_ADT7411 is not set ++# CONFIG_SENSORS_ADT7462 is not set ++# CONFIG_SENSORS_ADT7470 is not set ++# CONFIG_SENSORS_ADT7475 is not set ++# CONFIG_SENSORS_AHT10 is not set ++# CONFIG_SENSORS_AS370 is not set ++# CONFIG_SENSORS_ASC7621 is not set ++# CONFIG_SENSORS_AXI_FAN_CONTROL is not set ++# CONFIG_SENSORS_ASPEED is not set ++# CONFIG_SENSORS_ATXP1 is not set ++# CONFIG_SENSORS_CORSAIR_CPRO is not set ++# CONFIG_SENSORS_CORSAIR_PSU is not set ++# CONFIG_SENSORS_DS620 is not set ++# CONFIG_SENSORS_DS1621 is not set ++# CONFIG_SENSORS_DA9052_ADC is not set ++# CONFIG_SENSORS_F71805F is not set ++# CONFIG_SENSORS_F71882FG is not set ++# CONFIG_SENSORS_F75375S is not set ++CONFIG_SENSORS_MC13783_ADC=y ++# CONFIG_SENSORS_FTSTEUTATES is not set ++# CONFIG_SENSORS_GL518SM is not set ++# CONFIG_SENSORS_GL520SM is not set ++# CONFIG_SENSORS_G760A is not set ++# CONFIG_SENSORS_G762 is not set ++CONFIG_SENSORS_GPIO_FAN=y ++# CONFIG_SENSORS_HIH6130 is not set ++# CONFIG_SENSORS_IT87 is not set ++# CONFIG_SENSORS_JC42 is not set ++# CONFIG_SENSORS_POWR1220 is not set ++# CONFIG_SENSORS_LINEAGE is not set ++# CONFIG_SENSORS_LTC2945 is not set ++# CONFIG_SENSORS_LTC2947_I2C is not set ++# CONFIG_SENSORS_LTC2990 is not set ++# CONFIG_SENSORS_LTC2992 is not set ++# CONFIG_SENSORS_LTC4151 is not set ++# CONFIG_SENSORS_LTC4215 is not set ++# CONFIG_SENSORS_LTC4222 is not set ++# CONFIG_SENSORS_LTC4245 is not set ++# CONFIG_SENSORS_LTC4260 is not set ++# CONFIG_SENSORS_LTC4261 is not set ++# CONFIG_SENSORS_MAX127 is not set ++# CONFIG_SENSORS_MAX16065 is not set ++# CONFIG_SENSORS_MAX1619 is not set ++# CONFIG_SENSORS_MAX1668 is not set ++CONFIG_SENSORS_MAX17135=y ++# CONFIG_SENSORS_MAX197 is not set ++# CONFIG_SENSORS_MAX31730 is not set ++# CONFIG_SENSORS_MAX6621 is not set ++# CONFIG_SENSORS_MAX6639 is not set ++# CONFIG_SENSORS_MAX6642 is not set ++# CONFIG_SENSORS_MAX6650 is not set ++# CONFIG_SENSORS_MAX6697 is not set ++# CONFIG_SENSORS_MAX31790 is not set ++# CONFIG_SENSORS_MCP3021 is not set ++# CONFIG_SENSORS_TC654 is not set ++# CONFIG_SENSORS_TPS23861 is not set ++# CONFIG_SENSORS_MR75203 is not set ++# CONFIG_SENSORS_LM63 is not set ++# CONFIG_SENSORS_LM73 is not set ++# CONFIG_SENSORS_LM75 is not set ++# CONFIG_SENSORS_LM77 is not set ++# CONFIG_SENSORS_LM78 is not set ++# CONFIG_SENSORS_LM80 is not set ++# CONFIG_SENSORS_LM83 is not set ++# CONFIG_SENSORS_LM85 is not set ++# CONFIG_SENSORS_LM87 is not set ++# CONFIG_SENSORS_LM90 is not set ++# CONFIG_SENSORS_LM92 is not set ++# CONFIG_SENSORS_LM93 is not set ++# CONFIG_SENSORS_LM95234 is not set ++# CONFIG_SENSORS_LM95241 is not set ++# CONFIG_SENSORS_LM95245 is not set ++# CONFIG_SENSORS_PC87360 is not set ++# CONFIG_SENSORS_PC87427 is not set ++# CONFIG_SENSORS_NTC_THERMISTOR is not set ++# CONFIG_SENSORS_NCT6683 is not set ++# CONFIG_SENSORS_NCT6775 is not set ++# CONFIG_SENSORS_NCT7802 is not set ++# CONFIG_SENSORS_NCT7904 is not set ++# CONFIG_SENSORS_NPCM7XX is not set ++# CONFIG_SENSORS_OCC_P8_I2C is not set ++# CONFIG_SENSORS_PCF8591 is not set ++# CONFIG_PMBUS is not set ++# CONFIG_SENSORS_SBTSI is not set ++# CONFIG_SENSORS_SBRMI is not set ++# CONFIG_SENSORS_SHT15 is not set ++# CONFIG_SENSORS_SHT21 is not set ++# CONFIG_SENSORS_SHT3x is not set ++# CONFIG_SENSORS_SHT4x is not set ++# CONFIG_SENSORS_SHTC1 is not set ++# CONFIG_SENSORS_DME1737 is not set ++# CONFIG_SENSORS_EMC1403 is not set ++# CONFIG_SENSORS_EMC2103 is not set ++# CONFIG_SENSORS_EMC6W201 is not set ++# CONFIG_SENSORS_SMSC47M1 is not set ++# CONFIG_SENSORS_SMSC47M192 is not set ++# CONFIG_SENSORS_SMSC47B397 is not set ++# CONFIG_SENSORS_SCH5627 is not set ++# CONFIG_SENSORS_SCH5636 is not set ++# CONFIG_SENSORS_STTS751 is not set ++# CONFIG_SENSORS_SMM665 is not set ++# CONFIG_SENSORS_ADC128D818 is not set ++# CONFIG_SENSORS_ADS7828 is not set ++# CONFIG_SENSORS_AMC6821 is not set ++# CONFIG_SENSORS_INA209 is not set ++# CONFIG_SENSORS_INA2XX is not set ++# CONFIG_SENSORS_INA3221 is not set ++# CONFIG_SENSORS_TC74 is not set ++# CONFIG_SENSORS_THMC50 is not set ++# CONFIG_SENSORS_TMP102 is not set ++# CONFIG_SENSORS_TMP103 is not set ++# CONFIG_SENSORS_TMP108 is not set ++# CONFIG_SENSORS_TMP401 is not set ++# CONFIG_SENSORS_TMP421 is not set ++# CONFIG_SENSORS_TMP513 is not set ++# CONFIG_SENSORS_VT1211 is not set ++# CONFIG_SENSORS_W83773G is not set ++# CONFIG_SENSORS_W83781D is not set ++# CONFIG_SENSORS_W83791D is not set ++# CONFIG_SENSORS_W83792D is not set ++# CONFIG_SENSORS_W83793 is not set ++# CONFIG_SENSORS_W83795 is not set ++# CONFIG_SENSORS_W83L785TS is not set ++# CONFIG_SENSORS_W83L786NG is not set ++# CONFIG_SENSORS_W83627HF is not set ++# CONFIG_SENSORS_W83627EHF is not set ++CONFIG_THERMAL=y ++# CONFIG_THERMAL_NETLINK is not set ++CONFIG_THERMAL_STATISTICS=y ++CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 ++CONFIG_THERMAL_HWMON=y ++CONFIG_THERMAL_OF=y ++CONFIG_THERMAL_WRITABLE_TRIPS=y ++CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y ++# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set ++# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set ++# CONFIG_THERMAL_GOV_FAIR_SHARE is not set ++CONFIG_THERMAL_GOV_STEP_WISE=y ++# CONFIG_THERMAL_GOV_BANG_BANG is not set ++# CONFIG_THERMAL_GOV_USER_SPACE is not set ++CONFIG_CPU_THERMAL=y ++CONFIG_CPU_FREQ_THERMAL=y ++# CONFIG_THERMAL_EMULATION is not set ++# CONFIG_THERMAL_MMIO is not set ++CONFIG_IMX_THERMAL=y ++# CONFIG_IMX8MM_THERMAL is not set ++CONFIG_DEVICE_THERMAL=y ++# CONFIG_DA9062_THERMAL is not set ++CONFIG_WATCHDOG=y ++CONFIG_WATCHDOG_CORE=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y ++CONFIG_WATCHDOG_OPEN_TIMEOUT=0 ++# CONFIG_WATCHDOG_SYSFS is not set ++# CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT is not set ++ ++# ++# Watchdog Pretimeout Governors ++# ++# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set ++ ++# ++# Watchdog Device Drivers ++# ++# CONFIG_SOFT_WATCHDOG is not set ++# CONFIG_DA9052_WATCHDOG is not set ++CONFIG_DA9063_WATCHDOG=y ++CONFIG_DA9062_WATCHDOG=y ++# CONFIG_GPIO_WATCHDOG is not set ++# CONFIG_XILINX_WATCHDOG is not set ++# CONFIG_ZIIRAVE_WATCHDOG is not set ++# CONFIG_CADENCE_WATCHDOG is not set ++# CONFIG_FTWDT010_WATCHDOG is not set ++# CONFIG_DW_WATCHDOG is not set ++CONFIG_RN5T618_WATCHDOG=y ++# CONFIG_MAX63XX_WATCHDOG is not set ++CONFIG_IMX2_WDT=y ++CONFIG_IMX7ULP_WDT=y ++# CONFIG_ARM_SMC_WATCHDOG is not set ++# CONFIG_MEN_A21_WDT is not set ++CONFIG_SSB_POSSIBLE=y ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_ADP5585 is not set ++# CONFIG_MFD_ACT8945A is not set ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_ATMEL_FLEXCOM is not set ++# CONFIG_MFD_ATMEL_HLCDC is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_BD9571MWV is not set ++# CONFIG_MFD_AXP20X_I2C is not set ++# CONFIG_MFD_MADERA is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++CONFIG_PMIC_DA9052=y ++CONFIG_MFD_DA9052_I2C=y ++# CONFIG_MFD_DA9055 is not set ++CONFIG_MFD_DA9062=y ++CONFIG_MFD_DA9063=y ++# CONFIG_MFD_DA9150 is not set ++# CONFIG_MFD_GATEWORKS_GSC is not set ++CONFIG_MFD_MC13XXX=y ++CONFIG_MFD_MC13XXX_I2C=y ++# CONFIG_MFD_MP2629 is not set ++# CONFIG_MFD_IMX_MIX is not set ++CONFIG_MFD_MXC_HDMI=y ++# CONFIG_MFD_HI6421_PMIC is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_MFD_IQS62X is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++CONFIG_MFD_MAX17135=y ++# CONFIG_MFD_FP9931 is not set ++# CONFIG_MFD_MAX77620 is not set ++# CONFIG_MFD_MAX77650 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX77843 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MT6360 is not set ++# CONFIG_MFD_MT6397 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_MFD_NTXEC is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8XXX is not set ++# CONFIG_MFD_RT4831 is not set ++# CONFIG_MFD_RT5033 is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++CONFIG_MFD_RN5T618=y ++# CONFIG_MFD_SEC_CORE is not set ++CONFIG_MFD_SI476X_CORE=y ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SKY81452 is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_TI_LMU is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65086 is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TI_LP873X is not set ++# CONFIG_MFD_TI_LP87565 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_TQMX86 is not set ++# CONFIG_MFD_LOCHNAGAR is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM8350_I2C is not set ++CONFIG_MFD_WM8994=y ++# CONFIG_MFD_ROHM_BD718XX is not set ++# CONFIG_MFD_ROHM_BD70528 is not set ++# CONFIG_MFD_ROHM_BD71828 is not set ++# CONFIG_MFD_ROHM_BD957XMUF is not set ++# CONFIG_MFD_STPMIC1 is not set ++# CONFIG_MFD_STMFX is not set ++# CONFIG_MFD_ATC260X_I2C is not set ++# CONFIG_MFD_QCOM_PM8008 is not set ++# CONFIG_RAVE_SP_CORE is not set ++# CONFIG_MFD_RSMU_I2C is not set ++# end of Multifunction device drivers ++ ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_88PG86X is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++CONFIG_REGULATOR_ANATOP=y ++CONFIG_REGULATOR_DA9052=y ++CONFIG_REGULATOR_DA9062=y ++CONFIG_REGULATOR_DA9063=y ++# CONFIG_REGULATOR_DA9121 is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_FAN53880 is not set ++CONFIG_REGULATOR_GPIO=y ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++CONFIG_REGULATOR_LTC3676=y ++# CONFIG_REGULATOR_MAX1586 is not set ++CONFIG_REGULATOR_MAX17135=y ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8893 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_MAX77826 is not set ++CONFIG_REGULATOR_MC13XXX_CORE=y ++CONFIG_REGULATOR_MC13783=y ++CONFIG_REGULATOR_MC13892=y ++# CONFIG_REGULATOR_MCP16502 is not set ++# CONFIG_REGULATOR_MP5416 is not set ++# CONFIG_REGULATOR_MP8859 is not set ++# CONFIG_REGULATOR_MP886X is not set ++# CONFIG_REGULATOR_MPQ7920 is not set ++# CONFIG_REGULATOR_MT6311 is not set ++# CONFIG_REGULATOR_PCA9450 is not set ++# CONFIG_REGULATOR_PF8X00 is not set ++# CONFIG_REGULATOR_PF1550_RPMSG is not set ++CONFIG_REGULATOR_PFUZE100=y ++# CONFIG_REGULATOR_PV88060 is not set ++# CONFIG_REGULATOR_PV88080 is not set ++# CONFIG_REGULATOR_PV88090 is not set ++# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set ++CONFIG_REGULATOR_RN5T618=y ++# CONFIG_REGULATOR_RT4801 is not set ++# CONFIG_REGULATOR_RT6160 is not set ++# CONFIG_REGULATOR_RT6245 is not set ++# CONFIG_REGULATOR_RTQ2134 is not set ++# CONFIG_REGULATOR_RTMV20 is not set ++# CONFIG_REGULATOR_RTQ6752 is not set ++# CONFIG_REGULATOR_SLG51000 is not set ++# CONFIG_REGULATOR_SY8106A is not set ++# CONFIG_REGULATOR_SY8824X is not set ++# CONFIG_REGULATOR_SY8827N is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS65132 is not set ++# CONFIG_REGULATOR_VCTRL is not set ++# CONFIG_REGULATOR_WM8994 is not set ++# CONFIG_RC_CORE is not set ++# CONFIG_MEDIA_CEC_SUPPORT is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++# CONFIG_IMX_DPU_CORE is not set ++# CONFIG_IMX_LCDIF_CORE is not set ++# CONFIG_IMX_LCDIFV3_CORE is not set ++CONFIG_DRM=y ++# CONFIG_DRM_DP_AUX_CHARDEV is not set ++# CONFIG_DRM_DEBUG_MM is not set ++# CONFIG_DRM_DEBUG_SELFTEST is not set ++CONFIG_DRM_KMS_HELPER=y ++# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set ++# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set ++# CONFIG_DRM_DP_CEC is not set ++ ++# ++# I2C encoder or helper chips ++# ++# CONFIG_DRM_I2C_CH7006 is not set ++# CONFIG_DRM_I2C_SIL164 is not set ++# CONFIG_DRM_I2C_NXP_TDA998X is not set ++# CONFIG_DRM_I2C_NXP_TDA9950 is not set ++# end of I2C encoder or helper chips ++ ++# ++# ARM devices ++# ++# CONFIG_DRM_HDLCD is not set ++# CONFIG_DRM_MALI_DISPLAY is not set ++# CONFIG_DRM_KOMEDA is not set ++# end of ARM devices ++ ++# CONFIG_DRM_VGEM is not set ++# CONFIG_DRM_VKMS is not set ++# CONFIG_DRM_EXYNOS is not set ++# CONFIG_DRM_ARMADA is not set ++# CONFIG_DRM_RCAR_DW_HDMI is not set ++# CONFIG_DRM_RCAR_LVDS is not set ++# CONFIG_DRM_OMAP is not set ++# CONFIG_DRM_TILCDC is not set ++# CONFIG_DRM_FSL_DCU is not set ++# CONFIG_DRM_STM is not set ++CONFIG_DRM_PANEL=y ++ ++# ++# Display Panels ++# ++# CONFIG_DRM_PANEL_ARM_VERSATILE is not set ++CONFIG_DRM_PANEL_LVDS=y ++# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set ++# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set ++# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set ++# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set ++CONFIG_DRM_PANEL_SEIKO_43WVF1G=y ++# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set ++# CONFIG_DRM_PANEL_WKS_101WX001 is not set ++# end of Display Panels ++ ++CONFIG_DRM_BRIDGE=y ++CONFIG_DRM_PANEL_BRIDGE=y ++ ++# ++# Display Interface Bridges ++# ++# CONFIG_DRM_CDNS_DSI is not set ++# CONFIG_DRM_CHIPONE_ICN6211 is not set ++# CONFIG_DRM_CHRONTEL_CH7033 is not set ++# CONFIG_DRM_DISPLAY_CONNECTOR is not set ++# CONFIG_DRM_LONTIUM_LT8912B is not set ++# CONFIG_DRM_LONTIUM_LT9611 is not set ++# CONFIG_DRM_LONTIUM_LT9611UXC is not set ++# CONFIG_DRM_ITE_IT66121 is not set ++# CONFIG_DRM_FSL_IMX_LVDS_BRIDGE is not set ++# CONFIG_DRM_LVDS_CODEC is not set ++# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set ++# CONFIG_DRM_NWL_MIPI_DSI is not set ++# CONFIG_DRM_SEC_MIPI_DSIM is not set ++# CONFIG_DRM_NXP_SEIKO_43WVFIG is not set ++# CONFIG_DRM_NXP_PTN3460 is not set ++# CONFIG_DRM_PARADE_PS8622 is not set ++# CONFIG_DRM_PARADE_PS8640 is not set ++# CONFIG_DRM_SIL_SII8620 is not set ++# CONFIG_DRM_SII902X is not set ++# CONFIG_DRM_SII9234 is not set ++# CONFIG_DRM_SIMPLE_BRIDGE is not set ++# CONFIG_DRM_THINE_THC63LVD1024 is not set ++# CONFIG_DRM_TOSHIBA_TC358762 is not set ++# CONFIG_DRM_TOSHIBA_TC358764 is not set ++# CONFIG_DRM_TOSHIBA_TC358767 is not set ++# CONFIG_DRM_TOSHIBA_TC358768 is not set ++# CONFIG_DRM_TOSHIBA_TC358775 is not set ++CONFIG_DRM_TI_TFP410=y ++# CONFIG_DRM_TI_SN65DSI83 is not set ++# CONFIG_DRM_TI_SN65DSI86 is not set ++# CONFIG_DRM_TI_TPD12S015 is not set ++# CONFIG_DRM_ANALOGIX_ANX6345 is not set ++# CONFIG_DRM_ANALOGIX_ANX78XX is not set ++# CONFIG_DRM_ANALOGIX_ANX7625 is not set ++# CONFIG_DRM_I2C_ADV7511 is not set ++# CONFIG_DRM_CDNS_MHDP8546 is not set ++# CONFIG_DRM_CDNS_MHDP is not set ++# CONFIG_DRM_CDNS_HDMI_CEC is not set ++# CONFIG_DRM_ITE_IT6263 is not set ++# CONFIG_DRM_ITE_IT6161 is not set ++# end of Display Interface Bridges ++ ++# CONFIG_DRM_STI is not set ++# CONFIG_IMX8MP_HDMI_PAVI is not set ++# CONFIG_DRM_IMX_DCNANO is not set ++# CONFIG_DRM_ETNAVIV is not set ++# CONFIG_DRM_MXSFB is not set ++# CONFIG_DRM_ARCPGU is not set ++# CONFIG_DRM_SIMPLEDRM is not set ++# CONFIG_DRM_PL111 is not set ++# CONFIG_DRM_TVE200 is not set ++# CONFIG_DRM_LIMA is not set ++# CONFIG_DRM_PANFROST is not set ++# CONFIG_DRM_MCDE is not set ++# CONFIG_DRM_TIDSS is not set ++# CONFIG_DRM_LEGACY is not set ++CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB is not set ++# CONFIG_FB_MXC_DISP_FRAMEWORK is not set ++# end of Frame buffer Devices ++ ++# ++# Backlight & LCD device support ++# ++CONFIG_LCD_CLASS_DEVICE=y ++CONFIG_LCD_PLATFORM=y ++CONFIG_BACKLIGHT_CLASS_DEVICE=y ++# CONFIG_BACKLIGHT_KTD253 is not set ++# CONFIG_BACKLIGHT_DA9052 is not set ++# CONFIG_BACKLIGHT_QCOM_WLED is not set ++# CONFIG_BACKLIGHT_ADP8860 is not set ++# CONFIG_BACKLIGHT_ADP8870 is not set ++# CONFIG_BACKLIGHT_LM3639 is not set ++CONFIG_BACKLIGHT_GPIO=y ++# CONFIG_BACKLIGHT_LV5207LP is not set ++# CONFIG_BACKLIGHT_BD6107 is not set ++# CONFIG_BACKLIGHT_ARCXCNN is not set ++# end of Backlight & LCD device support ++ ++CONFIG_VIDEOMODE_HELPERS=y ++CONFIG_HDMI=y ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# end of Console display driver support ++# end of Graphics support ++ ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++# CONFIG_HID_A4TECH is not set ++# CONFIG_HID_ACRUX is not set ++# CONFIG_HID_APPLE is not set ++# CONFIG_HID_AUREAL is not set ++# CONFIG_HID_BELKIN is not set ++# CONFIG_HID_CHERRY is not set ++# CONFIG_HID_COUGAR is not set ++# CONFIG_HID_MACALLY is not set ++# CONFIG_HID_CMEDIA is not set ++# CONFIG_HID_CYPRESS is not set ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_EZKEY is not set ++# CONFIG_HID_GEMBIRD is not set ++# CONFIG_HID_GFRM is not set ++# CONFIG_HID_GLORIOUS is not set ++# CONFIG_HID_VIVALDI is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_VIEWSONIC is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_ITE is not set ++# CONFIG_HID_JABRA is not set ++# CONFIG_HID_TWINHAN is not set ++# CONFIG_HID_KENSINGTON is not set ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++# CONFIG_HID_MAGICMOUSE is not set ++# CONFIG_HID_MALTRON is not set ++# CONFIG_HID_MAYFLASH is not set ++# CONFIG_HID_REDRAGON is not set ++# CONFIG_HID_MICROSOFT is not set ++# CONFIG_HID_MONTEREY is not set ++CONFIG_HID_MULTITOUCH=y ++# CONFIG_HID_NTI is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PLANTRONICS is not set ++# CONFIG_HID_PLAYSTATION is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SEMITEK is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEAM is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_UDRAW_PS3 is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++# CONFIG_HID_ALPS is not set ++# end of Special HID drivers ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID_OF is not set ++# CONFIG_I2C_HID_OF_GOODIX is not set ++# end of I2C HID support ++# end of HID support ++ ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++# CONFIG_USB_SUPPORT is not set ++CONFIG_MMC=y ++CONFIG_PWRSEQ_EMMC=y ++CONFIG_PWRSEQ_SIMPLE=y ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_DEBUG is not set ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_IO_ACCESSORS=y ++CONFIG_MMC_SDHCI_PLTFM=y ++# CONFIG_MMC_SDHCI_OF_ARASAN is not set ++# CONFIG_MMC_SDHCI_OF_ASPEED is not set ++# CONFIG_MMC_SDHCI_OF_AT91 is not set ++# CONFIG_MMC_SDHCI_OF_ESDHC is not set ++# CONFIG_MMC_SDHCI_OF_DWCMSHC is not set ++# CONFIG_MMC_SDHCI_CADENCE is not set ++CONFIG_MMC_SDHCI_ESDHC_IMX=y ++# CONFIG_MMC_SDHCI_F_SDH30 is not set ++# CONFIG_MMC_SDHCI_MILBEAUT is not set ++# CONFIG_MMC_MXC is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_MMC_CQHCI=y ++# CONFIG_MMC_HSQ is not set ++# CONFIG_MMC_MTK is not set ++# CONFIG_MMC_SDHCI_XENON is not set ++# CONFIG_MMC_SDHCI_OMAP is not set ++# CONFIG_MMC_SDHCI_AM654 is not set ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_INFINIBAND is not set ++CONFIG_EDAC_ATOMIC_SCRUB=y ++CONFIG_EDAC_SUPPORT=y ++CONFIG_RTC_LIB=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_HCTOSYS=y ++CONFIG_RTC_HCTOSYS_DEVICE="rtc0" ++CONFIG_RTC_SYSTOHC=y ++CONFIG_RTC_SYSTOHC_DEVICE="rtc0" ++# CONFIG_RTC_DEBUG is not set ++CONFIG_RTC_NVMEM=y ++ ++# ++# RTC interfaces ++# ++CONFIG_RTC_INTF_SYSFS=y ++CONFIG_RTC_INTF_PROC=y ++CONFIG_RTC_INTF_DEV=y ++CONFIG_RTC_INTF_DEV_UIE_EMUL=y ++# CONFIG_RTC_DRV_TEST is not set ++ ++# ++# I2C RTC drivers ++# ++# CONFIG_RTC_DRV_ABB5ZES3 is not set ++# CONFIG_RTC_DRV_ABEOZ9 is not set ++# CONFIG_RTC_DRV_ABX80X is not set ++CONFIG_RTC_DRV_DS1307=y ++# CONFIG_RTC_DRV_DS1307_CENTURY is not set ++# CONFIG_RTC_DRV_DS1374 is not set ++# CONFIG_RTC_DRV_DS1672 is not set ++# CONFIG_RTC_DRV_HYM8563 is not set ++# CONFIG_RTC_DRV_MAX6900 is not set ++# CONFIG_RTC_DRV_RS5C372 is not set ++CONFIG_RTC_DRV_ISL1208=y ++# CONFIG_RTC_DRV_ISL12022 is not set ++# CONFIG_RTC_DRV_ISL12026 is not set ++# CONFIG_RTC_DRV_X1205 is not set ++CONFIG_RTC_DRV_PCF8523=y ++# CONFIG_RTC_DRV_PCF85063 is not set ++# CONFIG_RTC_DRV_PCF85363 is not set ++CONFIG_RTC_DRV_PCF8563=y ++# CONFIG_RTC_DRV_PCF8583 is not set ++CONFIG_RTC_DRV_M41T80=y ++# CONFIG_RTC_DRV_M41T80_WDT is not set ++# CONFIG_RTC_DRV_BQ32K is not set ++CONFIG_RTC_DRV_RC5T619=y ++# CONFIG_RTC_DRV_S35390A is not set ++# CONFIG_RTC_DRV_FM3130 is not set ++# CONFIG_RTC_DRV_RX8010 is not set ++# CONFIG_RTC_DRV_RX8581 is not set ++# CONFIG_RTC_DRV_RX8025 is not set ++# CONFIG_RTC_DRV_EM3027 is not set ++# CONFIG_RTC_DRV_RV3028 is not set ++# CONFIG_RTC_DRV_RV3032 is not set ++# CONFIG_RTC_DRV_RV8803 is not set ++# CONFIG_RTC_DRV_SD3078 is not set ++ ++# ++# SPI RTC drivers ++# ++CONFIG_RTC_I2C_AND_SPI=y ++ ++# ++# SPI and I2C RTC drivers ++# ++# CONFIG_RTC_DRV_DS3232 is not set ++# CONFIG_RTC_DRV_PCF2127 is not set ++# CONFIG_RTC_DRV_RV3029C2 is not set ++# CONFIG_RTC_DRV_RX6110 is not set ++ ++# ++# Platform RTC drivers ++# ++# CONFIG_RTC_DRV_CMOS is not set ++# CONFIG_RTC_DRV_DS1286 is not set ++# CONFIG_RTC_DRV_DS1511 is not set ++# CONFIG_RTC_DRV_DS1553 is not set ++# CONFIG_RTC_DRV_DS1685_FAMILY is not set ++# CONFIG_RTC_DRV_DS1742 is not set ++# CONFIG_RTC_DRV_DS2404 is not set ++# CONFIG_RTC_DRV_DA9052 is not set ++CONFIG_RTC_DRV_DA9063=y ++# CONFIG_RTC_DRV_STK17TA8 is not set ++# CONFIG_RTC_DRV_M48T86 is not set ++# CONFIG_RTC_DRV_M48T35 is not set ++# CONFIG_RTC_DRV_M48T59 is not set ++# CONFIG_RTC_DRV_MSM6242 is not set ++# CONFIG_RTC_DRV_BQ4802 is not set ++# CONFIG_RTC_DRV_RP5C01 is not set ++# CONFIG_RTC_DRV_V3020 is not set ++# CONFIG_RTC_DRV_ZYNQMP is not set ++ ++# ++# on-CPU RTC drivers ++# ++# CONFIG_RTC_DRV_IMXDI is not set ++# CONFIG_RTC_DRV_CADENCE is not set ++# CONFIG_RTC_DRV_FTRTC010 is not set ++CONFIG_RTC_DRV_MC13XXX=y ++CONFIG_RTC_DRV_MXC=y ++CONFIG_RTC_DRV_MXC_V2=y ++CONFIG_RTC_DRV_SNVS=y ++# CONFIG_RTC_DRV_BBNSM is not set ++CONFIG_RTC_DRV_IMX_RPMSG=y ++# CONFIG_RTC_DRV_R7301 is not set ++ ++# ++# HID Sensor RTC drivers ++# ++# CONFIG_RTC_DRV_GOLDFISH is not set ++CONFIG_DMADEVICES=y ++# CONFIG_DMADEVICES_DEBUG is not set ++ ++# ++# DMA Devices ++# ++CONFIG_DMA_ENGINE=y ++CONFIG_DMA_VIRTUAL_CHANNELS=y ++CONFIG_DMA_OF=y ++# CONFIG_ALTERA_MSGDMA is not set ++# CONFIG_CRYPTO_DEV_FSL_CAAM_DMA is not set ++# CONFIG_DW_AXI_DMAC is not set ++CONFIG_FSL_EDMA=y ++# CONFIG_FSL_QDMA is not set ++# CONFIG_FSL_EDMA_V3 is not set ++# CONFIG_IMX_DMA is not set ++CONFIG_IMX_SDMA=y ++# CONFIG_INTEL_IDMA64 is not set ++CONFIG_MXS_DMA=y ++# CONFIG_MXC_PXP_V2 is not set ++# CONFIG_MXC_PXP_V3 is not set ++# CONFIG_MX3_IPU is not set ++# CONFIG_NBPFAXI_DMA is not set ++# CONFIG_XILINX_ZYNQMP_DPDMA is not set ++# CONFIG_QCOM_HIDMA_MGMT is not set ++# CONFIG_QCOM_HIDMA is not set ++# CONFIG_DW_DMAC is not set ++# CONFIG_SF_PDMA is not set ++ ++# ++# DMA Clients ++# ++# CONFIG_ASYNC_TX_DMA is not set ++# CONFIG_DMATEST is not set ++ ++# ++# DMABUF options ++# ++CONFIG_SYNC_FILE=y ++# CONFIG_UDMABUF is not set ++# CONFIG_DMABUF_MOVE_NOTIFY is not set ++# CONFIG_DMABUF_DEBUG is not set ++# CONFIG_DMABUF_SELFTESTS is not set ++# CONFIG_DMABUF_HEAPS is not set ++# CONFIG_DMABUF_SYSFS_STATS is not set ++# end of DMABUF options ++ ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VFIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++CONFIG_VIRTIO=y ++# CONFIG_VIRTIO_MENU is not set ++# CONFIG_VDPA is not set ++# CONFIG_VHOST_MENU is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# end of Microsoft Hyper-V guest support ++ ++# CONFIG_GREYBUS is not set ++# CONFIG_COMEDI is not set ++# CONFIG_STAGING is not set ++# CONFIG_GOLDFISH is not set ++# CONFIG_CHROME_PLATFORMS is not set ++# CONFIG_MELLANOX_PLATFORM is not set ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Clock driver for ARM Reference designs ++# ++# CONFIG_ICST is not set ++# CONFIG_CLK_SP810 is not set ++# end of Clock driver for ARM Reference designs ++ ++# CONFIG_COMMON_CLK_MAX9485 is not set ++# CONFIG_COMMON_CLK_SI5341 is not set ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI514 is not set ++# CONFIG_COMMON_CLK_SI544 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_CDCE706 is not set ++# CONFIG_COMMON_CLK_CDCE925 is not set ++# CONFIG_COMMON_CLK_CS2000_CP is not set ++# CONFIG_COMMON_CLK_AXI_CLKGEN is not set ++# CONFIG_COMMON_CLK_VC5 is not set ++# CONFIG_COMMON_CLK_FIXED_MMIO is not set ++CONFIG_MXC_CLK=y ++CONFIG_CLK_IMX6Q=y ++CONFIG_CLK_IMX6SL=y ++CONFIG_CLK_IMX6SLL=y ++CONFIG_CLK_IMX6SX=y ++CONFIG_CLK_IMX6UL=y ++CONFIG_CLK_IMX7D=y ++# CONFIG_CLK_IMX8MM is not set ++# CONFIG_CLK_IMX8MN is not set ++# CONFIG_CLK_IMX8MP is not set ++# CONFIG_CLK_IMX8MQ is not set ++# CONFIG_CLK_IMX8ULP is not set ++# CONFIG_CLK_IMX93 is not set ++# CONFIG_ARCH_S32_CLK is not set ++# CONFIG_XILINX_VCU is not set ++# CONFIG_HWSPINLOCK is not set ++ ++# ++# Clock Source drivers ++# ++CONFIG_TIMER_OF=y ++CONFIG_TIMER_PROBE=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++CONFIG_CLKSRC_IMX_GPT=y ++# CONFIG_MICROCHIP_PIT64B is not set ++# end of Clock Source drivers ++ ++CONFIG_MAILBOX=y ++CONFIG_IMX_MBOX=y ++# CONFIG_PLATFORM_MHU is not set ++# CONFIG_ALTERA_MBOX is not set ++# CONFIG_MAILBOX_TEST is not set ++CONFIG_IOMMU_SUPPORT=y ++ ++# ++# Generic IOMMU Pagetable Support ++# ++# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set ++# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set ++# end of Generic IOMMU Pagetable Support ++ ++# CONFIG_ARM_SMMU is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_REMOTEPROC is not set ++# end of Remoteproc drivers ++ ++# ++# Rpmsg drivers ++# ++CONFIG_RPMSG=y ++# CONFIG_RPMSG_CHAR is not set ++# CONFIG_RPMSG_CTRL is not set ++CONFIG_RPMSG_NS=y ++# CONFIG_RPMSG_QCOM_GLINK_RPM is not set ++CONFIG_RPMSG_VIRTIO=y ++# CONFIG_IMX_RPMSG_PINGPONG is not set ++# CONFIG_IMX_RPMSG_TTY is not set ++# end of Rpmsg drivers ++ ++# CONFIG_SOUNDWIRE is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++ ++# ++# Amlogic SoC drivers ++# ++# end of Amlogic SoC drivers ++ ++# ++# Broadcom SoC drivers ++# ++# CONFIG_SOC_BRCMSTB is not set ++# end of Broadcom SoC drivers ++ ++# ++# NXP/Freescale QorIQ SoC drivers ++# ++# CONFIG_QUICC_ENGINE is not set ++# CONFIG_FSL_QIXIS is not set ++# end of NXP/Freescale QorIQ SoC drivers ++ ++# ++# i.MX SoC drivers ++# ++# CONFIG_SOC_IMX_MU is not set ++# CONFIG_SOC_IMX9 is not set ++# CONFIG_SOC_IMX8M is not set ++# CONFIG_RPMSG_LIFE_CYCLE is not set ++# CONFIG_IMX8ULP_LPM_CTRL is not set ++# end of i.MX SoC drivers ++ ++# ++# Enable LiteX SoC Builder specific drivers ++# ++# CONFIG_LITEX_SOC_CONTROLLER is not set ++# end of Enable LiteX SoC Builder specific drivers ++ ++# ++# Qualcomm SoC drivers ++# ++# end of Qualcomm SoC drivers ++ ++# CONFIG_SOC_TI is not set ++ ++# ++# Xilinx SoC drivers ++# ++# end of Xilinx SoC drivers ++# end of SOC (System On Chip) specific Drivers ++ ++# CONFIG_PM_DEVFREQ is not set ++CONFIG_EXTCON=y ++ ++# ++# Extcon Device Drivers ++# ++# CONFIG_EXTCON_FSA9480 is not set ++# CONFIG_EXTCON_GPIO is not set ++# CONFIG_EXTCON_MAX3355 is not set ++# CONFIG_EXTCON_PTN5150 is not set ++# CONFIG_EXTCON_RT8973A is not set ++# CONFIG_EXTCON_SM5502 is not set ++CONFIG_EXTCON_USB_GPIO=y ++# CONFIG_EXTCON_USBC_TUSB320 is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++ ++# ++# IRQ chip support ++# ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++CONFIG_ARM_GIC_MAX_NR=1 ++# CONFIG_AL_FIC is not set ++CONFIG_IMX_GPCV2=y ++# CONFIG_IMX_IRQSTEER is not set ++CONFIG_IMX_INTMUX=y ++# end of IRQ chip support ++ ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++CONFIG_RESET_DISPMIX=y ++CONFIG_RESET_IMX7=y ++# CONFIG_RESET_IMX8ULP_SIM is not set ++CONFIG_RESET_GPIO=y ++# CONFIG_RESET_TI_SYSCON is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_PHY_CAN_TRANSCEIVER is not set ++# CONFIG_PHY_MIXEL_LVDS is not set ++# CONFIG_PHY_MIXEL_LVDS_COMBO is not set ++# CONFIG_BCM_KONA_USB2_PHY is not set ++# CONFIG_PHY_CADENCE_TORRENT is not set ++# CONFIG_PHY_CADENCE_DPHY is not set ++# CONFIG_PHY_CADENCE_SIERRA is not set ++# CONFIG_PHY_CADENCE_SALVO is not set ++# CONFIG_PHY_FSL_IMX8MP_LVDS is not set ++# CONFIG_PHY_FSL_IMX8MQ_USB is not set ++# CONFIG_PHY_FSL_IMX93_MIPI_DPHY is not set ++# CONFIG_PHY_MIXEL_MIPI_DPHY is not set ++# CONFIG_PHY_SAMSUNG_HDMI_PHY is not set ++CONFIG_PHY_FSL_IMX_PCIE=y ++# CONFIG_PHY_FSL_LYNX_28G is not set ++# CONFIG_PHY_PXA_28NM_HSIC is not set ++# CONFIG_PHY_PXA_28NM_USB2 is not set ++# CONFIG_PHY_OCELOT_SERDES is not set ++# end of PHY Subsystem ++ ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_RAS is not set ++ ++# ++# Android ++# ++# CONFIG_ANDROID is not set ++# end of Android ++ ++# CONFIG_DAX is not set ++CONFIG_NVMEM=y ++CONFIG_NVMEM_SYSFS=y ++# CONFIG_NVMEM_IMX_IIM is not set ++CONFIG_NVMEM_IMX_OCOTP=y ++CONFIG_NVMEM_SNVS_LPGPR=y ++# CONFIG_NVMEM_RMEM is not set ++ ++# ++# HW tracing support ++# ++# CONFIG_STM is not set ++# CONFIG_INTEL_TH is not set ++# end of HW tracing support ++ ++# CONFIG_FPGA is not set ++# CONFIG_FSI is not set ++CONFIG_TEE=y ++ ++# ++# TEE drivers ++# ++CONFIG_OPTEE=y ++CONFIG_OPTEE_SHM_NUM_PRIV_PAGES=1 ++# end of TEE drivers ++ ++CONFIG_PM_OPP=y ++# CONFIG_SIOX is not set ++# CONFIG_SLIMBUS is not set ++# CONFIG_INTERCONNECT is not set ++# CONFIG_COUNTER is not set ++# CONFIG_MOST is not set ++ ++# ++# MXC support drivers ++# ++# CONFIG_MXC_SIM is not set ++# CONFIG_MXC_IPU is not set ++ ++# ++# MXC Vivante GPU support ++# ++# CONFIG_MXC_GPU_VIV is not set ++# end of MXC Vivante GPU support ++ ++# ++# MXC SIM Support ++# ++# end of MXC SIM Support ++ ++# ++# MXC Media Local Bus Driver ++# ++CONFIG_MXC_MLB=y ++CONFIG_MXC_MLB150=y ++# end of MXC Media Local Bus Driver ++ ++# ++# MXC HDMI CEC (Consumer Electronics Control) support ++# ++# end of MXC HDMI CEC (Consumer Electronics Control) support ++ ++# ++# MXC MIPI Support ++# ++CONFIG_MXC_MIPI_CSI2=y ++# end of MXC MIPI Support ++ ++# ++# MXC VPU(Video Processing Unit) support ++# ++CONFIG_MXC_VPU=y ++# CONFIG_MXC_VPU_DEBUG is not set ++# CONFIG_MX6_VPU_352M is not set ++# end of MXC VPU(Video Processing Unit) support ++# end of MXC support drivers ++# end of Device Drivers ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_VALIDATE_FS_PARSER is not set ++CONFIG_FS_IOMAP=y ++CONFIG_EXT2_FS=y ++# CONFIG_EXT2_FS_XATTR is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_EXPORTFS=y ++# CONFIG_EXPORTFS_BLOCK_OPS is not set ++CONFIG_FILE_LOCKING=y ++# CONFIG_FS_ENCRYPTION is not set ++# CONFIG_FS_VERITY is not set ++# CONFIG_DNOTIFY is not set ++# CONFIG_INOTIFY_USER is not set ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++# end of Caches ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++# end of CD-ROM/DVD Filesystems ++ ++# ++# DOS/FAT/EXFAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_FAT_DEFAULT_UTF8 is not set ++# CONFIG_EXFAT_FS is not set ++# CONFIG_NTFS_FS is not set ++# CONFIG_NTFS3_FS is not set ++# end of DOS/FAT/EXFAT/NT Filesystems ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_PROC_CHILDREN=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_TMPFS_XATTR=y ++CONFIG_MEMFD_CREATE=y ++CONFIG_CONFIGFS_FS=y ++# end of Pseudo filesystems ++ ++# CONFIG_MISC_FILESYSTEMS is not set ++# CONFIG_NETWORK_FILESYSTEMS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="cp437" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++CONFIG_NLS_ISO8859_15=y ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++# CONFIG_UNICODE is not set ++CONFIG_IO_WQ=y ++# end of File systems ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y ++# CONFIG_HARDENED_USERCOPY is not set ++# CONFIG_FORTIFY_SOURCE is not set ++# CONFIG_STATIC_USERMODEHELPER is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity,bpf" ++ ++# ++# Kernel hardening options ++# ++ ++# ++# Memory initialization ++# ++CONFIG_INIT_STACK_NONE=y ++# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set ++# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set ++CONFIG_CC_HAS_ZERO_CALL_USED_REGS=y ++# CONFIG_ZERO_CALL_USED_REGS is not set ++# end of Memory initialization ++# end of Kernel hardening options ++# end of Security options ++ ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_SKCIPHER=y ++CONFIG_CRYPTO_SKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_RNG_DEFAULT=y ++CONFIG_CRYPTO_AKCIPHER2=y ++CONFIG_CRYPTO_AKCIPHER=y ++CONFIG_CRYPTO_KPP2=y ++CONFIG_CRYPTO_KPP=y ++CONFIG_CRYPTO_ACOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++CONFIG_CRYPTO_USER=y ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++CONFIG_CRYPTO_GF128MUL=y ++CONFIG_CRYPTO_NULL=y ++CONFIG_CRYPTO_NULL2=y ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_AUTHENC=y ++# CONFIG_CRYPTO_TEST is not set ++CONFIG_CRYPTO_ENGINE=y ++ ++# ++# Public-key cryptography ++# ++CONFIG_CRYPTO_RSA=y ++# CONFIG_CRYPTO_DH is not set ++CONFIG_CRYPTO_ECC=y ++CONFIG_CRYPTO_ECDH=y ++# CONFIG_CRYPTO_ECDSA is not set ++# CONFIG_CRYPTO_ECRDSA is not set ++# CONFIG_CRYPTO_SM2 is not set ++# CONFIG_CRYPTO_CURVE25519 is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=y ++CONFIG_CRYPTO_GCM=y ++# CONFIG_CRYPTO_CHACHA20POLY1305 is not set ++# CONFIG_CRYPTO_AEGIS128 is not set ++# CONFIG_CRYPTO_SEQIV is not set ++CONFIG_CRYPTO_ECHAINIV=y ++CONFIG_CRYPTO_TLS=y ++ ++# ++# Block modes ++# ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_CFB=y ++CONFIG_CRYPTO_CTR=y ++CONFIG_CRYPTO_CTS=y ++CONFIG_CRYPTO_ECB=y ++CONFIG_CRYPTO_LRW=y ++CONFIG_CRYPTO_OFB=y ++CONFIG_CRYPTO_PCBC=y ++CONFIG_CRYPTO_XTS=y ++# CONFIG_CRYPTO_KEYWRAP is not set ++# CONFIG_CRYPTO_ADIANTUM is not set ++CONFIG_CRYPTO_ESSIV=y ++ ++# ++# Hash modes ++# ++CONFIG_CRYPTO_CMAC=y ++CONFIG_CRYPTO_HMAC=y ++CONFIG_CRYPTO_XCBC=y ++CONFIG_CRYPTO_VMAC=y ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++CONFIG_CRYPTO_XXHASH=y ++CONFIG_CRYPTO_BLAKE2B=y ++CONFIG_CRYPTO_BLAKE2S=y ++CONFIG_CRYPTO_CRCT10DIF=y ++CONFIG_CRYPTO_GHASH=y ++# CONFIG_CRYPTO_POLY1305 is not set ++CONFIG_CRYPTO_MD4=y ++CONFIG_CRYPTO_MD5=y ++CONFIG_CRYPTO_MICHAEL_MIC=y ++CONFIG_CRYPTO_RMD160=y ++CONFIG_CRYPTO_SHA1=y ++CONFIG_CRYPTO_SHA256=y ++CONFIG_CRYPTO_SHA512=y ++CONFIG_CRYPTO_SHA3=y ++CONFIG_CRYPTO_SM3=y ++CONFIG_CRYPTO_STREEBOG=y ++CONFIG_CRYPTO_WP512=y ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_TI is not set ++CONFIG_CRYPTO_ANUBIS=y ++CONFIG_CRYPTO_ARC4=y ++CONFIG_CRYPTO_BLOWFISH=y ++CONFIG_CRYPTO_BLOWFISH_COMMON=y ++CONFIG_CRYPTO_CAMELLIA=y ++CONFIG_CRYPTO_CAST_COMMON=y ++CONFIG_CRYPTO_CAST5=y ++CONFIG_CRYPTO_CAST6=y ++CONFIG_CRYPTO_DES=y ++CONFIG_CRYPTO_FCRYPT=y ++CONFIG_CRYPTO_KHAZAD=y ++# CONFIG_CRYPTO_CHACHA20 is not set ++CONFIG_CRYPTO_SEED=y ++CONFIG_CRYPTO_SERPENT=y ++CONFIG_CRYPTO_SM4=y ++CONFIG_CRYPTO_TEA=y ++CONFIG_CRYPTO_TWOFISH=y ++CONFIG_CRYPTO_TWOFISH_COMMON=y ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_842 is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++CONFIG_CRYPTO_ZSTD=y ++ ++# ++# Random Number Generation ++# ++CONFIG_CRYPTO_ANSI_CPRNG=y ++CONFIG_CRYPTO_DRBG_MENU=y ++CONFIG_CRYPTO_DRBG_HMAC=y ++# CONFIG_CRYPTO_DRBG_HASH is not set ++# CONFIG_CRYPTO_DRBG_CTR is not set ++CONFIG_CRYPTO_DRBG=y ++CONFIG_CRYPTO_JITTERENTROPY=y ++CONFIG_CRYPTO_USER_API=y ++CONFIG_CRYPTO_USER_API_HASH=y ++CONFIG_CRYPTO_USER_API_SKCIPHER=y ++CONFIG_CRYPTO_USER_API_RNG=y ++# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set ++CONFIG_CRYPTO_USER_API_AEAD=y ++CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE=y ++# CONFIG_CRYPTO_STATS is not set ++CONFIG_CRYPTO_HW=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_COMMON=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API_DESC=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_KEYBLOB_API_DESC=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y ++CONFIG_CRYPTO_DEV_FSL_CAAM=y ++# CONFIG_CRYPTO_DEV_FSL_CAAM_DEBUG is not set ++CONFIG_CRYPTO_DEV_FSL_CAAM_JR=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_RINGSIZE=9 ++# CONFIG_CRYPTO_DEV_FSL_CAAM_INTC is not set ++CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_PKC_API=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_PRNG_API=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API=y ++# CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API_TEST is not set ++# CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST is not set ++CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE=7 ++# CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST is not set ++CONFIG_CRYPTO_DEV_SAHARA=y ++# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set ++# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set ++CONFIG_CRYPTO_DEV_MXS_DCP=y ++# CONFIG_CRYPTO_DEV_VIRTIO is not set ++# CONFIG_CRYPTO_DEV_SAFEXCEL is not set ++# CONFIG_CRYPTO_DEV_CCREE is not set ++# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set ++ ++# ++# Certificates for signature checking ++# ++# end of Certificates for signature checking ++ ++# ++# Library routines ++# ++CONFIG_LINEAR_RANGES=y ++# CONFIG_PACKING is not set ++CONFIG_BITREVERSE=y ++CONFIG_HAVE_ARCH_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++# CONFIG_CORDIC is not set ++# CONFIG_PRIME_NUMBERS is not set ++CONFIG_RATIONAL=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_STMP_DEVICE=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++ ++# ++# Crypto library routines ++# ++CONFIG_CRYPTO_LIB_AES=y ++CONFIG_CRYPTO_LIB_ARC4=y ++CONFIG_CRYPTO_ARCH_HAVE_LIB_BLAKE2S=y ++CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y ++CONFIG_CRYPTO_ARCH_HAVE_LIB_CHACHA=y ++# CONFIG_CRYPTO_LIB_CHACHA is not set ++# CONFIG_CRYPTO_LIB_CURVE25519 is not set ++CONFIG_CRYPTO_LIB_DES=y ++CONFIG_CRYPTO_LIB_POLY1305_RSIZE=9 ++CONFIG_CRYPTO_ARCH_HAVE_LIB_POLY1305=y ++# CONFIG_CRYPTO_LIB_POLY1305 is not set ++# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set ++CONFIG_CRYPTO_LIB_SHA256=y ++CONFIG_CRYPTO_LIB_SM4=y ++# end of Crypto library routines ++ ++CONFIG_LIB_MEMNEQ=y ++CONFIG_CRC_CCITT=y ++CONFIG_CRC16=y ++CONFIG_CRC_T10DIF=y ++CONFIG_CRC_ITU_T=y ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC64 is not set ++# CONFIG_CRC4 is not set ++CONFIG_CRC7=y ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++CONFIG_XXHASH=y ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_ZSTD_COMPRESS=y ++CONFIG_ZSTD_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DMA_OPS=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_DMA_DECLARE_COHERENT=y ++CONFIG_ARCH_HAS_SETUP_DMA_OPS=y ++CONFIG_ARCH_HAS_TEARDOWN_DMA_OPS=y ++CONFIG_DMA_NONCOHERENT_MMAP=y ++CONFIG_DMA_REMAP=y ++CONFIG_DMA_CMA=y ++# CONFIG_DMA_PERNUMA_CMA is not set ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++# CONFIG_DMA_API_DEBUG is not set ++CONFIG_SGL_ALLOC=y ++CONFIG_DQL=y ++CONFIG_GLOB=y ++# CONFIG_GLOB_SELFTEST is not set ++CONFIG_NLATTR=y ++CONFIG_CLZ_TAB=y ++# CONFIG_IRQ_POLL is not set ++CONFIG_MPILIB=y ++CONFIG_LIBFDT=y ++CONFIG_HAVE_GENERIC_VDSO=y ++CONFIG_GENERIC_GETTIMEOFDAY=y ++CONFIG_GENERIC_VDSO_32=y ++CONFIG_SBITMAP=y ++# end of Library routines ++ ++CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++# CONFIG_PRINTK_CALLER is not set ++# CONFIG_STACKTRACE_BUILD_ID is not set ++CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 ++CONFIG_CONSOLE_LOGLEVEL_QUIET=4 ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++# CONFIG_DYNAMIC_DEBUG is not set ++# CONFIG_DYNAMIC_DEBUG_CORE is not set ++# CONFIG_SYMBOLIC_ERRNAME is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# end of printk and dmesg options ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_HEADERS_INSTALL is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set ++# CONFIG_VMLINUX_MAP is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# end of Compile-time checks and compiler options ++ ++# ++# Generic Kernel Debugging Instruments ++# ++CONFIG_MAGIC_SYSRQ=y ++CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 ++CONFIG_MAGIC_SYSRQ_SERIAL=y ++CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE="" ++# CONFIG_DEBUG_FS is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_UBSAN is not set ++CONFIG_HAVE_KCSAN_COMPILER=y ++# end of Generic Kernel Debugging Instruments ++ ++CONFIG_DEBUG_KERNEL=y ++CONFIG_DEBUG_MISC=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_PAGE_EXTENSION is not set ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_PAGE_OWNER is not set ++# CONFIG_PAGE_POISONING is not set ++# CONFIG_DEBUG_RODATA_TEST is not set ++# CONFIG_DEBUG_WX is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y ++# CONFIG_DEBUG_VIRTUAL is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++CONFIG_HAVE_ARCH_KASAN=y ++CONFIG_CC_HAS_KASAN_GENERIC=y ++CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y ++# CONFIG_KASAN is not set ++# end of Memory Debugging ++ ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Oops, Lockups and Hangs ++# ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++# CONFIG_SOFTLOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_WQ_WATCHDOG is not set ++# CONFIG_TEST_LOCKUP is not set ++# end of Debug Oops, Lockups and Hangs ++ ++# ++# Scheduler Debugging ++# ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# end of Scheduler Debugging ++ ++# CONFIG_DEBUG_TIMEKEEPING is not set ++CONFIG_DEBUG_PREEMPT=y ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++CONFIG_LOCK_DEBUGGING_SUPPORT=y ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_RWSEMS is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++# CONFIG_WW_MUTEX_SELFTEST is not set ++# CONFIG_SCF_TORTURE_TEST is not set ++# end of Lock Debugging (spinlocks, mutexes, etc...) ++ ++# CONFIG_DEBUG_IRQFLAGS is not set ++# CONFIG_STACKTRACE is not set ++# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set ++# CONFIG_DEBUG_KOBJECT is not set ++ ++# ++# Debug kernel data structures ++# ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PLIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_BUG_ON_DATA_CORRUPTION is not set ++# end of Debug kernel data structures ++ ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_RCU_SCALE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_REF_SCALE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=21 ++CONFIG_RCU_TRACE=y ++# CONFIG_RCU_EQS_DEBUG is not set ++# end of RCU Debugging ++ ++# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACE_CLOCK=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++# CONFIG_SAMPLES is not set ++ ++# ++# arm Debugging ++# ++# CONFIG_ARM_PTDUMP_DEBUGFS is not set ++# CONFIG_UNWINDER_FRAME_POINTER is not set ++CONFIG_UNWINDER_ARM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_CORESIGHT is not set ++# end of arm Debugging ++ ++# ++# Kernel Testing and Coverage ++# ++# CONFIG_KUNIT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_ARCH_HAS_KCOV=y ++CONFIG_CC_HAS_SANCOV_TRACE_PC=y ++# CONFIG_KCOV is not set ++# CONFIG_RUNTIME_TESTING_MENU is not set ++CONFIG_ARCH_USE_MEMTEST=y ++# CONFIG_MEMTEST is not set ++# end of Kernel Testing and Coverage ++# end of Kernel hacking +diff --git a/arch/arm/configs/imx_avb.config b/arch/arm/configs/imx_avb.config +new file mode 100644 +index 000000000000..49b8d258c306 +--- /dev/null ++++ b/arch/arm/configs/imx_avb.config +@@ -0,0 +1,8 @@ ++CONFIG_EXPERT=y ++CONFIG_PREEMPT_RT=y ++CONFIG_AVB_SUPPORT=y ++CONFIG_NET_SWITCHDEV=y ++CONFIG_DEBUG_INFO=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y +diff --git a/arch/arm/configs/imx_up.config b/arch/arm/configs/imx_up.config +new file mode 100644 +index 000000000000..18b877a41fa7 +--- /dev/null ++++ b/arch/arm/configs/imx_up.config +@@ -0,0 +1,2 @@ ++CONFIG_SMP=n ++CONFIG_HIGHMEM=n +diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig +index 591aaaeffc04..b9dc12e991e2 100644 +--- a/arch/arm/configs/imx_v6_v7_defconfig ++++ b/arch/arm/configs/imx_v6_v7_defconfig +@@ -49,7 +49,8 @@ CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" + CONFIG_KEXEC=y + CONFIG_CPU_FREQ=y + CONFIG_CPU_FREQ_STAT=y +-CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_STAT_DETAILS=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y + CONFIG_CPU_FREQ_GOV_POWERSAVE=y + CONFIG_CPU_FREQ_GOV_USERSPACE=y + CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +diff --git a/arch/arm/configs/imx_v7_defconfig b/arch/arm/configs/imx_v7_defconfig +index 87c777f33baa..bd5ee5bf5ec9 100644 +--- a/arch/arm/configs/imx_v7_defconfig ++++ b/arch/arm/configs/imx_v7_defconfig +@@ -25,7 +25,7 @@ CONFIG_SOC_IMX6SLL=y + CONFIG_SOC_IMX6SX=y + CONFIG_SOC_IMX6UL=y + CONFIG_SOC_IMX7D=y +-CONFIG_SOC_IMX7ULP=y ++# CONFIG_SOC_IMX7ULP is not set + CONFIG_SMP=y + CONFIG_VMSPLIT_2G=y + CONFIG_ARM_PSCI=y +@@ -35,7 +35,8 @@ CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" + CONFIG_KEXEC=y + CONFIG_CPU_FREQ=y + CONFIG_CPU_FREQ_STAT=y +-CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_STAT_DETAILS=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y + CONFIG_CPU_FREQ_GOV_POWERSAVE=y + CONFIG_CPU_FREQ_GOV_USERSPACE=y + CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +@@ -287,7 +288,7 @@ CONFIG_REGULATOR_LTC3676=y + CONFIG_REGULATOR_MAX17135=y + CONFIG_REGULATOR_MC13783=y + CONFIG_REGULATOR_MC13892=y +-CONFIG_REGULATOR_PF1550_RPMSG=y ++# CONFIG_REGULATOR_PF1550_RPMSG is not set + CONFIG_REGULATOR_PFUZE100=y + CONFIG_REGULATOR_RN5T618=y + CONFIG_RC_CORE=y +@@ -594,3 +595,5 @@ CONFIG_CRYPTO_SM3_GENERIC=m + CONFIG_CRYPTO_SM4_GENERIC=m + CONFIG_CRYPTO_ARIA=m + CONFIG_CRYPTO_POLYVAL_ARM64_CE=m ++ ++CONFIG_PREEMPT_RT=y +diff --git a/arch/arm/configs/lsdk.config b/arch/arm/configs/lsdk.config +index d10724fbff81..1fcf47b5c19d 100644 +--- a/arch/arm/configs/lsdk.config ++++ b/arch/arm/configs/lsdk.config +@@ -64,6 +64,26 @@ CONFIG_IP6_NF_NAT=y + CONFIG_IP6_NF_FILTER=y + CONFIG_NETFILTER_XT_MATCH_COMMENT=y + ++# can ++CONFIG_CAN_FLEXCAN=y ++# dspi ++CONFIG_SPI_BITBANG=y ++CONFIG_SPI_FSL_DSPI=y ++# watchdog ++CONFIG_IMX2_WDT=y ++# sound ++CONFIG_SND_MIXER_OSS=y ++CONFIG_SND_PCM_OSS=y ++CONFIG_SND_SOC_FSL_SAI=y ++CONFIG_SND_SOC_SGTL5000=y ++CONFIG_SND_SIMPLE_CARD=y ++# Video ++CONFIG_FB=y ++CONFIG_FB_FSL_DCU=y ++CONFIG_FB_FSL_SII902X=y ++CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y ++CONFIG_LOGO=y ++CONFIG_SOC_VF610=n + # filesystems + CONFIG_EXT4_FS_POSIX_ACL=y + CONFIG_EXT4_FS_SECURITY=y +diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig +index a20a26c864fe..f6710d000cf4 100644 +--- a/arch/arm/configs/multi_v7_defconfig ++++ b/arch/arm/configs/multi_v7_defconfig +@@ -1276,3 +1276,5 @@ CONFIG_CMA_SIZE_MBYTES=64 + CONFIG_PRINTK_TIME=y + CONFIG_MAGIC_SYSRQ=y + CONFIG_DEBUG_FS=y ++# PREEMPT RT ++CONFIG_PREEMPT_RT=y +\ No newline at end of file +diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h +index 2799dc6a8a81..d10630ddf491 100644 +--- a/arch/arm/include/asm/thread_info.h ++++ b/arch/arm/include/asm/thread_info.h +@@ -64,6 +64,7 @@ struct cpu_context_save { + struct thread_info { + unsigned long flags; /* low level flags */ + int preempt_count; /* 0 => preemptable, <0 => bug */ ++ int preempt_lazy_count; /* 0 => preemptable, <0 => bug */ + mm_segment_t addr_limit; /* address limit */ + __u32 cpu; /* cpu */ + __u32 cpu_domain; /* cpu domain */ +@@ -133,6 +134,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp *, + #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ + #define TIF_UPROBE 3 /* breakpointed or singlestepping */ + #define TIF_NOTIFY_SIGNAL 4 /* signal notifications exist */ ++#define TIF_NEED_RESCHED_LAZY 5 + + #define TIF_USING_IWMMXT 17 + #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ +@@ -152,6 +154,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp *, + #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) + #define _TIF_SECCOMP (1 << TIF_SECCOMP) + #define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) ++#define _TIF_NEED_RESCHED_LAZY (1 << TIF_NEED_RESCHED_LAZY) + #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) + + /* Checks for any syscall work in entry-common.S */ +@@ -161,7 +164,8 @@ extern int vfp_restore_user_hwstate(struct user_vfp *, + /* + * Change these and you break ASM code in entry-common.S + */ +-#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ ++#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY | \ ++ _TIF_SIGPENDING | \ + _TIF_NOTIFY_RESUME | _TIF_UPROBE | \ + _TIF_NOTIFY_SIGNAL) + +diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c +index 69eab049ea09..1e91df6537d9 100644 +--- a/arch/arm/kernel/asm-offsets.c ++++ b/arch/arm/kernel/asm-offsets.c +@@ -2,7 +2,7 @@ + /* + * Copyright (C) 1995-2003 Russell King + * 2001-2002 Keith Owens +- * ++ * + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed to extract + * and format the required data. +@@ -43,6 +43,7 @@ int main(void) + BLANK(); + DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); + DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); ++ DEFINE(TI_PREEMPT_LAZY, offsetof(struct thread_info, preempt_lazy_count)); + DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); + DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); + DEFINE(TI_CPU_DOMAIN, offsetof(struct thread_info, cpu_domain)); +@@ -171,5 +172,5 @@ int main(void) + DEFINE(KEXEC_INDIR_PAGE, offsetof(struct kexec_relocate_data, kexec_indirection_page)); + DEFINE(KEXEC_MACH_TYPE, offsetof(struct kexec_relocate_data, kexec_mach_type)); + DEFINE(KEXEC_R2, offsetof(struct kexec_relocate_data, kexec_r2)); +- return 0; ++ return 0; + } +diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S +index c39303e5c234..cfb4660e9fea 100644 +--- a/arch/arm/kernel/entry-armv.S ++++ b/arch/arm/kernel/entry-armv.S +@@ -222,11 +222,18 @@ __irq_svc: + + #ifdef CONFIG_PREEMPTION + ldr r8, [tsk, #TI_PREEMPT] @ get preempt count +- ldr r0, [tsk, #TI_FLAGS] @ get flags + teq r8, #0 @ if preempt count != 0 ++ bne 1f @ return from exeption ++ ldr r0, [tsk, #TI_FLAGS] @ get flags ++ tst r0, #_TIF_NEED_RESCHED @ if NEED_RESCHED is set ++ blne svc_preempt @ preempt! ++ ++ ldr r8, [tsk, #TI_PREEMPT_LAZY] @ get preempt lazy count ++ teq r8, #0 @ if preempt lazy count != 0 + movne r0, #0 @ force flags to 0 +- tst r0, #_TIF_NEED_RESCHED ++ tst r0, #_TIF_NEED_RESCHED_LAZY + blne svc_preempt ++1: + #endif + + svc_exit r5, irq = 1 @ return from exception +@@ -241,8 +248,14 @@ svc_preempt: + 1: bl preempt_schedule_irq @ irq en/disable is done inside + ldr r0, [tsk, #TI_FLAGS] @ get new tasks TI_FLAGS + tst r0, #_TIF_NEED_RESCHED ++ bne 1b ++ tst r0, #_TIF_NEED_RESCHED_LAZY + reteq r8 @ go again +- b 1b ++ ldr r0, [tsk, #TI_PREEMPT_LAZY] @ get preempt lazy count ++ teq r0, #0 @ if preempt lazy count != 0 ++ beq 1b ++ ret r8 @ go again ++ + #endif + + __und_fault: +diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c +index 688400336ef1..f2a5049c7083 100644 +--- a/arch/arm/kernel/signal.c ++++ b/arch/arm/kernel/signal.c +@@ -607,7 +607,8 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) + */ + trace_hardirqs_off(); + do { +- if (likely(thread_flags & _TIF_NEED_RESCHED)) { ++ if (likely(thread_flags & (_TIF_NEED_RESCHED | ++ _TIF_NEED_RESCHED_LAZY))) { + schedule(); + } else { + if (unlikely(!user_mode(regs))) +diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c +index 978db2d96b44..c0b43ac8db6e 100644 +--- a/arch/arm/kernel/smp.c ++++ b/arch/arm/kernel/smp.c +@@ -51,6 +51,10 @@ + #define CREATE_TRACE_POINTS + #include + ++#ifdef CONFIG_BAREMETAL ++#include ++#endif ++ + /* + * as from 2.5, kernels no longer have an init_tasks structure + * so we need some other way of telling a new secondary core +@@ -66,6 +70,9 @@ enum ipi_msg_type { + IPI_CPU_STOP, + IPI_IRQ_WORK, + IPI_COMPLETION, ++#ifdef CONFIG_BAREMETAL ++ IPI_BAREMETAL_COMM = 8, ++#endif + NR_IPI, + /* + * CPU_BACKTRACE is special and not included in NR_IPI +@@ -542,6 +549,9 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = { + [IPI_CPU_STOP] = "CPU stop interrupts", + [IPI_IRQ_WORK] = "IRQ work interrupts", + [IPI_COMPLETION] = "completion interrupts", ++#ifdef CONFIG_BAREMETAL ++ [IPI_BAREMETAL_COMM] = "Baremetal inter-core interrupts", ++#endif + }; + + static void smp_cross_call(const struct cpumask *target, unsigned int ipinr); +@@ -673,6 +683,18 @@ static void do_handle_IPI(int ipinr) + ipi_complete(cpu); + break; + ++#ifdef CONFIG_BAREMETAL ++#define GICC_IAR_MASK 0x1fff ++ case IPI_BAREMETAL_COMM: { ++ /* FIXME: use the fixed source coreID from core1 */ ++ int irqsrc = 1; ++ /*linux core is 0 core, so iterate from 1 core.*/ ++ for(irqsrc = 1; irqsrc < CONFIG_MAX_CPUS; irqsrc++) ++ ipi_baremetal_handle(ipinr, irqsrc); ++ } ++ break; ++#endif ++ + case IPI_CPU_BACKTRACE: + printk_deferred_enter(); + nmi_cpu_backtrace(get_irq_regs()); +diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig +index 0e672fc9a80f..1ee568a27ff5 100644 +--- a/arch/arm/mach-imx/Kconfig ++++ b/arch/arm/mach-imx/Kconfig +@@ -217,6 +217,16 @@ config SOC_LS1021A + help + This enables support for Freescale LS1021A processor. + ++config LS1021A_BAREMETAL ++ bool "NXP LS1021A baremetal support" ++ depends on SOC_LS1021A ++ select BAREMETAL ++ ++config SOC_IMX6Q_BAREMETAL ++ bool "NXP IMX6Q baremetal support" ++ depends on SOC_IMX6Q ++ select BAREMETAL ++ + endif + + if ARCH_MULTI_V7 || ARM_SINGLE_ARMV7M +diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c +index b0db85310331..77877dcb54ed 100644 +--- a/arch/arm/mm/fault.c ++++ b/arch/arm/mm/fault.c +@@ -400,6 +400,9 @@ do_translation_fault(unsigned long addr, unsigned int fsr, + if (addr < TASK_SIZE) + return do_page_fault(addr, fsr, regs); + ++ if (interrupts_enabled(regs)) ++ local_irq_enable(); ++ + if (user_mode(regs)) + goto bad_area; + +@@ -470,6 +473,9 @@ do_translation_fault(unsigned long addr, unsigned int fsr, + static int + do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) + { ++ if (interrupts_enabled(regs)) ++ local_irq_enable(); ++ + do_bad_area(addr, fsr, regs); + return 0; + } +-- +2.34.1 + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0002-RT-add-RT-localversion.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0002-RT-add-RT-localversion.patch new file mode 100644 index 000000000..f7c7d3ee4 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0002-RT-add-RT-localversion.patch @@ -0,0 +1,21 @@ +From 5a9357f51ccd92dd11f188a14edf4b92ba1543ee Mon Sep 17 00:00:00 2001 +From: Mike Engel +Date: Fri, 23 Feb 2024 10:12:23 +0100 +Subject: [PATCH 02/10] RT: add RT localversion + +Signed-off-by: Mike Engel +--- + localversion-rt | 1 + + 1 file changed, 1 insertion(+) + create mode 100644 localversion-rt + +diff --git a/localversion-rt b/localversion-rt +new file mode 100644 +index 000000000000..6e44e540b927 +--- /dev/null ++++ b/localversion-rt +@@ -0,0 +1 @@ ++-rt12 +-- +2.34.1 + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0003-arch-arm64-add-NXP-RT-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0003-arch-arm64-add-NXP-RT-support.patch new file mode 100644 index 000000000..95badd0d6 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0003-arch-arm64-add-NXP-RT-support.patch @@ -0,0 +1,5862 @@ +From 163328f80e9cf316752e46023857fb5939140652 Mon Sep 17 00:00:00 2001 +From: Mike Engel +Date: Fri, 23 Feb 2024 12:20:36 +0100 +Subject: [PATCH 03/10] arch:arm64: add NXP RT support + +Signed-off-by: Mike Engel +--- + arch/arm64/Kconfig | 2 + + arch/arm64/Kconfig.platforms | 30 ++ + arch/arm64/boot/dts/freescale/Makefile | 54 ++- + .../boot/dts/freescale/fii-ls1028a-tsn.dts | 325 ++++++++++++++++++ + .../fsl-ls1028a-rdb-dsa-swp5-eno3.dts | 37 ++ + ...sl-ls1028a-rdb-jailhouse-without-enetc.dts | 98 ++++++ + .../freescale/fsl-ls1028a-rdb-jailhouse.dts | 59 ++++ + .../dts/freescale/fsl-ls1028a-rdb-sdk-bm.dts | 15 + + .../boot/dts/freescale/fsl-ls1028a-rdb.dts | 25 ++ + .../arm64/boot/dts/freescale/fsl-ls1028a.dtsi | 2 +- + .../dts/freescale/fsl-ls1043a-rdb-sdk-bm.dts | 44 +++ + ...sl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dts | 263 ++++++++++++++ + .../fsl-ls1043a-rdb-sdk-jailhouse.dts | 267 ++++++++++++++ + .../arm64/boot/dts/freescale/fsl-ls1043a.dtsi | 2 +- + .../dts/freescale/fsl-ls1046a-rdb-sdk-bm.dts | 47 +++ + ...sl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dts | 274 +++++++++++++++ + .../fsl-ls1046a-rdb-sdk-jailhouse.dts | 278 +++++++++++++++ + .../arm64/boot/dts/freescale/fsl-ls1046a.dtsi | 2 +- + .../boot/dts/freescale/imx8-ss-audio.dtsi | 20 ++ + .../dts/freescale/imx8dxl-evk-enet0-avb.dts | 9 + + .../dts/freescale/imx8dxl-evk-enet0-avb.dtsi | 54 +++ + .../freescale/imx8dxl-evk-enet0-sja1105.dts | 168 +++++++++ + .../imx8dxl-evk-enet0-tja1100-avb.dts | 9 + + arch/arm64/boot/dts/freescale/imx8dxl-evk.dts | 58 ++++ + .../boot/dts/freescale/imx8dxl-ss-adma.dtsi | 1 + + .../dts/freescale/imx8m-generic-mbox-1.dtsi | 27 ++ + .../dts/freescale/imx8m-generic-mbox.dtsi | 27 ++ + .../dts/freescale/imx8m-rpmsg-ca53-1.dtsi | 37 ++ + .../boot/dts/freescale/imx8m-rpmsg-ca53.dtsi | 37 ++ + .../boot/dts/freescale/imx8mm-evk-avb.dts | 37 ++ + .../dts/freescale/imx8mm-evk-baremetal.dts | 39 +++ + .../boot/dts/freescale/imx8mm-evk-ecat.dts | 10 + + .../freescale/imx8mm-evk-multicore-rpmsg.dts | 10 + + .../freescale/imx8mm-evk-multicore-rtos.dts | 79 +++++ + .../dts/freescale/imx8mm-evk-rpmsg-8m-buf.dts | 228 ++++++++++++ + .../dts/freescale/imx8mm-evk-rpmsg-ca53.dts | 39 +++ + .../boot/dts/freescale/imx8mm-evk-rpmsg.dts | 96 +++++- + .../freescale/imx8mm-evk-virtio-net-ca53.dts | 82 +++++ + .../freescale/imx8mm-evk-virtio-net-cm4.dts | 81 +++++ + .../freescale/imx8mm-evk-virtio-perf-ca53.dts | 78 +++++ + .../freescale/imx8mm-evk-virtio-perf-cm4.dts | 70 ++++ + arch/arm64/boot/dts/freescale/imx8mm.dtsi | 11 + + .../boot/dts/freescale/imx8mp-evk-avb.dts | 42 +++ + .../dts/freescale/imx8mp-evk-baremetal.dts | 37 ++ + .../boot/dts/freescale/imx8mp-evk-dsa.dts | 79 +++++ + .../boot/dts/freescale/imx8mp-evk-ecat.dts | 10 + + .../freescale/imx8mp-evk-multicore-lwip.dts | 21 ++ + .../freescale/imx8mp-evk-multicore-rpmsg.dts | 117 +++++++ + .../freescale/imx8mp-evk-multicore-rtos.dts | 79 +++++ + .../freescale/imx8mp-evk-virtio-net-ca53.dts | 81 +++++ + .../freescale/imx8mp-evk-virtio-net-cm7.dts | 72 ++++ + .../dts/freescale/imx8mp-generic-mbox-1.dtsi | 27 ++ + .../dts/freescale/imx8mp-generic-mbox.dtsi | 27 ++ + .../dts/freescale/imx8mp-rpmsg-ca53-1.dtsi | 37 ++ + .../boot/dts/freescale/imx8mp-rpmsg-ca53.dtsi | 37 ++ + arch/arm64/boot/dts/freescale/imx8mp.dtsi | 10 + + .../dts/freescale/imx93-11x11-evk-avb.dts | 41 +++ + .../freescale/imx93-11x11-evk-baremetal.dts | 31 ++ + .../dts/freescale/imx93-11x11-evk-dsa.dts | 96 ++++++ + .../dts/freescale/imx93-11x11-evk-ecat.dts | 11 + + .../imx93-11x11-evk-multicore-rtos.dts | 38 ++ + .../imx93-11x11-evk-uart-sharing-cm33.dts | 96 ++++++ + .../imx93-11x11-evk-virtio-net-ca55.dts | 55 +++ + .../imx93-11x11-evk-virtio-net-cm33.dts | 53 +++ + .../boot/dts/freescale/imx93-11x11-evk.dts | 60 +++- + .../dts/freescale/imx93-9x9-qsb-baremetal.dts | 31 ++ + .../dts/freescale/imx93-9x9-qsb-inmate.dts | 151 ++++++++ + .../boot/dts/freescale/imx93-9x9-qsb-root.dts | 54 +++ + .../imx93-9x9-qsb-uart-sharing-cm33.dts | 96 ++++++ + .../boot/dts/freescale/imx93-9x9-qsb.dts | 58 ++++ + .../dts/freescale/imx93-generic-mbox.dtsi | 27 ++ + arch/arm64/configs/imx_avb.config | 8 + + arch/arm64/configs/imx_v8_defconfig | 38 +- + arch/arm64/configs/linux-dpaa-ethercat.config | 1 + + arch/arm64/configs/linux-rpmsg-8m-buf.config | 1 + + arch/arm64/configs/lsdk.config | 25 +- + arch/arm64/include/asm/preempt.h | 25 +- + arch/arm64/include/asm/thread_info.h | 8 +- + arch/arm64/kernel/asm-offsets.c | 1 + + arch/arm64/kernel/signal.c | 2 +- + arch/arm64/kernel/smp.c | 25 ++ + 81 files changed, 4813 insertions(+), 26 deletions(-) + create mode 100644 arch/arm64/boot/dts/freescale/fii-ls1028a-tsn.dts + create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-dsa-swp5-eno3.dts + create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse-without-enetc.dts + create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse.dts + create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-sdk-bm.dts + create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-bm.dts + create mode 100755 arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dts + create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse.dts + create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-bm.dts + create mode 100755 arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dts + create mode 100755 arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-sja1105.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-tja1100-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8m-generic-mbox-1.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx8m-generic-mbox.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53-1.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-baremetal.dts + create mode 100755 arch/arm64/boot/dts/freescale/imx8mm-evk-ecat.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rpmsg.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rtos.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-8m-buf.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-ca53.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-ca53.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-cm4.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-ca53.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-cm4.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-baremetal.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-dsa.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-ecat.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-lwip.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rpmsg.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rtos.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-ca53.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-cm7.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-generic-mbox-1.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-generic-mbox.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53-1.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-baremetal.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-uart-sharing-cm33.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-ca55.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-cm33.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-9x9-qsb-baremetal.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-9x9-qsb-inmate.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-9x9-qsb-root.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-9x9-qsb-uart-sharing-cm33.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-generic-mbox.dtsi + create mode 100644 arch/arm64/configs/imx_avb.config + create mode 100644 arch/arm64/configs/linux-dpaa-ethercat.config + create mode 100644 arch/arm64/configs/linux-rpmsg-8m-buf.config + +diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig +index 386c2f1f5c86..5f0415085211 100644 +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -93,6 +93,7 @@ config ARM64 + select ARCH_SUPPORTS_INT128 if CC_HAS_INT128 + select ARCH_SUPPORTS_NUMA_BALANCING + select ARCH_SUPPORTS_PAGE_TABLE_CHECK ++ select ARCH_SUPPORTS_RT + select ARCH_WANT_COMPAT_IPC_PARSE_VERSION if COMPAT + select ARCH_WANT_DEFAULT_BPF_JIT + select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT +@@ -199,6 +200,7 @@ config ARM64 + select HAVE_PERF_USER_STACK_DUMP + select HAVE_PREEMPT_DYNAMIC_KEY + select HAVE_REGS_AND_STACK_ACCESS_API ++ select HAVE_PREEMPT_LAZY + select HAVE_POSIX_CPU_TIMERS_TASK_WORK + select HAVE_FUNCTION_ARG_ACCESS_API + select MMU_GATHER_RCU_TABLE_FREE +diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms +index d7ff250db02a..fb56a84ab54f 100644 +--- a/arch/arm64/Kconfig.platforms ++++ b/arch/arm64/Kconfig.platforms +@@ -204,6 +204,24 @@ config ARCH_LAYERSCAPE + help + This enables support for the Freescale Layerscape SoC family. + ++config LS104XA_BAREMETAL ++ bool "NXP LS1043A/LS1046A baremetal support" ++ select BAREMETAL ++ help ++ This enables support for Freescale LS1043A and LS1046A baremetal. ++ ++config LS1028A_BAREMETAL ++ bool "NXP LS1028A baremetal support" ++ select BAREMETAL ++ help ++ This enables support for NXP LS1028A baremetal. ++ ++config LX2160A_BAREMETAL ++ bool "NXP LX2160A baremetal support" ++ select BAREMETAL ++ help ++ This enables support for NXP LX2160A baremetal. ++ + config ARCH_MXC + bool "ARMv8 based NXP i.MX SoC family" + select ARM64_ERRATUM_843419 +@@ -251,6 +269,18 @@ config ARCH_NPCM + General support for NPCM8xx BMC (Arbel). + Nuvoton NPCM8xx BMC based on the Cortex A35. + ++config IMX8M_BAREMETAL ++ bool "NXP IMX8MM/IMX8MP baremetal support" ++ select BAREMETAL ++ help ++ This enables support for NXP i.MX8MM and i.MX8MP baremetal. ++ ++config IMX93_BAREMETAL ++ bool "NXP IMX93 baremetal support" ++ select BAREMETAL ++ help ++ This enables support for NXP i.MX93 baremetal. ++ + config ARCH_QCOM + bool "Qualcomm Platforms" + select GPIOLIB +diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile +index 40006fbe1a43..ad95bb0acce5 100644 +--- a/arch/arm64/boot/dts/freescale/Makefile ++++ b/arch/arm64/boot/dts/freescale/Makefile +@@ -14,11 +14,19 @@ dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-kontron-sl28-var3-ads2.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-kontron-sl28-var4.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-qds.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb.dtb ++dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb-jailhouse.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb-dpdk.dtb ++dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb-jailhouse-without-enetc.dtb ++dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb-sdk-bm.dtb ++dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb-dsa-swp5-eno3.dtb ++dtb-$(CONFIG_ARCH_LAYERSCAPE) += fii-ls1028a-tsn.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-qds.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-qds-sdk.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb-sdk.dtb ++dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb-sdk-jailhouse.dtb ++dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dtb ++dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb-sdk-bm.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb-usdpaa.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-frwy.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-frwy-sdk.dtb +@@ -26,7 +34,10 @@ dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-frwy-usdpaa.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-qds.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-qds-sdk.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb.dtb ++dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-sdk-bm.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-sdk.dtb ++dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-sdk-jailhouse.dtb ++dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-usdpaa.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-shared-mac9-only.dtb + dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-usdpaa-shared-mac10.dtb +@@ -68,13 +79,29 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mm-ab2.dtb imx8mm-ab2-m4.dtb imx8mm-ddr4-ab2.dtb i + dtb-$(CONFIG_ARCH_MXC) += imx8mm-evk.dtb imx8mm-evk-rpmsg.dtb imx8mm-evk-rm67191.dtb \ + imx8mm-evk-root.dtb imx8mm-evk-inmate.dtb imx8mm-evk-revb-qca-wifi.dtb \ + imx8mm-evk-ecspi-slave.dtb \ ++ imx8mm-evk-baremetal.dtb \ ++ imx8mm-evk-avb.dtb \ + imx8mm-evk-pcie-ep.dtb \ + imx8mm-evk-usd-wifi.dtb \ + imx8mm-evk-qca-wifi.dtb \ + imx8mm-evk-dpdk.dtb \ + imx8mm-evk-rm67199.dtb imx8mm-evk-rm67191-cmd-ram.dtb imx8mm-evk-rm67199-cmd-ram.dtb \ +- imx8mm-evk-lk.dtb imx8mm-evk-rpmsg-wm8524-lpv.dtb ++ imx8mm-evk-lk.dtb imx8mm-evk-rpmsg-wm8524-lpv.dtb \ ++ imx8mm-evk-rpmsg-8m-buf.dtb \ ++ imx8mm-evk-ecat.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mm-evk-rpmsg-wm8524.dtb ++dtb-$(CONFIG_ARCH_MXC) += imx8mm-evk-rpmsg-ca53.dtb \ ++ imx8mm-evk-virtio-perf-ca53.dtb \ ++ imx8mm-evk-virtio-perf-cm4.dtb \ ++ imx8mm-evk-virtio-net-ca53.dtb \ ++ imx8mm-evk-virtio-net-cm4.dtb \ ++ imx8mm-evk-multicore-rtos.dtb \ ++ imx8mm-evk-multicore-rpmsg.dtb \ ++ imx8mp-evk-virtio-net-ca53.dtb \ ++ imx8mp-evk-virtio-net-cm7.dtb \ ++ imx8mp-evk-multicore-rtos.dtb \ ++ imx8mp-evk-multicore-lwip.dtb \ ++ imx8mp-evk-multicore-rpmsg.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mm-evk-ak4497.dtb imx8mm-evk-ak5558.dtb imx8mm-evk-audio-tdm.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mm-evk-8mic-revE.dtb imx8mm-evk-8mic-swpdm.dtb \ + imx8mm-evk-iqaudio-dacplus.dtb imx8mm-evk-iqaudio-dacpro.dtb imx8mm-evk-hifiberry-dacplus.dtb \ +@@ -124,12 +151,15 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk.dtb imx8mp-evk-rm67191.dtb imx8mp-evk-it626 + imx8mp-evk-iqaudio-dacplus.dtb imx8mp-evk-iqaudio-dacpro.dtb imx8mp-evk-hifiberry-dacplus.dtb \ + imx8mp-evk-hifiberry-dac2.dtb imx8mp-evk-hifiberry-dacplusadc.dtb \ + imx8mp-evk-usdhc1-m2.dtb imx8mp-evk-rm67199.dtb \ +- imx8mp-evk-dpdk.dtb imx8mp-evk-8mic-swpdm.dtb imx8mp-evk-rpmsg-lpv.dtb imx8mp-evk-revA3-8mic-revE.dtb ++ imx8mp-evk-dpdk.dtb imx8mp-evk-8mic-swpdm.dtb imx8mp-evk-rpmsg-lpv.dtb imx8mp-evk-revA3-8mic-revE.dtb \ ++ imx8mp-evk-baremetal.dtb imx8mp-evk-avb.dtb \ ++ imx8mp-evk-ecat.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mp-msc-sm2s-ep1.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mp-phyboard-pollux-rdk.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mp-ab2.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mp-ddr4-evk.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-ndm.dtb ++dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-dsa.dtb + + imx8mp-evk-revb4-dtbs := imx8mp-evk.dtb imx8mp-evk-revb4.dtbo + imx8mp-evk-revb4-rm67191-dtbs := imx8mp-evk-rm67191.dtb imx8mp-evk-revb4.dtbo +@@ -162,6 +192,7 @@ imx8mp-evk-revb4-8mic-swpdm-dtbs := imx8mp-evk-8mic-swpdm.dtb imx8mp-evk-revb4.d + imx8mp-evk-revb4-8mic-revE-dtbs := imx8mp-evk-revA3-8mic-revE.dtb imx8mp-evk-revb4.dtbo + imx8mp-ddr4-evk-revb4-dtbs := imx8mp-ddr4-evk.dtb imx8mp-evk-revb4.dtbo + imx8mp-evk-revb4-ndm-dtbs := imx8mp-evk-ndm.dtb imx8mp-evk-revb4.dtbo ++imx8mp-evk-revb4-dsa-dtbs := imx8mp-evk-dsa.dtb imx8mp-evk-revb4.dtbo + + dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-rm67191.dtb +@@ -197,6 +228,7 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-ndm.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-sof-wm8962.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-rpmsg.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-rpmsg-lpv.dtb ++dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-dsa.dtb + + dtb-$(CONFIG_ARCH_MXC) += imx8mq-evk.dtb imx8mq-evk-rpmsg.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mq-evk.dtb imx8mq-evk-rpmsg.dtb imx8mq-evk-pcie1-m2.dtb imx8mq-evk-usd-wifi.dtb \ +@@ -298,7 +330,8 @@ dtb-$(CONFIG_ARCH_MXC) += imx8qm-mek-revd-root.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8qxp-ai_ml.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8qxp-colibri-eval-v3.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8dxl-evk.dtb \ +- imx8dxl-evk-enet0.dtb imx8dxl-evk-enet0-tja1100.dtb \ ++ imx8dxl-evk-enet0.dtb imx8dxl-evk-enet0-tja1100.dtb imx8dxl-evk-enet0-tja1100-avb.dtb \ ++ imx8dxl-evk-enet0-avb.dtb imx8dxl-evk-enet0-sja1105.dtb \ + imx8dxl-evk-pcie-ep.dtb \ + imx8dxl-evk-lcdif.dtb \ + imx8dxl-evk-lpspi-slave.dtb \ +@@ -359,6 +392,7 @@ dtb-$(CONFIG_ARCH_MXC) += imx93-14x14-evk.dtb \ + imx93-14x14-evk-lvds-it6263.dtb imx93-14x14-evk-sja1105.dtb \ + imx93-14x14-evk-flexspi-m2.dtb imx93-14x14-evk-dsi-serdes.dtb + dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-evk.dtb \ ++ imx93-11x11-evk-avb.dtb \ + imx93-11x11-evk-inmate.dtb imx93-11x11-evk-root.dtb imx93-11x11-evk-flexio-i2c.dtb \ + imx93-11x11-evk-lpspi.dtb imx93-11x11-evk-lpspi-slave.dtb \ + imx93-11x11-evk-i3c.dtb imx93-11x11-evk-lpuart.dtb \ +@@ -367,7 +401,14 @@ dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-evk.dtb \ + imx93-11x11-evk-flexspi-m2.dtb \ + imx93-11x11-evk-mt9m114.dtb \ + imx93-11x11-evk-ld.dtb \ +- imx93-11x11-evk-rpmsg.dtb imx93-11x11-evk-rpmsg-lpv.dtb ++ imx93-11x11-evk-rpmsg.dtb imx93-11x11-evk-rpmsg-lpv.dtb \ ++ imx93-11x11-evk-virtio-net-ca55.dtb \ ++ imx93-11x11-evk-virtio-net-cm33.dtb \ ++ imx93-11x11-evk-uart-sharing-cm33.dtb \ ++ imx93-11x11-evk-baremetal.dtb \ ++ imx93-11x11-evk-ecat.dtb \ ++ imx93-11x11-evk-multicore-rtos.dtb \ ++ imx93-11x11-evk-dsa.dtb + dtb-$(CONFIG_ARCH_MXC) += imx93-9x9-qsb.dtb \ + imx93-9x9-qsb-can1.dtb \ + imx93-9x9-qsb-flexspi-m2.dtb \ +@@ -375,8 +416,11 @@ dtb-$(CONFIG_ARCH_MXC) += imx93-9x9-qsb.dtb \ + imx93-9x9-qsb-mt9m114.dtb \ + imx93-9x9-qsb-i3c.dtb \ + imx93-9x9-qsb-lpspi.dtb imx93-9x9-qsb-lpspi-slave.dtb \ ++ imx93-9x9-qsb-baremetal.dtb \ + imx93-9x9-qsb-ld.dtb imx93-9x9-qsb-aud-hat.dtb \ +- imx93-9x9-qsb-rpmsg.dtb imx93-9x9-qsb-rpmsg-lpv.dtb ++ imx93-9x9-qsb-rpmsg.dtb imx93-9x9-qsb-rpmsg-lpv.dtb \ ++ imx93-9x9-qsb-uart-sharing-cm33.dtb \ ++ imx93-9x9-qsb-inmate.dtb imx93-9x9-qsb-root.dtb + + dtb-$(CONFIG_ARCH_MXC) += imx91p-9x9-qsb.dtb imx91p-9x9-qsb-aud-hat.dtb \ + imx91p-9x9-qsb-mt9m114.dtb \ +diff --git a/arch/arm64/boot/dts/freescale/fii-ls1028a-tsn.dts b/arch/arm64/boot/dts/freescale/fii-ls1028a-tsn.dts +new file mode 100644 +index 000000000000..a28b47206334 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/fii-ls1028a-tsn.dts +@@ -0,0 +1,325 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Device Tree file for Foxconn LS1028A TSN Board. ++ * ++ * Copyright 2020-2022 NXP ++ * ++ * Wes Li ++ * Vladimir Oltean ++ */ ++ ++/dts-v1/; ++#include "fsl-ls1028a.dtsi" ++ ++/ { ++ model = "Foxconn LS1028A TSN Board"; ++ compatible = "fsl,ls1028a-rdb", "fsl,ls1028a"; ++ ++ aliases { ++ crypto = &crypto; ++ serial0 = &duart0; ++ serial1 = &duart1; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0x0 0x80000000 0x1 0x0000000>; ++ }; ++ ++ sys_mclk: clock-mclk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <25000000>; ++ }; ++ ++ reg_1p8v: regulator-1p8v { ++ compatible = "regulator-fixed"; ++ regulator-name = "1P8V"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-always-on; ++ }; ++ ++ sb_3v3: regulator-sb3v3 { ++ compatible = "regulator-fixed"; ++ regulator-name = "3v3_vbus"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++}; ++ ++&dspi2 { ++ bus-num = <2>; ++ status = "okay"; ++ ++ sja1105_switch0: ethernet-switch@0 { ++ reg = <0x0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "nxp,sja1105s"; ++ dsa,member = <1 1>; ++ /* 20 MHz */ ++ spi-max-frequency = <20000000>; ++ /* Sample data on trailing clock edge */ ++ spi-cpha; ++ /* SPI controller settings for SJA1105 timing requirements */ ++ fsl,spi-cs-sck-delay = <1000>; ++ fsl,spi-sck-cs-delay = <1000>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ label = "sw0p0"; ++ phy-handle = <&sw0p0_rgmii_phy>; ++ phy-mode = "rgmii-id"; ++ reg = <0>; ++ }; ++ ++ port@1 { ++ label = "sw0p1"; ++ phy-handle = <&sw0p1_rgmii_phy>; ++ phy-mode = "rgmii-id"; ++ reg = <1>; ++ }; ++ ++ port@2 { ++ label = "sw0p2"; ++ phy-handle = <&sw0p2_rgmii_phy>; ++ phy-mode = "rgmii-id"; ++ reg = <2>; ++ }; ++ ++ port@3 { ++ reg = <3>; ++ status = "disabled"; ++ }; ++ ++ port@4 { ++ ethernet = <&mscc_felix_port0>; ++ phy-mode = "sgmii"; ++ reg = <4>; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ }; ++ }; ++ ++ /* Daughter card 1 */ ++ sja1105_switch1: ethernet-switch@1 { ++ reg = <0x1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "nxp,sja1105s"; ++ dsa,member = <2 2>; ++ /* 20 MHz */ ++ spi-max-frequency = <20000000>; ++ spi-cpha; ++ fsl,spi-cs-sck-delay = <1000>; ++ fsl,spi-sck-cs-delay = <1000>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ label = "sw1p0"; ++ phy-handle = <&sw1p0_rgmii_phy>; ++ phy-mode = "rgmii-id"; ++ reg = <0>; ++ }; ++ ++ port@1 { ++ label = "sw1p1"; ++ phy-handle = <&sw1p1_rgmii_phy>; ++ phy-mode = "rgmii-id"; ++ reg = <1>; ++ }; ++ ++ port@2 { ++ label = "sw1p2"; ++ phy-handle = <&sw1p2_rgmii_phy>; ++ phy-mode = "rgmii-id"; ++ reg = <2>; ++ }; ++ ++ port@3 { ++ label = "sw1p3"; ++ phy-handle = <&sw1p3_rgmii_phy>; ++ phy-mode = "rgmii-id"; ++ reg = <3>; ++ }; ++ ++ port@4 { ++ ethernet = <&mscc_felix_port2>; ++ phy-mode = "sgmii"; ++ reg = <4>; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&duart0 { ++ status = "okay"; ++}; ++ ++&duart1 { ++ status = "okay"; ++}; ++ ++&enetc_mdio_pf3 { ++ status = "okay"; ++ ++ /* ++ * PHYs on main board ++ */ ++ ++ /* RTL8211FSI */ ++ sw0p0_rgmii_phy: ethernet-phy@7 { ++ reg = <0x7>; ++ }; ++ ++ /* VSC8502 SGMII dual PHY */ ++ sw0p1_rgmii_phy: ethernet-phy@0 { ++ reg = <0x0>; ++ }; ++ ++ sw0p2_rgmii_phy: ethernet-phy@1 { ++ reg = <0x1>; ++ }; ++ ++ /* ++ * PHYs on daughter board 1 ++ */ ++ ++ /* RTL8211FSI */ ++ sw1p0_rgmii_phy: ethernet-phy@9 { ++ reg = <0x9>; ++ }; ++ ++ sw1p1_rgmii_phy: ethernet-phy@8 { ++ reg = <0x8>; ++ }; ++ ++ /* VSC8502 RGMII dual PHY */ ++ sw1p2_rgmii_phy: ethernet-phy@5 { ++ reg = <0x5>; ++ }; ++ ++ sw1p3_rgmii_phy: ethernet-phy@4 { ++ reg = <0x4>; ++ }; ++}; ++ ++&enetc_port0 { ++ status = "disabled"; ++}; ++ ++&enetc_port1 { ++ status = "disabled"; ++}; ++ ++&enetc_port2 { ++ status = "okay"; ++}; ++ ++&esdhc { ++ cap-sd-highspeed; ++ status = "okay"; ++}; ++ ++&esdhc1 { ++ mmc-hs200-1_8v; ++ status = "okay"; ++}; ++ ++&i2c0 { ++ status = "okay"; ++ ++ i2c-mux@77 { ++ compatible = "nxp,pca9847"; ++ reg = <0x77>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ i2c@2 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x02>; ++ ++ current-monitor@40 { ++ compatible = "ti,ina220"; ++ reg = <0x40>; ++ shunt-resistor = <500>; ++ }; ++ }; ++ ++ i2c@3 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x3>; ++ ++ temperature-sensor@4c { ++ compatible = "nxp,sa56004"; ++ reg = <0x4c>; ++ vcc-supply = <&sb_3v3>; ++ }; ++ ++ rtc@51 { ++ compatible = "nxp,pcf2129"; ++ reg = <0x51>; ++ }; ++ }; ++ }; ++}; ++ ++&mscc_felix { ++ status = "okay"; ++}; ++ ++/* Master for SJA1105 on main board */ ++&mscc_felix_port0 { ++ phy-mode = "sgmii"; ++ status = "okay"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++}; ++ ++/* Master for SJA1105 on daughter card 1 */ ++&mscc_felix_port2 { ++ phy-mode = "sgmii"; ++ status = "okay"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++}; ++ ++&mscc_felix_port4 { ++ ethernet = <&enetc_port2>; ++ status = "okay"; ++}; ++ ++&hdptx0 { ++ lane-mapping = <0x4e>; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-dsa-swp5-eno3.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-dsa-swp5-eno3.dts +new file mode 100644 +index 000000000000..4a2eebbc9b43 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-dsa-swp5-eno3.dts +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Device Tree file for NXP LS1028A RDB with dsa master swp5-eno3. ++ * ++ * Copyright 2018-2023 NXP ++ * ++ * Hongbo Wang ++ * ++ */ ++ ++/dts-v1/; ++#include "fsl-ls1028a-rdb.dts" ++ ++&enetc_port2 { ++ fixed-link { ++ pause; ++ }; ++}; ++ ++&enetc_port3 { ++ status = "okay"; ++}; ++ ++&mscc_felix_port4 { ++ label = "swp4"; ++ /delete-property/ ethernet; ++ status = "okay"; ++ ++ fixed-link { ++ pause; ++ }; ++}; ++ ++&mscc_felix_port5 { ++ ethernet = <&enetc_port3>; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse-without-enetc.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse-without-enetc.dts +new file mode 100644 +index 000000000000..00ade901c736 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse-without-enetc.dts +@@ -0,0 +1,98 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Device Tree file for NXP LS1028A RDB Board Jailhouse case. ++ * ++ * Copyright 2021-2023 NXP ++ * ++ * Hou Zhiqiang ++ */ ++ ++#include "fsl-ls1028a-rdb.dts" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* Reserve 832MB for Jailhouse from 0xc000,0000 */ ++ /* 4MB */ ++ jh_reserved: jh@0xc0000000 { ++ no-map; ++ reg = <0x0 0xc0000000 0x0 0x400000>; ++ }; ++ /* 1MB */ ++ loader_reserved: loader@0xc0400000 { ++ no-map; ++ reg = <0x0 0xc0400000 0x0 0x00100000>; ++ }; ++ /* 2MB */ ++ ivshmem_reserved: ivshmem@0xc0500000 { ++ no-map; ++ reg = <0x0 0xc0500000 0x0 0x00200000>; ++ }; ++ /* 2MB */ ++ pci_reserved: pci@0xc0700000 { ++ no-map; ++ reg = <0x0 0xc0700000 0x0 0x00200000>; ++ }; ++ /* 823MB */ ++ inmate_reserved: inmate@0xc0900000 { ++ no-map; ++ reg = <0x0 0xc0900000 0x0 0x33700000>; ++ }; ++ }; ++}; ++ ++&duart0 { ++ status = "okay"; ++ /delete-property/ interrupts; ++}; ++ ++&its { ++ status = "disabled"; ++}; ++ ++&duart1 { ++ status = "disabled"; ++}; ++ ++&smmu { ++ status = "disabled"; ++}; ++ ++&enetc_pcie { ++ status = "disabled"; ++}; ++ ++&enetc_port0 { ++ status = "disabled"; ++}; ++ ++&enetc_port2 { ++ status = "disabled"; ++}; ++ ++&mscc_felix { ++ status = "disabled"; ++}; ++ ++&mscc_felix_port0 { ++ status = "disabled"; ++}; ++ ++&mscc_felix_port1 { ++ status = "disabled"; ++}; ++ ++&mscc_felix_port2 { ++ status = "disabled"; ++}; ++ ++&mscc_felix_port3 { ++ status = "disabled"; ++}; ++ ++&mscc_felix_port4 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse.dts +new file mode 100644 +index 000000000000..00a241511258 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse.dts +@@ -0,0 +1,59 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Device Tree file for NXP LS1028A RDB Board Jailhouse case. ++ * ++ * Copyright 2018-2023 NXP ++ * ++ * Hongbo Wang ++ * ++ */ ++ ++#include "fsl-ls1028a-rdb.dts" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* Reserve 832MB for Jailhouse from 0xc000,0000 */ ++ /* 4MB */ ++ jh_reserved: jh@0xc0000000 { ++ no-map; ++ reg = <0x0 0xc0000000 0x0 0x400000>; ++ }; ++ /* 1MB */ ++ loader_reserved: loader@0xc0400000 { ++ no-map; ++ reg = <0x0 0xc0400000 0x0 0x00100000>; ++ }; ++ /* 2MB */ ++ ivshmem_reserved: ivshmem@0xc0500000 { ++ no-map; ++ reg = <0x0 0xc0500000 0x0 0x00200000>; ++ }; ++ /* 2MB */ ++ pci_reserved: pci@0xc0700000 { ++ no-map; ++ reg = <0x0 0xc0700000 0x0 0x00200000>; ++ }; ++ /* 823MB */ ++ inmate_reserved: inmate@0xc0900000 { ++ no-map; ++ reg = <0x0 0xc0900000 0x0 0x33700000>; ++ }; ++ }; ++}; ++ ++&duart0 { ++ status = "okay"; ++ /delete-property/ interrupts; ++}; ++ ++&duart1 { ++ status = "disabled"; ++}; ++ ++&gpio3 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-sdk-bm.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-sdk-bm.dts +new file mode 100644 +index 000000000000..dd9a9d7b425a +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-sdk-bm.dts +@@ -0,0 +1,15 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * DTS file for NXP Layerscape baremetal ++ * ++ * Copyright 2019-2023 NXP ++ * ++ * Author: Hongbo Wang ++ * ++ */ ++ ++#include "fsl-ls1028a-rdb.dts" ++ ++&enetc_pcie { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts +index 1f308455c613..8f9c6118f48b 100644 +--- a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts +@@ -96,6 +96,19 @@ simple-audio-card,codec { + }; + }; + ++&dspi2 { ++ bus-num = <2>; ++ status = "okay"; ++ ++ mikrobus@0 { ++ compatible = "semtech,sx1301"; ++ reg = <0>; ++ spi-max-frequency = <2000000>; ++ fsl,spi-cs-sck-delay = <1000000>; ++ fsl,spi-sck-cs-delay = <50>; ++ }; ++}; ++ + &can0 { + status = "okay"; + +@@ -250,6 +263,18 @@ rtc@51 { + reg = <0x51>; + }; + }; ++ ++ i2c@6 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x6>; ++ ++ pn7120: pn7120@28 { ++ compatible = "nxp,pn7120", "nxp,pn544"; ++ reg = <0x28>; ++ clock-frequency = <1000000>; ++ }; ++ }; + }; + }; + +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi +index 81ad51219c64..36dab630ab43 100644 +--- a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi +@@ -1086,7 +1086,7 @@ tmu: tmu@1f80000 { + #thermal-sensor-cells = <1>; + }; + +- pcie@1f0000000 { /* Integrated Endpoint Root Complex */ ++ enetc_pcie: pcie@1f0000000 { /* Integrated Endpoint Root Complex */ + compatible = "pci-host-ecam-generic"; + reg = <0x01 0xf0000000 0x0 0x100000>; + #address-cells = <3>; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-bm.dts b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-bm.dts +new file mode 100644 +index 000000000000..a7036efb1729 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-bm.dts +@@ -0,0 +1,44 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * DTS file for NXP Layerscape baremetal ++ * ++ * Copyright 2018-2023 NXP ++ * ++ * Author: Changming Huang ++ * ++ */ ++ ++#include "fsl-ls1043a-rdb-sdk.dts" ++ ++ ++&usb0 { ++ status = "disabled"; ++}; ++ ++&usb1 { ++ status = "disabled"; ++}; ++ ++&usb2 { ++ status = "disabled"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&pcie2 { ++ status = "disabled"; ++}; ++ ++&pcie3 { ++ status = "disabled"; ++}; ++ ++&optee { ++ status = "disabled"; ++}; ++ ++&ifc { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dts b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dts +new file mode 100755 +index 000000000000..77bd7c1e6ed1 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dts +@@ -0,0 +1,263 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * DTS file for NXP Layerscape-1043ARDB jailhouse case ++ * ++ * Copyright 2018-2023 NXP ++ * ++ * Author: Hongbo Wang ++ * ++ */ ++ ++#include "fsl-ls1043a-rdb.dts" ++#include "qoriq-qman-portals-sdk.dtsi" ++#include "qoriq-bman-portals-sdk.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* Reserve 832MB for Jailhouse from 0xc000,0000 */ ++ /* 4MB */ ++ jh_reserved: jh@0xc0000000 { ++ no-map; ++ reg = <0x0 0xc0000000 0x0 0x400000>; ++ }; ++ /* 1MB */ ++ loader_reserved: loader@0xc0400000 { ++ no-map; ++ reg = <0x0 0xc0400000 0x0 0x00100000>; ++ }; ++ /* 2MB */ ++ ivshmem_reserved: ivshmem@0xc0500000 { ++ no-map; ++ reg = <0x0 0xc0500000 0x0 0x00200000>; ++ }; ++ /* 2MB */ ++ pci_reserved: pci@0xc0700000 { ++ no-map; ++ reg = <0x0 0xc0700000 0x0 0x00200000>; ++ }; ++ /* 823MB */ ++ inmate_reserved: inmate@0xc0900000 { ++ no-map; ++ reg = <0x0 0xc0900000 0x0 0x33700000>; ++ }; ++ }; ++}; ++ ++&bman_fbpr { ++ compatible = "fsl,bman-fbpr"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++&qman_fqd { ++ compatible = "fsl,qman-fqd"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++&qman_pfdr { ++ compatible = "fsl,qman-pfdr"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++ ++&soc { ++/delete-property/ dma-coherent; ++ ++#include "qoriq-dpaa-eth.dtsi" ++#include "qoriq-fman3-0-6oh.dtsi" ++ ++pcie@3400000 { ++ /delete-property/ iommu-map; ++ dma-coherent; ++}; ++ ++pcie@3500000 { ++ /delete-property/ iommu-map; ++ dma-coherent; ++}; ++ ++pcie@3600000 { ++ /delete-property/ iommu-map; ++ dma-coherent; ++}; ++ ++/delete-node/ iommu@9000000; ++}; ++ ++&fman0 { ++ compatible = "fsl,fman", "simple-bus"; ++}; ++ ++&clockgen { ++ dma-coherent; ++}; ++ ++&scfg { ++ dma-coherent; ++}; ++ ++&crypto { ++ dma-coherent; ++}; ++ ++&dcfg { ++ dma-coherent; ++}; ++ ++&ifc { ++ dma-coherent; ++}; ++ ++&qspi { ++ dma-coherent; ++}; ++ ++&esdhc { ++ dma-coherent; ++}; ++ ++&ddr { ++ dma-coherent; ++}; ++ ++&tmu { ++ dma-coherent; ++}; ++ ++&qman { ++ dma-coherent; ++}; ++ ++&bman { ++ dma-coherent; ++}; ++ ++&bportals { ++ dma-coherent; ++}; ++ ++&qportals { ++ dma-coherent; ++}; ++ ++&dspi0 { ++ dma-coherent; ++}; ++ ++&dspi1 { ++ dma-coherent; ++}; ++ ++&i2c0 { ++ dma-coherent; ++}; ++ ++&i2c1 { ++ dma-coherent; ++}; ++ ++&i2c2 { ++ dma-coherent; ++}; ++ ++&i2c3 { ++ dma-coherent; ++}; ++ ++&duart0 { ++ dma-coherent; ++ /delete-property/ interrupts; ++}; ++ ++&duart1 { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&duart2 { ++ dma-coherent; ++}; ++ ++&duart3 { ++ dma-coherent; ++}; ++ ++&gpio1 { ++ dma-coherent; ++}; ++ ++&gpio2 { ++ dma-coherent; ++}; ++ ++&gpio3 { ++ dma-coherent; ++}; ++ ++&gpio4 { ++ dma-coherent; ++}; ++ ++&lpuart0 { ++ dma-coherent; ++}; ++ ++&lpuart1 { ++ dma-coherent; ++}; ++ ++&lpuart2 { ++ dma-coherent; ++}; ++ ++&lpuart3 { ++ dma-coherent; ++}; ++ ++&lpuart4 { ++ dma-coherent; ++}; ++ ++&lpuart5 { ++ dma-coherent; ++}; ++ ++&ftm_alarm0 { ++ dma-coherent; ++}; ++ ++&wdog0 { ++ dma-coherent; ++}; ++ ++&edma0 { ++ dma-coherent; ++}; ++ ++&qdma { ++ dma-coherent; ++}; ++ ++&msi1 { ++ dma-coherent; ++}; ++ ++&msi2 { ++ dma-coherent; ++}; ++ ++&msi3 { ++ dma-coherent; ++}; ++ ++&fman0 { ++ dma-coherent; ++}; ++ ++&ptp_timer0 { ++ dma-coherent; ++}; ++ ++&fsldpaa { ++ dma-coherent; ++}; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse.dts b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse.dts +new file mode 100644 +index 000000000000..a7335581c469 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse.dts +@@ -0,0 +1,267 @@ ++ // SPDX-License-Identifier: GPL-2.0+ ++/* ++ * DTS file for NXP Layerscape-1043ARDB jailhouse case ++ * ++ * Copyright 2018-2023 NXP ++ * ++ * Author: Hongbo Wang ++ * ++ */ ++ ++#include "fsl-ls1043a-rdb.dts" ++#include "qoriq-qman-portals-sdk.dtsi" ++#include "qoriq-bman-portals-sdk.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* Reserve 832MB for Jailhouse from 0xc000,0000 */ ++ /* 4MB */ ++ jh_reserved: jh@0xc0000000 { ++ no-map; ++ reg = <0x0 0xc0000000 0x0 0x400000>; ++ }; ++ /* 1MB */ ++ loader_reserved: loader@0xc0400000 { ++ no-map; ++ reg = <0x0 0xc0400000 0x0 0x00100000>; ++ }; ++ /* 2MB */ ++ ivshmem_reserved: ivshmem@0xc0500000 { ++ no-map; ++ reg = <0x0 0xc0500000 0x0 0x00200000>; ++ }; ++ /* 2MB */ ++ pci_reserved: pci@0xc0700000 { ++ no-map; ++ reg = <0x0 0xc0700000 0x0 0x00200000>; ++ }; ++ /* 823MB */ ++ inmate_reserved: inmate@0xc0900000 { ++ no-map; ++ reg = <0x0 0xc0900000 0x0 0x33700000>; ++ }; ++ }; ++}; ++ ++&bman_fbpr { ++ compatible = "fsl,bman-fbpr"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++&qman_fqd { ++ compatible = "fsl,qman-fqd"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++&qman_pfdr { ++ compatible = "fsl,qman-pfdr"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++ ++&soc { ++/delete-property/ dma-coherent; ++ ++#include "qoriq-dpaa-eth.dtsi" ++#include "qoriq-fman3-0-6oh.dtsi" ++ ++pcie@3400000 { ++ /delete-property/ iommu-map; ++ dma-coherent; ++}; ++ ++pcie@3500000 { ++ /delete-property/ iommu-map; ++ dma-coherent; ++}; ++ ++pcie@3600000 { ++ /delete-property/ iommu-map; ++ dma-coherent; ++}; ++ ++/delete-node/ iommu@9000000; ++/delete-node/ qman@1880000; ++/delete-node/ bman@1890000; ++/delete-node/ bman-portals@508000000; ++/delete-node/ qman-portals@500000000; ++}; ++ ++&fman0 { ++ compatible = "fsl,fman", "simple-bus"; ++}; ++ ++&clockgen { ++ dma-coherent; ++}; ++ ++&scfg { ++ dma-coherent; ++}; ++ ++&crypto { ++ dma-coherent; ++}; ++ ++&dcfg { ++ dma-coherent; ++}; ++ ++&ifc { ++ dma-coherent; ++}; ++ ++&qspi { ++ dma-coherent; ++}; ++ ++&esdhc { ++ dma-coherent; ++}; ++ ++&ddr { ++ dma-coherent; ++}; ++ ++&tmu { ++ dma-coherent; ++}; ++ ++&dspi0 { ++ dma-coherent; ++}; ++ ++&dspi1 { ++ dma-coherent; ++}; ++ ++&i2c0 { ++ dma-coherent; ++}; ++ ++&i2c1 { ++ dma-coherent; ++}; ++ ++&i2c2 { ++ dma-coherent; ++}; ++ ++&i2c3 { ++ dma-coherent; ++}; ++ ++&duart0 { ++ dma-coherent; ++ /delete-property/ interrupts; ++}; ++ ++&duart1 { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&duart2 { ++ dma-coherent; ++}; ++ ++&duart3 { ++ dma-coherent; ++}; ++ ++&gpio1 { ++ dma-coherent; ++}; ++ ++&gpio2 { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&gpio3 { ++ dma-coherent; ++}; ++ ++&gpio4 { ++ dma-coherent; ++}; ++ ++&lpuart0 { ++ dma-coherent; ++}; ++ ++&lpuart1 { ++ dma-coherent; ++}; ++ ++&lpuart2 { ++ dma-coherent; ++}; ++ ++&lpuart3 { ++ dma-coherent; ++}; ++ ++&lpuart4 { ++ dma-coherent; ++}; ++ ++&lpuart5 { ++ dma-coherent; ++}; ++ ++&ftm_alarm0 { ++ dma-coherent; ++}; ++ ++&wdog0 { ++ dma-coherent; ++}; ++ ++&edma0 { ++ dma-coherent; ++}; ++ ++&qdma { ++ dma-coherent; ++}; ++ ++&msi1 { ++ dma-coherent; ++}; ++ ++&msi2 { ++ dma-coherent; ++}; ++ ++&msi3 { ++ dma-coherent; ++}; ++ ++&fman0 { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&ptp_timer0 { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&fsldpaa { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&bman_fbpr { ++ status = "disabled"; ++}; ++ ++&qman_fqd { ++ status = "disabled"; ++}; ++ ++&qman_pfdr { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi +index 16ad696b9478..816a02798525 100644 +--- a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi +@@ -1061,7 +1061,7 @@ ftm_alarm0: timer@29d0000 { + }; + + firmware { +- optee { ++ optee: optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-bm.dts b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-bm.dts +new file mode 100644 +index 000000000000..097a95802919 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-bm.dts +@@ -0,0 +1,47 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * DTS file for NXP Layerscape baremetal ++ * ++ * Copyright 2018-2023 NXP ++ * ++ * Author: Changming Huang ++ * ++ */ ++ ++#include "fsl-ls1046a-rdb-sdk.dts" ++ ++&usb0 { ++ status = "disabled"; ++}; ++ ++&usb1 { ++ status = "disabled"; ++}; ++ ++&usb2 { ++ status = "disabled"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&pcie2 { ++ status = "disabled"; ++}; ++ ++&pcie3 { ++ status = "disabled"; ++}; ++ ++&optee { ++ status = "disabled"; ++}; ++ ++&ifc { ++ status = "disabled"; ++}; ++ ++&gpio1 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dts b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dts +new file mode 100755 +index 000000000000..35ec99ac4b3e +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dts +@@ -0,0 +1,274 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * DTS file for NXP Layerscape-1046ARDB jailhouse case ++ * ++ * Copyright 2018-2023 NXP ++ * ++ * Author: Hongbo Wang ++ * ++ */ ++ ++#include "fsl-ls1046a-rdb.dts" ++#include "qoriq-qman-portals-sdk.dtsi" ++#include "qoriq-bman-portals-sdk.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* Reserve 832MB for Jailhouse from 0xc000,0000 */ ++ /* 4MB */ ++ jh_reserved: jh@0xc0000000 { ++ no-map; ++ reg = <0x0 0xc0000000 0x0 0x400000>; ++ }; ++ /* 1MB */ ++ loader_reserved: loader@0xc0400000 { ++ no-map; ++ reg = <0x0 0xc0400000 0x0 0x00100000>; ++ }; ++ /* 2MB */ ++ ivshmem_reserved: ivshmem@0xc0500000 { ++ no-map; ++ reg = <0x0 0xc0500000 0x0 0x00200000>; ++ }; ++ /* 2MB */ ++ pci_reserved: pci@0xc0700000 { ++ no-map; ++ reg = <0x0 0xc0700000 0x0 0x00200000>; ++ }; ++ /* 823MB */ ++ inmate_reserved: inmate@0xc0900000 { ++ no-map; ++ reg = <0x0 0xc0900000 0x0 0x33700000>; ++ }; ++ }; ++}; ++ ++&bman_fbpr { ++ compatible = "fsl,bman-fbpr"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++&qman_fqd { ++ compatible = "fsl,qman-fqd"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++&qman_pfdr { ++ compatible = "fsl,qman-pfdr"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++ ++&soc { ++/delete-property/ dma-coherent; ++ ++#include "qoriq-dpaa-eth.dtsi" ++#include "qoriq-fman3-0-6oh.dtsi" ++ ++pcie@3400000 { ++ /delete-property/ iommu-map; ++}; ++ ++pcie@3500000 { ++ /delete-property/ iommu-map; ++}; ++ ++pcie@3600000 { ++ /delete-property/ iommu-map; ++}; ++ ++/delete-node/ iommu@9000000; ++}; ++ ++&fsldpaa { ++ ethernet@0 { ++ status = "disabled"; ++ }; ++ ethernet@1 { ++ status = "disabled"; ++ }; ++ ethernet@9 { ++ compatible = "fsl,dpa-ethernet"; ++ fsl,fman-mac = <&enet7>; ++ dma-coherent; ++ }; ++}; ++ ++&fman0 { ++ compatible = "fsl,fman", "simple-bus"; ++}; ++ ++&clockgen { ++ dma-coherent; ++}; ++ ++&scfg { ++ dma-coherent; ++}; ++ ++&crypto { ++ dma-coherent; ++}; ++ ++&dcfg { ++ dma-coherent; ++}; ++ ++&ifc { ++ dma-coherent; ++}; ++ ++&qspi { ++ dma-coherent; ++}; ++ ++&esdhc { ++ dma-coherent; ++}; ++ ++&ddr { ++ dma-coherent; ++}; ++ ++&tmu { ++ dma-coherent; ++}; ++ ++&qman { ++ dma-coherent; ++}; ++ ++&bman { ++ dma-coherent; ++}; ++ ++&bportals { ++ dma-coherent; ++}; ++ ++&qportals { ++ dma-coherent; ++}; ++ ++&dspi { ++ dma-coherent; ++}; ++ ++&i2c0 { ++ dma-coherent; ++}; ++ ++&i2c1 { ++ dma-coherent; ++}; ++ ++&i2c2 { ++ dma-coherent; ++}; ++ ++&i2c3 { ++ dma-coherent; ++}; ++ ++&duart0 { ++ dma-coherent; ++ /delete-property/ interrupts; ++}; ++ ++&duart1 { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&duart2 { ++ dma-coherent; ++}; ++ ++&duart3 { ++ dma-coherent; ++}; ++ ++&gpio0 { ++ dma-coherent; ++}; ++ ++&gpio1 { ++ dma-coherent; ++}; ++ ++&gpio2 { ++ dma-coherent; ++}; ++ ++&gpio3 { ++ dma-coherent; ++}; ++ ++&lpuart0 { ++ dma-coherent; ++}; ++ ++&lpuart1 { ++ dma-coherent; ++}; ++ ++&lpuart2 { ++ dma-coherent; ++}; ++ ++&lpuart3 { ++ dma-coherent; ++}; ++ ++&lpuart4 { ++ dma-coherent; ++}; ++ ++&lpuart5 { ++ dma-coherent; ++}; ++ ++&ftm_alarm0 { ++ dma-coherent; ++}; ++ ++&wdog0 { ++ dma-coherent; ++}; ++ ++&edma0 { ++ dma-coherent; ++}; ++ ++&sata { ++ dma-coherent; ++}; ++ ++&qdma { ++ dma-coherent; ++}; ++ ++&msi1 { ++ dma-coherent; ++}; ++ ++&msi2 { ++ dma-coherent; ++}; ++ ++&msi3 { ++ dma-coherent; ++}; ++ ++&fman0 { ++ dma-coherent; ++}; ++ ++&ptp_timer0 { ++ dma-coherent; ++}; ++ ++&fsldpaa { ++ dma-coherent; ++}; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse.dts b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse.dts +new file mode 100755 +index 000000000000..8bf8d3eb03ee +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse.dts +@@ -0,0 +1,278 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * DTS file for NXP Layerscape-1046ARDB jailhouse case ++ * ++ * Copyright 2018-2023 NXP ++ * ++ * Author: Hongbo Wang ++ * ++ */ ++ ++#include "fsl-ls1046a-rdb.dts" ++#include "qoriq-qman-portals-sdk.dtsi" ++#include "qoriq-bman-portals-sdk.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* Reserve 832MB for Jailhouse from 0xc000,0000 */ ++ /* 4MB */ ++ jh_reserved: jh@0xc0000000 { ++ no-map; ++ reg = <0x0 0xc0000000 0x0 0x400000>; ++ }; ++ /* 1MB */ ++ loader_reserved: loader@0xc0400000 { ++ no-map; ++ reg = <0x0 0xc0400000 0x0 0x00100000>; ++ }; ++ /* 2MB */ ++ ivshmem_reserved: ivshmem@0xc0500000 { ++ no-map; ++ reg = <0x0 0xc0500000 0x0 0x00200000>; ++ }; ++ /* 2MB */ ++ pci_reserved: pci@0xc0700000 { ++ no-map; ++ reg = <0x0 0xc0700000 0x0 0x00200000>; ++ }; ++ /* 823MB */ ++ inmate_reserved: inmate@0xc0900000 { ++ no-map; ++ reg = <0x0 0xc0900000 0x0 0x33700000>; ++ }; ++ }; ++}; ++ ++&bman_fbpr { ++ compatible = "fsl,bman-fbpr"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++&qman_fqd { ++ compatible = "fsl,qman-fqd"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++&qman_pfdr { ++ compatible = "fsl,qman-pfdr"; ++ alloc-ranges = <0 0 0x10000 0>; ++}; ++ ++&soc { ++/delete-property/ dma-coherent; ++ ++#include "qoriq-dpaa-eth.dtsi" ++#include "qoriq-fman3-0-6oh.dtsi" ++ ++pcie@3400000 { ++ /delete-property/ iommu-map; ++}; ++ ++pcie@3500000 { ++ /delete-property/ iommu-map; ++}; ++ ++pcie@3600000 { ++ /delete-property/ iommu-map; ++}; ++ ++/delete-node/ iommu@9000000; ++/delete-node/ qman@1880000; ++/delete-node/ bman@1890000; ++/delete-node/ bman-portals@508000000; ++/delete-node/ qman-portals@500000000; ++}; ++ ++&fsldpaa { ++ ethernet@0 { ++ status = "disabled"; ++ }; ++ ethernet@1 { ++ status = "disabled"; ++ }; ++ ethernet@9 { ++ compatible = "fsl,dpa-ethernet"; ++ fsl,fman-mac = <&enet7>; ++ dma-coherent; ++ }; ++}; ++ ++&fman0 { ++ compatible = "fsl,fman", "simple-bus"; ++}; ++ ++&clockgen { ++ dma-coherent; ++}; ++ ++&scfg { ++ dma-coherent; ++}; ++ ++&crypto { ++ dma-coherent; ++}; ++ ++&dcfg { ++ dma-coherent; ++}; ++ ++&ifc { ++ dma-coherent; ++}; ++ ++&qspi { ++ dma-coherent; ++}; ++ ++&esdhc { ++ dma-coherent; ++}; ++ ++&ddr { ++ dma-coherent; ++}; ++ ++&tmu { ++ dma-coherent; ++}; ++ ++&dspi { ++ dma-coherent; ++}; ++ ++&i2c0 { ++ dma-coherent; ++}; ++ ++&i2c1 { ++ dma-coherent; ++}; ++ ++&i2c2 { ++ dma-coherent; ++}; ++ ++&i2c3 { ++ dma-coherent; ++}; ++ ++&duart0 { ++ dma-coherent; ++ /delete-property/ interrupts; ++}; ++ ++&duart1 { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&duart2 { ++ dma-coherent; ++}; ++ ++&duart3 { ++ dma-coherent; ++}; ++ ++&gpio0 { ++ dma-coherent; ++}; ++ ++&gpio1 { ++ dma-coherent; ++}; ++ ++&gpio2 { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&gpio3 { ++ dma-coherent; ++}; ++ ++&lpuart0 { ++ dma-coherent; ++}; ++ ++&lpuart1 { ++ dma-coherent; ++}; ++ ++&lpuart2 { ++ dma-coherent; ++}; ++ ++&lpuart3 { ++ dma-coherent; ++}; ++ ++&lpuart4 { ++ dma-coherent; ++}; ++ ++&lpuart5 { ++ dma-coherent; ++}; ++ ++&ftm_alarm0 { ++ dma-coherent; ++}; ++ ++&wdog0 { ++ dma-coherent; ++}; ++ ++&edma0 { ++ dma-coherent; ++}; ++ ++&sata { ++ dma-coherent; ++}; ++ ++&qdma { ++ dma-coherent; ++}; ++ ++&msi1 { ++ dma-coherent; ++}; ++ ++&msi2 { ++ dma-coherent; ++}; ++ ++&msi3 { ++ dma-coherent; ++}; ++ ++&fman0 { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&ptp_timer0 { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&fsldpaa { ++ dma-coherent; ++ status = "disabled"; ++}; ++ ++&bman_fbpr { ++ status = "disabled"; ++}; ++ ++&qman_fqd { ++ status = "disabled"; ++}; ++ ++&qman_pfdr { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi +index 96f501c76ea8..7877f13e1682 100644 +--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi +@@ -1028,7 +1028,7 @@ qman_pfdr: qman-pfdr { + }; + + firmware { +- optee { ++ optee: optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; +diff --git a/arch/arm64/boot/dts/freescale/imx8-ss-audio.dtsi b/arch/arm64/boot/dts/freescale/imx8-ss-audio.dtsi +index c133f06ca748..c4ac0b498c4d 100644 +--- a/arch/arm64/boot/dts/freescale/imx8-ss-audio.dtsi ++++ b/arch/arm64/boot/dts/freescale/imx8-ss-audio.dtsi +@@ -239,6 +239,15 @@ sai3: sai@59070000 { + status = "disabled"; + }; + ++ gpt5: gpt@590b0000 { ++ reg = <0x590b0000 0x10000>; ++ clocks = <&gpt5_lpcg 1>, ++ <&gpt5_lpcg 1>; ++ clock-names = "ipg", "per"; ++ power-domains = <&pd IMX_SC_R_GPT_5>; ++ status = "disabled"; ++ }; ++ + asrc1: asrc@59800000 { + compatible = "fsl,imx8qm-asrc"; + reg = <0x59800000 0x10000>; +@@ -555,6 +564,17 @@ sai3_lpcg: clock-controller@59470000 { + power-domains = <&pd IMX_SC_R_SAI_3>; + }; + ++ gpt5_lpcg: clock-controller@594b0000 { ++ compatible = "fsl,imx8qxp-lpcg"; ++ reg = <0x594b0000 0x10000>; ++ #clock-cells = <1>; ++ clocks = <&acm IMX_ADMA_ACM_GPT0_MUX_CLK_SEL>, ++ <&audio_ipg_clk>; ++ bit-offset = <0 16>; ++ clock-output-names = "gpt5_lpcg_clk_in", "gpt5_lpcg_ipg_clk"; ++ power-domains = <&pd IMX_SC_R_GPT_5>; ++ }; ++ + asrc1_lpcg: clock-controller@59c00000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x59c00000 0x10000>; +diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dts +new file mode 100644 +index 000000000000..36cc1a616a9a +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dts +@@ -0,0 +1,9 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8dxl-evk-enet0.dts" ++#include "imx8dxl-evk-enet0-avb.dtsi" +diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dtsi b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dtsi +new file mode 100644 +index 000000000000..d7c268f056cc +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dtsi +@@ -0,0 +1,54 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/* Set GPT Capture input to Ethernet 0 event */ ++&acm { ++ gpt-capture-select = ; ++}; ++ ++/* AVB HW timer*/ ++&gpt5 { ++ compatible = "fsl,avb-gpt"; ++ ++ clocks = <&gpt5_lpcg 1>, ++ <&gpt5_lpcg 1>, ++ <&gpt5_lpcg 0>, ++ <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_PLL>; ++ clock-names = "ipg", "per", "clk_in", "audio_pll"; ++ ++ /* - Set ACM_AUD_CLK0 source to ACM_AUD_REC_CLK0 ++ * - Set clk_in to Audio PLL0 Divider ++ * - Enable Audio PLL and its Master Bus clock (AUD_REC_CLK0): keep this in sync with sai nodes ++ */ ++ ++ assigned-clocks = <&acm IMX_ADMA_ACM_AUD_CLK0_SEL>, ++ <&acm IMX_ADMA_ACM_GPT0_MUX_CLK_SEL>, ++ <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_PLL>, ++ <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_MST_BUS>; ++ ++ assigned-clock-parents = <&aud_rec0_lpcg 0>, ++ <&acm IMX_ADMA_ACM_AUD_CLK0_SEL>; ++ ++ assigned-clock-rates = <0>, <0>, <786432000>, <12288000>; ++ ++ /* Audio PLL is controlled through SCU */ ++ fsl,pll-scu-controlled; ++ ++ timer-channel = <1>; /* Use output compare channel 1*/ ++ prescale = <1>; ++ domain = <0>; ++ rec-channel = <1 0 1>; // capture channel, eth port, ENET TC id ++ ++ interrupts = ; ++ ++ status = "okay"; ++}; ++ ++&fec1 { ++ fsl,rx-phy-delay-100-ns = <670>; ++ fsl,tx-phy-delay-100-ns = <670>; ++ fsl,rx-phy-delay-1000-ns = <0>; ++ fsl,tx-phy-delay-1000-ns = <0>; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-sja1105.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-sja1105.dts +new file mode 100644 +index 000000000000..ddfdadfbe3f8 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-sja1105.dts +@@ -0,0 +1,168 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8dxl-evk.dts" ++ ++&iomuxc { ++ pinctrl_lpspi2: lpspi2grp { ++ fsl,pins = < ++ IMX8DXL_USDHC1_VSELECT_ADMA_SPI2_SDO 0x600004c ++ IMX8DXL_USDHC1_WP_ADMA_SPI2_SDI 0x600004c ++ IMX8DXL_USDHC1_RESET_B_ADMA_SPI2_SCK 0x600004c ++ >; ++ }; ++ ++ pinctrl_lpspi2_cs: lpspi2cs { ++ fsl,pins = < ++ IMX8DXL_USDHC1_CD_B_LSIO_GPIO4_IO22 0x21 ++ >; ++ }; ++}; ++ ++&lpspi2 { ++ fsl,spi-num-chipselects = <1>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_lpspi2 &pinctrl_lpspi2_cs>; ++ cs-gpios = <&lsio_gpio4 22 GPIO_ACTIVE_LOW>; ++ status = "okay"; ++ ++ sja1105p@0 { ++ compatible = "nxp,sja1105q"; ++ spi-max-frequency = <12500000>; ++ /* SJA1105PQRS operates in SPI MODE 1 : CPOL=0 CPHA=1 */ ++ spi-cpha; ++ /* use queue 5 for managment traffic */ ++ hostprio = <5>; ++ ++ reg = <0>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ label = "cpu"; ++ ethernet = <&fec1>; ++ phy-mode = "rgmii"; ++ rx-internal-delay-ps = <2000>; ++ tx-internal-delay-ps = <2000>; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ label = "swp0"; ++ phy-handle = <&phy0>; ++ phy-mode = "mii"; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ label = "swp1"; ++ phy-handle = <&phy1>; ++ phy-mode = "mii"; ++ }; ++ ++ port@3 { ++ reg = <3>; ++ label = "swp2"; ++ phy-handle = <&phy2>; ++ phy-mode = "rmii"; ++ }; ++ ++ port@4 { ++ reg = <4>; ++ label = "swp3"; ++ phy-handle = <&phy3>; ++ phy-mode = "rmii"; ++ }; ++ }; ++ }; ++}; ++ ++ðphy1 { ++ status = "disabled"; ++}; ++ ++&fec1 { ++ assigned-clocks = <&clk IMX_SC_R_ENET_0 IMX_SC_C_CLKDIV>; ++ assigned-clock-rates = <12000000>; ++ phy-supply = <&mii_select>; ++ status = "okay"; ++ ++ fsl,magic-packet; ++ phy-mode = "rgmii"; ++ /delete-property/ rx-internal-delay-ps; ++ /delete-property/ phy-handle; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ /* SJA1105Q EVB switch ports : NXP TJA1102 ++ the phys SMI addresses on EVB are hard coded giving ++ first TJA device at offset 0x8 and the 2nd once at offet 0xe */ ++ phy0: ethernet-phy@8 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x8>; ++ max-speed = <100>; ++ phy1: ethernet-phy@9 { ++ reg = <0x9>; ++ max-speed = <100>; ++ }; ++ }; ++ ++ phy2: ethernet-phy@e { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0xe>; ++ max-speed = <100>; ++ phy3: ethernet-phy@f { ++ reg = <0xf>; ++ max-speed = <100>; ++ }; ++ }; ++ }; ++}; ++ ++®_fec1_sel { ++ status = "okay"; ++}; ++ ++/* This would set max7233 (0) to high (intented for PHY power on) and would ++ * enable LPC SPI/SMI on the switch EVB which interferes with ++ * SPI communcation between the iMX and the SJA1105 ++*/ ++®_fec1_io { ++ status = "disabled"; ++}; ++ ++&max7322 { ++ status = "disabled"; ++}; ++ ++&mii_select { ++ /delete-property/ enable-active-high; ++}; ++ ++&eqos { ++ status = "disabled"; ++}; ++ ++&usdhc2 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-tja1100-avb.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-tja1100-avb.dts +new file mode 100644 +index 000000000000..121b506cbc8c +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-tja1100-avb.dts +@@ -0,0 +1,9 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8dxl-evk-enet0-tja1100.dts" ++#include "imx8dxl-evk-enet0-avb.dtsi" +diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts +index 139d3f1b8b7a..b5fc609be44f 100644 +--- a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts ++++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts +@@ -323,6 +323,9 @@ &eqos { + phy-handle = <ðphy0>; + nvmem-cells = <&fec_mac1>; + nvmem-cell-names = "mac-address"; ++ snps,force_thresh_dma_mode; ++ snps,mtl-tx-config = <&mtl_tx_setup>; ++ snps,mtl-rx-config = <&mtl_rx_setup>; + status = "okay"; + + mdio { +@@ -347,6 +350,61 @@ vddio0: vddio-regulator { + }; + }; + }; ++ ++ mtl_tx_setup: tx-queues-config { ++ snps,tx-queues-to-use = <5>; ++ snps,tx-sched-sp; ++ queue0 { ++ snps,dcb-algorithm; ++ snps,priority = <0x1>; ++ }; ++ queue1 { ++ snps,dcb-algorithm; ++ snps,priority = <0x2>; ++ }; ++ queue2 { ++ snps,dcb-algorithm; ++ snps,priority = <0x4>; ++ }; ++ queue3 { ++ snps,dcb-algorithm; ++ snps,priority = <0x8>; ++ }; ++ queue4 { ++ snps,dcb-algorithm; ++ snps,priority = <0xf0>; ++ }; ++ }; ++ ++ mtl_rx_setup: rx-queues-config { ++ snps,rx-queues-to-use = <5>; ++ snps,rx-sched-sp; ++ queue0 { ++ snps,dcb-algorithm; ++ snps,priority = <0x1>; ++ snps,map-to-dma-channel = <0>; ++ }; ++ queue1 { ++ snps,dcb-algorithm; ++ snps,priority = <0x2>; ++ snps,map-to-dma-channel = <1>; ++ }; ++ queue2 { ++ snps,dcb-algorithm; ++ snps,priority = <0x4>; ++ snps,map-to-dma-channel = <2>; ++ }; ++ queue3 { ++ snps,dcb-algorithm; ++ snps,priority = <0x8>; ++ snps,map-to-dma-channel = <3>; ++ }; ++ queue4 { ++ snps,dcb-algorithm; ++ snps,priority = <0xf0>; ++ snps,map-to-dma-channel = <4>; ++ }; ++ }; + }; + + /* +diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-ss-adma.dtsi b/arch/arm64/boot/dts/freescale/imx8dxl-ss-adma.dtsi +index 2e6cd063ae9a..81541d269bb8 100644 +--- a/arch/arm64/boot/dts/freescale/imx8dxl-ss-adma.dtsi ++++ b/arch/arm64/boot/dts/freescale/imx8dxl-ss-adma.dtsi +@@ -32,6 +32,7 @@ &acm { + <&pd IMX_SC_R_AUDIO_PLL_0>, + <&pd IMX_SC_R_AUDIO_PLL_1>, + <&pd IMX_SC_R_ASRC_0>, ++ <&pd IMX_SC_R_GPT_5>, + <&pd IMX_SC_R_SAI_0>, + <&pd IMX_SC_R_SAI_1>, + <&pd IMX_SC_R_SAI_2>, +diff --git a/arch/arm64/boot/dts/freescale/imx8m-generic-mbox-1.dtsi b/arch/arm64/boot/dts/freescale/imx8m-generic-mbox-1.dtsi +new file mode 100644 +index 000000000000..428e9b8cb4ae +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8m-generic-mbox-1.dtsi +@@ -0,0 +1,27 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ gen_sw_mbox_1_reserved: gen-sw-mbox-1@b8501000 { ++ reg = <0 0xb8501000 0 0x1000>; ++ no-map; ++ }; ++ ++ }; ++ ++ gen_sw_mbox_1: generic-software-mailbox-1@b8501000 { ++ compatible = "fsl,generic-software-mbox"; ++ reg = <0 0xb8501000 0 0x1000>; ++ #mbox-cells = <3>; ++ /* Use 2 unused SPI interrupts */ ++ interrupts = , ; ++ interrupt-names = "irq", "remote_irq"; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8m-generic-mbox.dtsi b/arch/arm64/boot/dts/freescale/imx8m-generic-mbox.dtsi +new file mode 100644 +index 000000000000..e97e82c78aaa +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8m-generic-mbox.dtsi +@@ -0,0 +1,27 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ gen_sw_mbox_reserved: gen-sw-mbox@b8500000 { ++ reg = <0 0xb8500000 0 0x1000>; ++ no-map; ++ }; ++ ++ }; ++ ++ gen_sw_mbox: generic-software-mailbox@b8500000 { ++ compatible = "fsl,generic-software-mbox"; ++ reg = <0 0xb8500000 0 0x1000>; ++ #mbox-cells = <3>; ++ /* Use 2 unused SPI interrupts */ ++ interrupts = , ; ++ interrupt-names = "irq", "remote_irq"; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53-1.dtsi b/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53-1.dtsi +new file mode 100644 +index 000000000000..70e33e6d834b +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53-1.dtsi +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx8m-generic-mbox-1.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ rpmsg_ca53_1_reserved: rpmsg-ca53-1@b8610000 { ++ reg = <0 0xb8610000 0 0x10000>; ++ no-map; ++ }; ++ ++ vdevbuffer_ca53_1: vdevbuffer-ca53-1@b8800000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xb8800000 0 0x100000>; ++ no-map; ++ }; ++ }; ++ ++ rpmsg-ca53-1 { ++ compatible = "fsl,imx8mm-rpmsg"; ++ reg = <0x0 0xb8610000 0x0 0x10000>; /* 64K for one rpmsg instance */ ++ dma-coherent; ++ memory-region = <&vdevbuffer_ca53_1>; ++ mbox-names = "tx", "rx", "rxdb"; ++ mboxes = <&gen_sw_mbox_1 0 0 1 /* Tx channel with ACK */ ++ &gen_sw_mbox_1 1 0 0 /* Rx channel without ACK */ ++ &gen_sw_mbox_1 2 0 1>; /* RXDB channel with ACK */ ++ vdev-nums = <1>; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53.dtsi b/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53.dtsi +new file mode 100644 +index 000000000000..a3b0dbf563e8 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53.dtsi +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++#include "imx8m-generic-mbox.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ rpmsg_ca53_reserved: rpmsg-ca53@b8600000 { ++ reg = <0 0xb8600000 0 0x10000>; ++ no-map; ++ }; ++ ++ vdevbuffer_ca53: vdevbuffer-ca53@b8700000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xb8700000 0 0x100000>; ++ no-map; ++ }; ++ }; ++ ++ rpmsg-ca53 { ++ compatible = "fsl,imx8mm-rpmsg"; ++ reg = <0x0 0xb8600000 0x0 0x10000>; /* 64K for one rpmsg instance */ ++ dma-coherent; ++ memory-region = <&vdevbuffer_ca53>; ++ mbox-names = "tx", "rx", "rxdb"; ++ mboxes = <&gen_sw_mbox 0 0 1 /* Tx channel with ACK */ ++ &gen_sw_mbox 1 0 0 /* Rx channel without ACK */ ++ &gen_sw_mbox 2 0 1>; /* RXDB channel with ACK */ ++ vdev-nums = <1>; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-avb.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-avb.dts +new file mode 100644 +index 000000000000..3ed975c348d4 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-avb.dts +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2020-2021 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mm-evk.dts" ++ ++/* AVB HW timer*/ ++&gpt1 { ++ compatible = "fsl,avb-gpt"; ++ timer-channel = <1>; /* Use output compare channel 1*/ ++ prescale = <1>; ++ domain = <0>; ++ ++ clocks = <&clk IMX8MM_CLK_GPT1_ROOT>, ++ <&clk IMX8MM_CLK_GPT1_ROOT>, <&clk IMX8MM_AUDIO_PLL1>; ++ clock-names = "ipg", "per", "audio_pll"; ++ ++ /* Make the GPT clk root derive from the audio PLL */ ++ assigned-clocks = <&clk IMX8MM_CLK_GPT1>; ++ assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>; ++ assigned-clock-rates = <0>; ++ ++ /* Enble SW sampling for media clock recovery on port 0 */ ++ sw-recovery = <0>; ++ ++ status = "okay"; ++}; ++ ++&fec1 { ++ fsl,rx-phy-delay-100-ns = <670>; ++ fsl,tx-phy-delay-100-ns = <670>; ++ fsl,rx-phy-delay-1000-ns = <0>; ++ fsl,tx-phy-delay-1000-ns = <0>; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-baremetal.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-baremetal.dts +new file mode 100644 +index 000000000000..4a36d0e0c7a1 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-baremetal.dts +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2020-2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mm-evk.dts" ++ ++/ { ++ model = "FSL i.MX8MM LPDDR4 EVK RevB board - Baremetal"; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ rpmsg_reserved: rpmsg@b8000000 { ++ no-map; ++ reg = <0 0xb8000000 0 0x400000>; ++ }; ++ bm_reserved: baremetal@60000000 { ++ no-map; ++ reg = <0 0x60000000 0 0x10000000>; ++ }; ++ }; ++}; ++ ++&fec1 { ++ status = "disabled"; ++}; ++ ++&gpio5 { ++ status = "disabled"; ++}; ++ ++&uart3 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-ecat.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-ecat.dts +new file mode 100755 +index 000000000000..cde59ddaa3cf +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-ecat.dts +@@ -0,0 +1,10 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx8mm-evk.dts" ++ ++&fec1 { ++ compatible = "fsl,imx8mm-fec-ecat", "fsl,imx8mq-fec-ecat", "fsl,imx6sx-fec-ecat"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rpmsg.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rpmsg.dts +new file mode 100644 +index 000000000000..16919505668c +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rpmsg.dts +@@ -0,0 +1,10 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mm-evk-multicore-rtos.dts" ++#include "imx8m-rpmsg-ca53.dtsi" ++#include "imx8m-rpmsg-ca53-1.dtsi" +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rtos.dts +new file mode 100644 +index 000000000000..9f3dfaaf1650 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rtos.dts +@@ -0,0 +1,79 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mm-evk.dts" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* ++ * Reserve up to 48MB (16MB x 3) for three FreeRTOS instances running on ++ * three Cortex-A Cores when booting Linux on at least on Cortex-A Core. ++ */ ++ rtos_ca53_reserved: rtos-ca53@93c00000 { ++ no-map; ++ reg = <0 0x93c00000 0x0 0x3000000>; ++ }; ++ ++ /* Reserve 16MB for FreeRTOS running on CM4 */ ++ m4_reserved: m4@80000000 { ++ no-map; ++ reg = <0 0x80000000 0 0x1000000>; ++ }; ++ ++ vdev0vring0: vdev0vring0@b8000000 { ++ reg = <0 0xb8000000 0 0x8000>; ++ no-map; ++ }; ++ ++ vdev0vring1: vdev0vring1@b8008000 { ++ reg = <0 0xb8008000 0 0x8000>; ++ no-map; ++ }; ++ ++ rsc_table: rsc-table@b80ff000 { ++ reg = <0 0xb80ff000 0 0x1000>; ++ no-map; ++ }; ++ ++ vdevbuffer: vdevbuffer@b8400000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xb8400000 0 0x100000>; ++ no-map; ++ }; ++ }; ++ ++ imx8mm-cm4 { ++ compatible = "fsl,imx8mm-cm4"; ++ rsc-da = <0xb8000000>; ++ clocks = <&clk IMX8MM_CLK_M4_DIV>; ++ mbox-names = "tx", "rx", "rxdb"; ++ mboxes = <&mu 0 1 ++ &mu 1 1 ++ &mu 3 1>; ++ memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, <&rsc_table>; ++ syscon = <&src>; ++ fsl,startup-delay-ms = <500>; ++ }; ++ ++ ++}; ++ ++&{/busfreq} { ++ status = "disabled"; ++}; ++ ++&uart4 { ++ status = "disabled"; ++}; ++ ++&clk { ++ init-on-array = ; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-8m-buf.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-8m-buf.dts +new file mode 100644 +index 000000000000..ea5dbfd3b793 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-8m-buf.dts +@@ -0,0 +1,228 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2019-2023 NXP ++ */ ++ ++/dts-v1/; ++#include ++#include "imx8mm-evk.dts" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ m4_reserved: m4@80000000 { ++ no-map; ++ reg = <0 0x80000000 0 0x1000000>; ++ }; ++ ++ vdev0vring0: vdev0vring0@b8000000 { ++ reg = <0 0xb8000000 0 0x100000>; ++ no-map; ++ }; ++ ++ vdev0vring1: vdev0vring1@b8100000 { ++ reg = <0 0xb8100000 0 0x100000>; ++ no-map; ++ }; ++ ++ rsc_table: rsc-table@b8a00000 { ++ reg = <0 0xb8a00000 0 0x1000>; ++ no-map; ++ }; ++ ++ vdevbuffer: vdevbuffer@b8200000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xb8200000 0 0x800000>; ++ no-map; ++ }; ++ }; ++ ++ bt_sco_codec: bt_sco_codec { ++ status = "disabled"; ++ }; ++ ++ sound-bt-sco { ++ status = "disabled"; ++ }; ++ ++ sound-wm8524 { ++ status = "disabled"; ++ }; ++ ++ wm8524: audio-codec { ++ status = "disabled"; ++ }; ++ ++ rpmsg_audio: rpmsg_audio { ++ compatible = "fsl,imx8mm-rpmsg-audio"; ++ model = "ak4497-audio"; ++ fsl,platform = "rpmsg-audio-channel"; ++ fsl,enable-lpa; ++ fsl,rpmsg-out; ++ assigned-clocks = <&clk IMX8MM_CLK_SAI1>; ++ assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>; ++ assigned-clock-rates = <49152000>; ++ clocks = <&clk IMX8MM_CLK_SAI1_IPG>, ++ <&clk IMX8MM_CLK_SAI1_ROOT>, ++ <&clk IMX8MM_CLK_SDMA3_ROOT>, ++ <&clk IMX8MM_AUDIO_PLL1_OUT>, ++ <&clk IMX8MM_AUDIO_PLL2_OUT>; ++ clock-names = "ipg", "mclk", "dma", "pll8k", "pll11k"; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_0: uart-rpbus-0 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_1: uart-rpbus-1 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_2: uart-rpbus-2 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_3: uart-rpbus-3 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_4: uart-rpbus-4 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_5: uart-rpbus-5 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_6: uart-rpbus-6 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_7: uart-rpbus-7 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_8: uart-rpbus-8 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_9: uart-rpbus-9 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_10: uart-rpbus-10 { ++ compatible = "fsl,uart-rpbus"; ++ status = "okay"; /* mcore directly print data that receive from acore */ ++ }; ++ ++ imx8mm-cm4 { ++ compatible = "fsl,imx8mm-cm4"; ++ rsc-da = <0xb8000000>; ++ clocks = <&clk IMX8MM_CLK_M4_DIV>; ++ mbox-names = "tx", "rx", "rxdb"; ++ mboxes = <&mu 0 1 ++ &mu 1 1 ++ &mu 3 1>; ++ memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, <&rsc_table>; ++ syscon = <&src>; ++ fsl,startup-delay-ms = <500>; ++ }; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++/* ++ * ATTENTION: M4 may use IPs like below ++ * ECSPI0/ECSPI2, GPIO1/GPIO5, GPT1, I2C3, I2S3, WDOG1, UART3, UART4, PWM3, SDMA1 ++ */ ++ ++&i2c3 { ++ status = "disabled"; ++}; ++ ++/* the uart3 is used by m4 to support srtm uart service */ ++&uart3 { ++ status = "disabled"; ++}; ++ ++&uart4 { ++ status = "disabled"; ++}; ++ ++&sdma3 { ++ status = "disabled"; ++}; ++ ++&sai3 { ++ status = "disabled"; ++}; ++ ++&sai1 { ++ status = "disabled"; ++}; ++ ++&sai2 { ++ status = "disabled"; ++}; ++ ++&sai6 { ++ status = "disabled"; ++}; ++ ++&flexspi { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-ca53.dts +new file mode 100644 +index 000000000000..89b686615df5 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-ca53.dts +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mm-evk.dts" ++#include "imx8m-rpmsg-ca53.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ rtos_ca53_reserved: rtos-ca53@93c00000 { ++ reg = <0 0x93c00000 0x0 0x24000000>; ++ no-map; ++ }; ++ }; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++/* ++ * ATTENTION: The isolated CA53 core uses IPs below ++ * UART4 ++ */ ++ ++&uart4 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg.dts +index 2a477c74b634..237bf6eafef6 100644 +--- a/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg.dts ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg.dts +@@ -1,10 +1,10 @@ + // SPDX-License-Identifier: (GPL-2.0 OR MIT) + /* +- * Copyright 2019 NXP ++ * Copyright 2019-2023 NXP + */ + + /dts-v1/; +- ++#include + #include "imx8mm-evk.dts" + + / { +@@ -74,6 +74,91 @@ rpmsg_audio: rpmsg_audio { + status = "okay"; + }; + ++ uart_rpbus_0: uart-rpbus-0 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_1: uart-rpbus-1 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_2: uart-rpbus-2 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_3: uart-rpbus-3 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_4: uart-rpbus-4 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_5: uart-rpbus-5 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_6: uart-rpbus-6 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_7: uart-rpbus-7 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_8: uart-rpbus-8 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_9: uart-rpbus-9 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ //flags=; ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_10: uart-rpbus-10 { ++ compatible = "fsl,uart-rpbus"; ++ status = "okay"; /* mcore directly print data that receive from acore */ ++ }; ++ + imx8mm-cm4 { + compatible = "fsl,imx8mm-cm4"; + rsc-da = <0xb8000000>; +@@ -102,13 +187,18 @@ IMX8MM_CLK_DISP_APB + + /* + * ATTENTION: M4 may use IPs like below +- * ECSPI0/ECSPI2, GPIO1/GPIO5, GPT1, I2C3, I2S3, WDOG1, UART4, PWM3, SDMA1 ++ * ECSPI0/ECSPI2, GPIO1/GPIO5, GPT1, I2C3, I2S3, WDOG1, UART3, UART4, PWM3, SDMA1 + */ + + &i2c3 { + status = "disabled"; + }; + ++/* the uart3 is used by m4 to support srtm uart service */ ++&uart3 { ++ status = "disabled"; ++}; ++ + &uart4 { + status = "disabled"; + }; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-ca53.dts +new file mode 100644 +index 000000000000..f4f3726a52fe +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-ca53.dts +@@ -0,0 +1,82 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mm-evk.dts" ++#include "imx8m-generic-mbox.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ virtio_reserved: virtio@b8400000 { ++ no-map; ++ reg = <0 0xb8400000 0x0 0x00100000>; ++ }; ++ ++ /* 512MB */ ++ freertos_reserved: inmate@93c00000 { ++ no-map; ++ reg = <0 0x93c00000 0x0 0x20000000>; ++ }; ++ ++ virtio_buffer: virtio-buffer@b3c00000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xb3c00000 0 0x4000000>; ++ }; ++ }; ++ ++ virtio_net@b8400000 { ++ compatible = "virtio,mmio"; ++ reg = <0x0 0xb8400000 0x0 0x1000>; ++ dma-coherent; ++ hypervisor_less; ++ memory-region = <&virtio_buffer>; ++ mbox-names = "mmiowr", "mmioirq"; ++ mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ ++ &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ ++ }; ++}; ++ ++&{/busfreq} { ++ /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ ++ status = "disabled"; ++}; ++ ++&uart4 { ++ status = "disabled"; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++&iomuxc { ++ /* ++ * Used for the 2nd Linux. ++ * TODO: M4 may use these pins. ++ */ ++ pinctrl_uart4: uart4grp { ++ fsl,pins = < ++ MX8MM_IOMUXC_UART4_RXD_UART4_DCE_RX 0x140 ++ MX8MM_IOMUXC_UART4_TXD_UART4_DCE_TX 0x140 ++ >; ++ }; ++}; ++ ++&uart2 { ++ /* uart2 is used by the 2nd OS, so configure pin and clk */ ++ pinctrl-0 = <&pinctrl_uart2>, <&pinctrl_uart4>; ++ assigned-clocks = <&clk IMX8MM_CLK_UART4>; ++ assigned-clock-parents = <&clk IMX8MM_CLK_24M>; ++}; ++ ++&fec1 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-cm4.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-cm4.dts +new file mode 100644 +index 000000000000..90965238e6dd +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-cm4.dts +@@ -0,0 +1,81 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mm-evk.dts" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ cm4_reserved: cm4@80000000 { ++ reg = <0 0x80000000 0x0 0x01000000>; ++ no-map; ++ }; ++ ++ virtio_buffer: virtio-buffer@b3c00000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xb3c00000 0 0x4000000>; ++ no-map; ++ }; ++ ++ virtio_reserved: virtio@b8400000 { ++ no-map; ++ reg = <0 0xb8400000 0x0 0x00100000>; ++ }; ++ }; ++ ++ virtio_net@b8400000 { ++ compatible = "virtio,mmio"; ++ reg = <0x0 0xb8400000 0x0 0x1000>; ++ hypervisor_less; ++ memory-region = <&virtio_buffer>; ++ interrupts = ; //MU IRQ to CA53 ++ mbox-names = "mmiowr", "mmioirq"; ++ mboxes = <&mu 0 2 ++ &mu 1 2>; ++ }; ++}; ++ ++&{/busfreq} { ++ /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ ++ status = "disabled"; ++}; ++ ++&uart4 { ++ status = "disabled"; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++&iomuxc { ++ /* ++ * Used for the 2nd Linux. ++ * TODO: M4 may use these pins. ++ */ ++ pinctrl_uart4: uart4grp { ++ fsl,pins = < ++ MX8MM_IOMUXC_UART4_RXD_UART4_DCE_RX 0x140 ++ MX8MM_IOMUXC_UART4_TXD_UART4_DCE_TX 0x140 ++ >; ++ }; ++}; ++ ++&uart2 { ++ /* uart2 is used by the 2nd OS, so configure pin and clk */ ++ pinctrl-0 = <&pinctrl_uart2>, <&pinctrl_uart4>; ++ assigned-clocks = <&clk IMX8MM_CLK_UART4>; ++ assigned-clock-parents = <&clk IMX8MM_CLK_24M>; ++}; ++ ++&fec1 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-ca53.dts +new file mode 100644 +index 000000000000..b6adb7aa27d8 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-ca53.dts +@@ -0,0 +1,78 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mm-evk.dts" ++#include "imx8m-generic-mbox.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ virtio_reserved: virtio@b8400000 { ++ no-map; ++ reg = <0 0xb8400000 0x0 0x00100000>; ++ }; ++ ++ /* 512MB */ ++ freertos_reserved: inmate@93c00000 { ++ no-map; ++ reg = <0 0x93c00000 0x0 0x20000000>; ++ }; ++ ++ virtio_buffer: virtio-buffer@b3c00000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xb3c00000 0 0x4000000>; ++ }; ++ }; ++ ++ virtio_trans@b8400000 { ++ compatible = "virtio,mmio"; ++ reg = <0x0 0xb8400000 0x0 0x1000>; ++ hypervisor_less; ++ dma-coherent; ++ memory-region = <&virtio_buffer>; ++ mbox-names = "mmiowr", "mmioirq"; ++ mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ ++ &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ ++ }; ++}; ++ ++&{/busfreq} { ++ /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ ++ status = "disabled"; ++}; ++ ++&uart4 { ++ status = "disabled"; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++&iomuxc { ++ /* ++ * Used for the 2nd Linux. ++ * TODO: M4 may use these pins. ++ */ ++ pinctrl_uart4: uart4grp { ++ fsl,pins = < ++ MX8MM_IOMUXC_UART4_RXD_UART4_DCE_RX 0x140 ++ MX8MM_IOMUXC_UART4_TXD_UART4_DCE_TX 0x140 ++ >; ++ }; ++}; ++ ++&uart2 { ++ /* uart2 is used by the 2nd OS, so configure pin and clk */ ++ pinctrl-0 = <&pinctrl_uart2>, <&pinctrl_uart4>; ++ assigned-clocks = <&clk IMX8MM_CLK_UART4>; ++ assigned-clock-parents = <&clk IMX8MM_CLK_24M>; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-cm4.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-cm4.dts +new file mode 100644 +index 000000000000..9196eaf43015 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-cm4.dts +@@ -0,0 +1,70 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mm-evk.dts" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ cm4_reserved: cm4@80000000 { ++ reg = <0 0x80000000 0x0 0x01000000>; ++ no-map; ++ }; ++ ++ virtio_reserved: virtio@b8400000 { ++ no-map; ++ reg = <0 0xb8400000 0x0 0x00100000>; ++ }; ++ }; ++ ++ virtio_trans@b8400000 { ++ compatible = "virtio,mmio"; ++ reg = <0x0 0xb8400000 0x0 0x1000>; ++ hypervisor_less; ++ interrupts = ; //MU IRQ to CA53 ++ mbox-names = "mmiowr", "mmioirq"; ++ mboxes = <&mu 0 2 ++ &mu 1 2>; ++ }; ++}; ++ ++&{/busfreq} { ++ /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ ++ status = "disabled"; ++}; ++ ++&uart4 { ++ status = "disabled"; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++&iomuxc { ++ /* ++ * Used for the 2nd Linux. ++ * TODO: M4 may use these pins. ++ */ ++ pinctrl_uart4: uart4grp { ++ fsl,pins = < ++ MX8MM_IOMUXC_UART4_RXD_UART4_DCE_RX 0x140 ++ MX8MM_IOMUXC_UART4_TXD_UART4_DCE_TX 0x140 ++ >; ++ }; ++}; ++ ++&uart2 { ++ /* uart2 is used by the 2nd OS, so configure pin and clk */ ++ pinctrl-0 = <&pinctrl_uart2>, <&pinctrl_uart4>; ++ assigned-clocks = <&clk IMX8MM_CLK_UART4>; ++ assigned-clock-parents = <&clk IMX8MM_CLK_24M>; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi +index c63b5c467be2..589b52636e85 100755 +--- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi ++++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi +@@ -593,6 +593,17 @@ sdma3: dma-controller@302b0000 { + fsl,sdma-ram-script-name = "imx/sdma/sdma-imx7d.bin"; + }; + ++ gpt1: gpt@302d0000 { ++ compatible = "fsl,imx8mq-gpt", "fsl,imx7d-gpt"; ++ reg = <0x302d0000 0x10000>; ++ interrupts = ; ++ clocks = <&clk IMX8MM_CLK_GPT1_ROOT>, ++ <&clk IMX8MM_CLK_GPT1_ROOT>, ++ <&clk IMX8MM_CLK_GPT_3M>; ++ clock-names = "ipg", "per", "osc_per"; ++ status = "disabled"; ++ }; ++ + iomuxc: pinctrl@30330000 { + compatible = "fsl,imx8mm-iomuxc"; + reg = <0x30330000 0x10000>; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-avb.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-avb.dts +new file mode 100644 +index 000000000000..70e3dba61494 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-avb.dts +@@ -0,0 +1,42 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2021 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mp-evk.dts" ++ ++/* AVB HW timer*/ ++&gpt1 { ++ compatible = "fsl,avb-gpt"; ++ timer-channel = <1>; /* Use output compare channel 1*/ ++ rec-channel = <1 0 1>; // capture channel, eth port, ENET TC id ++ prescale = <1>; ++ domain = <0>; ++ ++ clocks = <&clk IMX8MP_CLK_GPT1_ROOT>, ++ <&clk IMX8MP_CLK_GPT1_ROOT>, ++ <&clk IMX8MP_AUDIO_PLL1>; ++ clock-names = "ipg", "per", "audio_pll"; ++ ++ /* Make the GPT clk root derive from the audio PLL*/ ++ assigned-clocks = <&clk IMX8MP_CLK_GPT1>; ++ assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>; ++ assigned-clock-rates = <0>; ++ ++ gpt1_capin1_sel = <&gpr 0x4 2>; ++ ++ status = "okay"; ++}; ++ ++&fec { ++ fsl,rx-phy-delay-100-ns = <670>; ++ fsl,tx-phy-delay-100-ns = <670>; ++ fsl,rx-phy-delay-1000-ns = <0>; ++ fsl,tx-phy-delay-1000-ns = <0>; ++}; ++ ++ðphy1 { ++ eee-broken-100tx; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-baremetal.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-baremetal.dts +new file mode 100644 +index 000000000000..ce893a1d27c6 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-baremetal.dts +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2020-2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mp-evk.dts" ++ ++/ { ++ model = "NXP i.MX8MPlus EVK board - Baremetal"; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ rpmsg_reserved: rpmsg@55800000 { ++ no-map; ++ reg = <0 0x55800000 0 0x800000>; ++ }; ++ ++ /* baremetal: slave cores reserved memory */ ++ bm_reserved: baremetal@60000000 { ++ no-map; ++ reg = <0 0x60000000 0 0x10000000>; ++ }; ++ }; ++}; ++ ++&fec { ++ status = "disabled"; ++}; ++ ++&uart3 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa.dts +new file mode 100644 +index 000000000000..18de77535a98 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa.dts +@@ -0,0 +1,79 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// Copyright 2023 NXP ++ ++#include "imx8mp-evk.dts" ++ ++/delete-node/&spidev1; ++ ++&ecspi2 { ++ status = "okay"; ++ ++ netcdsa: ethernet-switch@0 { ++ reg = <0x0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "nxp,imxrt1180-netc"; ++ /* 500 kHz */ ++ spi-max-frequency = <500000>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ label = "swp0"; ++ reg = <0>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <100>; ++ full-duplex; ++ }; ++ }; ++ ++ port@1 { ++ label = "swp1"; ++ reg = <1>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ port@2 { ++ label = "swp2"; ++ reg = <2>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ port@3 { ++ /* cpu port connected to eqos */ ++ ethernet = <&eqos>; ++ label = "cpu"; ++ reg = <3>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&pinctrl_ecspi2 { ++ fsl,pins = < ++ MX8MP_IOMUXC_ECSPI2_SCLK__ECSPI2_SCLK 0x84 ++ MX8MP_IOMUXC_ECSPI2_MOSI__ECSPI2_MOSI 0x84 ++ MX8MP_IOMUXC_ECSPI2_MISO__ECSPI2_MISO 0x84 ++ >; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-ecat.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-ecat.dts +new file mode 100644 +index 000000000000..60d6028a240a +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-ecat.dts +@@ -0,0 +1,10 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx8mp-evk.dts" ++ ++&fec { ++ compatible = "fsl,imx8mp-fec-ecat", "fsl,imx8mq-fec-ecat", "fsl,imx6sx-fec-ecat"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-lwip.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-lwip.dts +new file mode 100644 +index 000000000000..06cf65278b98 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-lwip.dts +@@ -0,0 +1,21 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mp-evk-multicore-rtos.dts" ++ ++&fec { ++ status = "disabled"; ++}; ++ ++&eqos { ++ /delete-property/ clocks; ++ /delete-property/ clock-names; ++ clocks = <&clk IMX8MP_CLK_QOS_ENET_ROOT>, ++ <&clk IMX8MP_CLK_ENET_QOS_TIMER>, ++ <&clk IMX8MP_CLK_ENET_QOS>; ++ clock-names = "pclk", "ptp_ref", "tx"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rpmsg.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rpmsg.dts +new file mode 100644 +index 000000000000..85f1b55ee381 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rpmsg.dts +@@ -0,0 +1,117 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mp-evk-multicore-rtos.dts" ++#include "imx8mp-rpmsg-ca53.dtsi" ++#include "imx8mp-rpmsg-ca53-1.dtsi" ++ ++/ { ++ aliases { ++ i2c0 = &i2c1; ++ i2c1 = &i2c2; ++ i2c2 = &i2c_rpbus_3; ++ }; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ vdev0vring0: vdev0vring0@55000000 { ++ reg = <0 0x55000000 0 0x8000>; ++ no-map; ++ }; ++ ++ vdev0vring1: vdev0vring1@55008000 { ++ reg = <0 0x55008000 0 0x8000>; ++ no-map; ++ }; ++ ++ vdevbuffer: vdevbuffer@55400000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0x55400000 0 0x100000>; ++ no-map; ++ }; ++ ++ rsc_table: rsc-table@550ff000 { ++ reg = <0 0x550ff000 0 0x1000>; ++ no-map; ++ }; ++ }; ++ ++ sound-wm8960 { ++ status = "disabled"; ++ }; ++ ++ imx8mp-cm7 { ++ clocks = <&clk IMX8MP_CLK_M7_DIV>, ++ <&audio_blk_ctrl IMX8MP_CLK_AUDIO_BLK_CTRL_AUDPLL_ROOT>; ++ clock-names = "core", "audio"; ++ }; ++}; ++ ++&sai3 { ++ status = "disabled"; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++/delete-node/ &i2c3; ++ ++&i2c_rpbus_3 { ++ compatible = "fsl,i2c-rpbus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ pca6416: gpio@20 { ++ compatible = "ti,tca6416"; ++ reg = <0x20>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ }; ++ ++ ov5640_1: ov5640_mipi@3c { ++ compatible = "ovti,ov5640"; ++ reg = <0x3c>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_csi0_pwn>, <&pinctrl_csi0_rst>; ++ clocks = <&clk IMX8MP_CLK_IPP_DO_CLKO2>; ++ clock-names = "xclk"; ++ assigned-clocks = <&clk IMX8MP_CLK_IPP_DO_CLKO2>; ++ assigned-clock-parents = <&clk IMX8MP_CLK_24M>; ++ assigned-clock-rates = <24000000>; ++ csi_id = <0>; ++ powerdown-gpios = <&gpio4 1 GPIO_ACTIVE_HIGH>; ++ reset-gpios = <&gpio4 0 GPIO_ACTIVE_LOW>; ++ mclk = <24000000>; ++ mclk_source = <0>; ++ mipi_csi; ++ status = "disabled"; ++ ++ port { ++ ov5640_mipi_1_ep: endpoint { ++ remote-endpoint = <&mipi_csi1_ep>; ++ data-lanes = <1 2>; ++ clock-lanes = <0>; ++ }; ++ }; ++ }; ++ ++ codec: wm8960@1a { ++ compatible = "wlf,wm8960,lpa"; ++ reg = <0x1a>; ++ wlf,shared-lrclk; ++ SPKVDD1-supply = <®_audio_pwr>; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rtos.dts +new file mode 100644 +index 000000000000..1799cad88062 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rtos.dts +@@ -0,0 +1,79 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mp-evk.dts" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* ++ * Reserve up to 48MB (16MB x 3) for three FreeRTOS instances running on ++ * three Cortex-A Cores when booting Linux on at least on Cortex-A Core. ++ */ ++ ca53_reserved: ca53@c0000000 { ++ no-map; ++ reg = <0 0xc0000000 0x0 0x3000000>; ++ }; ++ ++ /* Reserve 16MB for RTOS running on CM7 */ ++ m7_reserved: m7@80000000 { ++ no-map; ++ reg = <0 0x80000000 0 0x1000000>; ++ }; ++ ++ vdev0vring0: vdev0vring0@55000000 { ++ reg = <0 0x55000000 0 0x8000>; ++ no-map; ++ }; ++ ++ vdev0vring1: vdev0vring1@55008000 { ++ reg = <0 0x55008000 0 0x8000>; ++ no-map; ++ }; ++ ++ vdevbuffer: vdevbuffer@55400000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0x55400000 0 0x100000>; ++ no-map; ++ }; ++ ++ rsc_table: rsc-table@550ff000 { ++ reg = <0 0x550ff000 0 0x1000>; ++ no-map; ++ }; ++ }; ++ ++ imx8mp-cm7 { ++ compatible = "fsl,imx8mn-cm7"; ++ rsc-da = <0x55000000>; ++ clocks = <&clk IMX8MP_CLK_M7_DIV>; ++ clock-names = "core"; ++ mbox-names = "tx", "rx", "rxdb"; ++ mboxes = <&mu 0 1 ++ &mu 1 1 ++ &mu 3 1>; ++ memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, <&rsc_table>; ++ status = "okay"; ++ fsl,startup-delay-ms = <500>; ++ }; ++}; ++ ++&{/busfreq} { ++ /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ ++ status = "disabled"; ++}; ++ ++&uart4 { ++ status = "disabled"; ++}; ++ ++&clk { ++ init-on-array = ; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-ca53.dts +new file mode 100644 +index 000000000000..489daf4c30d3 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-ca53.dts +@@ -0,0 +1,81 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mp-evk.dts" ++#include "imx8mp-generic-mbox.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ virtio_reserved: virtio@fc700000 { ++ no-map; ++ reg = <0 0xfc700000 0x0 0x00100000>; ++ }; ++ ++ /* 512MB */ ++ freertos_reserved: inmate@c0000000 { ++ no-map; ++ reg = <0 0xc0000000 0x0 0x20000000>; ++ }; ++ ++ virtio_buffer: virtio-buffer@f8700000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xf8700000 0 0x4000000>; ++ }; ++ }; ++ ++ virtio_net@fc700000 { ++ compatible = "virtio,mmio"; ++ reg = <0x0 0xfc700000 0x0 0x1000>; ++ dma-coherent; ++ hypervisor_less; ++ memory-region = <&virtio_buffer>; ++ mbox-names = "mmiowr", "mmioirq"; ++ mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ ++ &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ ++ }; ++}; ++ ++&{/busfreq} { ++ /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ ++ status = "disabled"; ++}; ++ ++&uart4 { ++ status = "disabled"; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++&iomuxc { ++ pinctrl_uart4: uart4grp { ++ fsl,pins = < ++ MX8MP_IOMUXC_UART4_RXD__UART4_DCE_RX 0x140 ++ MX8MP_IOMUXC_UART4_TXD__UART4_DCE_TX 0x140 ++ >; ++ }; ++}; ++ ++&fec { ++ status = "disabled"; ++}; ++ ++&eqos { ++ /delete-property/ clocks; ++ /delete-property/ clock-names; ++ clocks = <&clk IMX8MP_CLK_QOS_ENET_ROOT>, ++ <&clk IMX8MP_CLK_ENET_QOS_TIMER>, ++ <&clk IMX8MP_CLK_ENET_QOS>; ++ clock-names = "pclk", "ptp_ref", "tx"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-cm7.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-cm7.dts +new file mode 100644 +index 000000000000..3f8609b0413d +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-cm7.dts +@@ -0,0 +1,72 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mp-evk.dts" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ cm7_reserved: cm7@80000000 { ++ reg = <0 0x80000000 0x0 0x01000000>; ++ no-map; ++ }; ++ ++ virtio_buffer: virtio-buffer@b3c00000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xb3c00000 0 0x4000000>; ++ no-map; ++ }; ++ ++ virtio_reserved: virtio@b8400000 { ++ no-map; ++ reg = <0 0xb8400000 0x0 0x00100000>; ++ }; ++ }; ++ ++ virtio_net@b8400000 { ++ compatible = "virtio,mmio"; ++ reg = <0x0 0xb8400000 0x0 0x1000>; ++ hypervisor_less; ++ memory-region = <&virtio_buffer>; ++ mbox-names = "mmiowr", "mmioirq"; ++ mboxes = <&mu 0 2 ++ &mu 1 2>; ++ }; ++}; ++ ++&{/busfreq} { ++ /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ ++ status = "disabled"; ++}; ++ ++&uart4 { ++ status = "disabled"; ++}; ++ ++ ++&fec { ++ status = "disabled"; ++}; ++ ++&eqos { ++ /delete-property/ clocks; ++ /delete-property/ clock-names; ++ clocks = <&clk IMX8MP_CLK_QOS_ENET_ROOT>, ++ <&clk IMX8MP_CLK_ENET_QOS_TIMER>, ++ <&clk IMX8MP_CLK_ENET_QOS>; ++ clock-names = "pclk", "ptp_ref", "tx"; ++}; ++ ++&i2c3 { ++ status = "disabled"; ++}; ++ ++&sai3 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox-1.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox-1.dtsi +new file mode 100644 +index 000000000000..5f8ba823c0bd +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox-1.dtsi +@@ -0,0 +1,27 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ gen_sw_mbox_1_reserved: gen-sw-mbox-1@fe001000 { ++ reg = <0 0xfe001000 0 0x1000>; ++ no-map; ++ }; ++ ++ }; ++ ++ gen_sw_mbox_1: generic-software-mailbox-1@fe001000 { ++ compatible = "fsl,generic-software-mbox"; ++ reg = <0 0xfe001000 0 0x1000>; ++ #mbox-cells = <3>; ++ /* Use 2 unused SPI interrupts */ ++ interrupts = , ; ++ interrupt-names = "irq", "remote_irq"; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox.dtsi +new file mode 100644 +index 000000000000..8709e076bcc0 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox.dtsi +@@ -0,0 +1,27 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ gen_sw_mbox_reserved: gen-sw-mbox@fe000000 { ++ reg = <0 0xfe000000 0 0x1000>; ++ no-map; ++ }; ++ ++ }; ++ ++ gen_sw_mbox: generic-software-mailbox@fe000000 { ++ compatible = "fsl,generic-software-mbox"; ++ reg = <0 0xfe000000 0 0x1000>; ++ #mbox-cells = <3>; ++ /* Use 2 unused SPI interrupts */ ++ interrupts = , ; ++ interrupt-names = "irq", "remote_irq"; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53-1.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53-1.dtsi +new file mode 100644 +index 000000000000..0e1f484209ff +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53-1.dtsi +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx8mp-generic-mbox-1.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ rpmsg_ca53_1_reserved: rpmsg-ca53-1@fe110000 { ++ reg = <0 0xfe110000 0 0x10000>; ++ no-map; ++ }; ++ ++ vdevbuffer_ca53_1: vdevbuffer-ca53-1@fe300000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xfe300000 0 0x100000>; ++ no-map; ++ }; ++ }; ++ ++ rpmsg-ca53-1 { ++ compatible = "fsl,imx8mm-rpmsg"; ++ reg = <0x0 0xfe110000 0x0 0x10000>; /* 64K for one rpmsg instance */ ++ dma-coherent; ++ memory-region = <&vdevbuffer_ca53_1>; ++ mbox-names = "tx", "rx", "rxdb"; ++ mboxes = <&gen_sw_mbox_1 0 0 1 /* Tx channel with ACK */ ++ &gen_sw_mbox_1 1 0 0 /* Rx channel without ACK */ ++ &gen_sw_mbox_1 2 0 1>; /* RXDB channel with ACK */ ++ vdev-nums = <1>; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53.dtsi +new file mode 100644 +index 000000000000..32e127a6d203 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53.dtsi +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++#include "imx8mp-generic-mbox.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ rpmsg_ca53_reserved: rpmsg-ca53@fe100000 { ++ reg = <0 0xfe100000 0 0x10000>; ++ no-map; ++ }; ++ ++ vdevbuffer_ca53: vdevbuffer-ca53@fe200000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xfe200000 0 0x100000>; ++ no-map; ++ }; ++ }; ++ ++ rpmsg-ca53 { ++ compatible = "fsl,imx8mm-rpmsg"; ++ reg = <0x0 0xfe100000 0x0 0x10000>; /* 64K for one rpmsg instance */ ++ dma-coherent; ++ memory-region = <&vdevbuffer_ca53>; ++ mbox-names = "tx", "rx", "rxdb"; ++ mboxes = <&gen_sw_mbox 0 0 1 /* Tx channel with ACK */ ++ &gen_sw_mbox 1 0 0 /* Rx channel without ACK */ ++ &gen_sw_mbox 2 0 1>; /* RXDB channel with ACK */ ++ vdev-nums = <1>; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi +index 593b0c6ae72f..4a6b9d168c20 100644 +--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi ++++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi +@@ -569,6 +569,16 @@ wdog3: watchdog@302a0000 { + status = "disabled"; + }; + ++ gpt1: gpt@302d0000 { ++ compatible = "fsl,imx8mq-gpt", "fsl,imx7d-gpt"; ++ reg = <0x302d0000 0x10000>; ++ interrupts = ; ++ clocks = <&clk IMX8MP_CLK_GPT1_ROOT>, ++ <&clk IMX8MP_CLK_GPT1_ROOT>; ++ clock-names = "ipg", "per"; ++ status = "disabled"; ++ }; ++ + iomuxc: pinctrl@30330000 { + compatible = "fsl,imx8mp-iomuxc"; + reg = <0x30330000 0x10000>; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-avb.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-avb.dts +new file mode 100644 +index 000000000000..f6dd57285814 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-avb.dts +@@ -0,0 +1,41 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx93-11x11-evk.dts" ++ ++/* AVB HW timer*/ ++&tpm4 { ++ compatible = "fsl,avb-tpm"; ++ timer-channel = <1>; /* Use output compare channel 1*/ ++ prescale = <1>; ++ domain = <0>; ++ interrupts = ; ++ ++ clocks = <&clk IMX93_CLK_BUS_WAKEUP>, <&clk IMX93_CLK_TPM4_GATE>, <&clk IMX93_CLK_AUDIO_PLL>; ++ clock-names = "ipg", "per", "audio_pll"; ++ ++ /* Set TPM4 clock root to Audio PLL. */ ++ assigned-clocks = <&clk IMX93_CLK_TPM4>; ++ assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>; ++ assigned-clock-rates = <0>; ++ ++ /* Enble SW sampling for media clock recovery on port 0 */ ++ sw-recovery = <0>; ++ ++ status = "okay"; ++}; ++ ++&fec { ++ fsl,rx-phy-delay-100-ns = <670>; ++ fsl,tx-phy-delay-100-ns = <670>; ++ fsl,rx-phy-delay-1000-ns = <0>; ++ fsl,tx-phy-delay-1000-ns = <0>; ++}; ++ ++ðphy2 { ++ eee-broken-100tx; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-baremetal.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-baremetal.dts +new file mode 100644 +index 000000000000..84f9700437a1 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-baremetal.dts +@@ -0,0 +1,31 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx93-11x11-evk.dts" ++ ++/ { ++ model = "NXP i.MX93 11X11 EVK board - Baremetal"; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ bm_reserved: baremetal@b0000000 { ++ no-map; ++ reg = <0 0xb0000000 0 0x10000000>; ++ }; ++ }; ++}; ++ ++&fec { ++ status = "disabled"; ++}; ++ ++&lpuart2 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa.dts +new file mode 100644 +index 000000000000..954e78a84f43 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa.dts +@@ -0,0 +1,96 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx93-11x11-evk.dts" ++ ++&flexcan2 { ++ status = "disabled"; ++}; ++ ++&lpuart7 { ++ status = "disabled"; ++}; ++ ++&lpspi3 { ++ fsl,spi-num-chipselects = <1>; ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&pinctrl_lpspi3>; ++ pinctrl-1 = <&pinctrl_lpspi3>; ++ cs-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>; ++ pinctrl-assert-gpios = <&adp5585gpio 4 GPIO_ACTIVE_HIGH>; ++ status = "okay"; ++ ++ netcdsa: ethernet-switch@0 { ++ reg = <0x0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "nxp,imxrt1180-netc"; ++ /* 1 MHz */ ++ spi-max-frequency = <1000000>; ++ /* Sample data on trailing clock edge */ ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ label = "swp0"; ++ reg = <0>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <100>; ++ full-duplex; ++ }; ++ }; ++ ++ port@1 { ++ label = "swp1"; ++ reg = <1>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ port@2 { ++ label = "swp2"; ++ reg = <2>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ port@3 { ++ /* cpu port connected to eqos */ ++ ethernet = <&eqos>; ++ label = "cpu"; ++ reg = <3>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl_lpspi3: lpspi3grp { ++ fsl,pins = < ++ MX93_PAD_GPIO_IO08__GPIO2_IO08 0x3fe ++ MX93_PAD_GPIO_IO09__LPSPI3_SIN 0x3fe ++ MX93_PAD_GPIO_IO10__LPSPI3_SOUT 0x3fe ++ MX93_PAD_GPIO_IO11__LPSPI3_SCK 0x3fe ++ >; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat.dts +new file mode 100644 +index 000000000000..3626356fb9b6 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx93-11x11-evk.dts" ++ ++ ++&fec { ++ compatible = "fsl,imx93-fec-ecat", "fsl,imx8mp-fec-ecat", "fsl,imx8mq-fec-ecat"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts +new file mode 100644 +index 000000000000..cac3133ddc82 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts +@@ -0,0 +1,38 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx93-11x11-evk.dts" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* ++ * Reserve up to 16MB for one possible FreeRTOS instances running on ++ * one Cortex-A Cores when booting Linux on at least on Cortex-A Core. ++ */ ++ rtos_ca55_reserved: rtos-ca55@d0000000 { ++ no-map; ++ reg = <0 0xd0000000 0x0 0x1000000>; ++ }; ++ ++ /* Reserve 16MB for FreeRTOS on M33 */ ++ m33_reserved: m33@a5000000 { ++ no-map; ++ reg = <0 0xa5000000 0 0x1000000>; ++ }; ++ }; ++}; ++ ++&lpuart2 { ++ status = "disabled"; ++}; ++ ++&clk { ++ init-on-array = ; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-uart-sharing-cm33.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-uart-sharing-cm33.dts +new file mode 100644 +index 000000000000..e55552658d09 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-uart-sharing-cm33.dts +@@ -0,0 +1,96 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include ++#include "imx93-11x11-evk.dts" ++ ++/ { ++ uart_rpbus_0: uart-rpbus-0 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_1: uart-rpbus-1 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_2: uart-rpbus-2 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_3: uart-rpbus-3 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_4: uart-rpbus-4 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_5: uart-rpbus-5 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_6: uart-rpbus-6 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_7: uart-rpbus-7 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_8: uart-rpbus-8 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_9: uart-rpbus-9 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_10: uart-rpbus-10 { ++ compatible = "fsl,uart-rpbus"; ++ status = "okay"; /* mcore directly print data that receive from acore */ ++ }; ++}; ++ ++/* ++ * ATTENTION: M33 may use IPs like below ++ * LPUART5 ++ */ ++ ++/* the lpuart5 is used by m33 to support srtm uart service */ ++&lpuart5 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-ca55.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-ca55.dts +new file mode 100644 +index 000000000000..17a6fdeb8ca0 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-ca55.dts +@@ -0,0 +1,55 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx93-11x11-evk.dts" ++#include "imx93-generic-mbox.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ virtio_reserved: virtio@fc700000 { ++ no-map; ++ reg = <0 0xfc700000 0x0 0x00100000>; ++ }; ++ ++ /* 512MB */ ++ freertos_reserved: inmate@d0000000 { ++ no-map; ++ reg = <0 0xd0000000 0x0 0x20000000>; ++ }; ++ ++ virtio_buffer: virtio-buffer@f8700000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xf8700000 0 0x4000000>; ++ }; ++ }; ++ ++ virtio_net@fc700000 { ++ compatible = "virtio,mmio"; ++ reg = <0x0 0xfc700000 0x0 0x1000>; ++ dma-coherent; ++ hypervisor_less; ++ memory-region = <&virtio_buffer>; ++ mbox-names = "mmiowr", "mmioirq"; ++ mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ ++ &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ ++ }; ++}; ++ ++&lpuart2 { ++ status = "disabled"; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++&fec { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-cm33.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-cm33.dts +new file mode 100644 +index 000000000000..f92138a94ab8 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-cm33.dts +@@ -0,0 +1,53 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx93-11x11-evk.dts" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ cm33_reserved: cm33@a0000000 { ++ reg = <0 0xa0000000 0x0 0x01000000>; ++ no-map; ++ }; ++ ++ virtio_buffer: virtio-buffer@a1000000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xa1000000 0 0x3000000>; ++ no-map; ++ }; ++ ++ virtio_reserved: virtio@a8400000 { ++ reg = <0 0xa8400000 0x0 0x00100000>; ++ no-map; ++ }; ++ }; ++ ++ virtio_net@a8400000 { ++ compatible = "virtio,mmio"; ++ reg = <0x0 0xa8400000 0x0 0x1000>; ++ hypervisor_less; ++ memory-region = <&virtio_buffer>; ++ mbox-names = "mmiowr", "mmioirq"; ++ mboxes = <&mu1 0 2 ++ &mu1 1 2>; ++ }; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++&lpuart2 { ++ status = "disabled"; ++}; ++ ++&fec { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts +index d23880c3afcf..a597842e652c 100644 +--- a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts +@@ -331,7 +331,7 @@ &xcvr { + assigned-clock-rates = <12288000>, <200000000>; + status = "okay"; + }; +- ++ + &adc1 { + vref-supply = <®_vref_1v8>; + status = "okay"; +@@ -367,6 +367,9 @@ &eqos { + pinctrl-0 = <&pinctrl_eqos>; + phy-mode = "rgmii-id"; + phy-handle = <ðphy1>; ++ snps,force_thresh_dma_mode; ++ snps,mtl-tx-config = <&mtl_tx_setup>; ++ snps,mtl-rx-config = <&mtl_rx_setup>; + status = "okay"; + + mdio { +@@ -381,6 +384,61 @@ ethphy1: ethernet-phy@1 { + eee-broken-1000t; + }; + }; ++ ++ mtl_tx_setup: tx-queues-config { ++ snps,tx-queues-to-use = <5>; ++ snps,tx-sched-sp; ++ queue0 { ++ snps,dcb-algorithm; ++ snps,priority = <0x1>; ++ }; ++ queue1 { ++ snps,dcb-algorithm; ++ snps,priority = <0x2>; ++ }; ++ queue2 { ++ snps,dcb-algorithm; ++ snps,priority = <0x4>; ++ }; ++ queue3 { ++ snps,dcb-algorithm; ++ snps,priority = <0x8>; ++ }; ++ queue4 { ++ snps,dcb-algorithm; ++ snps,priority = <0xf0>; ++ }; ++ }; ++ ++ mtl_rx_setup: rx-queues-config { ++ snps,rx-queues-to-use = <5>; ++ snps,rx-sched-sp; ++ queue0 { ++ snps,dcb-algorithm; ++ snps,priority = <0x1>; ++ snps,map-to-dma-channel = <0>; ++ }; ++ queue1 { ++ snps,dcb-algorithm; ++ snps,priority = <0x2>; ++ snps,map-to-dma-channel = <1>; ++ }; ++ queue2 { ++ snps,dcb-algorithm; ++ snps,priority = <0x4>; ++ snps,map-to-dma-channel = <2>; ++ }; ++ queue3 { ++ snps,dcb-algorithm; ++ snps,priority = <0x8>; ++ snps,map-to-dma-channel = <3>; ++ }; ++ queue4 { ++ snps,dcb-algorithm; ++ snps,priority = <0xf0>; ++ snps,map-to-dma-channel = <4>; ++ }; ++ }; + }; + + &fec { +diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-baremetal.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-baremetal.dts +new file mode 100644 +index 000000000000..8b4cbd366ae6 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-baremetal.dts +@@ -0,0 +1,31 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx93-9x9-qsb.dts" ++ ++/ { ++ model = "NXP i.MX93 9X9 QSB board - Baremetal"; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ bm_reserved: baremetal@b0000000 { ++ no-map; ++ reg = <0 0xb0000000 0 0x10000000>; ++ }; ++ }; ++}; ++ ++&fec { ++ status = "disabled"; ++}; ++ ++&lpuart2 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-inmate.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-inmate.dts +new file mode 100644 +index 000000000000..cd1f0e0cc401 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-inmate.dts +@@ -0,0 +1,151 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include ++ ++/ { ++ model = "NXP i.MX93 9x9 QSB"; ++ compatible = "fsl,imx93-9x9-qsb", "fsl,imx93"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ aliases { ++ mmc0 = &usdhc1; ++ serial1 = &lpuart2; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ A55_0: cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a55"; ++ reg = <0x0>; ++ enable-method = "psci"; ++ #cooling-cells = <2>; ++ }; ++ }; ++ ++ psci { ++ compatible = "arm,psci-1.0"; ++ method = "smc"; ++ }; ++ ++ gic: interrupt-controller@48000000 { ++ compatible = "arm,gic-v3"; ++ reg = <0 0x48000000 0 0x10000>, ++ <0 0x48040000 0 0xc0000>; ++ #interrupt-cells = <3>; ++ interrupt-controller; ++ interrupts = ; ++ interrupt-parent = <&gic>; ++ }; ++ ++ timer { ++ compatible = "arm,armv8-timer"; ++ interrupts = , ++ , ++ , ++ ; ++ clock-frequency = <24000000>; ++ }; ++ ++ clk_dummy: clock-dummy { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <0>; ++ clock-output-names = "clk_dummy"; ++ }; ++ ++ clk_400m: clock-400m { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <200000000>; ++ clock-output-names = "200m"; ++ }; ++ ++ osc_24m: clock-osc-24m { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ clock-output-names = "osc_24m"; ++ }; ++ ++ pci@fd700000 { ++ compatible = "pci-host-ecam-generic"; ++ device_type = "pci"; ++ bus-range = <0 0>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &gic GIC_SPI 227 IRQ_TYPE_EDGE_RISING>, ++ <0 0 0 2 &gic GIC_SPI 228 IRQ_TYPE_EDGE_RISING>, ++ <0 0 0 3 &gic GIC_SPI 229 IRQ_TYPE_EDGE_RISING>, ++ <0 0 0 4 &gic GIC_SPI 230 IRQ_TYPE_EDGE_RISING>; ++ reg = <0x0 0xfd700000 0x0 0x100000>; ++ ranges = <0x02000000 0x00 0x10000000 0x0 0x10000000 0x00 0x10000>; ++ }; ++ ++ soc@0 { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x0 0x0 0x0 0x80000000>, ++ <0x28000000 0x0 0x28000000 0x10000000>; ++ ++ aips1: bus@44000000 { ++ compatible = "fsl,aips-bus", "simple-bus"; ++ reg = <0x44000000 0x800000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ lpuart2: serial@44390000 { ++ compatible = "fsl,imx93-lpuart", "fsl,imx8ulp-lpuart", "fsl,imx7ulp-lpuart"; ++ reg = <0x44390000 0x1000>; ++ interrupts = ; ++ status = "disabled"; ++ }; ++ }; ++ ++ aips3: bus@42800000 { ++ compatible = "fsl,aips-bus", "simple-bus"; ++ reg = <0x42800000 0x800000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ usdhc1: mmc@42850000 { ++ compatible = "fsl,imx93-usdhc", "fsl,imx8mm-usdhc"; ++ reg = <0x42850000 0x10000>; ++ interrupts = ; ++ fsl,tuning-start-tap = <20>; ++ fsl,tuning-step= <2>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++}; ++ ++&lpuart2 { ++ clocks = <&osc_24m>; ++ clock-names = "ipg"; ++ status = "okay"; ++}; ++ ++&usdhc1 { ++ clocks = <&clk_dummy>, ++ <&clk_dummy>, ++ <&clk_400m>; ++ clock-names = "ipg", "ahb", "per"; ++ bus-width = <8>; ++ non-removable; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-root.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-root.dts +new file mode 100644 +index 000000000000..e61f46be93b7 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-root.dts +@@ -0,0 +1,54 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx93-9x9-qsb.dts" ++ ++/{ ++ interrupt-parent = <&gic>; ++ ++ resmem: reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ }; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++&iomuxc { ++ pinctrl_uart2: uart2grp { ++ fsl,pins = < ++ MX93_PAD_UART2_TXD__LPUART2_TX 0x31e ++ MX93_PAD_UART2_RXD__LPUART2_RX 0x31e ++ >; ++ }; ++}; ++ ++&lpuart2 { ++ /delete-property/ dmas; ++ /delete-property/ dma-names; ++ status = "disabled"; ++}; ++ ++&lpuart1 { ++ pinctrl-0 = <&pinctrl_uart1>, <&pinctrl_uart2>; ++ assigned-clocks = <&clk IMX93_CLK_LPUART2>; ++ assigned-clock-parents = <&clk IMX93_CLK_24M>; ++}; ++ ++&usdhc1 { ++ status = "disabled"; ++}; ++ ++&usdhc2 { ++ pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>, <&pinctrl_usdhc1>; ++ pinctrl-1 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>, <&pinctrl_usdhc1>; ++ pinctrl-2 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>, <&pinctrl_usdhc1>; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-uart-sharing-cm33.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-uart-sharing-cm33.dts +new file mode 100644 +index 000000000000..c897b9e9ced2 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-uart-sharing-cm33.dts +@@ -0,0 +1,96 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++ ++#include ++#include "imx93-9x9-qsb.dts" ++ ++/ { ++ uart_rpbus_0: uart-rpbus-0 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_1: uart-rpbus-1 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_2: uart-rpbus-2 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_3: uart-rpbus-3 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_4: uart-rpbus-4 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_5: uart-rpbus-5 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_6: uart-rpbus-6 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_7: uart-rpbus-7 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_8: uart-rpbus-8 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_9: uart-rpbus-9 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use lpuart5 */ ++ flags=; ++ status = "okay"; ++ }; ++ ++ uart_rpbus_10: uart-rpbus-10 { ++ compatible = "fsl,uart-rpbus"; ++ status = "okay"; /* mcore directly print data that receive from acore */ ++ }; ++}; ++ ++/* ++ * ATTENTION: M33 may use IPs like below ++ * LPUART5 ++ */ ++ ++/* the lpuart5 is used by m33 to support srtm uart service */ ++&lpuart5 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts +index fba9380bdc69..314bd2ef7820 100644 +--- a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts ++++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts +@@ -224,6 +224,9 @@ &eqos { + pinctrl-0 = <&pinctrl_eqos>; + phy-mode = "rgmii-id"; + phy-handle = <ðphy1>; ++ snps,force_thresh_dma_mode; ++ snps,mtl-tx-config = <&mtl_tx_setup>; ++ snps,mtl-rx-config = <&mtl_rx_setup>; + status = "okay"; + + mdio { +@@ -238,6 +241,61 @@ ethphy1: ethernet-phy@1 { + eee-broken-1000t; + }; + }; ++ ++ mtl_tx_setup: tx-queues-config { ++ snps,tx-queues-to-use = <5>; ++ snps,tx-sched-sp; ++ queue0 { ++ snps,dcb-algorithm; ++ snps,priority = <0x1>; ++ }; ++ queue1 { ++ snps,dcb-algorithm; ++ snps,priority = <0x2>; ++ }; ++ queue2 { ++ snps,dcb-algorithm; ++ snps,priority = <0x4>; ++ }; ++ queue3 { ++ snps,dcb-algorithm; ++ snps,priority = <0x8>; ++ }; ++ queue4 { ++ snps,dcb-algorithm; ++ snps,priority = <0xf0>; ++ }; ++ }; ++ ++ mtl_rx_setup: rx-queues-config { ++ snps,rx-queues-to-use = <5>; ++ snps,rx-sched-sp; ++ queue0 { ++ snps,dcb-algorithm; ++ snps,priority = <0x1>; ++ snps,map-to-dma-channel = <0>; ++ }; ++ queue1 { ++ snps,dcb-algorithm; ++ snps,priority = <0x2>; ++ snps,map-to-dma-channel = <1>; ++ }; ++ queue2 { ++ snps,dcb-algorithm; ++ snps,priority = <0x4>; ++ snps,map-to-dma-channel = <2>; ++ }; ++ queue3 { ++ snps,dcb-algorithm; ++ snps,priority = <0x8>; ++ snps,map-to-dma-channel = <3>; ++ }; ++ queue4 { ++ snps,dcb-algorithm; ++ snps,priority = <0xf0>; ++ snps,map-to-dma-channel = <4>; ++ }; ++ }; + }; + + &lpm { +diff --git a/arch/arm64/boot/dts/freescale/imx93-generic-mbox.dtsi b/arch/arm64/boot/dts/freescale/imx93-generic-mbox.dtsi +new file mode 100644 +index 000000000000..5e6e1723a05d +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-generic-mbox.dtsi +@@ -0,0 +1,27 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ gen_sw_mbox_reserved: gen-sw-mbox@fe000000 { ++ reg = <0 0xfe000000 0 0x1000>; ++ no-map; ++ }; ++ ++ }; ++ ++ gen_sw_mbox: generic-software-mailbox@fe000000 { ++ compatible = "fsl,generic-software-mbox"; ++ reg = <0 0xfe000000 0 0x1000>; ++ #mbox-cells = <3>; ++ /* Use 2 unused SPI interrupts */ ++ interrupts = , ; ++ interrupt-names = "irq", "remote_irq"; ++ }; ++}; +diff --git a/arch/arm64/configs/imx_avb.config b/arch/arm64/configs/imx_avb.config +new file mode 100644 +index 000000000000..1c8ba04c0381 +--- /dev/null ++++ b/arch/arm64/configs/imx_avb.config +@@ -0,0 +1,8 @@ ++CONFIG_EXPERT=y ++CONFIG_PREEMPT_RT=y ++CONFIG_AVB_SUPPORT=y ++CONFIG_NET_SWITCHDEV=y ++CONFIG_DEBUG_INFO=y ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y +diff --git a/arch/arm64/configs/imx_v8_defconfig b/arch/arm64/configs/imx_v8_defconfig +index 10979905f06e..2937c1bc49c5 100644 +--- a/arch/arm64/configs/imx_v8_defconfig ++++ b/arch/arm64/configs/imx_v8_defconfig +@@ -5,6 +5,8 @@ CONFIG_NO_HZ_IDLE=y + CONFIG_HIGH_RES_TIMERS=y + CONFIG_BPF_SYSCALL=y + CONFIG_BPF_JIT=y ++CONFIG_BPF_JIT_ALWAYS_ON=y ++CONFIG_XDP_SOCKETS=y + CONFIG_PREEMPT=y + CONFIG_IRQ_TIME_ACCOUNTING=y + CONFIG_BSD_PROCESS_ACCT=y +@@ -136,29 +138,37 @@ CONFIG_BRIDGE_NF_EBTABLES=m + CONFIG_BRIDGE=y + CONFIG_BRIDGE_VLAN_FILTERING=y + CONFIG_NET_DSA=m ++CONFIG_NET_DSA_SJA1105=m ++CONFIG_NET_DSA_SJA1105_PTP=y ++CONFIG_NET_DSA_SJA1105_TAS=y ++CONFIG_NET_DSA_SJA1105_VL=y + CONFIG_VLAN_8021Q_GVRP=y + CONFIG_VLAN_8021Q_MVRP=y + CONFIG_LLC2=y + CONFIG_NET_SCHED=y +-CONFIG_NET_SCH_MULTIQ=m +-CONFIG_NET_SCH_CBS=m +-CONFIG_NET_SCH_ETF=m +-CONFIG_NET_SCH_TAPRIO=m +-CONFIG_NET_SCH_MQPRIO=m +-CONFIG_NET_SCH_INGRESS=m ++CONFIG_NET_SCH_MULTIQ=y ++CONFIG_NET_SCH_CBS=y ++CONFIG_NET_SCH_ETF=y ++CONFIG_NET_SCH_TAPRIO=y ++CONFIG_NET_SCH_MQPRIO=y ++CONFIG_NET_SCH_INGRESS=y + CONFIG_NET_CLS_BASIC=m +-CONFIG_NET_CLS_U32=m ++CONFIG_NET_CLS_U32=y + CONFIG_NET_CLS_FLOWER=m + CONFIG_NET_CLS_ACT=y + CONFIG_NET_ACT_GACT=m + CONFIG_NET_ACT_MIRRED=m +-CONFIG_NET_ACT_SKBEDIT=m ++CONFIG_NET_ACT_SKBEDIT=y + CONFIG_NET_ACT_GATE=m ++CONFIG_NET_EMATCH=y ++CONFIG_NET_EMATCH_STACK=32 ++CONFIG_NET_EMATCH_U32=y ++CONFIG_NET_EMATCH_META=y + CONFIG_TSN=y + CONFIG_QRTR=m + CONFIG_QRTR_SMD=m + CONFIG_QRTR_TUN=m +-CONFIG_NET_PKTGEN=m ++CONFIG_NET_PKTGEN=y + CONFIG_CAN=m + CONFIG_BT=y + CONFIG_BT_RFCOMM=y +@@ -286,6 +296,7 @@ CONFIG_TUN=y + CONFIG_VETH=m + CONFIG_VIRTIO_NET=y + CONFIG_NET_DSA_MSCC_FELIX=m ++CONFIG_NET_DSA_NETC=m + CONFIG_NET_DSA_SJA1105=m + CONFIG_NET_DSA_SJA1105_PTP=y + CONFIG_NET_DSA_SJA1105_TAS=y +@@ -298,6 +309,7 @@ CONFIG_SYSTEMPORT=m + CONFIG_MACB=y + CONFIG_THUNDER_NIC_PF=y + CONFIG_FEC=y ++CONFIG_FEC_ECAT=y + CONFIG_FEC_UIO=y + CONFIG_FSL_FMAN=y + CONFIG_FSL_DPAA_ETH=y +@@ -1131,3 +1143,11 @@ CONFIG_CORESIGHT_STM=m + CONFIG_CORESIGHT_CPU_DEBUG=m + CONFIG_CORESIGHT_CTI=m + CONFIG_MEMTEST=y ++ ++# Preempt RT, depend on CONFIG_EXPERT ++CONFIG_EXPERT=y ++CONFIG_PREEMPT_RT=y ++ ++CONFIG_RPMSG_TTY=m ++CONFIG_GENERIC_SOFTWARE_MAILBOX=y ++CONFIG_VIRTIO_TRANS=y +diff --git a/arch/arm64/configs/linux-dpaa-ethercat.config b/arch/arm64/configs/linux-dpaa-ethercat.config +new file mode 100644 +index 000000000000..15e4b306bc3d +--- /dev/null ++++ b/arch/arm64/configs/linux-dpaa-ethercat.config +@@ -0,0 +1 @@ ++CONFIG_FSL_DPAA_ETHERCAT=y +diff --git a/arch/arm64/configs/linux-rpmsg-8m-buf.config b/arch/arm64/configs/linux-rpmsg-8m-buf.config +new file mode 100644 +index 000000000000..d7780b2b8299 +--- /dev/null ++++ b/arch/arm64/configs/linux-rpmsg-8m-buf.config +@@ -0,0 +1 @@ ++CONFIG_RPMSG_8M_BUF=y +diff --git a/arch/arm64/configs/lsdk.config b/arch/arm64/configs/lsdk.config +index e27248011e58..eb33235e4eab 100644 +--- a/arch/arm64/configs/lsdk.config ++++ b/arch/arm64/configs/lsdk.config +@@ -84,12 +84,25 @@ CONFIG_BRIDGE_NF_EBTABLES=y + CONFIG_BRIDGE_EBT_T_NAT=y + CONFIG_BRIDGE_EBT_DNAT=y + CONFIG_BRIDGE_EBT_SNAT=y ++CONFIG_BRIDGE_EBT_BROUTE=y ++CONFIG_BRIDGE_EBT_802_3=y ++CONFIG_BRIDGE_EBT_IP=y ++CONFIG_BRIDGE_EBT_IP6=y + CONFIG_NETFILTER_XT_MATCH_COMMENT=y + CONFIG_SFP=y + CONFIG_PHY_FSL_LYNX_28G=y + CONFIG_BPF_SYSCALL=y + CONFIG_BPF_JIT_ALWAYS_ON=y + CONFIG_XDP_SOCKETS=y ++CONFIG_NET_SCH_INGRESS=y ++CONFIG_NET_ACT_POLICE=y ++CONFIG_NET_ACT_GACT=y ++CONFIG_NET_ACT_GATE=y ++CONFIG_NET_CLS_MATCHALL=y ++CONFIG_NET_PKTGEN=y ++ ++# Enable felix switch TSN ++CONFIG_MSCC_FELIX_SWITCH_TSN=y + + # GPU + CONFIG_DRM=y +@@ -97,7 +110,6 @@ CONFIG_MXC_GPU_VIV=y + + # disable unneeded options and override default options set by defconfig to deduce the size of modules + # CONFIG_NET_VENDOR_STMICRO is not set +-# CONFIG_MEDIA_SUPPORT is not set + # CONFIG_BACKLIGHT_GENERIC is not set + # CONFIG_TEGRA_HOST1X is not set + # CONFIG_BT is not set +@@ -181,6 +193,7 @@ CONFIG_NET_CLS_FLOWER=y + CONFIG_NET_EMATCH=y + CONFIG_NET_EMATCH_STACK=32 + CONFIG_NET_EMATCH_U32=y ++CONFIG_NET_EMATCH_META=y + CONFIG_NET_CLS_ACT=y + CONFIG_NET_ACT_PEDIT=y + CONFIG_NET_ACT_SKBEDIT=y +@@ -191,8 +204,16 @@ CONFIG_USB_RTL8152=y + CONFIG_USB_USBNET=y + CONFIG_USB_NET_AX8817X=y + CONFIG_USB_NET_AX88179_178A=y +-# CONFIG_NET_PKTGEN is not set + + #build-in drivers + CONFIG_FSL_DPAA2_SWITCH=y + CONFIG_QORIQ_THERMAL=y ++ ++# Preempt RT, depend on CONFIG_EXPERT ++CONFIG_PREEMPT_RT=y ++ ++CONFIG_NFC_NXP_PN5XX=m ++ ++# IVSHMEM ++CONFIG_IVSHMEM_NET=y ++CONFIG_UIO_IVSHMEM=y +diff --git a/arch/arm64/include/asm/preempt.h b/arch/arm64/include/asm/preempt.h +index 0159b625cc7f..a5486918e5ee 100644 +--- a/arch/arm64/include/asm/preempt.h ++++ b/arch/arm64/include/asm/preempt.h +@@ -71,13 +71,36 @@ static inline bool __preempt_count_dec_and_test(void) + * interrupt occurring between the non-atomic READ_ONCE/WRITE_ONCE + * pair. + */ +- return !pc || !READ_ONCE(ti->preempt_count); ++ if (!pc || !READ_ONCE(ti->preempt_count)) ++ return true; ++#ifdef CONFIG_PREEMPT_LAZY ++ if ((pc & ~PREEMPT_NEED_RESCHED)) ++ return false; ++ if (current_thread_info()->preempt_lazy_count) ++ return false; ++ return test_thread_flag(TIF_NEED_RESCHED_LAZY); ++#else ++ return false; ++#endif + } + + static inline bool should_resched(int preempt_offset) + { ++#ifdef CONFIG_PREEMPT_LAZY ++ u64 pc = READ_ONCE(current_thread_info()->preempt_count); ++ if (pc == preempt_offset) ++ return true; ++ ++ if ((pc & ~PREEMPT_NEED_RESCHED) != preempt_offset) ++ return false; ++ ++ if (current_thread_info()->preempt_lazy_count) ++ return false; ++ return test_thread_flag(TIF_NEED_RESCHED_LAZY); ++#else + u64 pc = READ_ONCE(current_thread_info()->preempt_count); + return pc == preempt_offset; ++#endif + } + + #ifdef CONFIG_PREEMPTION +diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h +index 848739c15de8..4b7148fd5551 100644 +--- a/arch/arm64/include/asm/thread_info.h ++++ b/arch/arm64/include/asm/thread_info.h +@@ -26,6 +26,7 @@ struct thread_info { + #ifdef CONFIG_ARM64_SW_TTBR0_PAN + u64 ttbr0; /* saved TTBR0_EL1 */ + #endif ++ int preempt_lazy_count; /* 0 => preemptable, <0 => bug */ + union { + u64 preempt_count; /* 0 => preemptible, <0 => bug */ + struct { +@@ -68,6 +69,7 @@ int arch_dup_task_struct(struct task_struct *dst, + #define TIF_UPROBE 4 /* uprobe breakpoint or singlestep */ + #define TIF_MTE_ASYNC_FAULT 5 /* MTE Asynchronous Tag Check Fault */ + #define TIF_NOTIFY_SIGNAL 6 /* signal notifications exist */ ++#define TIF_NEED_RESCHED_LAZY 7 + #define TIF_SYSCALL_TRACE 8 /* syscall trace active */ + #define TIF_SYSCALL_AUDIT 9 /* syscall auditing */ + #define TIF_SYSCALL_TRACEPOINT 10 /* syscall tracepoint for ftrace */ +@@ -100,8 +102,10 @@ int arch_dup_task_struct(struct task_struct *dst, + #define _TIF_SVE (1 << TIF_SVE) + #define _TIF_MTE_ASYNC_FAULT (1 << TIF_MTE_ASYNC_FAULT) + #define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) ++#define _TIF_NEED_RESCHED_LAZY (1 << TIF_NEED_RESCHED_LAZY) + +-#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ ++#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY | \ ++ _TIF_SIGPENDING | \ + _TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \ + _TIF_UPROBE | _TIF_MTE_ASYNC_FAULT | \ + _TIF_NOTIFY_SIGNAL) +@@ -110,6 +114,8 @@ int arch_dup_task_struct(struct task_struct *dst, + _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \ + _TIF_SYSCALL_EMU) + ++#define _TIF_NEED_RESCHED_MASK (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY) ++ + #ifdef CONFIG_SHADOW_CALL_STACK + #define INIT_SCS \ + .scs_base = init_shadow_call_stack, \ +diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c +index 1197e7679882..e74c0415f67e 100644 +--- a/arch/arm64/kernel/asm-offsets.c ++++ b/arch/arm64/kernel/asm-offsets.c +@@ -32,6 +32,7 @@ int main(void) + DEFINE(TSK_TI_CPU, offsetof(struct task_struct, thread_info.cpu)); + DEFINE(TSK_TI_FLAGS, offsetof(struct task_struct, thread_info.flags)); + DEFINE(TSK_TI_PREEMPT, offsetof(struct task_struct, thread_info.preempt_count)); ++ DEFINE(TSK_TI_PREEMPT_LAZY, offsetof(struct task_struct, thread_info.preempt_lazy_count)); + #ifdef CONFIG_ARM64_SW_TTBR0_PAN + DEFINE(TSK_TI_TTBR0, offsetof(struct task_struct, thread_info.ttbr0)); + #endif +diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c +index 82f4572c8ddf..2a606c7bf025 100644 +--- a/arch/arm64/kernel/signal.c ++++ b/arch/arm64/kernel/signal.c +@@ -1108,7 +1108,7 @@ static void do_signal(struct pt_regs *regs) + void do_notify_resume(struct pt_regs *regs, unsigned long thread_flags) + { + do { +- if (thread_flags & _TIF_NEED_RESCHED) { ++ if (thread_flags & _TIF_NEED_RESCHED_MASK) { + /* Unmask Debug and SError for the next task */ + local_daif_restore(DAIF_PROCCTX_NOIRQ); + +diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c +index 5241f13c4068..8e534c956720 100644 +--- a/arch/arm64/kernel/smp.c ++++ b/arch/arm64/kernel/smp.c +@@ -54,6 +54,10 @@ + #define CREATE_TRACE_POINTS + #include + ++#ifdef CONFIG_BAREMETAL ++#include ++#endif ++ + DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number); + EXPORT_PER_CPU_SYMBOL(cpu_number); + +@@ -74,6 +78,13 @@ enum ipi_msg_type { + IPI_TIMER, + IPI_IRQ_WORK, + IPI_WAKEUP, ++#ifdef CONFIG_BAREMETAL ++#if defined(CONFIG_IMX8M_BAREMETAL) || defined(CONFIG_IMX93_BAREMETAL) ++ IPI_BAREMETAL_COMM = 9, ++#else ++ IPI_BAREMETAL_COMM = 8, ++#endif ++#endif + NR_IPI + }; + +@@ -770,6 +781,9 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = { + [IPI_TIMER] = "Timer broadcast interrupts", + [IPI_IRQ_WORK] = "IRQ work interrupts", + [IPI_WAKEUP] = "CPU wake-up interrupts", ++#ifdef CONFIG_BAREMETAL ++ [IPI_BAREMETAL_COMM] = "Baremetal inter-core interrupts", ++#endif + }; + + static void smp_cross_call(const struct cpumask *target, unsigned int ipinr); +@@ -908,6 +922,17 @@ static void do_handle_IPI(int ipinr) + break; + #endif + ++#ifdef CONFIG_BAREMETAL ++ case IPI_BAREMETAL_COMM: { ++ /* FIXME: use the fixed source coreID from core1 */ ++ int irqsrc = 1; ++ /*linux core is 0 core, so iterate from 1 core.*/ ++ for(irqsrc = 1; irqsrc < CONFIG_MAX_CPUS; irqsrc++) ++ ipi_baremetal_handle(ipinr, irqsrc); ++ } ++ break; ++#endif ++ + default: + pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); + break; +-- +2.34.1 + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0004-Documentation-add-NXP-RT-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0004-Documentation-add-NXP-RT-support.patch new file mode 100644 index 000000000..21144d5b7 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0004-Documentation-add-NXP-RT-support.patch @@ -0,0 +1,581 @@ +From e418c26b14e840304aa6e00ef847ec41ac0b995e Mon Sep 17 00:00:00 2001 +From: Mike Engel +Date: Fri, 23 Feb 2024 12:28:42 +0100 +Subject: [PATCH 04/10] Documentation: add NXP RT support + +Signed-off-by: Mike Engel +--- + .../mailbox/generic-software-mbox.yaml | 65 +++ + .../devicetree/bindings/net/fsl,fec.yaml | 24 ++ + .../devicetree/bindings/tty/rpmsg_tty.yaml | 67 ++++ + Documentation/printk-ringbuffer.txt | 377 ++++++++++++++++++ + 4 files changed, 533 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mailbox/generic-software-mbox.yaml + create mode 100644 Documentation/devicetree/bindings/tty/rpmsg_tty.yaml + create mode 100644 Documentation/printk-ringbuffer.txt + +diff --git a/Documentation/devicetree/bindings/mailbox/generic-software-mbox.yaml b/Documentation/devicetree/bindings/mailbox/generic-software-mbox.yaml +new file mode 100644 +index 000000000000..57c91ab2c80a +--- /dev/null ++++ b/Documentation/devicetree/bindings/mailbox/generic-software-mbox.yaml +@@ -0,0 +1,65 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/mailbox/generic-software-mbox.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Generic Software Mailbox ++ ++maintainers: ++ - Hou Zhiqiang ++ ++description: | ++ The Generic Software Mailbox is a virtual device, which uses unused ++ interrupt line to notify remote side and shared memory to emulate ++ device MMIO registers. ++ ++properties: ++ compatible: ++ oneOf: ++ - const: fsl,generic-software-mbox ++ reg: ++ maxItems: 1 ++ ++ "#mbox-cells": ++ description: | ++ <&phandle type channel ack> ++ phandle : Label name of controller ++ type : Channel type ++ channel : Channel index ++ ack : 0: Receiver doesn't trigger an ACK interrupt to sender after receive message ++ 1: Receiver triggers an ACK interrupt to sender after receive message ++ ++ This mailbox support 3 type of unidirectional channels, each type ++ has 32 channels. Following types are supported: ++ 0 - TX channel with 32bit transmit register ++ 1 - RX channel with 32bit receive register and IRQ support ++ 2 - RX doorbell channel. ++ const: 2 ++ ++required: ++ - compatible ++ - reg ++ - "#mbox-cells" ++ - interrupts ++ - interrupt-names ++ ++additionalProperties: false ++ ++examples: ++ - | ++ reserved-memory { ++ gen-sw-mbox@b8500000 { ++ no-map; ++ reg = <0 0xb8500000 0 0x1000>; ++ }; ++ }; ++ ++ generic-software-mailbox@b8500000 { ++ compatible = "fsl,generic-software-mbox"; ++ reg = <0 0xb8500000 0 0x1000>; ++ #mbox-cells = <3>; ++ interrupts = , ++ ; ++ interrupt-names = "irq", "remote_irq"; ++ }; +diff --git a/Documentation/devicetree/bindings/net/fsl,fec.yaml b/Documentation/devicetree/bindings/net/fsl,fec.yaml +index 18f04d110d6e..f53b6ba17d5d 100644 +--- a/Documentation/devicetree/bindings/net/fsl,fec.yaml ++++ b/Documentation/devicetree/bindings/net/fsl,fec.yaml +@@ -176,6 +176,30 @@ properties: + description: + Register bits of stop mode control, the format is <&gpr req_gpr req_bit>. + ++ fsl,rx-phy-delay-100-ns: ++ $ref: /schemas/types.yaml#/definitions/uint32 ++ description: ++ If present, should specify the delay (MAC-PHY) compensation ++ in ns to be applied on packets timestamps on receive for 100 Mbps link speed ++ ++ fsl,tx-phy-delay-100-ns: ++ $ref: /schemas/types.yaml#/definitions/uint32 ++ description: ++ If present, should specify the delay (MAC-PHY) compensation ++ in ns to be applied on packets timestamps on transmit for 100 Mbps link speed ++ ++ fsl,rx-phy-delay-1000-ns: ++ $ref: /schemas/types.yaml#/definitions/uint32 ++ description: ++ If present, should specify the delay (MAC-PHY) compensation ++ in ns to be applied on packets timestamps on receive for 1 Gbps link speed ++ ++ fsl,tx-phy-delay-1000-ns: ++ $ref: /schemas/types.yaml#/definitions/uint32 ++ description: ++ If present, should specify the delay (MAC-PHY) compensation ++ in ns to be applied on packets timestamps on transmit for 1 Gbps link speed ++ + mii-exclusive: + $ref: /schemas/types.yaml#/definitions/flag + description: +diff --git a/Documentation/devicetree/bindings/tty/rpmsg_tty.yaml b/Documentation/devicetree/bindings/tty/rpmsg_tty.yaml +new file mode 100644 +index 000000000000..9e11fadd16eb +--- /dev/null ++++ b/Documentation/devicetree/bindings/tty/rpmsg_tty.yaml +@@ -0,0 +1,67 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/tty/rpmsg_tty.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: rpmsg tty driver ++ ++maintainers: ++ - Biwen Li ++ ++properties: ++ compatible: ++ const: fsl,uart-rpbus ++ ++ bus_id: ++ items: ++ - description: Used for imx srtm uart application protocol. Specify which real uart(LPUARTx/UARTx, x is instance number) will be used by a virtual tty(/dev/ttyRPMSGx), bus_id = 0xff when bus_id is not specified in dts and specified the flag(IMX_SRTM_UART_SUPPORT_MULTI_UART_MSG_FLAG). ++ maxItems: 1 ++ ++ flags: ++ items: ++ - description: used for imx srtm uart application protocol(IMX_SRTM_UART_SUPPORT_MULTI_UART_MSG_FLAG: support multi uart message protocol; IMX_SRTM_UART_SPECIFY_PORT_NUM_MASK: whether specify destination uart id; IMX_SRTM_UART_PORT_NUM_MASK: which destination uart id will be used) ++ maxItems: 1 ++ ++ status: ++ maxItems: 1 ++ ++examples: ++ - | ++ #include ++ ++ uart-rpbus-0 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ flags=; ++ status = "okay"; ++ }; /* /dev/ttyRPMSG0 on linux(running on acore) <--SRTM PROTOCOL--> srtm uart channel 0(endpoint on mcore) <---MULTI UART MSG PROTOCOL---> UART3(mcore is the owner) */ ++ ++ uart-rpbus-1 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <3>; /* use uart3 */ ++ flags=; ++ status = "okay"; ++ }; /* /dev/ttyRPMSG1 on linux(running on acore) <--SRTM PROTOCOL--> srtm uart channel 1(endpoint on mcore) <---MULTI UART MSG PROTOCOL---> UART3(mcore is the owner) */ ++ ++ uart-rpbus-2 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <2>; /* use uart2 */ ++ flags=; ++ status = "okay"; ++ }; /* /dev/ttyRPMSG2 on linux(running on acore) <--SRTM PROTOCOL--> srtm uart channel 2(endpoint on mcore) <---MULTI UART MSG PROTOCOL---> UART2(mcore is the owner) */ ++ ++ uart-rpbus-3 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <4>; /* use uart4 */ ++ flags=; ++ status = "okay"; ++ }; /* [/dev/ttyRPMSG3 on linux(running on acore) <--SRTM PROTOCOL--> srtm uart channel 3(endpoint on mcore) <---MULTI UART MSG PROTOCOL---> UART4(mcore is the owner), first board] <---MULTI UART MSG PROTOCOL--> [UARTx(mcore is the owner) <---MULTI UART MSG PROTOCOL---> srtm uart channel 8 <---SRTM PROTOCOL--> /dev/ttyRPMSG8 on linux(running on acore), second board ] */ ++ ++ uart-rpbus-4 { ++ compatible = "fsl,uart-rpbus"; ++ bus_id = <5>; /* use uart5 */ ++ status = "okay"; ++ }; /* /dev/ttyRPMSG4 on linux(running on acore) <--SRTM PROTOCOL--> srtm uart channel 4(endpoint on mcore) <-----> UART5(mcore is the owner) <----> GPS device(The device have exclusive use of the UART5, other virtual tty cannot use UART5) */ ++ ++ /* Note: when nothing is specified(include compatible, bus_id, flags, status), it's a normal rpmsg tty. /dev/ttyRPMSG7(running on acore) <--RPMSG--> rpmsg endpoint(running on mcore) */ +diff --git a/Documentation/printk-ringbuffer.txt b/Documentation/printk-ringbuffer.txt +new file mode 100644 +index 000000000000..6bde5dbd8545 +--- /dev/null ++++ b/Documentation/printk-ringbuffer.txt +@@ -0,0 +1,377 @@ ++struct printk_ringbuffer ++------------------------ ++John Ogness ++ ++Overview ++~~~~~~~~ ++As the name suggests, this ring buffer was implemented specifically to serve ++the needs of the printk() infrastructure. The ring buffer itself is not ++specific to printk and could be used for other purposes. _However_, the ++requirements and semantics of printk are rather unique. If you intend to use ++this ring buffer for anything other than printk, you need to be very clear on ++its features, behavior, and pitfalls. ++ ++Features ++^^^^^^^^ ++The printk ring buffer has the following features: ++ ++- single global buffer ++- resides in initialized data section (available at early boot) ++- lockless readers ++- supports multiple writers ++- supports multiple non-consuming readers ++- safe from any context (including NMI) ++- groups bytes into variable length blocks (referenced by entries) ++- entries tagged with sequence numbers ++ ++Behavior ++^^^^^^^^ ++Since the printk ring buffer readers are lockless, there exists no ++synchronization between readers and writers. Basically writers are the tasks ++in control and may overwrite any and all committed data at any time and from ++any context. For this reason readers can miss entries if they are overwritten ++before the reader was able to access the data. The reader API implementation ++is such that reader access to entries is atomic, so there is no risk of ++readers having to deal with partial or corrupt data. Also, entries are ++tagged with sequence numbers so readers can recognize if entries were missed. ++ ++Writing to the ring buffer consists of 2 steps. First a writer must reserve ++an entry of desired size. After this step the writer has exclusive access ++to the memory region. Once the data has been written to memory, it needs to ++be committed to the ring buffer. After this step the entry has been inserted ++into the ring buffer and assigned an appropriate sequence number. ++ ++Once committed, a writer must no longer access the data directly. This is ++because the data may have been overwritten and no longer exists. If a ++writer must access the data, it should either keep a private copy before ++committing the entry or use the reader API to gain access to the data. ++ ++Because of how the data backend is implemented, entries that have been ++reserved but not yet committed act as barriers, preventing future writers ++from filling the ring buffer beyond the location of the reserved but not ++yet committed entry region. For this reason it is *important* that writers ++perform both reserve and commit as quickly as possible. Also, be aware that ++preemption and local interrupts are disabled and writing to the ring buffer ++is processor-reentrant locked during the reserve/commit window. Writers in ++NMI contexts can still preempt any other writers, but as long as these ++writers do not write a large amount of data with respect to the ring buffer ++size, this should not become an issue. ++ ++API ++~~~ ++ ++Declaration ++^^^^^^^^^^^ ++The printk ring buffer can be instantiated as a static structure: ++ ++ /* declare a static struct printk_ringbuffer */ ++ #define DECLARE_STATIC_PRINTKRB(name, szbits, cpulockptr) ++ ++The value of szbits specifies the size of the ring buffer in bits. The ++cpulockptr field is a pointer to a prb_cpulock struct that is used to ++perform processor-reentrant spin locking for the writers. It is specified ++externally because it may be used for multiple ring buffers (or other ++code) to synchronize writers without risk of deadlock. ++ ++Here is an example of a declaration of a printk ring buffer specifying a ++32KB (2^15) ring buffer: ++ ++.... ++DECLARE_STATIC_PRINTKRB_CPULOCK(rb_cpulock); ++DECLARE_STATIC_PRINTKRB(rb, 15, &rb_cpulock); ++.... ++ ++If writers will be using multiple ring buffers and the ordering of that usage ++is not clear, the same prb_cpulock should be used for both ring buffers. ++ ++Writer API ++^^^^^^^^^^ ++The writer API consists of 2 functions. The first is to reserve an entry in ++the ring buffer, the second is to commit that data to the ring buffer. The ++reserved entry information is stored within a provided `struct prb_handle`. ++ ++ /* reserve an entry */ ++ char *prb_reserve(struct prb_handle *h, struct printk_ringbuffer *rb, ++ unsigned int size); ++ ++ /* commit a reserved entry to the ring buffer */ ++ void prb_commit(struct prb_handle *h); ++ ++Here is an example of a function to write data to a ring buffer: ++ ++.... ++int write_data(struct printk_ringbuffer *rb, char *data, int size) ++{ ++ struct prb_handle h; ++ char *buf; ++ ++ buf = prb_reserve(&h, rb, size); ++ if (!buf) ++ return -1; ++ memcpy(buf, data, size); ++ prb_commit(&h); ++ ++ return 0; ++} ++.... ++ ++Pitfalls ++++++++++ ++Be aware that prb_reserve() can fail. A retry might be successful, but it ++depends entirely on whether or not the next part of the ring buffer to ++overwrite belongs to reserved but not yet committed entries of other writers. ++Writers can use the prb_inc_lost() function to allow readers to notice that a ++message was lost. ++ ++Reader API ++^^^^^^^^^^ ++The reader API utilizes a `struct prb_iterator` to track the reader's ++position in the ring buffer. ++ ++ /* declare a pre-initialized static iterator for a ring buffer */ ++ #define DECLARE_STATIC_PRINTKRB_ITER(name, rbaddr) ++ ++ /* initialize iterator for a ring buffer (if static macro NOT used) */ ++ void prb_iter_init(struct prb_iterator *iter, ++ struct printk_ringbuffer *rb, u64 *seq); ++ ++ /* make a deep copy of an iterator */ ++ void prb_iter_copy(struct prb_iterator *dest, ++ struct prb_iterator *src); ++ ++ /* non-blocking, advance to next entry (and read the data) */ ++ int prb_iter_next(struct prb_iterator *iter, char *buf, ++ int size, u64 *seq); ++ ++ /* blocking, advance to next entry (and read the data) */ ++ int prb_iter_wait_next(struct prb_iterator *iter, char *buf, ++ int size, u64 *seq); ++ ++ /* position iterator at the entry seq */ ++ int prb_iter_seek(struct prb_iterator *iter, u64 seq); ++ ++ /* read data at current position */ ++ int prb_iter_data(struct prb_iterator *iter, char *buf, ++ int size, u64 *seq); ++ ++Typically prb_iter_data() is not needed because the data can be retrieved ++directly with prb_iter_next(). ++ ++Here is an example of a non-blocking function that will read all the data in ++a ring buffer: ++ ++.... ++void read_all_data(struct printk_ringbuffer *rb, char *buf, int size) ++{ ++ struct prb_iterator iter; ++ u64 prev_seq = 0; ++ u64 seq; ++ int ret; ++ ++ prb_iter_init(&iter, rb, NULL); ++ ++ for (;;) { ++ ret = prb_iter_next(&iter, buf, size, &seq); ++ if (ret > 0) { ++ if (seq != ++prev_seq) { ++ /* "seq - prev_seq" entries missed */ ++ prev_seq = seq; ++ } ++ /* process buf here */ ++ } else if (ret == 0) { ++ /* hit the end, done */ ++ break; ++ } else if (ret < 0) { ++ /* ++ * iterator is invalid, a writer overtook us, reset the ++ * iterator and keep going, entries were missed ++ */ ++ prb_iter_init(&iter, rb, NULL); ++ } ++ } ++} ++.... ++ ++Pitfalls ++++++++++ ++The reader's iterator can become invalid at any time because the reader was ++overtaken by a writer. Typically the reader should reset the iterator back ++to the current oldest entry (which will be newer than the entry the reader ++was at) and continue, noting the number of entries that were missed. ++ ++Utility API ++^^^^^^^^^^^ ++Several functions are available as convenience for external code. ++ ++ /* query the size of the data buffer */ ++ int prb_buffer_size(struct printk_ringbuffer *rb); ++ ++ /* skip a seq number to signify a lost record */ ++ void prb_inc_lost(struct printk_ringbuffer *rb); ++ ++ /* processor-reentrant spin lock */ ++ void prb_lock(struct prb_cpulock *cpu_lock, unsigned int *cpu_store); ++ ++ /* processor-reentrant spin unlock */ ++ void prb_lock(struct prb_cpulock *cpu_lock, unsigned int *cpu_store); ++ ++Pitfalls ++++++++++ ++Although the value returned by prb_buffer_size() does represent an absolute ++upper bound, the amount of data that can be stored within the ring buffer ++is actually less because of the additional storage space of a header for each ++entry. ++ ++The prb_lock() and prb_unlock() functions can be used to synchronize between ++ring buffer writers and other external activities. The function of a ++processor-reentrant spin lock is to disable preemption and local interrupts ++and synchronize against other processors. It does *not* protect against ++multiple contexts of a single processor, i.e NMI. ++ ++Implementation ++~~~~~~~~~~~~~~ ++This section describes several of the implementation concepts and details to ++help developers better understand the code. ++ ++Entries ++^^^^^^^ ++All ring buffer data is stored within a single static byte array. The reason ++for this is to ensure that any pointers to the data (past and present) will ++always point to valid memory. This is important because the lockless readers ++may be preempted for long periods of time and when they resume may be working ++with expired pointers. ++ ++Entries are identified by start index and size. (The start index plus size ++is the start index of the next entry.) The start index is not simply an ++offset into the byte array, but rather a logical position (lpos) that maps ++directly to byte array offsets. ++ ++For example, for a byte array of 1000, an entry may have have a start index ++of 100. Another entry may have a start index of 1100. And yet another 2100. ++All of these entry are pointing to the same memory region, but only the most ++recent entry is valid. The other entries are pointing to valid memory, but ++represent entries that have been overwritten. ++ ++Note that due to overflowing, the most recent entry is not necessarily the one ++with the highest lpos value. Indeed, the printk ring buffer initializes its ++data such that an overflow happens relatively quickly in order to validate the ++handling of this situation. The implementation assumes that an lpos (unsigned ++long) will never completely wrap while a reader is preempted. If this were to ++become an issue, the seq number (which never wraps) could be used to increase ++the robustness of handling this situation. ++ ++Buffer Wrapping ++^^^^^^^^^^^^^^^ ++If an entry starts near the end of the byte array but would extend beyond it, ++a special terminating entry (size = -1) is inserted into the byte array and ++the real entry is placed at the beginning of the byte array. This can waste ++space at the end of the byte array, but simplifies the implementation by ++allowing writers to always work with contiguous buffers. ++ ++Note that the size field is the first 4 bytes of the entry header. Also note ++that calc_next() always ensures that there are at least 4 bytes left at the ++end of the byte array to allow room for a terminating entry. ++ ++Ring Buffer Pointers ++^^^^^^^^^^^^^^^^^^^^ ++Three pointers (lpos values) are used to manage the ring buffer: ++ ++ - _tail_: points to the oldest entry ++ - _head_: points to where the next new committed entry will be ++ - _reserve_: points to where the next new reserved entry will be ++ ++These pointers always maintain a logical ordering: ++ ++ tail <= head <= reserve ++ ++The reserve pointer moves forward when a writer reserves a new entry. The ++head pointer moves forward when a writer commits a new entry. ++ ++The reserve pointer cannot overwrite the tail pointer in a wrap situation. In ++such a situation, the tail pointer must be "pushed forward", thus ++invalidating that oldest entry. Readers identify if they are accessing a ++valid entry by ensuring their entry pointer is `>= tail && < head`. ++ ++If the tail pointer is equal to the head pointer, it cannot be pushed and any ++reserve operation will fail. The only resolution is for writers to commit ++their reserved entries. ++ ++Processor-Reentrant Locking ++^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++The purpose of the processor-reentrant locking is to limit the interruption ++scenarios of writers to 2 contexts. This allows for a simplified ++implementation where: ++ ++- The reserve/commit window only exists on 1 processor at a time. A reserve ++ can never fail due to uncommitted entries of other processors. ++ ++- When committing entries, it is trivial to handle the situation when ++ subsequent entries have already been committed, i.e. managing the head ++ pointer. ++ ++Performance ++~~~~~~~~~~~ ++Some basic tests were performed on a quad Intel(R) Xeon(R) CPU E5-2697 v4 at ++2.30GHz (36 cores / 72 threads). All tests involved writing a total of ++32,000,000 records at an average of 33 bytes each. Each writer was pinned to ++its own CPU and would write as fast as it could until a total of 32,000,000 ++records were written. All tests involved 2 readers that were both pinned ++together to another CPU. Each reader would read as fast as it could and track ++how many of the 32,000,000 records it could read. All tests used a ring buffer ++of 16KB in size, which holds around 350 records (header + data for each ++entry). ++ ++The only difference between the tests is the number of writers (and thus also ++the number of records per writer). As more writers are added, the time to ++write a record increases. This is because data pointers, modified via cmpxchg, ++and global data access in general become more contended. ++ ++1 writer ++^^^^^^^^ ++ runtime: 0m 18s ++ reader1: 16219900/32000000 (50%) records ++ reader2: 16141582/32000000 (50%) records ++ ++2 writers ++^^^^^^^^^ ++ runtime: 0m 32s ++ reader1: 16327957/32000000 (51%) records ++ reader2: 16313988/32000000 (50%) records ++ ++4 writers ++^^^^^^^^^ ++ runtime: 0m 42s ++ reader1: 16421642/32000000 (51%) records ++ reader2: 16417224/32000000 (51%) records ++ ++8 writers ++^^^^^^^^^ ++ runtime: 0m 43s ++ reader1: 16418300/32000000 (51%) records ++ reader2: 16432222/32000000 (51%) records ++ ++16 writers ++^^^^^^^^^^ ++ runtime: 0m 54s ++ reader1: 16539189/32000000 (51%) records ++ reader2: 16542711/32000000 (51%) records ++ ++32 writers ++^^^^^^^^^^ ++ runtime: 1m 13s ++ reader1: 16731808/32000000 (52%) records ++ reader2: 16735119/32000000 (52%) records ++ ++Comments ++^^^^^^^^ ++It is particularly interesting to compare/contrast the 1-writer and 32-writer ++tests. Despite the writing of the 32,000,000 records taking over 4 times ++longer, the readers (which perform no cmpxchg) were still unable to keep up. ++This shows that the memory contention between the increasing number of CPUs ++also has a dramatic effect on readers. ++ ++It should also be noted that in all cases each reader was able to read >=50% ++of the records. This means that a single reader would have been able to keep ++up with the writer(s) in all cases, becoming slightly easier as more writers ++are added. This was the purpose of pinning 2 readers to 1 CPU: to observe how ++maximum reader performance changes. +-- +2.34.1 + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0005-include-add-NXP-RT-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0005-include-add-NXP-RT-support.patch new file mode 100644 index 000000000..00a52e95d --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0005-include-add-NXP-RT-support.patch @@ -0,0 +1,1392 @@ +From b1f8c7c885dc4c9619cfc1232bb380c1dd229a90 Mon Sep 17 00:00:00 2001 +From: Mike Engel +Date: Fri, 23 Feb 2024 12:30:39 +0100 +Subject: [PATCH 05/10] include: add NXP RT support + +Signed-off-by: Mike Engel +--- + include/dt-bindings/clock/imx8-clock.h | 10 +++ + include/dt-bindings/rpmsg/imx_srtm.h | 20 +++++ + include/linux/clk/imx-pll.h | 26 ++++++ + include/linux/console.h | 33 ++++++- + include/linux/dsa/netc.h | 56 ++++++++++++ + include/linux/dsa/sja1105.h | 2 + + include/linux/entry-common.h | 8 +- + include/linux/ethtool.h | 7 ++ + include/linux/fec.h | 119 +++++++++++++++++++++++++ + include/linux/fsl_qman.h | 8 ++ + include/linux/interrupt.h | 29 ++++++ + include/linux/ipi_baremetal.h | 27 ++++++ + include/linux/lockdep.h | 1 - + include/linux/net.h | 2 + + include/linux/netdevice.h | 48 ++++++++++ + include/linux/preempt.h | 54 ++++++++++- + include/linux/printk.h | 15 ++++ + include/linux/rpmsg/imx_srtm.h | 65 ++++++++++++++ + include/linux/sched.h | 37 ++++++++ + include/linux/serial_8250.h | 5 ++ + include/linux/thread_info.h | 12 ++- + include/linux/trace_events.h | 10 ++- + include/linux/u64_stats_sync.h | 12 --- + include/net/dsa.h | 18 ++++ + include/net/pkt_cls.h | 10 --- + include/net/pkt_sched.h | 11 +++ + include/net/switchdev.h | 1 + + include/soc/mscc/ocelot.h | 5 ++ + include/soc/mscc/ocelot_vcap.h | 1 + + include/uapi/linux/ethtool.h | 30 +++++++ + include/uapi/linux/ethtool_netlink.h | 21 +++++ + include/uapi/linux/virtio_ids.h | 1 + + include/uapi/linux/virtio_mmio.h | 7 +- + include/uapi/linux/virtio_trans.h | 60 +++++++++++++ + 34 files changed, 740 insertions(+), 31 deletions(-) + create mode 100644 include/dt-bindings/rpmsg/imx_srtm.h + create mode 100644 include/linux/clk/imx-pll.h + create mode 100644 include/linux/dsa/netc.h + create mode 100644 include/linux/ipi_baremetal.h + create mode 100644 include/linux/rpmsg/imx_srtm.h + create mode 100644 include/uapi/linux/virtio_trans.h + +diff --git a/include/dt-bindings/clock/imx8-clock.h b/include/dt-bindings/clock/imx8-clock.h +index 2a1122928062..c5d884409872 100644 +--- a/include/dt-bindings/clock/imx8-clock.h ++++ b/include/dt-bindings/clock/imx8-clock.h +@@ -61,4 +61,14 @@ + + #define IMX_ADMA_ACM_CLK_END 50 + ++/* ACM GPT Event Mux Control Register Offset */ ++#define IMX_ADMA_ACM_GPT0_CAPIN1_SEL 0x80004 ++#define IMX_ADMA_ACM_GPT0_CAPIN2_SEL 0x80008 ++#define IMX_ADMA_ACM_GPT1_CAPIN1_SEL 0x90004 ++#define IMX_ADMA_ACM_GPT1_CAPIN2_SEL 0x90008 ++ ++/* ACM GPT Event Mux Select Control */ ++#define IMX_ADMA_ACM_GPT_EVENT_INPUT_ETH0 3 ++#define IMX_ADMA_ACM_GPT_EVENT_INPUT_ETH1 4 ++ + #endif /* __DT_BINDINGS_CLOCK_IMX_H */ +diff --git a/include/dt-bindings/rpmsg/imx_srtm.h b/include/dt-bindings/rpmsg/imx_srtm.h +new file mode 100644 +index 000000000000..644ab9ceb407 +--- /dev/null ++++ b/include/dt-bindings/rpmsg/imx_srtm.h +@@ -0,0 +1,20 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2023 NXP ++ * ++ */ ++ ++#ifndef _DT_BINDINGS_RPMSG_IMX_SRTM_H ++#define _DT_BINDINGS_RPMSG_IMX_SRTM_H ++ ++/* Bit 0 as RPMSG Over UART flag */ ++#define IMX_SRTM_RPMSG_OVER_UART_FLAG (1 << 0) ++#define IMX_SRTM_UART_SUPPORT_MULTI_UART_MSG_FLAG (1 << 1) ++/* [15:11]: port number, such as /dev/ttySRTM3, 3 is the port number */ ++#define IMX_SRTM_UART_PORT_NUM_SHIFT (11U) ++#define IMX_SRTM_UART_PORT_NUM_MASK (0x1F << 11U) ++/* [10]: 0b1, specify port number; 0b0, not specify port number */ ++#define IMX_SRTM_UART_SPECIFY_PORT_NUM_SHIFT (10U) ++#define IMX_SRTM_UART_SPECIFY_PORT_NUM_MASK (1 << IMX_SRTM_UART_SPECIFY_PORT_NUM_SHIFT) ++ ++#endif +diff --git a/include/linux/clk/imx-pll.h b/include/linux/clk/imx-pll.h +new file mode 100644 +index 000000000000..9759ca15a6cc +--- /dev/null ++++ b/include/linux/clk/imx-pll.h +@@ -0,0 +1,26 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2018 NXP ++ * ++ */ ++ ++#ifndef __LINUX_CLK_IMX_PLL_H__ ++#define __LINUX_CLK_IMX_PLL_H__ ++ ++enum clk_imx_pll_error { ++ IMX_CLK_PLL_SUCCESS = 0, ++ IMX_CLK_PLL_INVALID_PARAM, ++ IMX_CLK_PLL_PREC_ERR, ++ IMX_CLK_PLL_LOCK_ERR, ++}; ++ ++struct clk_imx_pll; ++ ++struct clk_imx_pll *clk_imx_pll_get_by_name(const char *name); ++int clk_imx_pll_adjust(struct clk_imx_pll *pll, int *ppb); ++unsigned long clk_imx_pll_get_rate(struct clk_imx_pll *pll, ++ unsigned long parent_rate); ++int clk_imx_pll_set_rate(struct clk_imx_pll *pll, unsigned long rate, ++ unsigned long parent_rate); ++ ++#endif /*__LINUX_CLK_IMX_PLL_H__*/ +diff --git a/include/linux/console.h b/include/linux/console.h +index 8c1686e2c233..8a813cbaf928 100644 +--- a/include/linux/console.h ++++ b/include/linux/console.h +@@ -16,6 +16,7 @@ + + #include + #include ++#include + + struct vc_data; + struct console_font_op; +@@ -137,9 +138,19 @@ static inline int con_debug_leave(void) + #define CON_BRL (32) /* Used for a braille device */ + #define CON_EXTENDED (64) /* Use the extended output format a la /dev/kmsg */ + ++#ifdef CONFIG_HAVE_ATOMIC_CONSOLE ++struct console_atomic_data { ++ u64 seq; ++ char *text; ++ char *ext_text; ++ char *dropped_text; ++}; ++#endif ++ + struct console { + char name[16]; + void (*write)(struct console *, const char *, unsigned); ++ void (*write_atomic)(struct console *, const char *, unsigned); + int (*read)(struct console *, char *, unsigned); + struct tty_driver *(*device)(struct console *, int *); + void (*unblank)(void); +@@ -152,7 +163,26 @@ struct console { + uint ispeed; + uint ospeed; + u64 seq; +- unsigned long dropped; ++ atomic_long_t dropped; ++#ifdef CONFIG_HAVE_ATOMIC_CONSOLE ++ struct console_atomic_data *atomic_data; ++#endif ++ struct task_struct *thread; ++ bool blocked; ++ ++ /* ++ * The per-console lock is used by printing kthreads to synchronize ++ * this console with callers of console_lock(). This is necessary in ++ * order to allow printing kthreads to run in parallel to each other, ++ * while each safely accessing the @blocked field and synchronizing ++ * against direct printing via console_lock/console_unlock. ++ * ++ * Note: For synchronizing against direct printing via ++ * console_trylock/console_unlock, see the static global ++ * variable @console_kthreads_active. ++ */ ++ struct mutex lock; ++ + void *data; + struct console *next; + }; +@@ -167,6 +197,7 @@ extern int console_set_on_cmdline; + extern struct console *early_console; + + enum con_flush_mode { ++ CONSOLE_ATOMIC_FLUSH_PENDING, + CONSOLE_FLUSH_PENDING, + CONSOLE_REPLAY_ALL, + }; +diff --git a/include/linux/dsa/netc.h b/include/linux/dsa/netc.h +new file mode 100644 +index 000000000000..a5aef045f1b5 +--- /dev/null ++++ b/include/linux/dsa/netc.h +@@ -0,0 +1,56 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#ifndef _NET_DSA_NETC_H ++#define _NET_DSA_NETC_H ++ ++#include ++#include ++#include ++#include ++ ++#define ETH_P_NETC 0x88A8 ++#define ETH_P_NETC_META 0x0008 ++ ++#define NETC_DEFAULT_VLAN 1 ++ ++#define IFH_TAG_TYPE_C 0 ++#define IFH_TAG_TYPE_S 1 ++ ++/* IEEE 802.3 Annex 57A: Slow Protocols PDUs (01:80:C2:xx:xx:xx) */ ++#define NETC_LINKLOCAL_FILTER_A 0x0180C2000000ull ++#define NETC_LINKLOCAL_FILTER_A_MASK 0xFFFFFF000000ull ++ ++/* IEEE 1588 Annex F: Transport of PTP over Ethernet (01:1B:19:xx:xx:xx) */ ++#define NETC_LINKLOCAL_FILTER_B 0x011B19000000ull ++#define NETC_LINKLOCAL_FILTER_B_MASK 0xFFFFFF000000ull ++ ++struct netc_deferred_xmit_work { ++ struct dsa_port *dp; ++ struct sk_buff *skb; ++ struct kthread_work work; ++}; ++ ++struct netc_skb_cb { ++ struct sk_buff *clone; ++ u64 tstamp; ++ u8 ts_id; ++}; ++ ++#define NETC_SKB_CB(skb) \ ++ ((struct netc_skb_cb *)((skb)->cb)) ++ ++struct netc_tagger_data { ++ void (*xmit_work_fn)(struct kthread_work *work); ++}; ++ ++static inline struct netc_tagger_data * ++netc_tagger_data(struct dsa_switch *ds) ++{ ++ WARN_ON_ONCE(ds->dst->tag_ops->proto != DSA_TAG_PROTO_NETC); ++ return ds->tagger_data; ++} ++ ++#endif /* _NET_DSA_NETC_H */ +diff --git a/include/linux/dsa/sja1105.h b/include/linux/dsa/sja1105.h +index c177322f793d..31779f9bc92b 100644 +--- a/include/linux/dsa/sja1105.h ++++ b/include/linux/dsa/sja1105.h +@@ -35,6 +35,8 @@ + #define SJA1105_META_SMAC 0x222222222222ull + #define SJA1105_META_DMAC 0x0180C200000Eull + ++#define SJA1105_MAX_NUM_PCP 8 ++ + enum sja1110_meta_tstamp { + SJA1110_META_TSTAMP_TX = 0, + SJA1110_META_TSTAMP_RX = 1, +diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h +index d95ab85f96ba..3dc3704a3cdb 100644 +--- a/include/linux/entry-common.h ++++ b/include/linux/entry-common.h +@@ -57,9 +57,15 @@ + # define ARCH_EXIT_TO_USER_MODE_WORK (0) + #endif + ++#ifdef CONFIG_PREEMPT_LAZY ++# define _TIF_NEED_RESCHED_MASK (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY) ++#else ++# define _TIF_NEED_RESCHED_MASK (_TIF_NEED_RESCHED) ++#endif ++ + #define EXIT_TO_USER_MODE_WORK \ + (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE | \ +- _TIF_NEED_RESCHED | _TIF_PATCH_PENDING | _TIF_NOTIFY_SIGNAL | \ ++ _TIF_NEED_RESCHED_MASK | _TIF_PATCH_PENDING | _TIF_NOTIFY_SIGNAL | \ + ARCH_EXIT_TO_USER_MODE_WORK) + + /** +diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h +index 15893c3b2c55..c9fffafc4e6b 100644 +--- a/include/linux/ethtool.h ++++ b/include/linux/ethtool.h +@@ -609,6 +609,8 @@ struct ethtool_module_power_mode_params { + * @get_ethtool_phy_stats: Return extended statistics about the PHY device. + * This is only useful if the device maintains PHY statistics and + * cannot use the standard PHY library helpers. ++ * @get_preempt: Get the network device Frame Preemption parameters. ++ * @set_preempt: Set the network device Frame Preemption parameters. + * @get_phy_tunable: Read the value of a PHY tunable. + * @set_phy_tunable: Set the value of a PHY tunable. + * @get_module_eeprom_by_page: Get a region of plug-in module EEPROM data from +@@ -736,6 +738,11 @@ struct ethtool_ops { + struct ethtool_fecparam *); + int (*set_fecparam)(struct net_device *, + struct ethtool_fecparam *); ++ int (*get_preempt)(struct net_device *, ++ struct ethtool_fp *); ++ int (*set_preempt)(struct net_device *, ++ struct ethtool_fp *); ++ int (*reset_preempt)(struct net_device *, bool enable); + void (*get_ethtool_phy_stats)(struct net_device *, + struct ethtool_stats *, u64 *); + int (*get_phy_tunable)(struct net_device *, +diff --git a/include/linux/fec.h b/include/linux/fec.h +index 9aaf53f07269..e72014b995c4 100644 +--- a/include/linux/fec.h ++++ b/include/linux/fec.h +@@ -19,4 +19,123 @@ struct fec_platform_data { + void (*sleep_mode_enable)(int enabled); + }; + ++/* The number of Tx and Rx buffers. These are allocated from the page ++ * pool. The code may assume these are power of two, so it it best ++ * to keep them that size. ++ * We don't need to allocate pages for the transmitter. We just use ++ * the skbuffer directly. ++ */ ++ ++#define FEC_ENET_XDP_HEADROOM (XDP_PACKET_HEADROOM) ++#define FEC_ENET_RX_PAGES 256 ++#define FEC_ENET_RX_FRSIZE (PAGE_SIZE - FEC_ENET_XDP_HEADROOM \ ++ - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) ++#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) ++#ifdef CONFIG_AVB_SUPPORT ++#define FEC_RX_RING_SIZE 256 ++#define FEC_ENET_AVB_RX_FRSIZE 1522 ++#else ++#define FEC_RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) ++#endif ++#define FEC_ENET_TX_FRSIZE 2048 ++#define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE) ++ ++#ifdef CONFIG_AVB_SUPPORT ++#define FEC_TX_RING_SIZE 256 /* Must be power of two */ ++#else ++#define FEC_TX_RING_SIZE 512 /* Must be power of two */ ++#endif ++ ++#ifdef CONFIG_AVB_SUPPORT ++struct avb_desc { ++ u16 offset; ++ u16 len; ++ u32 ts; ++ u32 flags; ++ u32 private; /* Will be used for saving userspace private value on TX and esc hw descriptor value on RX */ ++}; ++ ++struct avb_tx_desc { ++ struct avb_desc common; ++ ++ unsigned long dma_addr; ++ void *data; ++ u32 esc; ++ unsigned short queue_id; ++ unsigned short sc; ++ unsigned long bufaddr; ++ unsigned short datlen; ++}; ++ ++struct avb_rx_desc { ++ struct avb_desc common; ++ ++ /* end of common rx fields */ ++ unsigned long dma_addr; ++ unsigned short sc; /* Control and status info */ ++ unsigned short queue_id; ++}; ++ ++#define AVB_WAKE_THREAD (1 << 0) ++#define AVB_WAKE_NAPI (1 << 1) ++ ++#define AVB_TX_FLAG_SKB (1 << 0) ++#define AVB_TX_FLAG_HW_TS (1 << 1) ++#define AVB_TX_FLAG_HW_CSUM (1 << 2) ++#define AVB_TX_FLAG_TS (1 << 3) ++ ++struct avb_ops { ++ void (*open)(void *, void *, int); ++ void (*close)(void *); ++ ++ void * (*alloc)(void *); ++ void (*free)(void *, struct avb_desc *); ++ ++ int (*rx)(void *, struct avb_rx_desc *); ++ void * (*dequeue)(void *); ++ ++ int (*tx)(void *, struct avb_tx_desc *); ++ int (*tx_full)(void *); ++ ++ int (*tx_cleanup)(void *, struct avb_tx_desc *); ++ int (*tx_cleanup_ready)(void *); ++ void * (*tx_cleanup_dequeue)(void *); ++ ++ int (*tx_ts)(void *, struct avb_desc *); ++ ++ struct module *owner; ++}; ++ ++#define TX_QUEUE_FLAGS_STRICT_PRIORITY BIT(0) ++#define TX_QUEUE_FLAGS_CREDIT_SHAPER BIT(1) ++ ++#define TX_QUEUE_PROP_MAX 8 ++ ++struct tx_queue_property { ++ unsigned int priority; ++ unsigned int flags; ++}; ++ ++struct tx_queue_properties { ++ int num_queues; ++ struct tx_queue_property queue[TX_QUEUE_PROP_MAX]; ++}; ++ ++int fec_enet_get_tx_queue_properties(int ifindex, struct tx_queue_properties *prop); ++int fec_enet_set_idle_slope(void *data, unsigned int queue_id, u32 idle_slope); ++int fec_enet_avb_register(const char *ifname, const struct avb_ops *avb, void *data); ++struct device *fec_enet_avb_get_device(const char *ifname); ++int fec_enet_avb_unregister(int ifindex, const struct avb_ops *avb); ++int fec_enet_rx_poll_avb(void *data); ++int fec_enet_start_xmit_avb(void *data, struct avb_tx_desc *desc); ++void fec_enet_finish_xmit_avb(void *data, unsigned int queue_id); ++int fec_enet_tx_avb(void *data); ++ ++int fec_ptp_read_cnt(void *data, u32 *cnt); ++int fec_ptp_tc_start(void *data, u8 id, u32 ts_0, u32 ts_1, u32 tcsr_val); ++void fec_ptp_tc_stop(void *data, u8 id); ++int fec_ptp_tc_reload(void *data, u8 id, u32 ts); ++ ++#endif ++ + #endif +diff --git a/include/linux/fsl_qman.h b/include/linux/fsl_qman.h +index 3dc9bfa8658d..abc4c5117da3 100644 +--- a/include/linux/fsl_qman.h ++++ b/include/linux/fsl_qman.h +@@ -1,4 +1,5 @@ + /* Copyright 2008-2012 Freescale Semiconductor, Inc. ++ * Copyright 2019-2023 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: +@@ -2120,6 +2121,9 @@ const cpumask_t *qman_affine_cpus(void); + * member of the mask returned from qman_affine_cpus(). + */ + u16 qman_affine_channel(int cpu); ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++u16 qman_affine_channel_ethercat(int cpu); ++#endif + + /** + * qman_get_affine_portal - return the portal pointer affine to cpu +@@ -2127,6 +2131,10 @@ u16 qman_affine_channel(int cpu); + * + */ + void *qman_get_affine_portal(int cpu); ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++void *qman_get_affine_portal_ethercat(int cpu); ++u32 qman_get_affine_last_cpu(void); ++#endif + + /** + * qman_poll_dqrr - process DQRR (fast-path) entries +diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h +index a92bce40b04b..bf82980f569d 100644 +--- a/include/linux/interrupt.h ++++ b/include/linux/interrupt.h +@@ -605,6 +605,35 @@ extern void __raise_softirq_irqoff(unsigned int nr); + extern void raise_softirq_irqoff(unsigned int nr); + extern void raise_softirq(unsigned int nr); + ++#ifdef CONFIG_PREEMPT_RT ++DECLARE_PER_CPU(struct task_struct *, timersd); ++DECLARE_PER_CPU(unsigned long, pending_timer_softirq); ++ ++extern void raise_timer_softirq(void); ++extern void raise_hrtimer_softirq(void); ++ ++static inline unsigned int local_pending_timers(void) ++{ ++ return __this_cpu_read(pending_timer_softirq); ++} ++ ++#else ++static inline void raise_timer_softirq(void) ++{ ++ raise_softirq(TIMER_SOFTIRQ); ++} ++ ++static inline void raise_hrtimer_softirq(void) ++{ ++ raise_softirq_irqoff(HRTIMER_SOFTIRQ); ++} ++ ++static inline unsigned int local_pending_timers(void) ++{ ++ return local_softirq_pending(); ++} ++#endif ++ + DECLARE_PER_CPU(struct task_struct *, ksoftirqd); + + static inline struct task_struct *this_cpu_ksoftirqd(void) +diff --git a/include/linux/ipi_baremetal.h b/include/linux/ipi_baremetal.h +new file mode 100644 +index 000000000000..5f124be40dcc +--- /dev/null ++++ b/include/linux/ipi_baremetal.h +@@ -0,0 +1,27 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * include/linux/ipi_baremetal.h ++ * ++ * Copyright 2018-2023 NXP ++ * ++ */ ++ ++#ifndef __LINUX_IPI_BAREMETAL_H ++#define __LINUX_IPI_BAREMETAL_H ++ ++#include ++ ++#if defined(CONFIG_LS1021A_BAREMETAL) || \ ++ defined(CONFIG_LS1028A_BAREMETAL) || \ ++ defined(CONFIG_IMX93_BAREMETAL) ++#define CONFIG_MAX_CPUS 2 ++#elif defined(CONFIG_IMX8M_BAREMETAL) ++#define CONFIG_MAX_CPUS 4 ++#elif defined(CONFIG_LX2160A_BAREMETAL) ++#define CONFIG_MAX_CPUS 16 ++#else ++#define CONFIG_MAX_CPUS 4 ++#endif ++ ++int ipi_baremetal_handle(u32 irqnr, u32 irqsrc); ++#endif /* !__LINUX_IPI_BAREMETAL_H */ +diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h +index 1f1099dac3f0..1023f349af71 100644 +--- a/include/linux/lockdep.h ++++ b/include/linux/lockdep.h +@@ -435,7 +435,6 @@ enum xhlock_context_t { + XHLOCK_CTX_NR, + }; + +-#define lockdep_init_map_crosslock(m, n, k, s) do {} while (0) + /* + * To initialize a lockdep_map statically use this macro. + * Note that _name must not be NULL. +diff --git a/include/linux/net.h b/include/linux/net.h +index 18d942bbdf6e..38f9d5318f58 100644 +--- a/include/linux/net.h ++++ b/include/linux/net.h +@@ -124,6 +124,8 @@ struct socket { + const struct proto_ops *ops; + + struct socket_wq wq; ++ ++ struct net_device *ndev; + }; + + /* +diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h +index 5a04fbf72476..980f2d75a6d2 100644 +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -338,6 +338,8 @@ struct gro_list { + */ + #define GRO_HASH_BUCKETS 8 + ++ ++#define NAPINAMSIZ 8 + /* + * Structure for NAPI scheduling similar to tasklet but with weighting + */ +@@ -368,6 +370,7 @@ struct napi_struct { + struct hlist_node napi_hash_node; + unsigned int napi_id; + struct task_struct *thread; ++ char name[NAPINAMSIZ]; + }; + + enum { +@@ -1404,6 +1407,9 @@ struct net_device_ops { + int (*ndo_stop)(struct net_device *dev); + netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb, + struct net_device *dev); ++ struct sk_buff* (*ndo_start_recv)(struct net_device *dev, int *err); ++ int (*ndo_fast_recv)(struct net_device *ndev, void __user *buff, size_t len, struct sockaddr __user *addr, int __user *addr_len); ++ int (*ndo_fast_xmit)(struct net_device *ndev, void __user *buff, size_t len); + netdev_features_t (*ndo_features_check)(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features); +@@ -2027,6 +2033,8 @@ struct net_device { + unsigned long mem_start; + unsigned long base_addr; + ++ unsigned int fast_raw_device; ++ + /* + * Some hardware also needs these fields (state,dev_list, + * napi_list,unreg_list,close_list) but they are not +@@ -2532,6 +2540,21 @@ void dev_net_set(struct net_device *dev, struct net *net) + write_pnet(&dev->nd_net, net); + } + ++/** ++ * netif_napi_add_named - initialize a NAPI context ++ * @dev: network device ++ * @napi: NAPI context ++ * @poll: polling function ++ * @weight: default weight ++ * @name: napi instance name ++ * ++ * netif_napi_add_named() must be used to initialize a NAPI context prior to calling ++ * *any* of the other NAPI-related functions. ++ */ ++void netif_napi_add_named(struct net_device *dev, struct napi_struct *napi, ++ int (*poll)(struct napi_struct *, int), int weight, ++ const char *name); ++ + /** + * netdev_priv - access network device private data + * @dev: network device +@@ -2605,6 +2628,27 @@ static inline void netif_napi_add_tx(struct net_device *dev, + netif_napi_add_tx_weight(dev, napi, poll, NAPI_POLL_WEIGHT); + } + ++/** ++ * netif_napi_add_tx_named - initialize a NAPI context ++ * @dev: network device ++ * @napi: NAPI context ++ * @poll: polling function ++ * @weight: default weight ++ * @name: napi instance name ++ * ++ * This variant of netif_napi_add_named() should be used from drivers using NAPI ++ * to exclusively poll a TX queue. ++ * This will avoid we add it into napi_hash[], thus polluting this hash table. ++ */ ++static inline void netif_napi_add_tx_named(struct net_device *dev, ++ struct napi_struct *napi, ++ int (*poll)(struct napi_struct *, int), ++ int weight, const char *name) ++{ ++ set_bit(NAPI_STATE_NO_BUSY_POLL, &napi->state); ++ netif_napi_add_named(dev, napi, poll, weight, name); ++} ++ + /** + * __netif_napi_del - remove a NAPI context + * @napi: NAPI context +@@ -3169,7 +3213,11 @@ struct softnet_data { + int defer_count; + int defer_ipi_scheduled; + struct sk_buff *defer_list; ++#ifndef CONFIG_PREEMPT_RT + call_single_data_t defer_csd; ++#else ++ struct work_struct defer_work; ++#endif + }; + + static inline void input_queue_head_incr(struct softnet_data *sd) +diff --git a/include/linux/preempt.h b/include/linux/preempt.h +index 0df425bf9bd7..12f59cdaaedd 100644 +--- a/include/linux/preempt.h ++++ b/include/linux/preempt.h +@@ -196,6 +196,20 @@ extern void preempt_count_sub(int val); + #define preempt_count_inc() preempt_count_add(1) + #define preempt_count_dec() preempt_count_sub(1) + ++#ifdef CONFIG_PREEMPT_LAZY ++#define add_preempt_lazy_count(val) do { preempt_lazy_count() += (val); } while (0) ++#define sub_preempt_lazy_count(val) do { preempt_lazy_count() -= (val); } while (0) ++#define inc_preempt_lazy_count() add_preempt_lazy_count(1) ++#define dec_preempt_lazy_count() sub_preempt_lazy_count(1) ++#define preempt_lazy_count() (current_thread_info()->preempt_lazy_count) ++#else ++#define add_preempt_lazy_count(val) do { } while (0) ++#define sub_preempt_lazy_count(val) do { } while (0) ++#define inc_preempt_lazy_count() do { } while (0) ++#define dec_preempt_lazy_count() do { } while (0) ++#define preempt_lazy_count() (0) ++#endif ++ + #ifdef CONFIG_PREEMPT_COUNT + + #define preempt_disable() \ +@@ -204,6 +218,12 @@ do { \ + barrier(); \ + } while (0) + ++#define preempt_lazy_disable() \ ++do { \ ++ inc_preempt_lazy_count(); \ ++ barrier(); \ ++} while (0) ++ + #define sched_preempt_enable_no_resched() \ + do { \ + barrier(); \ +@@ -235,6 +255,18 @@ do { \ + __preempt_schedule(); \ + } while (0) + ++/* ++ * open code preempt_check_resched() because it is not exported to modules and ++ * used by local_unlock() or bpf_enable_instrumentation(). ++ */ ++#define preempt_lazy_enable() \ ++do { \ ++ dec_preempt_lazy_count(); \ ++ barrier(); \ ++ if (should_resched(0)) \ ++ __preempt_schedule(); \ ++} while (0) ++ + #else /* !CONFIG_PREEMPTION */ + #define preempt_enable() \ + do { \ +@@ -242,6 +274,12 @@ do { \ + preempt_count_dec(); \ + } while (0) + ++#define preempt_lazy_enable() \ ++do { \ ++ dec_preempt_lazy_count(); \ ++ barrier(); \ ++} while (0) ++ + #define preempt_enable_notrace() \ + do { \ + barrier(); \ +@@ -282,6 +320,9 @@ do { \ + #define preempt_enable_notrace() barrier() + #define preemptible() 0 + ++#define preempt_lazy_disable() barrier() ++#define preempt_lazy_enable() barrier() ++ + #endif /* CONFIG_PREEMPT_COUNT */ + + #ifdef MODULE +@@ -300,7 +341,7 @@ do { \ + } while (0) + #define preempt_fold_need_resched() \ + do { \ +- if (tif_need_resched()) \ ++ if (tif_need_resched_now()) \ + set_preempt_need_resched(); \ + } while (0) + +@@ -416,8 +457,15 @@ extern void migrate_enable(void); + + #else + +-static inline void migrate_disable(void) { } +-static inline void migrate_enable(void) { } ++static inline void migrate_disable(void) ++{ ++ preempt_lazy_disable(); ++} ++ ++static inline void migrate_enable(void) ++{ ++ preempt_lazy_enable(); ++} + + #endif /* CONFIG_SMP */ + +diff --git a/include/linux/printk.h b/include/linux/printk.h +index 8c81806c2e99..f8c4e4fa6d7d 100644 +--- a/include/linux/printk.h ++++ b/include/linux/printk.h +@@ -168,6 +168,9 @@ extern void __printk_safe_exit(void); + */ + #define printk_deferred_enter __printk_safe_enter + #define printk_deferred_exit __printk_safe_exit ++extern void printk_prefer_direct_enter(void); ++extern void printk_prefer_direct_exit(void); ++extern void try_block_console_kthreads(int timeout_ms); + + /* + * Please don't use printk_ratelimit(), because it shares ratelimiting state +@@ -219,6 +222,18 @@ static inline void printk_deferred_exit(void) + { + } + ++static inline void printk_prefer_direct_enter(void) ++{ ++} ++ ++static inline void printk_prefer_direct_exit(void) ++{ ++} ++ ++static inline void try_block_console_kthreads(int timeout_ms) ++{ ++} ++ + static inline int printk_ratelimit(void) + { + return 0; +diff --git a/include/linux/rpmsg/imx_srtm.h b/include/linux/rpmsg/imx_srtm.h +new file mode 100644 +index 000000000000..7c0c53916cce +--- /dev/null ++++ b/include/linux/rpmsg/imx_srtm.h +@@ -0,0 +1,65 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright 2022-2023 NXP ++ * ++ * 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. ++ */ ++ ++/* ++ * @file linux/rpmsg/imx_srtm.h ++ * ++ * @brief Global header file for iMX SRTM (Simplified Real Time Message Application Protocol, base on rpmsg) ++ * ++ * @ingroup SRTM ++ */ ++#ifndef __LINUX_RPMSG_IMX_SRTM_H__ ++#define __LINUX_RPMSG_IMX_SRTM_H__ ++ ++/* Category define */ ++#define IMX_SRTM_CATEGORY_LIFECYCLE (0x1) ++#define IMX_SRTM_CATEGORY_PMIC (0x2) ++#define IMX_SRTM_CATEGORY_AUDIO (0x3) ++#define IMX_SRTM_CATEGORY_KEY (0x4) ++#define IMX_SRTM_CATEGORY_GPIO (0x5) ++#define IMX_SRTM_CATEGORY_RTC (0x6) ++#define IMX_SRTM_CATEGORY_SENSOR (0x7) ++#define IMX_SRTM_CATEGORY_AUTO (0x8) ++#define IMX_SRTM_CATEGORY_I2C (0x9) ++#define IMX_SRTM_CATEGORY_PWM (0xA) ++#define IMX_SRTM_CATEGORY_UART (0xB) ++ ++/* srtm version */ ++#define IMX_SRTM_VER_UART (0x0001) ++ ++/* type */ ++#define IMX_SRTM_TYPE_REQUEST (0) ++#define IMX_SRTM_TYPE_RESPONSE (1) ++#define IMX_SRTM_TYPE_NOTIFY (2) ++ ++/* command */ ++#define IMX_SRTM_UART_COMMAND_SEND (1) ++#define IMX_SRTM_UART_COMMAND_HELLO (2) ++ ++/* priority */ ++#define IMX_SRTM_UART_PRIORITY (0x01) ++ ++/* flags */ ++#define IMX_SRTM_RPMSG_OVER_UART_FLAG (1 << 0) ++#define IMX_SRTM_UART_SUPPORT_MULTI_UART_MSG_FLAG (1 << 1) ++#define IMX_SRTM_UART_PORT_NUM_SHIFT (11U) ++#define IMX_SRTM_UART_PORT_NUM_MASK (0x1F << 11U) ++#define IMX_SRTM_UART_SPECIFY_PORT_NUM_SHIFT (10U) ++#define IMX_SRTM_UART_SPECIFY_PORT_NUM_MASK (1 << IMX_SRTM_UART_SPECIFY_PORT_NUM_SHIFT) ++ ++struct imx_srtm_head { ++ u8 cate; ++ u8 major; ++ u8 minor; ++ u8 type; ++ u8 cmd; ++ u8 reserved[5]; ++} __packed; ++ ++#endif /* __LINUX_RPMSG_IMX_SRTM_H__ */ +diff --git a/include/linux/sched.h b/include/linux/sched.h +index 0cac69902ec5..67ec36dbfacf 100644 +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -2061,6 +2061,43 @@ static inline int test_tsk_need_resched(struct task_struct *tsk) + return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED)); + } + ++#ifdef CONFIG_PREEMPT_LAZY ++static inline void set_tsk_need_resched_lazy(struct task_struct *tsk) ++{ ++ set_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY); ++} ++ ++static inline void clear_tsk_need_resched_lazy(struct task_struct *tsk) ++{ ++ clear_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY); ++} ++ ++static inline int test_tsk_need_resched_lazy(struct task_struct *tsk) ++{ ++ return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY)); ++} ++ ++static inline int need_resched_lazy(void) ++{ ++ return test_thread_flag(TIF_NEED_RESCHED_LAZY); ++} ++ ++static inline int need_resched_now(void) ++{ ++ return test_thread_flag(TIF_NEED_RESCHED); ++} ++ ++#else ++static inline void clear_tsk_need_resched_lazy(struct task_struct *tsk) { } ++static inline int need_resched_lazy(void) { return 0; } ++ ++static inline int need_resched_now(void) ++{ ++ return test_thread_flag(TIF_NEED_RESCHED); ++} ++ ++#endif ++ + /* + * cond_resched() and cond_resched_lock(): latency reduction via + * explicit rescheduling in places that are safe. The return +diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h +index 79b328861c5f..35f44352e641 100644 +--- a/include/linux/serial_8250.h ++++ b/include/linux/serial_8250.h +@@ -7,6 +7,7 @@ + #ifndef _LINUX_SERIAL_8250_H + #define _LINUX_SERIAL_8250_H + ++#include + #include + #include + #include +@@ -124,6 +125,8 @@ struct uart_8250_port { + #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA + unsigned char msr_saved_flags; + ++ atomic_t console_printing; ++ + struct uart_8250_dma *dma; + const struct uart_8250_ops *ops; + +@@ -179,6 +182,8 @@ void serial8250_init_port(struct uart_8250_port *up); + void serial8250_set_defaults(struct uart_8250_port *up); + void serial8250_console_write(struct uart_8250_port *up, const char *s, + unsigned int count); ++void serial8250_console_write_atomic(struct uart_8250_port *up, const char *s, ++ unsigned int count); + int serial8250_console_setup(struct uart_port *port, char *options, bool probe); + int serial8250_console_exit(struct uart_port *port); + +diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h +index 9f392ec76f2b..779e0e96b9cb 100644 +--- a/include/linux/thread_info.h ++++ b/include/linux/thread_info.h +@@ -177,7 +177,17 @@ static __always_inline unsigned long read_ti_thread_flags(struct thread_info *ti + clear_ti_thread_flag(task_thread_info(t), TIF_##fl) + #endif /* !CONFIG_GENERIC_ENTRY */ + +-#define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED) ++#ifdef CONFIG_PREEMPT_LAZY ++#define tif_need_resched() (test_thread_flag(TIF_NEED_RESCHED) || \ ++ test_thread_flag(TIF_NEED_RESCHED_LAZY)) ++#define tif_need_resched_now() (test_thread_flag(TIF_NEED_RESCHED)) ++#define tif_need_resched_lazy() test_thread_flag(TIF_NEED_RESCHED_LAZY) ++ ++#else ++#define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED) ++#define tif_need_resched_now() test_thread_flag(TIF_NEED_RESCHED) ++#define tif_need_resched_lazy() 0 ++#endif + + #ifndef CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES + static inline int arch_within_stack_frames(const void * const stack, +diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h +index 422f4ca656cf..da13c633d1d8 100644 +--- a/include/linux/trace_events.h ++++ b/include/linux/trace_events.h +@@ -70,6 +70,7 @@ struct trace_entry { + unsigned char flags; + unsigned char preempt_count; + int pid; ++ unsigned char preempt_lazy_count; + }; + + #define TRACE_EVENT_TYPE_MAX \ +@@ -159,9 +160,10 @@ static inline void tracing_generic_entry_update(struct trace_entry *entry, + unsigned int trace_ctx) + { + entry->preempt_count = trace_ctx & 0xff; ++ entry->preempt_lazy_count = (trace_ctx >> 16) & 0xff; + entry->pid = current->pid; + entry->type = type; +- entry->flags = trace_ctx >> 16; ++ entry->flags = trace_ctx >> 24; + } + + unsigned int tracing_gen_ctx_irq_test(unsigned int irqs_status); +@@ -172,7 +174,13 @@ enum trace_flag_type { + TRACE_FLAG_NEED_RESCHED = 0x04, + TRACE_FLAG_HARDIRQ = 0x08, + TRACE_FLAG_SOFTIRQ = 0x10, ++#ifdef CONFIG_PREEMPT_LAZY ++ TRACE_FLAG_PREEMPT_RESCHED = 0x00, ++ TRACE_FLAG_NEED_RESCHED_LAZY = 0x20, ++#else ++ TRACE_FLAG_NEED_RESCHED_LAZY = 0x00, + TRACE_FLAG_PREEMPT_RESCHED = 0x20, ++#endif + TRACE_FLAG_NMI = 0x40, + TRACE_FLAG_BH_OFF = 0x80, + }; +diff --git a/include/linux/u64_stats_sync.h b/include/linux/u64_stats_sync.h +index 46040d66334a..ffe48e69b3f3 100644 +--- a/include/linux/u64_stats_sync.h ++++ b/include/linux/u64_stats_sync.h +@@ -213,16 +213,4 @@ static inline bool u64_stats_fetch_retry(const struct u64_stats_sync *syncp, + return __u64_stats_fetch_retry(syncp, start); + } + +-/* Obsolete interfaces */ +-static inline unsigned int u64_stats_fetch_begin_irq(const struct u64_stats_sync *syncp) +-{ +- return u64_stats_fetch_begin(syncp); +-} +- +-static inline bool u64_stats_fetch_retry_irq(const struct u64_stats_sync *syncp, +- unsigned int start) +-{ +- return u64_stats_fetch_retry(syncp, start); +-} +- + #endif /* _LINUX_U64_STATS_SYNC_H */ +diff --git a/include/net/dsa.h b/include/net/dsa.h +index 765b6a6d98da..71772299cdf0 100644 +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -55,6 +55,7 @@ struct phylink_link_state; + #define DSA_TAG_PROTO_RTL8_4T_VALUE 25 + #define DSA_TAG_PROTO_RZN1_A5PSW_VALUE 26 + #define DSA_TAG_PROTO_LAN937X_VALUE 27 ++#define DSA_TAG_PROTO_NETC_VALUE 28 + + enum dsa_tag_protocol { + DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, +@@ -85,6 +86,7 @@ enum dsa_tag_protocol { + DSA_TAG_PROTO_RTL8_4T = DSA_TAG_PROTO_RTL8_4T_VALUE, + DSA_TAG_PROTO_RZN1_A5PSW = DSA_TAG_PROTO_RZN1_A5PSW_VALUE, + DSA_TAG_PROTO_LAN937X = DSA_TAG_PROTO_LAN937X_VALUE, ++ DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE, + }; + + struct dsa_switch; +@@ -961,6 +963,22 @@ struct dsa_switch_ops { + int (*port_del_dscp_prio)(struct dsa_switch *ds, int port, u8 dscp, + u8 prio); + ++ /* ++ * ethtool --set-frame-preemption ++ */ ++ int (*set_preempt)(struct dsa_switch *ds, int port, ++ struct ethtool_fp *fpcmd); ++ ++ /* ++ * ethtool --show-frame-preemption ++ */ ++ int (*get_preempt)(struct dsa_switch *ds, int port, ++ struct ethtool_fp *fpcmd); ++ ++ /* ++ * ethtool --reset-frame-preemption ++ */ ++ int (*reset_preempt)(struct dsa_switch *ds, int port, bool enable); + /* + * Suspend and resume + */ +diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h +index 4cabb32a2ad9..cd410a87517b 100644 +--- a/include/net/pkt_cls.h ++++ b/include/net/pkt_cls.h +@@ -788,16 +788,6 @@ struct tc_cls_bpf_offload { + bool exts_integrated; + }; + +-struct tc_mqprio_qopt_offload { +- /* struct tc_mqprio_qopt must always be the first element */ +- struct tc_mqprio_qopt qopt; +- u16 mode; +- u16 shaper; +- u32 flags; +- u64 min_rate[TC_QOPT_MAX_QUEUE]; +- u64 max_rate[TC_QOPT_MAX_QUEUE]; +-}; +- + /* This structure holds cookie structure that is passed from user + * to the kernel for actions and classifiers + */ +diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h +index fa9a8df3fa76..822c8b5587ec 100644 +--- a/include/net/pkt_sched.h ++++ b/include/net/pkt_sched.h +@@ -162,6 +162,16 @@ struct tc_etf_qopt_offload { + s32 queue; + }; + ++struct tc_mqprio_qopt_offload { ++ /* struct tc_mqprio_qopt must always be the first element */ ++ struct tc_mqprio_qopt qopt; ++ u16 mode; ++ u16 shaper; ++ u32 flags; ++ u64 min_rate[TC_QOPT_MAX_QUEUE]; ++ u64 max_rate[TC_QOPT_MAX_QUEUE]; ++}; ++ + struct tc_taprio_caps { + bool supports_queue_max_sdu:1; + }; +@@ -175,6 +185,7 @@ struct tc_taprio_sched_entry { + }; + + struct tc_taprio_qopt_offload { ++ struct tc_mqprio_qopt_offload mqprio; + u8 enable; + ktime_t base_time; + u64 cycle_time; +diff --git a/include/net/switchdev.h b/include/net/switchdev.h +index 7dcdc97c0bc3..5b6a4ce1f47c 100644 +--- a/include/net/switchdev.h ++++ b/include/net/switchdev.h +@@ -97,6 +97,7 @@ struct switchdev_obj_port_vlan { + struct switchdev_obj obj; + u16 flags; + u16 vid; ++ u16 proto; + /* If set, the notifier signifies a change of one of the following + * flags for a VLAN that already exists: + * - BRIDGE_VLAN_INFO_PVID +diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h +index ca9ed16b6140..831587238e3f 100644 +--- a/include/soc/mscc/ocelot.h ++++ b/include/soc/mscc/ocelot.h +@@ -1044,6 +1044,7 @@ struct ocelot_port { + + u8 stp_state; + bool vlan_aware; ++ bool qinq_mode; + bool is_dsa_8021q_cpu; + bool learn_ena; + +@@ -1054,6 +1055,7 @@ struct ocelot_port { + bool force_forward; + u8 cut_thru; + u8 preemptable_prios; ++ bool fp_enabled_admin; + + int speed; + }; +@@ -1139,6 +1141,9 @@ struct ocelot { + spinlock_t ptp_clock_lock; + struct ptp_pin_desc ptp_pins[OCELOT_PTP_PINS_NUM]; + ++ bool qinq_enable; ++ struct kref qinq_refcount; ++ + struct ocelot_fdma *fdma; + }; + +diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h +index c601a4598b0d..f8cde2408890 100644 +--- a/include/soc/mscc/ocelot_vcap.h ++++ b/include/soc/mscc/ocelot_vcap.h +@@ -704,6 +704,7 @@ struct ocelot_vcap_filter { + enum ocelot_vcap_bit dmac_mc; + enum ocelot_vcap_bit dmac_bc; + struct ocelot_vcap_key_vlan vlan; ++ struct ocelot_vcap_key_vlan cvlan; + + enum ocelot_vcap_key_type key_type; + union { +diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h +index dc2aa3d75b39..b662a48658dc 100644 +--- a/include/uapi/linux/ethtool.h ++++ b/include/uapi/linux/ethtool.h +@@ -381,6 +381,32 @@ struct ethtool_eee { + __u32 reserved[2]; + }; + ++/** ++ * struct ethtool_fp - Frame Preemption information ++ * @cmd: ETHTOOL_{G,S}FP ++ * @disabled: Disable hardware preemption supports. ++ * @fp_supported: If frame preemption is supported. ++ * @fp_enabled: If frame preemption should be advertised to the link partner ++ * as enabled. ++ * @supported_queues_mask: Bitmask indicating which queues support being ++ * configured as preemptible (bit 0 -> queue 0, bit N -> queue N). ++ * @preemptible_queues_mask: Bitmask indicating which queues are ++ * configured as preemptible (bit 0 -> queue 0, bit N -> queue N). ++ * @min_frag_size: Minimum size for all non-final fragment size. ++ */ ++struct ethtool_fp { ++ __u32 cmd; ++ __u8 disabled; ++ __u8 fp_supported; ++ __u8 fp_enabled; ++ __u8 fp_status; ++ __u8 fp_active; ++ __u32 supported_queues_mask; ++ __u32 preemptible_queues_mask; ++ __u32 min_frag_size; ++ __u32 reserved[2]; ++}; ++ + /** + * struct ethtool_modinfo - plugin module eeprom information + * @cmd: %ETHTOOL_GMODULEINFO +@@ -1630,6 +1656,10 @@ enum ethtool_fec_config_bits { + #define ETHTOOL_GFECPARAM 0x00000050 /* Get FEC settings */ + #define ETHTOOL_SFECPARAM 0x00000051 /* Set FEC settings */ + ++#define ETHTOOL_GFP 0x00000052 /* Get Frame Preemption settings */ ++#define ETHTOOL_SFP 0x00000053 /* Set Frame Preemption settings */ ++#define ETHTOOL_RFP 0x00000054 /* Reset Frame Preemption settings */ ++ + /* compatibility with older code */ + #define SPARC_ETH_GSET ETHTOOL_GSET + #define SPARC_ETH_SSET ETHTOOL_SSET +diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h +index 69f5bec347c2..47650669c3c1 100644 +--- a/include/uapi/linux/ethtool_netlink.h ++++ b/include/uapi/linux/ethtool_netlink.h +@@ -41,6 +41,8 @@ enum { + ETHTOOL_MSG_TSINFO_GET, + ETHTOOL_MSG_CABLE_TEST_ACT, + ETHTOOL_MSG_CABLE_TEST_TDR_ACT, ++ ETHTOOL_MSG_PREEMPT_GET, ++ ETHTOOL_MSG_PREEMPT_SET, + ETHTOOL_MSG_TUNNEL_INFO_GET, + ETHTOOL_MSG_FEC_GET, + ETHTOOL_MSG_FEC_SET, +@@ -88,6 +90,8 @@ enum { + ETHTOOL_MSG_TSINFO_GET_REPLY, + ETHTOOL_MSG_CABLE_TEST_NTF, + ETHTOOL_MSG_CABLE_TEST_TDR_NTF, ++ ETHTOOL_MSG_PREEMPT_GET_REPLY, ++ ETHTOOL_MSG_PREEMPT_NTF, + ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY, + ETHTOOL_MSG_FEC_GET_REPLY, + ETHTOOL_MSG_FEC_NTF, +@@ -879,6 +883,23 @@ enum { + ETHTOOL_A_PSE_MAX = (__ETHTOOL_A_PSE_CNT - 1) + }; + ++/* FRAME PREEMPTION */ ++enum { ++ ETHTOOL_A_PREEMPT_UNSPEC, ++ ETHTOOL_A_PREEMPT_HEADER, /* nest - _A_HEADER_* */ ++ ETHTOOL_A_PREEMPT_DISABLED, /* bool */ ++ ETHTOOL_A_PREEMPT_SUPPORTED, /* u8 */ ++ ETHTOOL_A_PREEMPT_STATUS, /* u8 */ ++ ETHTOOL_A_PREEMPT_ACTIVE, /* u8 */ ++ ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE, /* u32 */ ++ ETHTOOL_A_PREEMPT_QUEUES_SUPPORTED, /* u32 */ ++ ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE, /* u32 */ ++ ++ /* add new constants above here */ ++ __ETHTOOL_A_PREEMPT_CNT, ++ ETHTOOL_A_PREEMPT_MAX = (__ETHTOOL_A_PREEMPT_CNT - 1) ++}; ++ + /* generic netlink info */ + #define ETHTOOL_GENL_NAME "ethtool" + #define ETHTOOL_GENL_VERSION 1 +diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h +index 7aa2eb766205..67f7fe400730 100644 +--- a/include/uapi/linux/virtio_ids.h ++++ b/include/uapi/linux/virtio_ids.h +@@ -68,6 +68,7 @@ + #define VIRTIO_ID_AUDIO_POLICY 39 /* virtio audio policy */ + #define VIRTIO_ID_BT 40 /* virtio bluetooth */ + #define VIRTIO_ID_GPIO 41 /* virtio gpio */ ++#define VIRTIO_ID_TRANS 42 /* virtio transfer test */ + + /* + * Virtio Transitional IDs +diff --git a/include/uapi/linux/virtio_mmio.h b/include/uapi/linux/virtio_mmio.h +index 0650f91bea6c..ee672e1b4a6e 100644 +--- a/include/uapi/linux/virtio_mmio.h ++++ b/include/uapi/linux/virtio_mmio.h +@@ -110,6 +110,9 @@ + /* Device status register - Read Write */ + #define VIRTIO_MMIO_STATUS 0x070 + ++/* Hypervisor-less virtio: Device write operation done status register */ ++#define VIRTIO_MMIO_WD_STATUS 0x074 ++ + /* Selected queue's Descriptor Table address, 64 bits in two halves */ + #define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 + #define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 +@@ -140,7 +143,9 @@ + * the per-driver configuration space - Read Write */ + #define VIRTIO_MMIO_CONFIG 0x100 + +- ++/* Hypervisor-less virtio: write operation space shared with remote */ ++#define VIRTIO_MMIO_RW_OPS_MEM_OFFSET 0x400 ++#define VIRTIO_MMIO_RW_OPS_MEM_SIZE 64 + + /* + * Interrupt flags (re: interrupt status & acknowledge registers) +diff --git a/include/uapi/linux/virtio_trans.h b/include/uapi/linux/virtio_trans.h +new file mode 100644 +index 000000000000..24a10efb370e +--- /dev/null ++++ b/include/uapi/linux/virtio_trans.h +@@ -0,0 +1,60 @@ ++/* SPDX-License-Identifier: BSD-3-Clause */ ++/* ++ * Copyright 2022 NXP ++ */ ++ ++#ifndef _LINUX_VIRTIO_TRANS_H ++#define _LINUX_VIRTIO_TRANS_H ++ ++#include ++ ++/* ++ * status: ++ * bit0: TX ready ++ * bit1: RX ready ++ * config: ++ * bit0: TX ++ * bit1: RX ++ * bit2: 0: Backend do NOT copy buffer ++ * 1: Backend do copy buffer ++ * bit3: 0: Backend interrupt mode ++ * 1: Backend polling mode ++ * bit4: 0: Frontend interrupt mode ++ * 1: Frontend polling mode ++ * control: ++ * bit0: start transfer ++ * bit1: reset ++ * pkt_size: packet size in Byte ++ * tx_count: Completed count for TX packet, update by Backend ++ * rx_count: Completed count for RX packet, update by Backend ++ * regression: Packets to test, set by Frontend ++ */ ++ ++#define VT_STATUS 0x0 ++#define VT_CONFIG 0x4 ++#define VT_CONTROL 0x8 ++#define VT_PKT_SIZE 0xc ++#define VT_TX_COUNT 0x10 ++#define VT_RX_COUNT 0x14 ++#define VT_REGRESSION 0x18 ++ ++#define VT_CFG_TX BIT(0) ++#define VT_CFG_RX BIT(1) ++#define VT_CFG_COPY BIT(2) ++#define VT_CFG_B_POLL BIT(3) ++#define VT_CFG_F_POLL BIT(4) ++ ++#define VT_CTRL_START BIT(0) ++#define VT_CTRL_RESET BIT(1) ++ ++struct virtio_trans_config { ++ __le32 status; ++ __le32 config; ++ __le32 control; ++ __le32 pkt_size; ++ __le32 tx_count; ++ __le32 rx_count; ++ __le32 regression; ++}; ++ ++#endif /* _LINUX_VIRTIO_TRANS_H */ +-- +2.34.1 + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0006-kernel-add-NXP-RT-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0006-kernel-add-NXP-RT-support.patch new file mode 100644 index 000000000..dbc0ad49b --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0006-kernel-add-NXP-RT-support.patch @@ -0,0 +1,2435 @@ +From 117dccc8d01ab70decb5d6a529f4780aaba20e0b Mon Sep 17 00:00:00 2001 +From: Mike Engel +Date: Sat, 24 Feb 2024 17:03:36 +0100 +Subject: [PATCH 06/10] kernel: add NXP RT support + +Signed-off-by: Mike Engel +--- + kernel/Kconfig.preempt | 6 + + kernel/bpf/syscall.c | 4 +- + kernel/dma/coherent.c | 15 +- + kernel/entry/common.c | 4 +- + kernel/hung_task.c | 11 +- + kernel/ksysfs.c | 12 + + kernel/panic.c | 10 +- + kernel/printk/internal.h | 2 + + kernel/printk/printk.c | 890 ++++++++++++++++++++++++++++++++---- + kernel/printk/printk_safe.c | 32 ++ + kernel/rcu/rcutorture.c | 6 + + kernel/rcu/tree_stall.h | 2 + + kernel/reboot.c | 16 +- + kernel/sched/core.c | 160 ++++++- + kernel/sched/fair.c | 16 +- + kernel/sched/features.h | 3 + + kernel/sched/sched.h | 9 + + kernel/signal.c | 8 +- + kernel/softirq.c | 82 +++- + kernel/time/hrtimer.c | 4 +- + kernel/time/tick-sched.c | 2 +- + kernel/time/timer.c | 2 +- + kernel/trace/trace.c | 50 +- + kernel/trace/trace_events.c | 1 + + kernel/trace/trace_output.c | 18 +- + kernel/watchdog.c | 4 + + kernel/watchdog_hld.c | 4 + + 27 files changed, 1234 insertions(+), 139 deletions(-) + +diff --git a/kernel/Kconfig.preempt b/kernel/Kconfig.preempt +index c2f1fd95a821..260c08efeb48 100644 +--- a/kernel/Kconfig.preempt ++++ b/kernel/Kconfig.preempt +@@ -1,5 +1,11 @@ + # SPDX-License-Identifier: GPL-2.0-only + ++config HAVE_PREEMPT_LAZY ++ bool ++ ++config PREEMPT_LAZY ++ def_bool y if HAVE_PREEMPT_LAZY && PREEMPT_RT ++ + config PREEMPT_NONE_BUILD + bool + +diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c +index 0c8b7733573e..c0915e2424f1 100644 +--- a/kernel/bpf/syscall.c ++++ b/kernel/bpf/syscall.c +@@ -2115,11 +2115,11 @@ static void bpf_prog_get_stats(const struct bpf_prog *prog, + + st = per_cpu_ptr(prog->stats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&st->syncp); ++ start = u64_stats_fetch_begin(&st->syncp); + tnsecs = u64_stats_read(&st->nsecs); + tcnt = u64_stats_read(&st->cnt); + tmisses = u64_stats_read(&st->misses); +- } while (u64_stats_fetch_retry_irq(&st->syncp, start)); ++ } while (u64_stats_fetch_retry(&st->syncp, start)); + nsecs += tnsecs; + cnt += tcnt; + misses += tmisses; +diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c +index c21abc77c53e..f15ba6c6358e 100644 +--- a/kernel/dma/coherent.c ++++ b/kernel/dma/coherent.c +@@ -36,7 +36,8 @@ static inline dma_addr_t dma_get_device_base(struct device *dev, + } + + static struct dma_coherent_mem *dma_init_coherent_memory(phys_addr_t phys_addr, +- dma_addr_t device_addr, size_t size, bool use_dma_pfn_offset) ++ dma_addr_t device_addr, size_t size, bool use_dma_pfn_offset, ++ bool cacheable) + { + struct dma_coherent_mem *dma_mem; + int pages = size >> PAGE_SHIFT; +@@ -45,7 +46,8 @@ static struct dma_coherent_mem *dma_init_coherent_memory(phys_addr_t phys_addr, + if (!size) + return ERR_PTR(-EINVAL); + +- mem_base = memremap(phys_addr, size, MEMREMAP_WC); ++ mem_base = memremap(phys_addr, size, cacheable ? MEMREMAP_WB : ++ MEMREMAP_WC); + if (!mem_base) + return ERR_PTR(-EINVAL); + +@@ -119,8 +121,10 @@ int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, + { + struct dma_coherent_mem *mem; + int ret; ++ bool cacheable = dev_is_dma_coherent(dev); + +- mem = dma_init_coherent_memory(phys_addr, device_addr, size, false); ++ mem = dma_init_coherent_memory(phys_addr, device_addr, size, false, ++ cacheable); + if (IS_ERR(mem)) + return PTR_ERR(mem); + +@@ -310,7 +314,7 @@ int dma_init_global_coherent(phys_addr_t phys_addr, size_t size) + { + struct dma_coherent_mem *mem; + +- mem = dma_init_coherent_memory(phys_addr, phys_addr, size, true); ++ mem = dma_init_coherent_memory(phys_addr, phys_addr, size, true, false); + if (IS_ERR(mem)) + return PTR_ERR(mem); + dma_coherent_default_memory = mem; +@@ -335,9 +339,10 @@ static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev) + { + if (!rmem->priv) { + struct dma_coherent_mem *mem; ++ bool cacheable = dev_is_dma_coherent(dev); + + mem = dma_init_coherent_memory(rmem->base, rmem->base, +- rmem->size, true); ++ rmem->size, true, cacheable); + if (IS_ERR(mem)) + return PTR_ERR(mem); + rmem->priv = mem; +diff --git a/kernel/entry/common.c b/kernel/entry/common.c +index be61332c66b5..c6301e520d47 100644 +--- a/kernel/entry/common.c ++++ b/kernel/entry/common.c +@@ -155,7 +155,7 @@ static unsigned long exit_to_user_mode_loop(struct pt_regs *regs, + + local_irq_enable_exit_to_user(ti_work); + +- if (ti_work & _TIF_NEED_RESCHED) ++ if (ti_work & _TIF_NEED_RESCHED_MASK) + schedule(); + + if (ti_work & _TIF_UPROBE) +@@ -386,7 +386,7 @@ void raw_irqentry_exit_cond_resched(void) + rcu_irq_exit_check_preempt(); + if (IS_ENABLED(CONFIG_DEBUG_ENTRY)) + WARN_ON_ONCE(!on_thread_stack()); +- if (need_resched()) ++ if (should_resched(0)) + preempt_schedule_irq(); + } + } +diff --git a/kernel/hung_task.c b/kernel/hung_task.c +index c71889f3f3fc..e2d2344cb9f4 100644 +--- a/kernel/hung_task.c ++++ b/kernel/hung_task.c +@@ -127,6 +127,8 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout) + * complain: + */ + if (sysctl_hung_task_warnings) { ++ printk_prefer_direct_enter(); ++ + if (sysctl_hung_task_warnings > 0) + sysctl_hung_task_warnings--; + pr_err("INFO: task %s:%d blocked for more than %ld seconds.\n", +@@ -142,6 +144,8 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout) + + if (sysctl_hung_task_all_cpu_backtrace) + hung_task_show_all_bt = true; ++ ++ printk_prefer_direct_exit(); + } + + touch_nmi_watchdog(); +@@ -212,12 +216,17 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) + } + unlock: + rcu_read_unlock(); +- if (hung_task_show_lock) ++ if (hung_task_show_lock) { ++ printk_prefer_direct_enter(); + debug_show_all_locks(); ++ printk_prefer_direct_exit(); ++ } + + if (hung_task_show_all_bt) { + hung_task_show_all_bt = false; ++ printk_prefer_direct_enter(); + trigger_all_cpu_backtrace(); ++ printk_prefer_direct_exit(); + } + + if (hung_task_call_panic) +diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c +index 65dba9076f31..ab18048e2186 100644 +--- a/kernel/ksysfs.c ++++ b/kernel/ksysfs.c +@@ -142,6 +142,15 @@ KERNEL_ATTR_RO(vmcoreinfo); + + #endif /* CONFIG_CRASH_CORE */ + ++#if defined(CONFIG_PREEMPT_RT) ++static ssize_t realtime_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", 1); ++} ++KERNEL_ATTR_RO(realtime); ++#endif ++ + /* whether file capabilities are enabled */ + static ssize_t fscaps_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +@@ -232,6 +241,9 @@ static struct attribute * kernel_attrs[] = { + #ifndef CONFIG_TINY_RCU + &rcu_expedited_attr.attr, + &rcu_normal_attr.attr, ++#endif ++#ifdef CONFIG_PREEMPT_RT ++ &realtime_attr.attr, + #endif + NULL + }; +diff --git a/kernel/panic.c b/kernel/panic.c +index 63e94f3bd8dc..97cc495d95f8 100644 +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -322,7 +322,6 @@ void panic(const char *fmt, ...) + panic_smp_self_stop(); + + console_verbose(); +- bust_spinlocks(1); + va_start(args, fmt); + len = vscnprintf(buf, sizeof(buf), fmt, args); + va_end(args); +@@ -339,6 +338,11 @@ void panic(const char *fmt, ...) + dump_stack(); + #endif + ++ /* If atomic consoles are available, flush the kernel log. */ ++ console_flush_on_panic(CONSOLE_ATOMIC_FLUSH_PENDING); ++ ++ bust_spinlocks(1); ++ + /* + * If kgdb is enabled, give it a chance to run before we stop all + * the other CPUs or else we won't be able to debug processes left +@@ -653,6 +657,8 @@ void __warn(const char *file, int line, void *caller, unsigned taint, + { + disable_trace_on_warning(); + ++ printk_prefer_direct_enter(); ++ + if (file) + pr_warn("WARNING: CPU: %d PID: %d at %s:%d %pS\n", + raw_smp_processor_id(), current->pid, file, line, +@@ -681,6 +687,8 @@ void __warn(const char *file, int line, void *caller, unsigned taint, + + /* Just a warning, don't kill lockdep. */ + add_taint(taint, LOCKDEP_STILL_OK); ++ ++ printk_prefer_direct_exit(); + } + + #ifndef __WARN_FLAGS +diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h +index d947ca6c84f9..e7d8578860ad 100644 +--- a/kernel/printk/internal.h ++++ b/kernel/printk/internal.h +@@ -20,6 +20,8 @@ enum printk_info_flags { + LOG_CONT = 8, /* text is a fragment of a continuation line */ + }; + ++extern bool block_console_kthreads; ++ + __printf(4, 0) + int vprintk_store(int facility, int level, + const struct dev_printk_info *dev_info, +diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c +index cc53fb77f77c..b245b2f60e6a 100644 +--- a/kernel/printk/printk.c ++++ b/kernel/printk/printk.c +@@ -44,6 +44,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -220,6 +221,36 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, + } + #endif /* CONFIG_PRINTK && CONFIG_SYSCTL */ + ++/* ++ * Used to synchronize printing kthreads against direct printing via ++ * console_trylock/console_unlock. ++ * ++ * Values: ++ * -1 = console kthreads atomically blocked (via global trylock) ++ * 0 = no kthread printing, console not locked (via trylock) ++ * >0 = kthread(s) actively printing ++ * ++ * Note: For synchronizing against direct printing via ++ * console_lock/console_unlock, see the @lock variable in ++ * struct console. ++ */ ++static atomic_t console_kthreads_active = ATOMIC_INIT(0); ++ ++#define console_kthreads_atomic_tryblock() \ ++ (atomic_cmpxchg(&console_kthreads_active, 0, -1) == 0) ++#define console_kthreads_atomic_unblock() \ ++ atomic_cmpxchg(&console_kthreads_active, -1, 0) ++#define console_kthreads_atomically_blocked() \ ++ (atomic_read(&console_kthreads_active) == -1) ++ ++#define console_kthread_printing_tryenter() \ ++ atomic_inc_unless_negative(&console_kthreads_active) ++#define console_kthread_printing_exit() \ ++ atomic_dec(&console_kthreads_active) ++ ++/* Block console kthreads to avoid processing new messages. */ ++bool block_console_kthreads; ++ + /* + * Helper macros to handle lockdep when locking/unlocking console_sem. We use + * macros instead of functions so that _RET_IP_ contains useful information. +@@ -268,14 +299,49 @@ static bool panic_in_progress(void) + } + + /* +- * This is used for debugging the mess that is the VT code by +- * keeping track if we have the console semaphore held. It's +- * definitely not the perfect debug tool (we don't know if _WE_ +- * hold it and are racing, but it helps tracking those weird code +- * paths in the console code where we end up in places I want +- * locked without the console semaphore held). ++ * Tracks whether kthread printers are all blocked. A value of true implies ++ * that the console is locked via console_lock() or the console is suspended. ++ * Writing to this variable requires holding @console_sem. ++ */ ++static bool console_kthreads_blocked; ++ ++/* ++ * Block all kthread printers from a schedulable context. ++ * ++ * Requires holding @console_sem. ++ */ ++static void console_kthreads_block(void) ++{ ++ struct console *con; ++ ++ for_each_console(con) { ++ mutex_lock(&con->lock); ++ con->blocked = true; ++ mutex_unlock(&con->lock); ++ } ++ ++ console_kthreads_blocked = true; ++} ++ ++/* ++ * Unblock all kthread printers from a schedulable context. ++ * ++ * Requires holding @console_sem. + */ +-static int console_locked, console_suspended; ++static void console_kthreads_unblock(void) ++{ ++ struct console *con; ++ ++ for_each_console(con) { ++ mutex_lock(&con->lock); ++ con->blocked = false; ++ mutex_unlock(&con->lock); ++ } ++ ++ console_kthreads_blocked = false; ++} ++ ++static int console_suspended; + + /* + * Array of consoles built from command line options (console=) +@@ -358,7 +424,75 @@ static int console_msg_format = MSG_FORMAT_DEFAULT; + /* syslog_lock protects syslog_* variables and write access to clear_seq. */ + static DEFINE_MUTEX(syslog_lock); + ++/* ++ * A flag to signify if printk_activate_kthreads() has already started the ++ * kthread printers. If true, any later registered consoles must start their ++ * own kthread directly. The flag is write protected by the console_lock. ++ */ ++static bool printk_kthreads_available; ++ + #ifdef CONFIG_PRINTK ++static atomic_t printk_prefer_direct = ATOMIC_INIT(0); ++ ++/** ++ * printk_prefer_direct_enter - cause printk() calls to attempt direct ++ * printing to all enabled consoles ++ * ++ * Since it is not possible to call into the console printing code from any ++ * context, there is no guarantee that direct printing will occur. ++ * ++ * This globally effects all printk() callers. ++ * ++ * Context: Any context. ++ */ ++void printk_prefer_direct_enter(void) ++{ ++ atomic_inc(&printk_prefer_direct); ++} ++ ++/** ++ * printk_prefer_direct_exit - restore printk() behavior ++ * ++ * Context: Any context. ++ */ ++void printk_prefer_direct_exit(void) ++{ ++ WARN_ON(atomic_dec_if_positive(&printk_prefer_direct) < 0); ++} ++ ++/* ++ * Calling printk() always wakes kthread printers so that they can ++ * flush the new message to their respective consoles. Also, if direct ++ * printing is allowed, printk() tries to flush the messages directly. ++ * ++ * Direct printing is allowed in situations when the kthreads ++ * are not available or the system is in a problematic state. ++ * ++ * See the implementation about possible races. ++ */ ++static inline bool allow_direct_printing(void) ++{ ++ /* ++ * Checking kthread availability is a possible race because the ++ * kthread printers can become permanently disabled during runtime. ++ * However, doing that requires holding the console_lock, so any ++ * pending messages will be direct printed by console_unlock(). ++ */ ++ if (!printk_kthreads_available) ++ return true; ++ ++ /* ++ * Prefer direct printing when the system is in a problematic state. ++ * The context that sets this state will always see the updated value. ++ * The other contexts do not care. Anyway, direct printing is just a ++ * best effort. The direct output is only possible when console_lock ++ * is not already taken and no kthread printers are actively printing. ++ */ ++ return (system_state > SYSTEM_RUNNING || ++ oops_in_progress || ++ atomic_read(&printk_prefer_direct)); ++} ++ + DECLARE_WAIT_QUEUE_HEAD(log_wait); + /* All 3 protected by @syslog_lock. */ + /* the next printk record to read by syslog(READ) or /proc/kmsg */ +@@ -1847,6 +1981,7 @@ static int console_lock_spinning_disable_and_check(void) + return 1; + } + ++#if !IS_ENABLED(CONFIG_PREEMPT_RT) + /** + * console_trylock_spinning - try to get console_lock by busy waiting + * +@@ -1920,6 +2055,7 @@ static int console_trylock_spinning(void) + + return 1; + } ++#endif /* CONFIG_PREEMPT_RT */ + + /* + * Call the specified console driver, asking it to write out the specified +@@ -1927,19 +2063,28 @@ static int console_trylock_spinning(void) + * dropped, a dropped message will be written out first. + */ + static void call_console_driver(struct console *con, const char *text, size_t len, +- char *dropped_text) ++ char *dropped_text, bool atomic_printing) + { ++ unsigned long dropped = 0; + size_t dropped_len; + +- if (con->dropped && dropped_text) { ++ if (dropped_text) ++ dropped = atomic_long_xchg_relaxed(&con->dropped, 0); ++ ++ if (dropped) { + dropped_len = snprintf(dropped_text, DROPPED_TEXT_MAX, + "** %lu printk messages dropped **\n", +- con->dropped); +- con->dropped = 0; +- con->write(con, dropped_text, dropped_len); ++ dropped); ++ if (atomic_printing) ++ con->write_atomic(con, dropped_text, dropped_len); ++ else ++ con->write(con, dropped_text, dropped_len); + } + +- con->write(con, text, len); ++ if (atomic_printing) ++ con->write_atomic(con, text, len); ++ else ++ con->write(con, text, len); + } + + /* +@@ -2249,10 +2394,22 @@ asmlinkage int vprintk_emit(int facility, int level, + printed_len = vprintk_store(facility, level, dev_info, fmt, args); + + /* If called from the scheduler, we can not call up(). */ +- if (!in_sched) { ++ if (!in_sched && allow_direct_printing()) { ++#if IS_ENABLED(CONFIG_PREEMPT_RT) ++ /* ++ * Use the non-spinning trylock since PREEMPT_RT does not ++ * support console lock handovers. ++ * ++ * Direct printing will most likely involve taking spinlocks. ++ * For PREEMPT_RT, this is only allowed if in a preemptible ++ * context. ++ */ ++ if (preemptible() && console_trylock()) ++ console_unlock(); ++#else + /* + * The caller may be holding system-critical or +- * timing-sensitive locks. Disable preemption during ++ * timing-sensitive locks. Disable preemption during direct + * printing of all remaining records to all consoles so that + * this context can return as soon as possible. Hopefully + * another printk() caller will take over the printing. +@@ -2267,6 +2424,7 @@ asmlinkage int vprintk_emit(int facility, int level, + if (console_trylock_spinning()) + console_unlock(); + preempt_enable(); ++#endif + } + + if (in_sched) +@@ -2297,9 +2455,81 @@ asmlinkage __visible int _printk(const char *fmt, ...) + } + EXPORT_SYMBOL(_printk); + ++#ifdef CONFIG_HAVE_ATOMIC_CONSOLE ++static void __free_atomic_data(struct console_atomic_data *d) ++{ ++ kfree(d->text); ++ kfree(d->ext_text); ++ kfree(d->dropped_text); ++} ++ ++static void free_atomic_data(struct console_atomic_data *d) ++{ ++ int count = 1; ++ int i; ++ ++ if (!d) ++ return; ++ ++#ifdef CONFIG_HAVE_NMI ++ count = 2; ++#endif ++ ++ for (i = 0; i < count; i++) ++ __free_atomic_data(&d[i]); ++ kfree(d); ++} ++ ++static int __alloc_atomic_data(struct console_atomic_data *d, short flags) ++{ ++ d->text = kmalloc(CONSOLE_LOG_MAX, GFP_KERNEL); ++ if (!d->text) ++ return -1; ++ ++ if (flags & CON_EXTENDED) { ++ d->ext_text = kmalloc(CONSOLE_EXT_LOG_MAX, GFP_KERNEL); ++ if (!d->ext_text) ++ return -1; ++ } else { ++ d->dropped_text = kmalloc(DROPPED_TEXT_MAX, GFP_KERNEL); ++ if (!d->dropped_text) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static struct console_atomic_data *alloc_atomic_data(short flags) ++{ ++ struct console_atomic_data *d; ++ int count = 1; ++ int i; ++ ++#ifdef CONFIG_HAVE_NMI ++ count = 2; ++#endif ++ ++ d = kzalloc(sizeof(*d) * count, GFP_KERNEL); ++ if (!d) ++ goto err_out; ++ ++ for (i = 0; i < count; i++) { ++ if (__alloc_atomic_data(&d[i], flags) != 0) ++ goto err_out; ++ } ++ ++ return d; ++err_out: ++ free_atomic_data(d); ++ return NULL; ++} ++#endif /* CONFIG_HAVE_ATOMIC_CONSOLE */ ++ + static bool pr_flush(int timeout_ms, bool reset_on_progress); + static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress); + ++static void printk_start_kthread(struct console *con); ++ + #else /* CONFIG_PRINTK */ + + #define CONSOLE_LOG_MAX 0 +@@ -2310,6 +2540,8 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre + #define prb_first_valid_seq(rb) 0 + #define prb_next_seq(rb) 0 + ++#define free_atomic_data(d) ++ + static u64 syslog_seq; + + static size_t record_print_text(const struct printk_record *r, +@@ -2328,12 +2560,14 @@ static ssize_t msg_print_ext_body(char *buf, size_t size, + static void console_lock_spinning_enable(void) { } + static int console_lock_spinning_disable_and_check(void) { return 0; } + static void call_console_driver(struct console *con, const char *text, size_t len, +- char *dropped_text) ++ char *dropped_text, bool atomic_printing) + { + } + static bool suppress_message_printing(int level) { return false; } + static bool pr_flush(int timeout_ms, bool reset_on_progress) { return true; } + static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress) { return true; } ++static void printk_start_kthread(struct console *con) { } ++static bool allow_direct_printing(void) { return true; } + + #endif /* CONFIG_PRINTK */ + +@@ -2552,6 +2786,14 @@ static int console_cpu_notify(unsigned int cpu) + /* If trylock fails, someone else is doing the printing */ + if (console_trylock()) + console_unlock(); ++ else { ++ /* ++ * If a new CPU comes online, the conditions for ++ * printer_should_wake() may have changed for some ++ * kthread printer with !CON_ANYTIME. ++ */ ++ wake_up_klogd(); ++ } + } + return 0; + } +@@ -2594,7 +2836,7 @@ void console_lock(void) + down_console_sem(); + if (console_suspended) + return; +- console_locked = 1; ++ console_kthreads_block(); + console_may_schedule = 1; + } + EXPORT_SYMBOL(console_lock); +@@ -2618,30 +2860,36 @@ int console_trylock(void) + up_console_sem(); + return 0; + } +- console_locked = 1; ++ if (!console_kthreads_atomic_tryblock()) { ++ up_console_sem(); ++ return 0; ++ } + console_may_schedule = 0; + return 1; + } + EXPORT_SYMBOL(console_trylock); + ++/* ++ * This is used to help to make sure that certain paths within the VT code are ++ * running with the console lock held. It is definitely not the perfect debug ++ * tool (it is not known if the VT code is the task holding the console lock), ++ * but it helps tracking those weird code paths in the console code such as ++ * when the console is suspended: where the console is not locked but no ++ * console printing may occur. ++ * ++ * Note: This returns true when the console is suspended but is not locked. ++ * This is intentional because the VT code must consider that situation ++ * the same as if the console was locked. ++ */ + int is_console_locked(void) + { +- return console_locked; ++ return (console_kthreads_blocked || atomic_read(&console_kthreads_active)); + } + EXPORT_SYMBOL(is_console_locked); + +-/* +- * Check if the given console is currently capable and allowed to print +- * records. +- * +- * Requires the console_lock. +- */ +-static inline bool console_is_usable(struct console *con) ++static inline bool __console_is_usable(short flags) + { +- if (!(con->flags & CON_ENABLED)) +- return false; +- +- if (!con->write) ++ if (!(flags & CON_ENABLED)) + return false; + + /* +@@ -2650,18 +2898,116 @@ static inline bool console_is_usable(struct console *con) + * cope (CON_ANYTIME) don't call them until this CPU is officially up. + */ + if (!cpu_online(raw_smp_processor_id()) && +- !(con->flags & CON_ANYTIME)) ++ !(flags & CON_ANYTIME)) + return false; + + return true; + } + ++/* ++ * Check if the given console is currently capable and allowed to print ++ * records. ++ * ++ * Requires holding the console_lock. ++ */ ++static inline bool console_is_usable(struct console *con, bool atomic_printing) ++{ ++ if (atomic_printing) { ++#ifdef CONFIG_HAVE_ATOMIC_CONSOLE ++ if (!con->write_atomic) ++ return false; ++ if (!con->atomic_data) ++ return false; ++#else ++ return false; ++#endif ++ } else if (!con->write) { ++ return false; ++ } ++ ++ return __console_is_usable(con->flags); ++} ++ + static void __console_unlock(void) + { +- console_locked = 0; ++ /* ++ * Depending on whether console_lock() or console_trylock() was used, ++ * appropriately allow the kthread printers to continue. ++ */ ++ if (console_kthreads_blocked) ++ console_kthreads_unblock(); ++ else ++ console_kthreads_atomic_unblock(); ++ ++ /* ++ * New records may have arrived while the console was locked. ++ * Wake the kthread printers to print them. ++ */ ++ wake_up_klogd(); ++ + up_console_sem(); + } + ++static u64 read_console_seq(struct console *con) ++{ ++#ifdef CONFIG_HAVE_ATOMIC_CONSOLE ++ unsigned long flags; ++ u64 seq2; ++ u64 seq; ++ ++ if (!con->atomic_data) ++ return con->seq; ++ ++ printk_cpu_sync_get_irqsave(flags); ++ ++ seq = con->seq; ++ seq2 = con->atomic_data[0].seq; ++ if (seq2 > seq) ++ seq = seq2; ++#ifdef CONFIG_HAVE_NMI ++ seq2 = con->atomic_data[1].seq; ++ if (seq2 > seq) ++ seq = seq2; ++#endif ++ ++ printk_cpu_sync_put_irqrestore(flags); ++ ++ return seq; ++#else /* CONFIG_HAVE_ATOMIC_CONSOLE */ ++ return con->seq; ++#endif ++} ++ ++static void write_console_seq(struct console *con, u64 val, bool atomic_printing) ++{ ++#ifdef CONFIG_HAVE_ATOMIC_CONSOLE ++ unsigned long flags; ++ u64 *seq; ++ ++ if (!con->atomic_data) { ++ con->seq = val; ++ return; ++ } ++ ++ printk_cpu_sync_get_irqsave(flags); ++ ++ if (atomic_printing) { ++ seq = &con->atomic_data[0].seq; ++#ifdef CONFIG_HAVE_NMI ++ if (in_nmi()) ++ seq = &con->atomic_data[1].seq; ++#endif ++ } else { ++ seq = &con->seq; ++ } ++ *seq = val; ++ ++ printk_cpu_sync_put_irqrestore(flags); ++#else /* CONFIG_HAVE_ATOMIC_CONSOLE */ ++ con->seq = val; ++#endif ++} ++ + /* + * Print one record for the given console. The record printed is whatever + * record is the next available record for the given console. +@@ -2674,36 +3020,47 @@ static void __console_unlock(void) + * If dropped messages should be printed, @dropped_text is a buffer of size + * DROPPED_TEXT_MAX. Otherwise @dropped_text must be NULL. + * ++ * @atomic_printing specifies if atomic printing should be used. ++ * + * @handover will be set to true if a printk waiter has taken over the + * console_lock, in which case the caller is no longer holding the +- * console_lock. Otherwise it is set to false. ++ * console_lock. Otherwise it is set to false. A NULL pointer may be provided ++ * to disable allowing the console_lock to be taken over by a printk waiter. + * + * Returns false if the given console has no next record to print, otherwise + * true. + * +- * Requires the console_lock. ++ * Requires the console_lock if @handover is non-NULL. ++ * Requires con->lock otherwise. + */ +-static bool console_emit_next_record(struct console *con, char *text, char *ext_text, +- char *dropped_text, bool *handover) ++static bool __console_emit_next_record(struct console *con, char *text, char *ext_text, ++ char *dropped_text, bool atomic_printing, ++ bool *handover) + { +- static int panic_console_dropped; ++ static atomic_t panic_console_dropped = ATOMIC_INIT(0); + struct printk_info info; + struct printk_record r; + unsigned long flags; + char *write_text; + size_t len; ++ u64 seq; + + prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX); + +- *handover = false; ++ if (handover) ++ *handover = false; + +- if (!prb_read_valid(prb, con->seq, &r)) ++ seq = read_console_seq(con); ++ ++ if (!prb_read_valid(prb, seq, &r)) + return false; + +- if (con->seq != r.info->seq) { +- con->dropped += r.info->seq - con->seq; +- con->seq = r.info->seq; +- if (panic_in_progress() && panic_console_dropped++ > 10) { ++ if (seq != r.info->seq) { ++ atomic_long_add((unsigned long)(r.info->seq - seq), &con->dropped); ++ write_console_seq(con, r.info->seq, atomic_printing); ++ seq = r.info->seq; ++ if (panic_in_progress() && ++ atomic_fetch_inc_relaxed(&panic_console_dropped) > 10) { + suppress_panic_printk = 1; + pr_warn_once("Too many dropped messages. Suppress messages on non-panic CPUs to prevent livelock.\n"); + } +@@ -2711,7 +3068,7 @@ static bool console_emit_next_record(struct console *con, char *text, char *ext_ + + /* Skip record that has level above the console loglevel. */ + if (suppress_message_printing(r.info->level)) { +- con->seq++; ++ write_console_seq(con, seq + 1, atomic_printing); + goto skip; + } + +@@ -2725,31 +3082,65 @@ static bool console_emit_next_record(struct console *con, char *text, char *ext_ + len = record_print_text(&r, console_msg_format & MSG_FORMAT_SYSLOG, printk_time); + } + +- /* +- * While actively printing out messages, if another printk() +- * were to occur on another CPU, it may wait for this one to +- * finish. This task can not be preempted if there is a +- * waiter waiting to take over. +- * +- * Interrupts are disabled because the hand over to a waiter +- * must not be interrupted until the hand over is completed +- * (@console_waiter is cleared). +- */ +- printk_safe_enter_irqsave(flags); +- console_lock_spinning_enable(); ++ if (handover) { ++ /* ++ * While actively printing out messages, if another printk() ++ * were to occur on another CPU, it may wait for this one to ++ * finish. This task can not be preempted if there is a ++ * waiter waiting to take over. ++ * ++ * Interrupts are disabled because the hand over to a waiter ++ * must not be interrupted until the hand over is completed ++ * (@console_waiter is cleared). ++ */ ++ printk_safe_enter_irqsave(flags); ++ console_lock_spinning_enable(); + +- stop_critical_timings(); /* don't trace print latency */ +- call_console_driver(con, write_text, len, dropped_text); +- start_critical_timings(); ++ /* don't trace irqsoff print latency */ ++ stop_critical_timings(); ++ } + +- con->seq++; ++ call_console_driver(con, write_text, len, dropped_text, atomic_printing); + +- *handover = console_lock_spinning_disable_and_check(); +- printk_safe_exit_irqrestore(flags); ++ write_console_seq(con, seq + 1, atomic_printing); ++ ++ if (handover) { ++ start_critical_timings(); ++ *handover = console_lock_spinning_disable_and_check(); ++ printk_safe_exit_irqrestore(flags); ++ } + skip: + return true; + } + ++/* ++ * Print a record for a given console, but allow another printk() caller to ++ * take over the console_lock and continue printing. ++ * ++ * Requires the console_lock, but depending on @handover after the call, the ++ * caller may no longer have the console_lock. ++ * ++ * See __console_emit_next_record() for argument and return details. ++ */ ++static bool console_emit_next_record_transferable(struct console *con, char *text, char *ext_text, ++ char *dropped_text, bool *handover) ++{ ++ /* ++ * Handovers are only supported if threaded printers are atomically ++ * blocked. The context taking over the console_lock may be atomic. ++ * ++ * PREEMPT_RT also does not support handovers because the spinning ++ * waiter can cause large latencies. ++ */ ++ if (!console_kthreads_atomically_blocked() || ++ IS_ENABLED(CONFIG_PREEMPT_RT)) { ++ *handover = false; ++ handover = NULL; ++ } ++ ++ return __console_emit_next_record(con, text, ext_text, dropped_text, false, handover); ++} ++ + /* + * Print out all remaining records to all consoles. + * +@@ -2768,8 +3159,8 @@ static bool console_emit_next_record(struct console *con, char *text, char *ext_ + * were flushed to all usable consoles. A returned false informs the caller + * that everything was not flushed (either there were no usable consoles or + * another context has taken over printing or it is a panic situation and this +- * is not the panic CPU). Regardless the reason, the caller should assume it +- * is not useful to immediately try again. ++ * is not the panic CPU or direct printing is not preferred). Regardless the ++ * reason, the caller should assume it is not useful to immediately try again. + * + * Requires the console_lock. + */ +@@ -2786,24 +3177,26 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove + *handover = false; + + do { ++ /* Let the kthread printers do the work if they can. */ ++ if (!allow_direct_printing()) ++ return false; ++ + any_progress = false; + + for_each_console(con) { + bool progress; + +- if (!console_is_usable(con)) ++ if (!console_is_usable(con, false)) + continue; + any_usable = true; + + if (con->flags & CON_EXTENDED) { + /* Extended consoles do not print "dropped messages". */ +- progress = console_emit_next_record(con, &text[0], +- &ext_text[0], NULL, +- handover); ++ progress = console_emit_next_record_transferable(con, &text[0], ++ &ext_text[0], NULL, handover); + } else { +- progress = console_emit_next_record(con, &text[0], +- NULL, &dropped_text[0], +- handover); ++ progress = console_emit_next_record_transferable(con, &text[0], ++ NULL, &dropped_text[0], handover); + } + if (*handover) + return false; +@@ -2828,6 +3221,68 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove + return any_usable; + } + ++#if defined(CONFIG_HAVE_ATOMIC_CONSOLE) && defined(CONFIG_PRINTK) ++static bool console_emit_next_record(struct console *con, char *text, char *ext_text, ++ char *dropped_text, bool atomic_printing); ++ ++static void atomic_console_flush_all(void) ++{ ++ unsigned long flags; ++ struct console *con; ++ bool any_progress; ++ int index = 0; ++ ++ if (console_suspended) ++ return; ++ ++#ifdef CONFIG_HAVE_NMI ++ if (in_nmi()) ++ index = 1; ++#endif ++ ++ printk_cpu_sync_get_irqsave(flags); ++ ++ do { ++ any_progress = false; ++ ++ for_each_console(con) { ++ bool progress; ++ ++ if (!console_is_usable(con, true)) ++ continue; ++ ++ if (con->flags & CON_EXTENDED) { ++ /* Extended consoles do not print "dropped messages". */ ++ progress = console_emit_next_record(con, ++ &con->atomic_data->text[index], ++ &con->atomic_data->ext_text[index], ++ NULL, ++ true); ++ } else { ++ progress = console_emit_next_record(con, ++ &con->atomic_data->text[index], ++ NULL, ++ &con->atomic_data->dropped_text[index], ++ true); ++ } ++ ++ if (!progress) ++ continue; ++ any_progress = true; ++ ++ touch_softlockup_watchdog_sync(); ++ clocksource_touch_watchdog(); ++ rcu_cpu_stall_reset(); ++ touch_nmi_watchdog(); ++ } ++ } while (any_progress); ++ ++ printk_cpu_sync_put_irqrestore(flags); ++} ++#else /* CONFIG_HAVE_ATOMIC_CONSOLE && CONFIG_PRINTK */ ++#define atomic_console_flush_all() ++#endif ++ + /** + * console_unlock - unlock the console system + * +@@ -2918,10 +3373,13 @@ void console_unblank(void) + if (oops_in_progress) { + if (down_trylock_console_sem() != 0) + return; ++ if (!console_kthreads_atomic_tryblock()) { ++ up_console_sem(); ++ return; ++ } + } else + console_lock(); + +- console_locked = 1; + console_may_schedule = 0; + for_each_console(c) + if ((c->flags & CON_ENABLED) && c->unblank) +@@ -2940,6 +3398,11 @@ void console_unblank(void) + */ + void console_flush_on_panic(enum con_flush_mode mode) + { ++ if (mode == CONSOLE_ATOMIC_FLUSH_PENDING) { ++ atomic_console_flush_all(); ++ return; ++ } ++ + /* + * If someone else is holding the console lock, trylock will fail + * and may_schedule may be set. Ignore and proceed to unlock so +@@ -2956,7 +3419,7 @@ void console_flush_on_panic(enum con_flush_mode mode) + + seq = prb_first_valid_seq(prb); + for_each_console(c) +- c->seq = seq; ++ write_console_seq(c, seq, false); + } + console_unlock(); + } +@@ -3196,16 +3659,27 @@ void register_console(struct console *newcon) + console_drivers->next = newcon; + } + +- newcon->dropped = 0; ++ atomic_long_set(&newcon->dropped, 0); ++ newcon->thread = NULL; ++ newcon->blocked = true; ++ mutex_init(&newcon->lock); ++#ifdef CONFIG_HAVE_ATOMIC_CONSOLE ++ newcon->atomic_data = NULL; ++#endif ++ + if (newcon->flags & CON_PRINTBUFFER) { + /* Get a consistent copy of @syslog_seq. */ + mutex_lock(&syslog_lock); +- newcon->seq = syslog_seq; ++ write_console_seq(newcon, syslog_seq, false); + mutex_unlock(&syslog_lock); + } else { + /* Begin with next message. */ +- newcon->seq = prb_next_seq(prb); ++ write_console_seq(newcon, prb_next_seq(prb), false); + } ++ ++ if (printk_kthreads_available) ++ printk_start_kthread(newcon); ++ + console_unlock(); + console_sysfs_notify(); + +@@ -3229,6 +3703,7 @@ EXPORT_SYMBOL(register_console); + + int unregister_console(struct console *console) + { ++ struct task_struct *thd; + struct console *con; + int res; + +@@ -3266,9 +3741,26 @@ int unregister_console(struct console *console) + console_drivers->flags |= CON_CONSDEV; + + console->flags &= ~CON_ENABLED; ++ ++ /* ++ * console->thread can only be cleared under the console lock. But ++ * stopping the thread must be done without the console lock. The ++ * task that clears @thread is the task that stops the kthread. ++ */ ++ thd = console->thread; ++ console->thread = NULL; ++ + console_unlock(); ++ ++ if (thd) ++ kthread_stop(thd); ++ + console_sysfs_notify(); + ++#ifdef CONFIG_HAVE_ATOMIC_CONSOLE ++ free_atomic_data(console->atomic_data); ++#endif ++ + if (console->exit) + res = console->exit(console); + +@@ -3362,6 +3854,20 @@ static int __init printk_late_init(void) + } + late_initcall(printk_late_init); + ++static int __init printk_activate_kthreads(void) ++{ ++ struct console *con; ++ ++ console_lock(); ++ printk_kthreads_available = true; ++ for_each_console(con) ++ printk_start_kthread(con); ++ console_unlock(); ++ ++ return 0; ++} ++early_initcall(printk_activate_kthreads); ++ + #if defined CONFIG_PRINTK + /* If @con is specified, only wait for that console. Otherwise wait for all. */ + static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress) +@@ -3385,7 +3891,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre + for_each_console(c) { + if (con && con != c) + continue; +- if (!console_is_usable(c)) ++ if (!console_is_usable(c, false)) + continue; + printk_seq = c->seq; + if (printk_seq < seq) +@@ -3444,11 +3950,214 @@ static bool pr_flush(int timeout_ms, bool reset_on_progress) + return __pr_flush(NULL, timeout_ms, reset_on_progress); + } + ++static void __printk_fallback_preferred_direct(void) ++{ ++ printk_prefer_direct_enter(); ++ pr_err("falling back to preferred direct printing\n"); ++ printk_kthreads_available = false; ++} ++ ++/* ++ * Enter preferred direct printing, but never exit. Mark console threads as ++ * unavailable. The system is then forever in preferred direct printing and ++ * any printing threads will exit. ++ * ++ * Must *not* be called under console_lock. Use ++ * __printk_fallback_preferred_direct() if already holding console_lock. ++ */ ++static void printk_fallback_preferred_direct(void) ++{ ++ console_lock(); ++ __printk_fallback_preferred_direct(); ++ console_unlock(); ++} ++ ++/* ++ * Print a record for a given console, not allowing another printk() caller ++ * to take over. This is appropriate for contexts that do not have the ++ * console_lock. ++ * ++ * See __console_emit_next_record() for argument and return details. ++ */ ++static bool console_emit_next_record(struct console *con, char *text, char *ext_text, ++ char *dropped_text, bool atomic_printing) ++{ ++ return __console_emit_next_record(con, text, ext_text, dropped_text, ++ atomic_printing, NULL); ++} ++ ++static bool printer_should_wake(struct console *con, u64 seq) ++{ ++ short flags; ++ ++ if (kthread_should_stop() || !printk_kthreads_available) ++ return true; ++ ++ if (con->blocked || ++ console_kthreads_atomically_blocked() || ++ block_console_kthreads || ++ system_state > SYSTEM_RUNNING || ++ oops_in_progress) { ++ return false; ++ } ++ ++ /* ++ * This is an unsafe read from con->flags, but a false positive is ++ * not a problem. Worst case it would allow the printer to wake up ++ * although it is disabled. But the printer will notice that when ++ * attempting to print and instead go back to sleep. ++ */ ++ flags = data_race(READ_ONCE(con->flags)); ++ ++ if (!__console_is_usable(flags)) ++ return false; ++ ++ return prb_read_valid(prb, seq, NULL); ++} ++ ++static int printk_kthread_func(void *data) ++{ ++ struct console *con = data; ++ char *dropped_text = NULL; ++ char *ext_text = NULL; ++ u64 seq = 0; ++ char *text; ++ int error; ++ ++#ifdef CONFIG_HAVE_ATOMIC_CONSOLE ++ if (con->write_atomic) ++ con->atomic_data = alloc_atomic_data(con->flags); ++#endif ++ ++ text = kmalloc(CONSOLE_LOG_MAX, GFP_KERNEL); ++ if (!text) { ++ con_printk(KERN_ERR, con, "failed to allocate text buffer\n"); ++ printk_fallback_preferred_direct(); ++ goto out; ++ } ++ ++ if (con->flags & CON_EXTENDED) { ++ ext_text = kmalloc(CONSOLE_EXT_LOG_MAX, GFP_KERNEL); ++ if (!ext_text) { ++ con_printk(KERN_ERR, con, "failed to allocate ext_text buffer\n"); ++ printk_fallback_preferred_direct(); ++ goto out; ++ } ++ } else { ++ dropped_text = kmalloc(DROPPED_TEXT_MAX, GFP_KERNEL); ++ if (!dropped_text) { ++ con_printk(KERN_ERR, con, "failed to allocate dropped_text buffer\n"); ++ printk_fallback_preferred_direct(); ++ goto out; ++ } ++ } ++ ++ con_printk(KERN_INFO, con, "printing thread started\n"); ++ for (;;) { ++ /* ++ * Guarantee this task is visible on the waitqueue before ++ * checking the wake condition. ++ * ++ * The full memory barrier within set_current_state() of ++ * prepare_to_wait_event() pairs with the full memory barrier ++ * within wq_has_sleeper(). ++ * ++ * This pairs with __wake_up_klogd:A. ++ */ ++ error = wait_event_interruptible(log_wait, ++ printer_should_wake(con, seq)); /* LMM(printk_kthread_func:A) */ ++ ++ if (kthread_should_stop() || !printk_kthreads_available) ++ break; ++ ++ if (error) ++ continue; ++ ++ error = mutex_lock_interruptible(&con->lock); ++ if (error) ++ continue; ++ ++ if (con->blocked || ++ !console_kthread_printing_tryenter()) { ++ /* Another context has locked the console_lock. */ ++ mutex_unlock(&con->lock); ++ continue; ++ } ++ ++ /* ++ * Although this context has not locked the console_lock, it ++ * is known that the console_lock is not locked and it is not ++ * possible for any other context to lock the console_lock. ++ * Therefore it is safe to read con->flags. ++ */ ++ ++ if (!__console_is_usable(con->flags)) { ++ console_kthread_printing_exit(); ++ mutex_unlock(&con->lock); ++ continue; ++ } ++ ++ /* ++ * Even though the printk kthread is always preemptible, it is ++ * still not allowed to call cond_resched() from within ++ * console drivers. The task may become non-preemptible in the ++ * console driver call chain. For example, vt_console_print() ++ * takes a spinlock and then can call into fbcon_redraw(), ++ * which can conditionally invoke cond_resched(). ++ */ ++ console_may_schedule = 0; ++ console_emit_next_record(con, text, ext_text, dropped_text, false); ++ ++ seq = con->seq; ++ ++ console_kthread_printing_exit(); ++ ++ mutex_unlock(&con->lock); ++ } ++ ++ con_printk(KERN_INFO, con, "printing thread stopped\n"); ++out: ++ kfree(dropped_text); ++ kfree(ext_text); ++ kfree(text); ++ ++ console_lock(); ++ /* ++ * If this kthread is being stopped by another task, con->thread will ++ * already be NULL. That is fine. The important thing is that it is ++ * NULL after the kthread exits. ++ */ ++ con->thread = NULL; ++ console_unlock(); ++ ++ return 0; ++} ++ ++/* Must be called under console_lock. */ ++static void printk_start_kthread(struct console *con) ++{ ++ /* ++ * Do not start a kthread if there is no write() callback. The ++ * kthreads assume the write() callback exists. ++ */ ++ if (!con->write) ++ return; ++ ++ con->thread = kthread_run(printk_kthread_func, con, ++ "pr/%s%d", con->name, con->index); ++ if (IS_ERR(con->thread)) { ++ con->thread = NULL; ++ con_printk(KERN_ERR, con, "unable to start printing thread\n"); ++ __printk_fallback_preferred_direct(); ++ return; ++ } ++} ++ + /* + * Delayed printk version, for scheduler-internal messages: + */ +-#define PRINTK_PENDING_WAKEUP 0x01 +-#define PRINTK_PENDING_OUTPUT 0x02 ++#define PRINTK_PENDING_WAKEUP 0x01 ++#define PRINTK_PENDING_DIRECT_OUTPUT 0x02 + + static DEFINE_PER_CPU(int, printk_pending); + +@@ -3456,10 +4165,14 @@ static void wake_up_klogd_work_func(struct irq_work *irq_work) + { + int pending = this_cpu_xchg(printk_pending, 0); + +- if (pending & PRINTK_PENDING_OUTPUT) { ++ if (pending & PRINTK_PENDING_DIRECT_OUTPUT) { ++ printk_prefer_direct_enter(); ++ + /* If trylock fails, someone else is doing the printing */ + if (console_trylock()) + console_unlock(); ++ ++ printk_prefer_direct_exit(); + } + + if (pending & PRINTK_PENDING_WAKEUP) +@@ -3484,10 +4197,11 @@ static void __wake_up_klogd(int val) + * prepare_to_wait_event(), which is called after ___wait_event() adds + * the waiter but before it has checked the wait condition. + * +- * This pairs with devkmsg_read:A and syslog_print:A. ++ * This pairs with devkmsg_read:A, syslog_print:A, and ++ * printk_kthread_func:A. + */ + if (wq_has_sleeper(&log_wait) || /* LMM(__wake_up_klogd:A) */ +- (val & PRINTK_PENDING_OUTPUT)) { ++ (val & PRINTK_PENDING_DIRECT_OUTPUT)) { + this_cpu_or(printk_pending, val); + irq_work_queue(this_cpu_ptr(&wake_up_klogd_work)); + } +@@ -3527,7 +4241,17 @@ void defer_console_output(void) + * New messages may have been added directly to the ringbuffer + * using vprintk_store(), so wake any waiters as well. + */ +- __wake_up_klogd(PRINTK_PENDING_WAKEUP | PRINTK_PENDING_OUTPUT); ++ int val = PRINTK_PENDING_WAKEUP; ++ ++ /* ++ * Make sure that some context will print the messages when direct ++ * printing is allowed. This happens in situations when the kthreads ++ * may not be as reliable or perhaps unusable. ++ */ ++ if (allow_direct_printing()) ++ val |= PRINTK_PENDING_DIRECT_OUTPUT; ++ ++ __wake_up_klogd(val); + } + + void printk_trigger_flush(void) +diff --git a/kernel/printk/printk_safe.c b/kernel/printk/printk_safe.c +index 6d10927a07d8..8e8fd2fb0a5b 100644 +--- a/kernel/printk/printk_safe.c ++++ b/kernel/printk/printk_safe.c +@@ -8,7 +8,9 @@ + #include + #include + #include ++#include + #include ++#include + + #include "internal.h" + +@@ -45,3 +47,33 @@ asmlinkage int vprintk(const char *fmt, va_list args) + return vprintk_default(fmt, args); + } + EXPORT_SYMBOL(vprintk); ++ ++/** ++ * try_block_console_kthreads() - Try to block console kthreads and ++ * make the global console_lock() avaialble ++ * ++ * @timeout_ms: The maximum time (in ms) to wait. ++ * ++ * Prevent console kthreads from starting processing new messages. Wait ++ * until the global console_lock() become available. ++ * ++ * Context: Can be called in any context. ++ */ ++void try_block_console_kthreads(int timeout_ms) ++{ ++ block_console_kthreads = true; ++ ++ /* Do not wait when the console lock could not be safely taken. */ ++ if (this_cpu_read(printk_context) || in_nmi()) ++ return; ++ ++ while (timeout_ms > 0) { ++ if (console_trylock()) { ++ console_unlock(); ++ return; ++ } ++ ++ udelay(1000); ++ timeout_ms -= 1; ++ } ++} +diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c +index 503c2aa845a4..dcd8c0e44c00 100644 +--- a/kernel/rcu/rcutorture.c ++++ b/kernel/rcu/rcutorture.c +@@ -2363,6 +2363,12 @@ static int rcutorture_booster_init(unsigned int cpu) + WARN_ON_ONCE(!t); + sp.sched_priority = 2; + sched_setscheduler_nocheck(t, SCHED_FIFO, &sp); ++#ifdef CONFIG_PREEMPT_RT ++ t = per_cpu(timersd, cpu); ++ WARN_ON_ONCE(!t); ++ sp.sched_priority = 2; ++ sched_setscheduler_nocheck(t, SCHED_FIFO, &sp); ++#endif + } + + /* Don't allow time recalculation while creating a new task. */ +diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h +index 5653560573e2..dcbbcf93d608 100644 +--- a/kernel/rcu/tree_stall.h ++++ b/kernel/rcu/tree_stall.h +@@ -642,6 +642,7 @@ static void print_cpu_stall(unsigned long gps) + * See Documentation/RCU/stallwarn.rst for info on how to debug + * RCU CPU stall warnings. + */ ++ printk_prefer_direct_enter(); + trace_rcu_stall_warning(rcu_state.name, TPS("SelfDetected")); + pr_err("INFO: %s self-detected stall on CPU\n", rcu_state.name); + raw_spin_lock_irqsave_rcu_node(rdp->mynode, flags); +@@ -676,6 +677,7 @@ static void print_cpu_stall(unsigned long gps) + */ + set_tsk_need_resched(current); + set_preempt_need_resched(); ++ printk_prefer_direct_exit(); + } + + static void check_cpu_stall(struct rcu_data *rdp) +diff --git a/kernel/reboot.c b/kernel/reboot.c +index 3bba88c7ffc6..57cedc330660 100644 +--- a/kernel/reboot.c ++++ b/kernel/reboot.c +@@ -82,6 +82,7 @@ void kernel_restart_prepare(char *cmd) + { + blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); + system_state = SYSTEM_RESTART; ++ try_block_console_kthreads(10000); + usermodehelper_disable(); + device_shutdown(); + } +@@ -282,6 +283,7 @@ static void kernel_shutdown_prepare(enum system_states state) + blocking_notifier_call_chain(&reboot_notifier_list, + (state == SYSTEM_HALT) ? SYS_HALT : SYS_POWER_OFF, NULL); + system_state = state; ++ try_block_console_kthreads(10000); + usermodehelper_disable(); + device_shutdown(); + } +@@ -836,9 +838,11 @@ static int __orderly_reboot(void) + ret = run_cmd(reboot_cmd); + + if (ret) { ++ printk_prefer_direct_enter(); + pr_warn("Failed to start orderly reboot: forcing the issue\n"); + emergency_sync(); + kernel_restart(NULL); ++ printk_prefer_direct_exit(); + } + + return ret; +@@ -851,6 +855,7 @@ static int __orderly_poweroff(bool force) + ret = run_cmd(poweroff_cmd); + + if (ret && force) { ++ printk_prefer_direct_enter(); + pr_warn("Failed to start orderly shutdown: forcing the issue\n"); + + /* +@@ -860,6 +865,7 @@ static int __orderly_poweroff(bool force) + */ + emergency_sync(); + kernel_power_off(); ++ printk_prefer_direct_exit(); + } + + return ret; +@@ -917,6 +923,8 @@ EXPORT_SYMBOL_GPL(orderly_reboot); + */ + static void hw_failure_emergency_poweroff_func(struct work_struct *work) + { ++ printk_prefer_direct_enter(); ++ + /* + * We have reached here after the emergency shutdown waiting period has + * expired. This means orderly_poweroff has not been able to shut off +@@ -933,6 +941,8 @@ static void hw_failure_emergency_poweroff_func(struct work_struct *work) + */ + pr_emerg("Hardware protection shutdown failed. Trying emergency restart\n"); + emergency_restart(); ++ ++ printk_prefer_direct_exit(); + } + + static DECLARE_DELAYED_WORK(hw_failure_emergency_poweroff_work, +@@ -971,11 +981,13 @@ void hw_protection_shutdown(const char *reason, int ms_until_forced) + { + static atomic_t allow_proceed = ATOMIC_INIT(1); + ++ printk_prefer_direct_enter(); ++ + pr_emerg("HARDWARE PROTECTION shutdown (%s)\n", reason); + + /* Shutdown should be initiated only once. */ + if (!atomic_dec_and_test(&allow_proceed)) +- return; ++ goto out; + + /* + * Queue a backup emergency shutdown in the event of +@@ -983,6 +995,8 @@ void hw_protection_shutdown(const char *reason, int ms_until_forced) + */ + hw_failure_emergency_poweroff(ms_until_forced); + orderly_poweroff(true); ++out: ++ printk_prefer_direct_exit(); + } + EXPORT_SYMBOL_GPL(hw_protection_shutdown); + +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index 0f6a92737c91..f26292bed420 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -1040,6 +1040,46 @@ void resched_curr(struct rq *rq) + trace_sched_wake_idle_without_ipi(cpu); + } + ++#ifdef CONFIG_PREEMPT_LAZY ++ ++static int tsk_is_polling(struct task_struct *p) ++{ ++#ifdef TIF_POLLING_NRFLAG ++ return test_tsk_thread_flag(p, TIF_POLLING_NRFLAG); ++#else ++ return 0; ++#endif ++} ++ ++void resched_curr_lazy(struct rq *rq) ++{ ++ struct task_struct *curr = rq->curr; ++ int cpu; ++ ++ if (!sched_feat(PREEMPT_LAZY)) { ++ resched_curr(rq); ++ return; ++ } ++ ++ if (test_tsk_need_resched(curr)) ++ return; ++ ++ if (test_tsk_need_resched_lazy(curr)) ++ return; ++ ++ set_tsk_need_resched_lazy(curr); ++ ++ cpu = cpu_of(rq); ++ if (cpu == smp_processor_id()) ++ return; ++ ++ /* NEED_RESCHED_LAZY must be visible before we test polling */ ++ smp_mb(); ++ if (!tsk_is_polling(curr)) ++ smp_send_reschedule(cpu); ++} ++#endif ++ + void resched_cpu(int cpu) + { + struct rq *rq = cpu_rq(cpu); +@@ -2224,6 +2264,7 @@ void migrate_disable(void) + preempt_disable(); + this_rq()->nr_pinned++; + p->migration_disabled = 1; ++ preempt_lazy_disable(); + preempt_enable(); + } + EXPORT_SYMBOL_GPL(migrate_disable); +@@ -2255,6 +2296,7 @@ void migrate_enable(void) + barrier(); + p->migration_disabled = 0; + this_rq()->nr_pinned--; ++ preempt_lazy_enable(); + preempt_enable(); + } + EXPORT_SYMBOL_GPL(migrate_enable); +@@ -3277,6 +3319,76 @@ int migrate_swap(struct task_struct *cur, struct task_struct *p, + } + #endif /* CONFIG_NUMA_BALANCING */ + ++#ifdef CONFIG_PREEMPT_RT ++ ++/* ++ * Consider: ++ * ++ * set_special_state(X); ++ * ++ * do_things() ++ * // Somewhere in there is an rtlock that can be contended: ++ * current_save_and_set_rtlock_wait_state(); ++ * [...] ++ * schedule_rtlock(); (A) ++ * [...] ++ * current_restore_rtlock_saved_state(); ++ * ++ * schedule(); (B) ++ * ++ * If p->saved_state is anything else than TASK_RUNNING, then p blocked on an ++ * rtlock (A) *before* voluntarily calling into schedule() (B) after setting its ++ * state to X. For things like ptrace (X=TASK_TRACED), the task could have more ++ * work to do upon acquiring the lock in do_things() before whoever called ++ * wait_task_inactive() should return. IOW, we have to wait for: ++ * ++ * p.saved_state = TASK_RUNNING ++ * p.__state = X ++ * ++ * which implies the task isn't blocked on an RT lock and got to schedule() (B). ++ * ++ * Also see comments in ttwu_state_match(). ++ */ ++ ++static __always_inline bool state_mismatch(struct task_struct *p, unsigned int match_state) ++{ ++ unsigned long flags; ++ bool mismatch; ++ ++ raw_spin_lock_irqsave(&p->pi_lock, flags); ++ if (READ_ONCE(p->__state) & match_state) ++ mismatch = false; ++ else if (READ_ONCE(p->saved_state) & match_state) ++ mismatch = false; ++ else ++ mismatch = true; ++ ++ raw_spin_unlock_irqrestore(&p->pi_lock, flags); ++ return mismatch; ++} ++static __always_inline bool state_match(struct task_struct *p, unsigned int match_state, ++ bool *wait) ++{ ++ if (READ_ONCE(p->__state) & match_state) ++ return true; ++ if (READ_ONCE(p->saved_state) & match_state) { ++ *wait = true; ++ return true; ++ } ++ return false; ++} ++#else ++static __always_inline bool state_mismatch(struct task_struct *p, unsigned int match_state) ++{ ++ return !(READ_ONCE(p->__state) & match_state); ++} ++static __always_inline bool state_match(struct task_struct *p, unsigned int match_state, ++ bool *wait) ++{ ++ return (READ_ONCE(p->__state) & match_state); ++} ++#endif ++ + /* + * wait_task_inactive - wait for a thread to unschedule. + * +@@ -3295,7 +3407,7 @@ int migrate_swap(struct task_struct *cur, struct task_struct *p, + */ + unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state) + { +- int running, queued; ++ bool running, wait; + struct rq_flags rf; + unsigned long ncsw; + struct rq *rq; +@@ -3321,7 +3433,7 @@ unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state + * is actually now running somewhere else! + */ + while (task_on_cpu(rq, p)) { +- if (!(READ_ONCE(p->__state) & match_state)) ++ if (state_mismatch(p, match_state)) + return 0; + cpu_relax(); + } +@@ -3334,9 +3446,10 @@ unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state + rq = task_rq_lock(p, &rf); + trace_sched_wait_task(p); + running = task_on_cpu(rq, p); +- queued = task_on_rq_queued(p); ++ wait = task_on_rq_queued(p); + ncsw = 0; +- if (READ_ONCE(p->__state) & match_state) ++ ++ if (state_match(p, match_state, &wait)) + ncsw = p->nvcsw | LONG_MIN; /* sets MSB */ + task_rq_unlock(rq, p, &rf); + +@@ -3366,7 +3479,7 @@ unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state + * running right now), it's preempted, and we should + * yield - it could be a while. + */ +- if (unlikely(queued)) { ++ if (unlikely(wait)) { + ktime_t to = NSEC_PER_SEC / HZ; + + set_current_state(TASK_UNINTERRUPTIBLE); +@@ -4647,6 +4760,9 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p) + p->on_cpu = 0; + #endif + init_task_preempt_count(p); ++#ifdef CONFIG_HAVE_PREEMPT_LAZY ++ task_thread_info(p)->preempt_lazy_count = 0; ++#endif + #ifdef CONFIG_SMP + plist_node_init(&p->pushable_tasks, MAX_PRIO); + RB_CLEAR_NODE(&p->pushable_dl_tasks); +@@ -6517,6 +6633,7 @@ static void __sched notrace __schedule(unsigned int sched_mode) + + next = pick_next_task(rq, prev, &rf); + clear_tsk_need_resched(prev); ++ clear_tsk_need_resched_lazy(prev); + clear_preempt_need_resched(); + #ifdef CONFIG_SCHED_DEBUG + rq->last_seen_need_resched_ns = 0; +@@ -6731,6 +6848,30 @@ static void __sched notrace preempt_schedule_common(void) + } while (need_resched()); + } + ++#ifdef CONFIG_PREEMPT_LAZY ++/* ++ * If TIF_NEED_RESCHED is then we allow to be scheduled away since this is ++ * set by a RT task. Oterwise we try to avoid beeing scheduled out as long as ++ * preempt_lazy_count counter >0. ++ */ ++static __always_inline int preemptible_lazy(void) ++{ ++ if (test_thread_flag(TIF_NEED_RESCHED)) ++ return 1; ++ if (current_thread_info()->preempt_lazy_count) ++ return 0; ++ return 1; ++} ++ ++#else ++ ++static inline int preemptible_lazy(void) ++{ ++ return 1; ++} ++ ++#endif ++ + #ifdef CONFIG_PREEMPTION + /* + * This is the entry point to schedule() from in-kernel preemption +@@ -6744,6 +6885,8 @@ asmlinkage __visible void __sched notrace preempt_schedule(void) + */ + if (likely(!preemptible())) + return; ++ if (!preemptible_lazy()) ++ return; + preempt_schedule_common(); + } + NOKPROBE_SYMBOL(preempt_schedule); +@@ -6791,6 +6934,9 @@ asmlinkage __visible void __sched notrace preempt_schedule_notrace(void) + if (likely(!preemptible())) + return; + ++ if (!preemptible_lazy()) ++ return; ++ + do { + /* + * Because the function tracer can trace preempt_count_sub() +@@ -9056,7 +9202,9 @@ void __init init_idle(struct task_struct *idle, int cpu) + + /* Set the preempt count _outside_ the spinlocks! */ + init_idle_preempt_count(idle, cpu); +- ++#ifdef CONFIG_HAVE_PREEMPT_LAZY ++ task_thread_info(idle)->preempt_lazy_count = 0; ++#endif + /* + * The idle tasks have their own, simple scheduling class: + */ +diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c +index 612873ec2197..9493473e8e16 100644 +--- a/kernel/sched/fair.c ++++ b/kernel/sched/fair.c +@@ -4930,7 +4930,7 @@ check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr) + ideal_runtime = sched_slice(cfs_rq, curr); + delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime; + if (delta_exec > ideal_runtime) { +- resched_curr(rq_of(cfs_rq)); ++ resched_curr_lazy(rq_of(cfs_rq)); + /* + * The current task ran long enough, ensure it doesn't get + * re-elected due to buddy favours. +@@ -4954,7 +4954,7 @@ check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr) + return; + + if (delta > ideal_runtime) +- resched_curr(rq_of(cfs_rq)); ++ resched_curr_lazy(rq_of(cfs_rq)); + } + + static void +@@ -5100,7 +5100,7 @@ entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued) + * validating it and just reschedule. + */ + if (queued) { +- resched_curr(rq_of(cfs_rq)); ++ resched_curr_lazy(rq_of(cfs_rq)); + return; + } + /* +@@ -5249,7 +5249,7 @@ static void __account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec) + * hierarchy can be throttled + */ + if (!assign_cfs_rq_runtime(cfs_rq) && likely(cfs_rq->curr)) +- resched_curr(rq_of(cfs_rq)); ++ resched_curr_lazy(rq_of(cfs_rq)); + } + + static __always_inline +@@ -6000,7 +6000,7 @@ static void hrtick_start_fair(struct rq *rq, struct task_struct *p) + + if (delta < 0) { + if (task_current(rq, p)) +- resched_curr(rq); ++ resched_curr_lazy(rq); + return; + } + hrtick_start(rq, delta); +@@ -7729,7 +7729,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ + return; + + preempt: +- resched_curr(rq); ++ resched_curr_lazy(rq); + /* + * Only set the backward buddy when the current task is still + * on the rq. This can happen when a wakeup gets interleaved +@@ -11892,7 +11892,7 @@ static void task_fork_fair(struct task_struct *p) + * 'current' within the tree based on its new key value. + */ + swap(curr->vruntime, se->vruntime); +- resched_curr(rq); ++ resched_curr_lazy(rq); + } + + se->vruntime -= cfs_rq->min_vruntime; +@@ -11919,7 +11919,7 @@ prio_changed_fair(struct rq *rq, struct task_struct *p, int oldprio) + */ + if (task_current(rq, p)) { + if (p->prio > oldprio) +- resched_curr(rq); ++ resched_curr_lazy(rq); + } else + check_preempt_curr(rq, p, 0); + } +diff --git a/kernel/sched/features.h b/kernel/sched/features.h +index ee7f23c76bd3..e13090e33f3c 100644 +--- a/kernel/sched/features.h ++++ b/kernel/sched/features.h +@@ -48,6 +48,9 @@ SCHED_FEAT(NONTASK_CAPACITY, true) + + #ifdef CONFIG_PREEMPT_RT + SCHED_FEAT(TTWU_QUEUE, false) ++# ifdef CONFIG_PREEMPT_LAZY ++SCHED_FEAT(PREEMPT_LAZY, true) ++# endif + #else + + /* +diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h +index b62d53d7c264..f2577f511a41 100644 +--- a/kernel/sched/sched.h ++++ b/kernel/sched/sched.h +@@ -2350,6 +2350,15 @@ extern void reweight_task(struct task_struct *p, int prio); + extern void resched_curr(struct rq *rq); + extern void resched_cpu(int cpu); + ++#ifdef CONFIG_PREEMPT_LAZY ++extern void resched_curr_lazy(struct rq *rq); ++#else ++static inline void resched_curr_lazy(struct rq *rq) ++{ ++ resched_curr(rq); ++} ++#endif ++ + extern struct rt_bandwidth def_rt_bandwidth; + extern void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime); + extern bool sched_rt_bandwidth_account(struct rt_rq *rt_rq); +diff --git a/kernel/signal.c b/kernel/signal.c +index 5d45f5da2b36..58e919c7c936 100644 +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -2302,13 +2302,13 @@ static int ptrace_stop(int exit_code, int why, unsigned long message, + /* + * Don't want to allow preemption here, because + * sys_ptrace() needs this task to be inactive. +- * +- * XXX: implement read_unlock_no_resched(). + */ +- preempt_disable(); ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) ++ preempt_disable(); + read_unlock(&tasklist_lock); + cgroup_enter_frozen(); +- preempt_enable_no_resched(); ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) ++ preempt_enable_no_resched(); + schedule(); + cgroup_leave_frozen(true); + +diff --git a/kernel/softirq.c b/kernel/softirq.c +index c8a6913c067d..82f3e68fbe22 100644 +--- a/kernel/softirq.c ++++ b/kernel/softirq.c +@@ -637,6 +637,24 @@ static inline void tick_irq_exit(void) + #endif + } + ++#ifdef CONFIG_PREEMPT_RT ++DEFINE_PER_CPU(struct task_struct *, timersd); ++DEFINE_PER_CPU(unsigned long, pending_timer_softirq); ++ ++static void wake_timersd(void) ++{ ++ struct task_struct *tsk = __this_cpu_read(timersd); ++ ++ if (tsk) ++ wake_up_process(tsk); ++} ++ ++#else ++ ++static inline void wake_timersd(void) { } ++ ++#endif ++ + static inline void __irq_exit_rcu(void) + { + #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED +@@ -649,6 +667,10 @@ static inline void __irq_exit_rcu(void) + if (!in_interrupt() && local_softirq_pending()) + invoke_softirq(); + ++ if (IS_ENABLED(CONFIG_PREEMPT_RT) && local_pending_timers() && ++ !(in_nmi() | in_hardirq())) ++ wake_timersd(); ++ + tick_irq_exit(); + } + +@@ -976,12 +998,70 @@ static struct smp_hotplug_thread softirq_threads = { + .thread_comm = "ksoftirqd/%u", + }; + ++#ifdef CONFIG_PREEMPT_RT ++static void timersd_setup(unsigned int cpu) ++{ ++ sched_set_fifo_low(current); ++} ++ ++static int timersd_should_run(unsigned int cpu) ++{ ++ return local_pending_timers(); ++} ++ ++static void run_timersd(unsigned int cpu) ++{ ++ unsigned int timer_si; ++ ++ ksoftirqd_run_begin(); ++ ++ timer_si = local_pending_timers(); ++ __this_cpu_write(pending_timer_softirq, 0); ++ or_softirq_pending(timer_si); ++ ++ __do_softirq(); ++ ++ ksoftirqd_run_end(); ++} ++ ++static void raise_ktimers_thread(unsigned int nr) ++{ ++ trace_softirq_raise(nr); ++ __this_cpu_or(pending_timer_softirq, 1 << nr); ++} ++ ++void raise_hrtimer_softirq(void) ++{ ++ raise_ktimers_thread(HRTIMER_SOFTIRQ); ++} ++ ++void raise_timer_softirq(void) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ raise_ktimers_thread(TIMER_SOFTIRQ); ++ wake_timersd(); ++ local_irq_restore(flags); ++} ++ ++static struct smp_hotplug_thread timer_threads = { ++ .store = &timersd, ++ .setup = timersd_setup, ++ .thread_should_run = timersd_should_run, ++ .thread_fn = run_timersd, ++ .thread_comm = "ktimers/%u", ++}; ++#endif ++ + static __init int spawn_ksoftirqd(void) + { + cpuhp_setup_state_nocalls(CPUHP_SOFTIRQ_DEAD, "softirq:dead", NULL, + takeover_tasklets); + BUG_ON(smpboot_register_percpu_thread(&softirq_threads)); +- ++#ifdef CONFIG_PREEMPT_RT ++ BUG_ON(smpboot_register_percpu_thread(&timer_threads)); ++#endif + return 0; + } + early_initcall(spawn_ksoftirqd); +diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c +index e4f0e3b0c4f4..9e2cfba065de 100644 +--- a/kernel/time/hrtimer.c ++++ b/kernel/time/hrtimer.c +@@ -1805,7 +1805,7 @@ void hrtimer_interrupt(struct clock_event_device *dev) + if (!ktime_before(now, cpu_base->softirq_expires_next)) { + cpu_base->softirq_expires_next = KTIME_MAX; + cpu_base->softirq_activated = 1; +- raise_softirq_irqoff(HRTIMER_SOFTIRQ); ++ raise_hrtimer_softirq(); + } + + __hrtimer_run_queues(cpu_base, now, flags, HRTIMER_ACTIVE_HARD); +@@ -1918,7 +1918,7 @@ void hrtimer_run_queues(void) + if (!ktime_before(now, cpu_base->softirq_expires_next)) { + cpu_base->softirq_expires_next = KTIME_MAX; + cpu_base->softirq_activated = 1; +- raise_softirq_irqoff(HRTIMER_SOFTIRQ); ++ raise_hrtimer_softirq(); + } + + __hrtimer_run_queues(cpu_base, now, flags, HRTIMER_ACTIVE_HARD); +diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c +index 798e1841d286..b52e1861b913 100644 +--- a/kernel/time/tick-sched.c ++++ b/kernel/time/tick-sched.c +@@ -800,7 +800,7 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now) + + static inline bool local_timer_softirq_pending(void) + { +- return local_softirq_pending() & BIT(TIMER_SOFTIRQ); ++ return local_pending_timers() & BIT(TIMER_SOFTIRQ); + } + + static ktime_t tick_nohz_next_event(struct tick_sched *ts, int cpu) +diff --git a/kernel/time/timer.c b/kernel/time/timer.c +index 717fcb9fb14a..e6219da89933 100644 +--- a/kernel/time/timer.c ++++ b/kernel/time/timer.c +@@ -1822,7 +1822,7 @@ static void run_local_timers(void) + if (time_before(jiffies, base->next_expiry)) + return; + } +- raise_softirq(TIMER_SOFTIRQ); ++ raise_timer_softirq(); + } + + /* +diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c +index 9db92a6e1463..0efcfac975b6 100644 +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -2658,11 +2658,19 @@ unsigned int tracing_gen_ctx_irq_test(unsigned int irqs_status) + if (softirq_count() >> (SOFTIRQ_SHIFT + 1)) + trace_flags |= TRACE_FLAG_BH_OFF; + +- if (tif_need_resched()) ++ if (tif_need_resched_now()) + trace_flags |= TRACE_FLAG_NEED_RESCHED; ++#ifdef CONFIG_PREEMPT_LAZY ++ /* Run out of bits. Share the LAZY and PREEMPT_RESCHED */ ++ if (need_resched_lazy()) ++ trace_flags |= TRACE_FLAG_NEED_RESCHED_LAZY; ++#else + if (test_preempt_need_resched()) + trace_flags |= TRACE_FLAG_PREEMPT_RESCHED; +- return (trace_flags << 16) | (min_t(unsigned int, pc & 0xff, 0xf)) | ++#endif ++ ++ return (trace_flags << 24) | (min_t(unsigned int, pc & 0xff, 0xf)) | ++ (preempt_lazy_count() & 0xff) << 16 | + (min_t(unsigned int, migration_disable_value(), 0xf)) << 4; + } + +@@ -4255,15 +4263,17 @@ unsigned long trace_total_entries(struct trace_array *tr) + + static void print_lat_help_header(struct seq_file *m) + { +- seq_puts(m, "# _------=> CPU# \n" +- "# / _-----=> irqs-off/BH-disabled\n" +- "# | / _----=> need-resched \n" +- "# || / _---=> hardirq/softirq \n" +- "# ||| / _--=> preempt-depth \n" +- "# |||| / _-=> migrate-disable \n" +- "# ||||| / delay \n" +- "# cmd pid |||||| time | caller \n" +- "# \\ / |||||| \\ | / \n"); ++ seq_puts(m, "# _--------=> CPU# \n" ++ "# / _-------=> irqs-off/BH-disabled\n" ++ "# | / _------=> need-resched \n" ++ "# || / _-----=> need-resched-lazy\n" ++ "# ||| / _----=> hardirq/softirq \n" ++ "# |||| / _---=> preempt-depth \n" ++ "# ||||| / _--=> preempt-lazy-depth\n" ++ "# |||||| / _-=> migrate-disable \n" ++ "# ||||||| / delay \n" ++ "# cmd pid |||||||| time | caller \n" ++ "# \\ / |||||||| \\ | / \n"); + } + + static void print_event_info(struct array_buffer *buf, struct seq_file *m) +@@ -4297,14 +4307,16 @@ static void print_func_help_header_irq(struct array_buffer *buf, struct seq_file + + print_event_info(buf, m); + +- seq_printf(m, "# %.*s _-----=> irqs-off/BH-disabled\n", prec, space); +- seq_printf(m, "# %.*s / _----=> need-resched\n", prec, space); +- seq_printf(m, "# %.*s| / _---=> hardirq/softirq\n", prec, space); +- seq_printf(m, "# %.*s|| / _--=> preempt-depth\n", prec, space); +- seq_printf(m, "# %.*s||| / _-=> migrate-disable\n", prec, space); +- seq_printf(m, "# %.*s|||| / delay\n", prec, space); +- seq_printf(m, "# TASK-PID %.*s CPU# ||||| TIMESTAMP FUNCTION\n", prec, " TGID "); +- seq_printf(m, "# | | %.*s | ||||| | |\n", prec, " | "); ++ seq_printf(m, "# %.*s _-------=> irqs-off/BH-disabled\n", prec, space); ++ seq_printf(m, "# %.*s / _------=> need-resched\n", prec, space); ++ seq_printf(m, "# %.*s| / _-----=> need-resched-lazy\n", prec, space); ++ seq_printf(m, "# %.*s|| / _----=> hardirq/softirq\n", prec, space); ++ seq_printf(m, "# %.*s||| / _---=> preempt-depth\n", prec, space); ++ seq_printf(m, "# %.*s|||| / _--=> preempt-lazy-depth\n", prec, space); ++ seq_printf(m, "# %.*s||||| / _-=> migrate-disable\n", prec, space); ++ seq_printf(m, "# %.*s|||||| / delay\n", prec, space); ++ seq_printf(m, "# TASK-PID %.*s CPU# ||||||| TIMESTAMP FUNCTION\n", prec, " TGID "); ++ seq_printf(m, "# | | %.*s | ||||||| | |\n", prec, " | "); + } + + void +diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c +index 9da418442a06..1d8c513d1121 100644 +--- a/kernel/trace/trace_events.c ++++ b/kernel/trace/trace_events.c +@@ -208,6 +208,7 @@ static int trace_define_common_fields(void) + /* Holds both preempt_count and migrate_disable */ + __common_field(unsigned char, preempt_count); + __common_field(int, pid); ++ __common_field(unsigned char, preempt_lazy_count); + + return ret; + } +diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c +index 5cd4fb656306..3c227e2843ae 100644 +--- a/kernel/trace/trace_output.c ++++ b/kernel/trace/trace_output.c +@@ -442,6 +442,7 @@ int trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry) + { + char hardsoft_irq; + char need_resched; ++ char need_resched_lazy; + char irqs_off; + int hardirq; + int softirq; +@@ -462,20 +463,27 @@ int trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry) + + switch (entry->flags & (TRACE_FLAG_NEED_RESCHED | + TRACE_FLAG_PREEMPT_RESCHED)) { ++#ifndef CONFIG_PREEMPT_LAZY + case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_PREEMPT_RESCHED: + need_resched = 'N'; + break; ++#endif + case TRACE_FLAG_NEED_RESCHED: + need_resched = 'n'; + break; ++#ifndef CONFIG_PREEMPT_LAZY + case TRACE_FLAG_PREEMPT_RESCHED: + need_resched = 'p'; + break; ++#endif + default: + need_resched = '.'; + break; + } + ++ need_resched_lazy = ++ (entry->flags & TRACE_FLAG_NEED_RESCHED_LAZY) ? 'L' : '.'; ++ + hardsoft_irq = + (nmi && hardirq) ? 'Z' : + nmi ? 'z' : +@@ -484,14 +492,20 @@ int trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry) + softirq ? 's' : + '.' ; + +- trace_seq_printf(s, "%c%c%c", +- irqs_off, need_resched, hardsoft_irq); ++ trace_seq_printf(s, "%c%c%c%c", ++ irqs_off, need_resched, need_resched_lazy, ++ hardsoft_irq); + + if (entry->preempt_count & 0xf) + trace_seq_printf(s, "%x", entry->preempt_count & 0xf); + else + trace_seq_putc(s, '.'); + ++ if (entry->preempt_lazy_count) ++ trace_seq_printf(s, "%x", entry->preempt_lazy_count); ++ else ++ trace_seq_putc(s, '.'); ++ + if (entry->preempt_count & 0xf0) + trace_seq_printf(s, "%x", entry->preempt_count >> 4); + else +diff --git a/kernel/watchdog.c b/kernel/watchdog.c +index 8e61f21e7e33..41596c415111 100644 +--- a/kernel/watchdog.c ++++ b/kernel/watchdog.c +@@ -424,6 +424,8 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) + /* Start period for the next softlockup warning. */ + update_report_ts(); + ++ printk_prefer_direct_enter(); ++ + pr_emerg("BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n", + smp_processor_id(), duration, + current->comm, task_pid_nr(current)); +@@ -442,6 +444,8 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) + add_taint(TAINT_SOFTLOCKUP, LOCKDEP_STILL_OK); + if (softlockup_panic) + panic("softlockup: hung tasks"); ++ ++ printk_prefer_direct_exit(); + } + + return HRTIMER_RESTART; +diff --git a/kernel/watchdog_hld.c b/kernel/watchdog_hld.c +index 1e8a49dc956e..7c977b945c92 100644 +--- a/kernel/watchdog_hld.c ++++ b/kernel/watchdog_hld.c +@@ -135,6 +135,8 @@ static void watchdog_overflow_callback(struct perf_event *event, + if (__this_cpu_read(hard_watchdog_warn) == true) + return; + ++ printk_prefer_direct_enter(); ++ + pr_emerg("Watchdog detected hard LOCKUP on cpu %d\n", + this_cpu); + print_modules(); +@@ -155,6 +157,8 @@ static void watchdog_overflow_callback(struct perf_event *event, + if (hardlockup_panic) + nmi_panic(regs, "Hard LOCKUP"); + ++ printk_prefer_direct_exit(); ++ + __this_cpu_write(hard_watchdog_warn, true); + return; + } +-- +2.34.1 + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0007-drivers-add-NXP-RT-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0007-drivers-add-NXP-RT-support.patch new file mode 100644 index 000000000..023bdfd1a --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0007-drivers-add-NXP-RT-support.patch @@ -0,0 +1,21976 @@ +From cb81c271e9e609d9dbd19d5d26a298c723c30225 Mon Sep 17 00:00:00 2001 +From: Mike Engel +Date: Sat, 24 Feb 2024 18:16:35 +0100 +Subject: [PATCH 07/10] drivers: add NXP RT support + +Signed-off-by: Mike Engel +--- + drivers/base/Kconfig | 2 +- + drivers/block/zram/zram_drv.c | 36 + + drivers/block/zram/zram_drv.h | 3 + + drivers/char/tpm/tpm_tis.c | 29 +- + drivers/clk/imx/Makefile | 1 + + drivers/clk/imx/clk-frac-pll.c | 130 + + drivers/clk/imx/clk-fracn-gppll.c | 208 +- + drivers/clk/imx/clk-imx8dxl-acm.c | 49 + + drivers/clk/imx/clk-imx8mq.c | 2 +- + drivers/clk/imx/clk-pll.c | 83 + + drivers/clk/imx/clk-pll.h | 33 + + drivers/clk/imx/clk-pll14xx.c | 120 + + drivers/clk/imx/clk-pllv3.c | 109 + + drivers/clk/imx/clk-scu.c | 86 + + drivers/firmware/imx/scu-pd.c | 1 + + drivers/gpu/drm/i915/Kconfig | 1 - + drivers/gpu/drm/i915/display/intel_crtc.c | 15 +- + drivers/gpu/drm/i915/gt/intel_breadcrumbs.c | 5 +- + .../drm/i915/gt/intel_execlists_submission.c | 17 +- + drivers/gpu/drm/i915/i915_irq.c | 6 +- + drivers/gpu/drm/i915/i915_request.c | 2 - + drivers/gpu/drm/i915/i915_trace.h | 6 +- + drivers/gpu/drm/i915/i915_utils.h | 2 +- + drivers/irqchip/Kconfig | 4 + + drivers/irqchip/Makefile | 1 + + drivers/irqchip/ipi-baremetal.c | 454 +++ + drivers/irqchip/irq-gic-v3.c | 10 +- + drivers/irqchip/irq-gic.c | 4 +- + drivers/mailbox/Kconfig | 9 + + drivers/mailbox/Makefile | 2 + + drivers/mailbox/generic-software-mailbox.c | 322 ++ + drivers/mailbox/mailbox.c | 2 +- + drivers/mxc/ipu3/ipu_common.c | 10 +- + drivers/mxc/ipu3/ipu_device.c | 2 +- + drivers/net/dsa/Kconfig | 2 + + drivers/net/dsa/Makefile | 1 + + drivers/net/dsa/netc/Kconfig | 10 + + drivers/net/dsa/netc/Makefile | 12 + + drivers/net/dsa/netc/netc.h | 102 + + drivers/net/dsa/netc/netc_config.c | 322 ++ + drivers/net/dsa/netc/netc_config.h | 288 ++ + drivers/net/dsa/netc/netc_devlink.c | 111 + + drivers/net/dsa/netc/netc_ethtool.c | 344 ++ + drivers/net/dsa/netc/netc_main.c | 928 +++++ + drivers/net/dsa/netc/netc_ptp.c | 12 + + drivers/net/dsa/netc/netc_spi.c | 119 + + drivers/net/dsa/ocelot/felix.c | 184 +- + drivers/net/dsa/ocelot/felix.h | 5 + + drivers/net/dsa/ocelot/felix_tsn.c | 6 +- + drivers/net/dsa/ocelot/felix_vsc9959.c | 101 + + drivers/net/dsa/sja1105/sja1105.h | 5 + + drivers/net/dsa/sja1105/sja1105_clocking.c | 21 +- + drivers/net/dsa/sja1105/sja1105_main.c | 64 +- + drivers/net/dsa/sja1105/sja1105_tas.c | 4 + + drivers/net/ethernet/alacritech/slic.h | 12 +- + drivers/net/ethernet/amazon/ena/ena_ethtool.c | 4 +- + drivers/net/ethernet/amazon/ena/ena_netdev.c | 12 +- + .../net/ethernet/aquantia/atlantic/aq_main.c | 1 + + .../net/ethernet/aquantia/atlantic/aq_ring.c | 8 +- + drivers/net/ethernet/asix/ax88796c_main.c | 4 +- + drivers/net/ethernet/broadcom/b44.c | 8 +- + drivers/net/ethernet/broadcom/bcmsysport.c | 12 +- + .../ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.h | 2 +- + drivers/net/ethernet/cortina/gemini.c | 24 +- + .../net/ethernet/emulex/benet/be_ethtool.c | 12 +- + drivers/net/ethernet/emulex/benet/be_main.c | 16 +- + drivers/net/ethernet/freescale/Kconfig | 20 + + drivers/net/ethernet/freescale/Makefile | 2 + + drivers/net/ethernet/freescale/enetc/enetc.h | 5 + + .../ethernet/freescale/enetc/enetc_ethtool.c | 152 + + .../net/ethernet/freescale/enetc/enetc_hw.h | 6 + + .../net/ethernet/freescale/enetc/enetc_pf.c | 16 +- + .../net/ethernet/freescale/enetc/enetc_ptp.c | 15 +- + .../net/ethernet/freescale/enetc/enetc_tsn.c | 194 +- + drivers/net/ethernet/freescale/fec.h | 100 +- + drivers/net/ethernet/freescale/fec_ecat.c | 3008 +++++++++++++++++ + drivers/net/ethernet/freescale/fec_ecat.h | 713 ++++ + drivers/net/ethernet/freescale/fec_main.c | 1460 +++++++- + drivers/net/ethernet/freescale/fec_ptp.c | 451 ++- + drivers/net/ethernet/freescale/fec_uio.c | 4 +- + .../net/ethernet/freescale/gianfar_ethtool.c | 29 + + .../net/ethernet/freescale/sdk_dpaa/Kconfig | 11 +- + .../net/ethernet/freescale/sdk_dpaa/Makefile | 3 + + .../ethernet/freescale/sdk_dpaa/dpaa_eth.h | 13 +- + .../freescale/sdk_dpaa/dpaa_eth_common.c | 56 + + .../ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c | 28 +- + .../freescale/sdk_dpaa/dpaa_ethercat.c | 1217 +++++++ + .../ethernet/fungible/funeth/funeth_txrx.h | 4 +- + drivers/net/ethernet/google/gve/gve_ethtool.c | 16 +- + drivers/net/ethernet/google/gve/gve_main.c | 12 +- + drivers/net/ethernet/hisilicon/hns3/hnae3.h | 1 + + .../net/ethernet/hisilicon/hns3/hns3_enet.c | 5 +- + drivers/net/ethernet/huawei/hinic/hinic_rx.c | 4 +- + drivers/net/ethernet/huawei/hinic/hinic_tx.c | 4 +- + .../net/ethernet/intel/fm10k/fm10k_netdev.c | 8 +- + drivers/net/ethernet/intel/i40e/i40e.h | 1 + + .../net/ethernet/intel/i40e/i40e_ethtool.c | 8 +- + drivers/net/ethernet/intel/i40e/i40e_main.c | 20 +- + drivers/net/ethernet/intel/iavf/iavf.h | 1 + + .../net/ethernet/intel/iavf/iavf_ethtool.c | 8 +- + drivers/net/ethernet/intel/ice/ice.h | 1 + + drivers/net/ethernet/intel/ice/ice_main.c | 4 +- + drivers/net/ethernet/intel/igb/igb_ethtool.c | 12 +- + drivers/net/ethernet/intel/igb/igb_main.c | 8 +- + drivers/net/ethernet/intel/igc/igc_ethtool.c | 12 +- + drivers/net/ethernet/intel/igc/igc_main.c | 8 +- + .../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 8 +- + drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 8 +- + drivers/net/ethernet/intel/ixgbevf/ethtool.c | 12 +- + .../net/ethernet/intel/ixgbevf/ixgbevf_main.c | 8 +- + drivers/net/ethernet/marvell/mvneta.c | 10 +- + .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 8 +- + drivers/net/ethernet/marvell/sky2.c | 8 +- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 8 +- + .../net/ethernet/mellanox/mlx5/core/en_main.c | 1 + + .../net/ethernet/mellanox/mlxsw/spectrum.c | 4 +- + .../ethernet/microchip/lan966x/lan966x_tc.c | 1 + + .../net/ethernet/microchip/sparx5/sparx5_tc.c | 1 + + drivers/net/ethernet/microsoft/mana/mana_en.c | 8 +- + .../ethernet/microsoft/mana/mana_ethtool.c | 8 +- + drivers/net/ethernet/mscc/ocelot.c | 52 +- + drivers/net/ethernet/mscc/ocelot_flower.c | 30 +- + drivers/net/ethernet/mscc/ocelot_ptp.c | 1 + + drivers/net/ethernet/mscc/ocelot_vcap.c | 173 +- + .../ethernet/netronome/nfp/nfp_net_common.c | 8 +- + .../ethernet/netronome/nfp/nfp_net_ethtool.c | 8 +- + .../net/ethernet/netronome/nfp/nfp_net_repr.c | 4 +- + drivers/net/ethernet/nvidia/forcedeth.c | 8 +- + .../net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 4 +- + drivers/net/ethernet/realtek/8139too.c | 8 +- + drivers/net/ethernet/socionext/sni_ave.c | 8 +- + .../net/ethernet/stmicro/stmmac/dwmac4_core.c | 38 +- + drivers/net/ethernet/stmicro/stmmac/dwmac5.c | 19 +- + drivers/net/ethernet/stmicro/stmmac/dwmac5.h | 6 +- + .../ethernet/stmicro/stmmac/dwxgmac2_core.c | 3 +- + drivers/net/ethernet/stmicro/stmmac/hwif.h | 11 +- + drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 + + .../ethernet/stmicro/stmmac/stmmac_ethtool.c | 103 + + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 40 +- + .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 37 +- + drivers/net/ethernet/ti/am65-cpsw-nuss.c | 4 +- + drivers/net/ethernet/ti/cpsw_priv.c | 1 + + drivers/net/ethernet/ti/netcp_core.c | 8 +- + drivers/net/ethernet/via/via-rhine.c | 8 +- + .../net/ethernet/xilinx/xilinx_axienet_main.c | 8 +- + drivers/net/hyperv/netvsc_drv.c | 32 +- + drivers/net/ifb.c | 12 +- + drivers/net/ipvlan/ipvlan_main.c | 4 +- + drivers/net/loopback.c | 4 +- + drivers/net/macsec.c | 12 +- + drivers/net/macvlan.c | 4 +- + drivers/net/mhi_net.c | 8 +- + drivers/net/netdevsim/netdev.c | 4 +- + drivers/net/phy/phy.c | 5 +- + drivers/net/team/team.c | 4 +- + drivers/net/team/team_mode_loadbalance.c | 4 +- + drivers/net/veth.c | 12 +- + drivers/net/virtio_net.c | 16 +- + drivers/net/vrf.c | 4 +- + drivers/net/vxlan/vxlan_vnifilter.c | 4 +- + drivers/net/wwan/mhi_wwan_mbim.c | 8 +- + drivers/net/xen-netfront.c | 8 +- + drivers/nfc/Kconfig | 1 + + drivers/nfc/Makefile | 1 + + drivers/nfc/pn5xx/Kconfig | 12 + + drivers/nfc/pn5xx/Makefile | 6 + + drivers/nfc/pn5xx/README.md | 2 + + drivers/nfc/pn5xx/pn5xx_i2c.c | 672 ++++ + drivers/nfc/pn5xx/pn5xx_i2c.h | 38 + + drivers/nfc/pn5xx/sample_devicetree.txt | 17 + + drivers/rpmsg/Kconfig | 16 +- + drivers/rpmsg/Makefile | 1 + + drivers/rpmsg/imx_rpmsg.c | 57 +- + drivers/rpmsg/imx_rpmsg_tty.c | 18 +- + drivers/rpmsg/rpmsg_perf.c | 545 +++ + drivers/rpmsg/virtio_rpmsg_bus.c | 5 + + drivers/rtc/Kconfig | 1 + + drivers/spi/spi.c | 4 +- + drivers/staging/fsl_qbman/qman_driver.c | 50 + + drivers/staging/fsl_qbman/qman_high.c | 38 + + drivers/staging/fsl_qbman/qman_private.h | 6 + + drivers/tty/Kconfig | 2 + + drivers/tty/rpmsg_tty.c | 192 +- + drivers/tty/serial/8250/8250.h | 41 +- + drivers/tty/serial/8250/8250_aspeed_vuart.c | 2 +- + drivers/tty/serial/8250/8250_bcm7271.c | 21 +- + drivers/tty/serial/8250/8250_core.c | 24 +- + drivers/tty/serial/8250/8250_exar.c | 4 +- + drivers/tty/serial/8250/8250_fsl.c | 3 +- + drivers/tty/serial/8250/8250_ingenic.c | 3 +- + drivers/tty/serial/8250/8250_mtk.c | 32 +- + drivers/tty/serial/8250/8250_omap.c | 20 +- + drivers/tty/serial/8250/8250_port.c | 158 +- + drivers/tty/serial/8250/Kconfig | 1 + + drivers/tty/serial/amba-pl011.c | 17 +- + drivers/tty/serial/omap-serial.c | 12 +- + drivers/tty/sysrq.c | 2 + + drivers/vdpa/vdpa_user/iova_domain.h | 1 - + drivers/virtio/Kconfig | 8 + + drivers/virtio/Makefile | 1 + + drivers/virtio/virtio_mmio.c | 318 +- + drivers/virtio/virtio_ring.c | 2 - + drivers/virtio/virtio_trans.c | 793 +++++ + 203 files changed, 15306 insertions(+), 832 deletions(-) + create mode 100644 drivers/clk/imx/clk-pll.c + create mode 100644 drivers/clk/imx/clk-pll.h + create mode 100644 drivers/irqchip/ipi-baremetal.c + create mode 100644 drivers/mailbox/generic-software-mailbox.c + create mode 100644 drivers/net/dsa/netc/Kconfig + create mode 100644 drivers/net/dsa/netc/Makefile + create mode 100644 drivers/net/dsa/netc/netc.h + create mode 100644 drivers/net/dsa/netc/netc_config.c + create mode 100644 drivers/net/dsa/netc/netc_config.h + create mode 100644 drivers/net/dsa/netc/netc_devlink.c + create mode 100644 drivers/net/dsa/netc/netc_ethtool.c + create mode 100644 drivers/net/dsa/netc/netc_main.c + create mode 100644 drivers/net/dsa/netc/netc_ptp.c + create mode 100644 drivers/net/dsa/netc/netc_spi.c + create mode 100644 drivers/net/ethernet/freescale/fec_ecat.c + create mode 100644 drivers/net/ethernet/freescale/fec_ecat.h + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ethercat.c + create mode 100644 drivers/nfc/pn5xx/Kconfig + create mode 100644 drivers/nfc/pn5xx/Makefile + create mode 100644 drivers/nfc/pn5xx/README.md + create mode 100644 drivers/nfc/pn5xx/pn5xx_i2c.c + create mode 100644 drivers/nfc/pn5xx/pn5xx_i2c.h + create mode 100644 drivers/nfc/pn5xx/sample_devicetree.txt + create mode 100644 drivers/rpmsg/rpmsg_perf.c + create mode 100644 drivers/virtio/virtio_trans.c + +diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig +index 6f04b831a5c0..5cb0ba920813 100644 +--- a/drivers/base/Kconfig ++++ b/drivers/base/Kconfig +@@ -199,7 +199,7 @@ source "drivers/base/regmap/Kconfig" + + config DMA_SHARED_BUFFER + bool +- default n ++ default y + select IRQ_WORK + help + This option enables the framework for buffer-sharing between +diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c +index 966aab902d19..ee69e4443691 100644 +--- a/drivers/block/zram/zram_drv.c ++++ b/drivers/block/zram/zram_drv.c +@@ -57,6 +57,40 @@ static void zram_free_page(struct zram *zram, size_t index); + static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio); + ++#ifdef CONFIG_PREEMPT_RT ++static void zram_meta_init_table_locks(struct zram *zram, size_t num_pages) ++{ ++ size_t index; ++ ++ for (index = 0; index < num_pages; index++) ++ spin_lock_init(&zram->table[index].lock); ++} ++ ++static int zram_slot_trylock(struct zram *zram, u32 index) ++{ ++ int ret; ++ ++ ret = spin_trylock(&zram->table[index].lock); ++ if (ret) ++ __set_bit(ZRAM_LOCK, &zram->table[index].flags); ++ return ret; ++} ++ ++static void zram_slot_lock(struct zram *zram, u32 index) ++{ ++ spin_lock(&zram->table[index].lock); ++ __set_bit(ZRAM_LOCK, &zram->table[index].flags); ++} ++ ++static void zram_slot_unlock(struct zram *zram, u32 index) ++{ ++ __clear_bit(ZRAM_LOCK, &zram->table[index].flags); ++ spin_unlock(&zram->table[index].lock); ++} ++ ++#else ++ ++static void zram_meta_init_table_locks(struct zram *zram, size_t num_pages) { } + + static int zram_slot_trylock(struct zram *zram, u32 index) + { +@@ -72,6 +106,7 @@ static void zram_slot_unlock(struct zram *zram, u32 index) + { + bit_spin_unlock(ZRAM_LOCK, &zram->table[index].flags); + } ++#endif + + static inline bool init_done(struct zram *zram) + { +@@ -1187,6 +1222,7 @@ static bool zram_meta_alloc(struct zram *zram, u64 disksize) + + if (!huge_class_size) + huge_class_size = zs_huge_class_size(zram->mem_pool); ++ zram_meta_init_table_locks(zram, num_pages); + return true; + } + +diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h +index a2bda53020fd..ae7950b26db5 100644 +--- a/drivers/block/zram/zram_drv.h ++++ b/drivers/block/zram/zram_drv.h +@@ -62,6 +62,9 @@ struct zram_table_entry { + unsigned long element; + }; + unsigned long flags; ++#ifdef CONFIG_PREEMPT_RT ++ spinlock_t lock; ++#endif + #ifdef CONFIG_ZRAM_MEMORY_TRACKING + ktime_t ac_time; + #endif +diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c +index 0d084d6652c4..5d620322bdc2 100644 +--- a/drivers/char/tpm/tpm_tis.c ++++ b/drivers/char/tpm/tpm_tis.c +@@ -50,6 +50,31 @@ static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *da + return container_of(data, struct tpm_tis_tcg_phy, priv); + } + ++#ifdef CONFIG_PREEMPT_RT ++/* ++ * Flushes previous write operations to chip so that a subsequent ++ * ioread*()s won't stall a cpu. ++ */ ++static inline void tpm_tis_flush(void __iomem *iobase) ++{ ++ ioread8(iobase + TPM_ACCESS(0)); ++} ++#else ++#define tpm_tis_flush(iobase) do { } while (0) ++#endif ++ ++static inline void tpm_tis_iowrite8(u8 b, void __iomem *iobase, u32 addr) ++{ ++ iowrite8(b, iobase + addr); ++ tpm_tis_flush(iobase); ++} ++ ++static inline void tpm_tis_iowrite32(u32 b, void __iomem *iobase, u32 addr) ++{ ++ iowrite32(b, iobase + addr); ++ tpm_tis_flush(iobase); ++} ++ + static int interrupts = -1; + module_param(interrupts, int, 0444); + MODULE_PARM_DESC(interrupts, "Enable interrupts"); +@@ -202,12 +227,12 @@ static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len, + switch (io_mode) { + case TPM_TIS_PHYS_8: + while (len--) +- iowrite8(*value++, phy->iobase + addr); ++ tpm_tis_iowrite8(*value++, phy->iobase, addr); + break; + case TPM_TIS_PHYS_16: + return -EINVAL; + case TPM_TIS_PHYS_32: +- iowrite32(le32_to_cpu(*((__le32 *)value)), phy->iobase + addr); ++ tpm_tis_iowrite32(le32_to_cpu(*((__le32 *)value)), phy->iobase, addr); + break; + } + +diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile +index bc6579e0cb0b..53e75b26648f 100644 +--- a/drivers/clk/imx/Makefile ++++ b/drivers/clk/imx/Makefile +@@ -16,6 +16,7 @@ mxc-clk-objs += clk-gate-93.o + mxc-clk-objs += clk-gate-exclusive.o + mxc-clk-objs += clk-pfd.o + mxc-clk-objs += clk-pfdv2.o ++mxc-clk-objs += clk-pll.o + mxc-clk-objs += clk-pllv1.o + mxc-clk-objs += clk-pllv2.o + mxc-clk-objs += clk-pllv3.o +diff --git a/drivers/clk/imx/clk-frac-pll.c b/drivers/clk/imx/clk-frac-pll.c +index c703056fae85..2085d8e91ca8 100644 +--- a/drivers/clk/imx/clk-frac-pll.c ++++ b/drivers/clk/imx/clk-frac-pll.c +@@ -17,6 +17,7 @@ + #include + + #include "clk.h" ++#include "clk-pll.h" + + #define PLL_CFG0 0x0 + #define PLL_CFG1 0x4 +@@ -35,7 +36,11 @@ + #define PLL_FRAC_ACK_TIMEOUT 500000 + + struct clk_frac_pll { ++ u32 orig_divff; ++ u32 orig_divfi; ++ u32 orig_pllcfg0; + struct clk_hw hw; ++ struct clk_imx_pll imx_pll; + void __iomem *base; + }; + +@@ -202,6 +207,126 @@ static const struct clk_ops clk_frac_pll_ops = { + .set_rate = clk_pll_set_rate, + }; + ++/* This function fetches the original PLL parameters to use ++ * them later for ppb adjustment ++ */ ++static void imx_frac_pll_init(struct clk_imx_pll *pll) ++{ ++ struct clk_frac_pll *frac_pll; ++ u32 val; ++ ++ frac_pll = (struct clk_frac_pll *) container_of(pll, ++ struct clk_frac_pll, imx_pll); ++ ++ val = readl_relaxed(frac_pll->base + PLL_CFG1); ++ ++ frac_pll->orig_divff = (val >> 7) & PLL_FRAC_DIV_MASK; ++ frac_pll->orig_divfi = val & PLL_INT_DIV_MASK; ++ ++ frac_pll->orig_pllcfg0 = readl_relaxed(frac_pll->base + PLL_CFG0); ++} ++ ++/** ++ * imx_frac_pll_adjust - Adjust the Audio pll by ppb. ++ * ++ * This function adjust the audio pll by ppb (part per billion) and returns ++ * the exact number of ppb adjusted. ++ * The adjustment is done by only modifying the Fractional Divide part ++ * of the audio PLL. ++ * Since the pllout = parent_rate * 8 / 2 * (1 + DIVFI + DIVFF / 2^24) ++ * and the adjusted value is ++ * pllout_new = pllout * (1 + ppb/1e9) which equals: ++ * parent_rate * 8 / 2 * (1 + DIVFI + DIVFF_new / 2^24) ++ * The new divff is calculated as the following: ++ * DIVFF_new = ((1 + DIVFI) * ppb * 2^24 + DIVFF * 1e9 + DIVFF * ppb) / (1e9) ++ */ ++ ++static int imx_frac_pll_adjust(struct clk_imx_pll *pll, int *ppb) ++{ ++ u64 temp64; ++ u32 val; ++ s64 applied_ppb; ++ struct clk_frac_pll *frac_pll; ++ int rc = IMX_CLK_PLL_SUCCESS; ++ ++ int req_ppb = *ppb; ++ ++ frac_pll = (struct clk_frac_pll *) container_of(pll, ++ struct clk_frac_pll, imx_pll); ++ ++ /*Calcultate the new PLL Numerator*/ ++ temp64 = ((u64) frac_pll->orig_divfi + 1) * PLL_FRAC_DENOM * req_ppb ++ + (u64) frac_pll->orig_divff * 1000000000 ++ + (u64) frac_pll->orig_divff * req_ppb; ++ ++ do_div(temp64, 1000000000); ++ ++ if (temp64 >= PLL_FRAC_DENOM) { ++ rc = -IMX_CLK_PLL_PREC_ERR; ++ goto exit; ++ } ++ ++ /* clear the NEW_DIV_VAL */ ++ val = frac_pll->orig_pllcfg0; ++ val &= ~PLL_NEWDIV_VAL; ++ writel_relaxed(val, frac_pll->base + PLL_CFG0); ++ ++ /* Write the PLL control settings with the new DIVFF ++ * NOTE: This sets the reserved bit (bit 31) to zero ++ */ ++ ++ val = 0; ++ val |= (((u32)temp64 << 7) | frac_pll->orig_divfi); ++ writel_relaxed(val, frac_pll->base + PLL_CFG1); ++ ++ /* Set the NEW_DIV_VAL to reload the DIVFI and DIVFF */ ++ val = frac_pll->orig_pllcfg0; ++ val |= PLL_NEWDIV_VAL; ++ writel_relaxed(val, frac_pll->base + PLL_CFG0); ++ ++ /*Calculate and return the actual applied ppb*/ ++ applied_ppb = div64_s64((s64) (temp64 - frac_pll->orig_divff) * 1000000000, ++ frac_pll->orig_divff + ((s64) frac_pll->orig_divfi + 1) * PLL_FRAC_DENOM); ++ ++ *ppb = (int) applied_ppb; ++ ++ exit: ++ return rc; ++} ++ ++static unsigned long imx_frac_pll_get_rate(struct clk_imx_pll *pll, ++ unsigned long parent_rate) ++{ ++ struct clk_frac_pll *frac_pll; ++ ++ frac_pll = (struct clk_frac_pll *) container_of(pll, ++ struct clk_frac_pll, imx_pll); ++ ++ return clk_pll_recalc_rate(&frac_pll->hw, parent_rate); ++} ++ ++static int imx_frac_pll_set_rate(struct clk_imx_pll *pll, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_frac_pll *frac_pll; ++ int rc = IMX_CLK_PLL_SUCCESS; ++ ++ frac_pll = (struct clk_frac_pll *) container_of(pll, ++ struct clk_frac_pll, imx_pll); ++ ++ if (clk_pll_set_rate(&frac_pll->hw, rate, parent_rate) < 0) ++ rc = -IMX_CLK_PLL_INVALID_PARAM; ++ ++ return rc; ++} ++ ++static const struct clk_imx_pll_ops imx_clk_frac_pll_ops = { ++ .set_rate = imx_frac_pll_set_rate, ++ .get_rate = imx_frac_pll_get_rate, ++ .adjust = imx_frac_pll_adjust, ++ .init = imx_frac_pll_init, ++}; ++ + struct clk_hw *imx_clk_hw_frac_pll(const char *name, + const char *parent_name, + void __iomem *base) +@@ -232,6 +357,11 @@ struct clk_hw *imx_clk_hw_frac_pll(const char *name, + return ERR_PTR(ret); + } + ++ pll->imx_pll.ops = &imx_clk_frac_pll_ops; ++ ++ if (imx_pll_register(&pll->imx_pll, name) < 0) ++ pr_warn("Failed to register %s into imx pll\n", name); ++ + return hw; + } + EXPORT_SYMBOL_GPL(imx_clk_hw_frac_pll); +diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c +index d611e81cf15b..c1bd42a4e479 100644 +--- a/drivers/clk/imx/clk-fracn-gppll.c ++++ b/drivers/clk/imx/clk-fracn-gppll.c +@@ -13,6 +13,7 @@ + #include + + #include "clk.h" ++#include "clk-pll.h" + + #define PLL_CTRL 0x0 + #define HW_CTRL_SEL BIT(16) +@@ -53,10 +54,21 @@ + .odiv = (_odiv), \ + } + ++ ++/* MFN : Numerator of the fractional part of divider (30-bits, signed) */ ++#define MFN_MAX_VALUE ((s32)(GENMASK(29, 0) >> 1)) ++#define MFN_MIN_VALUE ((s32)(-MFN_MAX_VALUE - 1)) ++ + struct clk_fracn_gppll { + struct clk_hw hw; + void __iomem *base; + const struct imx_fracn_gppll_rate_table *rate_table; ++ u32 orig_mfd; ++ u32 orig_mfi; ++ u32 orig_odiv; ++ u32 orig_rdiv; ++ s32 orig_mfn; ++ struct clk_imx_pll imx_pll; + int rate_count; + }; + +@@ -79,7 +91,7 @@ static const struct imx_fracn_gppll_rate_table fracn_tbl[] = { + PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6), + PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9), + PLL_FRACN_GP(400000000U, 200, 0, 1, 0, 12), +- PLL_FRACN_GP(393216000U, 163, 84, 100, 0, 10), ++ PLL_FRACN_GP(393216000U, 163, 1680000, 2000000, 0, 10), + PLL_FRACN_GP(300000000U, 150, 0, 1, 0, 12) + }; + +@@ -123,18 +135,19 @@ static long clk_fracn_gppll_round_rate(struct clk_hw *hw, unsigned long rate, + return rate_table[pll->rate_count - 1].rate; + } + +-static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) ++/* This function calculates the actual rate based on the configured PLL registers */ ++static unsigned long __clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) + { + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); +- const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; + u32 pll_numerator, pll_denominator, pll_div; +- u32 mfi, mfn, mfd, rdiv, odiv; ++ u32 mfi, mfd, rdiv, odiv; ++ s32 mfn; + u64 fvco = parent_rate; +- long rate = 0; +- int i; + + pll_numerator = readl_relaxed(pll->base + PLL_NUMERATOR); +- mfn = FIELD_GET(PLL_MFN_MASK, pll_numerator); ++ ++ /* Numerator is 30-bits signed value */ ++ mfn = sign_extend32(FIELD_GET(PLL_MFN_MASK, pll_numerator), 29); + + pll_denominator = readl_relaxed(pll->base + PLL_DENOMINATOR); + mfd = FIELD_GET(PLL_MFD_MASK, pll_denominator); +@@ -145,22 +158,6 @@ static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned lon + rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div); + odiv = FIELD_GET(PLL_ODIV_MASK, pll_div); + +- /* +- * Sometimes, the recalculated rate has deviation due to +- * the frac part. So find the accurate pll rate from the table +- * first, if no match rate in the table, use the rate calculated +- * from the equation below. +- */ +- for (i = 0; i < pll->rate_count; i++) { +- if (rate_table[i].mfn == mfn && rate_table[i].mfi == mfi && +- rate_table[i].mfd == mfd && rate_table[i].rdiv == rdiv && +- rate_table[i].odiv == odiv) +- rate = rate_table[i].rate; +- } +- +- if (rate) +- return (unsigned long)rate; +- + if (!rdiv) + rdiv = rdiv + 1; + +@@ -176,12 +173,49 @@ static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned lon + } + + /* Fvco = Fref * (MFI + MFN / MFD) */ +- fvco = fvco * mfi * mfd + fvco * mfn; ++ fvco *= (mfi * mfd + mfn); + do_div(fvco, mfd * rdiv * odiv); + + return (unsigned long)fvco; + } + ++static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) ++{ ++ struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); ++ const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; ++ u32 pll_numerator, pll_denominator, pll_div; ++ u32 mfi, mfn, mfd, rdiv, odiv; ++ long rate = 0; ++ int i; ++ ++ pll_numerator = readl_relaxed(pll->base + PLL_NUMERATOR); ++ mfn = FIELD_GET(PLL_MFN_MASK, pll_numerator); ++ ++ pll_denominator = readl_relaxed(pll->base + PLL_DENOMINATOR); ++ mfd = FIELD_GET(PLL_MFD_MASK, pll_denominator); ++ ++ pll_div = readl_relaxed(pll->base + PLL_DIV); ++ mfi = FIELD_GET(PLL_MFI_MASK, pll_div); ++ ++ rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div); ++ odiv = FIELD_GET(PLL_ODIV_MASK, pll_div); ++ ++ /* ++ * Sometimes, the recalculated rate has deviation due to ++ * the frac part. So find the accurate pll rate from the table ++ * first, if no match rate in the table, use the rate calculated ++ * from the equation below. ++ */ ++ for (i = 0; i < pll->rate_count; i++) { ++ if (rate_table[i].mfn == mfn && rate_table[i].mfi == mfi && ++ rate_table[i].mfd == mfd && rate_table[i].rdiv == rdiv && ++ rate_table[i].odiv == odiv) ++ rate = rate_table[i].rate; ++ } ++ ++ return rate ? (unsigned long) rate : __clk_fracn_gppll_recalc_rate(hw, parent_rate); ++} ++ + static int clk_fracn_gppll_wait_lock(struct clk_fracn_gppll *pll) + { + u32 val; +@@ -199,6 +233,11 @@ static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate, + int ret; + + rate = imx_get_pll_settings(pll, drate); ++ if (!rate) { ++ pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, ++ drate, clk_hw_get_name(hw)); ++ return -EINVAL; ++ } + + /* Hardware control select disable. PLL is control by register */ + tmp = readl_relaxed(pll->base + PLL_CTRL); +@@ -306,6 +345,122 @@ static const struct clk_ops clk_fracn_gppll_ops = { + .set_rate = clk_fracn_gppll_set_rate, + }; + ++/* This function fetches the original PLL parameters to use ++ * them later for ppb adjustment ++ */ ++static void imx_fracn_gppll_init(struct clk_imx_pll *pll) ++{ ++ struct clk_fracn_gppll *fracn_pll; ++ u32 pll_numerator, pll_denominator, pll_div, mfn; ++ ++ fracn_pll = (struct clk_fracn_gppll *) container_of(pll, ++ struct clk_fracn_gppll, imx_pll); ++ ++ pll_numerator = readl_relaxed(fracn_pll->base + PLL_NUMERATOR); ++ mfn = FIELD_GET(PLL_MFN_MASK, pll_numerator); ++ ++ /* Numerator is 30-bits signed value */ ++ fracn_pll->orig_mfn = sign_extend32(mfn, 29); ++ ++ pll_denominator = readl_relaxed(fracn_pll->base + PLL_DENOMINATOR); ++ fracn_pll->orig_mfd = FIELD_GET(PLL_MFD_MASK, pll_denominator); ++ ++ pll_div = readl_relaxed(fracn_pll->base + PLL_DIV); ++ fracn_pll->orig_mfi = FIELD_GET(PLL_MFI_MASK, pll_div); ++ ++ fracn_pll->orig_rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div); ++ fracn_pll->orig_odiv = FIELD_GET(PLL_ODIV_MASK, pll_div); ++} ++ ++/** ++ * imx_fracn_gppll_adjust - Adjust the Audio pll by ppb. ++ * ++ * This function adjusts the audio pll by ppb (part per billion) and returns ++ * the exact number of ppb adjusted. ++ * The adjustment is done by only modifying the numerator of the fractional part ++ * of the audio PLL. ++ * Since the pllout = (ref * (mfi + mfn/mfd)) / (rdiv * odiv) ++ * and the adjusted value is ++ * pllout_new = pllout * (1 + ppb/1e9) which equals: ++ * (ref * (mfi + mfn_new/mfd)) / (rdiv * odiv) ++ * The new numerator (mfn_new) is calculated as following: ++ * mfn_new = (1e9 * mfn + (mfi * mfd + mfn) * ppb) / (1e9) ++ */ ++ ++static int imx_fracn_gppll_adjust(struct clk_imx_pll *pll, int *ppb) ++{ ++ s64 temp64; ++ s64 applied_ppb; ++ struct clk_fracn_gppll *fracn_pll; ++ int rc = IMX_CLK_PLL_SUCCESS; ++ ++ int req_ppb = *ppb; ++ ++ fracn_pll = (struct clk_fracn_gppll *) container_of(pll, ++ struct clk_fracn_gppll, imx_pll); ++ ++ /* Calculate the new numerator value */ ++ temp64 = ((s64) fracn_pll->orig_mfn * 1000000000) ++ + ((s64) fracn_pll->orig_mfi * fracn_pll->orig_mfd + (s64) fracn_pll->orig_mfn) * req_ppb; ++ ++ temp64 = div_s64(temp64, 1000000000); ++ ++ /* Sanity check on the new numerator value: ++ * - the value is inside the 30-bits signed values range ++ * - mfn/mfd should be in the range [-2, 2] ++ */ ++ if (temp64 > MFN_MAX_VALUE || temp64 < MFN_MIN_VALUE || (abs(temp64) > (2 * fracn_pll->orig_mfd))) { ++ rc = -IMX_CLK_PLL_PREC_ERR; ++ goto exit; ++ } ++ ++ /* Write the PLL control settings with the new numerator */ ++ ++ writel_relaxed(FIELD_PREP(PLL_MFN_MASK, temp64), fracn_pll->base + PLL_NUMERATOR); ++ ++ /* Calculate and return the actual applied ppb */ ++ applied_ppb = div64_s64((s64) (temp64 - fracn_pll->orig_mfn) * 1000000000, ++ fracn_pll->orig_mfn + fracn_pll->orig_mfd * (s64) fracn_pll->orig_mfi); ++ ++ *ppb = (int) applied_ppb; ++ ++ exit: ++ return rc; ++} ++ ++static unsigned long imx_fracn_gppll_get_rate(struct clk_imx_pll *pll, ++ unsigned long parent_rate) ++{ ++ struct clk_fracn_gppll *fracn_pll; ++ ++ fracn_pll = (struct clk_fracn_gppll *) container_of(pll, ++ struct clk_fracn_gppll, imx_pll); ++ ++ return __clk_fracn_gppll_recalc_rate(&fracn_pll->hw, parent_rate); ++} ++ ++static int imx_fracn_gppll_set_rate(struct clk_imx_pll *pll, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_fracn_gppll *fracn_pll; ++ int rc = IMX_CLK_PLL_SUCCESS; ++ ++ fracn_pll = (struct clk_fracn_gppll *) container_of(pll, ++ struct clk_fracn_gppll, imx_pll); ++ ++ if (clk_fracn_gppll_set_rate(&fracn_pll->hw, rate, parent_rate) < 0) ++ rc = -IMX_CLK_PLL_INVALID_PARAM; ++ ++ return rc; ++} ++ ++static const struct clk_imx_pll_ops imx_clk_fracn_gppll_ops = { ++ .set_rate = imx_fracn_gppll_set_rate, ++ .get_rate = imx_fracn_gppll_get_rate, ++ .adjust = imx_fracn_gppll_adjust, ++ .init = imx_fracn_gppll_init, ++}; ++ + struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk) + { +@@ -338,6 +493,11 @@ struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, vo + return ERR_PTR(ret); + } + ++ pll->imx_pll.ops = &imx_clk_fracn_gppll_ops; ++ ++ if (imx_pll_register(&pll->imx_pll, name) < 0) ++ pr_warn("%s: failed to register %s into imx pll\n", __func__, name); ++ + return hw; + } + EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll); +diff --git a/drivers/clk/imx/clk-imx8dxl-acm.c b/drivers/clk/imx/clk-imx8dxl-acm.c +index b11254522bc5..bef8d33f543b 100644 +--- a/drivers/clk/imx/clk-imx8dxl-acm.c ++++ b/drivers/clk/imx/clk-imx8dxl-acm.c +@@ -55,6 +55,14 @@ static const char *mclk_out_sels[] = { + "dummy", + }; + ++static const char *gpt_mux_clk_sels[] = { ++ "aud_pll_div_clk0_lpcg_clk", ++ "aud_pll_div_clk1_lpcg_clk", ++ "acm_aud_clk0_sel", ++ "acm_aud_clk1_sel", ++ "dummy", ++}; ++ + static const char *sai_mclk_sels[] = { + "aud_pll_div_clk0_lpcg_clk", + "aud_pll_div_clk1_lpcg_clk", +@@ -76,6 +84,41 @@ static const char *mqs_mclk_sels[] = { + "acm_aud_clk1_sel", + }; + ++/* The number of cells for the GPT capture device tree attribute */ ++#define OF_GPT_CAPTURE_CELLS_NB 2 ++ ++static void imx8dxl_acm_gpt_input_mux(struct device_node *np, struct imx8dxl_acm_priv *priv) ++{ ++ u32 len, reg_offset, event_sel_control, num_capture_select; ++ int i, offset; ++ ++ if (!of_get_property(np, "gpt-capture-select", &len)) ++ return; ++ ++ num_capture_select = len / (sizeof(u32) * OF_GPT_CAPTURE_CELLS_NB); ++ ++ for (i = 0; i < num_capture_select; i++) { ++ offset = i * OF_GPT_CAPTURE_CELLS_NB; ++ if (of_property_read_u32_index(np, ++ "gpt-capture-select", ++ offset, ®_offset)) { ++ pr_err("failed to read gpt register offset cell at offset %d\n", ++ offset); ++ return; ++ } ++ ++ if (of_property_read_u32_index(np, ++ "gpt-capture-select", ++ offset + 1, &event_sel_control)) { ++ pr_err("failed to read gpt event select control cell at offset %d\n", ++ offset + 1); ++ return; ++ } ++ ++ writel_relaxed(event_sel_control, priv->reg + reg_offset); ++ } ++} ++ + static int imx8dxl_acm_clk_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -137,6 +180,8 @@ static int imx8dxl_acm_clk_probe(struct platform_device *pdev) + clks[IMX_ADMA_ACM_MCLKOUT0_SEL] = imx_dev_clk_mux(dev, "acm_mclkout0_sel", base+0x020000, 0, 3, mclk_out_sels, ARRAY_SIZE(mclk_out_sels)); + clks[IMX_ADMA_ACM_MCLKOUT1_SEL] = imx_dev_clk_mux(dev, "acm_mclkout1_sel", base+0x030000, 0, 3, mclk_out_sels, ARRAY_SIZE(mclk_out_sels)); + ++ clks[IMX_ADMA_ACM_GPT0_MUX_CLK_SEL] = imx_dev_clk_mux(dev, "acm_gpt0_mux_clk_sel", base+0x080000, 0, 3, gpt_mux_clk_sels, ARRAY_SIZE(gpt_mux_clk_sels)); ++ + clks[IMX_ADMA_ACM_SAI0_MCLK_SEL] = imx_dev_clk_mux(dev, "acm_sai0_mclk_sel", base+0x0E0000, 0, 2, sai_mclk_sels, ARRAY_SIZE(sai_mclk_sels)); + clks[IMX_ADMA_ACM_SAI1_MCLK_SEL] = imx_dev_clk_mux(dev, "acm_sai1_mclk_sel", base+0x0F0000, 0, 2, sai_mclk_sels, ARRAY_SIZE(sai_mclk_sels)); + clks[IMX_ADMA_ACM_SAI2_MCLK_SEL] = imx_dev_clk_mux(dev, "acm_sai2_mclk_sel", base+0x100000, 0, 2, sai_mclk_sels, ARRAY_SIZE(sai_mclk_sels)); +@@ -153,6 +198,8 @@ static int imx8dxl_acm_clk_probe(struct platform_device *pdev) + + ret = of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); + ++ imx8dxl_acm_gpt_input_mux(np, priv); ++ + pm_runtime_put_sync(&pdev->dev); + + return ret; +@@ -182,6 +229,7 @@ static int __maybe_unused imx8dxl_acm_runtime_suspend(struct device *dev) + priv->regs[1] = readl_relaxed(priv->reg + 0x010000); + priv->regs[2] = readl_relaxed(priv->reg + 0x020000); + priv->regs[3] = readl_relaxed(priv->reg + 0x030000); ++ priv->regs[8] = readl_relaxed(priv->reg + 0x080000); + priv->regs[14] = readl_relaxed(priv->reg + 0x0E0000); + priv->regs[15] = readl_relaxed(priv->reg + 0x0F0000); + priv->regs[16] = readl_relaxed(priv->reg + 0x100000); +@@ -200,6 +248,7 @@ static int __maybe_unused imx8dxl_acm_runtime_resume(struct device *dev) + writel_relaxed(priv->regs[1], priv->reg + 0x010000); + writel_relaxed(priv->regs[2], priv->reg + 0x020000); + writel_relaxed(priv->regs[3], priv->reg + 0x030000); ++ writel_relaxed(priv->regs[8], priv->reg + 0x080000); + writel_relaxed(priv->regs[14], priv->reg + 0x0E0000); + writel_relaxed(priv->regs[15], priv->reg + 0x0F0000); + writel_relaxed(priv->regs[16], priv->reg + 0x100000); +diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c +index 8e49b49ddc3e..1e9de317ed54 100644 +--- a/drivers/clk/imx/clk-imx8mq.c ++++ b/drivers/clk/imx/clk-imx8mq.c +@@ -216,7 +216,7 @@ static const char * const imx8mq_pwm4_sels[] = {"osc_25m", "sys2_pll_100m", "sys + "sys3_pll_out", "clk_ext2", "sys1_pll_80m", "video_pll1_out", }; + + static const char * const imx8mq_gpt1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_400m", "sys1_pll_40m", +- "sys1_pll_80m", "audio_pll1_out", "clk_ext1", }; ++ "video_pll1_out", "sys1_pll_80m", "audio_pll1_out", "clk_ext1", }; + + static const char * const imx8mq_wdog_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_160m", "vpu_pll_out", + "sys2_pll_125m", "sys3_pll_out", "sys1_pll_80m", "sys2_pll_166m", }; +diff --git a/drivers/clk/imx/clk-pll.c b/drivers/clk/imx/clk-pll.c +new file mode 100644 +index 000000000000..b77375a11add +--- /dev/null ++++ b/drivers/clk/imx/clk-pll.c +@@ -0,0 +1,83 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2018 NXP ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "clk-pll.h" ++ ++ ++static struct clk_imx_pll *clk_imx_pll_array[MAX_IMX_PLL_NUM] = { NULL }; ++ ++struct clk_imx_pll *clk_imx_pll_get_by_name(const char *name) ++{ ++ int i; ++ struct clk_imx_pll *pll = NULL; ++ ++ for (i = 0; i < MAX_IMX_PLL_NUM; i++) { ++ if (clk_imx_pll_array[i] && clk_imx_pll_array[i]->ops ++ && !strncmp(clk_imx_pll_array[i]->pll_name, name, MAX_PLL_NAME_SIZE)) { ++ ++ pll = clk_imx_pll_array[i]; ++ ++ /* Init all PLL original parameters*/ ++ if (pll && pll->ops && pll->ops->init) ++ pll->ops->init(pll); ++ ++ break; ++ } ++ } ++ ++ return pll; ++} ++EXPORT_SYMBOL(clk_imx_pll_get_by_name); ++ ++int clk_imx_pll_adjust(struct clk_imx_pll *pll, int *ppb) ++{ ++ if (pll && pll->ops && pll->ops->adjust) ++ return pll->ops->adjust(pll, ppb); ++ ++ return -IMX_CLK_PLL_INVALID_PARAM; ++} ++EXPORT_SYMBOL(clk_imx_pll_adjust); ++ ++unsigned long clk_imx_pll_get_rate(struct clk_imx_pll *pll, ++ unsigned long parent_rate) ++{ ++ if (pll && pll->ops && pll->ops->get_rate) ++ return pll->ops->get_rate(pll, parent_rate); ++ ++ return 0; ++} ++EXPORT_SYMBOL(clk_imx_pll_get_rate); ++ ++int clk_imx_pll_set_rate(struct clk_imx_pll *pll, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ if (pll && pll->ops && pll->ops->set_rate) ++ return pll->ops->set_rate(pll, rate, parent_rate); ++ ++ return -IMX_CLK_PLL_INVALID_PARAM; ++} ++EXPORT_SYMBOL(clk_imx_pll_set_rate); ++ ++int imx_pll_register(struct clk_imx_pll *pll, const char *name) ++{ ++ int i; ++ ++ strncpy(pll->pll_name, name, MAX_PLL_NAME_SIZE); ++ ++ for (i = 0; i < MAX_IMX_PLL_NUM; i++) { ++ if (!clk_imx_pll_array[i]) { ++ clk_imx_pll_array[i] = pll; ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} +diff --git a/drivers/clk/imx/clk-pll.h b/drivers/clk/imx/clk-pll.h +new file mode 100644 +index 000000000000..f12167842059 +--- /dev/null ++++ b/drivers/clk/imx/clk-pll.h +@@ -0,0 +1,33 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2018 NXP ++ * ++ */ ++ ++#ifndef __IMX_CLK_PLL_H ++#define __IMX_CLK_PLL_H ++ ++#include ++ ++#define MAX_IMX_PLL_NUM 16 ++#define MAX_PLL_NAME_SIZE 64 ++ ++struct clk_imx_pll; ++ ++struct clk_imx_pll_ops { ++ int (*set_rate)(struct clk_imx_pll *pll, unsigned long rate, ++ unsigned long parent_rate); ++ unsigned long (*get_rate)(struct clk_imx_pll *pll, ++ unsigned long parent_rate); ++ int (*adjust)(struct clk_imx_pll *pll, int *ppb); ++ void (*init)(struct clk_imx_pll *pll); ++}; ++ ++struct clk_imx_pll { ++ char pll_name[MAX_PLL_NAME_SIZE]; ++ const struct clk_imx_pll_ops *ops; ++}; ++ ++int imx_pll_register(struct clk_imx_pll *pll, const char *name); ++ ++#endif /*__IMX_CLK_PLL_H*/ +diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c +index c3a29e0d345e..50b848f23fd4 100644 +--- a/drivers/clk/imx/clk-pll14xx.c ++++ b/drivers/clk/imx/clk-pll14xx.c +@@ -16,6 +16,7 @@ + #include + + #include "clk.h" ++#include "clk-pll.h" + + #define GNRL_CTL 0x0 + #define DIV_CTL0 0x4 +@@ -37,8 +38,13 @@ + struct clk_pll14xx { + struct clk_hw hw; + void __iomem *base; ++ u32 orig_mdiv; ++ u32 orig_pdiv; ++ u32 orig_sdiv; ++ short int orig_kdiv; + enum imx_pll14xx_type type; + const struct imx_pll14xx_rate_table *rate_table; ++ struct clk_imx_pll imx_pll; + int rate_count; + }; + +@@ -519,6 +525,113 @@ static const struct clk_ops clk_pll1443x_ops = { + .set_rate = clk_pll1443x_set_rate, + }; + ++/* This function fetches the original PLL parameters to use ++ * them later for ppb adjustment ++ */ ++static void imx_pll1443x_init(struct clk_imx_pll *pll) ++{ ++ struct clk_pll14xx *pll14xx; ++ u32 pll_div_ctl0, pll_div_ctl1; ++ ++ pll14xx = (struct clk_pll14xx *) container_of(pll, ++ struct clk_pll14xx, imx_pll); ++ ++ pll_div_ctl0 = readl_relaxed(pll14xx->base + DIV_CTL0); ++ pll_div_ctl1 = readl_relaxed(pll14xx->base + DIV_CTL1); ++ ++ pll14xx->orig_kdiv = FIELD_GET(KDIV_MASK, pll_div_ctl1); ++ pll14xx->orig_mdiv = FIELD_GET(MDIV_MASK, pll_div_ctl0); ++ pll14xx->orig_pdiv = FIELD_GET(PDIV_MASK, pll_div_ctl0); ++ pll14xx->orig_sdiv = FIELD_GET(SDIV_MASK, pll_div_ctl0); ++} ++ ++/** ++ * imx_pll1443x_adjust - Adjust the Audio pll by ppb. ++ * ++ * This function adjust the audio pll by ppb (part per billion) and returns ++ * the exact number of ppb adjusted. ++ * The adjustment is done by only modifying the delta-sigma modulator(DSM) part ++ * of the audio PLL. ++ * Since the pllout = (parent_rate * (m + k/65536)) / (p * 2^s) ++ * and the adjusted value is ++ * pllout_new = pllout * (1 + ppb/1e9) which equals: ++ * (parent_rate * (m + k_new/65536)) / (p * 2^s) ++ * The new DSM (k_new) is calculated as the following: ++ * k_new = (1e9 * k + (m * 65536 + k) * ppb) / (1e9) ++ */ ++ ++static int imx_pll1443x_adjust(struct clk_imx_pll *pll, int *ppb) ++{ ++ s64 temp64; ++ s64 applied_ppb; ++ struct clk_pll14xx *pll14xx; ++ int rc = IMX_CLK_PLL_SUCCESS; ++ ++ int req_ppb = *ppb; ++ ++ pll14xx = (struct clk_pll14xx *) container_of(pll, ++ struct clk_pll14xx, imx_pll); ++ ++ /* Calcultate the new DSM value */ ++ temp64 = ((s64) pll14xx->orig_kdiv * 1000000000) ++ + ((s64) pll14xx->orig_mdiv * 65536 + (s64) pll14xx->orig_kdiv) * req_ppb; ++ ++ temp64 = div_s64(temp64, 1000000000); ++ ++ if (temp64 > KDIV_MAX || temp64 < KDIV_MIN) { ++ rc = -IMX_CLK_PLL_PREC_ERR; ++ goto exit; ++ } ++ ++ /* Write the PLL control settings with the new DSM ++ */ ++ ++ writel_relaxed(FIELD_PREP(KDIV_MASK, temp64), pll14xx->base + DIV_CTL1); ++ ++ /* Calculate and return the actual applied ppb */ ++ applied_ppb = div64_s64((s64) (temp64 - pll14xx->orig_kdiv) * 1000000000, ++ pll14xx->orig_kdiv + 65536 * (s64) pll14xx->orig_mdiv); ++ ++ *ppb = (int) applied_ppb; ++ ++ exit: ++ return rc; ++} ++ ++static unsigned long imx_pll1443x_get_rate(struct clk_imx_pll *pll, ++ unsigned long parent_rate) ++{ ++ struct clk_pll14xx *pll14xx; ++ ++ pll14xx = (struct clk_pll14xx *) container_of(pll, ++ struct clk_pll14xx, imx_pll); ++ ++ return clk_pll14xx_recalc_rate(&pll14xx->hw, parent_rate); ++} ++ ++static int imx_pll1443x_set_rate(struct clk_imx_pll *pll, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_pll14xx *pll14xx; ++ int rc = IMX_CLK_PLL_SUCCESS; ++ ++ pll14xx = (struct clk_pll14xx *) container_of(pll, ++ struct clk_pll14xx, imx_pll); ++ ++ if (clk_pll1443x_set_rate(&pll14xx->hw, rate, parent_rate) < 0) ++ rc = -IMX_CLK_PLL_INVALID_PARAM; ++ ++ return rc; ++} ++ ++static const struct clk_imx_pll_ops imx_clk_pll1443x_ops = { ++ .set_rate = imx_pll1443x_set_rate, ++ .get_rate = imx_pll1443x_get_rate, ++ .adjust = imx_pll1443x_adjust, ++ .init = imx_pll1443x_init, ++}; ++ ++ + struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, + const char *parent_name, void __iomem *base, + const struct imx_pll14xx_clk *pll_clk) +@@ -573,6 +686,13 @@ struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, + return ERR_PTR(ret); + } + ++ if (pll_clk->type == PLL_1443X) { ++ pll->imx_pll.ops = &imx_clk_pll1443x_ops; ++ ++ if (imx_pll_register(&pll->imx_pll, name) < 0) ++ pr_warn("Failed to register %s into imx pll\n", name); ++ } ++ + return hw; + } + EXPORT_SYMBOL_GPL(imx_dev_clk_hw_pll14xx); +diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c +index aeabb9b15675..dbd5f081d1e7 100644 +--- a/drivers/clk/imx/clk-pllv3.c ++++ b/drivers/clk/imx/clk-pllv3.c +@@ -14,6 +14,7 @@ + #include + #include + #include "clk.h" ++#include "clk-pll.h" + + #define PLL_NUM_OFFSET 0x10 + #define PLL_DENOM_OFFSET 0x20 +@@ -47,6 +48,10 @@ + */ + struct clk_pllv3 { + struct clk_hw hw; ++ struct clk_imx_pll imx_pll; ++ u32 orig_num; ++ u32 orig_denom; ++ u32 orig_div; + void __iomem *base; + u32 power_bit; + bool powerup_set; +@@ -440,6 +445,106 @@ static const struct clk_ops clk_pllv3_vf610_ops = { + .set_rate = clk_pllv3_vf610_set_rate, + }; + ++/** ++ * imx_pllv3_av_adjust - Adjust the Audio pll by ppb. ++ * ++ * This function adjust the audio pll by ppb (part per billion) and returns ++ * the exact number of ppb adjusted. ++ * The adjustment is done by only modifying the Audio PLL Numerator. ++ * Since the pllout = parent * (div + num/denom) and the adjusted Value is ++ * pllout_new = PLL_out * (1 + ppb/1e9) ++ * Also pllout_new = parent * (div + new_num/denom) ++ * The new numerator is calculated as the following: ++ * new_num = (div * ppb * denom + num * 1e9 + num * ppb) / (1e9) ++ */ ++ ++static int imx_pllv3_av_adjust(struct clk_imx_pll *pll, int *ppb) ++{ ++ struct clk_pllv3 *av_pll; ++ u64 temp64; ++ s32 applied_ppb; ++ int rc = IMX_CLK_PLL_SUCCESS; ++ ++ int req_ppb = *ppb; ++ ++ av_pll = (struct clk_pllv3 *) container_of(pll, struct clk_pllv3, ++ imx_pll); ++ ++ /*Calcultate the new PLL Numerator*/ ++ temp64 = (u64) av_pll->orig_denom * av_pll->orig_div * req_ppb ++ + (u64) av_pll->orig_num * 1000000000 ++ + (u64) av_pll->orig_num * req_ppb; ++ ++ do_div(temp64, 1000000000); ++ ++ if (temp64 >= av_pll->orig_denom) { ++ rc = -IMX_CLK_PLL_PREC_ERR; ++ goto exit; ++ } ++ ++ /*Write the new PLL num*/ ++ writel_relaxed((u32) temp64, av_pll->base + av_pll->num_offset); ++ ++ /*Calculate and return the actual applied ppb*/ ++ applied_ppb = div64_s64((s64) (temp64 - av_pll->orig_num) * 1000000000, ++ av_pll->orig_num ++ + (s64) av_pll->orig_denom * av_pll->orig_div); ++ ++ *ppb = (int) applied_ppb; ++ ++exit: ++ return rc; ++ ++} ++ ++/* This function fetches the original PLL parameters to use ++ * them later for ppb adjustment ++ */ ++static void imx_pllv3_av_init(struct clk_imx_pll *pll) ++{ ++ struct clk_pllv3 *av_pll; ++ ++ av_pll = (struct clk_pllv3 *) container_of(pll, struct clk_pllv3, ++ imx_pll); ++ ++ av_pll->orig_num = readl_relaxed(av_pll->base + av_pll->num_offset); ++ av_pll->orig_denom = readl_relaxed(av_pll->base + av_pll->denom_offset); ++ av_pll->orig_div = readl_relaxed(av_pll->base) & av_pll->div_mask; ++} ++ ++static unsigned long imx_pllv3_av_get_rate(struct clk_imx_pll *pll, ++ unsigned long parent_rate) ++{ ++ struct clk_pllv3 *av_pll; ++ ++ av_pll = (struct clk_pllv3 *) container_of(pll, struct clk_pllv3, ++ imx_pll); ++ ++ return clk_pllv3_av_recalc_rate(&av_pll->hw, parent_rate); ++} ++ ++static int imx_pllv3_av_set_rate(struct clk_imx_pll *pll, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_pllv3 *av_pll; ++ int rc = IMX_CLK_PLL_SUCCESS; ++ ++ av_pll = (struct clk_pllv3 *) container_of(pll, struct clk_pllv3, ++ imx_pll); ++ ++ if (clk_pllv3_av_set_rate(&av_pll->hw, rate, parent_rate) < 0) ++ rc = -IMX_CLK_PLL_INVALID_PARAM; ++ ++ return rc; ++} ++ ++static const struct clk_imx_pll_ops imx_clk_pllv3_av_ops = { ++ .set_rate = imx_pllv3_av_set_rate, ++ .get_rate = imx_pllv3_av_get_rate, ++ .adjust = imx_pllv3_av_adjust, ++ .init = imx_pllv3_av_init, ++}; ++ + static unsigned long clk_pllv3_enet_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) + { +@@ -495,6 +600,7 @@ struct clk_hw *imx_clk_hw_pllv3(enum imx_pllv3_type type, const char *name, + fallthrough; + case IMX_PLLV3_AV: + ops = &clk_pllv3_av_ops; ++ pll->imx_pll.ops = &imx_clk_pllv3_av_ops; + break; + case IMX_PLLV3_ENET_IMX7: + pll->power_bit = IMX7_ENET_PLL_POWER; +@@ -532,5 +638,8 @@ struct clk_hw *imx_clk_hw_pllv3(enum imx_pllv3_type type, const char *name, + return ERR_PTR(ret); + } + ++ if (imx_pll_register(&pll->imx_pll, name) < 0) ++ pr_warn("Failed to register %s into imx pll\n", name); ++ + return hw; + } +diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c +index 6c54017529d5..bc683fb0dda9 100644 +--- a/drivers/clk/imx/clk-scu.c ++++ b/drivers/clk/imx/clk-scu.c +@@ -18,6 +18,7 @@ + #include + + #include "clk-scu.h" ++#include "clk-pll.h" + + #define IMX_SIP_CPUFREQ 0xC2000001 + #define IMX_SIP_SET_CPUFREQ 0x00 +@@ -56,6 +57,8 @@ struct clk_scu { + u8 parent_index; + bool is_enabled; + u32 rate; ++ unsigned long original_rate; ++ struct clk_imx_pll imx_pll; + }; + + /* +@@ -449,6 +452,82 @@ static const struct clk_ops clk_scu_pi_ops = { + .set_rate = clk_scu_set_rate, + }; + ++static void imx_scu_init(struct clk_imx_pll *pll) ++{ ++ struct clk_scu *clk; ++ ++ clk = (struct clk_scu *) container_of(pll, struct clk_scu, imx_pll); ++ ++ clk->original_rate = clk_scu_recalc_rate(&clk->hw, 0); ++ ++ pr_info("SCU clock %p for PLL API init with original rate %lu\n", ++ clk, clk->original_rate); ++ ++ return; ++} ++ ++/* SCU is stating possible variation of +/-250Khz which gives around 300 ppm @ 786MHz ++ * Set a maximum adjustement at 250 ppm ++ */ ++#define IMX_SCU_MAX_PPB_ADJUSTEMENT 250000 ++ ++static int imx_scu_adjust(struct clk_imx_pll *pll, int *ppb) ++{ ++ struct clk_scu * clk = (struct clk_scu *) container_of(pll, ++ struct clk_scu, imx_pll); ++ unsigned long new_rate; ++ int delta; ++ ++ if (!clk->original_rate) { ++ pr_warn_once("failed to get original rate for clock\n"); ++ return -IMX_CLK_PLL_LOCK_ERR; ++ } ++ ++ if (abs(*ppb) > IMX_SCU_MAX_PPB_ADJUSTEMENT) { ++ return -IMX_CLK_PLL_PREC_ERR; ++ } ++ ++ if (*ppb < 0) ++ delta = -1; ++ else ++ delta = 1; ++ ++ delta *= (int)div64_u64((u64)clk->original_rate * (u64)abs(*ppb), ++ 1000000000); ++ ++ new_rate = clk->original_rate + delta; ++ ++ if (clk_scu_set_rate(&clk->hw, new_rate, 0) < 0) ++ return -IMX_CLK_PLL_INVALID_PARAM; ++ ++ return IMX_CLK_PLL_SUCCESS; ++} ++ ++static int imx_scu_set_rate(struct clk_imx_pll *pll, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_scu * clk = (struct clk_scu *) container_of(pll, ++ struct clk_scu, imx_pll); ++ ++ return clk_scu_set_rate(&clk->hw, rate, 0); ++} ++ ++static unsigned long imx_scu_get_rate(struct clk_imx_pll *pll, ++ unsigned long parent_rate) ++{ ++ struct clk_scu * clk = (struct clk_scu *) container_of(pll, ++ struct clk_scu, imx_pll); ++ ++ return clk_scu_recalc_rate(&clk->hw, 0); ++} ++ ++static const struct clk_imx_pll_ops imx_clk_scu_ops = { ++ .set_rate = imx_scu_set_rate, ++ .get_rate = imx_scu_get_rate, ++ .adjust = imx_scu_adjust, ++ .init = imx_scu_init, ++}; ++ + struct clk_hw *__imx_clk_scu(struct device *dev, const char *name, + const char * const *parents, int num_parents, + u32 rsrc_id, u8 clk_type) +@@ -497,6 +576,13 @@ struct clk_hw *__imx_clk_scu(struct device *dev, const char *name, + if (dev) + dev_set_drvdata(dev, clk); + ++ if (rsrc_id == IMX_SC_R_AUDIO_PLL_0 && clk_type == IMX_SC_PM_CLK_PLL) { ++ clk->imx_pll.ops = &imx_clk_scu_ops; ++ ++ if (imx_pll_register(&clk->imx_pll, name) < 0) ++ pr_warn("%s: failed to register %s into imx pll\n", __func__, name); ++ } ++ + return hw; + } + +diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c +index fbef30aa49ba..ce31ff3aab1c 100755 +--- a/drivers/firmware/imx/scu-pd.c ++++ b/drivers/firmware/imx/scu-pd.c +@@ -147,6 +147,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { + { "esai1", IMX_SC_R_ESAI_1, 1, false, 0 }, + { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, + { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 }, ++ { "gpt", IMX_SC_R_GPT_5, 4, true, 5 }, + { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, + { "sai3", IMX_SC_R_SAI_3, 1, false, 0 }, + { "sai4", IMX_SC_R_SAI_4, 1, false, 0 }, +diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig +index 6b10868ec72f..1fbdb7b4e6e1 100644 +--- a/drivers/gpu/drm/i915/Kconfig ++++ b/drivers/gpu/drm/i915/Kconfig +@@ -3,7 +3,6 @@ config DRM_I915 + tristate "Intel 8xx/9xx/G3x/G4x/HD Graphics" + depends on DRM + depends on X86 && PCI +- depends on !PREEMPT_RT + select INTEL_GTT if X86 + select INTERVAL_TREE + # we need shmfs for the swappable backing store, and in particular +diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c +index 6792a9056f46..43cedfef104f 100644 +--- a/drivers/gpu/drm/i915/display/intel_crtc.c ++++ b/drivers/gpu/drm/i915/display/intel_crtc.c +@@ -521,7 +521,8 @@ void intel_pipe_update_start(struct intel_crtc_state *new_crtc_state) + */ + intel_psr_wait_for_idle_locked(new_crtc_state); + +- local_irq_disable(); ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) ++ local_irq_disable(); + + crtc->debug.min_vbl = min; + crtc->debug.max_vbl = max; +@@ -546,11 +547,13 @@ void intel_pipe_update_start(struct intel_crtc_state *new_crtc_state) + break; + } + +- local_irq_enable(); ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) ++ local_irq_enable(); + + timeout = schedule_timeout(timeout); + +- local_irq_disable(); ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) ++ local_irq_disable(); + } + + finish_wait(wq, &wait); +@@ -583,7 +586,8 @@ void intel_pipe_update_start(struct intel_crtc_state *new_crtc_state) + return; + + irq_disable: +- local_irq_disable(); ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) ++ local_irq_disable(); + } + + #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_VBLANK_EVADE) +@@ -684,7 +688,8 @@ void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state) + */ + intel_vrr_send_push(new_crtc_state); + +- local_irq_enable(); ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) ++ local_irq_enable(); + + if (intel_vgpu_active(dev_priv)) + return; +diff --git a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c +index ecc990ec1b95..8d04b10681f0 100644 +--- a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c ++++ b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c +@@ -312,10 +312,9 @@ void __intel_breadcrumbs_park(struct intel_breadcrumbs *b) + /* Kick the work once more to drain the signalers, and disarm the irq */ + irq_work_sync(&b->irq_work); + while (READ_ONCE(b->irq_armed) && !atomic_read(&b->active)) { +- local_irq_disable(); +- signal_irq_work(&b->irq_work); +- local_irq_enable(); ++ irq_work_queue(&b->irq_work); + cond_resched(); ++ irq_work_sync(&b->irq_work); + } + } + +diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +index fc4a84628985..fc937697fe14 100644 +--- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c ++++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +@@ -1302,7 +1302,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + * and context switches) submission. + */ + +- spin_lock(&sched_engine->lock); ++ spin_lock_irq(&sched_engine->lock); + + /* + * If the queue is higher priority than the last +@@ -1402,7 +1402,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + * Even if ELSP[1] is occupied and not worthy + * of timeslices, our queue might be. + */ +- spin_unlock(&sched_engine->lock); ++ spin_unlock_irq(&sched_engine->lock); + return; + } + } +@@ -1428,7 +1428,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + + if (last && !can_merge_rq(last, rq)) { + spin_unlock(&ve->base.sched_engine->lock); +- spin_unlock(&engine->sched_engine->lock); ++ spin_unlock_irq(&engine->sched_engine->lock); + return; /* leave this for another sibling */ + } + +@@ -1590,7 +1590,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + */ + sched_engine->queue_priority_hint = queue_prio(sched_engine); + i915_sched_engine_reset_on_empty(sched_engine); +- spin_unlock(&sched_engine->lock); ++ spin_unlock_irq(&sched_engine->lock); + + /* + * We can skip poking the HW if we ended up with exactly the same set +@@ -1616,13 +1616,6 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + } + } + +-static void execlists_dequeue_irq(struct intel_engine_cs *engine) +-{ +- local_irq_disable(); /* Suspend interrupts across request submission */ +- execlists_dequeue(engine); +- local_irq_enable(); /* flush irq_work (e.g. breadcrumb enabling) */ +-} +- + static void clear_ports(struct i915_request **ports, int count) + { + memset_p((void **)ports, NULL, count); +@@ -2476,7 +2469,7 @@ static void execlists_submission_tasklet(struct tasklet_struct *t) + } + + if (!engine->execlists.pending[0]) { +- execlists_dequeue_irq(engine); ++ execlists_dequeue(engine); + start_timeslice(engine); + } + +diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c +index f93ffa6626a5..6e9d033cf808 100644 +--- a/drivers/gpu/drm/i915/i915_irq.c ++++ b/drivers/gpu/drm/i915/i915_irq.c +@@ -917,7 +917,8 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, + */ + spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); + +- /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ ++ if (IS_ENABLED(CONFIG_PREEMPT_RT)) ++ preempt_disable(); + + /* Get optional system timestamp before query. */ + if (stime) +@@ -981,7 +982,8 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, + if (etime) + *etime = ktime_get(); + +- /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ ++ if (IS_ENABLED(CONFIG_PREEMPT_RT)) ++ preempt_enable(); + + spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); + +diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c +index 7ce126a01cbf..64a032dfaa90 100644 +--- a/drivers/gpu/drm/i915/i915_request.c ++++ b/drivers/gpu/drm/i915/i915_request.c +@@ -609,7 +609,6 @@ bool __i915_request_submit(struct i915_request *request) + + RQ_TRACE(request, "\n"); + +- GEM_BUG_ON(!irqs_disabled()); + lockdep_assert_held(&engine->sched_engine->lock); + + /* +@@ -718,7 +717,6 @@ void __i915_request_unsubmit(struct i915_request *request) + */ + RQ_TRACE(request, "\n"); + +- GEM_BUG_ON(!irqs_disabled()); + lockdep_assert_held(&engine->sched_engine->lock); + + /* +diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h +index 37b5c9e9d260..73f29d8008f0 100644 +--- a/drivers/gpu/drm/i915/i915_trace.h ++++ b/drivers/gpu/drm/i915/i915_trace.h +@@ -6,6 +6,10 @@ + #if !defined(_I915_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) + #define _I915_TRACE_H_ + ++#ifdef CONFIG_PREEMPT_RT ++#define NOTRACE ++#endif ++ + #include + #include + #include +@@ -323,7 +327,7 @@ DEFINE_EVENT(i915_request, i915_request_add, + TP_ARGS(rq) + ); + +-#if defined(CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS) ++#if defined(CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS) && !defined(NOTRACE) + DEFINE_EVENT(i915_request, i915_request_guc_submit, + TP_PROTO(struct i915_request *rq), + TP_ARGS(rq) +diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h +index 6c14d13364bf..de58855e6926 100644 +--- a/drivers/gpu/drm/i915/i915_utils.h ++++ b/drivers/gpu/drm/i915/i915_utils.h +@@ -294,7 +294,7 @@ wait_remaining_ms_from_jiffies(unsigned long timestamp_jiffies, int to_wait_ms) + #define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 10, 1000) + + /* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. */ +-#if defined(CONFIG_DRM_I915_DEBUG) && defined(CONFIG_PREEMPT_COUNT) ++#if defined(CONFIG_DRM_I915_DEBUG) && defined(CONFIG_PREEMPT_COUNT) && !defined(CONFIG_PREEMPT_RT) + # define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) WARN_ON_ONCE((ATOMIC) && !in_atomic()) + #else + # define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) do { } while (0) +diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig +index 1602a37db178..b11fdbf93b80 100644 +--- a/drivers/irqchip/Kconfig ++++ b/drivers/irqchip/Kconfig +@@ -706,4 +706,8 @@ config SUNPLUS_SP7021_INTC + chained controller, routing all interrupt source in P-Chip to + the primary controller on C-Chip. + ++config BAREMETAL ++ bool ++ depends on ARM_GIC || ARM_GIC_V3 ++ + endmenu +diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile +index 87b49a10962c..1c6fe6f29519 100644 +--- a/drivers/irqchip/Makefile ++++ b/drivers/irqchip/Makefile +@@ -121,3 +121,4 @@ obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o + obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o + obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o + obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o ++obj-$(CONFIG_BAREMETAL) += ipi-baremetal.o +\ No newline at end of file +diff --git a/drivers/irqchip/ipi-baremetal.c b/drivers/irqchip/ipi-baremetal.c +new file mode 100644 +index 000000000000..58f88546b6f0 +--- /dev/null ++++ b/drivers/irqchip/ipi-baremetal.c +@@ -0,0 +1,454 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * IPI for inter-core communiction for NXP Layerscape baremetal ++ * ++ * Copyright 2018-2023 NXP ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++int pid; ++int mycoreid; ++ ++#undef IPI_BAREMETAL_SIGNAL ++ ++#define DEVICE_NAME "ipi_bm" ++ ++/* ++ * choose 50 as baremetal signal number. ++ * real-time signals are in the range of 33 to 64. ++ */ ++#define SIG_BM 50 ++ ++#ifndef IPI_BAREMETAL_SIGNAL ++void __iomem *share_base; ++#ifdef CONFIG_SOC_IMX6Q_BAREMETAL ++#define GICD_BASE 0x00A01000 ++#define GICD_SIZE 0x1000 ++#define GICC_BASE 0x00A00100 ++#define GICC_SIZE 0x1000 ++#define GIC_DIST_IGROUP 0x080 ++#define GIC_DIST_CTRL 0x000 ++#define GIC_CPU_CTRL 0x00 ++#define GICD_ENABLE 0x3 ++#define GICC_ENABLE 0x7 ++#endif ++#if defined(CONFIG_SOC_IMX6Q_BAREMETAL) ++#define CONFIG_SYS_DDR_SDRAM_BASE 0x10000000UL ++#define CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE (128 * 1024 * 1024) ++#define CONFIG_SYS_DDR_SDRAM_MASTER_SIZE (512 * 1024 * 1024) ++#elif defined(CONFIG_LX2160A_BAREMETAL) ++#define CONFIG_SYS_DDR_SDRAM_BASE 0x80000000UL ++#define CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE (64 * 1024 * 1024) ++#define CONFIG_SYS_DDR_SDRAM_MASTER_SIZE (512 * 1024 * 1024) ++#elif defined(CONFIG_IMX8M_BAREMETAL) ++#define CONFIG_SYS_DDR_SDRAM_SLAVE_ADDR (0x60000000) ++#define CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE (32 * 1024 * 1024) ++#define CONFIG_SYS_DDR_SDRAM_SLAVE_RESERVE_SIZE (32 * 1024 *1024) ++#elif defined(CONFIG_IMX93_BAREMETAL) ++#define CONFIG_SYS_DDR_SDRAM_SLAVE_ADDR (0xb0000000) ++#define CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE (32 * 1024 * 1024) ++#define CONFIG_SYS_DDR_SDRAM_SLAVE_RESERVE_SIZE (32 * 1024 *1024) ++#else ++#define CONFIG_SYS_DDR_SDRAM_BASE 0x80000000UL ++#define CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE (256 * 1024 * 1024) ++#define CONFIG_SYS_DDR_SDRAM_MASTER_SIZE (512 * 1024 * 1024) ++#endif ++ ++#if defined(CONFIG_IMX8M_BAREMETAL) || defined(CONFIG_IMX93_BAREMETAL) ++#define CONFIG_SYS_DDR_SDRAM_SHARE_BASE (CONFIG_SYS_DDR_SDRAM_SLAVE_ADDR \ ++ + CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE*(CONFIG_MAX_CPUS-1)) ++#define CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE (4 * 1024 * 1024) ++#else ++#define CONFIG_SYS_DDR_SDRAM_SHARE_BASE \ ++ (CONFIG_SYS_DDR_SDRAM_BASE + CONFIG_SYS_DDR_SDRAM_MASTER_SIZE \ ++ + CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE * (CONFIG_MAX_CPUS - 1)) ++ ++#define CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE (16 * 1024 * 1024) ++#endif ++ ++#if defined(CONFIG_SOC_IMX6Q_BAREMETAL) ++#define CONFIG_SYS_DDR_SDRAM_SHARE_SIZE \ ++ ((128 * 1024 * 1024) - CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE) ++#elif defined(CONFIG_LX2160A_BAREMETAL) ++#define CONFIG_SYS_DDR_SDRAM_SHARE_SIZE \ ++ ((64 * 1024 * 1024) - CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE) ++#elif defined(CONFIG_IMX8M_BAREMETAL) || defined(CONFIG_IMX93_BAREMETAL) ++#define CONFIG_SYS_DDR_SDRAM_SHARE_SIZE (CONFIG_SYS_DDR_SDRAM_SLAVE_RESERVE_SIZE \ ++ - CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE) ++#else ++#define CONFIG_SYS_DDR_SDRAM_SHARE_SIZE \ ++ ((256 * 1024 * 1024) - CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE) ++#endif ++#define CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_BASE \ ++ (CONFIG_SYS_DDR_SDRAM_SHARE_BASE + CONFIG_SYS_DDR_SDRAM_SHARE_SIZE) ++ ++/* number of descriptor for each ring */ ++#define ICC_RING_ENTRY 128 ++/* size of each block */ ++#define ICC_BLOCK_UNIT_SIZE (4 * 1024) ++/* 2M space for core's ring and desc struct */ ++#define ICC_RING_DESC_SPACE (2 * 1024 * 1024) ++ ++/* share memory size for each core icc */ ++#define ICC_CORE_MEM_SPACE (CONFIG_SYS_DDR_SDRAM_SHARE_SIZE / CONFIG_MAX_CPUS) ++/* share memory base for core x */ ++#define ICC_CORE_MEM_BASE_PHY(x) \ ++ (CONFIG_SYS_DDR_SDRAM_SHARE_BASE + (x) * ICC_CORE_MEM_SPACE) ++/* share memory base for core x */ ++#define ICC_CORE_MEM_BASE(x) \ ++ ((unsigned long)share_base + (x) * ICC_CORE_MEM_SPACE) ++/* the ring struct addr of core x ring y */ ++#define ICC_CORE_RING_BASE(x, y) \ ++ (ICC_CORE_MEM_BASE(x) + (y) * sizeof(struct icc_ring)) ++/* the desc struct addr of core x */ ++#define ICC_CORE_DESC_BASE_PHY(x) \ ++ (ICC_CORE_MEM_BASE_PHY(x) + CONFIG_MAX_CPUS * sizeof(struct icc_ring)) ++/* ++ * The core x block memory base addr for icc data transfer. ++ * The beginning 2M space of core x icc memory is for ++ * core x ring and desc struct. ++ */ ++#define ICC_CORE_BLOCK_BASE_PHY(x) \ ++ (ICC_CORE_MEM_BASE_PHY(x) + ICC_RING_DESC_SPACE) ++#define ICC_CORE_BLOCK_BASE(x) (ICC_CORE_MEM_BASE(x) + ICC_RING_DESC_SPACE) ++#define ICC_CORE_BLOCK_END_PHY(x) \ ++ (ICC_CORE_MEM_BASE_PHY(x) + ICC_CORE_MEM_SPACE) ++#define ICC_CORE_BLOCK_END(x) (ICC_CORE_MEM_BASE(x) + ICC_CORE_MEM_SPACE) ++#define ICC_CORE_BLOCK_COUNT \ ++ ((ICC_CORE_MEM_SPACE - ICC_RING_DESC_SPACE)/ICC_BLOCK_UNIT_SIZE) ++ ++#define ICC_PHY2VIRT(x) \ ++ (((void *)x - ICC_CORE_MEM_BASE_PHY(mycoreid)) \ ++ + ICC_CORE_MEM_BASE(mycoreid)) ++#define ICC_VIRT2PHY(x) \ ++ (((void *)x - ICC_CORE_MEM_BASE(mycoreid)) \ ++ + ICC_CORE_MEM_BASE_PHY(mycoreid)) ++ ++#define IPIDEV_IOCIRQ 1 ++ ++#define ICC_CMD_TX_DATA 0x00 ++#define ICC_CMD_DUMP_TIME 0x01 ++ ++struct icc_desc { ++ unsigned long block_addr; /* block address */ ++ unsigned int byte_count; /* available bytes */ ++ unsigned int option_mode; /* option mode for this icc irq */ ++}; ++ ++struct icc_ring { ++ unsigned int src_coreid; /* which core created the ring */ ++ unsigned int dest_coreid; /* which core the ring sends SGI to */ ++ unsigned int interrupt; /* which interrupt (SGI) be used */ ++ unsigned int desc_num; /* number of descriptor */ ++ struct icc_desc *desc; /* pointer of the first descriptor */ ++ unsigned int desc_head; /* modified by producer */ ++ unsigned int desc_tail; /* modified by consumer */ ++ unsigned long busy_counts; /* statistic: ring full */ ++ unsigned long interrupt_counts; /* statistic: total sent number */ ++ unsigned int irq_status; /* status of the ring, set by producer, reset by consumer */ ++}; ++#endif ++ ++int ipi_baremetal_open(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++ssize_t ipi_baremetal_read(struct file *file, ++ char __user *buff, size_t count, loff_t *offp) ++{ ++ return 0; ++} ++ ++ssize_t ipi_baremetal_write(struct file *file, ++ const char __user *buff, size_t count, loff_t *offp) ++{ ++ char mybuf[10]; ++ int ret; ++ ++ /* read the value from user space */ ++ if (count > 10) ++ return -EINVAL; ++ copy_from_user(mybuf, buff, count); ++ ret = kstrtoint(mybuf, 0, &pid); ++ if (ret) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int ipi_baremetal_release(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++#ifndef CONFIG_LS1021A_BAREMETAL ++static long ipi_baremetal_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ unsigned long val; ++ unsigned long __user *argp = (unsigned long __user *)arg; ++ int err; ++#if defined(CONFIG_LX2160A_BAREMETAL) ++ unsigned long i, cluster, mask; ++#endif ++ ++ err = copy_from_user(&val, argp, sizeof(val)); ++ if (err) ++ return -EFAULT; ++ ++ val |= ((unsigned long)1 << 40); ++ ++#if defined(CONFIG_LX2160A_BAREMETAL) ++ val = *(unsigned long *)arg; ++ ++ for (i = 0; i < 16; i++) { ++ if ((val >> i) & 0x1) { ++ cluster = i / 2; ++ mask = i % 2; ++ val |= (1 << mask) | (cluster << 16); ++ } ++ } ++#endif ++ switch (cmd) { ++ case IPIDEV_IOCIRQ: ++ write_sysreg_s(val, SYS_ICC_SGI1R_EL1); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++#endif ++ ++static int icc_ring_empty(struct icc_ring *ring) ++{ ++ if (ring->desc_tail == ring->desc_head) ++ return 1; ++ return 0; ++} ++ ++/* how many rx blocks are valid waiting to be handled */ ++static int icc_ring_valid(struct icc_ring *ring) ++{ ++ int valid; ++ ++ if (icc_ring_empty(ring)) ++ return 0; ++ ++ if (ring->desc_head > ring->desc_tail) ++ valid = ring->desc_head - ring->desc_tail; ++ else ++ valid = ring->desc_num - ring->desc_tail + ring->desc_head; ++ return valid; ++} ++ ++int ipi_baremetal_handle(u32 irqnr, u32 irqsrc) ++{ ++#ifdef IPI_BAREMETAL_SIGNAL ++ struct siginfo info; ++ struct task_struct *t; ++ int si_data; ++ int ret; ++ ++ if (!pid) ++ return 0; ++ ++ si_data = (irqnr << 16) | irqsrc; ++ /* send the signal */ ++ memset(&info, 0, sizeof(struct siginfo)); ++ info.si_signo = SIG_BM; ++ info.si_code = SI_QUEUE; ++ info.si_int = si_data; ++ ++ rcu_read_lock(); ++ /* find the task_struct associated with this pid */ ++ t = find_task_by_vpid(pid); ++ if (t == NULL) { ++ pr_info("no such pid\n"); ++ rcu_read_unlock(); ++ return -ENODEV; ++ } ++ rcu_read_unlock(); ++ ++ /* send the signal */ ++ ret = send_sig_info(SIG_BM, &info, t); ++ if (ret < 0) { ++ pr_info("error sending signal\n"); ++ return ret; ++ } ++#else ++ struct icc_ring *ring; ++ struct icc_desc *desc; ++ struct icc_desc *desc_phy; ++ unsigned long block_addr; ++ unsigned int byte_count, option_mode; ++ int i, valid; ++ int hw_irq, src_coreid; ++ ++ hw_irq = irqnr; ++ src_coreid = irqsrc; ++ ++ if (src_coreid == mycoreid) { ++ pr_err("Do not support self-icc now!\n"); ++ return -1; ++ } ++ ++ /* get the ring for this core from source core */ ++ ring = (struct icc_ring *)ICC_CORE_RING_BASE(src_coreid, mycoreid); ++ valid = icc_ring_valid(ring); ++ for (i = 0; i < valid; i++) { ++ desc_phy = ring->desc + ring->desc_tail; ++ desc = ICC_PHY2VIRT(desc_phy); ++ option_mode = desc->option_mode; ++ ++ if (option_mode & ICC_CMD_DUMP_TIME) { ++ pr_info("Get the SGI from CoreID: %d\n", src_coreid); ++ } else { ++ block_addr = desc->block_addr; ++ byte_count = desc->byte_count; ++ ++ if ((*(char *)ICC_PHY2VIRT(block_addr)) != 0x5a) ++ pr_info("Get the ICC from core %d; block: 0x%lx, bytes: %d, value: 0x%x\n", ++ src_coreid, block_addr, byte_count, ++ (*(char *)ICC_PHY2VIRT(block_addr))); ++ } ++ ++ /* add desc_tail */ ++ ring->desc_tail = (ring->desc_tail + 1) % ring->desc_num; ++ } ++#endif ++ return 0; ++} ++ ++static const struct vm_operations_struct shd_mmap_mem_ops = { ++#ifdef CONFIG_HAVE_IOREMAP_PROT ++ .access = generic_access_phys ++#endif ++}; ++ ++static int shd_mmap_mem(struct file *file, struct vm_area_struct *vma) ++{ ++ size_t size = vma->vm_end - vma->vm_start; ++ ++#if defined(CONFIG_LS1021A_BAREMETAL) || defined(CONFIG_SOC_IMX6Q_BAREMETAL) ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++#else ++ vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); ++#endif ++ vma->vm_ops = &shd_mmap_mem_ops; ++ ++ /* Remap-pfn-range will mark the range VM_IO */ ++ if (remap_pfn_range(vma, ++ vma->vm_start, ++ vma->vm_pgoff, ++ size, ++ vma->vm_page_prot)) { ++ return -EAGAIN; ++ } ++ return 0; ++} ++ ++const struct file_operations ipi_bm_ops = { ++ .owner = THIS_MODULE, ++ .open = ipi_baremetal_open, ++ .release = ipi_baremetal_release, ++ .read = ipi_baremetal_read, ++ .write = ipi_baremetal_write, ++ .mmap = shd_mmap_mem, ++#ifndef CONFIG_LS1021A_BAREMETAL ++ .unlocked_ioctl = ipi_baremetal_ioctl, ++#endif ++}; ++ ++static struct miscdevice ipi_bm_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = DEVICE_NAME, ++ .fops = &ipi_bm_ops, ++}; ++ ++#ifdef CONFIG_SOC_IMX6Q_BAREMETAL ++void gic_enable_dist(void) ++{ ++ void __iomem *gicd_base, *gicc_base; ++ ++ gicd_base = ioremap((phys_addr_t)GICD_BASE, GICD_SIZE); ++ if (!gicd_base) { ++ pr_err("failed to remap gicd base for ICC\n"); ++ return -ENOMEM; ++ } ++ gicc_base = ioremap((phys_addr_t)GICC_BASE, GICC_SIZE); ++ if (!gicc_base) { ++ pr_err("failed to remap gicc base for ICC\n"); ++ return -ENOMEM; ++ } ++ /* set the SGI interrupts for this core to group 1 */ ++ writel(0xffffffff, gicd_base + GIC_DIST_IGROUP); ++ writel(GICD_ENABLE, gicd_base + GIC_DIST_CTRL); ++ writel(GICC_ENABLE, gicc_base + GIC_CPU_CTRL); ++ iounmap(gicd_base); ++ iounmap(gicc_base); ++} ++#endif ++ ++static int __init ipi_baremetal_init(void) ++{ ++ int ret; ++ ++ pr_info("NXP inter-core communiction IRQ driver\n"); ++#ifndef IPI_BAREMETAL_SIGNAL ++#if defined(CONFIG_LS1021A_BAREMETAL) || defined(CONFIG_SOC_IMX6Q_BAREMETAL) ++ share_base = ioremap((phys_addr_t)CONFIG_SYS_DDR_SDRAM_SHARE_BASE, ++ CONFIG_SYS_DDR_SDRAM_SHARE_SIZE); ++#else ++ share_base = ioremap_cache((phys_addr_t)CONFIG_SYS_DDR_SDRAM_SHARE_BASE, ++ CONFIG_SYS_DDR_SDRAM_SHARE_SIZE); ++#endif ++ if (!share_base) { ++ pr_err("failed to remap share base (%lu/%u) for ICC\n", ++ CONFIG_SYS_DDR_SDRAM_SHARE_BASE, ++ CONFIG_SYS_DDR_SDRAM_SHARE_SIZE); ++ return -ENOMEM; ++ } ++ mycoreid = 0; ++#ifdef CONFIG_SOC_IMX6Q_BAREMETAL ++ gic_enable_dist(); ++#endif ++#endif ++ ret = misc_register(&ipi_bm_misc); ++ if (ret < 0) { ++ pr_info("Register ipi_bm error! ret: %d\n", ret); ++ return ret; ++ } ++ pr_info("ipi_bm device created!\n"); ++ return 0; ++} ++ ++static void __exit ipi_baremetal_exit(void) ++{ ++ pid = 0; ++#ifndef IPI_BAREMETAL_SIGNAL ++ iounmap(share_base); ++#endif ++ misc_deregister(&ipi_bm_misc); ++ pr_info("ipi_bm device deleted!\n"); ++} ++ ++module_init(ipi_baremetal_init); ++module_exit(ipi_baremetal_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("NXP"); ++MODULE_DESCRIPTION("NXP inter-core communiction IPI driver"); +diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c +index eccd6bb52a0b..9d06b08b8baf 100644 +--- a/drivers/irqchip/irq-gic-v3.c ++++ b/drivers/irqchip/irq-gic-v3.c +@@ -27,6 +27,10 @@ + #include + #include + ++#ifdef CONFIG_BAREMETAL ++#include ++#endif ++ + #include + #include + #include +@@ -1392,14 +1396,14 @@ static void __init gic_smp_init(void) + "irqchip/arm/gicv3:starting", + gic_starting_cpu, NULL); + +- /* Register all 8 non-secure SGIs */ +- base_sgi = __irq_domain_alloc_irqs(gic_data.domain, -1, 8, ++ /* Register all 16 non-secure SGIs */ ++ base_sgi = __irq_domain_alloc_irqs(gic_data.domain, -1, 16, + NUMA_NO_NODE, &sgi_fwspec, + false, NULL); + if (WARN_ON(base_sgi <= 0)) + return; + +- set_smp_ipi_range(base_sgi, 8); ++ set_smp_ipi_range(base_sgi, 16); + } + + static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, +diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c +index 4c7bae0ec8f9..40fa366baec3 100644 +--- a/drivers/irqchip/irq-gic.c ++++ b/drivers/irqchip/irq-gic.c +@@ -867,13 +867,13 @@ static __init void gic_smp_init(void) + "irqchip/arm/gic:starting", + gic_starting_cpu, NULL); + +- base_sgi = __irq_domain_alloc_irqs(gic_data[0].domain, -1, 8, ++ base_sgi = __irq_domain_alloc_irqs(gic_data[0].domain, -1, 16, + NUMA_NO_NODE, &sgi_fwspec, + false, NULL); + if (WARN_ON(base_sgi <= 0)) + return; + +- set_smp_ipi_range(base_sgi, 8); ++ set_smp_ipi_range(base_sgi, 16); + } + #else + #define gic_smp_init() do { } while(0) +diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig +index 05d6fae800e3..0557e99d8dee 100644 +--- a/drivers/mailbox/Kconfig ++++ b/drivers/mailbox/Kconfig +@@ -294,4 +294,13 @@ config QCOM_IPCC + acts as an interrupt controller for receiving interrupts from clients. + Say Y here if you want to build this driver. + ++config GENERIC_SOFTWARE_MAILBOX ++ tristate "Generic software Mailbox driver" ++ depends on OF ++ select GENERIC_IRQ_INJECTION ++ select GIC_GENTLE_CONFIG if ARM_GIC_V3 ++ help ++ This driver leverages unused interrupt line and shared memory to ++ implement a mailbox, which is used to send messages between different ++ OSes. + endif +diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile +index fc9376117111..38d6994df5e1 100644 +--- a/drivers/mailbox/Makefile ++++ b/drivers/mailbox/Makefile +@@ -62,3 +62,5 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o + obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o + + obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o ++ ++obj-$(CONFIG_GENERIC_SOFTWARE_MAILBOX) += generic-software-mailbox.o +diff --git a/drivers/mailbox/generic-software-mailbox.c b/drivers/mailbox/generic-software-mailbox.c +new file mode 100644 +index 000000000000..1938f45de6ec +--- /dev/null ++++ b/drivers/mailbox/generic-software-mailbox.c +@@ -0,0 +1,322 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Generic software Registers: ++ * ++ * TX_STATUS[n]: TX channel n status ++ * RX_STATUS[n]: RX channel n status ++ * 0: indicates message in T/RX_CH[n] is invalid and channel ready. ++ * 1: indicates message in T/RX_CH[n] is valid and channel busy. ++ * 2: indicates message in T/RX_CH[n] has been received by the peer. ++ * RXDB_STATUS[n]: RX doorbell channel n status ++ * 0: indicates channel ready. ++ * 1: indicates channel busy. ++ * 2: indicates channel doorbell has been received by the peer. ++ * TX_CH[n]: Transmit data register for channel n ++ * RX_CH[n]: Receive data register for channel n ++ * ++ * To send a message: ++ * Update the data register TX_CH[n] with the message, then set the ++ * TX_STATUS[n] to 1, inject a interrupt to remote side; after the ++ * transmission done set the TX_STATUS[n] back to 0. ++ * ++ * When received a message: ++ * Get the received data from RX_CH[n] and then set the RX_STATUS[n] to ++ * 2 and inject a interrupt to notify the remote side transmission done. ++ */ ++ ++#define MBOX_TX_CHAN (4) ++#define MBOX_RX_CHAN (4) ++#define MBOX_RXDB_CHAN (4) ++#define RX_CHAN_SHFT (MBOX_TX_CHAN) ++#define RXDB_CHAN_SHFT (MBOX_TX_CHAN + MBOX_RX_CHAN) ++#define MBOX_CHAN_MAX (MBOX_TX_CHAN + MBOX_RX_CHAN + MBOX_RXDB_CHAN) ++ ++struct sw_mbox_reg { ++ uint32_t tx_status[MBOX_TX_CHAN]; ++ uint32_t rx_status[MBOX_RX_CHAN]; ++ uint32_t rxdb_status[MBOX_RXDB_CHAN]; ++ uint32_t tx_ch[MBOX_TX_CHAN]; ++ uint32_t rx_ch[MBOX_RX_CHAN]; ++ uint32_t rxdb_ch[MBOX_RX_CHAN]; ++ uint32_t ch_ack_flags; /*from bit0 each bit for each channel(tx_ch, rx_ch, rxdb_ch), 1: ack, 0:noack */ ++}; ++ ++enum sw_mbox_channel_status { ++ S_READY, ++ S_BUSY, ++ S_DONE, ++}; ++ ++enum sw_mbox_type { ++ SW_TYPE_TX, /* Tx */ ++ SW_TYPE_RX, /* Rx */ ++ SW_TYPE_RXDB, /* Rx doorbell */ ++}; ++ ++struct sw_mbox_con_priv { ++ uint32_t idx; ++ enum sw_mbox_type type; ++ struct sw_mbox *priv; ++}; ++ ++struct sw_mbox { ++ struct device *dev; ++ struct sw_mbox_reg __iomem *base; ++ struct sw_mbox_con_priv cp[MBOX_CHAN_MAX]; ++ struct mbox_chan chan[MBOX_CHAN_MAX]; ++ struct mbox_controller controller; ++ int irq; ++ int remote_irq; ++}; ++ ++static int sw_mbox_send_data(struct mbox_chan *chan, void *msg) ++{ ++ struct sw_mbox_con_priv *cp = chan->con_priv; ++ struct sw_mbox *mbox = cp->priv; ++ uint32_t idx = cp->idx; ++ uint32_t *data = msg; ++ int ret; ++ ++ if (cp->type != SW_TYPE_TX) { ++ dev_err(mbox->dev, "Channel type error\n"); ++ return -EINVAL; ++ } ++ ++ writel(*data, &mbox->base->tx_ch[idx]); ++ writel(S_BUSY, &mbox->base->tx_status[idx]); ++ ret = irq_set_irqchip_state(mbox->remote_irq, IRQCHIP_STATE_PENDING, ++ true); ++ if (ret) { ++ dev_err(mbox->dev, "Fail to inject IRQ\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static irqreturn_t sw_mbox_interrupt(int irq, void *dev_id) ++{ ++ struct sw_mbox *mbox = dev_id; ++ irqreturn_t ret = IRQ_NONE; ++ uint32_t rxdb_status; ++ uint32_t rx_status; ++ uint32_t tx_status; ++ uint32_t rx_ch; ++ int i; ++ ++ for (i = 0; i < MBOX_TX_CHAN; i++) { ++ tx_status = readl(&mbox->base->tx_status[i]); ++ if (tx_status == S_DONE) { ++ writel(S_READY, &mbox->base->tx_status[i]); ++ mbox_chan_txdone(&mbox->chan[i], 0); ++ ret = IRQ_HANDLED; ++ } ++ } ++ ++ for (i = 0; i < MBOX_RX_CHAN; i++) { ++ rx_status = readl(&mbox->base->rx_status[i]); ++ if (rx_status == S_BUSY) { ++ rx_ch = readl(&mbox->base->rx_ch[i]); ++ mbox_chan_received_data(&mbox->chan[i + RX_CHAN_SHFT], ++ (void *)&rx_ch); ++ if (mbox->base->ch_ack_flags & (1 << (i + RX_CHAN_SHFT))) { ++ /* Sender need ACK */ ++ writel(S_DONE, &mbox->base->rx_status[i]); ++ irq_set_irqchip_state(mbox->remote_irq, ++ IRQCHIP_STATE_PENDING, true); ++ } else { ++ /* set status to be ready if sender doesn't need ACK */ ++ writel(S_READY, &mbox->base->rx_status[i]); ++ } ++ ret = IRQ_HANDLED; ++ } ++ } ++ ++ for (i = 0; i < MBOX_RXDB_CHAN; i++) { ++ rxdb_status = readl(&mbox->base->rxdb_status[i]); ++ if (rxdb_status == S_BUSY) { ++ mbox_chan_received_data(&mbox->chan[i + RXDB_CHAN_SHFT], ++ NULL); ++ if (mbox->base->ch_ack_flags & (1 << (i + RXDB_CHAN_SHFT))) { ++ /* Sender need ACK */ ++ writel(S_DONE, &mbox->base->rxdb_status[i]); ++ irq_set_irqchip_state(mbox->remote_irq, ++ IRQCHIP_STATE_PENDING, true); ++ } else { ++ /* set status to be ready if sender doesn't need ACK */ ++ writel(S_READY, &mbox->base->rxdb_status[i]); ++ } ++ ret = IRQ_HANDLED; ++ } ++ } ++ ++ return ret; ++} ++ ++static int sw_mbox_startup(struct mbox_chan *chan) ++{ ++ return 0; ++} ++ ++static void sw_mbox_shutdown(struct mbox_chan *chan) ++{ ++} ++ ++static const struct mbox_chan_ops sw_mbox_ops = { ++ .send_data = sw_mbox_send_data, ++ .startup = sw_mbox_startup, ++ .shutdown = sw_mbox_shutdown, ++}; ++ ++ ++static struct mbox_chan *sw_mbox_xlate(struct mbox_controller *mbox, ++ const struct of_phandle_args *sp) ++{ ++ struct mbox_chan *chan; ++ struct sw_mbox_con_priv *cp; ++ struct sw_mbox *sw_mb; ++ uint32_t type, idx, chan_idx, ack; ++ ++ if (sp->args_count != 3) { ++ dev_err(mbox->dev, "Invalid argument count %d\n", ++ sp->args_count); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ type = sp->args[0]; ++ idx = sp->args[1]; ++ ack = sp->args[2]; ++ ++ switch (type) { ++ case SW_TYPE_TX: ++ chan_idx = idx; ++ break; ++ case SW_TYPE_RX: ++ chan_idx = RX_CHAN_SHFT + idx; ++ break; ++ case SW_TYPE_RXDB: ++ chan_idx = RXDB_CHAN_SHFT + idx; ++ break; ++ default: ++ dev_err(mbox->dev, "Invalid chan type: %d\n", type); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ if (chan_idx >= MBOX_CHAN_MAX) { ++ dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", ++ chan_idx, type, idx); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ chan = &mbox->chans[chan_idx]; ++ cp = chan->con_priv; ++ sw_mb = cp->priv; ++ if (ack) ++ sw_mb->base->ch_ack_flags |= 1 << chan_idx; ++ else ++ sw_mb->base->ch_ack_flags &= ~(1 << chan_idx); ++ ++ return chan; ++} ++ ++static const struct of_device_id sw_mbox_of_match[] = { ++ { .compatible = "fsl,generic-software-mbox", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, sw_mbox_of_match); ++ ++static int sw_mailbox_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct sw_mbox *mbox; ++ int err, i; ++ ++ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); ++ if (!mbox) ++ return -ENOMEM; ++ mbox->dev = dev; ++ ++ mbox->irq = platform_get_irq_byname(pdev, "irq"); ++ if (mbox->irq <= 0) { ++ dev_err(dev, "Failed to get irq\n"); ++ return mbox->irq; ++ } ++ mbox->remote_irq = platform_get_irq_byname(pdev, "remote_irq"); ++ if (mbox->remote_irq <= 0) { ++ dev_err(dev, "Failed to get remote irq\n"); ++ return mbox->remote_irq; ++ } ++ ++ err = devm_request_irq(dev, mbox->irq, sw_mbox_interrupt, IRQF_SHARED, ++ pdev->name, mbox); ++ if (err) ++ return err; ++ ++ mbox->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mbox->base)) ++ return PTR_ERR(mbox->base); ++ ++ memset_io(mbox->base->tx_status, 0, 4 * MBOX_TX_CHAN); ++ memset_io(mbox->base->rx_status, 0, 4 * MBOX_RX_CHAN); ++ memset_io(mbox->base->rxdb_status, 0, 4 * MBOX_RXDB_CHAN); ++ ++ mbox->controller.dev = dev; ++ mbox->controller.chans = mbox->chan; ++ mbox->controller.num_chans = MBOX_CHAN_MAX; ++ mbox->controller.ops = &sw_mbox_ops; ++ mbox->controller.of_xlate = sw_mbox_xlate; ++ mbox->controller.txdone_irq = true; ++ ++ for (i = 0; i < MBOX_CHAN_MAX; i++) { ++ mbox->chan[i].con_priv = &mbox->cp[i]; ++ mbox->cp[i].priv = mbox; ++ mbox->cp[i].idx = i; ++ } ++ ++ err = devm_mbox_controller_register(dev, &mbox->controller); ++ if (err) { ++ dev_err(dev, "Failed to register mailbox %d\n", err); ++ return err; ++ } ++ ++ platform_set_drvdata(pdev, mbox); ++ ++ return 0; ++} ++ ++static struct platform_driver sw_mbox_driver = { ++ .driver = { ++ .name = "generic-software-mailbox", ++ .of_match_table = sw_mbox_of_match, ++ }, ++ .probe = sw_mailbox_probe, ++}; ++ ++static int __init sw_mbox_init(void) ++{ ++ return platform_driver_register(&sw_mbox_driver); ++} ++ ++static void __exit sw_mbox_exit(void) ++{ ++ platform_driver_unregister(&sw_mbox_driver); ++} ++ ++module_init(sw_mbox_init); ++module_exit(sw_mbox_exit); ++ ++MODULE_DESCRIPTION("Generic Software mailbox driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c +index 4229b9b5da98..8e2eba4a0a96 100644 +--- a/drivers/mailbox/mailbox.c ++++ b/drivers/mailbox/mailbox.c +@@ -158,7 +158,7 @@ static enum hrtimer_restart txdone_hrtimer(struct hrtimer *hrtimer) + void mbox_chan_received_data(struct mbox_chan *chan, void *mssg) + { + /* No buffering the received data */ +- if (chan->cl->rx_callback) ++ if (chan->cl && chan->cl->rx_callback) + chan->cl->rx_callback(chan->cl, mssg); + } + EXPORT_SYMBOL_GPL(mbox_chan_received_data); +diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c +index 4ba7b6df7986..145fc0f1b4f9 100644 +--- a/drivers/mxc/ipu3/ipu_common.c ++++ b/drivers/mxc/ipu3/ipu_common.c +@@ -2923,8 +2923,9 @@ static irqreturn_t ipu_sync_irq_handler(int irq, void *desc) + uint32_t line, bit, int_stat, int_ctrl; + irqreturn_t result = IRQ_NONE; + const int int_reg[] = { 1, 2, 3, 4, 11, 12, 13, 14, 15, 0 }; ++ unsigned long flags; + +- spin_lock(&ipu->int_reg_spin_lock); ++ spin_lock_irqsave(&ipu->int_reg_spin_lock, flags); + + for (i = 0; int_reg[i] != 0; i++) { + int_stat = ipu_cm_read(ipu, +@@ -2949,7 +2950,7 @@ static irqreturn_t ipu_sync_irq_handler(int irq, void *desc) + } + } + +- spin_unlock(&ipu->int_reg_spin_lock); ++ spin_unlock_irqrestore(&ipu->int_reg_spin_lock, flags); + + return result; + } +@@ -2960,8 +2961,9 @@ static irqreturn_t ipu_err_irq_handler(int irq, void *desc) + int i; + uint32_t int_stat; + const int err_reg[] = { 5, 6, 9, 10, 0 }; ++ unsigned long flags; + +- spin_lock(&ipu->int_reg_spin_lock); ++ spin_lock_irqsave(&ipu->int_reg_spin_lock, flags); + + for (i = 0; err_reg[i] != 0; i++) { + int_stat = ipu_cm_read(ipu, +@@ -2980,7 +2982,7 @@ static irqreturn_t ipu_err_irq_handler(int irq, void *desc) + } + } + +- spin_unlock(&ipu->int_reg_spin_lock); ++ spin_unlock_irqrestore(&ipu->int_reg_spin_lock, flags); + + return IRQ_HANDLED; + } +diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c +index f4417c7f5cda..2dcd6511bcb4 100644 +--- a/drivers/mxc/ipu3/ipu_device.c ++++ b/drivers/mxc/ipu3/ipu_device.c +@@ -3249,7 +3249,7 @@ static int ipu_task_thread(void *argv) + uint32_t size; + unsigned long flags; + unsigned int cpu; +- struct cpumask cpu_mask; ++ struct cpumask cpu_mask = CPU_MASK_NONE; + struct ipu_thread_data *data = (struct ipu_thread_data *)argv; + + thread_id++; +diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig +index 07507b4820d7..b5c3cd5d0e65 100644 +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -62,6 +62,8 @@ source "drivers/net/dsa/xrs700x/Kconfig" + + source "drivers/net/dsa/realtek/Kconfig" + ++source "drivers/net/dsa/netc/Kconfig" ++ + config NET_DSA_RZN1_A5PSW + tristate "Renesas RZ/N1 A5PSW Ethernet switch support" + depends on OF && ARCH_RZN1 +diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile +index 16eb879e0cb4..47bec77c5839 100644 +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -24,3 +24,4 @@ obj-y += qca/ + obj-y += realtek/ + obj-y += sja1105/ + obj-y += xrs700x/ ++obj-y += netc/ +\ No newline at end of file +diff --git a/drivers/net/dsa/netc/Kconfig b/drivers/net/dsa/netc/Kconfig +new file mode 100644 +index 000000000000..40dbdd5e8a37 +--- /dev/null ++++ b/drivers/net/dsa/netc/Kconfig +@@ -0,0 +1,10 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++config NET_DSA_NETC ++ tristate "NXP NETC Ethernet switch family support" ++ depends on NET_DSA && SPI ++ select NET_DSA_TAG_NETC ++ help ++ This is the driver for the NXP ENTC Ethernet switch family. ++ These are managed over an SPI interface. Probing is handled ++ based on OF bindings and so is the linkage to PHYLINK. +diff --git a/drivers/net/dsa/netc/Makefile b/drivers/net/dsa/netc/Makefile +new file mode 100644 +index 000000000000..72d6d8e2a52f +--- /dev/null ++++ b/drivers/net/dsa/netc/Makefile +@@ -0,0 +1,12 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++obj-$(CONFIG_NET_DSA_NETC) += netcdsa.o ++ ++netcdsa-objs := \ ++ netc_spi.o \ ++ netc_config.o \ ++ netc_ethtool.o \ ++ netc_devlink.o \ ++ netc_ptp.o \ ++ netc_main.o ++ +diff --git a/drivers/net/dsa/netc/netc.h b/drivers/net/dsa/netc/netc.h +new file mode 100644 +index 000000000000..cf9e27aff73f +--- /dev/null ++++ b/drivers/net/dsa/netc/netc.h +@@ -0,0 +1,102 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#ifndef _NETC_H ++#define _NETC_H ++ ++#include ++#include ++#include ++#include ++#include ++#include "netc_config.h" ++ ++struct netc_private; ++ ++enum { ++ NETC_SPEED_AUTO, ++ NETC_SPEED_10MBPS, ++ NETC_SPEED_100MBPS, ++ NETC_SPEED_1000MBPS, ++ NETC_SPEED_2500MBPS, ++ NETC_SPEED_MAX, ++}; ++ ++enum netc_internal_phy_t { ++ NETC_NO_PHY = 0, ++}; ++ ++struct netc_info { ++ const char *name; ++ int device_id; ++ int num_ports; ++ enum dsa_tag_protocol tag_proto; ++ int ptp_ts_bits; ++ bool multiple_cascade_ports; ++ bool can_limit_mcast_flood; ++}; ++ ++struct netc_private { ++ const struct netc_info *info; ++ struct netc_config config; ++ int cpu_port; ++ phy_interface_t phy_mode[NETC_MAX_NUM_PORTS]; ++ bool fixed_link[NETC_MAX_NUM_PORTS]; ++ unsigned long ucast_egress_floods; ++ unsigned long bcast_egress_floods; ++ unsigned long hwts_tx_en; ++ ++ size_t max_xfer_len; ++ struct spi_device *spidev; ++ struct dsa_switch *ds; ++ u16 bridge_pvid[NETC_MAX_NUM_PORTS]; ++ u16 tag_8021q_pvid[NETC_MAX_NUM_PORTS]; ++ /* Serializes transmission of management frames so that ++ * the switch doesn't confuse them with one another. ++ */ ++ struct mutex mgmt_lock; ++ ++ struct devlink_region **regions; ++}; ++ ++int netc_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, ++ struct netlink_ext_ack *extack); ++void netc_frame_memory_partitioning(struct netc_private *priv); ++ ++/* From netc_devlink.c */ ++int netc_devlink_setup(struct dsa_switch *ds); ++void netc_devlink_teardown(struct dsa_switch *ds); ++int netc_devlink_info_get(struct dsa_switch *ds, ++ struct devlink_info_req *req, ++ struct netlink_ext_ack *extack); ++ ++/* From netc_spi.c */ ++int netc_xfer_cmd(const struct netc_private *priv, ++ enum netc_spi_rw_mode rw, enum netc_cmd cmd, ++ void *param, size_t param_len, ++ void *resp, size_t resp_len); ++int netc_xfer_set_cmd(const struct netc_private *priv, ++ enum netc_cmd cmd, ++ void *param, size_t param_len); ++int netc_xfer_get_cmd(const struct netc_private *priv, ++ enum netc_cmd cmd, uint32_t id, ++ void *resp, size_t resp_len); ++ ++int netc_xfer_write_reg(const struct netc_private *priv, ++ uint32_t reg, uint32_t value); ++int netc_xfer_read_reg(const struct netc_private *priv, ++ uint32_t reg, uint32_t *value); ++ ++/* From netc_ethtool.c */ ++void netc_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data); ++void netc_get_strings(struct dsa_switch *ds, int port, ++ uint32_t stringset, uint8_t *data); ++int netc_get_sset_count(struct dsa_switch *ds, int port, int sset); ++ ++/* From netc_ptp.c */ ++void netc_ptp_txtstamp_skb(struct dsa_switch *ds, int port, ++ struct sk_buff *skb); ++ ++#endif /* _NETC_H */ +diff --git a/drivers/net/dsa/netc/netc_config.c b/drivers/net/dsa/netc/netc_config.c +new file mode 100644 +index 000000000000..e4590c7684c9 +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_config.c +@@ -0,0 +1,322 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include ++#include ++#include ++#include ++#include "netc.h" ++ ++int netc_get_devinfo(struct netc_private *priv, struct netc_config *config) ++{ ++ struct netc_cmd_sysinfo info; ++ int rc; ++ ++ rc = netc_xfer_get_cmd(priv, NETC_CMD_SYS_INFO_GET, 0, ++ &info, sizeof(info)); ++ if (rc < 0) ++ return rc; ++ ++ config->device_id = info.device_id; ++ config->vendor_id = info.vendor_id; ++ config->version_major = info.version_major; ++ config->version_minor = info.version_minor; ++ config->version_revision = info.version_revision; ++ config->cpu_port_mode = info.cpu_port; ++ ++ return 0; ++} ++ ++int netc_port_mtu_set(struct netc_private *priv, int port, int mtu) ++{ ++ struct netc_cmd_port_mtu mtu_cmd = {0}; ++ ++ mtu_cmd.port = (uint8_t)port; ++ mtu_cmd.mtu = (uint16_t)mtu; ++ ++ return netc_xfer_set_cmd(priv, NETC_CMD_PORT_MTU_SET, ++ &mtu_cmd, sizeof(mtu_cmd)); ++} ++ ++int netc_port_mtu_get(struct netc_private *priv, int port, int *mtu) ++{ ++ int rc; ++ struct netc_cmd_port_mtu mtu_resp = {0}; ++ ++ rc = netc_xfer_get_cmd(priv, NETC_CMD_PORT_MTU_GET, port, ++ &mtu_resp, sizeof(mtu_resp)); ++ ++ if (rc != 0) ++ return rc; ++ ++ *mtu = mtu_resp.mtu; ++ ++ return 0; ++} ++ ++/* Set link speed in the MAC configuration for a specific port. */ ++int netc_port_phylink_mode_set(struct netc_private *priv, ++ struct netc_mac_config *mac) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_port_phylink_mode phylink_mode = {0}; ++ int rc; ++ ++ phylink_mode.port = mac->port; ++ phylink_mode.duplex = mac->duplex; ++ phylink_mode.speed = mac->speed; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_PHYLINK_MODE_SET, ++ &phylink_mode, sizeof(phylink_mode)); ++ if (rc < 0) { ++ dev_err(dev, "Failed to write phylink_mode: %d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++/* Get link speed in the MAC configuration for a specific port. */ ++int netc_port_phylink_status_get(struct netc_private *priv, ++ struct netc_mac_config *mac) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_port_phylink_status phylink_status = {0}; ++ int rc; ++ ++ rc = netc_xfer_get_cmd(priv, NETC_CMD_PORT_PHYLINK_STATUS_GET, ++ mac->port, ++ &phylink_status, sizeof(phylink_status)); ++ if (rc < 0) { ++ dev_err(dev, "Failed to get phylink status: %d\n", rc); ++ return rc; ++ } ++ ++ mac->link = phylink_status.link; ++ mac->speed = phylink_status.speed; ++ mac->duplex = phylink_status.duplex; ++ ++ return 0; ++} ++ ++int netc_port_pvid_set(struct netc_private *priv, int port, uint16_t pvid) ++{ ++ int rc = 0; ++ struct netc_cmd_port_pvid cmd_pvid = {0}; ++ ++ cmd_pvid.port = (uint8_t)port; ++ cmd_pvid.pvid = pvid; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_PVID_SET, ++ &cmd_pvid, sizeof(cmd_pvid)); ++ ++ return rc; ++} ++ ++int netc_port_link_set(struct netc_private *priv, int port, bool up) ++{ ++ int rc = 0; ++ struct netc_cmd_port_link egress = {0}; ++ ++ egress.port = (uint8_t)port; ++ egress.link = up; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_LINK_SET, ++ &egress, sizeof(egress)); ++ ++ return rc; ++} ++ ++int netc_port_dropuntag_set(struct netc_private *priv, int port, bool drop) ++{ ++ int rc = 0; ++ struct netc_cmd_port_dropuntag dropuntag = {0}; ++ ++ dropuntag.port = (uint8_t)port; ++ dropuntag.drop = (uint16_t)drop; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_DROPUNTAG_SET, ++ &dropuntag, sizeof(dropuntag)); ++ ++ return rc; ++} ++ ++int netc_port_dsa_add(struct netc_private *priv, int cpu_port, ++ int slave_port, const unsigned char *mac_addr) ++{ ++ int rc = 0; ++ struct netc_cmd_port_dsa_add dsa_add = {0}; ++ ++ dsa_add.cpu_port = (uint8_t)cpu_port; ++ dsa_add.slave_port = (uint8_t)slave_port; ++ ether_addr_copy(dsa_add.mac_addr, mac_addr); ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_DSA_ADD, ++ &dsa_add, sizeof(dsa_add)); ++ ++ return rc; ++} ++ ++int netc_port_dsa_del(struct netc_private *priv, int slave_port) ++{ ++ int rc = 0; ++ struct netc_cmd_port_dsa_del dsa_del = {0}; ++ ++ dsa_del.slave_port = (uint8_t)slave_port; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_DSA_DEL, ++ &dsa_del, sizeof(dsa_del)); ++ ++ return rc; ++} ++ ++int netc_vlan_entry_add(struct netc_private *priv, ++ uint16_t vid, int port, bool untagged) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_vlan cmd_vlan = {0}; ++ int rc; ++ ++ cmd_vlan.vid = vid; ++ cmd_vlan.port = (uint8_t)port; ++ cmd_vlan.untagged = untagged; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_VLAN_ADD, ++ &cmd_vlan, sizeof(cmd_vlan)); ++ if (rc < 0) { ++ dev_err(dev, "Failed to add vlan entry: %d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int netc_vlan_entry_del(struct netc_private *priv, uint16_t vid, int port) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_vlan cmd_vlan = {0}; ++ int rc; ++ ++ cmd_vlan.vid = vid; ++ cmd_vlan.port = (uint8_t)port; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_VLAN_DEL, ++ &cmd_vlan, sizeof(cmd_vlan)); ++ if (rc < 0) { ++ dev_err(dev, "Failed to add vlan entry: %d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int netc_vlan_entry_read(struct netc_private *priv, ++ struct netc_vlan_entry *vlan, ++ uint32_t entry_id, uint32_t *next_id) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_vlan_dump vlan_dump = {0}; ++ int rc; ++ ++ rc = netc_xfer_get_cmd(priv, NETC_CMD_VLAN_DUMP, entry_id, ++ &vlan_dump, sizeof(vlan_dump)); ++ if (rc < 0) { ++ dev_err(dev, "Failed to read vlan entry 0x%08x: %d\n", ++ entry_id, rc); ++ return rc; ++ } ++ ++ vlan->entry_id = entry_id; ++ vlan->vid = vlan_dump.vid; ++ vlan->port_map = vlan_dump.port_map; ++ *next_id = vlan_dump.resume_entry_id; ++ ++ return 0; ++} ++ ++int netc_fdb_entry_add(struct netc_private *priv, ++ const unsigned char *mac_addr, ++ uint16_t vid, int port) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_fdb fdb_add = {0}; ++ int rc; ++ ++ ether_addr_copy(fdb_add.mac_addr, mac_addr); ++ fdb_add.vid = vid; ++ fdb_add.port = (uint8_t)port; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_FDB_ADD, ++ &fdb_add, sizeof(fdb_add)); ++ if (rc < 0) { ++ dev_err(dev, "Failed to add fdb: %d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int netc_fdb_entry_del(struct netc_private *priv, ++ const unsigned char *mac_addr, ++ uint16_t vid) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_fdb_del fdb_del = {0}; ++ int rc; ++ ++ ether_addr_copy(fdb_del.mac_addr, mac_addr); ++ fdb_del.vid = vid; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_FDB_DEL, ++ &fdb_del, sizeof(fdb_del)); ++ if (rc < 0) { ++ dev_err(dev, "Failed to delete fdb: %d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int netc_fdb_entry_get(struct netc_private *priv, struct netc_fdb_entry *fdb, ++ uint32_t entry_id, uint32_t *next_id) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_fdb_dump fdb_dump = {0}; ++ int rc; ++ ++ rc = netc_xfer_get_cmd(priv, NETC_CMD_FDB_DUMP, entry_id, ++ &fdb_dump, sizeof(fdb_dump)); ++ if (rc < 0) { ++ dev_err(dev, "Failed to get fdb entry: %d\n", rc); ++ return rc; ++ } ++ ++ *next_id = fdb_dump.resume_entry_id; ++ ++ ether_addr_copy(fdb->mac_addr, fdb_dump.mac_addr); ++ fdb->vid = fdb_dump.vid; ++ fdb->port_map = fdb_dump.port_map; ++ fdb->dynamic = fdb_dump.dynamic; ++ ++ return 0; ++} ++ ++int netc_config_setup(struct netc_config *config) ++{ ++ if (config->vlan_max_count) { ++ config->vlan = kcalloc(config->vlan_max_count, ++ sizeof(*config->vlan), ++ GFP_KERNEL); ++ if (!config->vlan) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++void netc_config_free(struct netc_config *config) ++{ ++ kfree(config->vlan); ++} +diff --git a/drivers/net/dsa/netc/netc_config.h b/drivers/net/dsa/netc/netc_config.h +new file mode 100644 +index 000000000000..1b181e7ef56b +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_config.h +@@ -0,0 +1,288 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#ifndef _NETC_CONFIG_H ++#define _NETC_CONFIG_H ++ ++#include ++#include ++ ++#define NETC_RT1180_DEVICE_ID 0xe001 ++#define NETC_NUM_PORTS 5 ++#define NETC_MAX_NUM_PORTS NETC_NUM_PORTS ++#define NETC_NUM_TC 8 ++ ++#define NETC_ETHTOOL_STATS_NUM_MAX 120 ++ ++#define NETC_SPI_WORD_BITS 8 ++#define NETC_SPI_MSG_WORD_BYTES 4 ++#define NETC_SPI_MSG_HEADER_SIZE 16 ++#define NETC_SPI_MSG_PARAM_SIZE 12 ++#define NETC_SPI_MSG_MAXLEN 4096 ++#define NETC_SPI_MSG_RESPONSE_TIME 1000 /* us */ ++ ++#define NETC_CMD_DIR_SHIFT 31 ++#define NETC_CMD_LEN_SHIFT 16 ++ ++enum netc_spi_rw_mode { ++ SPI_READ = 0, ++ SPI_WRITE = 1, ++}; ++ ++struct netc_cmd_hdr { ++ uint32_t cmd; ++ uint8_t param[NETC_SPI_MSG_PARAM_SIZE]; ++}; ++ ++/* Command */ ++enum netc_cmd { ++ /* port related command */ ++ NETC_CMD_SYS_INFO_GET = 0x1, ++ NETC_CMD_PORT_DSA_ADD, ++ NETC_CMD_PORT_DSA_DEL, ++ NETC_CMD_PORT_MTU_SET, ++ NETC_CMD_PORT_MTU_GET, ++ NETC_CMD_PORT_PHYLINK_MODE_SET, ++ NETC_CMD_PORT_PHYLINK_STATUS_GET, ++ NETC_CMD_PORT_ETHTOOL_STATS_GET, ++ NETC_CMD_PORT_PVID_SET, ++ NETC_CMD_PORT_LINK_SET, ++ NETC_CMD_PORT_DROPUNTAG_SET, ++ ++ NETC_CMD_FDB_ADD = 0x1000, ++ NETC_CMD_FDB_DEL, ++ NETC_CMD_FDB_DUMP, ++ NETC_CMD_VLAN_ADD, ++ NETC_CMD_VLAN_DEL, ++ NETC_CMD_VLAN_DUMP, ++ NETC_CMD_FORWARD_MASK_SET, ++ ++ NETC_CMD_PTP_SYNC_SET = 0x2000, ++ ++ NETC_CMD_QBV_SET = 0x3000, ++ NETC_CMD_QBV_GET, ++ NETC_CMD_QBU_SET, ++ NETC_CMD_QBU_GET, ++ NETC_CMD_QCI_SET, ++ NETC_CMD_QCI_GET, ++ NETC_CMD_8021CB_SET, ++ NETC_CMD_8021CB_GET, ++ ++ NETC_CMD_REG_SET = 0x4000, ++ NETC_CMD_REG_GET, ++ NETC_CMD_MAX_NUM, ++}; ++ ++struct netc_cmd_sysinfo { ++ uint16_t device_id; ++ uint16_t vendor_id; ++ uint8_t version_major; ++ uint8_t version_minor; ++ uint8_t version_revision; ++ uint8_t cpu_port; ++}; ++ ++/* command data for NETC_CMD_PORT_DSA_ADD */ ++struct netc_cmd_port_dsa_add { ++ uint8_t cpu_port; /* switch port 0, 1, 2 or 3 */ ++ uint8_t slave_port; /* switch port 0, 1, 2 or 3 */ ++ uint8_t mac_addr[ETH_ALEN]; /* MAC address of master interface */ ++}; ++ ++/* command data for NETC_CMD_PORT_DSA_DEL */ ++struct netc_cmd_port_dsa_del { ++ uint8_t slave_port; /* switch port 0, 1, 2 or 3 */ ++ uint8_t reserved[3]; ++}; ++ ++/* command data for NETC_CMD_PORT_MTU_SET */ ++struct netc_cmd_port_mtu { ++ uint8_t port; /* switch port 0, 1, 2 or 3 */ ++ uint8_t reserved; ++ uint16_t mtu; ++}; ++ ++/* command data for NETC_CMD_PORT_PHYLINK_MODE_SET */ ++struct netc_cmd_port_phylink_mode { ++ uint8_t port; /* switch port 0, 1, 2 or 3 */ ++ bool duplex; /* 0: half duplex; 1: full duplex */ ++ uint16_t speed; /* 10: 10Mbps ; 100: 100Mbps ; 1000: 1000Mbps */ ++}; ++ ++/* command data for NETC_CMD_PORT_PVID_SET */ ++struct netc_cmd_port_pvid { ++ uint8_t port; /* switch port 0, 1, 2 or 3 */ ++ uint8_t reserved; ++ uint16_t pvid; ++}; ++ ++/* command data for netc_cmd_port_link */ ++struct netc_cmd_port_link { ++ uint8_t port; /* switch port 0, 1, 2 or 3 */ ++ bool link; /* 0: down; 1: up */ ++ uint8_t reserved[2]; ++}; ++ ++/* command data for netc_cmd_port_dropuntag */ ++struct netc_cmd_port_dropuntag { ++ uint8_t port; /* switch port 0, 1, 2 or 3 */ ++ uint8_t reserved; ++ uint16_t drop; ++}; ++ ++/* command data for NETC_CMD_FDB_ADD */ ++struct netc_cmd_fdb { ++ uint8_t mac_addr[ETH_ALEN]; ++ uint16_t vid; ++ uint8_t port; /* switch port 0, 1, 2 or 3 */ ++ uint8_t reserved[3]; ++}; ++ ++/* command data for NETC_CMD_FDB_DEL */ ++struct netc_cmd_fdb_del { ++ uint8_t mac_addr[ETH_ALEN]; ++ uint16_t vid; ++}; ++ ++/* command data for NETC_CMD_VLAN_ADD */ ++struct netc_cmd_vlan { ++ uint16_t vid; ++ uint8_t port; /* switch port 0, 1, 2 or 3 */ ++ bool untagged; ++}; ++ ++/* data returned for NETC_CMD_PORT_PHYLINK_STATUS_GET */ ++struct netc_cmd_port_phylink_status { ++ uint8_t port; /* switch port 0, 1, 2 or 3 */ ++ bool link; ++ uint16_t speed; ++ bool duplex; /* 0: down; 1: up */ ++ uint8_t reserved[3]; ++}; ++ ++/* command param */ ++struct netc_cmd_read_param { ++ uint32_t id; ++}; ++ ++/* command data for NETC_CMD_REG_SET */ ++struct netc_cmd_reg_cmd { ++ uint32_t reg; ++ uint32_t value; ++}; ++ ++/* data returned for NETC_CMD_FDB_DUMP */ ++struct netc_cmd_fdb_dump { ++ uint8_t mac_addr[ETH_ALEN]; ++ uint16_t vid; ++ /* bit 0: switch port 0 etc. */ ++ uint32_t port_map; ++ bool dynamic; ++ uint8_t reserved[3]; ++ /* non-zero means there are remaining entries, 0 means no more entries */ ++ uint32_t resume_entry_id; ++}; ++ ++/* data returned for NETC_CMD_VLAN_DUMP */ ++struct netc_cmd_vlan_dump { ++ uint16_t vid; ++ bool untagged; ++ uint8_t reserved; ++ /* bit 0: switch port 0 etc. */ ++ uint32_t port_map; ++ /* non-zero means there are remaining entries, 0 means no more entries */ ++ uint32_t resume_entry_id; ++}; ++ ++struct netc_cmd_port_ethtool_stats { ++ uint64_t values[NETC_ETHTOOL_STATS_NUM_MAX]; ++}; ++ ++struct netc_mac_config { ++ uint8_t port; ++ uint16_t speed; ++ uint16_t vlanid; ++ bool link; ++ bool egress; ++ bool ingress; ++ bool duplex; ++ bool drptag; ++ bool drpuntag; ++ bool retag; ++}; ++ ++struct netc_fdb_entry { ++ uint8_t mac_addr[ETH_ALEN]; ++ uint16_t vid; ++ uint32_t port_map; /* bit 0: switch port 0 etc. */ ++ bool dynamic; ++}; ++ ++struct netc_vlan_entry { ++ uint16_t vid; ++ uint16_t port; ++ uint32_t port_map; ++ uint32_t tag_ports; ++ uint32_t entry_id; ++}; ++ ++struct netc_config { ++ uint16_t device_id; ++ uint16_t vendor_id; ++ uint8_t version_major; ++ uint8_t version_minor; ++ uint8_t version_revision; ++ uint8_t cpu_port_mode; ++ uint16_t tpid; ++ uint16_t tpid2; ++ struct netc_mac_config mac[NETC_MAX_NUM_PORTS]; ++ int cpu_port; ++ int vlan_count; ++ int vlan_max_count; ++ struct netc_vlan_entry *vlan; ++}; ++ ++struct netc_private; ++ ++int netc_get_devinfo(struct netc_private *priv, struct netc_config *config); ++ ++int netc_port_phylink_mode_set(struct netc_private *priv, ++ struct netc_mac_config *mac); ++int netc_port_phylink_stats_get(struct netc_private *priv, ++ struct netc_mac_config *mac); ++int netc_port_pvid_set(struct netc_private *priv, int port, uint16_t pvid); ++int netc_port_link_set(struct netc_private *priv, int port, bool up); ++int netc_port_dropuntag_set(struct netc_private *priv, int port, bool drop); ++ ++int netc_port_mtu_set(struct netc_private *priv, int port, int mtu); ++int netc_port_mtu_get(struct netc_private *priv, int port, int *mtu); ++ ++int netc_port_pvid_set(struct netc_private *priv, int port, uint16_t pvid); ++ ++int netc_port_dsa_add(struct netc_private *priv, int cpu_port, ++ int slave_port, const unsigned char *mac_addr); ++int netc_port_dsa_del(struct netc_private *priv, int slave_port); ++ ++int netc_fdb_entry_add(struct netc_private *priv, ++ const unsigned char *mac_addr, ++ uint16_t vid, int port); ++int netc_fdb_entry_del(struct netc_private *priv, ++ const unsigned char *mac_addr, ++ uint16_t vid); ++int netc_fdb_entry_get(struct netc_private *priv, ++ struct netc_fdb_entry *fdb, ++ uint32_t entry_id, uint32_t *next_id); ++ ++int netc_vlan_entry_add(struct netc_private *priv, ++ uint16_t vid, int port, bool untagged); ++int netc_vlan_entry_del(struct netc_private *priv, uint16_t vid, int port); ++int netc_vlan_entry_get(struct netc_private *priv, ++ struct netc_vlan_entry *vlan, ++ uint32_t entry_id, uint32_t *next_id); ++ ++int netc_config_setup(struct netc_config *config); ++void netc_config_free(struct netc_config *config); ++ ++#endif /* _NETC_CONFIG_H */ +diff --git a/drivers/net/dsa/netc/netc_devlink.c b/drivers/net/dsa/netc/netc_devlink.c +new file mode 100644 +index 000000000000..bcf0de99b0cd +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_devlink.c +@@ -0,0 +1,111 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "netc.h" ++ ++static size_t netc_config_get_size(struct netc_private *priv) ++{ ++ return sizeof(struct netc_config); ++} ++ ++static int ++netc_region_config_snapshot(struct devlink *dl, ++ const struct devlink_region_ops *ops, ++ struct netlink_ext_ack *extack, ++ u8 **data) ++{ ++ struct dsa_switch *ds = dsa_devlink_to_ds(dl); ++ struct netc_private *priv = ds->priv; ++ size_t len; ++ ++ len = netc_config_get_size(priv); ++ *data = kcalloc(len, sizeof(u8), GFP_KERNEL); ++ if (!*data) ++ return -ENOMEM; ++ ++ return netc_xfer_get_cmd(priv, NETC_CMD_SYS_INFO_GET, 0, *data, len); ++} ++ ++static struct devlink_region_ops netc_region_config_ops = { ++ .name = "config", ++ .snapshot = netc_region_config_snapshot, ++ .destructor = kfree, ++}; ++ ++enum netc_region_id { ++ NETC_REGION_CONFIG = 0, ++}; ++ ++struct netc_region { ++ const struct devlink_region_ops *ops; ++ size_t (*get_size)(struct netc_private *priv); ++}; ++ ++static struct netc_region netc_regions[] = { ++ [NETC_REGION_CONFIG] = { ++ .ops = &netc_region_config_ops, ++ .get_size = netc_config_get_size, ++ }, ++}; ++ ++int netc_devlink_info_get(struct dsa_switch *ds, ++ struct devlink_info_req *req, ++ struct netlink_ext_ack *extack) ++{ ++ struct netc_private *priv = ds->priv; ++ int rc; ++ ++ rc = devlink_info_driver_name_put(req, "netc"); ++ if (rc) ++ return rc; ++ ++ rc = devlink_info_version_fixed_put(req, ++ DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, ++ priv->info->name); ++ return rc; ++} ++ ++int netc_devlink_setup(struct dsa_switch *ds) ++{ ++ int i, num_regions = ARRAY_SIZE(netc_regions); ++ struct netc_private *priv = ds->priv; ++ const struct devlink_region_ops *ops; ++ struct devlink_region *region; ++ u64 size; ++ ++ priv->regions = kcalloc(num_regions, sizeof(struct devlink_region *), ++ GFP_KERNEL); ++ if (!priv->regions) ++ return -ENOMEM; ++ ++ for (i = 0; i < num_regions; i++) { ++ size = netc_regions[i].get_size(priv); ++ ops = netc_regions[i].ops; ++ ++ region = dsa_devlink_region_create(ds, ops, 1, size); ++ if (IS_ERR(region)) { ++ while (--i >= 0) ++ dsa_devlink_region_destroy(priv->regions[i]); ++ ++ kfree(priv->regions); ++ return PTR_ERR(region); ++ } ++ ++ priv->regions[i] = region; ++ } ++ ++ return 0; ++} ++ ++void netc_devlink_teardown(struct dsa_switch *ds) ++{ ++ int i, num_regions = ARRAY_SIZE(netc_regions); ++ struct netc_private *priv = ds->priv; ++ ++ for (i = 0; i < num_regions; i++) ++ dsa_devlink_region_destroy(priv->regions[i]); ++ ++ kfree(priv->regions); ++} +diff --git a/drivers/net/dsa/netc/netc_ethtool.c b/drivers/net/dsa/netc/netc_ethtool.c +new file mode 100644 +index 000000000000..7e6ab46ffee6 +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_ethtool.c +@@ -0,0 +1,344 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "netc.h" ++ ++enum netc_stat_index { ++ /* RX stats */ ++ NETC_STAT_RX_BYTES, ++ NETC_STAT_RX_VALID_BYTES, ++ NETC_STAT_RX_PAUSE_FRAMES, ++ NETC_STAT_RX_VALID_FRAMES, ++ NETC_STAT_RX_VLAN_FRAMES, ++ NETC_STAT_RX_UC_FRAMES, ++ NETC_STAT_RX_MC_FRAMES, ++ NETC_STAT_RX_BC_FRAMES, ++ NETC_STAT_RX_FRAMES, ++ NETC_STAT_RX_MIN_FRAMES, ++ NETC_STAT_RX_64_FRAMES, ++ NETC_STAT_RX_65_127_FRAMES, ++ NETC_STAT_RX_128_255_FRAMES, ++ NETC_STAT_RX_256_511_FRAMES, ++ NETC_STAT_RX_512_1023_FRAMES, ++ NETC_STAT_RX_1024_1522_FRAMES, ++ NETC_STAT_RX_1523_MAX_FRAMES, ++ NETC_STAT_RX_CONTROL_FRAMES, ++ ++ /* TX stats */ ++ NETC_STAT_TX_BYTES, ++ NETC_STAT_TX_VALID_BYTES, ++ NETC_STAT_TX_PAUSE_FRAMES, ++ NETC_STAT_TX_VALID_FRAMES, ++ NETC_STAT_TX_VLAN_FRAMES, ++ NETC_STAT_TX_UC_FRAMES, ++ NETC_STAT_TX_MC_FRAMES, ++ NETC_STAT_TX_BC_FRAMES, ++ NETC_STAT_TX_FRAMES, ++ NETC_STAT_TX_MIN_FRAMES, ++ NETC_STAT_TX_64_FRAMES, ++ NETC_STAT_TX_65_127_FRAMES, ++ NETC_STAT_TX_128_255_FRAMES, ++ NETC_STAT_TX_256_511_FRAMES, ++ NETC_STAT_TX_512_1023_FRAMES, ++ NETC_STAT_TX_1024_1522_FRAMES, ++ NETC_STAT_TX_1523_MAX_FRAMES, ++ NETC_STAT_TX_CONTROL_FRAMES, ++ ++ NETC_STAT_RX_VALID_REASSEMBLED_FRAMES, ++ NETC_STAT_RX_ADDITIONAL_MPACKETS, ++ NETC_STAT_RX_ERROR_FRAME_REASSEMBLY, ++ NETC_STAT_RX_ERROR_FRAME_SMD, ++ NETC_STAT_TX_ADDITIONAL_MPACKETS, ++ NETC_STAT_TX_HOLD_TRANSITIONS, ++ ++ /* Error stats */ ++ NETC_STAT_RX_ERROR, ++ NETC_STAT_RX_ERROR_UNDERSIZE, ++ NETC_STAT_RX_ERROR_OVERSIZE, ++ NETC_STAT_RX_ERROR_FCS, ++ NETC_STAT_RX_ERROR_FRAGMENT, ++ NETC_STAT_RX_ERROR_JABBER, ++ NETC_STAT_RX_ERROR_DISCARD, ++ NETC_STAT_RX_ERROR_NO_TRUNCATED, ++ NETC_STAT_TX_ERROR_FCS, ++ NETC_STAT_TX_ERROR_UNDERSIZE, ++ ++ /* Discard stats */ ++ NETC_STAT_RX_DISCARD_COUNT, ++ NETC_STAT_RX_DISCARD_REASON0, ++ NETC_STAT_RX_DISCARD_TABLE_ID, ++ NETC_STAT_RX_DISCARD_ENTRY_ID, ++ NETC_STAT_TX_DISCARD_COUNT, ++ NETC_STAT_TX_DISCARD_REASON0, ++ NETC_STAT_TX_DISCARD_TABLE_ID, ++ NETC_STAT_TX_DISCARD_ENTRY_ID, ++ NETC_STAT_BRIDGE_DISCARD_COUNT, ++ NETC_STAT_BRIDGE_DISCARD_REASON0, ++ NETC_STAT_BRIDGE_DISCARD_TABLE_ID, ++ NETC_STAT_BRIDGE_DISCARD_ENTRY_ID, ++ ++ /* Q0 stats */ ++ NETC_STAT_Q0_REJECTED_BYTES, ++ NETC_STAT_Q0_REJECTED_FRAMES, ++ NETC_STAT_Q0_DEQUEUE_BYTES, ++ NETC_STAT_Q0_DEQUEUE_FRAMES, ++ NETC_STAT_Q0_DROPPED_BYTES, ++ NETC_STAT_Q0_DROPPED_FRAMES, ++ NETC_STAT_Q0_FRAMES, ++ ++ /* Q1 stats */ ++ NETC_STAT_Q1_REJECTED_BYTES, ++ NETC_STAT_Q1_REJECTED_FRAMES, ++ NETC_STAT_Q1_DEQUEUE_BYTES, ++ NETC_STAT_Q1_DEQUEUE_FRAMES, ++ NETC_STAT_Q1_DROPPED_BYTES, ++ NETC_STAT_Q1_DROPPED_FRAMES, ++ NETC_STAT_Q1_FRAMES, ++ ++ /* Q2 stats */ ++ NETC_STAT_Q2_REJECTED_BYTES, ++ NETC_STAT_Q2_REJECTED_FRAMES, ++ NETC_STAT_Q2_DEQUEUE_BYTES, ++ NETC_STAT_Q2_DEQUEUE_FRAMES, ++ NETC_STAT_Q2_DROPPED_BYTES, ++ NETC_STAT_Q2_DROPPED_FRAMES, ++ NETC_STAT_Q2_FRAMES, ++ ++ /* Q3 stats */ ++ NETC_STAT_Q3_REJECTED_BYTES, ++ NETC_STAT_Q3_REJECTED_FRAMES, ++ NETC_STAT_Q3_DEQUEUE_BYTES, ++ NETC_STAT_Q3_DEQUEUE_FRAMES, ++ NETC_STAT_Q3_DROPPED_BYTES, ++ NETC_STAT_Q3_DROPPED_FRAMES, ++ NETC_STAT_Q3_FRAMES, ++ ++ /* Q4 stats */ ++ NETC_STAT_Q4_REJECTED_BYTES, ++ NETC_STAT_Q4_REJECTED_FRAMES, ++ NETC_STAT_Q4_DEQUEUE_BYTES, ++ NETC_STAT_Q4_DEQUEUE_FRAMES, ++ NETC_STAT_Q4_DROPPED_BYTES, ++ NETC_STAT_Q4_DROPPED_FRAMES, ++ NETC_STAT_Q4_FRAMES, ++ ++ /* Q5 stats */ ++ NETC_STAT_Q5_REJECTED_BYTES, ++ NETC_STAT_Q5_REJECTED_FRAMES, ++ NETC_STAT_Q5_DEQUEUE_BYTES, ++ NETC_STAT_Q5_DEQUEUE_FRAMES, ++ NETC_STAT_Q5_DROPPED_BYTES, ++ NETC_STAT_Q5_DROPPED_FRAMES, ++ NETC_STAT_Q5_FRAMES, ++ ++ /* Q6 stats */ ++ NETC_STAT_Q6_REJECTED_BYTES, ++ NETC_STAT_Q6_REJECTED_FRAMES, ++ NETC_STAT_Q6_DEQUEUE_BYTES, ++ NETC_STAT_Q6_DEQUEUE_FRAMES, ++ NETC_STAT_Q6_DROPPED_BYTES, ++ NETC_STAT_Q6_DROPPED_FRAMES, ++ NETC_STAT_Q6_FRAMES, ++ ++ /* Q7 stats */ ++ NETC_STAT_Q7_REJECTED_BYTES, ++ NETC_STAT_Q7_REJECTED_FRAMES, ++ NETC_STAT_Q7_DEQUEUE_BYTES, ++ NETC_STAT_Q7_DEQUEUE_FRAMES, ++ NETC_STAT_Q7_DROPPED_BYTES, ++ NETC_STAT_Q7_DROPPED_FRAMES, ++ NETC_STAT_Q7_FRAMES, ++ NETC_STAT_NUM, ++}; ++ ++char netc_stat_name[][ETH_GSTRING_LEN] = { ++ /* RX stats */ ++ [NETC_STAT_RX_BYTES] = "in-bytes", ++ [NETC_STAT_RX_VALID_BYTES] = "in-valid-bytes", ++ [NETC_STAT_RX_PAUSE_FRAMES] = "in-pause-frames", ++ [NETC_STAT_RX_VALID_FRAMES] = "in-valid-frames", ++ [NETC_STAT_RX_VLAN_FRAMES] = "in-vlan-frames", ++ [NETC_STAT_RX_UC_FRAMES] = "in-uc-frames", ++ [NETC_STAT_RX_MC_FRAMES] = "in-mc-frames", ++ [NETC_STAT_RX_BC_FRAMES] = "in-bc-frames", ++ [NETC_STAT_RX_FRAMES] = "in-frames", ++ [NETC_STAT_RX_MIN_FRAMES] = "in-min-frames", ++ [NETC_STAT_RX_64_FRAMES] = "in-64-frames", ++ [NETC_STAT_RX_65_127_FRAMES] = "in-65-127-frames", ++ [NETC_STAT_RX_128_255_FRAMES] = "in-128-255-frames", ++ [NETC_STAT_RX_256_511_FRAMES] = "in-256-511-frames", ++ [NETC_STAT_RX_512_1023_FRAMES] = "in-512-1023-frames", ++ [NETC_STAT_RX_1024_1522_FRAMES] = "in-1024-1522-frames", ++ [NETC_STAT_RX_1523_MAX_FRAMES] = "in-1523-max-frames", ++ [NETC_STAT_RX_CONTROL_FRAMES] = "in-control-frames", ++ ++ /* TX stats */ ++ [NETC_STAT_TX_BYTES] = "out-bytes", ++ [NETC_STAT_TX_VALID_BYTES] = "out-valid-bytes", ++ [NETC_STAT_TX_PAUSE_FRAMES] = "out-pause-frames", ++ [NETC_STAT_TX_VALID_FRAMES] = "out-valid-frames", ++ [NETC_STAT_TX_VLAN_FRAMES] = "out-vlan-frames", ++ [NETC_STAT_TX_UC_FRAMES] = "out-uc-frames", ++ [NETC_STAT_TX_MC_FRAMES] = "out-mc-frames", ++ [NETC_STAT_TX_BC_FRAMES] = "out-bc-frames", ++ [NETC_STAT_TX_FRAMES] = "out-frames", ++ [NETC_STAT_TX_MIN_FRAMES] = "out-min-frames", ++ [NETC_STAT_TX_64_FRAMES] = "out-64-frames", ++ [NETC_STAT_TX_65_127_FRAMES] = "out-65-127-frames", ++ [NETC_STAT_TX_128_255_FRAMES] = "out-128-255-frames", ++ [NETC_STAT_TX_256_511_FRAMES] = "out-256-511-frames", ++ [NETC_STAT_TX_512_1023_FRAMES] = "out-512-1023-frames", ++ [NETC_STAT_TX_1024_1522_FRAMES] = "out-1024-1522-frames", ++ [NETC_STAT_TX_1523_MAX_FRAMES] = "out-1523-max-frames", ++ [NETC_STAT_TX_CONTROL_FRAMES] = "out-control-frames", ++ ++ [NETC_STAT_RX_VALID_REASSEMBLED_FRAMES] = "in-valid-reassembled-frames", ++ [NETC_STAT_RX_ADDITIONAL_MPACKETS] = "in-additional-mPackets", ++ [NETC_STAT_RX_ERROR_FRAME_REASSEMBLY] = "in-error-frame-reassembly", ++ [NETC_STAT_RX_ERROR_FRAME_SMD] = "in-error-frame-smd", ++ [NETC_STAT_TX_ADDITIONAL_MPACKETS] = "out-additional-mPackets", ++ [NETC_STAT_TX_HOLD_TRANSITIONS] = "out-hold-transitions", ++ ++ /* Error stats */ ++ [NETC_STAT_RX_ERROR] = "in-error", ++ [NETC_STAT_RX_ERROR_UNDERSIZE] = "in-error-undersize", ++ [NETC_STAT_RX_ERROR_OVERSIZE] = "in-error-oversize", ++ [NETC_STAT_RX_ERROR_FCS] = "in-error-fcs", ++ [NETC_STAT_RX_ERROR_FRAGMENT] = "in-error-fragment", ++ [NETC_STAT_RX_ERROR_JABBER] = "in-error-jabber", ++ [NETC_STAT_RX_ERROR_DISCARD] = "in-error-discard", ++ [NETC_STAT_RX_ERROR_NO_TRUNCATED] = "in-error-dicard-no-truncated", ++ [NETC_STAT_TX_ERROR_FCS] = "out-error-fcs", ++ [NETC_STAT_TX_ERROR_UNDERSIZE] = "out-error-undersize", ++ ++ /* Discard stats */ ++ [NETC_STAT_RX_DISCARD_COUNT] = "in-discard-count", ++ [NETC_STAT_RX_DISCARD_REASON0] = "in-discard-reason0", ++ [NETC_STAT_RX_DISCARD_TABLE_ID] = "in-discard-table-id", ++ [NETC_STAT_RX_DISCARD_ENTRY_ID] = "in-discard-entry-id", ++ [NETC_STAT_TX_DISCARD_COUNT] = "out-discard-count", ++ [NETC_STAT_TX_DISCARD_REASON0] = "out-discard-reason0", ++ [NETC_STAT_TX_DISCARD_TABLE_ID] = "out-discard-table-id", ++ [NETC_STAT_TX_DISCARD_ENTRY_ID] = "out-discard-entry-id", ++ [NETC_STAT_BRIDGE_DISCARD_COUNT] = "bridge-discard-count", ++ [NETC_STAT_BRIDGE_DISCARD_REASON0] = "bridge-discard-reason0", ++ [NETC_STAT_BRIDGE_DISCARD_TABLE_ID] = "bridge-discard-table-id", ++ [NETC_STAT_BRIDGE_DISCARD_ENTRY_ID] = "bridge-discard-entry-id", ++ ++ /* Q0 stats */ ++ [NETC_STAT_Q0_REJECTED_BYTES] = "q0-rejected-bytes", ++ [NETC_STAT_Q0_REJECTED_FRAMES] = "q0-rejected-frames", ++ [NETC_STAT_Q0_DEQUEUE_BYTES] = "q0-dequeue-bytes", ++ [NETC_STAT_Q0_DEQUEUE_FRAMES] = "q0-dequeue-frames", ++ [NETC_STAT_Q0_DROPPED_BYTES] = "q0-dropped-bytes", ++ [NETC_STAT_Q0_DROPPED_FRAMES] = "q0-dropped-frames", ++ [NETC_STAT_Q0_FRAMES] = "q0-frames", ++ ++ /* Q1 stats */ ++ [NETC_STAT_Q1_REJECTED_BYTES] = "q1-rejected-bytes", ++ [NETC_STAT_Q1_REJECTED_FRAMES] = "q1-rejected-frames", ++ [NETC_STAT_Q1_DEQUEUE_BYTES] = "q1-dequeue-bytes", ++ [NETC_STAT_Q1_DEQUEUE_FRAMES] = "q1-dequeue-frames", ++ [NETC_STAT_Q1_DROPPED_BYTES] = "q1-dropped-bytes", ++ [NETC_STAT_Q1_DROPPED_FRAMES] = "q1-dropped-frames", ++ [NETC_STAT_Q1_FRAMES] = "q1-frames", ++ ++ /* Q2 stats */ ++ [NETC_STAT_Q2_REJECTED_BYTES] = "q2-rejected-bytes", ++ [NETC_STAT_Q2_REJECTED_FRAMES] = "q2-rejected-frames", ++ [NETC_STAT_Q2_DEQUEUE_BYTES] = "q2-dequeue-bytes", ++ [NETC_STAT_Q2_DEQUEUE_FRAMES] = "q2-dequeue-frames", ++ [NETC_STAT_Q2_DROPPED_BYTES] = "q2-dropped-bytes", ++ [NETC_STAT_Q2_DROPPED_FRAMES] = "q2-dropped-frames", ++ [NETC_STAT_Q2_FRAMES] = "q2-frames", ++ ++ /* Q3 stats */ ++ [NETC_STAT_Q3_REJECTED_BYTES] = "q3-rejected-bytes", ++ [NETC_STAT_Q3_REJECTED_FRAMES] = "q3-rejected-frames", ++ [NETC_STAT_Q3_DEQUEUE_BYTES] = "q3-dequeue-bytes", ++ [NETC_STAT_Q3_DEQUEUE_FRAMES] = "q3-dequeue-frames", ++ [NETC_STAT_Q3_DROPPED_BYTES] = "q3-dropped-bytes", ++ [NETC_STAT_Q3_DROPPED_FRAMES] = "q3-dropped-frames", ++ [NETC_STAT_Q3_FRAMES] = "q3-frames", ++ ++ /* Q4 stats */ ++ [NETC_STAT_Q4_REJECTED_BYTES] = "q4-rejected-bytes", ++ [NETC_STAT_Q4_REJECTED_FRAMES] = "q4-rejected-frames", ++ [NETC_STAT_Q4_DEQUEUE_BYTES] = "q4-dequeue-bytes", ++ [NETC_STAT_Q4_DEQUEUE_FRAMES] = "q4-dequeue-frames", ++ [NETC_STAT_Q4_DROPPED_BYTES] = "q4-dropped-bytes", ++ [NETC_STAT_Q4_DROPPED_FRAMES] = "q4-dropped-frames", ++ [NETC_STAT_Q4_FRAMES] = "q4-frames", ++ ++ /* Q5 stats */ ++ [NETC_STAT_Q5_REJECTED_BYTES] = "q5-rejected-bytes", ++ [NETC_STAT_Q5_REJECTED_FRAMES] = "q5-rejected-frames", ++ [NETC_STAT_Q5_DEQUEUE_BYTES] = "q5-dequeue-bytes", ++ [NETC_STAT_Q5_DEQUEUE_FRAMES] = "q5-dequeue-frames", ++ [NETC_STAT_Q5_DROPPED_BYTES] = "q5-dropped-bytes", ++ [NETC_STAT_Q5_DROPPED_FRAMES] = "q5-dropped-frames", ++ [NETC_STAT_Q5_FRAMES] = "q5-frames", ++ ++ /* Q6 stats */ ++ [NETC_STAT_Q6_REJECTED_BYTES] = "q6-rejected-bytes", ++ [NETC_STAT_Q6_REJECTED_FRAMES] = "q6-rejected-frames", ++ [NETC_STAT_Q6_DEQUEUE_BYTES] = "q6-dequeue-bytes", ++ [NETC_STAT_Q6_DEQUEUE_FRAMES] = "q6-dequeue-frames", ++ [NETC_STAT_Q6_DROPPED_BYTES] = "q6-dropped-bytes", ++ [NETC_STAT_Q6_DROPPED_FRAMES] = "q6-dropped-frames", ++ [NETC_STAT_Q6_FRAMES] = "q6-frames", ++ ++ /* Q7 stats */ ++ [NETC_STAT_Q7_REJECTED_BYTES] = "q7-rejected-bytes", ++ [NETC_STAT_Q7_REJECTED_FRAMES] = "q7-rejected-frames", ++ [NETC_STAT_Q7_DEQUEUE_BYTES] = "q7-dequeue-bytes", ++ [NETC_STAT_Q7_DEQUEUE_FRAMES] = "q7-dequeue-frames", ++ [NETC_STAT_Q7_DROPPED_BYTES] = "q7-dropped-bytes", ++ [NETC_STAT_Q7_DROPPED_FRAMES] = "q7-dropped-frames", ++ [NETC_STAT_Q7_FRAMES] = "q7-frames", ++}; ++ ++void netc_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_cmd_port_ethtool_stats stats; ++ int rc; ++ enum netc_stat_index i; ++ ++ rc = netc_xfer_get_cmd(priv, NETC_CMD_PORT_ETHTOOL_STATS_GET, ++ port, &stats, sizeof(stats)); ++ ++ if (rc) { ++ dev_err(ds->dev, ++ "Failed to get port %d stats\n", port); ++ return; ++ } ++ ++ for (i = 0; i < NETC_STAT_NUM; i++) ++ data[i] = stats.values[i]; ++} ++ ++void netc_get_strings(struct dsa_switch *ds, int port, ++ u32 stringset, u8 *data) ++{ ++ enum netc_stat_index i; ++ char *p = data; ++ ++ if (stringset != ETH_SS_STATS) ++ return; ++ ++ for (i = 0; i < NETC_STAT_NUM; i++) { ++ strscpy(p, netc_stat_name[i], ETH_GSTRING_LEN); ++ p += ETH_GSTRING_LEN; ++ } ++} ++ ++int netc_get_sset_count(struct dsa_switch *ds, int port, int sset) ++{ ++ if (sset != ETH_SS_STATS) ++ return -EOPNOTSUPP; ++ ++ return NETC_STAT_NUM; ++} +diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c +new file mode 100644 +index 000000000000..5ba2b8d7ac93 +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_main.c +@@ -0,0 +1,928 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "netc.h" ++ ++int netc_is_vlan_configured(struct netc_private *priv, uint16_t vid) ++{ ++ struct netc_vlan_entry *vlan; ++ int count, i; ++ ++ vlan = priv->config.vlan; ++ count = priv->config.vlan_count; ++ ++ for (i = 0; i < count; i++) { ++ if (vlan[i].vid == vid) ++ return i; ++ } ++ ++ /* Return an invalid entry index if not found */ ++ return -1; ++} ++ ++static bool vid_is_netc_dsa_8021q(struct dsa_switch *ds, u16 vid) ++{ ++ int port; ++ struct dsa_port *dp; ++ unsigned int bridge_num; ++ u16 standalone_vid, bridge_vid; ++ ++ for (port = 0; port < ds->num_ports; port++) { ++ dp = dsa_to_port(ds, port); ++ standalone_vid = dsa_tag_8021q_standalone_vid(dp); ++ ++ if (vid == standalone_vid) ++ return true; ++ ++ if (dp->bridge) { ++ bridge_num = dsa_port_bridge_num_get(dp); ++ bridge_vid = dsa_tag_8021q_bridge_vid(bridge_num); ++ ++ if (vid == bridge_vid) ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++static int netc_drop_untagged(struct dsa_switch *ds, int port, bool drop) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_mac_config *mac; ++ ++ mac = &priv->config.mac[port]; ++ if (mac->drpuntag == drop) ++ return 0; ++ ++ mac->drpuntag = drop; ++ ++ return netc_port_dropuntag_set(priv, port, drop); ++} ++ ++static int netc_pvid_apply(struct netc_private *priv, int port, uint16_t pvid) ++{ ++ struct netc_mac_config *mac; ++ ++ mac = &priv->config.mac[port]; ++ if (mac->vlanid == pvid) ++ return 0; ++ ++ mac->vlanid = pvid; ++ ++ return netc_port_pvid_set(priv, port, pvid); ++} ++ ++static int netc_commit_pvid(struct dsa_switch *ds, int port) ++{ ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct net_device *br = dsa_port_bridge_dev_get(dp); ++ struct netc_private *priv = ds->priv; ++ bool drop_untagged = false; ++ int rc; ++ uint16_t pvid; ++ ++ if (br && br_vlan_enabled(br)) ++ pvid = priv->bridge_pvid[port]; ++ else ++ pvid = priv->tag_8021q_pvid[port]; ++ ++ rc = netc_pvid_apply(priv, port, pvid); ++ if (rc) ++ return rc; ++ ++ /* ++ * Only force dropping of untagged packets when the port is under a ++ * VLAN-aware bridge. When the tag_8021q pvid is used, we are ++ * deliberately removing the RX VLAN from the port's VMEMB_PORT list, ++ * to prevent DSA tag spoofing from the link partner. Untagged packets ++ * are the only ones that should be received with tag_8021q, so ++ * definitely don't drop them. ++ */ ++ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) ++ drop_untagged = true; ++ ++ return netc_drop_untagged(ds, port, drop_untagged); ++} ++ ++static int netc_fdb_add(struct dsa_switch *ds, int port, ++ const unsigned char *addr, uint16_t vid, ++ struct dsa_db db) ++{ ++ struct netc_private *priv = ds->priv; ++ ++ if (!vid) { ++ switch (db.type) { ++ case DSA_DB_PORT: ++ vid = dsa_tag_8021q_standalone_vid(db.dp); ++ break; ++ case DSA_DB_BRIDGE: ++ vid = dsa_tag_8021q_bridge_vid(db.bridge.num); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ /* Allow enough time between consecutive calls for adding FDB entry */ ++ usleep_range(NETC_SPI_MSG_RESPONSE_TIME, ++ NETC_SPI_MSG_RESPONSE_TIME * 10); ++ ++ return netc_fdb_entry_add(priv, addr, vid, port); ++} ++ ++static int netc_fdb_del(struct dsa_switch *ds, int port, ++ const unsigned char *addr, uint16_t vid, ++ struct dsa_db db) ++{ ++ struct netc_private *priv = ds->priv; ++ ++ if (!vid) { ++ switch (db.type) { ++ case DSA_DB_PORT: ++ vid = dsa_tag_8021q_standalone_vid(db.dp); ++ break; ++ case DSA_DB_BRIDGE: ++ vid = dsa_tag_8021q_bridge_vid(db.bridge.num); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ return netc_fdb_entry_del(priv, addr, vid); ++} ++ ++static int netc_fdb_dump(struct dsa_switch *ds, int port, ++ dsa_fdb_dump_cb_t *cb, void *data) ++{ ++ struct netc_private *priv = ds->priv; ++ struct device *dev = ds->dev; ++ u32 entry_id = 0, next_id = 0; ++ int rc; ++ ++ while (1) { ++ struct netc_fdb_entry fdb = {0}; ++ ++ rc = netc_fdb_entry_get(priv, &fdb, entry_id, &next_id); ++ /* No fdb entry at i, not an issue */ ++ if (rc) { ++ dev_err(dev, "Failed to dump FDB: %d\n", rc); ++ return rc; ++ } ++ ++ if (next_id == 0) /* This entry is empty */ ++ return 0; ++ ++ /* ++ * FDB dump callback is per port. This means we have to ++ * disregard a valid entry if it's not for this port, even if ++ * only to revisit it later. This is inefficient because the ++ * 1024-sized FDB table needs to be traversed 4 times through ++ * SPI during a 'bridge fdb show' command. ++ */ ++ if (fdb.port_map & BIT(port)) { ++ /* Need to hide the dsa_8021q VLANs from the user. */ ++ if (vid_is_netc_dsa_8021q(ds, fdb.vid)) ++ fdb.vid = 0; ++ ++ rc = cb(fdb.mac_addr, fdb.vid, fdb.dynamic, data); ++ if (rc) ++ return rc; ++ } ++ ++ entry_id = next_id; ++ ++ if (entry_id == 0 || entry_id == 0xffffffff) ++ break; ++ } ++ ++ return 0; ++} ++ ++static int netc_parse_ports_node(struct netc_private *priv, ++ struct device_node *ports_node) ++{ ++ struct device *dev = &priv->spidev->dev; ++ struct device_node *child; ++ ++ for_each_available_child_of_node(ports_node, child) { ++ struct device_node *phy_node; ++ phy_interface_t phy_mode; ++ u32 index; ++ int err; ++ ++ /* Get switch port number from DT */ ++ if (of_property_read_u32(child, "reg", &index) < 0) { ++ dev_err(dev, "Port number not defined in device tree\n"); ++ of_node_put(child); ++ return -ENODEV; ++ } ++ ++ /* Get PHY mode from DT */ ++ err = of_get_phy_mode(child, &phy_mode); ++ if (err) { ++ dev_err(dev, "Failed to read phy-mode or phy-interface-type %d\n", ++ index); ++ of_node_put(child); ++ return -ENODEV; ++ } ++ ++ phy_node = of_parse_phandle(child, "phy-handle", 0); ++ if (!phy_node) { ++ if (!of_phy_is_fixed_link(child)) { ++ dev_err(dev, "phy-handle or fixed-link properties missing!\n"); ++ of_node_put(child); ++ return -ENODEV; ++ } ++ /* phy-handle is missing, but fixed-link isn't. ++ * So it's a fixed link. Default to PHY role. ++ */ ++ priv->fixed_link[index] = true; ++ } else { ++ of_node_put(phy_node); ++ } ++ ++ priv->phy_mode[index] = phy_mode; ++ } ++ ++ return 0; ++} ++ ++static int netc_parse_dt(struct netc_private *priv) ++{ ++ struct device *dev = &priv->spidev->dev; ++ struct device_node *switch_node = dev->of_node; ++ struct device_node *ports_node; ++ int rc; ++ ++ ports_node = of_get_child_by_name(switch_node, "ports"); ++ if (!ports_node) ++ ports_node = of_get_child_by_name(switch_node, "ethernet-ports"); ++ if (!ports_node) { ++ dev_err(dev, "Incorrect bindings: absent \"ports\" node\n"); ++ return -ENODEV; ++ } ++ ++ rc = netc_parse_ports_node(priv, ports_node); ++ of_node_put(ports_node); ++ ++ return rc; ++} ++ ++static void netc_mac_link_down(struct dsa_switch *ds, int port, ++ unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_mac_config *mac; ++ ++ mac = &priv->config.mac[port]; ++ ++ mac->egress = false; ++ ++ netc_port_link_set(priv, port, false); ++} ++ ++static void netc_mac_link_up(struct dsa_switch *ds, int port, ++ unsigned int mode, ++ phy_interface_t interface, ++ struct phy_device *phydev, ++ int speed, int duplex, ++ bool tx_pause, bool rx_pause) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_mac_config *mac; ++ ++ mac = &priv->config.mac[port]; ++ ++ mac->speed = speed; ++ mac->egress = true; ++ ++ netc_port_phylink_mode_set(priv, mac); ++ netc_port_link_set(priv, port, true); ++} ++ ++static void netc_phylink_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) ++{ ++ struct netc_private *priv = ds->priv; ++ phy_interface_t phy_mode; ++ ++ /* ++ * This driver does not make use of the speed, duplex, pause or the ++ * advertisement in its mac_config, so it is safe to mark this driver ++ * as non-legacy. ++ */ ++ config->legacy_pre_march2020 = false; ++ ++ phy_mode = priv->phy_mode[port]; ++ __set_bit(phy_mode, config->supported_interfaces); ++ ++ /* ++ * The MAC does not support pause frames, and also doesn't ++ * support half-duplex traffic modes. ++ */ ++ config->mac_capabilities = MAC_10FD | MAC_100FD; ++ config->mac_capabilities |= MAC_1000FD; ++} ++ ++static int netc_bridge_member(struct dsa_switch *ds, int port, ++ struct dsa_bridge bridge, bool member) ++{ ++ int rc; ++ ++ rc = netc_commit_pvid(ds, port); ++ if (rc) ++ return rc; ++ ++ return 0; ++} ++ ++static int netc_bridge_join(struct dsa_switch *ds, int port, ++ struct dsa_bridge bridge, ++ bool *tx_fwd_offload, ++ struct netlink_ext_ack *extack) ++{ ++ int rc; ++ ++ rc = netc_bridge_member(ds, port, bridge, true); ++ if (rc) ++ return rc; ++ ++ rc = dsa_tag_8021q_bridge_join(ds, port, bridge); ++ if (rc) { ++ netc_bridge_member(ds, port, bridge, false); ++ return rc; ++ } ++ ++ *tx_fwd_offload = true; ++ ++ return 0; ++} ++ ++static void netc_bridge_leave(struct dsa_switch *ds, int port, ++ struct dsa_bridge bridge) ++{ ++ dsa_tag_8021q_bridge_leave(ds, port, bridge); ++ netc_bridge_member(ds, port, bridge, false); ++} ++ ++static enum dsa_tag_protocol ++netc_get_tag_protocol(struct dsa_switch *ds, int port, ++ enum dsa_tag_protocol mp) ++{ ++ struct netc_private *priv = ds->priv; ++ ++ return priv->info->tag_proto; ++} ++ ++int netc_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, ++ struct netlink_ext_ack *extack) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_config *config = &priv->config; ++ int rc; ++ ++ if (enabled) { ++ /* Enable VLAN filtering. */ ++ config->tpid = ETH_P_8021Q; ++ config->tpid2 = ETH_P_8021AD; ++ } else { ++ /* Disable VLAN filtering. */ ++ config->tpid = ETH_P_8021Q; ++ config->tpid2 = ETH_P_NETC; ++ } ++ ++ for (port = 0; port < ds->num_ports; port++) { ++ if (dsa_is_unused_port(ds, port)) ++ continue; ++ ++ rc = netc_commit_pvid(ds, port); ++ if (rc) ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int netc_bridge_vlan_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan, ++ struct netlink_ext_ack *extack) ++{ ++ struct netc_private *priv = ds->priv; ++ uint16_t flags = vlan->flags; ++ bool untagged = false; ++ int rc; ++ ++ /* Be sure to deny the configuration done by tag_8021q. */ ++ if (vid_is_netc_dsa_8021q(ds, vlan->vid)) { ++ NL_SET_ERR_MSG_MOD(extack, ++ "VLAN ID 3072-3076 & 3088 reserved for dsa_8021q operation"); ++ return -EBUSY; ++ } ++ ++ /* Always install bridge VLANs as egress-tagged on CPU and DSA ports */ ++ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) ++ flags = 0; ++ ++ if (flags & BRIDGE_VLAN_INFO_UNTAGGED) ++ untagged = true; ++ ++ rc = netc_vlan_entry_add(priv, vlan->vid, port, untagged); ++ if (rc) ++ return rc; ++ ++ if (vlan->flags & BRIDGE_VLAN_INFO_PVID) ++ priv->bridge_pvid[port] = vlan->vid; ++ ++ /* Allow enough time between adding VLAN entry and setting PVID */ ++ usleep_range(NETC_SPI_MSG_RESPONSE_TIME, ++ NETC_SPI_MSG_RESPONSE_TIME * 10); ++ ++ return netc_commit_pvid(ds, port); ++} ++ ++static int netc_bridge_vlan_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan) ++{ ++ struct netc_private *priv = ds->priv; ++ int rc; ++ ++ rc = netc_vlan_entry_del(priv, vlan->vid, port); ++ if (rc) ++ return rc; ++ ++ /* ++ * In case the pvid was deleted, make sure that untagged packets will ++ * be dropped. ++ */ ++ return netc_commit_pvid(ds, port); ++} ++ ++static int netc_8021q_vlan_add(struct dsa_switch *ds, int port, ++ uint16_t vid, uint16_t flags) ++{ ++ struct netc_private *priv = ds->priv; ++ int rc; ++ ++ rc = netc_vlan_entry_add(priv, vid, port, false); ++ if (rc) ++ return rc; ++ ++ if (flags & BRIDGE_VLAN_INFO_PVID) ++ priv->tag_8021q_pvid[port] = vid; ++ ++ /* Allow enough time between adding VLAN entry and setting PVID */ ++ usleep_range(NETC_SPI_MSG_RESPONSE_TIME, ++ NETC_SPI_MSG_RESPONSE_TIME * 10); ++ ++ return netc_commit_pvid(ds, port); ++} ++ ++static int netc_8021q_vlan_del(struct dsa_switch *ds, int port, uint16_t vid) ++{ ++ struct netc_private *priv = ds->priv; ++ ++ return netc_vlan_entry_del(priv, vid, port); ++} ++ ++static int netc_prechangeupper(struct dsa_switch *ds, int port, ++ struct netdev_notifier_changeupper_info *info) ++{ ++ struct netlink_ext_ack *extack = info->info.extack; ++ struct net_device *upper = info->upper_dev; ++ struct dsa_switch_tree *dst = ds->dst; ++ struct dsa_port *dp; ++ ++ if (is_vlan_dev(upper)) { ++ NL_SET_ERR_MSG_MOD(extack, "8021q uppers are not supported"); ++ return -EBUSY; ++ } ++ ++ if (netif_is_bridge_master(upper)) { ++ list_for_each_entry(dp, &dst->ports, list) { ++ struct net_device *br = dsa_port_bridge_dev_get(dp); ++ ++ if (br && br != upper && br_vlan_enabled(br)) { ++ NL_SET_ERR_MSG_MOD(extack, ++ "Only one VLAN-aware bridge is supported"); ++ return -EBUSY; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++#define work_to_xmit_work(w) \ ++ container_of((w), struct netc_deferred_xmit_work, work) ++ ++static void netc_port_deferred_xmit(struct kthread_work *work) ++{ ++ struct netc_deferred_xmit_work *xmit_work = work_to_xmit_work(work); ++ struct sk_buff *clone, *skb = xmit_work->skb; ++ struct dsa_switch *ds = xmit_work->dp->ds; ++ int port = xmit_work->dp->index; ++ ++ clone = NETC_SKB_CB(skb)->clone; ++ ++ /* Transfer skb to the host port. */ ++ dsa_enqueue_skb(skb, dsa_to_port(ds, port)->slave); ++ ++ /* The clone, if there, was made by dsa_skb_tx_timestamp */ ++ if (clone) ++ netc_ptp_txtstamp_skb(ds, port, clone); ++ ++ kfree(xmit_work); ++} ++ ++static int netc_connect_tag_protocol(struct dsa_switch *ds, ++ enum dsa_tag_protocol proto) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_tagger_data *tagger_data; ++ ++ if (proto != priv->info->tag_proto) ++ return -EPROTONOSUPPORT; ++ ++ tagger_data = netc_tagger_data(ds); ++ tagger_data->xmit_work_fn = netc_port_deferred_xmit; ++ ++ return 0; ++} ++ ++static int netc_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ++{ ++ struct netc_private *priv = ds->priv; ++ int maxlen = new_mtu + ETH_HLEN + ETH_FCS_LEN; ++ ++ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) ++ maxlen += VLAN_HLEN; ++ ++ return netc_port_mtu_set(priv, port, maxlen); ++} ++ ++static int netc_get_max_mtu(struct dsa_switch *ds, int port) ++{ ++ return 2000 - VLAN_ETH_HLEN - ETH_FCS_LEN; ++} ++ ++static int netc_mac_init(struct netc_private *priv) ++{ ++ struct netc_mac_config *mac; ++ struct dsa_switch *ds = priv->ds; ++ struct dsa_port *dp; ++ ++ mac = priv->config.mac; ++ ++ dsa_switch_for_each_port(dp, ds) { ++ mac[dp->index].port = dp->index; ++ mac[dp->index].speed = 1000; ++ mac[dp->index].vlanid = 1; ++ mac[dp->index].drpuntag = false; ++ mac[dp->index].retag = false; ++ ++ if (dsa_port_is_dsa(dp)) ++ dp->learning = true; ++ ++ /* Disallow untagged packets from being received on the ++ * CPU and DSA ports. ++ */ ++ if (dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)) ++ mac[dp->index].drpuntag = true; ++ } ++ ++ return 0; ++} ++ ++static int netc_dsa_init(struct netc_private *priv) ++{ ++ struct dsa_switch *ds = priv->ds; ++ struct dsa_port *dp, *cpu_dp = NULL; ++ const u8 *mac; ++ int port; ++ ++ for (port = 0; port < ds->num_ports; port++) { ++ if (dsa_is_cpu_port(ds, port)) { ++ cpu_dp = dsa_to_port(ds, port); ++ break; ++ } ++ } ++ ++ if (!cpu_dp) { ++ dev_err(ds->dev, "Failed to find cpu port\n"); ++ return -ENODEV; ++ } ++ ++ if (!is_zero_ether_addr(cpu_dp->mac)) ++ mac = cpu_dp->mac; ++ else ++ mac = cpu_dp->master->dev_addr; ++ ++ pr_info("NETC DSA: cpu port:%d master:%s\n", ++ cpu_dp->index, cpu_dp->master->name); ++ ++ for (port = 0; port < ds->num_ports; port++) { ++ dp = dsa_to_port(ds, port); ++ ++ if (dsa_port_is_unused(dp)) ++ continue; ++ if (dsa_port_is_cpu(dp)) ++ continue; ++ ++ pr_info("NETC DSA: add switch port:%d\n", port); ++ ++ netc_port_dsa_add(priv, cpu_dp->index, port, mac); ++ } ++ ++ return 0; ++} ++ ++static int netc_setup(struct dsa_switch *ds) ++{ ++ struct netc_private *priv = ds->priv; ++ int port; ++ int rc; ++ ++ rc = netc_config_setup(&priv->config); ++ if (rc < 0) { ++ dev_err(ds->dev, "Failed to setup config: %d\n", rc); ++ return rc; ++ } ++ ++ netc_mac_init(priv); ++ netc_dsa_init(priv); ++ ++ for (port = 0; port < ds->num_ports; port++) { ++ priv->tag_8021q_pvid[port] = NETC_DEFAULT_VLAN; ++ priv->bridge_pvid[port] = NETC_DEFAULT_VLAN; ++ } ++ ++ rc = netc_devlink_setup(ds); ++ if (rc < 0) ++ goto out_config_free; ++ ++ rtnl_lock(); ++ rc = dsa_tag_8021q_register(ds, htons(ETH_P_8021Q)); ++ rtnl_unlock(); ++ if (rc) ++ goto out_devlink_teardown; ++ ++ /* ++ * On netc, VLAN filtering per se is always enabled in hardware. ++ * The only thing we can do to disable it is lie about what the 802.1Q ++ * EtherType is. ++ * So it will still try to apply VLAN filtering, but all ingress ++ * traffic (except frames received with EtherType of ETH_P_NETC) ++ * will be internally tagged with a distorted VLAN header where the ++ * TPID is ETH_P_NETC, and the VLAN ID is the port pvid. ++ */ ++ ds->vlan_filtering_is_global = true; ++ ds->untag_bridge_pvid = true; ++ ds->fdb_isolation = true; ++ /* tag_8021q has 3 bits for the VBID, and the value 0 is reserved */ ++ ds->max_num_bridges = 7; ++ ++ /* Advertise the 8 egress queues */ ++ ds->num_tx_queues = NETC_NUM_TC; ++ ++ ds->mtu_enforcement_ingress = true; ++ ds->assisted_learning_on_cpu_port = true; ++ ++ return 0; ++ ++out_devlink_teardown: ++ netc_devlink_teardown(ds); ++out_config_free: ++ netc_config_free(&priv->config); ++ ++ return rc; ++} ++ ++static void netc_teardown(struct dsa_switch *ds) ++{ ++ struct netc_private *priv = ds->priv; ++ ++ rtnl_lock(); ++ dsa_tag_8021q_unregister(ds); ++ rtnl_unlock(); ++ ++ netc_devlink_teardown(ds); ++ netc_config_free(&priv->config); ++} ++ ++static const struct dsa_switch_ops netc_switch_ops = { ++ .get_tag_protocol = netc_get_tag_protocol, ++ .connect_tag_protocol = netc_connect_tag_protocol, ++ .setup = netc_setup, ++ .teardown = netc_teardown, ++ .port_change_mtu = netc_change_mtu, ++ .port_max_mtu = netc_get_max_mtu, ++ .phylink_get_caps = netc_phylink_get_caps, ++ .phylink_mac_link_up = netc_mac_link_up, ++ .phylink_mac_link_down = netc_mac_link_down, ++ .get_strings = netc_get_strings, ++ .get_ethtool_stats = netc_get_ethtool_stats, ++ .get_sset_count = netc_get_sset_count, ++ .port_fdb_dump = netc_fdb_dump, ++ .port_fdb_add = netc_fdb_add, ++ .port_fdb_del = netc_fdb_del, ++ .port_bridge_join = netc_bridge_join, ++ .port_bridge_leave = netc_bridge_leave, ++ .port_vlan_filtering = netc_vlan_filtering, ++ .port_vlan_add = netc_bridge_vlan_add, ++ .port_vlan_del = netc_bridge_vlan_del, ++ .devlink_info_get = netc_devlink_info_get, ++ .tag_8021q_vlan_add = netc_8021q_vlan_add, ++ .tag_8021q_vlan_del = netc_8021q_vlan_del, ++ .port_prechangeupper = netc_prechangeupper, ++}; ++ ++static const struct of_device_id netc_dt_ids[]; ++static int netc_check_device_id(struct netc_private *priv) ++{ ++ struct device *dev = &priv->spidev->dev; ++ struct netc_config *config = &priv->config; ++ int rc; ++ ++ rc = netc_get_devinfo(priv, config); ++ if (rc < 0) ++ return rc; ++ ++ if (config->device_id != priv->info->device_id) { ++ dev_err(dev, "Device tree specifies device ID 0x%x, but found 0x%x please fix it!\n", ++ priv->info->device_id, config->device_id); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static int netc_probe(struct spi_device *spi) ++{ ++ struct device *dev = &spi->dev; ++ struct netc_private *priv; ++ struct dsa_switch *ds; ++ size_t max_xfer, max_msg; ++ int rc; ++ ++ if (!dev->of_node) { ++ dev_err(dev, "No DTS bindings for netc driver\n"); ++ return -EINVAL; ++ } ++ ++ priv = devm_kzalloc(dev, sizeof(struct netc_private), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ /* ++ * Populate our driver private structure (priv) based on ++ * the device tree node that was probed (spi) ++ */ ++ priv->spidev = spi; ++ spi_set_drvdata(spi, priv); ++ ++ /* Configure the SPI bus */ ++ spi->bits_per_word = NETC_SPI_WORD_BITS; ++ rc = spi_setup(spi); ++ if (rc < 0) { ++ dev_err(dev, "Could not init SPI\n"); ++ return rc; ++ } ++ ++ max_xfer = spi_max_transfer_size(spi); ++ max_msg = spi_max_message_size(spi); ++ ++ /* ++ * We need to send at least one 64-bit word of SPI payload per message ++ * in order to be able to make useful progress. ++ */ ++ if (max_msg < NETC_SPI_MSG_HEADER_SIZE + 8) { ++ dev_err(dev, "SPI master cannot send large enough buffers, aborting\n"); ++ return -EINVAL; ++ } ++ ++ priv->max_xfer_len = NETC_SPI_MSG_MAXLEN; ++ if (priv->max_xfer_len > max_xfer) ++ priv->max_xfer_len = max_xfer; ++ if (priv->max_xfer_len > max_msg - NETC_SPI_MSG_HEADER_SIZE) ++ priv->max_xfer_len = max_msg - NETC_SPI_MSG_HEADER_SIZE; ++ ++ priv->info = of_device_get_match_data(dev); ++ ++ /* Detect hardware device */ ++ rc = netc_check_device_id(priv); ++ if (rc < 0) { ++ dev_err(dev, "Device ID check failed: %d\n", rc); ++ return rc; ++ } ++ ++ dev_info(dev, "Probed switch chip:%s ID:0x%x firmware:%d.%d.%d\n", ++ priv->info->name, ++ priv->config.device_id, ++ priv->config.version_major, ++ priv->config.version_minor, ++ priv->config.version_revision); ++ ++ ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); ++ if (!ds) ++ return -ENOMEM; ++ ++ ds->dev = dev; ++ ds->num_ports = priv->info->num_ports; ++ ds->ops = &netc_switch_ops; ++ ds->priv = priv; ++ priv->ds = ds; ++ ++ mutex_init(&priv->mgmt_lock); ++ ++ rc = netc_parse_dt(priv); ++ if (rc < 0) { ++ dev_err(ds->dev, "Failed to parse DT: %d\n", rc); ++ return rc; ++ } ++ ++ return dsa_register_switch(priv->ds); ++} ++ ++static void netc_remove(struct spi_device *spi) ++{ ++ struct netc_private *priv = spi_get_drvdata(spi); ++ ++ if (!priv) ++ return; ++ ++ dsa_unregister_switch(priv->ds); ++} ++ ++static void netc_shutdown(struct spi_device *spi) ++{ ++ struct netc_private *priv = spi_get_drvdata(spi); ++ ++ if (!priv) ++ return; ++ ++ dsa_switch_shutdown(priv->ds); ++ ++ spi_set_drvdata(spi, NULL); ++} ++ ++const struct netc_info netc_info = { ++ .device_id = NETC_RT1180_DEVICE_ID, ++ .tag_proto = DSA_TAG_PROTO_NETC_VALUE, ++ .can_limit_mcast_flood = false, ++ .num_ports = NETC_NUM_PORTS, ++ .name = "netc", ++}; ++ ++static const struct of_device_id netc_dt_ids[] = { ++ { .compatible = "nxp,imxrt1180-netc", .data = &netc_info}, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, netc_dt_ids); ++ ++static const struct spi_device_id netc_spi_ids[] = { ++ { "imxrt1180-netc" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(spi, netc_spi_ids); ++ ++static struct spi_driver netc_driver = { ++ .driver = { ++ .name = "netc-spi", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(netc_dt_ids), ++ }, ++ .id_table = netc_spi_ids, ++ .probe = netc_probe, ++ .remove = netc_remove, ++ .shutdown = netc_shutdown, ++}; ++ ++module_spi_driver(netc_driver); ++ ++MODULE_AUTHOR("Minghuan Lian "); ++ ++MODULE_DESCRIPTION("NETC DSA Driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/net/dsa/netc/netc_ptp.c b/drivers/net/dsa/netc/netc_ptp.c +new file mode 100644 +index 000000000000..de5fda25702d +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_ptp.c +@@ -0,0 +1,12 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "netc.h" ++ ++void netc_ptp_txtstamp_skb(struct dsa_switch *ds, int port, ++ struct sk_buff *skb) ++{ ++ /* To update tx timestamp in the skb */ ++} +diff --git a/drivers/net/dsa/netc/netc_spi.c b/drivers/net/dsa/netc/netc_spi.c +new file mode 100644 +index 000000000000..056501bfdd76 +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_spi.c +@@ -0,0 +1,119 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include ++#include "netc.h" ++ ++int netc_xfer_cmd(const struct netc_private *priv, ++ enum netc_spi_rw_mode rw, enum netc_cmd cmd, ++ void *param, size_t param_len, ++ void *resp, size_t resp_len) ++{ ++ struct netc_cmd_hdr hdr = {0}; ++ struct spi_device *spi = priv->spidev; ++ struct spi_transfer hdr_xfer, resp_xfer; ++ int rc; ++ ++ if (!IS_ALIGNED(resp_len, NETC_SPI_MSG_WORD_BYTES)) { ++ dev_err(&spi->dev, "netc cmd %d data size should be a multiple of 4 : %ld", ++ cmd, resp_len); ++ return -EINVAL; ++ } ++ ++ if (resp_len > priv->max_xfer_len) { ++ dev_err(&spi->dev, "netc cmd %d data size is too large\n", ++ cmd); ++ return -EINVAL; ++ } ++ ++ if (param_len > NETC_SPI_MSG_PARAM_SIZE) { ++ dev_err(&spi->dev, "netc cmd %d param size is too large\n", ++ cmd); ++ return -EINVAL; ++ } ++ ++ hdr.cmd = (rw << NETC_CMD_DIR_SHIFT) | ++ ((resp_len / NETC_SPI_MSG_WORD_BYTES) << ++ NETC_CMD_LEN_SHIFT) | ++ cmd; ++ if (param) ++ memcpy(hdr.param, param, param_len); ++ ++ hdr_xfer.tx_buf = &hdr; ++ hdr_xfer.len = NETC_SPI_MSG_HEADER_SIZE; ++ ++ rc = spi_sync_transfer(spi, &hdr_xfer, 1); ++ if (rc < 0) { ++ dev_err(&spi->dev, "netc cmd %d SPI transfer failed: %d\n", ++ cmd, rc); ++ return rc; ++ } ++ ++ usleep_range(NETC_SPI_MSG_RESPONSE_TIME, ++ NETC_SPI_MSG_RESPONSE_TIME * 10); ++ ++ if (!resp) ++ return 0; ++ ++ /* Populate the transfer's data buffer */ ++ if (rw == SPI_READ) ++ resp_xfer.rx_buf = resp; ++ else ++ resp_xfer.tx_buf = resp; ++ resp_xfer.len = resp_len; ++ ++ rc = spi_sync_transfer(spi, &resp_xfer, 1); ++ if (rc < 0) { ++ dev_err(&spi->dev, "netc cmd %d SPI transfer failed: %d\n", ++ cmd, rc); ++ return rc; ++ } ++ ++ usleep_range(NETC_SPI_MSG_RESPONSE_TIME, ++ NETC_SPI_MSG_RESPONSE_TIME * 10); ++ ++ return 0; ++} ++ ++int netc_xfer_set_cmd(const struct netc_private *priv, ++ enum netc_cmd cmd, ++ void *param, size_t param_len) ++{ ++ return netc_xfer_cmd(priv, SPI_WRITE, cmd, ++ param, param_len, ++ NULL, 0); ++} ++ ++int netc_xfer_get_cmd(const struct netc_private *priv, ++ enum netc_cmd cmd, uint32_t id, ++ void *resp, size_t resp_len) ++{ ++ struct netc_cmd_read_param param; ++ ++ param.id = id; ++ ++ return netc_xfer_cmd(priv, SPI_READ, cmd, ++ ¶m, sizeof(param), ++ resp, resp_len); ++} ++ ++int netc_xfer_write_reg(const struct netc_private *priv, ++ uint32_t reg, uint32_t value) ++{ ++ struct netc_cmd_reg_cmd reg_cmd; ++ ++ reg_cmd.reg = reg; ++ reg_cmd.value = value; ++ ++ return netc_xfer_set_cmd(priv, NETC_CMD_REG_SET, ++ ®_cmd, sizeof(reg_cmd)); ++} ++ ++int netc_xfer_read_reg(const struct netc_private *priv, ++ uint32_t reg, uint32_t *value) ++{ ++ return netc_xfer_get_cmd(priv, NETC_CMD_REG_GET, reg, ++ value, sizeof(*value)); ++} +diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c +index c8b45a131d77..f84020593ff2 100644 +--- a/drivers/net/dsa/ocelot/felix.c ++++ b/drivers/net/dsa/ocelot/felix.c +@@ -1025,17 +1025,49 @@ static int felix_vlan_add(struct dsa_switch *ds, int port, + if (err) + return err; + +- return ocelot_vlan_add(ocelot, port, vlan->vid, +- flags & BRIDGE_VLAN_INFO_PVID, +- flags & BRIDGE_VLAN_INFO_UNTAGGED); ++ err = ocelot_vlan_add(ocelot, port, vlan->vid, ++ flags & BRIDGE_VLAN_INFO_PVID, ++ flags & BRIDGE_VLAN_INFO_UNTAGGED); ++ if (err) ++ return err; ++ ++ if (vlan->proto == ETH_P_8021AD) { ++ if (!ocelot->qinq_enable) { ++ ocelot->qinq_enable = true; ++ kref_init(&ocelot->qinq_refcount); ++ } else { ++ kref_get(&ocelot->qinq_refcount); ++ } ++ } ++ ++ return 0; + } + ++static void felix_vlan_qinq_release(struct kref *ref) ++{ ++ struct ocelot *ocelot; ++ ++ ocelot = container_of(ref, struct ocelot, qinq_refcount); ++ ocelot->qinq_enable = false; ++} + static int felix_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) + { + struct ocelot *ocelot = ds->priv; ++ int err; + +- return ocelot_vlan_del(ocelot, port, vlan->vid); ++ err = ocelot_vlan_del(ocelot, port, vlan->vid); ++ if (err) { ++ dev_err(ds->dev, "Failed to remove VLAN %d from port %d: %d\n", ++ vlan->vid, port, err); ++ return err; ++ } ++ ++ if (ocelot->qinq_enable && vlan->proto == ETH_P_8021AD) ++ kref_put(&ocelot->qinq_refcount, ++ felix_vlan_qinq_release); ++ ++ return 0; + } + + static void felix_phylink_get_caps(struct dsa_switch *ds, int port, +@@ -1083,9 +1115,13 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, + phy_interface_t interface) + { + struct ocelot *ocelot = ds->priv; ++ struct felix *felix = ocelot_to_felix(ocelot); + + ocelot_phylink_mac_link_down(ocelot, port, link_an_mode, interface, + FELIX_MAC_QUIRKS); ++ ++ if (felix->info->port_preempt_reset) ++ felix->info->port_preempt_reset(ocelot, port, 0); + } + + static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, +@@ -1247,6 +1283,44 @@ static int felix_validate_phy_mode(struct felix *felix, int port, + return -EOPNOTSUPP; + } + ++static int felix_reset_preempt(struct dsa_switch *ds, int port, bool enable) ++{ ++ struct ocelot *ocelot = ds->priv; ++ struct felix *felix = ocelot_to_felix(ocelot); ++ ++ if (felix->info->port_preempt_reset) { ++ felix->info->port_preempt_reset(ocelot, port, enable); ++ ++ return 0; ++ } ++ ++ return -EOPNOTSUPP; ++} ++ ++static int felix_set_preempt(struct dsa_switch *ds, int port, ++ struct ethtool_fp *fpcmd) ++{ ++ struct ocelot *ocelot = ds->priv; ++ struct felix *felix = ocelot_to_felix(ocelot); ++ ++ if (felix->info->port_set_preempt) ++ return felix->info->port_set_preempt(ocelot, port, fpcmd); ++ ++ return -EOPNOTSUPP; ++} ++ ++static int felix_get_preempt(struct dsa_switch *ds, int port, ++ struct ethtool_fp *fpcmd) ++{ ++ struct ocelot *ocelot = ds->priv; ++ struct felix *felix = ocelot_to_felix(ocelot); ++ ++ if (felix->info->port_get_preempt) ++ return felix->info->port_get_preempt(ocelot, port, fpcmd); ++ ++ return -EOPNOTSUPP; ++} ++ + static int felix_parse_ports_node(struct felix *felix, + struct device_node *ports_node, + phy_interface_t *port_phy_modes) +@@ -1549,6 +1623,97 @@ static int felix_connect_tag_protocol(struct dsa_switch *ds, + } + } + ++static int felix_qinq_port_bitmap_get(struct dsa_switch *ds, u32 *bitmap) ++{ ++ struct ocelot *ocelot = ds->priv; ++ struct ocelot_port *ocelot_port; ++ int port; ++ ++ *bitmap = 0; ++ for (port = 0; port < ds->num_ports; port++) { ++ ocelot_port = ocelot->ports[port]; ++ if (ocelot_port->qinq_mode) ++ *bitmap |= 0x01 << port; ++ } ++ ++ return 0; ++} ++ ++static int felix_qinq_port_bitmap_set(struct dsa_switch *ds, u32 bitmap) ++{ ++ struct ocelot *ocelot = ds->priv; ++ struct ocelot_port *ocelot_port; ++ int port; ++ ++ for (port = 0; port < ds->num_ports; port++) { ++ ocelot_port = ocelot->ports[port]; ++ if (bitmap & (0x01 << port)) ++ ocelot_port->qinq_mode = true; ++ else ++ ocelot_port->qinq_mode = false; ++ } ++ ++ return 0; ++} ++ ++enum felix_devlink_param_id { ++ FELIX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, ++ FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP, ++}; ++ ++static int felix_devlink_param_get(struct dsa_switch *ds, u32 id, ++ struct devlink_param_gset_ctx *ctx) ++{ ++ int err; ++ ++ switch (id) { ++ case FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP: ++ err = felix_qinq_port_bitmap_get(ds, &ctx->val.vu32); ++ break; ++ default: ++ err = -EOPNOTSUPP; ++ break; ++ } ++ ++ return err; ++} ++ ++static int felix_devlink_param_set(struct dsa_switch *ds, u32 id, ++ struct devlink_param_gset_ctx *ctx) ++{ ++ int err; ++ ++ switch (id) { ++ case FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP: ++ err = felix_qinq_port_bitmap_set(ds, ctx->val.vu32); ++ break; ++ default: ++ err = -EOPNOTSUPP; ++ break; ++ } ++ ++ return err; ++} ++ ++static const struct devlink_param felix_devlink_params[] = { ++ DSA_DEVLINK_PARAM_DRIVER(FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP, ++ "qinq_port_bitmap", ++ DEVLINK_PARAM_TYPE_U32, ++ BIT(DEVLINK_PARAM_CMODE_RUNTIME)), ++}; ++ ++static int felix_setup_devlink_params(struct dsa_switch *ds) ++{ ++ return dsa_devlink_params_register(ds, felix_devlink_params, ++ ARRAY_SIZE(felix_devlink_params)); ++} ++ ++static void felix_teardown_devlink_params(struct dsa_switch *ds) ++{ ++ dsa_devlink_params_unregister(ds, felix_devlink_params, ++ ARRAY_SIZE(felix_devlink_params)); ++} ++ + /* Hardware initialization done here so that we can allocate structures with + * devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing + * us to allocate structures twice (leak memory) and map PCI memory twice +@@ -1601,6 +1766,10 @@ static int felix_setup(struct dsa_switch *ds) + ds->fdb_isolation = true; + ds->max_num_bridges = ds->num_ports; + ++ err = felix_setup_devlink_params(ds); ++ if (err < 0) ++ return err; ++ + return 0; + + out_deinit_ports: +@@ -1628,6 +1797,8 @@ static void felix_teardown(struct dsa_switch *ds) + felix->tag_proto_ops->teardown(ds); + rtnl_unlock(); + ++ felix_teardown_devlink_params(ds); ++ + dsa_switch_for_each_available_port(dp, ds) + ocelot_deinit_port(ocelot, dp->index); + +@@ -2080,6 +2251,9 @@ const struct dsa_switch_ops felix_switch_ops = { + .get_ethtool_stats = felix_get_ethtool_stats, + .get_sset_count = felix_get_sset_count, + .get_ts_info = felix_get_ts_info, ++ .reset_preempt = felix_reset_preempt, ++ .set_preempt = felix_set_preempt, ++ .get_preempt = felix_get_preempt, + .phylink_get_caps = felix_phylink_get_caps, + .phylink_validate = felix_phylink_validate, + .phylink_mac_select_pcs = felix_phylink_mac_select_pcs, +@@ -2135,6 +2309,8 @@ const struct dsa_switch_ops felix_switch_ops = { + .port_mrp_del_ring_role = felix_mrp_del_ring_role, + .tag_8021q_vlan_add = felix_tag_8021q_vlan_add, + .tag_8021q_vlan_del = felix_tag_8021q_vlan_del, ++ .devlink_param_get = felix_devlink_param_get, ++ .devlink_param_set = felix_devlink_param_set, + .port_get_default_prio = felix_port_get_default_prio, + .port_set_default_prio = felix_port_set_default_prio, + .port_get_dscp_prio = felix_port_get_dscp_prio, +diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h +index 0e57f9d518be..72bb1b687237 100644 +--- a/drivers/net/dsa/ocelot/felix.h ++++ b/drivers/net/dsa/ocelot/felix.h +@@ -61,6 +61,11 @@ struct felix_info { + void (*tas_guard_bands_update)(struct ocelot *ocelot, int port); + void (*port_sched_speed_set)(struct ocelot *ocelot, int port, + int speed); ++ int (*port_set_preempt)(struct ocelot *ocelot, int port, ++ struct ethtool_fp *fpcmd); ++ int (*port_get_preempt)(struct ocelot *ocelot, int port, ++ struct ethtool_fp *fpcmd); ++ void (*port_preempt_reset)(struct ocelot *ocelot, int port, bool enable); + }; + + /* Methods for initializing the hardware resources specific to a tagging +diff --git a/drivers/net/dsa/ocelot/felix_tsn.c b/drivers/net/dsa/ocelot/felix_tsn.c +index 302e21c7fddf..fd0ea4cd7d7b 100644 +--- a/drivers/net/dsa/ocelot/felix_tsn.c ++++ b/drivers/net/dsa/ocelot/felix_tsn.c +@@ -498,10 +498,12 @@ static int felix_cb_streamid_set(struct net_device *ndev, u32 index, bool enable + if (!stream) + return -EINVAL; + ++ dst_idx = stream->dst_idx; + ocelot_mact_forget(ocelot, stream->mac, stream->vid); ++ + felix_stream_table_del(index); + +- felix_streamid_force_forward_clear(ocelot, stream->dst_idx); ++ felix_streamid_force_forward_clear(ocelot, dst_idx); + + return 0; + } +@@ -1536,7 +1538,7 @@ static int felix_pcpmap_set(struct net_device *ndev, + ocelot = dp->ds->priv; + port = dp->index; + +- index = (c->pcp & GENMASK(2, 0)) * ((c->dei & BIT(0)) + 1); ++ index = (c->dei & BIT(0)) * 8 + (c->pcp & GENMASK(2, 0)); + + ocelot_rmw_ix(ocelot, + (ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL & (c->dpl << 3)) | +diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c +index 871a3f68b3ef..18d51bda4052 100644 +--- a/drivers/net/dsa/ocelot/felix_vsc9959.c ++++ b/drivers/net/dsa/ocelot/felix_vsc9959.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2663,6 +2664,103 @@ static const struct ocelot_ops vsc9959_ops = { + .update_stats = vsc9959_update_stats, + }; + ++static void vsc9959_port_preempt_reset(struct ocelot *ocelot, int port, bool enable) ++{ ++ struct ocelot_port *ocelot_port = ocelot->ports[port]; ++ ++ ocelot_port_rmwl(ocelot_port, 0, ++ DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA | ++ DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA, ++ DEV_MM_ENABLE_CONFIG); ++ ++ if (enable) { ++ if (ocelot_port->fp_enabled_admin) { ++ ocelot_port_rmwl(ocelot_port, ++ DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA | ++ DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA, ++ DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA | ++ DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA, ++ DEV_MM_ENABLE_CONFIG); ++ } ++ } ++} ++ ++static int vsc9959_port_set_preempt(struct ocelot *ocelot, int port, ++ struct ethtool_fp *fpcmd) ++{ ++ struct ocelot_port *ocelot_port = ocelot->ports[port]; ++ struct felix *felix = ocelot_to_felix(ocelot); ++ int p_queues = fpcmd->preemptible_queues_mask; ++ int mm_fragsize, val; ++ ++ if (!fpcmd->disabled && ++ (fpcmd->min_frag_size < 60 || fpcmd->min_frag_size > 252)) ++ return -EINVAL; ++ ++ mm_fragsize = DIV_ROUND_UP((fpcmd->min_frag_size + 4), 64) - 1; ++ ++ if (!fpcmd->disabled) { ++ val = DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA | ++ DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA; ++ ocelot_port->fp_enabled_admin = 1; ++ } else { ++ val = 0; ++ ocelot_port->fp_enabled_admin = 0; ++ } ++ ++ ocelot_port_rmwl(ocelot_port, val, ++ DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA | ++ DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA, ++ DEV_MM_ENABLE_CONFIG); ++ ++ ocelot_port_rmwl(ocelot_port, ++ (fpcmd->fp_enabled ? ++ 0 : DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS), ++ DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS, ++ DEV_MM_VERIF_CONFIG); ++ ++ ocelot_rmw_rix(ocelot, ++ QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(mm_fragsize) | ++ QSYS_PREEMPTION_CFG_P_QUEUES(p_queues), ++ QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M | ++ QSYS_PREEMPTION_CFG_P_QUEUES_M, ++ QSYS_PREEMPTION_CFG, ++ port); ++ ++ ocelot_port->preemptable_prios = p_queues; ++ ++ if (ocelot_port->taprio && felix->info->tas_guard_bands_update) ++ felix->info->tas_guard_bands_update(ocelot, port); ++ ++ return 0; ++} ++ ++static int vsc9959_port_get_preempt(struct ocelot *ocelot, int port, ++ struct ethtool_fp *fpcmd) ++{ ++ struct ocelot_port *ocelot_port = ocelot->ports[port]; ++ u8 fragsize; ++ u32 val; ++ ++ fpcmd->fp_supported = 1; ++ fpcmd->supported_queues_mask = GENMASK(7, 0); ++ ++ val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS); ++ val &= DEV_MM_STATISTICS_MM_STATUS_PRMPT_ACTIVE_STATUS; ++ fpcmd->fp_active = (val ? 1 : 0); ++ ++ val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG); ++ val &= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA; ++ fpcmd->fp_status = val; ++ ++ val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port); ++ fpcmd->preemptible_queues_mask = val & QSYS_PREEMPTION_CFG_P_QUEUES_M; ++ fragsize = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val); ++ fpcmd->min_frag_size = (fragsize + 1) * 64 - 4; ++ ++ return 0; ++} ++ + static const struct felix_info felix_info_vsc9959 = { + .resources = vsc9959_resources, + .num_resources = ARRAY_SIZE(vsc9959_resources), +@@ -2688,6 +2786,9 @@ static const struct felix_info felix_info_vsc9959 = { + .port_setup_tc = vsc9959_port_setup_tc, + .port_sched_speed_set = vsc9959_sched_speed_set, + .tas_guard_bands_update = vsc9959_tas_guard_bands_update, ++ .port_set_preempt = vsc9959_port_set_preempt, ++ .port_get_preempt = vsc9959_port_get_preempt, ++ .port_preempt_reset = vsc9959_port_preempt_reset, + }; + + static irqreturn_t felix_irq_handler(int irq, void *data) +diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h +index 06e0ebfe652a..0e52b6ceff78 100644 +--- a/drivers/net/dsa/sja1105/sja1105.h ++++ b/drivers/net/dsa/sja1105/sja1105.h +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include "sja1105_static_config.h" + +@@ -253,6 +254,7 @@ struct sja1105_private { + unsigned long bcast_egress_floods; + unsigned long hwts_tx_en; + unsigned long hwts_rx_en; ++ u32 hostprio; + const struct sja1105_info *info; + size_t max_xfer_len; + struct spi_device *spidev; +@@ -296,6 +298,7 @@ enum sja1105_reset_reason { + SJA1105_SCHEDULING, + SJA1105_BEST_EFFORT_POLICING, + SJA1105_VIRTUAL_LINKS, ++ SJA1105_VLAN_PCP_TO_TXQ_MAPPING, + }; + + int sja1105_static_config_reload(struct sja1105_private *priv, +@@ -303,6 +306,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv, + int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, + struct netlink_ext_ack *extack); + void sja1105_frame_memory_partitioning(struct sja1105_private *priv); ++int sja1105_setup_tc_mqprio(struct dsa_switch *ds, int port, ++ struct tc_mqprio_qopt_offload *mqprio); + + /* From sja1105_mdio.c */ + int sja1105_mdiobus_register(struct dsa_switch *ds); +diff --git a/drivers/net/dsa/sja1105/sja1105_clocking.c b/drivers/net/dsa/sja1105/sja1105_clocking.c +index e3699f76f6d7..08a3e7b96254 100644 +--- a/drivers/net/dsa/sja1105/sja1105_clocking.c ++++ b/drivers/net/dsa/sja1105/sja1105_clocking.c +@@ -153,14 +153,14 @@ static int sja1105_cgu_mii_tx_clk_config(struct sja1105_private *priv, + { + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cgu_mii_ctrl mii_tx_clk; +- const int mac_clk_sources[] = { ++ static const int mac_clk_sources[] = { + CLKSRC_MII0_TX_CLK, + CLKSRC_MII1_TX_CLK, + CLKSRC_MII2_TX_CLK, + CLKSRC_MII3_TX_CLK, + CLKSRC_MII4_TX_CLK, + }; +- const int phy_clk_sources[] = { ++ static const int phy_clk_sources[] = { + CLKSRC_IDIV0, + CLKSRC_IDIV1, + CLKSRC_IDIV2, +@@ -194,7 +194,7 @@ sja1105_cgu_mii_rx_clk_config(struct sja1105_private *priv, int port) + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cgu_mii_ctrl mii_rx_clk; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; +- const int clk_sources[] = { ++ static const int clk_sources[] = { + CLKSRC_MII0_RX_CLK, + CLKSRC_MII1_RX_CLK, + CLKSRC_MII2_RX_CLK, +@@ -221,7 +221,7 @@ sja1105_cgu_mii_ext_tx_clk_config(struct sja1105_private *priv, int port) + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cgu_mii_ctrl mii_ext_tx_clk; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; +- const int clk_sources[] = { ++ static const int clk_sources[] = { + CLKSRC_IDIV0, + CLKSRC_IDIV1, + CLKSRC_IDIV2, +@@ -248,7 +248,7 @@ sja1105_cgu_mii_ext_rx_clk_config(struct sja1105_private *priv, int port) + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cgu_mii_ctrl mii_ext_rx_clk; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; +- const int clk_sources[] = { ++ static const int clk_sources[] = { + CLKSRC_IDIV0, + CLKSRC_IDIV1, + CLKSRC_IDIV2, +@@ -349,8 +349,13 @@ static int sja1105_cgu_rgmii_tx_clk_config(struct sja1105_private *priv, + if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS]) { + clksrc = CLKSRC_PLL0; + } else { +- int clk_sources[] = {CLKSRC_IDIV0, CLKSRC_IDIV1, CLKSRC_IDIV2, +- CLKSRC_IDIV3, CLKSRC_IDIV4}; ++ static const int clk_sources[] = { ++ CLKSRC_IDIV0, ++ CLKSRC_IDIV1, ++ CLKSRC_IDIV2, ++ CLKSRC_IDIV3, ++ CLKSRC_IDIV4, ++ }; + clksrc = clk_sources[port]; + } + +@@ -638,7 +643,7 @@ static int sja1105_cgu_rmii_ref_clk_config(struct sja1105_private *priv, + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cgu_mii_ctrl ref_clk; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; +- const int clk_sources[] = { ++ static const int clk_sources[] = { + CLKSRC_MII0_TX_CLK, + CLKSRC_MII1_TX_CLK, + CLKSRC_MII2_TX_CLK, +diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c +index f1f1368e8146..f8b220ed97ff 100644 +--- a/drivers/net/dsa/sja1105/sja1105_main.c ++++ b/drivers/net/dsa/sja1105/sja1105_main.c +@@ -863,7 +863,7 @@ static int sja1105_init_general_params(struct sja1105_private *priv) + /* Priority queue for link-local management frames + * (both ingress to and egress from CPU - PTP, STP etc) + */ +- .hostprio = 7, ++ .hostprio = priv->hostprio, + .mac_fltres1 = SJA1105_LINKLOCAL_FILTER_A, + .mac_flt1 = SJA1105_LINKLOCAL_FILTER_A_MASK, + .incl_srcpt1 = true, +@@ -1256,6 +1256,15 @@ static int sja1105_parse_dt(struct sja1105_private *priv) + return -ENODEV; + } + ++ if (of_property_read_u32(switch_node, "hostprio", &priv->hostprio) < 0) { ++ priv->hostprio = 7; ++ } else if (priv->hostprio >= SJA1105_NUM_TC) { ++ dev_err(dev, "Out of range hostprio, must be between 0 and %d\n", (SJA1105_NUM_TC - 1)); ++ return -ERANGE; ++ } ++ ++ dev_info(dev, "Configured hostprio: using queue %u\n", priv->hostprio); ++ + rc = sja1105_parse_ports_node(priv, ports_node); + of_node_put(ports_node); + +@@ -2282,6 +2291,7 @@ static const char * const sja1105_reset_reasons[] = { + [SJA1105_SCHEDULING] = "Time-aware scheduling", + [SJA1105_BEST_EFFORT_POLICING] = "Best-effort policing", + [SJA1105_VIRTUAL_LINKS] = "Virtual links", ++ SJA1105_VLAN_PCP_TO_TXQ_MAPPING] = "VLAN PCP to TX queue mapping", + }; + + /* For situations where we need to change a setting at runtime that is only +@@ -2433,6 +2443,56 @@ sja1105_get_tag_protocol(struct dsa_switch *ds, int port, + return priv->info->tag_proto; + } + ++int sja1105_setup_tc_mqprio(struct dsa_switch *ds, int port, ++ struct tc_mqprio_qopt_offload *mqprio) ++{ ++ struct sja1105_l2_forwarding_entry *l2fwd; ++ struct sja1105_private *priv = ds->priv; ++ struct sja1105_table *table; ++ int pcp, tc; ++ ++ if (mqprio->qopt.num_tc > SJA1105_MAX_NUM_PCP) { ++ dev_err(ds->dev, ++ "Only a maximum of %u traffic classes are supported by hardware\n", ++ SJA1105_MAX_NUM_PCP); ++ return -ERANGE; ++ } ++ ++ table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING]; ++ ++ l2fwd = table->entries; ++ ++ if (!mqprio->qopt.num_tc) { ++ /* Delete qdisc: reset to default 1:1 mapping. */ ++ for (pcp = 0; pcp < SJA1105_MAX_NUM_PCP; pcp++) ++ l2fwd[ds->num_ports + pcp].vlan_pmap[port] = pcp; ++ } else { ++ /* We restrict a single TXQ per traffic class ++ * The SJA1105 doesn't offer round robin among TXQs of the same priority ++ */ ++ for (tc = 0; tc < mqprio->qopt.num_tc; tc++) { ++ if (mqprio->qopt.count[tc] != 1) { ++ dev_err(ds->dev, ++ "Only a single TXQ per traffic class is supported\n"); ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ /* Use MQPRIO mapping to configure Egress PCP to HW queue mapping. */ ++ for (pcp = 0; pcp < SJA1105_MAX_NUM_PCP; pcp++) ++ l2fwd[ds->num_ports + pcp].vlan_pmap[port] = mqprio->qopt.prio_tc_map[pcp]; ++ } ++ ++ /* Although, the Egress PCP to HW queue mapping (the latter 8 entries) ++ * should be configured dynamically (once max_dynp is properly set: e.g 7), ++ * that did not work in practice. The switch (SJA1105Q) kept using ++ * the old mapping until a switch reset appears and force the reload of ++ * the static configuration. ++ * So, force a switch reset on mapping offload from here. ++ */ ++ return sja1105_static_config_reload(priv, SJA1105_VLAN_PCP_TO_TXQ_MAPPING); ++} ++ + /* The TPID setting belongs to the General Parameters table, + * which can only be partially reconfigured at runtime (and not the TPID). + * So a switch reset is required. +@@ -2834,6 +2894,8 @@ static int sja1105_port_setup_tc(struct dsa_switch *ds, int port, + return sja1105_setup_tc_taprio(ds, port, type_data); + case TC_SETUP_QDISC_CBS: + return sja1105_setup_tc_cbs(ds, port, type_data); ++ case TC_SETUP_QDISC_MQPRIO: ++ return sja1105_setup_tc_mqprio(ds, port, type_data); + default: + return -EOPNOTSUPP; + } +diff --git a/drivers/net/dsa/sja1105/sja1105_tas.c b/drivers/net/dsa/sja1105/sja1105_tas.c +index e6153848a950..20a06266134e 100644 +--- a/drivers/net/dsa/sja1105/sja1105_tas.c ++++ b/drivers/net/dsa/sja1105/sja1105_tas.c +@@ -527,6 +527,8 @@ int sja1105_setup_tc_taprio(struct dsa_switch *ds, int port, + if (rc < 0) + return rc; + ++ sja1105_setup_tc_mqprio(ds, port, &admin->mqprio); ++ + return sja1105_static_config_reload(priv, SJA1105_SCHEDULING); + } + +@@ -575,6 +577,8 @@ int sja1105_setup_tc_taprio(struct dsa_switch *ds, int port, + if (rc < 0) + return rc; + ++ sja1105_setup_tc_mqprio(ds, port, &admin->mqprio); ++ + return sja1105_static_config_reload(priv, SJA1105_SCHEDULING); + } + +diff --git a/drivers/net/ethernet/alacritech/slic.h b/drivers/net/ethernet/alacritech/slic.h +index 4eecbdfff3ff..82071d0e5f7f 100644 +--- a/drivers/net/ethernet/alacritech/slic.h ++++ b/drivers/net/ethernet/alacritech/slic.h +@@ -288,13 +288,13 @@ do { \ + u64_stats_update_end(&(st)->syncp); \ + } while (0) + +-#define SLIC_GET_STATS_COUNTER(newst, st, counter) \ +-{ \ +- unsigned int start; \ ++#define SLIC_GET_STATS_COUNTER(newst, st, counter) \ ++{ \ ++ unsigned int start; \ + do { \ +- start = u64_stats_fetch_begin_irq(&(st)->syncp); \ +- newst = (st)->counter; \ +- } while (u64_stats_fetch_retry_irq(&(st)->syncp, start)); \ ++ start = u64_stats_fetch_begin(&(st)->syncp); \ ++ newst = (st)->counter; \ ++ } while (u64_stats_fetch_retry(&(st)->syncp, start)); \ + } + + struct slic_upr { +diff --git a/drivers/net/ethernet/amazon/ena/ena_ethtool.c b/drivers/net/ethernet/amazon/ena/ena_ethtool.c +index 444ccef76da2..8da79eedc057 100644 +--- a/drivers/net/ethernet/amazon/ena/ena_ethtool.c ++++ b/drivers/net/ethernet/amazon/ena/ena_ethtool.c +@@ -118,9 +118,9 @@ static void ena_safe_update_stat(u64 *src, u64 *dst, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(syncp); ++ start = u64_stats_fetch_begin(syncp); + *(dst) = *src; +- } while (u64_stats_fetch_retry_irq(syncp, start)); ++ } while (u64_stats_fetch_retry(syncp, start)); + } + + static void ena_queue_stats(struct ena_adapter *adapter, u64 **data) +diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c +index 5ce01ac72637..e8ad5ea31aff 100644 +--- a/drivers/net/ethernet/amazon/ena/ena_netdev.c ++++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c +@@ -3305,10 +3305,10 @@ static void ena_get_stats64(struct net_device *netdev, + tx_ring = &adapter->tx_ring[i]; + + do { +- start = u64_stats_fetch_begin_irq(&tx_ring->syncp); ++ start = u64_stats_fetch_begin(&tx_ring->syncp); + packets = tx_ring->tx_stats.cnt; + bytes = tx_ring->tx_stats.bytes; +- } while (u64_stats_fetch_retry_irq(&tx_ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&tx_ring->syncp, start)); + + stats->tx_packets += packets; + stats->tx_bytes += bytes; +@@ -3316,20 +3316,20 @@ static void ena_get_stats64(struct net_device *netdev, + rx_ring = &adapter->rx_ring[i]; + + do { +- start = u64_stats_fetch_begin_irq(&rx_ring->syncp); ++ start = u64_stats_fetch_begin(&rx_ring->syncp); + packets = rx_ring->rx_stats.cnt; + bytes = rx_ring->rx_stats.bytes; +- } while (u64_stats_fetch_retry_irq(&rx_ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&rx_ring->syncp, start)); + + stats->rx_packets += packets; + stats->rx_bytes += bytes; + } + + do { +- start = u64_stats_fetch_begin_irq(&adapter->syncp); ++ start = u64_stats_fetch_begin(&adapter->syncp); + rx_drops = adapter->dev_stats.rx_drops; + tx_drops = adapter->dev_stats.tx_drops; +- } while (u64_stats_fetch_retry_irq(&adapter->syncp, start)); ++ } while (u64_stats_fetch_retry(&adapter->syncp, start)); + + stats->rx_dropped = rx_drops; + stats->tx_dropped = tx_drops; +diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_main.c b/drivers/net/ethernet/aquantia/atlantic/aq_main.c +index 77609dc0a08d..0b2a52199914 100644 +--- a/drivers/net/ethernet/aquantia/atlantic/aq_main.c ++++ b/drivers/net/ethernet/aquantia/atlantic/aq_main.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + + MODULE_LICENSE("GPL v2"); +diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +index 2dc8d215a591..7f933175cbda 100644 +--- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c ++++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +@@ -948,7 +948,7 @@ unsigned int aq_ring_fill_stats_data(struct aq_ring_s *self, u64 *data) + /* This data should mimic aq_ethtool_queue_rx_stat_names structure */ + do { + count = 0; +- start = u64_stats_fetch_begin_irq(&self->stats.rx.syncp); ++ start = u64_stats_fetch_begin(&self->stats.rx.syncp); + data[count] = self->stats.rx.packets; + data[++count] = self->stats.rx.jumbo_packets; + data[++count] = self->stats.rx.lro_packets; +@@ -965,15 +965,15 @@ unsigned int aq_ring_fill_stats_data(struct aq_ring_s *self, u64 *data) + data[++count] = self->stats.rx.xdp_tx; + data[++count] = self->stats.rx.xdp_invalid; + data[++count] = self->stats.rx.xdp_redirect; +- } while (u64_stats_fetch_retry_irq(&self->stats.rx.syncp, start)); ++ } while (u64_stats_fetch_retry(&self->stats.rx.syncp, start)); + } else { + /* This data should mimic aq_ethtool_queue_tx_stat_names structure */ + do { + count = 0; +- start = u64_stats_fetch_begin_irq(&self->stats.tx.syncp); ++ start = u64_stats_fetch_begin(&self->stats.tx.syncp); + data[count] = self->stats.tx.packets; + data[++count] = self->stats.tx.queue_restarts; +- } while (u64_stats_fetch_retry_irq(&self->stats.tx.syncp, start)); ++ } while (u64_stats_fetch_retry(&self->stats.tx.syncp, start)); + } + + return ++count; +diff --git a/drivers/net/ethernet/asix/ax88796c_main.c b/drivers/net/ethernet/asix/ax88796c_main.c +index 8b7cdf015a16..21376c79f671 100644 +--- a/drivers/net/ethernet/asix/ax88796c_main.c ++++ b/drivers/net/ethernet/asix/ax88796c_main.c +@@ -662,12 +662,12 @@ static void ax88796c_get_stats64(struct net_device *ndev, + s = per_cpu_ptr(ax_local->stats, cpu); + + do { +- start = u64_stats_fetch_begin_irq(&s->syncp); ++ start = u64_stats_fetch_begin(&s->syncp); + rx_packets = u64_stats_read(&s->rx_packets); + rx_bytes = u64_stats_read(&s->rx_bytes); + tx_packets = u64_stats_read(&s->tx_packets); + tx_bytes = u64_stats_read(&s->tx_bytes); +- } while (u64_stats_fetch_retry_irq(&s->syncp, start)); ++ } while (u64_stats_fetch_retry(&s->syncp, start)); + + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; +diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c +index 7f876721596c..b751dc8486dc 100644 +--- a/drivers/net/ethernet/broadcom/b44.c ++++ b/drivers/net/ethernet/broadcom/b44.c +@@ -1680,7 +1680,7 @@ static void b44_get_stats64(struct net_device *dev, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&hwstat->syncp); ++ start = u64_stats_fetch_begin(&hwstat->syncp); + + /* Convert HW stats into rtnl_link_stats64 stats. */ + nstat->rx_packets = hwstat->rx_pkts; +@@ -1714,7 +1714,7 @@ static void b44_get_stats64(struct net_device *dev, + /* Carrier lost counter seems to be broken for some devices */ + nstat->tx_carrier_errors = hwstat->tx_carrier_lost; + #endif +- } while (u64_stats_fetch_retry_irq(&hwstat->syncp, start)); ++ } while (u64_stats_fetch_retry(&hwstat->syncp, start)); + + } + +@@ -2082,12 +2082,12 @@ static void b44_get_ethtool_stats(struct net_device *dev, + do { + data_src = &hwstat->tx_good_octets; + data_dst = data; +- start = u64_stats_fetch_begin_irq(&hwstat->syncp); ++ start = u64_stats_fetch_begin(&hwstat->syncp); + + for (i = 0; i < ARRAY_SIZE(b44_gstrings); i++) + *data_dst++ = *data_src++; + +- } while (u64_stats_fetch_retry_irq(&hwstat->syncp, start)); ++ } while (u64_stats_fetch_retry(&hwstat->syncp, start)); + } + + static void b44_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c +index 425d6ccd5413..f8b1adc389b3 100644 +--- a/drivers/net/ethernet/broadcom/bcmsysport.c ++++ b/drivers/net/ethernet/broadcom/bcmsysport.c +@@ -457,10 +457,10 @@ static void bcm_sysport_update_tx_stats(struct bcm_sysport_priv *priv, + for (q = 0; q < priv->netdev->num_tx_queues; q++) { + ring = &priv->tx_rings[q]; + do { +- start = u64_stats_fetch_begin_irq(&priv->syncp); ++ start = u64_stats_fetch_begin(&priv->syncp); + bytes = ring->bytes; + packets = ring->packets; +- } while (u64_stats_fetch_retry_irq(&priv->syncp, start)); ++ } while (u64_stats_fetch_retry(&priv->syncp, start)); + + *tx_bytes += bytes; + *tx_packets += packets; +@@ -504,9 +504,9 @@ static void bcm_sysport_get_stats(struct net_device *dev, + if (s->stat_sizeof == sizeof(u64) && + s->type == BCM_SYSPORT_STAT_NETDEV64) { + do { +- start = u64_stats_fetch_begin_irq(syncp); ++ start = u64_stats_fetch_begin(syncp); + data[i] = *(u64 *)p; +- } while (u64_stats_fetch_retry_irq(syncp, start)); ++ } while (u64_stats_fetch_retry(syncp, start)); + } else + data[i] = *(u32 *)p; + j++; +@@ -1878,10 +1878,10 @@ static void bcm_sysport_get_stats64(struct net_device *dev, + &stats->tx_packets); + + do { +- start = u64_stats_fetch_begin_irq(&priv->syncp); ++ start = u64_stats_fetch_begin(&priv->syncp); + stats->rx_packets = stats64->rx_packets; + stats->rx_bytes = stats64->rx_bytes; +- } while (u64_stats_fetch_retry_irq(&priv->syncp, start)); ++ } while (u64_stats_fetch_retry(&priv->syncp, start)); + } + + static void bcm_sysport_netif_start(struct net_device *dev) +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.h +index be96f1dc0372..d4a862a9fd7d 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.h +@@ -4,7 +4,7 @@ + #ifndef __CXGB4_TC_MQPRIO_H__ + #define __CXGB4_TC_MQPRIO_H__ + +-#include ++#include + + #define CXGB4_EOSW_TXQ_DEFAULT_DESC_NUM 128 + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index fdf10318758b..5715b9ab2712 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -1919,7 +1919,7 @@ static void gmac_get_stats64(struct net_device *netdev, + + /* Racing with RX NAPI */ + do { +- start = u64_stats_fetch_begin_irq(&port->rx_stats_syncp); ++ start = u64_stats_fetch_begin(&port->rx_stats_syncp); + + stats->rx_packets = port->stats.rx_packets; + stats->rx_bytes = port->stats.rx_bytes; +@@ -1931,11 +1931,11 @@ static void gmac_get_stats64(struct net_device *netdev, + stats->rx_crc_errors = port->stats.rx_crc_errors; + stats->rx_frame_errors = port->stats.rx_frame_errors; + +- } while (u64_stats_fetch_retry_irq(&port->rx_stats_syncp, start)); ++ } while (u64_stats_fetch_retry(&port->rx_stats_syncp, start)); + + /* Racing with MIB and TX completion interrupts */ + do { +- start = u64_stats_fetch_begin_irq(&port->ir_stats_syncp); ++ start = u64_stats_fetch_begin(&port->ir_stats_syncp); + + stats->tx_errors = port->stats.tx_errors; + stats->tx_packets = port->stats.tx_packets; +@@ -1945,15 +1945,15 @@ static void gmac_get_stats64(struct net_device *netdev, + stats->rx_missed_errors = port->stats.rx_missed_errors; + stats->rx_fifo_errors = port->stats.rx_fifo_errors; + +- } while (u64_stats_fetch_retry_irq(&port->ir_stats_syncp, start)); ++ } while (u64_stats_fetch_retry(&port->ir_stats_syncp, start)); + + /* Racing with hard_start_xmit */ + do { +- start = u64_stats_fetch_begin_irq(&port->tx_stats_syncp); ++ start = u64_stats_fetch_begin(&port->tx_stats_syncp); + + stats->tx_dropped = port->stats.tx_dropped; + +- } while (u64_stats_fetch_retry_irq(&port->tx_stats_syncp, start)); ++ } while (u64_stats_fetch_retry(&port->tx_stats_syncp, start)); + + stats->rx_dropped += stats->rx_missed_errors; + } +@@ -2031,18 +2031,18 @@ static void gmac_get_ethtool_stats(struct net_device *netdev, + /* Racing with MIB interrupt */ + do { + p = values; +- start = u64_stats_fetch_begin_irq(&port->ir_stats_syncp); ++ start = u64_stats_fetch_begin(&port->ir_stats_syncp); + + for (i = 0; i < RX_STATS_NUM; i++) + *p++ = port->hw_stats[i]; + +- } while (u64_stats_fetch_retry_irq(&port->ir_stats_syncp, start)); ++ } while (u64_stats_fetch_retry(&port->ir_stats_syncp, start)); + values = p; + + /* Racing with RX NAPI */ + do { + p = values; +- start = u64_stats_fetch_begin_irq(&port->rx_stats_syncp); ++ start = u64_stats_fetch_begin(&port->rx_stats_syncp); + + for (i = 0; i < RX_STATUS_NUM; i++) + *p++ = port->rx_stats[i]; +@@ -2050,13 +2050,13 @@ static void gmac_get_ethtool_stats(struct net_device *netdev, + *p++ = port->rx_csum_stats[i]; + *p++ = port->rx_napi_exits; + +- } while (u64_stats_fetch_retry_irq(&port->rx_stats_syncp, start)); ++ } while (u64_stats_fetch_retry(&port->rx_stats_syncp, start)); + values = p; + + /* Racing with TX start_xmit */ + do { + p = values; +- start = u64_stats_fetch_begin_irq(&port->tx_stats_syncp); ++ start = u64_stats_fetch_begin(&port->tx_stats_syncp); + + for (i = 0; i < TX_MAX_FRAGS; i++) { + *values++ = port->tx_frag_stats[i]; +@@ -2065,7 +2065,7 @@ static void gmac_get_ethtool_stats(struct net_device *netdev, + *values++ = port->tx_frags_linearized; + *values++ = port->tx_hw_csummed; + +- } while (u64_stats_fetch_retry_irq(&port->tx_stats_syncp, start)); ++ } while (u64_stats_fetch_retry(&port->tx_stats_syncp, start)); + } + + static int gmac_get_ksettings(struct net_device *netdev, +diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c +index 77edc3d9b505..a29de29bdf23 100644 +--- a/drivers/net/ethernet/emulex/benet/be_ethtool.c ++++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c +@@ -389,10 +389,10 @@ static void be_get_ethtool_stats(struct net_device *netdev, + struct be_rx_stats *stats = rx_stats(rxo); + + do { +- start = u64_stats_fetch_begin_irq(&stats->sync); ++ start = u64_stats_fetch_begin(&stats->sync); + data[base] = stats->rx_bytes; + data[base + 1] = stats->rx_pkts; +- } while (u64_stats_fetch_retry_irq(&stats->sync, start)); ++ } while (u64_stats_fetch_retry(&stats->sync, start)); + + for (i = 2; i < ETHTOOL_RXSTATS_NUM; i++) { + p = (u8 *)stats + et_rx_stats[i].offset; +@@ -405,19 +405,19 @@ static void be_get_ethtool_stats(struct net_device *netdev, + struct be_tx_stats *stats = tx_stats(txo); + + do { +- start = u64_stats_fetch_begin_irq(&stats->sync_compl); ++ start = u64_stats_fetch_begin(&stats->sync_compl); + data[base] = stats->tx_compl; +- } while (u64_stats_fetch_retry_irq(&stats->sync_compl, start)); ++ } while (u64_stats_fetch_retry(&stats->sync_compl, start)); + + do { +- start = u64_stats_fetch_begin_irq(&stats->sync); ++ start = u64_stats_fetch_begin(&stats->sync); + for (i = 1; i < ETHTOOL_TXSTATS_NUM; i++) { + p = (u8 *)stats + et_tx_stats[i].offset; + data[base + i] = + (et_tx_stats[i].size == sizeof(u64)) ? + *(u64 *)p : *(u32 *)p; + } +- } while (u64_stats_fetch_retry_irq(&stats->sync, start)); ++ } while (u64_stats_fetch_retry(&stats->sync, start)); + base += ETHTOOL_TXSTATS_NUM; + } + } +diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c +index b12152e2fca0..52d6d43e7bfe 100644 +--- a/drivers/net/ethernet/emulex/benet/be_main.c ++++ b/drivers/net/ethernet/emulex/benet/be_main.c +@@ -665,10 +665,10 @@ static void be_get_stats64(struct net_device *netdev, + const struct be_rx_stats *rx_stats = rx_stats(rxo); + + do { +- start = u64_stats_fetch_begin_irq(&rx_stats->sync); ++ start = u64_stats_fetch_begin(&rx_stats->sync); + pkts = rx_stats(rxo)->rx_pkts; + bytes = rx_stats(rxo)->rx_bytes; +- } while (u64_stats_fetch_retry_irq(&rx_stats->sync, start)); ++ } while (u64_stats_fetch_retry(&rx_stats->sync, start)); + stats->rx_packets += pkts; + stats->rx_bytes += bytes; + stats->multicast += rx_stats(rxo)->rx_mcast_pkts; +@@ -680,10 +680,10 @@ static void be_get_stats64(struct net_device *netdev, + const struct be_tx_stats *tx_stats = tx_stats(txo); + + do { +- start = u64_stats_fetch_begin_irq(&tx_stats->sync); ++ start = u64_stats_fetch_begin(&tx_stats->sync); + pkts = tx_stats(txo)->tx_pkts; + bytes = tx_stats(txo)->tx_bytes; +- } while (u64_stats_fetch_retry_irq(&tx_stats->sync, start)); ++ } while (u64_stats_fetch_retry(&tx_stats->sync, start)); + stats->tx_packets += pkts; + stats->tx_bytes += bytes; + } +@@ -2156,16 +2156,16 @@ static int be_get_new_eqd(struct be_eq_obj *eqo) + + for_all_rx_queues_on_eq(adapter, eqo, rxo, i) { + do { +- start = u64_stats_fetch_begin_irq(&rxo->stats.sync); ++ start = u64_stats_fetch_begin(&rxo->stats.sync); + rx_pkts += rxo->stats.rx_pkts; +- } while (u64_stats_fetch_retry_irq(&rxo->stats.sync, start)); ++ } while (u64_stats_fetch_retry(&rxo->stats.sync, start)); + } + + for_all_tx_queues_on_eq(adapter, eqo, txo, i) { + do { +- start = u64_stats_fetch_begin_irq(&txo->stats.sync); ++ start = u64_stats_fetch_begin(&txo->stats.sync); + tx_pkts += txo->stats.tx_reqs; +- } while (u64_stats_fetch_retry_irq(&txo->stats.sync, start)); ++ } while (u64_stats_fetch_retry(&txo->stats.sync, start)); + } + + /* Skip, if wrapped around or first calculation */ +diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig +index 4aaada823a67..c7707f62470c 100644 +--- a/drivers/net/ethernet/freescale/Kconfig ++++ b/drivers/net/ethernet/freescale/Kconfig +@@ -35,6 +35,18 @@ config FEC + Say Y here if you want to use the built-in 10/100 Fast ethernet + controller on some Motorola ColdFire and Freescale i.MX/S32 processors. + ++config FEC_ECAT ++ tristate "FEC native dirver for FEC ethernet controller (of ColdFire and some i.MX CPUs)" ++ depends on (ARCH_MXC) ++ default ARCH_MXC if ARM ++ depends on PTP_1588_CLOCK_OPTIONAL ++ select CRC32 ++ select PHYLIB ++ imply NET_SELFTESTS ++ help ++ Say Y here if you want to use the built-in 10/100 Fast ethercat ++ controller on some Motorola ColdFire and Freescale i.MX/S32 processors. ++ + config FEC_UIO + tristate "FEC_UIO ethernet controller (i.MX 8M Mini CPU)" + default n +@@ -43,6 +55,14 @@ config FEC_UIO + Say Y here if you want to use the built-in 10/100 Fast ethernet + controller on Freescale i.MX 8M Mini processor. + ++config AVB_SUPPORT ++ bool "AVB interface support" ++ depends on FEC && ARCH_MXC ++ help ++ Say Y here if you want to enable the AVB interface in the FEC ++ driver. For the actual AVB functionality an external module is ++ still required. ++ + config FEC_MPC52xx + tristate "FEC MPC52xx driver" + depends on PPC_MPC52xx && PPC_BESTCOMM +diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile +index 13ae9401e4c2..bd3ee981ce54 100644 +--- a/drivers/net/ethernet/freescale/Makefile ++++ b/drivers/net/ethernet/freescale/Makefile +@@ -7,6 +7,8 @@ obj-$(CONFIG_FEC) += fec.o + fec-objs :=fec_main.o fec_ptp.o + obj-$(CONFIG_FEC_UIO) += fec_uio.o + ++obj-$(CONFIG_FEC_ECAT) += fec_ecat.o ++ + obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o + ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) + obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o +diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h +index 538d9f1587c8..a7877b6e9999 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc.h ++++ b/drivers/net/ethernet/freescale/enetc/enetc.h +@@ -396,6 +396,8 @@ struct enetc_ndev_priv { + + struct work_struct tx_onestep_tstamp; + struct sk_buff_head tx_skbs; ++ ++ bool fp_enabled_admin; + }; + + /* Messaging */ +@@ -440,6 +442,7 @@ int enetc_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags); + + /* ethtool */ + void enetc_set_ethtool_ops(struct net_device *ndev); ++int enetc_pmac_reset(struct net_device *ndev, bool enable); + + /* control buffer descriptor ring (CBDR) */ + int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count, +@@ -594,11 +597,13 @@ static inline int enetc_set_psfp(struct net_device *ndev, bool en) + void enetc_tsn_pf_init(struct net_device *netdev, struct pci_dev *pdev); + void enetc_tsn_pf_deinit(struct net_device *netdev); + void enetc_pspeed_set(struct enetc_ndev_priv *priv, int speed); ++void enetc_ptp_clock_update(void); + + #else + + #define enetc_tsn_pf_init(netdev, pdev) (void)0 + #define enetc_tsn_pf_deinit(netdev) (void)0 + #define enetc_pspeed_set(priv, speed) (void)0 ++#define enetc_ptp_clock_update() (void)0 + + #endif +diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +index 5ad52e8c72f6..da33d37a83c2 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +@@ -898,6 +898,56 @@ static int enetc_set_link_ksettings(struct net_device *dev, + return phylink_ethtool_ksettings_set(priv->phylink, cmd); + } + ++static void enetc_configure_port_pmac(struct enetc_hw *hw, bool enable) ++{ ++ u32 temp; ++ ++ /* Set pMAC step lock */ ++ temp = enetc_port_rd(hw, ENETC_PFPMR); ++ enetc_port_wr(hw, ENETC_PFPMR, ++ temp | ENETC_PFPMR_PMACE | ENETC_PFPMR_MWLM); ++ ++ temp = enetc_port_rd(hw, ENETC_MMCSR); ++ if (enable) ++ temp |= ENETC_MMCSR_ME; ++ else ++ temp &= (~ENETC_MMCSR_ME); ++ enetc_port_wr(hw, ENETC_MMCSR, temp); ++} ++ ++int enetc_pmac_reset(struct net_device *ndev, bool enable) ++{ ++ struct enetc_ndev_priv *priv = netdev_priv(ndev); ++ u32 temp; ++ ++ temp = enetc_port_rd(&priv->si->hw, ENETC_PFPMR); ++ if (temp & ENETC_PFPMR_PMACE) ++ enetc_configure_port_pmac(&priv->si->hw, enable); ++ ++ return 0; ++} ++ ++static int enetc_reset_preempt(struct net_device *ndev, bool enable) ++{ ++ struct enetc_ndev_priv *priv = netdev_priv(ndev); ++ u32 temp; ++ ++ temp = enetc_rd(&priv->si->hw, ENETC_PTGCR); ++ if (temp & ENETC_PTGCR_TGE) ++ enetc_wr(&priv->si->hw, ENETC_PTGCR, ++ temp & (~ENETC_PTGCR_TGPE)); ++ ++ if (enable) { ++ if (priv->fp_enabled_admin) { ++ enetc_configure_port_pmac(&priv->si->hw, 1); ++ } ++ } else { ++ enetc_configure_port_pmac(&priv->si->hw, 0); ++ } ++ ++ return 0; ++} ++ + static void enetc_get_channels(struct net_device *ndev, + struct ethtool_channels *chan) + { +@@ -907,6 +957,105 @@ static void enetc_get_channels(struct net_device *ndev, + chan->combined_count = priv->bdr_int_num; + } + ++static int enetc_set_preempt(struct net_device *ndev, ++ struct ethtool_fp *pt) ++{ ++ struct enetc_ndev_priv *priv = netdev_priv(ndev); ++ u32 preempt, temp; ++ int rafs; ++ int i; ++ ++ if (!pt) ++ return -EINVAL; ++ ++ if (!pt->disabled && (pt->min_frag_size < 60 || pt->min_frag_size > 252)) ++ return -EINVAL; ++ ++ rafs = DIV_ROUND_UP((pt->min_frag_size + 4), 64) - 1; ++ ++ preempt = pt->preemptible_queues_mask; ++ ++ temp = enetc_rd(&priv->si->hw, ENETC_PTGCR); ++ if (temp & ENETC_PTGCR_TGE) ++ enetc_wr(&priv->si->hw, ENETC_PTGCR, ++ temp & (~ENETC_PTGCR_TGPE)); ++ ++ for (i = 0; i < 8; i++) { ++ /* 1 Enabled. Traffic is transmitted on the preemptive MAC. */ ++ temp = enetc_port_rd(&priv->si->hw, ENETC_PTCFPR(i)); ++ ++ if ((preempt >> i) & 0x1) ++ enetc_port_wr(&priv->si->hw, ++ ENETC_PTCFPR(i), ++ temp | ENETC_FPE); ++ else ++ enetc_port_wr(&priv->si->hw, ++ ENETC_PTCFPR(i), ++ temp & ~ENETC_FPE); ++ } ++ ++ temp = enetc_port_rd(&priv->si->hw, ENETC_MMCSR); ++ temp &= ~ENETC_MMCSR_RAFS_MASK; ++ temp |= ENETC_MMCSR_RAFS(rafs); ++ if (pt->fp_enabled) ++ temp &= ~ENETC_MMCSR_VDIS; ++ else ++ temp |= ENETC_MMCSR_VDIS; ++ enetc_port_wr(&priv->si->hw, ENETC_MMCSR, temp); ++ ++ if (pt->disabled) { ++ enetc_configure_port_pmac(&priv->si->hw, 0); ++ priv->fp_enabled_admin = 0; ++ } else { ++ enetc_configure_port_pmac(&priv->si->hw, 1); ++ priv->fp_enabled_admin = 1; ++ } ++ ++ if (pt->disabled) { ++ temp = enetc_port_rd(&priv->si->hw, ENETC_PFPMR); ++ enetc_port_wr(&priv->si->hw, ENETC_PFPMR, ++ temp & ~(ENETC_PFPMR_PMACE | ENETC_PFPMR_MWLM)); ++ } ++ ++ return 0; ++} ++ ++static int enetc_get_preempt(struct net_device *ndev, ++ struct ethtool_fp *pt) ++{ ++ struct enetc_ndev_priv *priv = netdev_priv(ndev); ++ u32 temp; ++ int i; ++ ++ if (!pt) ++ return -EINVAL; ++ ++ temp = enetc_port_rd(&priv->si->hw, ENETC_MMCSR); ++ if (!(temp & ENETC_MMCSR_VDIS) && (ENETC_MMCSR_GET_VSTS(temp) == 3)) ++ pt->fp_active = true; ++ else if ((temp & ENETC_MMCSR_VDIS) && (temp & ENETC_MMCSR_ME)) ++ pt->fp_active = true; ++ else ++ pt->fp_active = false; ++ ++ if (temp & ENETC_MMCSR_ME) ++ pt->fp_status = true; ++ else ++ pt->fp_status = false; ++ ++ pt->preemptible_queues_mask = 0; ++ for (i = 0; i < 8; i++) ++ if (enetc_port_rd(&priv->si->hw, ENETC_PTCFPR(i)) & 0x80000000) ++ pt->preemptible_queues_mask |= 1 << i; ++ ++ pt->fp_supported = !!(priv->si->hw_features & ENETC_SI_F_QBU); ++ pt->supported_queues_mask = 0xff; ++ temp = enetc_port_rd(&priv->si->hw, ENETC_MMCSR); ++ pt->min_frag_size = (ENETC_MMCSR_GET_RAFS(temp) + 1) * 64; ++ ++ return 0; ++} ++ + static const struct ethtool_ops enetc_pf_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_MAX_FRAMES | +@@ -938,6 +1087,9 @@ static const struct ethtool_ops enetc_pf_ethtool_ops = { + .get_pauseparam = enetc_get_pauseparam, + .set_pauseparam = enetc_set_pauseparam, + .get_channels = enetc_get_channels, ++ .set_preempt = enetc_set_preempt, ++ .get_preempt = enetc_get_preempt, ++ .reset_preempt = enetc_reset_preempt, + }; + + static const struct ethtool_ops enetc_vf_ethtool_ops = { +diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h +index 85db68c54e82..343a47962187 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h ++++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h +@@ -223,7 +223,13 @@ enum enetc_bdr_type {TX, RX}; + #define ENETC_PSIVHFR0(n) (0x1e00 + (n) * 8) /* n = SI index */ + #define ENETC_PSIVHFR1(n) (0x1e04 + (n) * 8) /* n = SI index */ + #define ENETC_MMCSR 0x1f00 ++#define ENETC_MMCSR_VSTS GENMASK(20, 18) ++#define ENETC_MMCSR_GET_VSTS(x) (((x) & ENETC_MMCSR_VSTS) >> 18) ++#define ENETC_MMCSR_VDIS BIT(17) + #define ENETC_MMCSR_ME BIT(16) ++#define ENETC_MMCSR_RAFS_MASK GENMASK(9, 8) ++#define ENETC_MMCSR_RAFS(x) (((x) << 8) & ENETC_MMCSR_RAFS_MASK) ++#define ENETC_MMCSR_GET_RAFS(x) (((x) & ENETC_MMCSR_RAFS_MASK) >> 8) + #define ENETC_PTCMSDUR(n) (0x2020 + (n) * 4) /* n = TC index [0..7] */ + + #define ENETC_PM0_CMD_CFG 0x8008 +diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c +index 3e8f2defd67f..08fe4cd64c44 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c +@@ -590,26 +590,11 @@ static void enetc_mac_enable(struct enetc_hw *hw, bool en) + enetc_port_wr(hw, ENETC_PM1_CMD_CFG, val); + } + +-static void enetc_configure_port_pmac(struct enetc_hw *hw) +-{ +- u32 temp; +- +- /* Set pMAC step lock */ +- temp = enetc_port_rd(hw, ENETC_PFPMR); +- enetc_port_wr(hw, ENETC_PFPMR, +- temp | ENETC_PFPMR_PMACE | ENETC_PFPMR_MWLM); +- +- temp = enetc_port_rd(hw, ENETC_MMCSR); +- enetc_port_wr(hw, ENETC_MMCSR, temp | ENETC_MMCSR_ME); +-} +- + static void enetc_configure_port(struct enetc_pf *pf) + { + u8 hash_key[ENETC_RSSHASH_KEY_SIZE]; + struct enetc_hw *hw = &pf->si->hw; + +- enetc_configure_port_pmac(hw); +- + enetc_configure_port_mac(hw); + + enetc_port_si_configure(pf->si); +@@ -1111,6 +1096,7 @@ static void enetc_pl_mac_link_down(struct phylink_config *config, + struct enetc_pf *pf = phylink_to_enetc_pf(config); + + enetc_mac_enable(&pf->si->hw, false); ++ enetc_pmac_reset(pf->si->ndev, 0); + } + + static const struct phylink_mac_ops enetc_mac_phylink_ops = { +diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ptp.c b/drivers/net/ethernet/freescale/enetc/enetc_ptp.c +index 5243fc031058..9c671494cd95 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc_ptp.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_ptp.c +@@ -10,6 +10,19 @@ + int enetc_phc_index = -1; + EXPORT_SYMBOL_GPL(enetc_phc_index); + ++int ptp_enetc_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts) ++{ ++ int ret; ++ ++ ret = ptp_qoriq_settime(ptp, ts); ++ if (ret) ++ return ret; ++ ++ enetc_ptp_clock_update(); ++ ++ return ret; ++} ++ + static struct ptp_clock_info enetc_ptp_caps = { + .owner = THIS_MODULE, + .name = "ENETC PTP clock", +@@ -22,7 +35,7 @@ static struct ptp_clock_info enetc_ptp_caps = { + .adjfine = ptp_qoriq_adjfine, + .adjtime = ptp_qoriq_adjtime, + .gettime64 = ptp_qoriq_gettime, +- .settime64 = ptp_qoriq_settime, ++ .settime64 = ptp_enetc_settime, + .enable = ptp_qoriq_enable, + }; + +diff --git a/drivers/net/ethernet/freescale/enetc/enetc_tsn.c b/drivers/net/ethernet/freescale/enetc/enetc_tsn.c +index 5ff36647be84..091e12c76cfa 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc_tsn.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_tsn.c +@@ -1,5 +1,5 @@ + // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +-/* Copyright 2017-2019 NXP */ ++/* Copyright 2017-2023 NXP */ + + #include "enetc.h" + +@@ -8,6 +8,13 @@ + #include + #include + ++struct netdev_list_entry { ++ struct list_head list; ++ struct net_device *dev; ++}; ++ ++static struct list_head netdev_list = {0}; ++ + static int alloc_cbdr(struct enetc_si *si, struct enetc_cbd **curr_cbd) + { + struct enetc_cbdr *ring = &si->cbd_ring; +@@ -184,17 +191,8 @@ static int enetc_qbv_set(struct net_device *ndev, + gcl_config->atc = admin_basic->gate_states; + gcl_config->acl_len = cpu_to_le16(gcl_len); + +- if (!admin_basic->base_time) { +- gcl_data->btl = +- cpu_to_le32(enetc_rd(hw, ENETC_SICTR0)); +- gcl_data->bth = +- cpu_to_le32(enetc_rd(hw, ENETC_SICTR1)); +- } else { +- gcl_data->btl = +- cpu_to_le32(lower_32_bits(admin_basic->base_time)); +- gcl_data->bth = +- cpu_to_le32(upper_32_bits(admin_basic->base_time)); +- } ++ gcl_data->btl = cpu_to_le32(lower_32_bits(admin_basic->base_time)); ++ gcl_data->bth = cpu_to_le32(upper_32_bits(admin_basic->base_time)); + + gcl_data->ct = cpu_to_le32(admin_basic->cycle_time); + gcl_data->cte = cpu_to_le32(admin_basic->cycle_time_extension); +@@ -256,8 +254,8 @@ static int enetc_qbv_get(struct net_device *ndev, + { + struct tsn_qbv_basic *admin_basic = &admin_conf->admin; + struct enetc_ndev_priv *priv = netdev_priv(ndev); ++ u16 data_size, admin_len, oper_len, maxlen; + struct enetc_hw *hw = &priv->si->hw; +- u16 data_size, admin_len, maxlen; + struct tgs_gcl_query *gcl_query; + struct tgs_gcl_resp *gcl_data; + struct enetc_cbd *cbdr; +@@ -290,6 +288,7 @@ static int enetc_qbv_get(struct net_device *ndev, + gce = (struct gce *)(gcl_data + 1); + + gcl_query->acl_len = cpu_to_le16(maxlen); ++ gcl_query->ocl_len = cpu_to_le16(maxlen); + + dma_size = cpu_to_le16(data_size); + cbdr->length = dma_size; +@@ -312,8 +311,12 @@ static int enetc_qbv_get(struct net_device *ndev, + + /* since cbdr already passed to free, below could be get wrong */ + admin_len = le16_to_cpu(gcl_query->admin_list_len); ++ oper_len = le16_to_cpu(gcl_query->oper_list_len); + +- admin_basic->control_list_length = admin_len; ++ if (!admin_len) ++ admin_basic->control_list_length = oper_len; ++ else ++ admin_basic->control_list_length = admin_len; + + temp = ((u64)le32_to_cpu(gcl_data->abth)) << 32; + admin_basic->base_time = le32_to_cpu(gcl_data->abtl) + temp; +@@ -321,7 +324,7 @@ static int enetc_qbv_get(struct net_device *ndev, + admin_basic->cycle_time = le32_to_cpu(gcl_data->act); + admin_basic->cycle_time_extension = le32_to_cpu(gcl_data->acte); + +- admin_basic->control_list = kcalloc(admin_len, ++ admin_basic->control_list = kcalloc(admin_basic->control_list_length, + sizeof(*admin_basic->control_list), + GFP_KERNEL); + if (!admin_basic->control_list) { +@@ -330,10 +333,15 @@ static int enetc_qbv_get(struct net_device *ndev, + return -ENOMEM; + } + +- for (i = 0; i < admin_len; i++) { +- struct gce *temp_gce = gce + i; ++ for (i = 0; i < admin_basic->control_list_length; i++) { ++ struct gce *temp_gce; + struct tsn_qbv_entry *temp_entry; + ++ if (!admin_len) ++ temp_gce = gce + maxlen + i; ++ else ++ temp_gce = gce + i; ++ + temp_entry = admin_basic->control_list + i; + + temp_entry->gate_state = temp_gce->gate; +@@ -468,6 +476,120 @@ static int enetc_qbv_get_status(struct net_device *ndev, + return 0; + } + ++static int enetc_qbv_gcl_reset(struct net_device *ndev, ++ struct tsn_qbv_conf *admin_conf) ++{ ++ struct tsn_qbv_basic *admin_basic = &admin_conf->admin; ++ struct enetc_ndev_priv *priv = netdev_priv(ndev); ++ struct enetc_hw *hw = &priv->si->hw; ++ struct tgs_gcl_conf *gcl_config; ++ struct tgs_gcl_data *gcl_data; ++ struct enetc_cbd *cbdr; ++ struct gce *gce; ++ dma_addr_t dma; ++ u16 data_size; ++ int curr_cbd; ++ u16 gcl_len; ++ u32 temp; ++ int i; ++ ++ gcl_len = admin_basic->control_list_length; ++ if (gcl_len > enetc_get_max_gcl_len(hw)) ++ return -EINVAL; ++ ++ temp = enetc_rd(hw, ENETC_PTGCR); ++ if (admin_conf->gate_enabled) { ++ enetc_wr(hw, ENETC_PTGCR, temp & ~ENETC_PTGCR_TGE); ++ usleep_range(10, 20); ++ enetc_wr(hw, ENETC_PTGCR, temp | ENETC_PTGCR_TGE); ++ } else if (!admin_conf->gate_enabled) { ++ return 0; ++ } ++ ++ /* Configure the (administrative) gate control list using the ++ * control BD descriptor. ++ */ ++ curr_cbd = alloc_cbdr(priv->si, &cbdr); ++ ++ gcl_config = &cbdr->gcl_conf; ++ ++ data_size = struct_size(gcl_data, entry, gcl_len); ++ ++ gcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); ++ if (!gcl_data) ++ return -ENOMEM; ++ ++ gce = &gcl_data->entry[0]; ++ ++ gcl_config->atc = admin_basic->gate_states; ++ gcl_config->acl_len = cpu_to_le16(gcl_len); ++ ++ gcl_data->btl = cpu_to_le32(lower_32_bits(admin_basic->base_time)); ++ gcl_data->bth = cpu_to_le32(upper_32_bits(admin_basic->base_time)); ++ ++ gcl_data->ct = cpu_to_le32(admin_basic->cycle_time); ++ gcl_data->cte = cpu_to_le32(admin_basic->cycle_time_extension); ++ ++ for (i = 0; i < gcl_len; i++) { ++ struct gce *temp_gce = gce + i; ++ struct tsn_qbv_entry *temp_entry; ++ ++ temp_entry = admin_basic->control_list + i; ++ ++ temp_gce->gate = temp_entry->gate_state; ++ temp_gce->period = cpu_to_le32(temp_entry->time_interval); ++ } ++ ++ cbdr->length = cpu_to_le16(data_size); ++ cbdr->status_flags = 0; ++ ++ dma = dma_map_single(&priv->si->pdev->dev, gcl_data, ++ data_size, DMA_TO_DEVICE); ++ if (dma_mapping_error(&priv->si->pdev->dev, dma)) { ++ netdev_err(priv->si->ndev, "DMA mapping failed!\n"); ++ kfree(gcl_data); ++ return -ENOMEM; ++ } ++ ++ cbdr->addr[0] = cpu_to_le32(lower_32_bits(dma)); ++ cbdr->addr[1] = cpu_to_le32(upper_32_bits(dma)); ++ cbdr->cmd = 0; ++ cbdr->cls = BDCR_CMD_PORT_GCL; ++ ++ /* Updated by ENETC on completion of the configuration ++ * command. A zero value indicates success. ++ */ ++ cbdr->status_flags = 0; ++ ++ xmit_cbdr(priv->si, curr_cbd); ++ ++ memset(cbdr, 0, sizeof(struct enetc_cbd)); ++ dma_unmap_single(&priv->si->pdev->dev, dma, data_size, DMA_TO_DEVICE); ++ kfree(gcl_data); ++ ++ return 0; ++} ++ ++static int enetc_est_reset(struct net_device *ndev) ++{ ++ struct tsn_qbv_conf admin_conf = {0}; ++ int ret; ++ ++ ret = enetc_qbv_get(ndev, &admin_conf); ++ if (ret) ++ return ret; ++ ++ return enetc_qbv_gcl_reset(ndev, &admin_conf); ++} ++ ++void enetc_ptp_clock_update() ++{ ++ struct netdev_list_entry *entry; ++ ++ list_for_each_entry(entry, &netdev_list, list) ++ enetc_est_reset(entry->dev); ++} ++ + /* CBD Class 7: Stream Identity Entry Set Descriptor - Long Format */ + static int enetc_cb_streamid_set(struct net_device *ndev, u32 index, + bool en, struct tsn_cb_streamid *streamid) +@@ -1560,6 +1682,14 @@ static int enetc_qbu_set(struct net_device *ndev, u8 ptvector) + temp & ~ENETC_FPE); + } + ++ /* Set pMAC step lock */ ++ temp = enetc_port_rd(&priv->si->hw, ENETC_PFPMR); ++ enetc_port_wr(&priv->si->hw, ENETC_PFPMR, ++ temp | ENETC_PFPMR_PMACE | ENETC_PFPMR_MWLM); ++ ++ temp = enetc_port_rd(&priv->si->hw, ENETC_MMCSR); ++ enetc_port_wr(&priv->si->hw, ENETC_MMCSR, temp | ENETC_MMCSR_ME); ++ + return 0; + } + +@@ -1922,6 +2052,33 @@ static struct tsn_ops enetc_tsn_ops_part = { + .qci_fmi_get = enetc_qci_fmi_get, + }; + ++static void enetc_tsn_netdev_list_add(struct net_device *ndev) ++{ ++ struct netdev_list_entry *entry; ++ ++ entry = kzalloc(sizeof(*entry), GFP_KERNEL); ++ if (!entry) ++ return; ++ ++ entry->dev = ndev; ++ ++ if (!netdev_list.next) ++ INIT_LIST_HEAD(&netdev_list); ++ ++ list_add_tail(&entry->list, &netdev_list); ++} ++ ++static void enetc_tsn_netdev_list_del(struct net_device *ndev) ++{ ++ struct netdev_list_entry *tmp, *n; ++ ++ list_for_each_entry_safe(tmp, n, &netdev_list, list) ++ if (tmp->dev == ndev) { ++ list_del(&tmp->list); ++ kfree(tmp); ++ } ++} ++ + void enetc_tsn_pf_init(struct net_device *netdev, struct pci_dev *pdev) + { + int port = pdev->devfn & 0x7; +@@ -1932,9 +2089,12 @@ void enetc_tsn_pf_init(struct net_device *netdev, struct pci_dev *pdev) + else + tsn_port_register(netdev, &enetc_tsn_ops_full, + (u16)pdev->bus->number); ++ ++ enetc_tsn_netdev_list_add(netdev); + } + + void enetc_tsn_pf_deinit(struct net_device *netdev) + { + tsn_port_unregister(netdev); ++ enetc_tsn_netdev_list_del(netdev); + } +diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h +index 0465d98795d7..6775eeb3c684 100644 +--- a/drivers/net/ethernet/freescale/fec.h ++++ b/drivers/net/ethernet/freescale/fec.h +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ + defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ +@@ -333,30 +334,30 @@ struct bufdesc_ex { + (IDLE_SLOPE_2 & IDLE_SLOPE_MASK)) + #define RCMR_MATCHEN (0x1 << 16) + #define RCMR_CMP_CFG(v, n) (((v) & 0x7) << (n << 2)) ++#ifdef CONFIG_AVB_SUPPORT ++#define SR_CLASS_A_PRIORITY 3 ++#define SR_CLASS_B_PRIORITY 2 ++#define RCMR_CMP_1 (RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 0) | \ ++ RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 1) | \ ++ RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 2) | \ ++ RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 3)) ++#define RCMR_CMP_2 (RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 0) | \ ++ RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 1) | \ ++ RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 2) | \ ++ RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 3)) ++#else + #define RCMR_CMP_1 (RCMR_CMP_CFG(0, 0) | RCMR_CMP_CFG(1, 1) | \ + RCMR_CMP_CFG(2, 2) | RCMR_CMP_CFG(3, 3)) + #define RCMR_CMP_2 (RCMR_CMP_CFG(4, 0) | RCMR_CMP_CFG(5, 1) | \ + RCMR_CMP_CFG(6, 2) | RCMR_CMP_CFG(7, 3)) ++#endif + #define RCMR_CMP(X) (((X) == 1) ? RCMR_CMP_1 : RCMR_CMP_2) + #define FEC_TX_BD_FTYPE(X) (((X) & 0xf) << 20) + +-/* The number of Tx and Rx buffers. These are allocated from the page +- * pool. The code may assume these are power of two, so it it best +- * to keep them that size. +- * We don't need to allocate pages for the transmitter. We just use +- * the skbuffer directly. +- */ ++#define FEC_RX_FLUSH(X) (1 << ((X) + 3)) + +-#define FEC_ENET_XDP_HEADROOM (XDP_PACKET_HEADROOM) +-#define FEC_ENET_RX_PAGES 256 +-#define FEC_ENET_RX_FRSIZE (PAGE_SIZE - FEC_ENET_XDP_HEADROOM \ +- - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) +-#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) +-#define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) +-#define FEC_ENET_TX_FRSIZE 2048 +-#define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE) +-#define TX_RING_SIZE 512 /* Must be power of two */ +-#define TX_RING_MOD_MASK 511 /* for this to work */ ++#define FEC_TX_SCHEME_CB 0x0 /* Credit based */ ++#define FEC_TX_SCHEME_RR 0x1 /* Round-robin */ + + #define BD_ENET_RX_INT 0x00800000 + #define BD_ENET_RX_PTP ((ushort)0x0400) +@@ -401,9 +402,14 @@ struct bufdesc_ex { + #define FEC_ITR_EN (0x1 << 31) + #define FEC_ITR_ICFT(X) (((X) & 0xff) << 20) + #define FEC_ITR_ICTT(X) ((X) & 0xffff) +-#define FEC_ITR_ICFT_DEFAULT 200 /* Set 200 frame count threshold */ + #define FEC_ITR_ICTT_DEFAULT 1000 /* Set 1000us timer threshold */ + ++#ifdef CONFIG_AVB_SUPPORT ++#define FEC_ITR_ICFT_DEFAULT 50 /* Keep it coherent with FEC_TX_RING_SIZE/FEC_RX_RING_SIZE */ ++#else ++#define FEC_ITR_ICFT_DEFAULT 200 /* Set 200 frame count threshold */ ++#endif ++ + #define FEC_VLAN_TAG_LEN 0x04 + #define FEC_ETHTYPE_LEN 0x02 + +@@ -556,20 +562,31 @@ struct fec_tx_buffer { + + struct fec_enet_priv_tx_q { + struct bufdesc_prop bd; +- unsigned char *tx_bounce[TX_RING_SIZE]; +- struct fec_tx_buffer tx_buf[TX_RING_SIZE]; ++ struct bufdesc *dirty_tx; ++ struct fec_tx_buffer tx_buf[FEC_TX_RING_SIZE]; ++ ++ unsigned int tx_bounce_size; + ++#ifdef CONFIG_AVB_SUPPORT ++ unsigned int tx_index; ++ unsigned char *tx_bounce[FEC_TX_RING_SIZE + 32]; ++#else ++ unsigned char *tx_bounce[FEC_TX_RING_SIZE]; ++#endif + unsigned short tx_stop_threshold; + unsigned short tx_wake_threshold; + +- struct bufdesc *dirty_tx; + char *tso_hdrs; + dma_addr_t tso_hdrs_dma; ++ ++#ifdef CONFIG_AVB_SUPPORT ++ unsigned long tx_idle_slope; ++#endif + }; + + struct fec_enet_priv_rx_q { + struct bufdesc_prop bd; +- struct fec_enet_priv_txrx_info rx_skb_info[RX_RING_SIZE]; ++ struct fec_enet_priv_txrx_info rx_skb_info[FEC_RX_RING_SIZE]; + + /* page_pool */ + struct page_pool *page_pool; +@@ -619,6 +636,12 @@ struct fec_enet_private { + unsigned int total_tx_ring_size; + unsigned int total_rx_ring_size; + ++#ifdef CONFIG_AVB_SUPPORT ++ const struct avb_ops *avb; ++ void *avb_data; ++ unsigned int avb_enabled; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_advertising); ++#endif + struct platform_device *pdev; + + int dev_id; +@@ -650,7 +673,7 @@ struct fec_enet_private { + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_caps; + unsigned long last_overflow_check; +- spinlock_t tmreg_lock; ++ raw_spinlock_t tmreg_lock; + struct cyclecounter cc; + struct timecounter tc; + int rx_hwtstamp_filter; +@@ -658,6 +681,13 @@ struct fec_enet_private { + u32 cycle_speed; + int hwts_rx_en; + int hwts_tx_en; ++ ++ /* Transmit and receive latency, depending on link speed, for ++ * packets timestamps in ns ++ */ ++ u32 rx_tstamp_latency; ++ u32 tx_tstamp_latency; ++ + struct delayed_work time_keep; + struct regulator *reg_phy; + struct fec_stop_mode_gpr stop_gpr; +@@ -695,9 +725,35 @@ struct fec_enet_private { + /* XDP BPF Program */ + struct bpf_prog *xdp_prog; + ++ /* Configured rx/tx timestamps delays for different link speeds ++ * to compensate for FEC-PHY latency in ns ++ */ ++ u32 rx_delay_100; ++ u32 tx_delay_100; ++ u32 rx_delay_1000; ++ u32 tx_delay_1000; ++ ++#ifdef CONFIG_AVB_SUPPORT ++ int rec_channel; ++ int rec_enable; ++#endif ++ + u64 ethtool_stats[]; + }; + ++#ifdef CONFIG_AVB_SUPPORT ++#define FEC_MAX_RATE 400 /* Mbps */ ++#define FEC_MAX_RATE_HAS_AVB 1000 /* Mbps */ ++ ++static inline int fec_max_rate(struct fec_enet_private *fep) ++{ ++ int max_rate = (fep->quirks & FEC_QUIRK_HAS_AVB) ? FEC_MAX_RATE_HAS_AVB : FEC_MAX_RATE; ++ return min(max_rate, fep->speed); ++} ++ ++#define IDLE_SLOPE_DIVISOR 512 ++#endif ++ + void fec_ptp_init(struct platform_device *pdev, int irq_idx); + void fec_ptp_stop(struct platform_device *pdev); + void fec_ptp_start_cyclecounter(struct net_device *ndev); +diff --git a/drivers/net/ethernet/freescale/fec_ecat.c b/drivers/net/ethernet/freescale/fec_ecat.c +new file mode 100644 +index 000000000000..86eb3341105b +--- /dev/null ++++ b/drivers/net/ethernet/freescale/fec_ecat.c +@@ -0,0 +1,3008 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "fec_ecat.h" ++ ++ ++#define DRIVER_NAME "fec_ecat" ++ ++/* Pause frame feild and FIFO threshold */ ++#define FEC_ENET_FCE (1 << 5) ++#define FEC_ENET_RSEM_V 0x84 ++#define FEC_ENET_RSFL_V 16 ++#define FEC_ENET_RAEM_V 0x8 ++#define FEC_ENET_RAFL_V 0x8 ++#define FEC_ENET_OPD_V 0xFFF0 ++#define FEC_MDIO_PM_TIMEOUT 100 /* ms */ ++ ++struct fec_devinfo { ++ u32 quirks; ++}; ++ ++static const struct fec_devinfo fec_imx25_info = { ++ .quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR | ++ FEC_QUIRK_HAS_FRREG, ++}; ++ ++static const struct fec_devinfo fec_imx27_info = { ++ .quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG, ++}; ++ ++static const struct fec_devinfo fec_imx28_info = { ++ .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | ++ FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC | ++ FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII | ++ FEC_QUIRK_NO_HARD_RESET, ++}; ++ ++static const struct fec_devinfo fec_imx6q_info = { ++ .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | ++ FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | ++ FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | ++ FEC_QUIRK_HAS_RACC | FEC_QUIRK_CLEAR_SETUP_MII | ++ FEC_QUIRK_HAS_PMQOS, ++}; ++ ++static const struct fec_devinfo fec_mvf600_info = { ++ .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, ++}; ++ ++static const struct fec_devinfo fec_imx6x_info = { ++ .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | ++ FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | ++ FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | ++ FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | ++ FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | ++ FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES, ++}; ++ ++static const struct fec_devinfo fec_imx6ul_info = { ++ .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | ++ FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | ++ FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 | ++ FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | ++ FEC_QUIRK_HAS_COALESCE | FEC_QUIRK_CLEAR_SETUP_MII, ++}; ++ ++static const struct fec_devinfo fec_imx8mq_info = { ++ .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | ++ FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | ++ FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | ++ FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | ++ FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | ++ FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | ++ FEC_QUIRK_HAS_EEE | FEC_QUIRK_WAKEUP_FROM_INT2, ++}; ++ ++static const struct fec_devinfo fec_imx8qm_info = { ++ .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | ++ FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | ++ FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | ++ FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | ++ FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | ++ FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | ++ FEC_QUIRK_DELAYED_CLKS_SUPPORT, ++}; ++ ++static const struct fec_devinfo fec_s32v234_info = { ++ .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | ++ FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | ++ FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | ++ FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE, ++}; ++ ++static struct platform_device_id fec_devtype[] = { ++ { ++ /* keep it for coldfire */ ++ .name = DRIVER_NAME, ++ .driver_data = 0, ++ }, { ++ .name = "imx25-fec", ++ .driver_data = (kernel_ulong_t)&fec_imx25_info, ++ }, { ++ .name = "imx27-fec", ++ .driver_data = (kernel_ulong_t)&fec_imx27_info, ++ }, { ++ .name = "imx28-fec", ++ .driver_data = (kernel_ulong_t)&fec_imx28_info, ++ }, { ++ .name = "imx6q-fec", ++ .driver_data = (kernel_ulong_t)&fec_imx6q_info, ++ }, { ++ .name = "mvf600-fec", ++ .driver_data = (kernel_ulong_t)&fec_mvf600_info, ++ }, { ++ .name = "imx6sx-fec", ++ .driver_data = (kernel_ulong_t)&fec_imx6x_info, ++ }, { ++ .name = "imx6ul-fec", ++ .driver_data = (kernel_ulong_t)&fec_imx6ul_info, ++ }, { ++ .name = "imx8mq-fec", ++ .driver_data = (kernel_ulong_t)&fec_imx8mq_info, ++ }, { ++ .name = "imx8qm-fec", ++ .driver_data = (kernel_ulong_t)&fec_imx8qm_info, ++ }, { ++ .name = "s32v234-fec", ++ .driver_data = (kernel_ulong_t)&fec_s32v234_info, ++ }, { ++ /* sentinel */ ++ } ++}; ++MODULE_DEVICE_TABLE(platform, fec_devtype); ++ ++enum imx_fec_type { ++ IMX25_FEC = 1, /* runs on i.mx25/50/53 */ ++ IMX27_FEC, /* runs on i.mx27/35/51 */ ++ IMX28_FEC, ++ IMX6Q_FEC, ++ MVF600_FEC, ++ IMX6SX_FEC, ++ IMX6UL_FEC, ++ IMX8MQ_FEC, ++ IMX8QM_FEC, ++ S32V234_FEC, ++}; ++ ++static const struct of_device_id fec_dt_ids[] = { ++ { .compatible = "fsl,imx25-fec-ecat", .data = &fec_devtype[IMX25_FEC], }, ++ { .compatible = "fsl,imx27-fec-ecat", .data = &fec_devtype[IMX27_FEC], }, ++ { .compatible = "fsl,imx28-fec-ecat", .data = &fec_devtype[IMX28_FEC], }, ++ { .compatible = "fsl,imx6q-fec-ecat", .data = &fec_devtype[IMX6Q_FEC], }, ++ { .compatible = "fsl,mvf600-fec-ecat", .data = &fec_devtype[MVF600_FEC], }, ++ { .compatible = "fsl,imx6sx-fec-ecat", .data = &fec_devtype[IMX6SX_FEC], }, ++ { .compatible = "fsl,imx6ul-fec-ecat", .data = &fec_devtype[IMX6UL_FEC], }, ++ { .compatible = "fsl,imx8mq-fec-ecat", .data = &fec_devtype[IMX8MQ_FEC], }, ++ { .compatible = "fsl,imx8qm-fec-ecat", .data = &fec_devtype[IMX8QM_FEC], }, ++ { .compatible = "fsl,s32v234-fec-ecat", .data = &fec_devtype[S32V234_FEC], }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, fec_dt_ids); ++ ++static unsigned char macaddr[ETH_ALEN]; ++module_param_array(macaddr, byte, NULL, 0); ++MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); ++ ++#if defined(CONFIG_M5272) ++/* ++ * Some hardware gets it MAC address out of local flash memory. ++ * if this is non-zero then assume it is the address to get MAC from. ++ */ ++#if defined(CONFIG_NETtel) ++#define FEC_FLASHMAC 0xf0006006 ++#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) ++#define FEC_FLASHMAC 0xf0006000 ++#elif defined(CONFIG_CANCam) ++#define FEC_FLASHMAC 0xf0020000 ++#elif defined (CONFIG_M5272C3) ++#define FEC_FLASHMAC (0xffe04000 + 4) ++#elif defined(CONFIG_MOD5272) ++#define FEC_FLASHMAC 0xffc0406b ++#else ++#define FEC_FLASHMAC 0 ++#endif ++#endif /* CONFIG_M5272 */ ++ ++/* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. ++ * ++ * 2048 byte skbufs are allocated. However, alignment requirements ++ * varies between FEC variants. Worst case is 64, so round down by 64. ++ */ ++#define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64)) ++#define PKT_MINBUF_SIZE 64 ++ ++/* FEC receive acceleration */ ++#define FEC_RACC_IPDIS (1 << 1) ++#define FEC_RACC_PRODIS (1 << 2) ++#define FEC_RACC_SHIFT16 BIT(7) ++#define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) ++ ++/* MIB Control Register */ ++#define FEC_MIB_CTRLSTAT_DISABLE BIT(31) ++ ++/* ++ * The 5270/5271/5280/5282/532x RX control register also contains maximum frame ++ * size bits. Other FEC hardware does not, so we need to take that into ++ * account when setting it. ++ */ ++#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ ++ defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ ++ defined(CONFIG_ARM64) ++#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) ++#else ++#define OPT_FRAME_SIZE 0 ++#endif ++ ++/* FEC MII MMFR bits definition */ ++#define FEC_MMFR_ST (1 << 30) ++#define FEC_MMFR_ST_C45 (0) ++#define FEC_MMFR_OP_READ (2 << 28) ++#define FEC_MMFR_OP_READ_C45 (3 << 28) ++#define FEC_MMFR_OP_WRITE (1 << 28) ++#define FEC_MMFR_OP_ADDR_WRITE (0) ++#define FEC_MMFR_PA(v) ((v & 0x1f) << 23) ++#define FEC_MMFR_RA(v) ((v & 0x1f) << 18) ++#define FEC_MMFR_TA (2 << 16) ++#define FEC_MMFR_DATA(v) (v & 0xffff) ++/* FEC ECR bits definition */ ++#define FEC_ECR_MAGICEN (1 << 2) ++#define FEC_ECR_SLEEP (1 << 3) ++ ++#define FEC_MII_TIMEOUT 30000 /* us */ ++ ++/* Transmitter timeout */ ++#define TX_TIMEOUT (2 * HZ) ++ ++#define FEC_PAUSE_FLAG_AUTONEG 0x1 ++#define FEC_PAUSE_FLAG_ENABLE 0x2 ++#define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) ++#define FEC_WOL_FLAG_ENABLE (0x1 << 1) ++#define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) ++ ++static void set_multicast_list(struct net_device *ndev); ++static int mii_cnt; ++ ++static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, ++ struct bufdesc_prop *bd) ++{ ++ return (bdp >= bd->last) ? bd->base ++ : (struct bufdesc *)(((void *)bdp) + bd->dsize); ++} ++ ++static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, ++ struct bufdesc_prop *bd) ++{ ++ return (bdp <= bd->base) ? bd->last ++ : (struct bufdesc *)(((void *)bdp) - bd->dsize); ++} ++ ++static int fec_enet_get_bd_index(struct bufdesc *bdp, ++ struct bufdesc_prop *bd) ++{ ++ return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2; ++} ++ ++static void swap_buffer(void *bufaddr, int len) ++{ ++ int i; ++ unsigned int *buf = bufaddr; ++ ++ for (i = 0; i < len; i += 4, buf++) ++ swab32s(buf); ++} ++ ++static void swap_buffer2(void *dst_buf, void *src_buf, int len) ++{ ++ int i; ++ unsigned int *src = src_buf; ++ unsigned int *dst = dst_buf; ++ ++ for (i = 0; i < len; i += 4, src++, dst++) ++ *dst = swab32p(src); ++} ++ ++static void fec_dump(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct bufdesc *bdp; ++ struct fec_enet_priv_tx_q *txq; ++ int index = 0; ++ ++ netdev_info(ndev, "TX ring dump\n"); ++ pr_info("Nr SC addr len SKB\n"); ++ ++ txq = fep->tx_queue; ++ bdp = txq->bd.base; ++ ++ do { ++ pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n", ++ index, ++ bdp == txq->bd.cur ? 'S' : ' ', ++ bdp == txq->dirty_tx ? 'H' : ' ', ++ fec16_to_cpu(bdp->cbd_sc), ++ fec32_to_cpu(bdp->cbd_bufaddr), ++ fec16_to_cpu(bdp->cbd_datlen), ++ txq->tx_skbuff[index]); ++ bdp = fec_enet_get_nextdesc(bdp, &txq->bd); ++ index++; ++ } while (bdp != txq->bd.base); ++} ++ ++ ++static int fec_ecat_txq_submit_buff(struct fec_enet_priv_tx_q *txq, ++ void __user *buff, size_t len, struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct bufdesc *bdp, *last_bdp; ++ void *bufaddr; ++ unsigned short status; ++ unsigned short buflen; ++ unsigned int index; ++ struct sk_buff *skb; ++ ++ bdp = txq->bd.cur; ++ last_bdp = bdp; ++ status = fec16_to_cpu(bdp->cbd_sc); ++ status &= ~BD_ENET_TX_STATS; ++ ++ index = fec_enet_get_bd_index(last_bdp, &txq->bd); ++ skb = txq->tx_skbuff[index]; ++ bufaddr = skb->data; ++ buflen = len; ++ copy_from_user(skb->data, buff, len); ++ bdp->cbd_datlen = cpu_to_fec16(buflen); ++ /* Push the data cache so the CPM does not get stale memory data. */ ++ dma_sync_single_for_device(&fep->pdev->dev, ++ fec32_to_cpu(bdp->cbd_bufaddr), ++ buflen, ++ DMA_TO_DEVICE); ++ ++ status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); ++ ++ wmb(); ++ ++ /* Send it on its way. Tell FEC it's ready, interrupt when done, ++ * it's the last BD of the frame, and to put the CRC on the end. ++ */ ++ status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); ++ bdp->cbd_sc = cpu_to_fec16(status); ++ ++ /* If this was the last BD in the ring, start at the beginning again. */ ++ bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); ++ ++ wmb(); ++ txq->bd.cur = bdp; ++ ++ /* Trigger transmission start */ ++ if (!(fep->quirks & FEC_QUIRK_ERR007885) || ++ !readl(txq->bd.reg_desc_active) || ++ !readl(txq->bd.reg_desc_active) || ++ !readl(txq->bd.reg_desc_active) || ++ !readl(txq->bd.reg_desc_active)) ++ writel(0, txq->bd.reg_desc_active); ++ ++ return 0; ++} ++ ++static void fec_ecat_tx_queue(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct bufdesc *bdp; ++ unsigned short status; ++ struct fec_enet_priv_tx_q *txq = fep->tx_queue; ++ int index = 0; ++ ++ /* get next bdp of dirty_tx */ ++ bdp = txq->dirty_tx; ++ ++ /* get next bdp of dirty_tx */ ++ bdp = fec_enet_get_nextdesc(bdp, &txq->bd); ++ ++ while (bdp != READ_ONCE(txq->bd.cur)) { ++ /* Order the load of bd.cur and cbd_sc */ ++ rmb(); ++ status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc)); ++ if (status & BD_ENET_TX_READY) ++ break; ++ ++ /* Check for errors. */ ++ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | ++ BD_ENET_TX_RL | BD_ENET_TX_UN | ++ BD_ENET_TX_CSL)) { ++ ndev->stats.tx_errors++; ++ } else { ++ ndev->stats.tx_packets++; ++ } ++ ++ wmb(); ++ txq->dirty_tx = bdp; ++ ++ /* Update pointer to next buffer descriptor to be transmitted */ ++ bdp = fec_enet_get_nextdesc(bdp, &txq->bd); ++ } ++ ++ /* ERR006358: Keep the transmitter going */ ++ if (bdp != txq->bd.cur && ++ readl(txq->bd.reg_desc_active) == 0) ++ writel(0, txq->bd.reg_desc_active); ++} ++ ++static int fec_ecat_fast_xmit(struct net_device *ndev, void __user *buff, size_t len) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ int ret; ++ ++ if (!mutex_trylock(&fep->fast_ndev_lock)) { ++ return -EBUSY; ++ } ++ ++ ret = fec_ecat_txq_submit_buff(fep->tx_queue, buff, len, ndev); ++ fec_ecat_tx_queue(ndev); ++ ++ mutex_unlock(&fep->fast_ndev_lock); ++ ++ return NETDEV_TX_OK; ++} ++ ++/* Init RX n TX buffer descriptors ++ */ ++static void fec_enet_bd_init(struct net_device *dev) ++{ ++ struct fec_enet_private *fep = netdev_priv(dev); ++ struct fec_enet_priv_tx_q *txq = fep->tx_queue; ++ struct fec_enet_priv_rx_q *rxq = fep->rx_queue; ++ struct bufdesc *bdp; ++ int i; ++ struct sk_buff *skb; ++ ++ /* Initialize the receive buffer descriptors. */ ++ bdp = rxq->bd.base; ++ ++ for (i = 0; i < rxq->bd.ring_size; i++) { ++ /* Initialize the BD for every fragment in the page. */ ++ if (bdp->cbd_bufaddr) ++ bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); ++ else ++ bdp->cbd_sc = cpu_to_fec16(0); ++ bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); ++ } ++ ++ /* Set the last buffer to wrap */ ++ bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); ++ bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); ++ rxq->bd.cur = rxq->bd.base; ++ ++ /* ...and the same for transmit */ ++ bdp = txq->bd.base; ++ txq->bd.cur = bdp; ++ ++ for (i = 0; i < txq->bd.ring_size; i++) { ++ bdp->cbd_sc = cpu_to_fec16(0); ++ bdp = fec_enet_get_nextdesc(bdp, &txq->bd); ++ } ++ ++ /* Set the last buffer to wrap */ ++ bdp = fec_enet_get_prevdesc(bdp, &txq->bd); ++ bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); ++ txq->dirty_tx = bdp; ++} ++ ++/* ++ * This function is called to start or restart the FEC during a link ++ * change, transmit timeout, or to reconfigure the FEC. The network ++ * packet processing for this device must be stopped before this call. ++ */ ++static void ++fec_restart(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ u32 temp_mac[2]; ++ u32 rcntl = OPT_FRAME_SIZE | 0x04; ++ u32 ecntl = FEC_ENET_ETHEREN; /* ETHEREN */ ++ ++ /* Always use disable MAC instead of MAC reset to: ++ * - Keep the ENET counter running ++ * - Avoid dead system bus for SoCs using the ENET-AXI bus ++ * and not the AHB bus, like the i.MX6SX ++ */ ++ writel(0, fep->hwp + FEC_ECNTRL); ++ ++ /* ++ * enet-mac reset will reset mac address registers too, ++ * so need to reconfigure it. ++ */ ++ memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); ++ writel((__force u32)cpu_to_be32(temp_mac[0]), ++ fep->hwp + FEC_ADDR_LOW); ++ writel((__force u32)cpu_to_be32(temp_mac[1]), ++ fep->hwp + FEC_ADDR_HIGH); ++ ++ /* Clear any outstanding interrupt, except MDIO. */ ++ writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT); ++ ++ fec_enet_bd_init(ndev); ++ ++ writel(fep->rx_queue->bd.dma, fep->hwp + FEC_R_DES_START(0)); ++ writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(0)); ++ ++ writel(fep->tx_queue->bd.dma, fep->hwp + FEC_X_DES_START(0)); ++ ++ if (fep->quirks & FEC_QUIRK_HAS_AVB) ++ writel(FEC_RX_FLUSH(0) | FEC_TX_SCHEME_CB, ++ fep->hwp + FEC_QOS_SCHEME); ++ ++ /* Enable MII mode */ ++ if (fep->full_duplex == DUPLEX_FULL) { ++ /* FD enable */ ++ writel(0x04, fep->hwp + FEC_X_CNTRL); ++ } else { ++ /* No Rcv on Xmit */ ++ rcntl |= 0x02; ++ writel(0x0, fep->hwp + FEC_X_CNTRL); ++ } ++ ++ /* Set MII speed */ ++ writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); ++ ++#if !defined(CONFIG_M5272) ++ if (fep->quirks & FEC_QUIRK_HAS_RACC) { ++ u32 val = readl(fep->hwp + FEC_RACC); ++ ++ /* align IP header */ ++ val |= FEC_RACC_SHIFT16; ++ if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) ++ /* set RX checksum */ ++ val |= FEC_RACC_OPTIONS; ++ else ++ val &= ~FEC_RACC_OPTIONS; ++ writel(val, fep->hwp + FEC_RACC); ++ writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); ++ } ++#endif ++ ++ /* ++ * The phy interface and speed need to get configured ++ * differently on enet-mac. ++ */ ++ if (fep->quirks & FEC_QUIRK_ENET_MAC) { ++ /* Enable flow control and length check */ ++ rcntl |= 0x40000000 | 0x00000020; ++ ++ /* RGMII, RMII or MII */ ++ if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII || ++ fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || ++ fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || ++ fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) ++ rcntl |= (1 << 6); ++ else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) ++ rcntl |= (1 << 8); ++ else ++ rcntl &= ~(1 << 8); ++ ++ /* 1G, 100M or 10M */ ++ if (ndev->phydev) { ++ if (ndev->phydev->speed == SPEED_1000) ++ ecntl |= (1 << 5); ++ else if (ndev->phydev->speed == SPEED_100) ++ rcntl &= ~(1 << 9); ++ else ++ rcntl |= (1 << 9); ++ } ++ } else { ++#ifdef FEC_MIIGSK_ENR ++ if (fep->quirks & FEC_QUIRK_USE_GASKET) { ++ u32 cfgr; ++ /* disable the gasket and wait */ ++ writel(0, fep->hwp + FEC_MIIGSK_ENR); ++ while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) ++ udelay(1); ++ ++ /* ++ * configure the gasket: ++ * RMII, 50 MHz, no loopback, no echo ++ * MII, 25 MHz, no loopback, no echo ++ */ ++ cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) ++ ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; ++ if (ndev->phydev && ndev->phydev->speed == SPEED_10) ++ cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; ++ writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); ++ ++ /* re-enable the gasket */ ++ writel(2, fep->hwp + FEC_MIIGSK_ENR); ++ } ++#endif ++ } ++ ++#if !defined(CONFIG_M5272) ++ /* enable pause frame*/ ++ if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || ++ ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && ++ ndev->phydev && ndev->phydev->pause)) { ++ rcntl |= FEC_ENET_FCE; ++ ++ /* set FIFO threshold parameter to reduce overrun */ ++ writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); ++ writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); ++ writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); ++ writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); ++ ++ /* OPD */ ++ writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); ++ } else { ++ rcntl &= ~FEC_ENET_FCE; ++ } ++#endif /* !defined(CONFIG_M5272) */ ++ ++ writel(rcntl, fep->hwp + FEC_R_CNTRL); ++ ++ /* Setup multicast filter. */ ++ set_multicast_list(ndev); ++#ifndef CONFIG_M5272 ++ writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); ++ writel(0, fep->hwp + FEC_HASH_TABLE_LOW); ++#endif ++ ++ if (fep->quirks & FEC_QUIRK_ENET_MAC) { ++ /* enable ENET endian swap */ ++ ecntl |= (1 << 8); ++ /* enable ENET store and forward mode */ ++ writel(1 << 8, fep->hwp + FEC_X_WMRK); ++ } ++ ++ if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && ++ fep->rgmii_txc_dly) ++ ecntl |= FEC_ENET_TXC_DLY; ++ if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && ++ fep->rgmii_rxc_dly) ++ ecntl |= FEC_ENET_RXC_DLY; ++ ++#ifndef CONFIG_M5272 ++ /* Enable the MIB statistic event counters */ ++ writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); ++#endif ++ ++ /* And last, enable the transmit and receive processing */ ++ writel(ecntl, fep->hwp + FEC_ECNTRL); ++ writel(0, fep->rx_queue->bd.reg_desc_active); ++ ++ writel(0, fep->hwp + FEC_IMASK); ++} ++ ++static int fec_enet_ipc_handle_init(struct fec_enet_private *fep) ++{ ++ if (!(of_machine_is_compatible("fsl,imx8qm") || ++ of_machine_is_compatible("fsl,imx8qxp") || ++ of_machine_is_compatible("fsl,imx8dxl"))) ++ return 0; ++ ++ return imx_scu_get_handle(&fep->ipc_handle); ++} ++ ++static void fec_enet_ipg_stop_set(struct fec_enet_private *fep, bool enabled) ++{ ++ struct device_node *np = fep->pdev->dev.of_node; ++ u32 rsrc_id, val; ++ int idx; ++ ++ if (!np || !fep->ipc_handle) ++ return; ++ ++ idx = of_alias_get_id(np, "ethernet"); ++ if (idx < 0) ++ idx = 0; ++ rsrc_id = idx ? IMX_SC_R_ENET_1 : IMX_SC_R_ENET_0; ++ ++ val = enabled ? 1 : 0; ++ imx_sc_misc_set_control(fep->ipc_handle, rsrc_id, IMX_SC_C_IPG_STOP, val); ++} ++ ++static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) ++{ ++ struct fec_platform_data *pdata = fep->pdev->dev.platform_data; ++ struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr; ++ ++ if (stop_gpr->gpr) { ++ if (enabled) ++ regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, ++ BIT(stop_gpr->bit), ++ BIT(stop_gpr->bit)); ++ else ++ regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, ++ BIT(stop_gpr->bit), 0); ++ } else if (pdata && pdata->sleep_mode_enable) { ++ pdata->sleep_mode_enable(enabled); ++ } else { ++ fec_enet_ipg_stop_set(fep, enabled); ++ } ++} ++ ++static inline void fec_irqs_disable(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ ++ writel(0, fep->hwp + FEC_IMASK); ++} ++ ++static void ++fec_stop(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); ++ ++ /* We cannot expect a graceful transmit stop without link !!! */ ++ if (fep->link) { ++ writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ ++ udelay(10); ++ if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) ++ netdev_err(ndev, "Graceful transmit stop did not complete!\n"); ++ } ++ ++ writel(0, fep->hwp + FEC_ECNTRL); ++ ++ writel(0, fep->hwp + FEC_IMASK); ++ writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); ++} ++ ++ ++static void ++fec_timeout(struct net_device *ndev, unsigned int txqueue) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ ++ fec_dump(ndev); ++ ++ ndev->stats.tx_errors++; ++ ++ schedule_work(&fep->tx_timeout_work); ++} ++ ++static void fec_enet_timeout_work(struct work_struct *work) ++{ ++ struct fec_enet_private *fep = ++ container_of(work, struct fec_enet_private, tx_timeout_work); ++ struct net_device *ndev = fep->netdev; ++ ++ if (netif_device_present(ndev) || netif_running(ndev)) { ++ mutex_lock(&fep->fast_ndev_lock); ++ ++ fec_restart(ndev); ++ mutex_unlock(&fep->fast_ndev_lock); ++ } ++} ++ ++// must be powers of 2 ++#define MAX_TX_BUF (64) ++#define MAX_RX_BUF (64) ++ ++static int fec_ecat_recv_from_queue(struct net_device *ndev, void __user *buff, size_t len, struct sockaddr __user *addr, int *addr_len) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct fec_enet_priv_rx_q *rxq = fep->rx_queue; ++ struct bufdesc *bdp; ++ unsigned short status; ++ struct sk_buff *skb = NULL; ++ ushort pkt_len; ++ __u8 *data; ++ int recv_len = 0; ++ int index = 0; ++ bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; ++ int ret = 0; ++ ++#ifdef CONFIG_M532x ++ flush_cache_all(); ++#endif ++ ++ /* First, grab all of the stats for the incoming packet. ++ * These get messed up if we get called due to a busy condition. ++ */ ++ bdp = rxq->bd.cur; ++ ++ while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { ++ ++ writel(FEC_ENET_RXF_GET(0), fep->hwp + FEC_IEVENT); ++ ++ /* Check for errors. */ ++ status ^= BD_ENET_RX_LAST; ++ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | ++ BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | ++ BD_ENET_RX_CL)) { ++ ndev->stats.rx_errors++; ++ if (status & BD_ENET_RX_OV) { ++ /* FIFO overrun */ ++ ndev->stats.rx_fifo_errors++; ++ goto rx_processing_done; ++ } ++ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH ++ | BD_ENET_RX_LAST)) { ++ /* Frame too long or too short. */ ++ ndev->stats.rx_length_errors++; ++ if (status & BD_ENET_RX_LAST) ++ netdev_err(ndev, "rcv is not +last\n"); ++ } ++ if (status & BD_ENET_RX_CR) /* CRC Error */ ++ ndev->stats.rx_crc_errors++; ++ /* Report late collisions as a frame error. */ ++ if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) ++ ndev->stats.rx_frame_errors++; ++ goto rx_processing_done; ++ } ++ ++ /* Process the incoming frame. */ ++ ndev->stats.rx_packets++; ++ pkt_len = fec16_to_cpu(bdp->cbd_datlen); ++ ndev->stats.rx_bytes += pkt_len; ++ ++ index = fec_enet_get_bd_index(bdp, &rxq->bd); ++ skb = rxq->rx_skbuff[index]; ++ ++ prefetch(skb->data - NET_IP_ALIGN); ++ dma_sync_single_for_cpu(&fep->pdev->dev, ++ fec32_to_cpu(bdp->cbd_bufaddr), ++ FEC_ENET_RX_FRSIZE - fep->rx_align, ++ DMA_FROM_DEVICE); ++ ++ pkt_len -= 6; ++ data = skb->data; ++#if !defined(CONFIG_M5272) ++ if (fep->quirks & FEC_QUIRK_HAS_RACC) ++ data += 2; ++#endif ++ if (data[12] ==0x88 && data[13] ==0xa4) { ++ len = len < pkt_len? len : pkt_len; ++ if (!need_swap) { ++ copy_to_user(buff, data, len); ++ } ++ else { ++ swap_buffer2(buff, data, len); ++ } ++ if (addr != NULL) { ++ struct sockaddr_ll *sll = (struct sockaddr_ll *)addr; ++ sll->sll_hatype = ndev->type; ++ sll->sll_ifindex = ndev->ifindex; ++ } ++ recv_len = len; ++ } ++ dma_sync_single_for_device(&fep->pdev->dev, ++ fec32_to_cpu(bdp->cbd_bufaddr), ++ FEC_ENET_RX_FRSIZE - fep->rx_align, ++ DMA_FROM_DEVICE); ++rx_processing_done: ++ /* Clear the status flags for this buffer */ ++ status &= ~BD_ENET_RX_STATS; ++ ++ /* Mark the buffer empty */ ++ status |= BD_ENET_RX_EMPTY; ++ ++ /* Make sure the updates to rest of the descriptor are ++ * performed before transferring ownership. ++ */ ++ wmb(); ++ bdp->cbd_sc = cpu_to_fec16(status); ++ ++ /* Update BD pointer to next entry */ ++ bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); ++ ++ /* Doing this here will keep the FEC running while we process ++ * incoming frames. On a heavily loaded network, we should be ++ * able to keep up at the expense of system resources. ++ */ ++ writel(0, rxq->bd.reg_desc_active); ++ if (recv_len) { ++ break; ++ } ++ } ++ rxq->bd.cur = bdp; ++ return recv_len; ++} ++ ++static int fec_ecat_fast_recv(struct net_device *ndev, void __user *buff, size_t len, struct sockaddr __user *addr, int *addr_len) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ int ret; ++ ++ if (!mutex_trylock(&fep->fast_ndev_lock)) { ++ return -EBUSY; ++ } ++ ++ ret = fec_ecat_recv_from_queue(ndev, buff, len, addr, addr_len); ++ ++ mutex_unlock(&fep->fast_ndev_lock); ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------- */ ++static int fec_get_mac(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ unsigned char *iap, tmpaddr[ETH_ALEN]; ++ int ret; ++ ++ /* ++ * try to get mac address in following order: ++ * ++ * 1) module parameter via kernel command line in form ++ * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 ++ */ ++ iap = macaddr; ++ ++ /* ++ * 2) from device tree data ++ */ ++ if (!is_valid_ether_addr(iap)) { ++ struct device_node *np = fep->pdev->dev.of_node; ++ if (np) { ++ ret = of_get_mac_address(np, tmpaddr); ++ if (!ret) ++ iap = tmpaddr; ++ else if (ret == -EPROBE_DEFER) ++ return ret; ++ } ++ } ++ ++ /* ++ * 3) from flash or fuse (via platform data) ++ */ ++ if (!is_valid_ether_addr(iap)) { ++#ifdef CONFIG_M5272 ++ if (FEC_FLASHMAC) ++ iap = (unsigned char *)FEC_FLASHMAC; ++#else ++ struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); ++ ++ if (pdata) ++ iap = (unsigned char *)&pdata->mac; ++#endif ++ } ++ ++ /* ++ * 4) FEC mac registers set by bootloader ++ */ ++ if (!is_valid_ether_addr(iap)) { ++ *((__be32 *) &tmpaddr[0]) = ++ cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); ++ *((__be16 *) &tmpaddr[4]) = ++ cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); ++ iap = &tmpaddr[0]; ++ } ++ ++ /* ++ * 5) random mac address ++ */ ++ if (!is_valid_ether_addr(iap)) { ++ /* Report it and use a random ethernet address instead */ ++ dev_err(&fep->pdev->dev, "Invalid MAC address: %pM\n", iap); ++ eth_hw_addr_random(ndev); ++ dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n", ++ ndev->dev_addr); ++ return 0; ++ } ++ ++ /* Adjust MAC if using macaddr */ ++ eth_hw_addr_gen(ndev, iap, iap == macaddr ? fep->dev_id : 0); ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* ++ * Phy section ++ */ ++static void fec_enet_adjust_link(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct phy_device *phy_dev = ndev->phydev; ++ int status_change = 0; ++ ++ /* ++ * If the netdev is down, or is going down, we're not interested ++ * in link state events, so just mark our idea of the link as down ++ * and ignore the event. ++ */ ++ if (!netif_running(ndev) || !netif_device_present(ndev)) { ++ fep->link = 0; ++ } else if (phy_dev->link) { ++ if (!fep->link) { ++ fep->link = phy_dev->link; ++ status_change = 1; ++ } ++ ++ if (fep->full_duplex != phy_dev->duplex) { ++ fep->full_duplex = phy_dev->duplex; ++ status_change = 1; ++ } ++ ++ if (phy_dev->speed != fep->speed) { ++ fep->speed = phy_dev->speed; ++ status_change = 1; ++ } ++ ++ switch (fep->speed) { ++ case SPEED_100: ++ fep->rx_tstamp_latency = fep->rx_delay_100; ++ fep->tx_tstamp_latency = fep->tx_delay_100; ++ break; ++ case SPEED_1000: ++ fep->rx_tstamp_latency = fep->rx_delay_1000; ++ fep->tx_tstamp_latency = fep->tx_delay_1000; ++ break; ++ default: ++ fep->rx_tstamp_latency = 0; ++ fep->tx_tstamp_latency = 0; ++ } ++ ++ /* if any of the above changed restart the FEC */ ++ if (status_change) { ++ mutex_lock(&fep->fast_ndev_lock); ++ fec_restart(ndev); ++ mutex_unlock(&fep->fast_ndev_lock); ++ } ++ } else { ++ if (fep->link) { ++ mutex_lock(&fep->fast_ndev_lock); ++ fec_stop(ndev); ++ mutex_unlock(&fep->fast_ndev_lock); ++ fep->link = phy_dev->link; ++ status_change = 1; ++ } ++ } ++ ++ if (status_change) ++ phy_print_status(phy_dev); ++} ++ ++static int fec_enet_mdio_wait(struct fec_enet_private *fep) ++{ ++ uint ievent; ++ int ret; ++ ++ ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent, ++ ievent & FEC_ENET_MII, 2, 30000); ++ ++ if (!ret) ++ writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); ++ ++ return ret; ++} ++ ++static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) ++{ ++ struct fec_enet_private *fep = bus->priv; ++ struct device *dev = &fep->pdev->dev; ++ int ret = 0, frame_start, frame_addr, frame_op; ++ bool is_c45 = !!(regnum & MII_ADDR_C45); ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret < 0) ++ return ret; ++ ++ if (is_c45) { ++ frame_start = FEC_MMFR_ST_C45; ++ ++ /* write address */ ++ frame_addr = (regnum >> 16); ++ writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | ++ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | ++ FEC_MMFR_TA | (regnum & 0xFFFF), ++ fep->hwp + FEC_MII_DATA); ++ ++ /* wait for end of transfer */ ++ ret = fec_enet_mdio_wait(fep); ++ if (ret) { ++ netdev_err(fep->netdev, "MDIO address write timeout\n"); ++ goto out; ++ } ++ ++ frame_op = FEC_MMFR_OP_READ_C45; ++ ++ } else { ++ /* C22 read */ ++ frame_op = FEC_MMFR_OP_READ; ++ frame_start = FEC_MMFR_ST; ++ frame_addr = regnum; ++ } ++ ++ /* start a read op */ ++ writel(frame_start | frame_op | ++ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | ++ FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); ++ ++ /* wait for end of transfer */ ++ ret = fec_enet_mdio_wait(fep); ++ if (ret) { ++ netdev_err(fep->netdev, "MDIO read timeout\n"); ++ goto out; ++ } ++ ++ ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); ++ ++out: ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); ++ ++ return ret; ++} ++ ++static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, ++ u16 value) ++{ ++ struct fec_enet_private *fep = bus->priv; ++ struct device *dev = &fep->pdev->dev; ++ int ret, frame_start, frame_addr; ++ bool is_c45 = !!(regnum & MII_ADDR_C45); ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret < 0) ++ return ret; ++ ++ if (is_c45) { ++ frame_start = FEC_MMFR_ST_C45; ++ ++ /* write address */ ++ frame_addr = (regnum >> 16); ++ writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | ++ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | ++ FEC_MMFR_TA | (regnum & 0xFFFF), ++ fep->hwp + FEC_MII_DATA); ++ ++ /* wait for end of transfer */ ++ ret = fec_enet_mdio_wait(fep); ++ if (ret) { ++ netdev_err(fep->netdev, "MDIO address write timeout\n"); ++ goto out; ++ } ++ } else { ++ /* C22 write */ ++ frame_start = FEC_MMFR_ST; ++ frame_addr = regnum; ++ } ++ ++ /* start a write op */ ++ writel(frame_start | FEC_MMFR_OP_WRITE | ++ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | ++ FEC_MMFR_TA | FEC_MMFR_DATA(value), ++ fep->hwp + FEC_MII_DATA); ++ ++ /* wait for end of transfer */ ++ ret = fec_enet_mdio_wait(fep); ++ if (ret) ++ netdev_err(fep->netdev, "MDIO write timeout\n"); ++ ++out: ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); ++ ++ return ret; ++} ++ ++static void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct phy_device *phy_dev = ndev->phydev; ++ ++ if (phy_dev) { ++ phy_reset_after_clk_enable(phy_dev); ++ } else if (fep->phy_node) { ++ /* ++ * If the PHY still is not bound to the MAC, but there is ++ * OF PHY node and a matching PHY device instance already, ++ * use the OF PHY node to obtain the PHY device instance, ++ * and then use that PHY device instance when triggering ++ * the PHY reset. ++ */ ++ phy_dev = of_phy_find_device(fep->phy_node); ++ phy_reset_after_clk_enable(phy_dev); ++ put_device(&phy_dev->mdio.dev); ++ } ++} ++ ++static int fec_enet_clk_enable(struct net_device *ndev, bool enable) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ int ret; ++ ++ if (enable) { ++ ret = clk_prepare_enable(fep->clk_enet_out); ++ if (ret) ++ return ret; ++ ++ if (fep->clk_ptp) { ++ mutex_lock(&fep->ptp_clk_mutex); ++ ret = clk_prepare_enable(fep->clk_ptp); ++ if (ret) { ++ mutex_unlock(&fep->ptp_clk_mutex); ++ goto failed_clk_ptp; ++ } else { ++ fep->ptp_clk_on = true; ++ } ++ mutex_unlock(&fep->ptp_clk_mutex); ++ } ++ ++ ret = clk_prepare_enable(fep->clk_ref); ++ if (ret) ++ goto failed_clk_ref; ++ ++ ret = clk_prepare_enable(fep->clk_2x_txclk); ++ if (ret) ++ goto failed_clk_2x_txclk; ++ ++ fec_enet_phy_reset_after_clk_enable(ndev); ++ } else { ++ clk_disable_unprepare(fep->clk_enet_out); ++ if (fep->clk_ptp) { ++ mutex_lock(&fep->ptp_clk_mutex); ++ clk_disable_unprepare(fep->clk_ptp); ++ fep->ptp_clk_on = false; ++ mutex_unlock(&fep->ptp_clk_mutex); ++ } ++ clk_disable_unprepare(fep->clk_ref); ++ clk_disable_unprepare(fep->clk_2x_txclk); ++ } ++ ++ return 0; ++ ++failed_clk_2x_txclk: ++ if (fep->clk_ref) ++ clk_disable_unprepare(fep->clk_ref); ++failed_clk_ref: ++ if (fep->clk_ptp) { ++ mutex_lock(&fep->ptp_clk_mutex); ++ clk_disable_unprepare(fep->clk_ptp); ++ fep->ptp_clk_on = false; ++ mutex_unlock(&fep->ptp_clk_mutex); ++ } ++failed_clk_ptp: ++ clk_disable_unprepare(fep->clk_enet_out); ++ ++ return ret; ++} ++ ++static int fec_enet_parse_rgmii_delay(struct fec_enet_private *fep, ++ struct device_node *np) ++{ ++ u32 rgmii_tx_delay, rgmii_rx_delay; ++ ++ /* For rgmii tx internal delay, valid values are 0ps and 2000ps */ ++ if (!of_property_read_u32(np, "tx-internal-delay-ps", &rgmii_tx_delay)) { ++ if (rgmii_tx_delay != 0 && rgmii_tx_delay != 2000) { ++ dev_err(&fep->pdev->dev, "The only allowed RGMII TX delay values are: 0ps, 2000ps"); ++ return -EINVAL; ++ } else if (rgmii_tx_delay == 2000) { ++ fep->rgmii_txc_dly = true; ++ } ++ } ++ ++ /* For rgmii rx internal delay, valid values are 0ps and 2000ps */ ++ if (!of_property_read_u32(np, "rx-internal-delay-ps", &rgmii_rx_delay)) { ++ if (rgmii_rx_delay != 0 && rgmii_rx_delay != 2000) { ++ dev_err(&fep->pdev->dev, "The only allowed RGMII RX delay values are: 0ps, 2000ps"); ++ return -EINVAL; ++ } else if (rgmii_rx_delay == 2000) { ++ fep->rgmii_rxc_dly = true; ++ } ++ } ++ ++ return 0; ++} ++ ++static int fec_restore_mii_bus(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ int ret; ++ ++ ret = pm_runtime_get_sync(&fep->pdev->dev); ++ if (ret < 0) ++ return ret; ++ ++ writel(0xffc00000, fep->hwp + FEC_IEVENT); ++ writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); ++ writel(FEC_ENET_MII, fep->hwp + FEC_IMASK); ++ writel(FEC_ENET_ETHEREN, fep->hwp + FEC_ECNTRL); ++ ++ pm_runtime_mark_last_busy(&fep->pdev->dev); ++ pm_runtime_put_autosuspend(&fep->pdev->dev); ++ ++ return 0; ++} ++ ++static int fec_enet_mii_probe(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct phy_device *phy_dev = NULL; ++ char mdio_bus_id[MII_BUS_ID_SIZE]; ++ char phy_name[MII_BUS_ID_SIZE + 3]; ++ int phy_id; ++ int dev_id = fep->dev_id; ++ ++ if (fep->phy_node) { ++ phy_dev = of_phy_connect(ndev, fep->phy_node, ++ &fec_enet_adjust_link, 0, ++ fep->phy_interface); ++ if (!phy_dev) { ++ netdev_err(ndev, "Unable to connect to phy\n"); ++ return -ENODEV; ++ } ++ } else { ++ /* check for attached phy */ ++ for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { ++ if (!mdiobus_is_registered_device(fep->mii_bus, phy_id)) ++ continue; ++ if (dev_id--) ++ continue; ++ strlcpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); ++ break; ++ } ++ ++ if (phy_id >= PHY_MAX_ADDR) { ++ netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); ++ strlcpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); ++ phy_id = 0; ++ } ++ ++ snprintf(phy_name, sizeof(phy_name), ++ PHY_ID_FMT, mdio_bus_id, phy_id); ++ phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, ++ fep->phy_interface); ++ } ++ ++ if (IS_ERR(phy_dev)) { ++ netdev_err(ndev, "could not attach to PHY\n"); ++ return PTR_ERR(phy_dev); ++ } ++ ++ /* mask with MAC supported features */ ++ if (fep->quirks & FEC_QUIRK_HAS_GBIT) { ++ phy_set_max_speed(phy_dev, 1000); ++ phy_remove_link_mode(phy_dev, ++ ETHTOOL_LINK_MODE_1000baseT_Half_BIT); ++#if !defined(CONFIG_M5272) ++ phy_support_sym_pause(phy_dev); ++#endif ++ } ++ else ++ phy_set_max_speed(phy_dev, 100); ++ ++ fep->link = 0; ++ fep->full_duplex = 0; ++ ++ phy_dev->mac_managed_pm = 1; ++ ++ phy_attached_info(phy_dev); ++ ++ return 0; ++} ++ ++static int fec_enet_mii_init(struct platform_device *pdev) ++{ ++ static struct mii_bus *fec0_mii_bus; ++ static bool *fec_mii_bus_share; ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ bool suppress_preamble = false; ++ struct device_node *node; ++ int err = -ENXIO; ++ u32 mii_speed, holdtime; ++ u32 bus_freq; ++ ++ /* ++ * The i.MX28 dual fec interfaces are not equal. ++ * Here are the differences: ++ * ++ * - fec0 supports MII & RMII modes while fec1 only supports RMII ++ * - fec0 acts as the 1588 time master while fec1 is slave ++ * - external phys can only be configured by fec0 ++ * ++ * That is to say fec1 can not work independently. It only works ++ * when fec0 is working. The reason behind this design is that the ++ * second interface is added primarily for Switch mode. ++ * ++ * Because of the last point above, both phys are attached on fec0 ++ * mdio interface in board design, and need to be configured by ++ * fec0 mii_bus. ++ */ ++ if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { ++ /* fec1 uses fec0 mii_bus */ ++ if (mii_cnt && fec0_mii_bus) { ++ fep->mii_bus = fec0_mii_bus; ++ *fec_mii_bus_share = true; ++ mii_cnt++; ++ return 0; ++ } ++ return -ENOENT; ++ } ++ ++ bus_freq = 2500000; /* 2.5MHz by default */ ++ node = of_get_child_by_name(pdev->dev.of_node, "mdio"); ++ if (node) { ++ of_property_read_u32(node, "clock-frequency", &bus_freq); ++ suppress_preamble = of_property_read_bool(node, ++ "suppress-preamble"); ++ } ++ ++ /* ++ * Set MII speed (= clk_get_rate() / 2 * phy_speed) ++ * ++ * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while ++ * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 ++ * Reference Manual has an error on this, and gets fixed on i.MX6Q ++ * document. ++ */ ++ mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2); ++ if (fep->quirks & FEC_QUIRK_ENET_MAC) ++ mii_speed--; ++ if (mii_speed > 63) { ++ dev_err(&pdev->dev, ++ "fec clock (%lu) too fast to get right mii speed\n", ++ clk_get_rate(fep->clk_ipg)); ++ err = -EINVAL; ++ goto err_out; ++ } ++ ++ /* ++ * The i.MX28 and i.MX6 types have another filed in the MSCR (aka ++ * MII_SPEED) register that defines the MDIO output hold time. Earlier ++ * versions are RAZ there, so just ignore the difference and write the ++ * register always. ++ * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. ++ * HOLDTIME + 1 is the number of clk cycles the fec is holding the ++ * output. ++ * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). ++ * Given that ceil(clkrate / 5000000) <= 64, the calculation for ++ * holdtime cannot result in a value greater than 3. ++ */ ++ holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; ++ ++ fep->phy_speed = mii_speed << 1 | holdtime << 8; ++ ++ if (suppress_preamble) ++ fep->phy_speed |= BIT(7); ++ ++ if (fep->quirks & FEC_QUIRK_CLEAR_SETUP_MII) { ++ /* Clear MMFR to avoid to generate MII event by writing MSCR. ++ * MII event generation condition: ++ * - writing MSCR: ++ * - mmfr[31:0]_not_zero & mscr[7:0]_is_zero & ++ * mscr_reg_data_in[7:0] != 0 ++ * - writing MMFR: ++ * - mscr[7:0]_not_zero ++ */ ++ writel(0, fep->hwp + FEC_MII_DATA); ++ } ++ ++ writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); ++ ++ /* Clear any pending transaction complete indication */ ++ writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); ++ ++ fep->mii_bus = mdiobus_alloc(); ++ if (fep->mii_bus == NULL) { ++ err = -ENOMEM; ++ goto err_out; ++ } ++ ++ fep->mii_bus->name = "fec_enet_mii_bus"; ++ fep->mii_bus->read = fec_enet_mdio_read; ++ fep->mii_bus->write = fec_enet_mdio_write; ++ snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", ++ pdev->name, fep->dev_id + 1); ++ fep->mii_bus->priv = fep; ++ fep->mii_bus->parent = &pdev->dev; ++ ++ err = of_mdiobus_register(fep->mii_bus, node); ++ if (err) ++ goto err_out_free_mdiobus; ++ of_node_put(node); ++ ++ mii_cnt++; ++ ++ /* save fec0 mii_bus */ ++ if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) { ++ fec0_mii_bus = fep->mii_bus; ++ fec_mii_bus_share = &fep->mii_bus_share; ++ } ++ ++ return 0; ++ ++err_out_free_mdiobus: ++ mdiobus_free(fep->mii_bus); ++err_out: ++ of_node_put(node); ++ return err; ++} ++ ++static void fec_enet_mii_remove(struct fec_enet_private *fep) ++{ ++ if (--mii_cnt == 0) { ++ mdiobus_unregister(fep->mii_bus); ++ mdiobus_free(fep->mii_bus); ++ } ++} ++ ++static void fec_enet_get_drvinfo(struct net_device *ndev, ++ struct ethtool_drvinfo *info) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ ++ strlcpy(info->driver, fep->pdev->dev.driver->name, ++ sizeof(info->driver)); ++ strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); ++} ++ ++static int fec_enet_get_regs_len(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct resource *r; ++ int s = 0; ++ ++ r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0); ++ if (r) ++ s = resource_size(r); ++ ++ return s; ++} ++ ++/* List of registers that can be safety be read to dump them with ethtool */ ++#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ ++ defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ ++ defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) ++static __u32 fec_enet_register_version = 2; ++static u32 fec_enet_register_offset[] = { ++ FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, ++ FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, ++ FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1, ++ FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH, ++ FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, ++ FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1, ++ FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2, ++ FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0, ++ FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, ++ FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2, ++ FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1, ++ FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME, ++ RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, ++ RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, ++ RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, ++ RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, ++ RMON_T_P_GTE2048, RMON_T_OCTETS, ++ IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, ++ IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, ++ IEEE_T_FDXFC, IEEE_T_OCTETS_OK, ++ RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, ++ RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, ++ RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, ++ RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, ++ RMON_R_P_GTE2048, RMON_R_OCTETS, ++ IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, ++ IEEE_R_FDXFC, IEEE_R_OCTETS_OK ++}; ++#else ++static __u32 fec_enet_register_version = 1; ++static u32 fec_enet_register_offset[] = { ++ FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0, ++ FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0, ++ FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED, ++ FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL, ++ FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, ++ FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0, ++ FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0, ++ FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0, ++ FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2 ++}; ++#endif ++ ++static void fec_enet_get_regs(struct net_device *ndev, ++ struct ethtool_regs *regs, void *regbuf) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ u32 __iomem *theregs = (u32 __iomem *)fep->hwp; ++ struct device *dev = &fep->pdev->dev; ++ u32 *buf = (u32 *)regbuf; ++ u32 i, off; ++ int ret; ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret < 0) ++ return; ++ ++ regs->version = fec_enet_register_version; ++ ++ memset(buf, 0, regs->len); ++ ++ for (i = 0; i < ARRAY_SIZE(fec_enet_register_offset); i++) { ++ off = fec_enet_register_offset[i]; ++ ++ if ((off == FEC_R_BOUND || off == FEC_R_FSTART) && ++ !(fep->quirks & FEC_QUIRK_HAS_FRREG)) ++ continue; ++ ++ off >>= 2; ++ buf[off] = readl(&theregs[off]); ++ } ++ ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); ++} ++ ++ ++#if !defined(CONFIG_M5272) ++ ++static void fec_enet_get_pauseparam(struct net_device *ndev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ ++ pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; ++ pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; ++ pause->rx_pause = pause->tx_pause; ++} ++ ++static int fec_enet_set_pauseparam(struct net_device *ndev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ ++ if (!ndev->phydev) ++ return -ENODEV; ++ ++ if (pause->tx_pause != pause->rx_pause) { ++ netdev_info(ndev, ++ "hardware only support enable/disable both tx and rx"); ++ return -EINVAL; ++ } ++ ++ fep->pause_flag = 0; ++ ++ /* tx pause must be same as rx pause */ ++ fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; ++ fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; ++ ++ phy_set_sym_pause(ndev->phydev, pause->rx_pause, pause->tx_pause, ++ pause->autoneg); ++ ++ if (pause->autoneg) { ++ if (netif_running(ndev)) ++ fec_stop(ndev); ++ phy_start_aneg(ndev->phydev); ++ } ++ if (netif_running(ndev)) { ++ mutex_lock(&fep->fast_ndev_lock); ++ ++ netif_tx_lock_bh(ndev); ++ //netif_tx_wake_all_queues(ndev); ++ ++ mutex_unlock(&fep->fast_ndev_lock); ++ } ++ ++ return 0; ++} ++ ++static const struct fec_stat { ++ char name[ETH_GSTRING_LEN]; ++ u16 offset; ++} fec_stats[] = { ++ /* RMON TX */ ++ { "tx_dropped", RMON_T_DROP }, ++ { "tx_packets", RMON_T_PACKETS }, ++ { "tx_broadcast", RMON_T_BC_PKT }, ++ { "tx_multicast", RMON_T_MC_PKT }, ++ { "tx_crc_errors", RMON_T_CRC_ALIGN }, ++ { "tx_undersize", RMON_T_UNDERSIZE }, ++ { "tx_oversize", RMON_T_OVERSIZE }, ++ { "tx_fragment", RMON_T_FRAG }, ++ { "tx_jabber", RMON_T_JAB }, ++ { "tx_collision", RMON_T_COL }, ++ { "tx_64byte", RMON_T_P64 }, ++ { "tx_65to127byte", RMON_T_P65TO127 }, ++ { "tx_128to255byte", RMON_T_P128TO255 }, ++ { "tx_256to511byte", RMON_T_P256TO511 }, ++ { "tx_512to1023byte", RMON_T_P512TO1023 }, ++ { "tx_1024to2047byte", RMON_T_P1024TO2047 }, ++ { "tx_GTE2048byte", RMON_T_P_GTE2048 }, ++ { "tx_octets", RMON_T_OCTETS }, ++ ++ /* IEEE TX */ ++ { "IEEE_tx_drop", IEEE_T_DROP }, ++ { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, ++ { "IEEE_tx_1col", IEEE_T_1COL }, ++ { "IEEE_tx_mcol", IEEE_T_MCOL }, ++ { "IEEE_tx_def", IEEE_T_DEF }, ++ { "IEEE_tx_lcol", IEEE_T_LCOL }, ++ { "IEEE_tx_excol", IEEE_T_EXCOL }, ++ { "IEEE_tx_macerr", IEEE_T_MACERR }, ++ { "IEEE_tx_cserr", IEEE_T_CSERR }, ++ { "IEEE_tx_sqe", IEEE_T_SQE }, ++ { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, ++ { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, ++ ++ /* RMON RX */ ++ { "rx_packets", RMON_R_PACKETS }, ++ { "rx_broadcast", RMON_R_BC_PKT }, ++ { "rx_multicast", RMON_R_MC_PKT }, ++ { "rx_crc_errors", RMON_R_CRC_ALIGN }, ++ { "rx_undersize", RMON_R_UNDERSIZE }, ++ { "rx_oversize", RMON_R_OVERSIZE }, ++ { "rx_fragment", RMON_R_FRAG }, ++ { "rx_jabber", RMON_R_JAB }, ++ { "rx_64byte", RMON_R_P64 }, ++ { "rx_65to127byte", RMON_R_P65TO127 }, ++ { "rx_128to255byte", RMON_R_P128TO255 }, ++ { "rx_256to511byte", RMON_R_P256TO511 }, ++ { "rx_512to1023byte", RMON_R_P512TO1023 }, ++ { "rx_1024to2047byte", RMON_R_P1024TO2047 }, ++ { "rx_GTE2048byte", RMON_R_P_GTE2048 }, ++ { "rx_octets", RMON_R_OCTETS }, ++ ++ /* IEEE RX */ ++ { "IEEE_rx_drop", IEEE_R_DROP }, ++ { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, ++ { "IEEE_rx_crc", IEEE_R_CRC }, ++ { "IEEE_rx_align", IEEE_R_ALIGN }, ++ { "IEEE_rx_macerr", IEEE_R_MACERR }, ++ { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, ++ { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, ++}; ++ ++#define FEC_STATS_SIZE (ARRAY_SIZE(fec_stats) * sizeof(u64)) ++ ++static void fec_enet_update_ethtool_stats(struct net_device *dev) ++{ ++ struct fec_enet_private *fep = netdev_priv(dev); ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(fec_stats); i++) ++ fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset); ++} ++ ++static void fec_enet_get_ethtool_stats(struct net_device *dev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ struct fec_enet_private *fep = netdev_priv(dev); ++ ++ if (netif_running(dev)) ++ fec_enet_update_ethtool_stats(dev); ++ ++ memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE); ++} ++ ++static void fec_enet_get_strings(struct net_device *netdev, ++ u32 stringset, u8 *data) ++{ ++ int i; ++ switch (stringset) { ++ case ETH_SS_STATS: ++ for (i = 0; i < ARRAY_SIZE(fec_stats); i++) ++ memcpy(data + i * ETH_GSTRING_LEN, ++ fec_stats[i].name, ETH_GSTRING_LEN); ++ break; ++ case ETH_SS_TEST: ++ net_selftest_get_strings(data); ++ break; ++ } ++} ++ ++static int fec_enet_get_sset_count(struct net_device *dev, int sset) ++{ ++ switch (sset) { ++ case ETH_SS_STATS: ++ return ARRAY_SIZE(fec_stats); ++ case ETH_SS_TEST: ++ return net_selftest_get_count(); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static void fec_enet_clear_ethtool_stats(struct net_device *dev) ++{ ++ struct fec_enet_private *fep = netdev_priv(dev); ++ int i; ++ ++ /* Disable MIB statistics counters */ ++ writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT); ++ ++ for (i = 0; i < ARRAY_SIZE(fec_stats); i++) ++ writel(0, fep->hwp + fec_stats[i].offset); ++ ++ /* Don't disable MIB statistics counters */ ++ writel(0, fep->hwp + FEC_MIB_CTRLSTAT); ++} ++ ++#else /* !defined(CONFIG_M5272) */ ++#define FEC_STATS_SIZE 0 ++static inline void fec_enet_update_ethtool_stats(struct net_device *dev) ++{ ++} ++ ++static inline void fec_enet_clear_ethtool_stats(struct net_device *dev) ++{ ++} ++#endif /* !defined(CONFIG_M5272) */ ++ ++/* ITR clock source is enet system clock (clk_ahb). ++ * TCTT unit is cycle_ns * 64 cycle ++ * So, the ICTT value = X us / (cycle_ns * 64) ++ */ ++static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ ++ return us * (fep->itr_clk_rate / 64000) / 1000; ++} ++ ++ ++/* LPI Sleep Ts count base on tx clk (clk_ref). ++ * The lpi sleep cnt value = X us / (cycle_ns). ++ */ ++static int fec_enet_us_to_tx_cycle(struct net_device *ndev, int us) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ ++ return us * (fep->clk_ref_rate / 1000) / 1000; ++} ++ ++static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct ethtool_eee *p = &fep->eee; ++ unsigned int sleep_cycle, wake_cycle; ++ int ret = 0; ++ ++ if (enable) { ++ ret = phy_init_eee(ndev->phydev, 0); ++ if (ret) ++ return ret; ++ ++ sleep_cycle = fec_enet_us_to_tx_cycle(ndev, p->tx_lpi_timer); ++ wake_cycle = sleep_cycle; ++ } else { ++ sleep_cycle = 0; ++ wake_cycle = 0; ++ } ++ ++ p->tx_lpi_enabled = enable; ++ p->eee_enabled = enable; ++ p->eee_active = enable; ++ ++ writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP); ++ writel(wake_cycle, fep->hwp + FEC_LPI_WAKE); ++ ++ return 0; ++} ++ ++static int ++fec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct ethtool_eee *p = &fep->eee; ++ ++ if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) ++ return -EOPNOTSUPP; ++ ++ if (!netif_running(ndev)) ++ return -ENETDOWN; ++ ++ edata->eee_enabled = p->eee_enabled; ++ edata->eee_active = p->eee_active; ++ edata->tx_lpi_timer = p->tx_lpi_timer; ++ edata->tx_lpi_enabled = p->tx_lpi_enabled; ++ ++ return phy_ethtool_get_eee(ndev->phydev, edata); ++} ++ ++static int ++fec_enet_set_eee(struct net_device *ndev, struct ethtool_eee *edata) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct ethtool_eee *p = &fep->eee; ++ int ret = 0; ++ ++ if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) ++ return -EOPNOTSUPP; ++ ++ if (!netif_running(ndev)) ++ return -ENETDOWN; ++ ++ p->tx_lpi_timer = edata->tx_lpi_timer; ++ ++ if (!edata->eee_enabled || !edata->tx_lpi_enabled || ++ !edata->tx_lpi_timer) ++ ret = fec_enet_eee_mode_set(ndev, false); ++ else ++ ret = fec_enet_eee_mode_set(ndev, true); ++ ++ if (ret) ++ return ret; ++ ++ return phy_ethtool_set_eee(ndev->phydev, edata); ++} ++ ++static const struct ethtool_ops fec_enet_ethtool_ops = { ++ .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ++ ETHTOOL_COALESCE_MAX_FRAMES, ++ .get_drvinfo = fec_enet_get_drvinfo, ++ .get_regs_len = fec_enet_get_regs_len, ++ .get_regs = fec_enet_get_regs, ++ .nway_reset = phy_ethtool_nway_reset, ++ .get_link = ethtool_op_get_link, ++#ifndef CONFIG_M5272 ++ .get_strings = fec_enet_get_strings, ++ .get_ethtool_stats = fec_enet_get_ethtool_stats, ++ .get_sset_count = fec_enet_get_sset_count, ++#endif ++ .get_eee = fec_enet_get_eee, ++ .set_eee = fec_enet_set_eee, ++ .get_link_ksettings = phy_ethtool_get_link_ksettings, ++ .set_link_ksettings = phy_ethtool_set_link_ksettings, ++ .self_test = net_selftest, ++}; ++ ++static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct phy_device *phydev = ndev->phydev; ++ ++ if (!netif_running(ndev)) ++ return -EINVAL; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_mii_ioctl(phydev, rq, cmd); ++} ++ ++static void fec_enet_free_buffers(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ unsigned int i; ++ struct sk_buff *skb; ++ struct bufdesc *bdp; ++ struct fec_enet_priv_tx_q *txq = fep->tx_queue; ++ struct fec_enet_priv_rx_q *rxq = fep->rx_queue; ++ unsigned int q; ++ ++ bdp = rxq->bd.base; ++ for (i = 0; i < rxq->bd.ring_size; i++) { ++ skb = rxq->rx_skbuff[i]; ++ rxq->rx_skbuff[i] = NULL; ++ if (skb) { ++ dma_unmap_single(&fep->pdev->dev, ++ fec32_to_cpu(bdp->cbd_bufaddr), ++ FEC_ENET_RX_FRSIZE - fep->rx_align, ++ DMA_FROM_DEVICE); ++ dev_kfree_skb(skb); ++ } ++ } ++ ++ for (i = 0; i < txq->bd.ring_size; i++) { ++ skb = txq->tx_skbuff[i]; ++ txq->tx_skbuff[i] = NULL; ++ if (skb) { ++ dma_unmap_single(&fep->pdev->dev, ++ fec32_to_cpu(bdp->cbd_bufaddr), ++ FEC_ENET_RX_FRSIZE - fep->rx_align, ++ DMA_FROM_DEVICE); ++ dev_kfree_skb(skb); ++ } ++ } ++} ++ ++static void fec_enet_free_queue(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ ++ if (fep->rx_queue) ++ kfree(fep->rx_queue); ++ if (fep->tx_queue) ++ kfree(fep->tx_queue); ++} ++ ++static int fec_enet_alloc_queue(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ int ret = 0; ++ ++ fep->tx_queue = kzalloc(sizeof(*fep->tx_queue), GFP_KERNEL); ++ if (!fep->tx_queue) { ++ ret = -ENOMEM; ++ goto alloc_failed; ++ } ++ ++ fep->tx_queue->bd.ring_size = FEC_TX_RING_SIZE; ++ fep->total_tx_ring_size += fep->tx_queue->bd.ring_size; ++ ++ ++ fep->rx_queue = kzalloc(sizeof(*fep->rx_queue), GFP_KERNEL); ++ if (!fep->rx_queue) { ++ ret = -ENOMEM; ++ goto alloc_failed; ++ } ++ ++ fep->rx_queue->bd.ring_size = FEC_RX_RING_SIZE; ++ fep->total_rx_ring_size += fep->rx_queue->bd.ring_size; ++ return ret; ++ ++alloc_failed: ++ fec_enet_free_queue(ndev); ++ return ret; ++} ++ ++static int fec_enet_alloc_buffers(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ unsigned int i; ++ struct sk_buff *skb; ++ struct bufdesc *bdp; ++ int off; ++ struct fec_enet_priv_rx_q *rxq; ++ struct fec_enet_priv_tx_q *txq; ++ ++ rxq = fep->rx_queue; ++ txq = fep->tx_queue; ++ bdp = rxq->bd.base; ++ for (i = 0; i < rxq->bd.ring_size; i++) { ++ skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); ++ if (!skb) ++ goto err_alloc; ++ ++ if ((off = ((unsigned long)skb->data) & fep->rx_align) > 0) ++ skb_reserve(skb, fep->rx_align + 1 - off); ++ ++ bdp->cbd_bufaddr = cpu_to_fec32(dma_map_single(&fep->pdev->dev, skb->data, FEC_ENET_RX_FRSIZE - fep->rx_align, DMA_FROM_DEVICE)); ++ if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) { ++ dev_kfree_skb(skb); ++ goto err_alloc; ++ } ++ ++ rxq->rx_skbuff[i] = skb; ++ bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); ++ ++ bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); ++ } ++ ++ /* Set the last buffer to wrap. */ ++ bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); ++ bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); ++ ++ bdp = txq->bd.base; ++ txq->bd.cur = bdp; ++ ++ for (i = 0; i < txq->bd.ring_size; i++) { ++ bdp->cbd_sc = cpu_to_fec16(0); ++ skb = netdev_alloc_skb(ndev, FEC_ENET_TX_FRSIZE); ++ if (!skb) ++ goto err_alloc; ++ ++ if ((off = ((unsigned long)skb->data) & fep->tx_align) > 0) ++ skb_reserve(skb, fep->tx_align + 1 - off); ++ ++ bdp->cbd_bufaddr = cpu_to_fec32(dma_map_single(&fep->pdev->dev, skb->data, FEC_ENET_TX_FRSIZE - fep->tx_align, DMA_TO_DEVICE)); ++ if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) { ++ goto err_alloc; ++ } ++ txq->tx_skbuff[i] = skb; ++ bdp = fec_enet_get_nextdesc(bdp, &txq->bd); ++ } ++ ++ /* Set the last buffer to wrap. */ ++ bdp = fec_enet_get_prevdesc(bdp, &txq->bd); ++ bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); ++ txq->dirty_tx = bdp; ++ return 0; ++ ++ err_alloc: ++ fec_enet_free_buffers(ndev); ++ return -ENOMEM; ++} ++ ++static int ++fec_enet_open(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ int ret; ++ bool reset_again; ++ int i; ++ ++ ret = pm_runtime_resume_and_get(&fep->pdev->dev); ++ if (ret < 0) ++ return ret; ++ ++ pinctrl_pm_select_default_state(&fep->pdev->dev); ++ ++ /* During the first fec_enet_open call the PHY isn't probed at this ++ * point. Therefore the phy_reset_after_clk_enable() call within ++ * fec_enet_clk_enable() fails. As we need this reset in order to be ++ * sure the PHY is working correctly we check if we need to reset again ++ * later when the PHY is probed ++ */ ++ if (ndev->phydev && ndev->phydev->drv) ++ reset_again = false; ++ else ++ reset_again = true; ++ ++ /* I should reset the ring buffers here, but I don't yet know ++ * a simple way to do that. ++ */ ++ ++ ret = fec_enet_alloc_buffers(ndev); ++ if (ret) ++ goto err_enet_alloc; ++ ++ /* Init MAC prior to mii bus probe */ ++ fec_restart(ndev); ++ ++ /* Call phy_reset_after_clk_enable() again if it failed during ++ * phy_reset_after_clk_enable() before because the PHY wasn't probed. ++ */ ++ if (reset_again) ++ fec_enet_phy_reset_after_clk_enable(ndev); ++ ++ /* Probe and connect to PHY when open the interface */ ++ ret = fec_enet_mii_probe(ndev); ++ if (ret) ++ goto err_enet_mii_probe; ++ ++ if (fep->quirks & FEC_QUIRK_ERR006687) ++ imx6q_cpuidle_fec_irqs_used(); ++ if (fep->quirks & FEC_QUIRK_HAS_PMQOS) ++ cpu_latency_qos_add_request(&fep->pm_qos_req, 0); ++ ++ phy_start(ndev->phydev); ++ mutex_unlock(&fep->fast_ndev_lock); ++ return 0; ++ ++err_enet_mii_probe: ++ fec_enet_free_buffers(ndev); ++err_enet_alloc: ++ pm_runtime_mark_last_busy(&fep->pdev->dev); ++ pm_runtime_put_autosuspend(&fep->pdev->dev); ++ if (!fep->mii_bus_share) ++ pinctrl_pm_select_sleep_state(&fep->pdev->dev); ++ ++ return ret; ++} ++ ++static int ++fec_enet_close(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ ++ phy_stop(ndev->phydev); ++ ++ if (netif_device_present(ndev)) { ++ netif_tx_disable(ndev); ++ fec_stop(ndev); ++ } ++ ++ phy_disconnect(ndev->phydev); ++ ndev->phydev = NULL; ++ ++ if (fep->quirks & FEC_QUIRK_ERR006687) ++ imx6q_cpuidle_fec_irqs_unused(); ++ ++ fec_enet_update_ethtool_stats(ndev); ++ ++ if (!fep->mii_bus_share) ++ pinctrl_pm_select_sleep_state(&fep->pdev->dev); ++ pm_runtime_mark_last_busy(&fep->pdev->dev); ++ pm_runtime_put_autosuspend(&fep->pdev->dev); ++ ++ fec_enet_free_buffers(ndev); ++ mutex_lock(&fep->fast_ndev_lock); ++ return 0; ++} ++ ++/* Set or clear the multicast filter for this adaptor. ++ * Skeleton taken from sunlance driver. ++ * The CPM Ethernet implementation allows Multicast as well as individual ++ * MAC address filtering. Some of the drivers check to make sure it is ++ * a group multicast address, and discard those that are not. I guess I ++ * will do the same for now, but just remove the test if you want ++ * individual filtering as well (do the upper net layers want or support ++ * this kind of feature?). ++ */ ++ ++#define FEC_HASH_BITS 6 /* #bits in hash */ ++ ++static void set_multicast_list(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct netdev_hw_addr *ha; ++ unsigned int crc, tmp; ++ unsigned char hash; ++ unsigned int hash_high = 0, hash_low = 0; ++ ++ if (ndev->flags & IFF_PROMISC) { ++ tmp = readl(fep->hwp + FEC_R_CNTRL); ++ tmp |= 0x8; ++ writel(tmp, fep->hwp + FEC_R_CNTRL); ++ return; ++ } ++ ++ tmp = readl(fep->hwp + FEC_R_CNTRL); ++ tmp &= ~0x8; ++ writel(tmp, fep->hwp + FEC_R_CNTRL); ++ ++ if (ndev->flags & IFF_ALLMULTI) { ++ /* Catch all multicast addresses, so set the ++ * filter to all 1's ++ */ ++ writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); ++ writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); ++ ++ return; ++ } ++ ++ /* Add the addresses in hash register */ ++ netdev_for_each_mc_addr(ha, ndev) { ++ /* calculate crc32 value of mac address */ ++ crc = ether_crc_le(ndev->addr_len, ha->addr); ++ ++ /* only upper 6 bits (FEC_HASH_BITS) are used ++ * which point to specific bit in the hash registers ++ */ ++ hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f; ++ ++ if (hash > 31) ++ hash_high |= 1 << (hash - 32); ++ else ++ hash_low |= 1 << hash; ++ } ++ ++ writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); ++ writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW); ++} ++ ++/* Set a MAC change in hardware. */ ++static int ++fec_set_mac_address(struct net_device *ndev, void *p) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct sockaddr *addr = p; ++ ++ if (addr) { ++ if (!is_valid_ether_addr(addr->sa_data)) ++ return -EADDRNOTAVAIL; ++ memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); ++ } ++ ++ /* Add netif status check here to avoid system hang in below case: ++ * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; ++ * After ethx down, fec all clocks are gated off and then register ++ * access causes system hang. ++ */ ++ if (!netif_running(ndev)) ++ return 0; ++ ++ writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | ++ (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), ++ fep->hwp + FEC_ADDR_LOW); ++ writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), ++ fep->hwp + FEC_ADDR_HIGH); ++ return 0; ++} ++ ++static inline void fec_enet_set_netdev_features(struct net_device *netdev, ++ netdev_features_t features) ++{ ++ struct fec_enet_private *fep = netdev_priv(netdev); ++ netdev_features_t changed = features ^ netdev->features; ++ ++ netdev->features = features; ++ ++ /* Receive checksum has been changed */ ++ if (changed & NETIF_F_RXCSUM) { ++ if (features & NETIF_F_RXCSUM) ++ fep->csum_flags |= FLAG_RX_CSUM_ENABLED; ++ else ++ fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; ++ } ++} ++ ++static int fec_set_features(struct net_device *netdev, ++ netdev_features_t features) ++{ ++ struct fec_enet_private *fep = netdev_priv(netdev); ++ netdev_features_t changed = features ^ netdev->features; ++ mutex_lock(&fep->fast_ndev_lock); ++ ++ if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { ++ fec_stop(netdev); ++ fec_enet_set_netdev_features(netdev, features); ++ fec_restart(netdev); ++ } else { ++ fec_enet_set_netdev_features(netdev, features); ++ } ++ ++ mutex_unlock(&fep->fast_ndev_lock); ++ return 0; ++} ++ ++static netdev_tx_t ++fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) ++{ ++ ++ return NETDEV_TX_OK; ++} ++ ++static u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, ++ struct net_device *sb_dev) ++{ ++ return 0; ++} ++ ++static const struct net_device_ops fec_netdev_ops = { ++ .ndo_open = fec_enet_open, ++ .ndo_stop = fec_enet_close, ++ .ndo_start_xmit = fec_enet_start_xmit, ++ .ndo_select_queue = fec_enet_select_queue, ++ .ndo_fast_xmit = fec_ecat_fast_xmit, ++ .ndo_fast_recv = fec_ecat_fast_recv, ++ .ndo_set_rx_mode = set_multicast_list, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_tx_timeout = fec_timeout, ++ .ndo_set_mac_address = fec_set_mac_address, ++ .ndo_eth_ioctl = fec_enet_ioctl, ++ .ndo_set_features = fec_set_features, ++}; ++ ++static int fec_enet_init(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct bufdesc *cbd_base; ++ dma_addr_t bd_dma; ++ int bd_size; ++ unsigned int i; ++ unsigned dsize = sizeof(struct bufdesc); ++ unsigned dsize_log2 = __fls(dsize); ++ int ret; ++ unsigned char *pm = NULL; ++ ++ WARN_ON(dsize != (1 << dsize_log2)); ++#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) ++ fep->rx_align = 0xf; ++ fep->tx_align = 0xf; ++#else ++ fep->rx_align = 0x3; ++ fep->tx_align = 0x3; ++#endif ++ ++ /* Check mask of the streaming and coherent API */ ++ ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32)); ++ if (ret < 0) { ++ dev_warn(&fep->pdev->dev, "No suitable DMA available\n"); ++ return ret; ++ } ++ ++ ret = fec_enet_alloc_queue(ndev); ++ if (ret) ++ return ret; ++ ++ bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize; ++ ++ /* Allocate memory for buffer descriptors. */ ++ cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, ++ GFP_KERNEL); ++ if (!cbd_base) { ++ ret = -ENOMEM; ++ goto free_queue_mem; ++ } ++ ++ /* Get the Ethernet address */ ++ ret = fec_get_mac(ndev); ++ if (ret) ++ goto free_queue_mem; ++ ++ /* make sure MAC we just acquired is programmed into the hw */ ++ fec_set_mac_address(ndev, NULL); ++ ++ /* Set receive and transmit descriptor base. */ ++ struct fec_enet_priv_rx_q *rxq = fep->rx_queue; ++ unsigned size; ++ ++ size = dsize * rxq->bd.ring_size; ++ rxq->bd.qid = i; ++ rxq->bd.base = cbd_base; ++ rxq->bd.cur = cbd_base; ++ rxq->bd.dma = bd_dma; ++ rxq->bd.dsize = dsize; ++ rxq->bd.dsize_log2 = dsize_log2; ++ rxq->bd.reg_desc_active = fep->hwp + FEC_R_DES_ACTIVE_0; ++ bd_dma += size; ++ cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); ++ rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); ++ ++ struct fec_enet_priv_tx_q *txq = fep->tx_queue; ++ size = dsize * txq->bd.ring_size; ++ txq->bd.qid = i; ++ txq->bd.base = cbd_base; ++ txq->bd.cur = cbd_base; ++ txq->bd.dma = bd_dma; ++ txq->bd.dsize = dsize; ++ txq->bd.dsize_log2 = dsize_log2; ++ txq->bd.reg_desc_active = fep->hwp + FEC_X_DES_ACTIVE_0; ++ bd_dma += size; ++ cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); ++ txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); ++ ++ fep->netdev = ndev; ++ ++ /* The FEC Ethernet specific entries in the device structure */ ++ ndev->watchdog_timeo = TX_TIMEOUT; ++ ndev->netdev_ops = &fec_netdev_ops; ++ ndev->ethtool_ops = &fec_enet_ethtool_ops; ++ ++ pm = ndev->dev_addr; ++ writel(0, fep->hwp + FEC_IMASK); ++ ++ if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { ++ fep->tx_align = 0; ++ fep->rx_align = 0x3f; ++ } ++ ++ ndev->hw_features = ndev->features; ++ ++ fec_restart(ndev); ++ ++ if (fep->quirks & FEC_QUIRK_MIB_CLEAR) ++ fec_enet_clear_ethtool_stats(ndev); ++ else ++ fec_enet_update_ethtool_stats(ndev); ++ ++ return 0; ++ ++free_queue_mem: ++ fec_enet_free_queue(ndev); ++ return ret; ++} ++ ++#ifdef CONFIG_OF ++static int fec_reset_phy(struct platform_device *pdev) ++{ ++ int err, phy_reset; ++ bool active_high = false; ++ int msec = 1, phy_post_delay = 0; ++ struct device_node *np = pdev->dev.of_node; ++ ++ if (!np) ++ return 0; ++ ++ err = of_property_read_u32(np, "phy-reset-duration", &msec); ++ /* A sane reset duration should not be longer than 1s */ ++ if (!err && msec > 1000) ++ msec = 1; ++ ++ phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); ++ if (phy_reset == -EPROBE_DEFER) ++ return phy_reset; ++ else if (!gpio_is_valid(phy_reset)) ++ return 0; ++ ++ err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay); ++ /* valid reset duration should be less than 1s */ ++ if (!err && phy_post_delay > 1000) ++ return -EINVAL; ++ ++ active_high = of_property_read_bool(np, "phy-reset-active-high"); ++ ++ err = devm_gpio_request_one(&pdev->dev, phy_reset, ++ active_high ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, ++ "phy-reset"); ++ if (err) { ++ dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); ++ return err; ++ } ++ ++ if (msec > 20) ++ msleep(msec); ++ else ++ usleep_range(msec * 1000, msec * 1000 + 1000); ++ ++ gpio_set_value_cansleep(phy_reset, !active_high); ++ ++ if (!phy_post_delay) ++ return 0; ++ ++ if (phy_post_delay > 20) ++ msleep(phy_post_delay); ++ else ++ usleep_range(phy_post_delay * 1000, ++ phy_post_delay * 1000 + 1000); ++ ++ return 0; ++} ++#else /* CONFIG_OF */ ++static int fec_reset_phy(struct platform_device *pdev) ++{ ++ /* ++ * In case of platform probe, the reset has been done ++ * by machine code. ++ */ ++ return 0; ++} ++#endif /* CONFIG_OF */ ++ ++static int fec_enet_init_stop_mode(struct fec_enet_private *fep, ++ struct device_node *np) ++{ ++ struct device_node *gpr_np; ++ u32 out_val[3]; ++ int ret = 0; ++ ++ gpr_np = of_parse_phandle(np, "fsl,stop-mode", 0); ++ if (!gpr_np) ++ return 0; ++ ++ ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val, ++ ARRAY_SIZE(out_val)); ++ if (ret) { ++ dev_dbg(&fep->pdev->dev, "no stop mode property\n"); ++ goto out; ++ } ++ ++ fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np); ++ if (IS_ERR(fep->stop_gpr.gpr)) { ++ dev_err(&fep->pdev->dev, "could not find gpr regmap\n"); ++ ret = PTR_ERR(fep->stop_gpr.gpr); ++ fep->stop_gpr.gpr = NULL; ++ goto out; ++ } ++ ++ fep->stop_gpr.reg = out_val[1]; ++ fep->stop_gpr.bit = out_val[2]; ++ ++out: ++ of_node_put(gpr_np); ++ ++ return ret; ++} ++ ++static int ++fec_probe(struct platform_device *pdev) ++{ ++ struct fec_enet_private *fep; ++ struct fec_platform_data *pdata; ++ phy_interface_t interface; ++ struct net_device *ndev; ++ int i, irq, ret = 0; ++ const struct of_device_id *of_id; ++ static int dev_id; ++ struct device_node *np = pdev->dev.of_node, *phy_node; ++ char irq_name[8]; ++ struct fec_devinfo *dev_info; ++ ++ /* Init network device */ ++ ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) + ++ FEC_STATS_SIZE, 1, 1); ++ if (!ndev) ++ return -ENOMEM; ++ ndev->fast_raw_device = 1; ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ ++ /* setup board info structure */ ++ fep = netdev_priv(ndev); ++ ++ of_id = of_match_device(fec_dt_ids, &pdev->dev); ++ if (of_id) ++ pdev->id_entry = of_id->data; ++ dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data; ++ if (dev_info) ++ fep->quirks = dev_info->quirks; ++ ++ fep->netdev = ndev; ++ mutex_init(&fep->fast_ndev_lock); ++#if !defined(CONFIG_M5272) ++ /* default enable pause frame auto negotiation */ ++ if (fep->quirks & FEC_QUIRK_HAS_GBIT) ++ fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; ++#endif ++ ++ /* Select default pin state */ ++ pinctrl_pm_select_default_state(&pdev->dev); ++ ++ fep->hwp = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(fep->hwp)) { ++ ret = PTR_ERR(fep->hwp); ++ goto failed_ioremap; ++ } ++ ++ fep->pdev = pdev; ++ fep->dev_id = dev_id++; ++ ++ platform_set_drvdata(pdev, ndev); ++ ++ if ((of_machine_is_compatible("fsl,imx6q") || ++ of_machine_is_compatible("fsl,imx6dl")) && ++ !of_property_read_bool(np, "fsl,err006687-workaround-present")) ++ fep->quirks |= FEC_QUIRK_ERR006687; ++ ++ ret = fec_enet_ipc_handle_init(fep); ++ if (ret) ++ goto failed_ipc_init; ++ ++ ret = fec_enet_init_stop_mode(fep, np); ++ if (ret) ++ goto failed_stop_mode; ++ ++ phy_node = of_parse_phandle(np, "phy-handle", 0); ++ if (!phy_node && of_phy_is_fixed_link(np)) { ++ ret = of_phy_register_fixed_link(np); ++ if (ret < 0) { ++ dev_err(&pdev->dev, ++ "broken fixed-link specification\n"); ++ goto failed_phy; ++ } ++ phy_node = of_node_get(np); ++ } ++ fep->phy_node = phy_node; ++ ++ ret = of_get_phy_mode(pdev->dev.of_node, &interface); ++ if (ret) { ++ pdata = dev_get_platdata(&pdev->dev); ++ if (pdata) ++ fep->phy_interface = pdata->phy; ++ else ++ fep->phy_interface = PHY_INTERFACE_MODE_MII; ++ } else { ++ fep->phy_interface = interface; ++ } ++ ++ ret = fec_enet_parse_rgmii_delay(fep, np); ++ if (ret) ++ goto failed_rgmii_delay; ++ ++ request_bus_freq(BUS_FREQ_HIGH); ++ ++ fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); ++ if (IS_ERR(fep->clk_ipg)) { ++ ret = PTR_ERR(fep->clk_ipg); ++ goto failed_clk; ++ } ++ ++ fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(fep->clk_ahb)) { ++ ret = PTR_ERR(fep->clk_ahb); ++ goto failed_clk; ++ } ++ ++ fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); ++ ++ /* enet_out is optional, depends on board */ ++ fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out"); ++ if (IS_ERR(fep->clk_enet_out)) ++ fep->clk_enet_out = NULL; ++ ++ fep->ptp_clk_on = false; ++ mutex_init(&fep->ptp_clk_mutex); ++ ++ /* clk_ref is optional, depends on board */ ++ fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref"); ++ if (IS_ERR(fep->clk_ref)) ++ fep->clk_ref = NULL; ++ fep->clk_ref_rate = clk_get_rate(fep->clk_ref); ++ ++ /* clk_2x_txclk is optional, depends on board */ ++ if (fep->rgmii_txc_dly || fep->rgmii_rxc_dly) { ++ fep->clk_2x_txclk = devm_clk_get(&pdev->dev, "enet_2x_txclk"); ++ if (IS_ERR(fep->clk_2x_txclk)) ++ fep->clk_2x_txclk = NULL; ++ } ++ ++ ret = fec_enet_clk_enable(ndev, true); ++ if (ret) ++ goto failed_clk; ++ ++ ret = clk_prepare_enable(fep->clk_ipg); ++ if (ret) ++ goto failed_clk_ipg; ++ ret = clk_prepare_enable(fep->clk_ahb); ++ if (ret) ++ goto failed_clk_ahb; ++ ++ fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy"); ++ if (!IS_ERR(fep->reg_phy)) { ++ ret = regulator_enable(fep->reg_phy); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "Failed to enable phy regulator: %d\n", ret); ++ goto failed_regulator; ++ } ++ } else { ++ if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) { ++ ret = -EPROBE_DEFER; ++ goto failed_regulator; ++ } ++ fep->reg_phy = NULL; ++ } ++ ++ if (of_property_read_u32(np, "fsl,rx-phy-delay-100-ns", &fep->rx_delay_100)) ++ fep->rx_delay_100 = 600; ++ ++ if (of_property_read_u32(np, "fsl,tx-phy-delay-100-ns", &fep->tx_delay_100)) ++ fep->tx_delay_100 = 0; ++ ++ if (of_property_read_u32(np, "fsl,rx-phy-delay-1000-ns", &fep->rx_delay_1000)) ++ fep->rx_delay_1000 = 0; ++ ++ if (of_property_read_u32(np, "fsl,tx-phy-delay-1000-ns", &fep->tx_delay_1000)) ++ fep->tx_delay_1000 = 0; ++ ++ pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); ++ pm_runtime_use_autosuspend(&pdev->dev); ++ pm_runtime_get_noresume(&pdev->dev); ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ ++ ret = fec_reset_phy(pdev); ++ if (ret) ++ goto failed_reset; ++ ++ //irq_cnt = fec_enet_get_irq_cnt(pdev); ++ ++ ret = fec_enet_init(ndev); ++ if (ret) ++ goto failed_init; ++ ++ /* board only enable one mii bus in default */ ++ if (!of_get_property(np, "fsl,mii-exclusive", NULL)) ++ fep->quirks |= FEC_QUIRK_SINGLE_MDIO; ++ ret = fec_enet_mii_init(pdev); ++ if (ret) ++ goto failed_mii_init; ++ ++ /* Carrier starts down, phylib will bring it up */ ++ netif_carrier_off(ndev); ++ pinctrl_pm_select_sleep_state(&pdev->dev); ++ ++ ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN; ++ ++ ret = register_netdev(ndev); ++ if (ret) ++ goto failed_register; ++ ++ device_init_wakeup(&ndev->dev, fep->wol_flag & ++ FEC_WOL_HAS_MAGIC_PACKET); ++ ++ ++ INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); ++ ++ pm_runtime_mark_last_busy(&pdev->dev); ++ pm_runtime_put_autosuspend(&pdev->dev); ++ mutex_lock(&fep->fast_ndev_lock); ++ return 0; ++ ++failed_register: ++ fec_enet_mii_remove(fep); ++failed_mii_init: ++failed_irq: ++failed_init: ++ //fec_ptp_stop(pdev); ++failed_reset: ++ pm_runtime_put_noidle(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ if (fep->reg_phy) ++ regulator_disable(fep->reg_phy); ++failed_regulator: ++ clk_disable_unprepare(fep->clk_ahb); ++failed_clk_ahb: ++ clk_disable_unprepare(fep->clk_ipg); ++failed_clk_ipg: ++ fec_enet_clk_enable(ndev, false); ++failed_clk: ++ release_bus_freq(BUS_FREQ_HIGH); ++failed_rgmii_delay: ++ if (of_phy_is_fixed_link(np)) ++ of_phy_deregister_fixed_link(np); ++ of_node_put(phy_node); ++failed_stop_mode: ++failed_ipc_init: ++failed_phy: ++ dev_id--; ++failed_ioremap: ++ free_netdev(ndev); ++ ++ return ret; ++} ++ ++static int ++fec_drv_remove(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct device_node *np = pdev->dev.of_node; ++ int ret; ++ ++ ret = pm_runtime_resume_and_get(&pdev->dev); ++ if (ret < 0) ++ return ret; ++ ++ cancel_work_sync(&fep->tx_timeout_work); ++ //fec_ptp_stop(pdev); ++ unregister_netdev(ndev); ++ fec_enet_mii_remove(fep); ++ if (fep->reg_phy) ++ regulator_disable(fep->reg_phy); ++ ++ if (of_phy_is_fixed_link(np)) ++ of_phy_deregister_fixed_link(np); ++ of_node_put(fep->phy_node); ++ ++ clk_disable_unprepare(fep->clk_ahb); ++ clk_disable_unprepare(fep->clk_ipg); ++ pm_runtime_put_noidle(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ ++ free_netdev(ndev); ++ mutex_unlock(&fep->fast_ndev_lock); ++ return 0; ++} ++ ++static int __maybe_unused fec_suspend(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ ++ if (netif_running(ndev)) { ++ int ret; ++ ++ phy_stop(ndev->phydev); ++ ++ netif_device_detach(ndev); ++ fec_stop(ndev); ++ fec_enet_clk_enable(ndev, false); ++ ++ fep->rpm_active = !pm_runtime_status_suspended(dev); ++ if (fep->rpm_active) { ++ ret = pm_runtime_force_suspend(dev); ++ if (ret < 0) ++ return ret; ++ } ++ } else if (fep->mii_bus_share && !ndev->phydev) { ++ pinctrl_pm_select_sleep_state(&fep->pdev->dev); ++ } ++ ++ if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) ++ regulator_disable(fep->reg_phy); ++ ++ /* SOC supply clock to phy, when clock is disabled, phy link down ++ * SOC control phy regulator, when regulator is disabled, phy link down ++ */ ++ if (fep->clk_enet_out || fep->reg_phy) ++ fep->link = 0; ++ ++ return 0; ++} ++ ++static int __maybe_unused fec_resume(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ int ret = 0; ++ int val; ++ ++ if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { ++ ret = regulator_enable(fep->reg_phy); ++ if (ret) ++ return ret; ++ } ++ ++ if (netif_running(ndev)) { ++ if (fep->rpm_active) ++ pm_runtime_force_resume(dev); ++ ++ ret = fec_enet_clk_enable(ndev, true); ++ if (ret) { ++ rtnl_unlock(); ++ goto failed_clk; ++ } ++ pinctrl_pm_select_default_state(&fep->pdev->dev); ++ fec_restart(ndev); ++ netif_device_attach(ndev); ++ ++ //napi_enable(&fep->napi); ++ ++ phy_init_hw(ndev->phydev); ++ phy_start(ndev->phydev); ++ } else if (fep->mii_bus_share && !ndev->phydev) { ++ pinctrl_pm_select_default_state(&fep->pdev->dev); ++ /* And then recovery mii bus */ ++ ret = fec_restore_mii_bus(ndev); ++ } ++ ++ return ret; ++ ++failed_clk: ++ if (fep->reg_phy) ++ regulator_disable(fep->reg_phy); ++ return ret; ++} ++ ++static int __maybe_unused fec_runtime_suspend(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ ++ clk_disable_unprepare(fep->clk_ahb); ++ clk_disable_unprepare(fep->clk_ipg); ++ release_bus_freq(BUS_FREQ_HIGH); ++ ++ return 0; ++} ++ ++static int __maybe_unused fec_runtime_resume(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ int ret; ++ ++ request_bus_freq(BUS_FREQ_HIGH); ++ ++ ret = clk_prepare_enable(fep->clk_ahb); ++ if (ret) ++ return ret; ++ ret = clk_prepare_enable(fep->clk_ipg); ++ if (ret) ++ goto failed_clk_ipg; ++ ++ return 0; ++ ++failed_clk_ipg: ++ clk_disable_unprepare(fep->clk_ahb); ++ release_bus_freq(BUS_FREQ_HIGH); ++ return ret; ++} ++ ++static const struct dev_pm_ops fec_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) ++ SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) ++}; ++ ++static struct platform_driver fec_ecat_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .pm = &fec_pm_ops, ++ .of_match_table = fec_dt_ids, ++ .suppress_bind_attrs = true, ++ }, ++ .id_table = fec_devtype, ++ .probe = fec_probe, ++ .remove = fec_drv_remove, ++}; ++ ++module_platform_driver(fec_ecat_driver); ++ ++MODULE_LICENSE("GPL"); +diff --git a/drivers/net/ethernet/freescale/fec_ecat.h b/drivers/net/ethernet/freescale/fec_ecat.h +new file mode 100644 +index 000000000000..87db4c85db24 +--- /dev/null ++++ b/drivers/net/ethernet/freescale/fec_ecat.h +@@ -0,0 +1,713 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/****************************************************************************/ ++#ifndef FEC_ECAT_H ++#define FEC_ECAT_H ++/****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ ++ defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ ++ defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) ++/* ++ * Just figures, Motorola would have to change the offsets for ++ * registers in the same peripheral device on different models ++ * of the ColdFire! ++ */ ++#define FEC_IEVENT 0x004 /* Interrupt event reg */ ++#define FEC_IMASK 0x008 /* Interrupt mask reg */ ++#define FEC_R_DES_ACTIVE_0 0x010 /* Receive descriptor reg */ ++#define FEC_X_DES_ACTIVE_0 0x014 /* Transmit descriptor reg */ ++#define FEC_ECNTRL 0x024 /* Ethernet control reg */ ++#define FEC_MII_DATA 0x040 /* MII manage frame reg */ ++#define FEC_MII_SPEED 0x044 /* MII speed control reg */ ++#define FEC_MIB_CTRLSTAT 0x064 /* MIB control/status reg */ ++#define FEC_R_CNTRL 0x084 /* Receive control reg */ ++#define FEC_X_CNTRL 0x0c4 /* Transmit Control reg */ ++#define FEC_ADDR_LOW 0x0e4 /* Low 32bits MAC address */ ++#define FEC_ADDR_HIGH 0x0e8 /* High 16bits MAC address */ ++#define FEC_OPD 0x0ec /* Opcode + Pause duration */ ++#define FEC_TXIC0 0x0f0 /* Tx Interrupt Coalescing for ring 0 */ ++#define FEC_TXIC1 0x0f4 /* Tx Interrupt Coalescing for ring 1 */ ++#define FEC_TXIC2 0x0f8 /* Tx Interrupt Coalescing for ring 2 */ ++#define FEC_RXIC0 0x100 /* Rx Interrupt Coalescing for ring 0 */ ++#define FEC_RXIC1 0x104 /* Rx Interrupt Coalescing for ring 1 */ ++#define FEC_RXIC2 0x108 /* Rx Interrupt Coalescing for ring 2 */ ++#define FEC_HASH_TABLE_HIGH 0x118 /* High 32bits hash table */ ++#define FEC_HASH_TABLE_LOW 0x11c /* Low 32bits hash table */ ++#define FEC_GRP_HASH_TABLE_HIGH 0x120 /* High 32bits hash table */ ++#define FEC_GRP_HASH_TABLE_LOW 0x124 /* Low 32bits hash table */ ++#define FEC_X_WMRK 0x144 /* FIFO transmit water mark */ ++#define FEC_R_BOUND 0x14c /* FIFO receive bound reg */ ++#define FEC_R_FSTART 0x150 /* FIFO receive start reg */ ++#define FEC_R_DES_START_1 0x160 /* Receive descriptor ring 1 */ ++#define FEC_X_DES_START_1 0x164 /* Transmit descriptor ring 1 */ ++#define FEC_R_BUFF_SIZE_1 0x168 /* Maximum receive buff ring1 size */ ++#define FEC_R_DES_START_2 0x16c /* Receive descriptor ring 2 */ ++#define FEC_X_DES_START_2 0x170 /* Transmit descriptor ring 2 */ ++#define FEC_R_BUFF_SIZE_2 0x174 /* Maximum receive buff ring2 size */ ++#define FEC_R_DES_START_0 0x180 /* Receive descriptor ring */ ++#define FEC_X_DES_START_0 0x184 /* Transmit descriptor ring */ ++#define FEC_R_BUFF_SIZE_0 0x188 /* Maximum receive buff size */ ++#define FEC_R_FIFO_RSFL 0x190 /* Receive FIFO section full threshold */ ++#define FEC_R_FIFO_RSEM 0x194 /* Receive FIFO section empty threshold */ ++#define FEC_R_FIFO_RAEM 0x198 /* Receive FIFO almost empty threshold */ ++#define FEC_R_FIFO_RAFL 0x19c /* Receive FIFO almost full threshold */ ++#define FEC_FTRL 0x1b0 /* Frame truncation receive length*/ ++#define FEC_RACC 0x1c4 /* Receive Accelerator function */ ++#define FEC_RCMR_1 0x1c8 /* Receive classification match ring 1 */ ++#define FEC_RCMR_2 0x1cc /* Receive classification match ring 2 */ ++#define FEC_DMA_CFG_1 0x1d8 /* DMA class configuration for ring 1 */ ++#define FEC_DMA_CFG_2 0x1dc /* DMA class Configuration for ring 2 */ ++#define FEC_R_DES_ACTIVE_1 0x1e0 /* Rx descriptor active for ring 1 */ ++#define FEC_X_DES_ACTIVE_1 0x1e4 /* Tx descriptor active for ring 1 */ ++#define FEC_R_DES_ACTIVE_2 0x1e8 /* Rx descriptor active for ring 2 */ ++#define FEC_X_DES_ACTIVE_2 0x1ec /* Tx descriptor active for ring 2 */ ++#define FEC_QOS_SCHEME 0x1f0 /* Set multi queues Qos scheme */ ++#define FEC_LPI_SLEEP 0x1f4 /* Set IEEE802.3az LPI Sleep Ts time */ ++#define FEC_LPI_WAKE 0x1f8 /* Set IEEE802.3az LPI Wake Tw time */ ++#define FEC_MIIGSK_CFGR 0x300 /* MIIGSK Configuration reg */ ++#define FEC_MIIGSK_ENR 0x308 /* MIIGSK Enable reg */ ++ ++#define BM_MIIGSK_CFGR_MII 0x00 ++#define BM_MIIGSK_CFGR_RMII 0x01 ++#define BM_MIIGSK_CFGR_FRCONT_10M 0x40 ++ ++#define RMON_T_DROP 0x200 /* Count of frames not cntd correctly */ ++#define RMON_T_PACKETS 0x204 /* RMON TX packet count */ ++#define RMON_T_BC_PKT 0x208 /* RMON TX broadcast pkts */ ++#define RMON_T_MC_PKT 0x20c /* RMON TX multicast pkts */ ++#define RMON_T_CRC_ALIGN 0x210 /* RMON TX pkts with CRC align err */ ++#define RMON_T_UNDERSIZE 0x214 /* RMON TX pkts < 64 bytes, good CRC */ ++#define RMON_T_OVERSIZE 0x218 /* RMON TX pkts > MAX_FL bytes good CRC */ ++#define RMON_T_FRAG 0x21c /* RMON TX pkts < 64 bytes, bad CRC */ ++#define RMON_T_JAB 0x220 /* RMON TX pkts > MAX_FL bytes, bad CRC */ ++#define RMON_T_COL 0x224 /* RMON TX collision count */ ++#define RMON_T_P64 0x228 /* RMON TX 64 byte pkts */ ++#define RMON_T_P65TO127 0x22c /* RMON TX 65 to 127 byte pkts */ ++#define RMON_T_P128TO255 0x230 /* RMON TX 128 to 255 byte pkts */ ++#define RMON_T_P256TO511 0x234 /* RMON TX 256 to 511 byte pkts */ ++#define RMON_T_P512TO1023 0x238 /* RMON TX 512 to 1023 byte pkts */ ++#define RMON_T_P1024TO2047 0x23c /* RMON TX 1024 to 2047 byte pkts */ ++#define RMON_T_P_GTE2048 0x240 /* RMON TX pkts > 2048 bytes */ ++#define RMON_T_OCTETS 0x244 /* RMON TX octets */ ++#define IEEE_T_DROP 0x248 /* Count of frames not counted crtly */ ++#define IEEE_T_FRAME_OK 0x24c /* Frames tx'd OK */ ++#define IEEE_T_1COL 0x250 /* Frames tx'd with single collision */ ++#define IEEE_T_MCOL 0x254 /* Frames tx'd with multiple collision */ ++#define IEEE_T_DEF 0x258 /* Frames tx'd after deferral delay */ ++#define IEEE_T_LCOL 0x25c /* Frames tx'd with late collision */ ++#define IEEE_T_EXCOL 0x260 /* Frames tx'd with excesv collisions */ ++#define IEEE_T_MACERR 0x264 /* Frames tx'd with TX FIFO underrun */ ++#define IEEE_T_CSERR 0x268 /* Frames tx'd with carrier sense err */ ++#define IEEE_T_SQE 0x26c /* Frames tx'd with SQE err */ ++#define IEEE_T_FDXFC 0x270 /* Flow control pause frames tx'd */ ++#define IEEE_T_OCTETS_OK 0x274 /* Octet count for frames tx'd w/o err */ ++#define RMON_R_PACKETS 0x284 /* RMON RX packet count */ ++#define RMON_R_BC_PKT 0x288 /* RMON RX broadcast pkts */ ++#define RMON_R_MC_PKT 0x28c /* RMON RX multicast pkts */ ++#define RMON_R_CRC_ALIGN 0x290 /* RMON RX pkts with CRC alignment err */ ++#define RMON_R_UNDERSIZE 0x294 /* RMON RX pkts < 64 bytes, good CRC */ ++#define RMON_R_OVERSIZE 0x298 /* RMON RX pkts > MAX_FL bytes good CRC */ ++#define RMON_R_FRAG 0x29c /* RMON RX pkts < 64 bytes, bad CRC */ ++#define RMON_R_JAB 0x2a0 /* RMON RX pkts > MAX_FL bytes, bad CRC */ ++#define RMON_R_RESVD_O 0x2a4 /* Reserved */ ++#define RMON_R_P64 0x2a8 /* RMON RX 64 byte pkts */ ++#define RMON_R_P65TO127 0x2ac /* RMON RX 65 to 127 byte pkts */ ++#define RMON_R_P128TO255 0x2b0 /* RMON RX 128 to 255 byte pkts */ ++#define RMON_R_P256TO511 0x2b4 /* RMON RX 256 to 511 byte pkts */ ++#define RMON_R_P512TO1023 0x2b8 /* RMON RX 512 to 1023 byte pkts */ ++#define RMON_R_P1024TO2047 0x2bc /* RMON RX 1024 to 2047 byte pkts */ ++#define RMON_R_P_GTE2048 0x2c0 /* RMON RX pkts > 2048 bytes */ ++#define RMON_R_OCTETS 0x2c4 /* RMON RX octets */ ++#define IEEE_R_DROP 0x2c8 /* Count frames not counted correctly */ ++#define IEEE_R_FRAME_OK 0x2cc /* Frames rx'd OK */ ++#define IEEE_R_CRC 0x2d0 /* Frames rx'd with CRC err */ ++#define IEEE_R_ALIGN 0x2d4 /* Frames rx'd with alignment err */ ++#define IEEE_R_MACERR 0x2d8 /* Receive FIFO overflow count */ ++#define IEEE_R_FDXFC 0x2dc /* Flow control pause frames rx'd */ ++#define IEEE_R_OCTETS_OK 0x2e0 /* Octet cnt for frames rx'd w/o err */ ++ ++#else ++ ++#define FEC_ECNTRL 0x000 /* Ethernet control reg */ ++#define FEC_IEVENT 0x004 /* Interrupt even reg */ ++#define FEC_IMASK 0x008 /* Interrupt mask reg */ ++#define FEC_IVEC 0x00c /* Interrupt vec status reg */ ++#define FEC_R_DES_ACTIVE_0 0x010 /* Receive descriptor reg */ ++#define FEC_R_DES_ACTIVE_1 FEC_R_DES_ACTIVE_0 ++#define FEC_R_DES_ACTIVE_2 FEC_R_DES_ACTIVE_0 ++#define FEC_X_DES_ACTIVE_0 0x014 /* Transmit descriptor reg */ ++#define FEC_X_DES_ACTIVE_1 FEC_X_DES_ACTIVE_0 ++#define FEC_X_DES_ACTIVE_2 FEC_X_DES_ACTIVE_0 ++#define FEC_MII_DATA 0x040 /* MII manage frame reg */ ++#define FEC_MII_SPEED 0x044 /* MII speed control reg */ ++#define FEC_R_BOUND 0x08c /* FIFO receive bound reg */ ++#define FEC_R_FSTART 0x090 /* FIFO receive start reg */ ++#define FEC_X_WMRK 0x0a4 /* FIFO transmit water mark */ ++#define FEC_X_FSTART 0x0ac /* FIFO transmit start reg */ ++#define FEC_R_CNTRL 0x104 /* Receive control reg */ ++#define FEC_MAX_FRM_LEN 0x108 /* Maximum frame length reg */ ++#define FEC_X_CNTRL 0x144 /* Transmit Control reg */ ++#define FEC_ADDR_LOW 0x3c0 /* Low 32bits MAC address */ ++#define FEC_ADDR_HIGH 0x3c4 /* High 16bits MAC address */ ++#define FEC_GRP_HASH_TABLE_HIGH 0x3c8 /* High 32bits hash table */ ++#define FEC_GRP_HASH_TABLE_LOW 0x3cc /* Low 32bits hash table */ ++#define FEC_R_DES_START_0 0x3d0 /* Receive descriptor ring */ ++#define FEC_R_DES_START_1 FEC_R_DES_START_0 ++#define FEC_R_DES_START_2 FEC_R_DES_START_0 ++#define FEC_X_DES_START_0 0x3d4 /* Transmit descriptor ring */ ++#define FEC_X_DES_START_1 FEC_X_DES_START_0 ++#define FEC_X_DES_START_2 FEC_X_DES_START_0 ++#define FEC_R_BUFF_SIZE_0 0x3d8 /* Maximum receive buff size */ ++#define FEC_R_BUFF_SIZE_1 FEC_R_BUFF_SIZE_0 ++#define FEC_R_BUFF_SIZE_2 FEC_R_BUFF_SIZE_0 ++#define FEC_FIFO_RAM 0x400 /* FIFO RAM buffer */ ++/* Not existed in real chip ++ * Just for pass build. ++ */ ++#define FEC_RCMR_1 0xfff ++#define FEC_RCMR_2 0xfff ++#define FEC_DMA_CFG_1 0xfff ++#define FEC_DMA_CFG_2 0xfff ++#define FEC_TXIC0 0xfff ++#define FEC_TXIC1 0xfff ++#define FEC_TXIC2 0xfff ++#define FEC_RXIC0 0xfff ++#define FEC_RXIC1 0xfff ++#define FEC_RXIC2 0xfff ++#define FEC_LPI_SLEEP 0xfff ++#define FEC_LPI_WAKE 0xfff ++#endif /* CONFIG_M5272 */ ++ ++ ++/* ++ * Define the buffer descriptor structure. ++ * ++ * Evidently, ARM SoCs have the FEC block generated in a ++ * little endian mode so adjust endianness accordingly. ++ */ ++#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) ++#define fec32_to_cpu le32_to_cpu ++#define fec16_to_cpu le16_to_cpu ++#define cpu_to_fec32 cpu_to_le32 ++#define cpu_to_fec16 cpu_to_le16 ++#define __fec32 __le32 ++#define __fec16 __le16 ++ ++struct bufdesc { ++ __fec16 cbd_datlen; /* Data length */ ++ __fec16 cbd_sc; /* Control and status info */ ++ __fec32 cbd_bufaddr; /* Buffer address */ ++}; ++#else ++#define fec32_to_cpu be32_to_cpu ++#define fec16_to_cpu be16_to_cpu ++#define cpu_to_fec32 cpu_to_be32 ++#define cpu_to_fec16 cpu_to_be16 ++#define __fec32 __be32 ++#define __fec16 __be16 ++ ++struct bufdesc { ++ __fec16 cbd_sc; /* Control and status info */ ++ __fec16 cbd_datlen; /* Data length */ ++ __fec32 cbd_bufaddr; /* Buffer address */ ++}; ++#endif ++ ++struct bufdesc_ex { ++ struct bufdesc desc; ++ __fec32 cbd_esc; ++ __fec32 cbd_prot; ++ __fec32 cbd_bdu; ++ __fec32 ts; ++ __fec16 res0[4]; ++}; ++ ++/* ++ * The following definitions courtesy of commproc.h, which where ++ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net). ++ */ ++#define BD_SC_EMPTY ((ushort)0x8000) /* Receive is empty */ ++#define BD_SC_READY ((ushort)0x8000) /* Transmit is ready */ ++#define BD_SC_WRAP ((ushort)0x2000) /* Last buffer descriptor */ ++#define BD_SC_INTRPT ((ushort)0x1000) /* Interrupt on change */ ++#define BD_SC_CM ((ushort)0x0200) /* Continuous mode */ ++#define BD_SC_ID ((ushort)0x0100) /* Rec'd too many idles */ ++#define BD_SC_P ((ushort)0x0100) /* xmt preamble */ ++#define BD_SC_BR ((ushort)0x0020) /* Break received */ ++#define BD_SC_FR ((ushort)0x0010) /* Framing error */ ++#define BD_SC_PR ((ushort)0x0008) /* Parity error */ ++#define BD_SC_OV ((ushort)0x0002) /* Overrun */ ++#define BD_SC_CD ((ushort)0x0001) /* ?? */ ++ ++/* Buffer descriptor control/status used by Ethernet receive. ++ */ ++#define BD_ENET_RX_EMPTY ((ushort)0x8000) ++#define BD_ENET_RX_WRAP ((ushort)0x2000) ++#define BD_ENET_RX_INTR ((ushort)0x1000) ++#define BD_ENET_RX_LAST ((ushort)0x0800) ++#define BD_ENET_RX_FIRST ((ushort)0x0400) ++#define BD_ENET_RX_MISS ((ushort)0x0100) ++#define BD_ENET_RX_LG ((ushort)0x0020) ++#define BD_ENET_RX_NO ((ushort)0x0010) ++#define BD_ENET_RX_SH ((ushort)0x0008) ++#define BD_ENET_RX_CR ((ushort)0x0004) ++#define BD_ENET_RX_OV ((ushort)0x0002) ++#define BD_ENET_RX_CL ((ushort)0x0001) ++#define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ ++ ++/* Enhanced buffer descriptor control/status used by Ethernet receive */ ++#define BD_ENET_RX_VLAN 0x00000004 ++ ++/* Buffer descriptor control/status used by Ethernet transmit. ++ */ ++#define BD_ENET_TX_READY ((ushort)0x8000) ++#define BD_ENET_TX_PAD ((ushort)0x4000) ++#define BD_ENET_TX_WRAP ((ushort)0x2000) ++#define BD_ENET_TX_INTR ((ushort)0x1000) ++#define BD_ENET_TX_LAST ((ushort)0x0800) ++#define BD_ENET_TX_TC ((ushort)0x0400) ++#define BD_ENET_TX_DEF ((ushort)0x0200) ++#define BD_ENET_TX_HB ((ushort)0x0100) ++#define BD_ENET_TX_LC ((ushort)0x0080) ++#define BD_ENET_TX_RL ((ushort)0x0040) ++#define BD_ENET_TX_RCMASK ((ushort)0x003c) ++#define BD_ENET_TX_UN ((ushort)0x0002) ++#define BD_ENET_TX_CSL ((ushort)0x0001) ++#define BD_ENET_TX_STATS ((ushort)0x0fff) /* All status bits */ ++ ++/* enhanced buffer descriptor control/status used by Ethernet transmit */ ++#define BD_ENET_TX_INT 0x40000000 ++#define BD_ENET_TX_TS 0x20000000 ++#define BD_ENET_TX_PINS 0x10000000 ++#define BD_ENET_TX_IINS 0x08000000 ++ ++ ++/* This device has up to three irqs on some platforms */ ++#define FEC_IRQ_NUM 3 ++ ++/* Maximum number of queues supported ++ * ENET with AVB IP can support up to 3 independent tx queues and rx queues. ++ * User can point the queue number that is less than or equal to 3. ++ */ ++#define FEC_ENET_MAX_TX_QS 3 ++#define FEC_ENET_MAX_RX_QS 3 ++ ++#define FEC_R_DES_START(X) (((X) == 1) ? FEC_R_DES_START_1 : \ ++ (((X) == 2) ? \ ++ FEC_R_DES_START_2 : FEC_R_DES_START_0)) ++#define FEC_X_DES_START(X) (((X) == 1) ? FEC_X_DES_START_1 : \ ++ (((X) == 2) ? \ ++ FEC_X_DES_START_2 : FEC_X_DES_START_0)) ++#define FEC_R_BUFF_SIZE(X) (((X) == 1) ? FEC_R_BUFF_SIZE_1 : \ ++ (((X) == 2) ? \ ++ FEC_R_BUFF_SIZE_2 : FEC_R_BUFF_SIZE_0)) ++ ++#define FEC_DMA_CFG(X) (((X) == 2) ? FEC_DMA_CFG_2 : FEC_DMA_CFG_1) ++ ++#define DMA_CLASS_EN (1 << 16) ++#define FEC_RCMR(X) (((X) == 2) ? FEC_RCMR_2 : FEC_RCMR_1) ++#define IDLE_SLOPE_MASK 0xffff ++#define IDLE_SLOPE_1 0x200 /* BW fraction: 0.5 */ ++#define IDLE_SLOPE_2 0x200 /* BW fraction: 0.5 */ ++#define IDLE_SLOPE(X) (((X) == 1) ? \ ++ (IDLE_SLOPE_1 & IDLE_SLOPE_MASK) : \ ++ (IDLE_SLOPE_2 & IDLE_SLOPE_MASK)) ++#define RCMR_MATCHEN (0x1 << 16) ++#define RCMR_CMP_CFG(v, n) (((v) & 0x7) << (n << 2)) ++#ifdef CONFIG_AVB_SUPPORT ++#define SR_CLASS_A_PRIORITY 3 ++#define SR_CLASS_B_PRIORITY 2 ++#define RCMR_CMP_1 (RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 0) | \ ++ RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 1) | \ ++ RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 2) | \ ++ RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 3)) ++#define RCMR_CMP_2 (RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 0) | \ ++ RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 1) | \ ++ RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 2) | \ ++ RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 3)) ++#else ++#define RCMR_CMP_1 (RCMR_CMP_CFG(0, 0) | RCMR_CMP_CFG(1, 1) | \ ++ RCMR_CMP_CFG(2, 2) | RCMR_CMP_CFG(3, 3)) ++#define RCMR_CMP_2 (RCMR_CMP_CFG(4, 0) | RCMR_CMP_CFG(5, 1) | \ ++ RCMR_CMP_CFG(6, 2) | RCMR_CMP_CFG(7, 3)) ++#endif ++#define RCMR_CMP(X) (((X) == 1) ? RCMR_CMP_1 : RCMR_CMP_2) ++#define FEC_TX_BD_FTYPE(X) (((X) & 0xf) << 20) ++ ++#define FEC_RX_FLUSH(X) (1 << ((X) + 3)) ++ ++#define FEC_TX_SCHEME_CB 0x0 /* Credit based */ ++#define FEC_TX_SCHEME_RR 0x1 /* Round-robin */ ++ ++#define BD_ENET_RX_INT 0x00800000 ++#define BD_ENET_RX_PTP ((ushort)0x0400) ++#define BD_ENET_RX_ICE 0x00000020 ++#define BD_ENET_RX_PCR 0x00000010 ++#define FLAG_RX_CSUM_ENABLED (BD_ENET_RX_ICE | BD_ENET_RX_PCR) ++#define FLAG_RX_CSUM_ERROR (BD_ENET_RX_ICE | BD_ENET_RX_PCR) ++ ++/* Interrupt events/masks. */ ++#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ ++#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ ++#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */ ++#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */ ++#define FEC_ENET_TXF_0 ((uint)0x08000000) /* Full frame transmitted */ ++#define FEC_ENET_TXF_1 ((uint)0x00000008) /* Full frame transmitted */ ++#define FEC_ENET_TXF_2 ((uint)0x00000080) /* Full frame transmitted */ ++#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */ ++#define FEC_ENET_RXF_0 ((uint)0x02000000) /* Full frame received */ ++#define FEC_ENET_RXF_1 ((uint)0x00000002) /* Full frame received */ ++#define FEC_ENET_RXF_2 ((uint)0x00000020) /* Full frame received */ ++#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ ++#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ ++#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ ++#define FEC_ENET_WAKEUP ((uint)0x00020000) /* Wakeup request */ ++#define FEC_ENET_TXF (FEC_ENET_TXF_0 | FEC_ENET_TXF_1 | FEC_ENET_TXF_2) ++#define FEC_ENET_RXF (FEC_ENET_RXF_0 | FEC_ENET_RXF_1 | FEC_ENET_RXF_2) ++#define FEC_ENET_RXF_GET(X) (((X) == 0) ? FEC_ENET_RXF_0 : \ ++ (((X) == 1) ? FEC_ENET_RXF_1 : \ ++ FEC_ENET_RXF_2)) ++#define FEC_ENET_TS_AVAIL ((uint)0x00010000) ++#define FEC_ENET_TS_TIMER ((uint)0x00008000) ++ ++#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF) ++#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) ++ ++#define FEC_ENET_ETHEREN ((uint)0x00000002) ++#define FEC_ENET_TXC_DLY ((uint)0x00010000) ++#define FEC_ENET_RXC_DLY ((uint)0x00020000) ++ ++/* ENET interrupt coalescing macro define */ ++#define FEC_ITR_CLK_SEL (0x1 << 30) ++#define FEC_ITR_EN (0x1 << 31) ++#define FEC_ITR_ICFT(X) (((X) & 0xff) << 20) ++#define FEC_ITR_ICTT(X) ((X) & 0xffff) ++#define FEC_ITR_ICFT_DEFAULT 200 /* Set 200 frame count threshold */ ++#define FEC_ITR_ICTT_DEFAULT 1000 /* Set 1000us timer threshold */ ++ ++#define FEC_VLAN_TAG_LEN 0x04 ++#define FEC_ETHTYPE_LEN 0x02 ++ ++/* Controller is ENET-MAC */ ++#define FEC_QUIRK_ENET_MAC (1 << 0) ++/* Controller needs driver to swap frame */ ++#define FEC_QUIRK_SWAP_FRAME (1 << 1) ++/* Controller uses gasket */ ++#define FEC_QUIRK_USE_GASKET (1 << 2) ++/* Controller has GBIT support */ ++#define FEC_QUIRK_HAS_GBIT (1 << 3) ++/* Controller has extend desc buffer */ ++#define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4) ++/* Controller has hardware checksum support */ ++#define FEC_QUIRK_HAS_CSUM (1 << 5) ++/* Controller has hardware vlan support */ ++#define FEC_QUIRK_HAS_VLAN (1 << 6) ++/* ENET IP errata ERR006358 ++ * ++ * If the ready bit in the transmit buffer descriptor (TxBD[R]) is previously ++ * detected as not set during a prior frame transmission, then the ++ * ENET_TDAR[TDAR] bit is cleared at a later time, even if additional TxBDs ++ * were added to the ring and the ENET_TDAR[TDAR] bit is set. This results in ++ * frames not being transmitted until there is a 0-to-1 transition on ++ * ENET_TDAR[TDAR]. ++ */ ++#define FEC_QUIRK_ERR006358 (1 << 7) ++/* ENET IP hw AVB ++ * ++ * i.MX6SX ENET IP add Audio Video Bridging (AVB) feature support. ++ * - Two class indicators on receive with configurable priority ++ * - Two class indicators and line speed timer on transmit allowing ++ * implementation class credit based shapers externally ++ * - Additional DMA registers provisioned to allow managing up to 3 ++ * independent rings ++ */ ++#define FEC_QUIRK_HAS_AVB (1 << 8) ++/* There is a TDAR race condition for mutliQ when the software sets TDAR ++ * and the UDMA clears TDAR simultaneously or in a small window (2-4 cycles). ++ * This will cause the udma_tx and udma_tx_arbiter state machines to hang. ++ * The issue exist at i.MX6SX enet IP. ++ */ ++#define FEC_QUIRK_ERR007885 (1 << 9) ++/* ENET Block Guide/ Chapter for the iMX6SX (PELE) address one issue: ++ * After set ENET_ATCR[Capture], there need some time cycles before the counter ++ * value is capture in the register clock domain. ++ * The wait-time-cycles is at least 6 clock cycles of the slower clock between ++ * the register clock and the 1588 clock. The 1588 ts_clk is fixed to 25Mhz, ++ * register clock is 66Mhz, so the wait-time-cycles must be greater than 240ns ++ * (40ns * 6). ++ */ ++#define FEC_QUIRK_BUG_CAPTURE (1 << 10) ++/* Controller has only one MDIO bus */ ++#define FEC_QUIRK_SINGLE_MDIO (1 << 11) ++/* Controller supports RACC register */ ++#define FEC_QUIRK_HAS_RACC (1 << 12) ++/* Controller supports interrupt coalesc */ ++#define FEC_QUIRK_HAS_COALESCE (1 << 13) ++/* Interrupt doesn't wake CPU from deep idle */ ++#define FEC_QUIRK_ERR006687 (1 << 14) ++/* The MIB counters should be cleared and enabled during ++ * initialisation. ++ */ ++#define FEC_QUIRK_MIB_CLEAR (1 << 15) ++/* Only i.MX25/i.MX27/i.MX28 controller supports FRBR,FRSR registers, ++ * those FIFO receive registers are resolved in other platforms. ++ */ ++#define FEC_QUIRK_HAS_FRREG (1 << 16) ++ ++/* Some FEC hardware blocks need the MMFR cleared at setup time to avoid ++ * the generation of an MII event. This must be avoided in the older ++ * FEC blocks where it will stop MII events being generated. ++ */ ++#define FEC_QUIRK_CLEAR_SETUP_MII (1 << 17) ++ ++/* Some link partners do not tolerate the momentary reset of the REF_CLK ++ * frequency when the RNCTL register is cleared by hardware reset. ++ */ ++#define FEC_QUIRK_NO_HARD_RESET (1 << 18) ++ ++/* i.MX6SX ENET IP supports multiple queues (3 queues), use this quirk to ++ * represents this ENET IP. ++ */ ++#define FEC_QUIRK_HAS_MULTI_QUEUES (1 << 19) ++ ++/* i.MX8MQ ENET IP version add new feature to support IEEE 802.3az EEE ++ * standard. For the transmission, MAC supply two user registers to set ++ * Sleep (TS) and Wake (TW) time. ++ */ ++#define FEC_QUIRK_HAS_EEE (1 << 20) ++ ++/* i.MX8QM ENET IP version add new feture to generate delayed TXC/RXC ++ * as an alternative option to make sure it works well with various PHYs. ++ * For the implementation of delayed clock, ENET takes synchronized 250MHz ++ * clocks to generate 2ns delay. ++ */ ++#define FEC_QUIRK_DELAYED_CLKS_SUPPORT (1 << 21) ++ ++ ++/* i.MX8MQ SoC integration mix wakeup interrupt signal into "int2" interrupt line. */ ++#define FEC_QUIRK_WAKEUP_FROM_INT2 (1 << 22) ++ ++/* request pmqos during low power */ ++#define FEC_QUIRK_HAS_PMQOS (1 << 23) ++ ++struct bufdesc_prop { ++ int qid; ++ /* Address of Rx and Tx buffers */ ++ struct bufdesc *base; ++ struct bufdesc *last; ++ struct bufdesc *cur; ++ void __iomem *reg_desc_active; ++ dma_addr_t dma; ++ unsigned short ring_size; ++ unsigned char dsize; ++ unsigned char dsize_log2; ++}; ++ ++struct fec_enet_priv_tx_q { ++ struct bufdesc_prop bd; ++ struct bufdesc *dirty_tx; ++ struct sk_buff *tx_skbuff[FEC_TX_RING_SIZE]; ++ ++ unsigned int tx_bounce_size; ++ ++#ifdef CONFIG_AVB_SUPPORT ++ unsigned int tx_index; ++ unsigned char *tx_bounce[FEC_TX_RING_SIZE + 32]; ++#else ++ unsigned char *tx_bounce[FEC_TX_RING_SIZE]; ++#endif ++ unsigned short tx_stop_threshold; ++ unsigned short tx_wake_threshold; ++ ++ char *tso_hdrs; ++ dma_addr_t tso_hdrs_dma; ++ ++#ifdef CONFIG_AVB_SUPPORT ++ unsigned long tx_idle_slope; ++#endif ++}; ++ ++struct fec_enet_priv_rx_q { ++ struct bufdesc_prop bd; ++ struct sk_buff *rx_skbuff[FEC_RX_RING_SIZE]; ++}; ++ ++struct fec_stop_mode_gpr { ++ struct regmap *gpr; ++ u8 reg; ++ u8 bit; ++}; ++ ++#ifdef CONFIG_AVB_SUPPORT ++#define AVB_DMA_MAPPING 1 ++#endif ++ ++/* The FEC buffer descriptors track the ring buffers. The rx_bd_base and ++ * tx_bd_base always point to the base of the buffer descriptors. The ++ * cur_rx and cur_tx point to the currently available buffer. ++ * The dirty_tx tracks the current buffer that is being sent by the ++ * controller. The cur_tx and dirty_tx are equal under both completely ++ * empty and completely full conditions. The empty/ready indicator in ++ * the buffer descriptor determines the actual condition. ++ */ ++struct fec_enet_private { ++ /* Hardware registers of the FEC device */ ++ void __iomem *hwp; ++ ++ struct net_device *netdev; ++ ++ struct clk *clk_ipg; ++ struct clk *clk_ahb; ++ struct clk *clk_ref; ++ struct clk *clk_enet_out; ++ struct clk *clk_ptp; ++ struct clk *clk_2x_txclk; ++ ++ bool ptp_clk_on; ++ struct mutex ptp_clk_mutex; ++ unsigned int num_tx_queues; ++ unsigned int num_rx_queues; ++ ++ /* The saved address of a sent-in-place packet/buffer, for skfree(). */ ++ struct fec_enet_priv_tx_q *tx_queue; ++ struct fec_enet_priv_rx_q *rx_queue; ++ ++ unsigned int total_tx_ring_size; ++ unsigned int total_rx_ring_size; ++ ++#ifdef CONFIG_AVB_SUPPORT ++ const struct avb_ops *avb; ++ void *avb_data; ++ unsigned int avb_enabled; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_advertising); ++#endif ++ struct platform_device *pdev; ++ ++ int dev_id; ++ ++ /* Phylib and MDIO interface */ ++ struct mii_bus *mii_bus; ++ uint phy_speed; ++ phy_interface_t phy_interface; ++ struct device_node *phy_node; ++ bool rgmii_txc_dly; ++ bool rgmii_rxc_dly; ++ bool mii_bus_share; ++ bool rpm_active; ++ int link; ++ int full_duplex; ++ int speed; ++ int irq[FEC_IRQ_NUM]; ++ bool bufdesc_ex; ++ int pause_flag; ++ int wol_flag; ++ int wake_irq; ++ u32 quirks; ++ ++ struct napi_struct napi; ++ int csum_flags; ++ ++ struct work_struct tx_timeout_work; ++ ++ struct ptp_clock *ptp_clock; ++ struct ptp_clock_info ptp_caps; ++ unsigned long last_overflow_check; ++ raw_spinlock_t tmreg_lock; ++ struct cyclecounter cc; ++ struct timecounter tc; ++ int rx_hwtstamp_filter; ++ u32 base_incval; ++ u32 cycle_speed; ++ int hwts_rx_en; ++ int hwts_tx_en; ++ ++ /* Transmit and receive latency, depending on link speed, for ++ * packets timestamps in ns ++ */ ++ u32 rx_tstamp_latency; ++ u32 tx_tstamp_latency; ++ ++ struct delayed_work time_keep; ++ struct regulator *reg_phy; ++ struct fec_stop_mode_gpr stop_gpr; ++ struct pm_qos_request pm_qos_req; ++ ++ unsigned int tx_align; ++ unsigned int rx_align; ++ ++ /* hw interrupt coalesce */ ++ unsigned int rx_pkts_itr; ++ unsigned int rx_time_itr; ++ unsigned int tx_pkts_itr; ++ unsigned int tx_time_itr; ++ unsigned int itr_clk_rate; ++ ++ /* tx lpi eee mode */ ++ struct ethtool_eee eee; ++ unsigned int clk_ref_rate; ++ ++ u32 rx_copybreak; ++ ++ /* ptp clock period in ns*/ ++ unsigned int ptp_inc; ++ ++ /* pps */ ++ int pps_channel; ++ unsigned int reload_period; ++ int pps_enable; ++ unsigned int next_counter; ++ ++ struct mutex fast_ndev_lock; ++ struct imx_sc_ipc *ipc_handle; ++ ++ /* Configured rx/tx timestamps delays for different link speeds ++ * to compensate for FEC-PHY latency in ns ++ */ ++ u32 rx_delay_100; ++ u32 tx_delay_100; ++ u32 rx_delay_1000; ++ u32 tx_delay_1000; ++ ++#ifdef CONFIG_AVB_SUPPORT ++ int rec_channel; ++ int rec_enable; ++#endif ++ ++ u64 ethtool_stats[]; ++}; ++ ++#ifdef CONFIG_AVB_SUPPORT ++#define FEC_MAX_RATE 400 /* Mbps */ ++#define FEC_MAX_RATE_HAS_AVB 1000 /* Mbps */ ++ ++static inline int fec_max_rate(struct fec_enet_private *fep) ++{ ++ int max_rate = (fep->quirks & FEC_QUIRK_HAS_AVB) ? FEC_MAX_RATE_HAS_AVB : FEC_MAX_RATE; ++ return min(max_rate, fep->speed); ++} ++ ++#define IDLE_SLOPE_DIVISOR 512 ++#endif ++ ++void fec_ptp_init(struct platform_device *pdev, int irq_idx); ++void fec_ptp_stop(struct platform_device *pdev); ++void fec_ptp_start_cyclecounter(struct net_device *ndev); ++void fec_ptp_disable_hwts(struct net_device *ndev); ++int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr); ++int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr); ++ ++/****************************************************************************/ ++#endif /* FEC_H */ +diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c +index 23f7604d9dbc..290d6cfa7fc8 100644 +--- a/drivers/net/ethernet/freescale/fec_main.c ++++ b/drivers/net/ethernet/freescale/fec_main.c +@@ -77,17 +77,25 @@ + + static void set_multicast_list(struct net_device *ndev); + static void fec_enet_itr_coal_set(struct net_device *ndev); ++#ifndef CONFIG_AVB_SUPPORT + static int fec_enet_xdp_tx_xmit(struct net_device *ndev, + struct xdp_buff *xdp); + static int fec_enet_xdp_xmit(struct net_device *dev, + int num_frames, + struct xdp_frame **frames, + u32 flags); ++#endif + + #define DRIVER_NAME "fec" + + static const u16 fec_enet_vlan_pri_to_queue[8] = {0, 0, 1, 1, 1, 2, 2, 2}; + ++#ifdef CONFIG_AVB_SUPPORT ++/* Idle Slope values specific to AVB capable boards */ ++static const unsigned short idle_slope_values[] = {1, 2, 4, 8, 16, 32, 64, 128, ++ 256, 384, 512, 640, 768, 896, 1024, 1152, 1280, 1408, 1536}; ++#endif ++ + /* Pause frame feild and FIFO threshold */ + #define FEC_ENET_FCE (1 << 5) + #define FEC_ENET_RSEM_V 0x84 +@@ -339,6 +347,24 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); + + static int mii_cnt; + ++#ifdef CONFIG_AVB_SUPPORT ++static inline void read16(void *dst, void *src) ++{ ++#ifdef CONFIG_ARM64 ++ asm volatile ( "ldp x10, x11, [%1]\n\t" ++ "stp x10, x11, [%0]\n\t" ++ : : "r" (dst), "r" (src) : "x10", "x11", "memory"); ++#elif CONFIG_ARM ++ asm volatile ( "ldmia %1, {r5-r8}\n\t" ++ "stmia %0, {r5-r8}\n\t" ++ : : "r" (dst), "r" (src) : "r5", "r6", "r7", "r8", "memory"); ++#else ++ ((u64 *)dst)[0] = ((u64 *)src)[0]; ++ ((u64 *)dst)[1] = ((u64 *)src)[1]; ++#endif ++} ++#endif /*CONFIG_AVB_SUPPORT*/ ++ + static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, + struct bufdesc_prop *bd) + { +@@ -470,6 +496,253 @@ fec_enet_create_page_pool(struct fec_enet_private *fep, + return err; + } + ++#ifdef CONFIG_AVB_SUPPORT ++/* ++ * Sends an AVB buffer on the network. ++ */ ++int fec_enet_start_xmit_avb(void *data, struct avb_tx_desc *desc) ++{ ++ struct fec_enet_private *fep = data; ++ struct bufdesc *bdp; ++ unsigned short status; ++ unsigned int index; ++ struct bufdesc_ex *ebdp; ++ unsigned long cbd_esc; ++ unsigned short queue_id = desc->queue_id; ++ struct fec_enet_priv_tx_q *txq = fep->tx_queue[queue_id]; ++ ++ /* ring buffer base address */ ++ /* registers base address */ ++ /* current descriptor pointer */ ++ bdp = txq->bd.cur; ++ ++ if (bdp == txq->dirty_tx) ++ return -2; ++ ++ status = fec16_to_cpu(bdp->cbd_sc); ++ ++ if (status & BD_ENET_TX_READY) ++ return -2; ++ ++ /* Clear all of the status flags */ ++ status &= ~BD_ENET_TX_STATS; ++ ++ index = fec_enet_get_bd_index(bdp, &txq->bd); ++ ++ /* Save desc pointer */ ++ txq->tx_buf[index].skb = (void *)desc; ++ ++ bdp->cbd_datlen = cpu_to_fec16(desc->common.len); ++ bdp->cbd_bufaddr = cpu_to_fec32(desc->dma_addr); ++ ++ ebdp = (struct bufdesc_ex *)bdp; ++ ++ ebdp->cbd_bdu = cpu_to_fec32(0); ++ ++ if (desc->common.flags & AVB_TX_FLAG_HW_TS) ++ cbd_esc = BD_ENET_TX_TS | desc->esc; ++ else ++ cbd_esc = desc->esc; ++ ++ if (desc->common.flags & AVB_TX_FLAG_HW_CSUM) ++ cbd_esc |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; ++ ++ if (fep->quirks & FEC_QUIRK_HAS_AVB) ++ cbd_esc |= FEC_TX_BD_FTYPE(txq->bd.qid); ++ ++ ebdp->cbd_esc = cpu_to_fec32(cbd_esc); ++ ++ wmb(); ++ ++ bdp->cbd_sc = cpu_to_fec16(status | (BD_ENET_TX_READY | BD_ENET_TX_LAST | BD_ENET_TX_TC)); ++ ++ /* If this was the last BD in the ring, start at the beginning again. */ ++ bdp = fec_enet_get_nextdesc(bdp, &txq->bd); ++ ++ txq->bd.cur = bdp; ++ ++ /* Trigger transmission start */ ++ if (!(fep->quirks & FEC_QUIRK_ERR006358)) ++ if (!(fep->quirks & FEC_QUIRK_ERR007885) || ++ !readl(txq->bd.reg_desc_active) || ++ !readl(txq->bd.reg_desc_active) || ++ !readl(txq->bd.reg_desc_active) || ++ !readl(txq->bd.reg_desc_active)) ++ writel(0, txq->bd.reg_desc_active); ++ ++ if (bdp == txq->dirty_tx) ++ return -1; ++ ++ return 0; ++} ++EXPORT_SYMBOL(fec_enet_start_xmit_avb); ++ ++void fec_enet_finish_xmit_avb(void *data, unsigned int queue_id) ++{ ++ struct fec_enet_private *fep = data; ++ ++ /* Trigger transmission start */ ++ if (fep->quirks & FEC_QUIRK_ERR006358) ++ if (!(fep->quirks & FEC_QUIRK_ERR007885) || ++ !readl(fep->tx_queue[queue_id]->bd.reg_desc_active) || ++ !readl(fep->tx_queue[queue_id]->bd.reg_desc_active) || ++ !readl(fep->tx_queue[queue_id]->bd.reg_desc_active) || ++ !readl(fep->tx_queue[queue_id]->bd.reg_desc_active)) ++ writel(0, fep->tx_queue[queue_id]->bd.reg_desc_active); ++} ++EXPORT_SYMBOL(fec_enet_finish_xmit_avb); ++ ++/* ++ * When AVB is enabled, it is the transmit function for the regular ++ * network traffic. It does not support any SG/TSO skb. ++ * Frames are posted to the AVB module for further scheduling. ++ */ ++static int fec_enet_start_xmit_best_effort(struct fec_enet_priv_tx_q *txq, ++ struct netdev_queue *nq, struct sk_buff *skb, struct net_device *ndev) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ void *bufaddr; ++ struct avb_tx_desc *desc; ++ int rc; ++ ++ if (fep->avb->tx_full(fep->avb_data)) { ++ netdev_err(ndev, "tx queue full!\n"); ++ return NETDEV_TX_BUSY; ++ } ++ ++ /* Protocol checksum off-load for TCP and UDP. */ ++ if (fec_enet_clear_csum(skb, ndev)) { ++ dev_kfree_skb_any(skb); ++ ndev->stats.tx_dropped++; ++ return NETDEV_TX_OK; ++ } ++ ++ if (skb_headroom(skb) < sizeof(struct avb_tx_desc)) { ++ if (pskb_expand_head(skb, sizeof(struct avb_tx_desc), 0, GFP_ATOMIC)) { ++ dev_kfree_skb_any(skb); ++ ndev->stats.tx_dropped++; ++ return NETDEV_TX_OK; ++ } ++ } ++ ++ bufaddr = skb->data; ++ desc = (struct avb_tx_desc *)(skb->data - sizeof(struct avb_tx_desc)); ++ ++ /* Set buffer length and buffer pointer */ ++ desc->common.offset = sizeof(struct avb_tx_desc); ++ desc->common.len = skb->len; ++ desc->queue_id = txq->bd.qid; ++ ++ if ((((unsigned long) bufaddr) & fep->tx_align) || ++ (fep->quirks & FEC_QUIRK_SWAP_FRAME)) { ++ memcpy(txq->tx_bounce[txq->tx_index], skb->data, skb->len); ++ bufaddr = txq->tx_bounce[txq->tx_index]; ++ ++ txq->tx_index++; ++ if (txq->tx_index >= txq->tx_bounce_size) ++ txq->tx_index = 0; ++ ++ if (fep->quirks & FEC_QUIRK_SWAP_FRAME) ++ swap_buffer(bufaddr, skb->len); ++ } ++ ++ /* Save skb pointer */ ++ desc->data = skb; ++ desc->common.flags = AVB_TX_FLAG_SKB; ++ ++ /* Push the data cache so the CPM does not get stale memory data. */ ++ desc->dma_addr = dma_map_single(&fep->pdev->dev, bufaddr, skb->len, DMA_TO_DEVICE); ++ if (dma_mapping_error(&fep->pdev->dev, desc->dma_addr)) { ++ dev_kfree_skb_any(skb); ++ if (net_ratelimit()) ++ netdev_err(ndev, "Tx DMA memory map failed\n"); ++ return NETDEV_TX_OK; ++ } ++ ++ desc->esc = 0; ++ ++ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && ++ fep->hwts_tx_en)) { ++ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; ++ desc->esc |= BD_ENET_TX_TS; ++ } ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) ++ desc->esc |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; ++ ++ skb_tx_timestamp(skb); ++ ++ if ((rc = fep->avb->tx(fep->avb_data, desc)) < 0) { ++ if (rc < -1) { ++ kfree_skb(skb); ++ ndev->stats.tx_dropped++; ++ } ++ ++ netif_tx_stop_queue(nq); ++ return NETDEV_TX_OK; ++ } ++ ++ return NETDEV_TX_OK; ++} ++ ++int fec_enet_set_idle_slope(void *data, unsigned int queue_id, ++ u32 desired_rate) ++{ ++ struct fec_enet_private *fep = data; ++ struct fec_enet_priv_tx_q *txq; ++ u64 idle_slope; ++ u32 line_rate; ++ int i; ++ ++ if (!fep) ++ return -EINVAL; ++ ++ /* Nothing to be done for non-AVB boards */ ++ if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) ++ return 0; ++ ++ if ((queue_id == 0) || (queue_id >= fep->num_tx_queues)) ++ return -EINVAL; ++ ++ if ((fep->speed != SPEED_100) && (fep->speed != SPEED_1000)) ++ return -EOPNOTSUPP; ++ ++ line_rate = fep->speed * 1000000ULL; ++ if (desired_rate > line_rate) ++ return -EINVAL; ++ ++ txq = fep->tx_queue[queue_id]; ++ ++ /* ++ * Compute the desired Idle-Slope based on the desired rate and use ++ * the round up to the next integer. ++ */ ++ idle_slope = (u64)desired_rate * IDLE_SLOPE_DIVISOR + line_rate - desired_rate - 1; ++ idle_slope = div_u64(idle_slope, line_rate - desired_rate); ++ ++ for (i = 0; i < ARRAY_SIZE(idle_slope_values); i++) ++ if (idle_slope <= idle_slope_values[i]) ++ break; ++ ++ if (i >= ARRAY_SIZE(idle_slope_values)) ++ return -EINVAL; ++ /* ++ * If the desired rate is higher than the last available bandwidth ++ * threshold then we should not configure the Credit-Based shaper ++ * at all. ++ */ ++ if (idle_slope > idle_slope_values[i]) ++ return -EINVAL; ++ ++ txq->tx_idle_slope = idle_slope_values[i]; ++ writel(DMA_CLASS_EN | txq->tx_idle_slope, ++ fep->hwp + FEC_DMA_CFG(queue_id)); ++ ++ return 0; ++} ++EXPORT_SYMBOL(fec_enet_set_idle_slope); ++#endif ++ + static struct bufdesc * + fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, + struct sk_buff *skb, +@@ -681,7 +954,12 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, + txq->bd.cur = bdp; + + /* Trigger transmission start */ +- writel(0, txq->bd.reg_desc_active); ++ if (!(fep->quirks & FEC_QUIRK_ERR007885) || ++ !readl(txq->bd.reg_desc_active) || ++ !readl(txq->bd.reg_desc_active) || ++ !readl(txq->bd.reg_desc_active) || ++ !readl(txq->bd.reg_desc_active)) ++ writel(0, txq->bd.reg_desc_active); + + return 0; + } +@@ -898,6 +1176,11 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) + txq = fep->tx_queue[queue]; + nq = netdev_get_tx_queue(ndev, queue); + ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ return fec_enet_start_xmit_best_effort(txq, nq, skb, ndev); ++#endif ++ + if (skb_is_gso(skb)) + ret = fec_enet_txq_submit_tso(txq, skb, ndev); + else +@@ -922,6 +1205,9 @@ static void fec_enet_bd_init(struct net_device *dev) + struct bufdesc *bdp; + unsigned int i; + unsigned int q; ++#ifdef CONFIG_AVB_SUPPORT ++ struct sk_buff *skb; ++#endif + + for (q = 0; q < fep->num_rx_queues; q++) { + /* Initialize the receive buffer descriptors. */ +@@ -945,6 +1231,23 @@ static void fec_enet_bd_init(struct net_device *dev) + rxq->bd.cur = rxq->bd.base; + } + ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) { ++ struct avb_tx_desc *desc; ++ ++ while ((desc = fep->avb->tx_cleanup_dequeue(fep->avb_data)) != (void *) -1) { ++ ++ skb = desc->data; ++ ++ dma_unmap_single(&fep->pdev->dev, desc->bufaddr, ++ skb->len, DMA_TO_DEVICE); ++ ++ /* Free the sk buffer associated with this last transmit */ ++ dev_kfree_skb_any(skb); ++ } ++ } ++#endif ++ + for (q = 0; q < fep->num_tx_queues; q++) { + /* ...and the same for transmit */ + txq = fep->tx_queue[q]; +@@ -954,6 +1257,42 @@ static void fec_enet_bd_init(struct net_device *dev) + for (i = 0; i < txq->bd.ring_size; i++) { + /* Initialize the BD for every fragment in the page. */ + bdp->cbd_sc = cpu_to_fec16(0); ++#ifdef CONFIG_AVB_SUPPORT ++ /* AVB not compatible with XDP: all buffers are either avb descriptors or skbs. */ ++ if (txq->tx_buf[i].skb) { ++ skb = NULL; ++ if (fep->avb_enabled) { ++ struct avb_tx_desc *desc = (struct avb_tx_desc *)txq->tx_buf[i].skb; ++ ++ if (!(desc->common.flags & AVB_TX_FLAG_SKB)) { ++ fep->avb->free(fep->avb_data, &desc->common); ++ /* Avoid unmapping AVB buffers below */ ++ bdp->cbd_bufaddr = cpu_to_fec32(0); ++ } else { ++ skb = desc->data; ++ } ++ } else ++ skb = txq->tx_buf[i].skb; ++ ++ if (skb) { ++ dev_kfree_skb_any(skb); ++ } ++ ++ txq->tx_buf[i].skb = NULL; ++ } ++ ++ if (bdp->cbd_bufaddr && ++ !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) ++ dma_unmap_single(&fep->pdev->dev, ++ fec32_to_cpu(bdp->cbd_bufaddr), ++ fec16_to_cpu(bdp->cbd_datlen), ++ DMA_TO_DEVICE); ++ if (txq->tx_buf[i].skb) { ++ dev_kfree_skb_any(txq->tx_buf[i].skb); ++ txq->tx_buf[i].skb = NULL; ++ } ++ ++#else + if (txq->tx_buf[i].type == FEC_TXBUF_T_SKB) { + if (bdp->cbd_bufaddr && + !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) +@@ -981,7 +1320,7 @@ static void fec_enet_bd_init(struct net_device *dev) + + txq->tx_buf[i].type = FEC_TXBUF_T_SKB; + } +- ++#endif + bdp->cbd_bufaddr = cpu_to_fec32(0); + bdp = fec_enet_get_nextdesc(bdp, &txq->bd); + } +@@ -1007,6 +1346,7 @@ static void fec_enet_enable_ring(struct net_device *ndev) + struct fec_enet_private *fep = netdev_priv(ndev); + struct fec_enet_priv_tx_q *txq; + struct fec_enet_priv_rx_q *rxq; ++ unsigned long idle_slope; + int i; + + for (i = 0; i < fep->num_rx_queues; i++) { +@@ -1025,10 +1365,25 @@ static void fec_enet_enable_ring(struct net_device *ndev) + writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i)); + + /* enable DMA1/2 */ +- if (i) +- writel(DMA_CLASS_EN | IDLE_SLOPE(i), ++ if (i) { ++#ifdef CONFIG_AVB_SUPPORT ++ idle_slope = txq->tx_idle_slope; ++#else ++ idle_slope = IDLE_SLOPE(i); ++#endif ++ ++ writel(DMA_CLASS_EN | idle_slope, + fep->hwp + FEC_DMA_CFG(i)); ++ } + } ++ ++ /* ++ * For AVB capable devices we should enable RX flushing for the ++ * best effort queue (ring 0) and also the TX credit based shaper. ++ */ ++ if (fep->quirks & FEC_QUIRK_HAS_AVB) ++ writel(FEC_RX_FLUSH(0) | FEC_TX_SCHEME_CB, ++ fep->hwp + FEC_QOS_SCHEME); + } + + /* +@@ -1044,17 +1399,12 @@ fec_restart(struct net_device *ndev) + u32 rcntl = OPT_FRAME_SIZE | 0x04; + u32 ecntl = 0x2; /* ETHEREN */ + +- /* Whack a reset. We should wait for this. +- * For i.MX6SX SOC, enet use AXI bus, we use disable MAC +- * instead of reset MAC itself. ++ /* Always use disable MAC instead of MAC reset to: ++ * - Keep the ENET counter running ++ * - Avoid dead system bus for SoCs using the ENET-AXI bus ++ * and not the AHB bus, like the i.MX6SX + */ +- if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES || +- ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) { +- writel(0, fep->hwp + FEC_ECNTRL); +- } else { +- writel(1, fep->hwp + FEC_ECNTRL); +- udelay(10); +- } ++ writel(0, fep->hwp + FEC_ECNTRL); + + /* + * enet-mac reset will reset mac address registers too, +@@ -1211,9 +1561,6 @@ fec_restart(struct net_device *ndev) + writel(ecntl, fep->hwp + FEC_ECNTRL); + fec_enet_active_rxring(ndev); + +- if (fep->bufdesc_ex) +- fec_ptp_start_cyclecounter(ndev); +- + /* Enable interrupts we wish to service */ + if (fep->link) + writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); +@@ -1303,17 +1650,14 @@ fec_stop(struct net_device *ndev) + netdev_err(ndev, "Graceful transmit stop did not complete!\n"); + } + +- /* Whack a reset. We should wait for this. +- * For i.MX6SX SOC, enet use AXI bus, we use disable MAC +- * instead of reset MAC itself. +- */ + if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { +- if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { +- writel(0, fep->hwp + FEC_ECNTRL); +- } else { +- writel(1, fep->hwp + FEC_ECNTRL); +- udelay(10); +- } ++ /* Always use disable MAC instead of MAC reset to: ++ * - Keep the ENET counter running ++ * - Avoid dead system bus for SoCs using the ENET-AXI bus ++ * and not the AHB bus, like the i.MX6SX ++ */ ++ writel(0, fep->hwp + FEC_ECNTRL); ++ + } else { + val = readl(fep->hwp + FEC_ECNTRL); + val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); +@@ -1353,7 +1697,15 @@ static void fec_enet_timeout_work(struct work_struct *work) + if (netif_device_present(ndev) || netif_running(ndev)) { + napi_disable(&fep->napi); + netif_tx_lock_bh(ndev); ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ fep->avb->close(fep->avb_data); ++#endif + fec_restart(ndev); ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ fep->avb->open(fep->avb_data, fep, fec_max_rate(fep)); ++#endif + netif_tx_wake_all_queues(ndev); + netif_tx_unlock_bh(ndev); + napi_enable(&fep->napi); +@@ -1368,14 +1720,194 @@ fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, + unsigned long flags; + u64 ns; + +- spin_lock_irqsave(&fep->tmreg_lock, flags); ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + ns = timecounter_cyc2time(&fep->tc, ts); +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + memset(hwtstamps, 0, sizeof(*hwtstamps)); + hwtstamps->hwtstamp = ns_to_ktime(ns); + } + ++#ifdef CONFIG_AVB_SUPPORT ++static int ++fec_enet_tx_queue_avb(struct net_device *ndev, u16 queue_id) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct bufdesc *bdp; ++ struct bufdesc_ex local_ebdp; ++ struct avb_tx_desc *desc; ++ unsigned short status; ++ struct fec_enet_priv_tx_q *txq; ++ struct netdev_queue *nq; ++ int index = 0; ++ int rc = 0; ++ unsigned int total_tx_packets = 0; ++ unsigned int total_tx_bytes = 0; ++ u16 tx_tstamp_latency = fep->tx_tstamp_latency; ++ ++ txq = fep->tx_queue[queue_id]; ++ nq = netdev_get_tx_queue(ndev, queue_id); ++ ++ bdp = txq->dirty_tx; ++ ++ /* get next bdp of dirty_tx */ ++ bdp = fec_enet_get_nextdesc(bdp, &txq->bd); ++ ++ while (bdp != READ_ONCE(txq->bd.cur)) { ++ /* Order the load of cur_tx and cbd_sc */ ++ rmb(); ++ ++ /* Read the first 16 bytes of the descriptor at once to avoid ++ * multiple reads of non cacheable memory from RAM */ ++ read16(&local_ebdp, bdp); ++ ++ status = fec16_to_cpu(local_ebdp.desc.cbd_sc); ++ if (status & BD_ENET_TX_READY) ++ break; ++ ++ index = fec_enet_get_bd_index(bdp, &txq->bd); ++ desc = (struct avb_tx_desc *)txq->tx_buf[index].skb; ++ ++ if (!(desc->common.flags & AVB_TX_FLAG_SKB)) { ++ if ((desc->common.flags & AVB_TX_FLAG_HW_TS)) { ++ struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; ++ ++ desc->common.ts = ebdp->ts + tx_tstamp_latency; ++ ++ /* upper layer will retrieve the timestamp and free the descriptor */ ++ rc |= fep->avb->tx_ts(fep->avb_data, &desc->common); ++ } ++ else ++ fep->avb->free(fep->avb_data, &desc->common); ++ ++ total_tx_packets++; ++ total_tx_bytes += desc->datlen; ++ } else { ++ /* Backup hardware descriptor fields in software descriptor */ ++ desc->sc = status; ++ desc->datlen = fec16_to_cpu(local_ebdp.desc.cbd_datlen); ++ desc->bufaddr = fec32_to_cpu(local_ebdp.desc.cbd_bufaddr); ++ desc->common.ts = fec32_to_cpu(((struct bufdesc_ex *)bdp)->ts); ++ ++ if (fep->avb->tx_cleanup(fep->avb_data, desc) < 0) ++ BUG(); ++ } ++ ++ txq->tx_buf[index].skb = NULL; ++ bdp->cbd_bufaddr = cpu_to_fec32(0); ++ txq->dirty_tx = bdp; ++ ++ /* Update pointer to next buffer descriptor to be transmitted */ ++ bdp = fec_enet_get_nextdesc(bdp, &txq->bd); ++ } ++ ++ /* schedule tx napi, based on level of tx cleanup queue or time passed */ ++ if (fep->avb->tx_cleanup_ready(fep->avb_data) || netif_tx_queue_stopped(nq)) { ++ if (napi_schedule_prep(&fep->napi)) { ++ __napi_schedule(&fep->napi); ++ } ++ } ++ ++ /* Update stats*/ ++ ndev->stats.tx_packets += total_tx_packets; ++ ndev->stats.tx_bytes += total_tx_bytes; ++ ++ return rc; ++} ++ ++int ++fec_enet_tx_avb(void *data) ++{ ++ struct fec_enet_private *fep = data; ++ u16 queue_id; ++ int rc = 0; ++ ++ for (queue_id = 0; queue_id < fep->num_tx_queues; queue_id++) ++ rc |= fec_enet_tx_queue_avb(fep->netdev, queue_id); ++ ++ return rc; ++} ++EXPORT_SYMBOL(fec_enet_tx_avb); ++ ++static void ++fec_enet_tx_best_effort(struct net_device *ndev) ++{ ++ struct fec_enet_private *fep; ++ unsigned short status; ++ struct avb_tx_desc *desc; ++ struct sk_buff *skb; ++ struct fec_enet_priv_tx_q *txq; ++ struct netdev_queue *nq; ++ ++ fep = netdev_priv(ndev); ++ ++ while ((desc = fep->avb->tx_cleanup_dequeue(fep->avb_data)) != (void *) -1) { ++ ++ txq = fep->tx_queue[desc->queue_id]; ++ nq = netdev_get_tx_queue(ndev, desc->queue_id); ++ ++ status = desc->sc; ++ ++ /* Check for errors. */ ++ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | ++ BD_ENET_TX_RL | BD_ENET_TX_UN | ++ BD_ENET_TX_CSL)) { ++ ndev->stats.tx_errors++; ++ if (status & BD_ENET_TX_HB) /* No heartbeat */ ++ ndev->stats.tx_heartbeat_errors++; ++ if (status & BD_ENET_TX_LC) /* Late collision */ ++ ndev->stats.tx_window_errors++; ++ if (status & BD_ENET_TX_RL) /* Retrans limit */ ++ ndev->stats.tx_aborted_errors++; ++ if (status & BD_ENET_TX_UN) /* Underrun */ ++ ndev->stats.tx_fifo_errors++; ++ if (status & BD_ENET_TX_CSL) /* Carrier lost */ ++ ndev->stats.tx_carrier_errors++; ++ } else { ++ ndev->stats.tx_packets++; ++ ndev->stats.tx_bytes += desc->datlen; ++ } ++ ++ skb = desc->data; ++ ++ if (!IS_TSO_HEADER(txq, desc->bufaddr)) ++ dma_unmap_single(&fep->pdev->dev, desc->bufaddr, ++ desc->datlen, DMA_TO_DEVICE); ++ ++ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { ++ struct skb_shared_hwtstamps shhwtstamps; ++ ++ desc->common.ts += fep->tx_tstamp_latency; ++ fec_enet_hwtstamp(fep, desc->common.ts, ++ &shhwtstamps); ++ skb_tstamp_tx(skb, &shhwtstamps); ++ } ++ ++ /* Deferred means some collisions occurred during transmit, ++ * but we eventually sent the packet OK. ++ */ ++ if (status & BD_ENET_TX_DEF) ++ ndev->stats.collisions++; ++ ++ /* Free the sk buffer associated with this last transmit */ ++ dev_kfree_skb_any(skb); ++ ++ /* Make sure the update to bdp and tx_buf are performed ++ * before dirty_tx ++ */ ++ wmb(); ++ ++ //FIXME add treshold ++ if (!fep->avb->tx_full(fep->avb_data)) { ++ if (netif_tx_queue_stopped(nq)) { ++ //` netdev_info(ndev, "wake queue\n"); ++ netif_tx_wake_queue(nq); ++ } ++ } ++ } ++} ++#endif ++ + static void + fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) + { +@@ -1474,6 +2006,11 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; + + fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); ++ /* Adjust for TX MAC-PHY latency ++ */ ++ shhwtstamps.hwtstamp = ++ ktime_add_ns(shhwtstamps.hwtstamp, fep->tx_tstamp_latency); ++ + skb_tstamp_tx(skb, &shhwtstamps); + } + +@@ -1538,6 +2075,7 @@ static void fec_enet_update_cbd(struct fec_enet_priv_rx_q *rxq, + bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); + } + ++#ifndef CONFIG_AVB_SUPPORT + static u32 + fec_enet_run_xdp(struct fec_enet_private *fep, struct bpf_prog *prog, + struct xdp_buff *xdp, struct fec_enet_priv_rx_q *rxq, int index) +@@ -1572,32 +2110,302 @@ fec_enet_run_xdp(struct fec_enet_private *fep, struct bpf_prog *prog, + } + break; + +- default: +- bpf_warn_invalid_xdp_action(fep->netdev, prog, act); +- fallthrough; ++ default: ++ bpf_warn_invalid_xdp_action(fep->netdev, prog, act); ++ fallthrough; ++ ++ case XDP_TX: ++ ret = fec_enet_xdp_tx_xmit(fep->netdev, xdp); ++ if (ret == FEC_ENET_XDP_CONSUMED) { ++ page = virt_to_head_page(xdp->data); ++ page_pool_put_page(rxq->page_pool, page, sync, true); ++ } else { ++ ret = FEC_ENET_XDP_TX; ++ } ++ break; ++ case XDP_ABORTED: ++ fallthrough; /* handle aborts by dropping packet */ ++ ++ case XDP_DROP: ++ rxq->stats[RX_XDP_DROP]++; ++ ret = FEC_ENET_XDP_CONSUMED; ++ page = virt_to_head_page(xdp->data); ++ page_pool_put_page(rxq->page_pool, page, sync, true); ++ break; ++ } ++ ++ return ret; ++} ++#else /* CONFIG_AVB_SUPPORT */ ++ ++static int ++fec_enet_rx_best_effort(struct net_device *ndev, int budget) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct avb_rx_desc *desc; ++ struct sk_buff *skb; ++ ushort pkt_len; ++ __u8 *data; ++ bool vlan_packet_rcvd = false; ++ u16 vlan_tag; ++ int pkt_received = 0; ++ ++ do { ++ desc = fep->avb->dequeue(fep->avb_data); ++ if (desc == (void *)-1) ++ break; ++ ++ /* Process the incoming frame. */ ++ pkt_len = desc->common.len; ++ data = (u8 *)desc + desc->common.offset; ++ ++ skb = netdev_alloc_skb(ndev, pkt_len - 4); ++ if (unlikely(!skb)) { ++ ndev->stats.rx_dropped++; ++ goto rx_processing_done; ++ } ++ else { ++ /* Make some room minus FCS */ ++ skb_put(skb, pkt_len - 4); ++ ++ /* Copy AVB buffer to skb */ ++ skb_copy_to_linear_data(skb, data, pkt_len - 4); ++ data = skb->data; ++ ++ /* Get receive timestamp from the skb */ ++ if (fep->hwts_rx_en) { ++ skb_reset_mac_header(skb); ++ fec_enet_hwtstamp(fep, desc->common.ts, ++ skb_hwtstamps(skb)); ++ } ++ ++ /* If this is a VLAN packet remove the VLAN Tag */ ++ vlan_packet_rcvd = false; ++ if (desc->common.private & BD_ENET_RX_VLAN) { ++ /* Push and remove the vlan tag */ ++ struct vlan_hdr *vlan_header = ++ (struct vlan_hdr *) (data + ETH_HLEN); ++ vlan_tag = ntohs(vlan_header->h_vlan_TCI); ++ ++ vlan_packet_rcvd = true; ++ ++ memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); ++ ++ skb_pull(skb, VLAN_HLEN); ++ } ++ ++ skb->protocol = eth_type_trans(skb, ndev); ++ ++ if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) { ++ if (!(desc->common.private & FLAG_RX_CSUM_ERROR)) { ++ /* don't check it */ ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ } else { ++ skb_checksum_none_assert(skb); ++ } ++ } ++ ++ /* Handle received VLAN packets */ ++ if (vlan_packet_rcvd) ++ __vlan_hwaccel_put_tag(skb, ++ htons(ETH_P_8021Q), ++ vlan_tag); ++ ++ napi_gro_receive(&fep->napi, skb); ++ } ++rx_processing_done: ++ fep->avb->free(fep->avb_data, &desc->common); ++ ++ } while (++pkt_received < budget); ++ ++ return pkt_received; ++} ++ ++static unsigned int ++fec_enet_rx_queue_avb(struct net_device *ndev, u16 queue_id) ++{ ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ struct fec_enet_priv_rx_q *rxq; ++ struct bufdesc *bdp; ++ unsigned short status; ++ ushort pkt_len; ++ __u8 *data, *new_data; ++ int index = 0; ++ struct bufdesc_ex *ebdp = NULL; ++ struct bufdesc_ex local_ebdp; ++ struct avb_rx_desc *desc; ++ unsigned int rc = 0; ++ unsigned int net_data_offset; ++ unsigned int count = 0; ++ unsigned int total_rx_packets = 0; ++ unsigned int total_rx_bytes = 0; ++ u16 rx_tstamp_latency = fep->rx_tstamp_latency; ++ bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; ++ bool has_racc = fep->quirks & FEC_QUIRK_HAS_RACC; ++ ++ rxq = fep->rx_queue[queue_id]; ++ ++ /* First, grab all of the stats for the incoming packet. ++ * These get messed up if we get called due to a busy condition. ++ */ ++ bdp = rxq->bd.cur; ++ ++ /* 20 packets per 125us > 64 bytes packets @ 100Mbps */ ++ while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY) && (count++ < 20)) { ++ ++ writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT); ++ ++ /* Read the first 16 bytes of the descriptor at once to avoid ++ * multiple reads of non cacheable memory from RAM */ ++ read16(&local_ebdp, bdp); ++ ++ /* Check for errors. */ ++ status ^= BD_ENET_RX_LAST; ++ if (unlikely(status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | ++ BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | ++ BD_ENET_RX_CL))) { ++ ndev->stats.rx_errors++; ++ if (status & BD_ENET_RX_OV) { ++ /* FIFO overrun */ ++ ndev->stats.rx_fifo_errors++; ++ goto rx_processing_done; ++ } ++ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH ++ | BD_ENET_RX_LAST)) { ++ /* Frame too long or too short. */ ++ ndev->stats.rx_length_errors++; ++ if (status & BD_ENET_RX_LAST) ++ netdev_err(ndev, "rcv is not +last\n"); ++ } ++ if (status & BD_ENET_RX_CR) /* CRC Error */ ++ ndev->stats.rx_crc_errors++; ++ /* Report late collisions as a frame error. */ ++ if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) ++ ndev->stats.rx_frame_errors++; ++ goto rx_processing_done; ++ } ++ ++ new_data = fep->avb->alloc(fep->avb_data); ++ if (unlikely(!new_data)) { ++ ndev->stats.rx_dropped++; ++ goto rx_processing_done; ++ } ++ ++ /* Process the incoming frame. */ ++ total_rx_packets++; ++ pkt_len = fec16_to_cpu(local_ebdp.desc.cbd_datlen); ++ total_rx_bytes += pkt_len; ++ index = fec_enet_get_bd_index(bdp, &rxq->bd); ++ data = (__u8 *)rxq->rx_skb_info[index].skb; ++ ++ /* FIXME, skip unmap of audio data */ ++ dma_sync_single_for_cpu(&fep->pdev->dev, fec32_to_cpu(local_ebdp.desc.cbd_bufaddr), ++ L1_CACHE_ALIGN(pkt_len), DMA_FROM_DEVICE); ++ ++ desc = (struct avb_rx_desc *)data; ++ ++ net_data_offset = desc->common.offset; ++ ++#if !defined(CONFIG_M5272) ++ if (has_racc) ++ net_data_offset -= 2; ++#endif ++ prefetch(data + net_data_offset); ++ ++ if (need_swap) ++ swap_buffer(data, pkt_len); ++ ++ desc->common.len = pkt_len; ++ desc->sc = fec16_to_cpu(local_ebdp.desc.cbd_sc); ++ ++ /* Extract the enhanced buffer descriptor */ ++ ebdp = (struct bufdesc_ex *)bdp; ++ ++ desc->common.ts = ebdp->ts - rx_tstamp_latency; ++ desc->common.private = fec32_to_cpu(local_ebdp.cbd_esc); ++ ++ rc |= fep->avb->rx(fep->avb_data, desc); ++ ++ data = new_data; ++ ++ desc = (struct avb_rx_desc *)data; ++ ++ desc->common.len = 0; ++ desc->queue_id = queue_id; ++ ++ bdp->cbd_bufaddr = cpu_to_fec32((dma_addr_t)(desc->dma_addr)); ++ ++#if !defined(CONFIG_M5272) ++ if (has_racc) ++ desc->common.offset += 2; ++#endif ++ rxq->rx_skb_info[index].skb = (void *)data; ++rx_processing_done: ++ /* Clear the status flags for this buffer */ ++ status &= ~BD_ENET_RX_STATS; ++ ++ /* Mark the buffer empty */ ++ status |= BD_ENET_RX_EMPTY; ++ bdp->cbd_sc = cpu_to_fec16(status); ++ ++ ebdp = (struct bufdesc_ex *)bdp; ++ ++ ebdp->cbd_esc = cpu_to_fec32(0); ++ ebdp->cbd_prot = cpu_to_fec32(0); ++ ebdp->cbd_bdu = cpu_to_fec32(0); ++ ++ /* Update BD pointer to next entry */ ++ ++ bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); ++ } ++ ++ /* If the receive ring buffer can hold at least double the maximum ++ number of packets per polling period (18.2 packets @ 100Mbps), it's ++ ok to only re-enable receive after processing all current packets */ ++ ++ writel(0, rxq->bd.reg_desc_active); ++ ++ rxq->bd.cur = bdp; ++ ++ /*Update stats*/ ++ ndev->stats.rx_packets += total_rx_packets; ++ ndev->stats.rx_bytes += total_rx_bytes; ++ ++ return rc; ++} ++ ++static unsigned int ++fec_enet_rx_avb(struct net_device *ndev) ++{ ++ u16 queue_id; ++ struct fec_enet_private *fep = netdev_priv(ndev); ++ unsigned int rc = 0; ++ ++ for (queue_id = 0; queue_id < fep->num_rx_queues; queue_id++) ++ rc |= fec_enet_rx_queue_avb(ndev, queue_id); ++ ++ return rc; ++} ++ ++int fec_enet_rx_poll_avb(void *data) ++{ ++ struct fec_enet_private *fep = data; ++ struct net_device *ndev = fep->netdev; ++ unsigned int rc; + +- case XDP_TX: +- ret = fec_enet_xdp_tx_xmit(fep->netdev, xdp); +- if (ret == FEC_ENET_XDP_CONSUMED) { +- page = virt_to_head_page(xdp->data); +- page_pool_put_page(rxq->page_pool, page, sync, true); +- } else { +- ret = FEC_ENET_XDP_TX; +- } +- break; +- case XDP_ABORTED: +- fallthrough; /* handle aborts by dropping packet */ ++ rc = fec_enet_rx_avb(ndev); + +- case XDP_DROP: +- rxq->stats[RX_XDP_DROP]++; +- ret = FEC_ENET_XDP_CONSUMED; +- page = virt_to_head_page(xdp->data); +- page_pool_put_page(rxq->page_pool, page, sync, true); +- break; ++ if (rc & AVB_WAKE_NAPI) { ++ /* Best effort packets were posted, schedule napi if not scheduled yet. */ ++ if (napi_schedule_prep(&fep->napi)) ++ __napi_schedule(&fep->napi); + } + +- return ret; ++ return rc; + } ++EXPORT_SYMBOL(fec_enet_rx_poll_avb); ++ ++#endif /* CONFIG_AVB_SUPPORT */ + + /* During a receive, the bd_rx.cur points to the current incoming buffer. + * When we update through the ring, if the next incoming buffer has +@@ -1620,10 +2428,12 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) + u16 vlan_tag; + int index = 0; + bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; ++#ifndef CONFIG_AVB_SUPPORT + struct bpf_prog *xdp_prog = READ_ONCE(fep->xdp_prog); + u32 ret, xdp_result = FEC_ENET_XDP_PASS; +- u32 data_start = FEC_ENET_XDP_HEADROOM; + struct xdp_buff xdp; ++#endif ++ u32 data_start = FEC_ENET_XDP_HEADROOM; + struct page *page; + u32 sub_len = 4; + +@@ -1646,7 +2456,9 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) + * These get messed up if we get called due to a busy condition. + */ + bdp = rxq->bd.cur; ++#ifndef CONFIG_AVB_SUPPORT + xdp_init_buff(&xdp, PAGE_SIZE, &rxq->xdp_rxq); ++#endif + + while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { + +@@ -1696,6 +2508,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) + prefetch(page_address(page)); + fec_enet_update_cbd(rxq, bdp, index); + ++#ifndef CONFIG_AVB_SUPPORT + if (xdp_prog) { + xdp_buff_clear_frags_flag(&xdp); + /* subtract 16bit shift and FCS */ +@@ -1706,6 +2519,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) + if (ret != FEC_ENET_XDP_PASS) + goto rx_processing_done; + } ++#endif + + /* The packet length includes FCS, but we don't want to + * include that when passing upstream as it messes up +@@ -1754,10 +2568,16 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) + skb->protocol = eth_type_trans(skb, ndev); + + /* Get receive timestamp from the skb */ +- if (fep->hwts_rx_en && fep->bufdesc_ex) ++ if (fep->hwts_rx_en && fep->bufdesc_ex) { + fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), + skb_hwtstamps(skb)); + ++ /* Adjust for RX MAC-PHY latency ++ */ ++ skb_hwtstamps(skb)->hwtstamp = ++ ktime_sub_ns(skb_hwtstamps(skb)->hwtstamp, fep->rx_tstamp_latency); ++ } ++ + if (fep->bufdesc_ex && + (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { + if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { +@@ -1808,8 +2628,10 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) + } + rxq->bd.cur = bdp; + ++#ifndef CONFIG_AVB_SUPPORT + if (xdp_result & FEC_ENET_XDP_REDIR) + xdp_do_flush_map(); ++#endif + + return pkt_received; + } +@@ -1850,6 +2672,10 @@ fec_enet_interrupt(int irq, void *dev_id) + if (fec_enet_collect_events(fep) && fep->link) { + ret = IRQ_HANDLED; + ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ dev_err(&fep->pdev->dev, "Rx/Tx IRQ with AVB enabled, should not happen\n"); ++#endif + if (napi_schedule_prep(&fep->napi)) { + /* Disable interrupts */ + writel(0, fep->hwp + FEC_IMASK); +@@ -1866,10 +2692,18 @@ static int fec_enet_rx_napi(struct napi_struct *napi, int budget) + struct fec_enet_private *fep = netdev_priv(ndev); + int done = 0; + +- do { +- done += fec_enet_rx(ndev, budget - done); +- fec_enet_tx(ndev); +- } while ((done < budget) && fec_enet_collect_events(fep)); ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) { ++ done = fec_enet_rx_best_effort(ndev, budget); ++ fec_enet_tx_best_effort(ndev); ++ } else ++#endif ++ { ++ do { ++ done += fec_enet_rx(ndev, budget - done); ++ fec_enet_tx(ndev); ++ } while ((done < budget) && fec_enet_collect_events(fep)); ++ } + + if (done < budget) { + napi_complete_done(napi, done); +@@ -1986,11 +2820,33 @@ static void fec_enet_adjust_link(struct net_device *ndev) + status_change = 1; + } + ++ switch (fep->speed) { ++ case SPEED_100: ++ fep->rx_tstamp_latency = fep->rx_delay_100; ++ fep->tx_tstamp_latency = fep->tx_delay_100; ++ break; ++ case SPEED_1000: ++ fep->rx_tstamp_latency = fep->rx_delay_1000; ++ fep->tx_tstamp_latency = fep->tx_delay_1000; ++ break; ++ default: ++ fep->rx_tstamp_latency = 0; ++ fep->tx_tstamp_latency = 0; ++ } ++ + /* if any of the above changed restart the FEC */ + if (status_change) { + napi_disable(&fep->napi); + netif_tx_lock_bh(ndev); ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ fep->avb->close(fep->avb_data); ++#endif + fec_restart(ndev); ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ fep->avb->open(fep->avb_data, fep, fec_max_rate(fep)); ++#endif + netif_tx_wake_all_queues(ndev); + netif_tx_unlock_bh(ndev); + napi_enable(&fep->napi); +@@ -1999,7 +2855,15 @@ static void fec_enet_adjust_link(struct net_device *ndev) + if (fep->link) { + napi_disable(&fep->napi); + netif_tx_lock_bh(ndev); ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ fep->avb->close(fep->avb_data); ++#endif + fec_stop(ndev); ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ fep->avb->open(fep->avb_data, fep, fec_max_rate(fep)); ++#endif + netif_tx_unlock_bh(ndev); + napi_enable(&fep->napi); + fep->link = phy_dev->link; +@@ -2322,6 +3186,12 @@ static int fec_enet_mii_probe(struct net_device *ndev) + else + phy_set_max_speed(phy_dev, 100); + ++#ifdef CONFIG_AVB_SUPPORT ++ /* Restore advertising settings saved last interface close */ ++ if (!linkmode_empty(fep->phy_advertising)) ++ linkmode_copy(phy_dev->advertising, fep->phy_advertising); ++#endif ++ + fep->link = 0; + fep->full_duplex = 0; + +@@ -3252,20 +4122,86 @@ static void fec_enet_free_buffers(struct net_device *ndev) + + for (q = 0; q < fep->num_rx_queues; q++) { + rxq = fep->rx_queue[q]; +- for (i = 0; i < rxq->bd.ring_size; i++) +- page_pool_put_full_page(rxq->page_pool, rxq->rx_skb_info[i].page, false); ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) { ++ struct bufdesc *bdp = rxq->bd.base; ++ for (i = 0; i < rxq->bd.ring_size; i++) { ++ if (bdp->cbd_bufaddr) { ++ struct avb_rx_desc *desc; ++ ++ desc = (struct avb_rx_desc *)rxq->rx_skb_info[i].skb; ++ fep->avb->free(fep->avb_data, &desc->common); ++ bdp->cbd_bufaddr = cpu_to_fec32(0); ++ } ++ ++ bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); ++ } ++ } else ++#endif ++ { ++ for (i = 0; i < rxq->bd.ring_size; i++) ++ page_pool_put_full_page(rxq->page_pool, rxq->rx_skb_info[i].page, false); + +- for (i = 0; i < XDP_STATS_TOTAL; i++) +- rxq->stats[i] = 0; ++ for (i = 0; i < XDP_STATS_TOTAL; i++) ++ rxq->stats[i] = 0; + +- if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) +- xdp_rxq_info_unreg(&rxq->xdp_rxq); +- page_pool_destroy(rxq->page_pool); +- rxq->page_pool = NULL; ++ if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) ++ xdp_rxq_info_unreg(&rxq->xdp_rxq); ++ page_pool_destroy(rxq->page_pool); ++ rxq->page_pool = NULL; ++ } ++ } ++ ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) { ++ struct avb_rx_desc *rx_desc; ++ struct avb_tx_desc *tx_desc; ++ ++ while ((rx_desc = fep->avb->dequeue(fep->avb_data)) != (void *) -1) ++ fep->avb->free(fep->avb_data, &rx_desc->common); ++ ++ while ((tx_desc = fep->avb->tx_cleanup_dequeue(fep->avb_data)) != (void *) -1) { ++ ++ skb = tx_desc->data; ++ ++ dma_unmap_single(&fep->pdev->dev, tx_desc->bufaddr, ++ skb->len, DMA_TO_DEVICE); ++ ++ /* Free the sk buffer associated with this last transmit */ ++ dev_kfree_skb_any(skb); ++ } + } ++#endif + + for (q = 0; q < fep->num_tx_queues; q++) { + txq = fep->tx_queue[q]; ++#ifdef CONFIG_AVB_SUPPORT ++ for (i = 0; i < txq->bd.ring_size; i++) { ++ if (txq->tx_buf[i].skb) { ++ skb = NULL; ++ if (fep->avb_enabled) { ++ struct avb_tx_desc *desc = (struct avb_tx_desc *)txq->tx_buf[i].skb; ++ ++ if (!(desc->common.flags & AVB_TX_FLAG_SKB)) ++ fep->avb->free(fep->avb_data, &desc->common); ++ else ++ skb = desc->data; ++ } else { ++ skb = txq->tx_buf[i].skb; ++ } ++ ++ if (skb) ++ dev_kfree_skb(skb); ++ ++ txq->tx_buf[i].skb = NULL; ++ } ++ } ++ ++ for (i = 0; i < txq->tx_bounce_size; i++) { ++ kfree(txq->tx_bounce[i]); ++ txq->tx_bounce[i] = NULL; ++ } ++#else + for (i = 0; i < txq->bd.ring_size; i++) { + kfree(txq->tx_bounce[i]); + txq->tx_bounce[i] = NULL; +@@ -3282,6 +4218,7 @@ static void fec_enet_free_buffers(struct net_device *ndev) + txq->tx_buf[i].type = FEC_TXBUF_T_SKB; + } + } ++#endif + } + } + +@@ -3321,10 +4258,14 @@ static int fec_enet_alloc_queue(struct net_device *ndev) + } + + fep->tx_queue[i] = txq; +- txq->bd.ring_size = TX_RING_SIZE; ++ txq->bd.ring_size = FEC_TX_RING_SIZE; + fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size; + ++#ifdef CONFIG_AVB_SUPPORT ++ txq->tx_stop_threshold = FEC_TX_RING_SIZE * 3/4; ++#else + txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; ++#endif + txq->tx_wake_threshold = + (txq->bd.ring_size - txq->tx_stop_threshold) / 2; + +@@ -3346,7 +4287,7 @@ static int fec_enet_alloc_queue(struct net_device *ndev) + goto alloc_failed; + } + +- fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE; ++ fep->rx_queue[i]->bd.ring_size = FEC_RX_RING_SIZE; + fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size; + } + return ret; +@@ -3367,32 +4308,77 @@ fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) + int i, err; + + rxq = fep->rx_queue[queue]; ++ ++#ifdef CONFIG_AVB_SUPPORT + bdp = rxq->bd.base; + +- err = fec_enet_create_page_pool(fep, rxq, rxq->bd.ring_size); +- if (err < 0) { +- netdev_err(ndev, "%s failed queue %d (%d)\n", __func__, queue, err); +- return err; ++ for (i = 0; i < rxq->bd.ring_size; i++) { ++ bdp->cbd_bufaddr = cpu_to_fec32(0); ++ bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); + } ++#endif + +- for (i = 0; i < rxq->bd.ring_size; i++) { +- page = page_pool_dev_alloc_pages(rxq->page_pool); +- if (!page) +- goto err_alloc; ++ bdp = rxq->bd.base; ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) { ++ for (i = 0; i < rxq->bd.ring_size; i++) { ++ struct avb_rx_desc *desc; ++ void *buffer; + +- phys_addr = page_pool_get_dma_addr(page) + FEC_ENET_XDP_HEADROOM; +- bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); ++ buffer = fep->avb->alloc(fep->avb_data); ++ if (!buffer) ++ goto err_alloc; + +- rxq->rx_skb_info[i].page = page; +- rxq->rx_skb_info[i].offset = FEC_ENET_XDP_HEADROOM; +- bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); ++ desc = buffer; + +- if (fep->bufdesc_ex) { +- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; +- ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); ++ desc->common.len = 0; ++ desc->queue_id = queue; ++ ++ rxq->rx_skb_info[i].skb = buffer; ++ bdp->cbd_bufaddr = cpu_to_fec32((dma_addr_t)(desc->dma_addr)); ++ ++#if !defined(CONFIG_M5272) ++ if (fep->quirks & FEC_QUIRK_HAS_RACC) ++ desc->common.offset += 2; ++#endif ++ ++ bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); ++ ++ if (fep->bufdesc_ex) { ++ struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; ++ ebdp->cbd_esc = cpu_to_fec32(0); ++ } ++ ++ bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); ++ } ++ } else ++#endif ++ { ++ err = fec_enet_create_page_pool(fep, rxq, rxq->bd.ring_size); ++ if (err < 0) { ++ netdev_err(ndev, "%s failed queue %d (%d)\n", __func__, queue, err); ++ return err; + } + +- bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); ++ for (i = 0; i < rxq->bd.ring_size; i++) { ++ page = page_pool_dev_alloc_pages(rxq->page_pool); ++ if (!page) ++ goto err_alloc; ++ ++ phys_addr = page_pool_get_dma_addr(page) + FEC_ENET_XDP_HEADROOM; ++ bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); ++ ++ rxq->rx_skb_info[i].page = page; ++ rxq->rx_skb_info[i].offset = FEC_ENET_XDP_HEADROOM; ++ bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); ++ ++ if (fep->bufdesc_ex) { ++ struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; ++ ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); ++ } ++ ++ bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); ++ } + } + + /* Set the last buffer to wrap. */ +@@ -3415,17 +4401,28 @@ fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) + + txq = fep->tx_queue[queue]; + bdp = txq->bd.base; +- for (i = 0; i < txq->bd.ring_size; i++) { ++ for (i = 0; i < txq->tx_bounce_size; i++) { + txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); + if (!txq->tx_bounce[i]) + goto err_alloc; ++ } ++ ++ bdp = txq->bd.base; ++ txq->bd.cur = bdp; + ++ for (i = 0; i < txq->bd.ring_size; i++) { + bdp->cbd_sc = cpu_to_fec16(0); + bdp->cbd_bufaddr = cpu_to_fec32(0); + + if (fep->bufdesc_ex) { + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; +- ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); ++ ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ ebdp->cbd_esc = cpu_to_fec32(0); ++ else ++#endif ++ ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); + } + + bdp = fec_enet_get_nextdesc(bdp, &txq->bd); +@@ -3434,6 +4431,7 @@ fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) + /* Set the last buffer to wrap. */ + bdp = fec_enet_get_prevdesc(bdp, &txq->bd); + bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); ++ txq->dirty_tx = bdp; + + return 0; + +@@ -3463,15 +4461,31 @@ fec_enet_open(struct net_device *ndev) + struct fec_enet_private *fep = netdev_priv(ndev); + int ret; + bool reset_again; ++ int i; ++ ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) { ++ if (!try_module_get(fep->avb->owner)) { ++ ret = -EIO; ++ goto err_module_get; ++ } ++ for (i = 0; i < fep->num_tx_queues; i++) ++ fep->tx_queue[i]->tx_bounce_size = FEC_TX_RING_SIZE + 32; ++ } else ++#endif ++ for (i = 0; i < fep->num_tx_queues; i++) ++ fep->tx_queue[i]->tx_bounce_size = FEC_TX_RING_SIZE; + + ret = pm_runtime_resume_and_get(&fep->pdev->dev); + if (ret < 0) + return ret; + + pinctrl_pm_select_default_state(&fep->pdev->dev); ++#ifndef CONFIG_AVB_SUPPORT + ret = fec_enet_clk_enable(ndev, true); + if (ret) + goto clk_enable; ++#endif + + /* During the first fec_enet_open call the PHY isn't probed at this + * point. Therefore the phy_reset_after_clk_enable() call within +@@ -3514,6 +4528,12 @@ fec_enet_open(struct net_device *ndev) + + napi_enable(&fep->napi); + phy_start(ndev->phydev); ++ ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ fep->avb->open(fep->avb_data, fep, fec_max_rate(fep)); ++#endif ++ + netif_tx_start_all_queues(ndev); + + device_set_wakeup_enable(&ndev->dev, fep->wol_flag & +@@ -3524,12 +4544,23 @@ fec_enet_open(struct net_device *ndev) + err_enet_mii_probe: + fec_enet_free_buffers(ndev); + err_enet_alloc: ++#ifndef CONFIG_AVB_SUPPORT + fec_enet_clk_enable(ndev, false); + clk_enable: ++#endif + pm_runtime_mark_last_busy(&fep->pdev->dev); + pm_runtime_put_autosuspend(&fep->pdev->dev); + if (!fep->mii_bus_share) + pinctrl_pm_select_sleep_state(&fep->pdev->dev); ++ ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ module_put(fep->avb->owner); ++#endif ++ ++#ifdef CONFIG_AVB_SUPPORT ++err_module_get: ++#endif + return ret; + } + +@@ -3543,9 +4574,20 @@ fec_enet_close(struct net_device *ndev) + if (netif_device_present(ndev)) { + napi_disable(&fep->napi); + netif_tx_disable(ndev); ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ fep->avb->close(fep->avb_data); ++#endif + fec_stop(ndev); + } + ++#ifdef CONFIG_AVB_SUPPORT ++ /* ++ * Save advertising settings so there are not lost ++ * when opening the interface again. ++ */ ++ linkmode_copy(fep->phy_advertising, ndev->phydev->advertising); ++#endif + phy_disconnect(ndev->phydev); + ndev->phydev = NULL; + +@@ -3554,7 +4596,9 @@ fec_enet_close(struct net_device *ndev) + + fec_enet_update_ethtool_stats(ndev); + ++#ifndef CONFIG_AVB_SUPPORT + fec_enet_clk_enable(ndev, false); ++#endif + if (fep->quirks & FEC_QUIRK_HAS_PMQOS) + cpu_latency_qos_remove_request(&fep->pm_qos_req); + +@@ -3565,6 +4609,11 @@ fec_enet_close(struct net_device *ndev) + + fec_enet_free_buffers(ndev); + ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ module_put(fep->avb->owner); ++#endif ++ + return 0; + } + +@@ -3749,6 +4798,7 @@ static u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, + return fec_enet_vlan_pri_to_queue[vlan_tag >> 13]; + } + ++#ifndef CONFIG_AVB_SUPPORT + static int fec_enet_bpf(struct net_device *dev, struct netdev_bpf *bpf) + { + struct fec_enet_private *fep = netdev_priv(dev); +@@ -3936,6 +4986,7 @@ static int fec_enet_xdp_xmit(struct net_device *dev, + + return sent_frames; + } ++#endif /* !CONFIG_AVB_SUPPORT */ + + static const struct net_device_ops fec_netdev_ops = { + .ndo_open = fec_enet_open, +@@ -3951,8 +5002,11 @@ static const struct net_device_ops fec_netdev_ops = { + .ndo_poll_controller = fec_poll_controller, + #endif + .ndo_set_features = fec_set_features, ++#ifndef CONFIG_AVB_SUPPORT ++ /* AVB support not compatible with XDP */ + .ndo_bpf = fec_enet_bpf, + .ndo_xdp_xmit = fec_enet_xdp_xmit, ++#endif + }; + + static const unsigned short offset_des_active_rxq[] = { +@@ -3963,6 +5017,196 @@ static const unsigned short offset_des_active_txq[] = { + FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2 + }; + ++#ifdef CONFIG_AVB_SUPPORT ++ ++static struct platform_driver fec_driver; ++ ++/* Checks if the net_device is registered by the fec */ ++static bool __is_fec_net_device(struct net_device *ndev) ++{ ++ if (!ndev) ++ return false; ++ ++ if (ndev->dev.parent->driver == &fec_driver.driver) ++ return true; ++ else ++ return false; ++} ++ ++struct device *fec_enet_avb_get_device(const char *ifname) ++{ ++ struct net_device *ndev; ++ struct fec_enet_private *fep; ++ ++ ndev = dev_get_by_name(&init_net, ifname); ++ if (!ndev) ++ goto err_dev_get; ++ ++ if (!__is_fec_net_device(ndev)) ++ goto err_ndev; ++ ++ fep = netdev_priv(ndev); ++ ++ dev_put(ndev); ++ ++ return &fep->pdev->dev; ++ ++err_ndev: ++ dev_put(ndev); ++ ++err_dev_get: ++ return NULL; ++} ++EXPORT_SYMBOL(fec_enet_avb_get_device); ++ ++int fec_enet_avb_register(const char *ifname, const struct avb_ops *avb, void *data) ++{ ++ struct net_device *ndev; ++ struct fec_enet_private *fep; ++ unsigned int up; ++ int ifindex; ++ ++ ndev = dev_get_by_name(&init_net, ifname); ++ if (!ndev) ++ goto err_dev_get; ++ ++ if (!__is_fec_net_device(ndev)) ++ goto err_ndev; ++ ++ fep = netdev_priv(ndev); ++ ++ if (fep->avb) ++ goto err_avb; ++ ++ rtnl_lock(); ++ up = ndev->flags & IFF_UP; ++ if (up) ++ dev_close(ndev); ++ ++ fep->avb = avb; ++ fep->avb_data = data; ++ fep->avb_enabled = 1; ++ ifindex = ndev->ifindex; ++ ++ if (up) { ++ /* In case of error, device is closed but avb interface is registered */ ++ dev_open(ndev, NULL); ++ } ++ ++ rtnl_unlock(); ++ ++ dev_put(ndev); ++ ++ return ifindex; ++ ++err_avb: ++err_ndev: ++ dev_put(ndev); ++ ++err_dev_get: ++ return -1; ++} ++EXPORT_SYMBOL(fec_enet_avb_register); ++ ++int fec_enet_avb_unregister(int ifindex, const struct avb_ops *avb) ++{ ++ struct net_device *ndev; ++ struct fec_enet_private *fep; ++ unsigned int up; ++ ++ ndev = dev_get_by_index(&init_net, ifindex); ++ if (!ndev) ++ goto err_dev_get; ++ ++ if (!__is_fec_net_device(ndev)) ++ goto err_ndev; ++ ++ fep = netdev_priv(ndev); ++ if (fep->avb != avb) ++ goto err_avb; ++ ++ rtnl_lock(); ++ up = ndev->flags & IFF_UP; ++ if (up) ++ dev_close(ndev); ++ ++ fep->avb = NULL; ++ fep->avb_data = NULL; ++ fep->avb_enabled = 0; ++ ++ if (up) ++ /* In case of error, device is closed but avb interface is unregistered */ ++ dev_open(ndev, NULL); ++ ++ rtnl_unlock(); ++ ++ dev_put(ndev); ++ ++ return 0; ++ ++err_avb: ++err_ndev: ++ dev_put(ndev); ++ ++err_dev_get: ++ return -1; ++} ++EXPORT_SYMBOL(fec_enet_avb_unregister); ++ ++int fec_enet_get_tx_queue_properties(int ifindex, struct tx_queue_properties *prop) ++{ ++ struct net_device *ndev; ++ struct fec_enet_private *fep; ++ ++ ndev = dev_get_by_index(&init_net, ifindex); ++ if (!ndev) ++ goto err_dev_get; ++ ++ if (!__is_fec_net_device(ndev)) ++ goto err_ndev; ++ ++ fep = netdev_priv(ndev); ++ ++ if (fep->num_tx_queues >= TX_QUEUE_PROP_MAX) ++ goto err_queues; ++ ++ if (fep->quirks & FEC_QUIRK_HAS_AVB) { ++ prop->num_queues = fep->num_tx_queues; ++ prop->queue[0].priority = 0; ++ prop->queue[0].flags = TX_QUEUE_FLAGS_STRICT_PRIORITY; ++ prop->queue[1].priority = 2; ++ prop->queue[1].flags = TX_QUEUE_FLAGS_CREDIT_SHAPER; ++ prop->queue[2].priority = 1; ++ prop->queue[2].flags = TX_QUEUE_FLAGS_CREDIT_SHAPER; ++ } else { ++ /* ++ * For now, there is no MAC non-AVB capable ++ * with more than 1 queue. ++ */ ++ if (fep->num_tx_queues == 1) { ++ prop->num_queues = fep->num_tx_queues; ++ prop->queue[0].priority = 0; ++ prop->queue[0].flags = TX_QUEUE_FLAGS_STRICT_PRIORITY; ++ } else { ++ netdev_err(ndev, "%s invalid/unknown TX queues configuration\n", __func__); ++ goto err_queues; ++ } ++ } ++ ++ dev_put(ndev); ++ ++ return 0; ++ ++err_queues: ++err_ndev: ++ dev_put(ndev); ++ ++err_dev_get: ++ return -1; ++} ++EXPORT_SYMBOL(fec_enet_get_tx_queue_properties); ++#endif ++ + /* + * XXX: We need to clean up on failure exits here. + * +@@ -4050,10 +5294,14 @@ static int fec_enet_init(struct net_device *ndev) + txq->bd.dsize_log2 = dsize_log2; + txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i]; + bd_dma += size; ++#ifdef CONFIG_AVB_SUPPORT ++ txq->tx_idle_slope = IDLE_SLOPE(i); ++#endif + cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); + txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); + } + ++ fep->netdev = ndev; + + /* The FEC Ethernet specific entries in the device structure */ + ndev->watchdog_timeo = TX_TIMEOUT; +@@ -4071,8 +5319,13 @@ static int fec_enet_init(struct net_device *ndev) + netif_set_tso_max_segs(ndev, FEC_MAX_TSO_SEGS); + + /* enable hw accelerator */ ++#ifdef CONFIG_AVB_SUPPORT ++ /* AVB support not compatible with SG or TSO */ ++ ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM); ++#else + ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM + | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); ++#endif + fep->csum_flags |= FLAG_RX_CSUM_ENABLED; + } + +@@ -4399,6 +5652,13 @@ fec_probe(struct platform_device *pdev) + } + + fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; ++#ifdef CONFIG_AVB_SUPPORT ++ if (!fep->bufdesc_ex) { ++ dev_err(&pdev->dev, ++ "Error: AVB Support requires extended buffer descriptor\n"); ++ goto failed_clk; ++ } ++#endif + fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); + if (IS_ERR(fep->clk_ptp)) { + fep->clk_ptp = NULL; +@@ -4432,12 +5692,32 @@ fec_probe(struct platform_device *pdev) + fep->reg_phy = NULL; + } + ++ if (of_property_read_u32(np, "fsl,rx-phy-delay-100-ns", &fep->rx_delay_100)) ++ fep->rx_delay_100 = 0; ++ ++ if (of_property_read_u32(np, "fsl,tx-phy-delay-100-ns", &fep->tx_delay_100)) ++ fep->tx_delay_100 = 0; ++ ++ if (of_property_read_u32(np, "fsl,rx-phy-delay-1000-ns", &fep->rx_delay_1000)) ++ fep->rx_delay_1000 = 0; ++ ++ if (of_property_read_u32(np, "fsl,tx-phy-delay-1000-ns", &fep->tx_delay_1000)) ++ fep->tx_delay_1000 = 0; ++ + pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + ++#ifdef CONFIG_AVB_SUPPORT ++ /* ++ * Prevent runtime pm with avb module to keep all clocks ++ * running even on link down. ++ */ ++ pm_runtime_forbid(&pdev->dev); ++#endif ++ + ret = fec_reset_phy(pdev); + if (ret) + goto failed_reset; +@@ -4480,7 +5760,9 @@ fec_probe(struct platform_device *pdev) + + /* Carrier starts down, phylib will bring it up */ + netif_carrier_off(ndev); ++#ifndef CONFIG_AVB_SUPPORT + fec_enet_clk_enable(ndev, false); ++#endif + pinctrl_pm_select_sleep_state(&pdev->dev); + + ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN; +@@ -4590,6 +5872,10 @@ static int __maybe_unused fec_suspend(struct device *dev) + netif_tx_lock_bh(ndev); + netif_device_detach(ndev); + netif_tx_unlock_bh(ndev); ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ fep->avb->close(fep->avb_data); ++#endif + fec_stop(ndev); + if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { + fec_irqs_disable(ndev); +@@ -4674,6 +5960,10 @@ static int __maybe_unused fec_resume(struct device *dev) + napi_enable(&fep->napi); + phy_init_hw(ndev->phydev); + phy_start(ndev->phydev); ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->avb_enabled) ++ fep->avb->open(fep->avb_data, fep, fec_max_rate(fep)); ++#endif + } else if (fep->mii_bus_share && !ndev->phydev) { + pinctrl_pm_select_default_state(&fep->pdev->dev); + /* And then recovery mii bus */ +diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c +index d15737962145..339d3f0fc3fe 100644 +--- a/drivers/net/ethernet/freescale/fec_ptp.c ++++ b/drivers/net/ethernet/freescale/fec_ptp.c +@@ -111,7 +111,14 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) + fep->pps_channel = DEFAULT_PPS_CHANNEL; + fep->reload_period = PPS_OUPUT_RELOAD_PERIOD; + +- spin_lock_irqsave(&fep->tmreg_lock, flags); ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); ++ ++#ifdef CONFIG_AVB_SUPPORT ++ if (fep->rec_enable && (fep->pps_channel == fep->rec_channel)) { ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ return -EBUSY; ++ } ++#endif + + if (enable) { + /* clear capture or output compare interrupt status if have. +@@ -196,7 +203,7 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) + } + + fep->pps_enable = enable; +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + return 0; + } +@@ -207,7 +214,7 @@ static int fec_ptp_pps_perout(struct fec_enet_private *fep) + u64 curr_time; + unsigned long flags; + +- spin_lock_irqsave(&fep->tmreg_lock, flags); ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + + /* Update time counter */ + timecounter_read(&fep->tc); +@@ -230,7 +237,7 @@ static int fec_ptp_pps_perout(struct fec_enet_private *fep) + */ + if (fep->perout_stime < curr_time + 100 * NSEC_PER_MSEC) { + dev_err(&fep->pdev->dev, "Current time is too close to the start time!\n"); +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + return -1; + } + +@@ -258,7 +265,7 @@ static int fec_ptp_pps_perout(struct fec_enet_private *fep) + */ + writel(fep->next_counter, fep->hwp + FEC_TCCR(fep->pps_channel)); + fep->next_counter = (fep->next_counter + fep->reload_period) & fep->cc.mask; +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + return 0; + } +@@ -273,6 +280,26 @@ static enum hrtimer_restart fec_ptp_pps_perout_handler(struct hrtimer *timer) + return HRTIMER_NORESTART; + } + ++#ifdef CONFIG_AVB_SUPPORT ++/** ++ * fec_timecounter_set ++ * @start_tstamp: new time in ns ++ * ++ * update the FEC timecounter structure to a new time, and make sure ++ * the HW counter matches that value. ++ */ ++static inline void fec_timecounter_set(struct fec_enet_private *fep, ++ u64 start_tstamp) ++{ ++ u32 tempval; ++ ++ tempval = start_tstamp & fep->cc.mask; ++ writel(tempval, fep->hwp + FEC_ATIME); ++ fep->tc.cycle_last = tempval; ++ fep->tc.nsec = start_tstamp; ++} ++#endif ++ + /** + * fec_ptp_read - read raw cycle counter (to be used by time counter) + * @cc: the cyclecounter structure +@@ -287,14 +314,14 @@ static u64 fec_ptp_read(const struct cyclecounter *cc) + container_of(cc, struct fec_enet_private, cc); + u32 tempval; + +- tempval = readl(fep->hwp + FEC_ATIME_CTRL); ++ tempval = readl_relaxed(fep->hwp + FEC_ATIME_CTRL); + tempval |= FEC_T_CTRL_CAPTURE; + writel(tempval, fep->hwp + FEC_ATIME_CTRL); + + if (fep->quirks & FEC_QUIRK_BUG_CAPTURE) + udelay(1); + +- return readl(fep->hwp + FEC_ATIME); ++ return readl_relaxed(fep->hwp + FEC_ATIME); + } + + /** +@@ -314,29 +341,312 @@ void fec_ptp_start_cyclecounter(struct net_device *ndev) + inc = 1000000000 / fep->cycle_speed; + + /* grab the ptp lock */ +- spin_lock_irqsave(&fep->tmreg_lock, flags); ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + +- /* 1ns counter */ +- writel(inc << FEC_T_INC_OFFSET, fep->hwp + FEC_ATIME_INC); ++ /* 1ns counter, disable correction period */ ++ writel_relaxed(0, fep->hwp + FEC_ATIME_CORR); ++ writel_relaxed(inc << FEC_T_INC_OFFSET, fep->hwp + FEC_ATIME_INC); + +- /* use 31-bit timer counter */ +- writel(FEC_COUNTER_PERIOD, fep->hwp + FEC_ATIME_EVT_PERIOD); + +- writel(FEC_T_CTRL_ENABLE | FEC_T_CTRL_PERIOD_RST, ++#ifdef CONFIG_AVB_SUPPORT ++ /* use 32-bits timer counter */ ++ writel_relaxed(0, fep->hwp + FEC_ATIME_EVT_PERIOD); ++ writel_relaxed(FEC_T_CTRL_ENABLE, fep->hwp + FEC_ATIME_CTRL); ++#else ++ /* use 31-bit timer counter */ ++ writel_relaxed(FEC_COUNTER_PERIOD, fep->hwp + FEC_ATIME_EVT_PERIOD); ++ writel_relaxed(FEC_T_CTRL_ENABLE | FEC_T_CTRL_PERIOD_RST, + fep->hwp + FEC_ATIME_CTRL); ++#endif + + memset(&fep->cc, 0, sizeof(fep->cc)); + fep->cc.read = fec_ptp_read; ++#ifdef CONFIG_AVB_SUPPORT ++ fep->cc.mask = CLOCKSOURCE_MASK(32); ++#else + fep->cc.mask = CLOCKSOURCE_MASK(31); ++#endif + fep->cc.shift = 31; + fep->cc.mult = FEC_CC_MULT; + + /* reset the ns time counter */ ++#ifdef CONFIG_AVB_SUPPORT ++ fep->tc.cc = &fep->cc; ++ fec_timecounter_set(fep, 0); ++#else + timecounter_init(&fep->tc, &fep->cc, 0); ++#endif + +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + } + ++#ifdef CONFIG_AVB_SUPPORT ++/** ++ * fec_ptp_adjfreq - adjust ptp cycle frequency ++ * @ptp: the ptp clock structure ++ * @ppb: parts per billion adjustment from base ++ * ++ * Adjust the frequency of the ptp cycle counter by the ++ * indicated ppb from the base frequency. ++ * ++ * This version adjusts the HW counter. The HW implementation results in the following equation: ++ * fe * diff = ppb * (pc + 1) ++ * with: ++ * . fe : ENET ref clock frequency in Hz ++ * . diff = inc_corr - inc : difference between default increment and correction increment ++ * . ppb : parts per billion adjustment from base ++ * . pc : correction period (in number of fe clock cycles) ++ * ++ * Limitations: ++ * . only add or remove 1ns per correction period. This will limit jitter and improve short term ++ * accuracy (in particular for trigger events during clock recovery) but increase the max possible ++ * error, since the correction period will be the shortest possible and thus may not be optimal. ++ * In the case of an adjustment of about 100ppm (which should be the max if the ref clock ++ * is within the 802.1AS spec), the max error will be about 0.2ppm with a 50MHz ref clock, and ++ * 0.08ppm with a 125MHz ref clock. ++ * Long term accuracy will also be lower (20ns per 100ms @50MHz, 8ns per 100ms @125MHz), but this ++ * can be fixed by phase adjustments. ++ * . It seems not all period/correction values are valid. With a 1ns correction, all even ++ * period values return wrong timings (half the requested correction), but on the other hand odd ++ * values are not taken into account systematically by the hardware, so we choose the closest even ++ * value that matches the following equation: ++ * fe * diff = 2 * ppb * (pc + 1) ++ * . given we force abs(diff) = 1, limit max adjustment to prevent pc < 1. ++ * ++ */ ++static int fec_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) ++{ ++ struct fec_enet_private *fep = ++ container_of(ptp, struct fec_enet_private, ptp_caps); ++ unsigned long flags; ++ u32 inc, cor, pc; ++ ++ inc = fep->ptp_inc; ++ if (ppb == 0) { ++ cor = 0; ++ pc = 0; ++ } else { ++ if (ppb < 0) { ++ ppb = -ppb; ++ cor = inc - 1; ++ } ++ else ++ cor = inc + 1; ++ ++ if (ppb > fep->ptp_caps.max_adj) { ++ dev_err(&fep->pdev->dev, "ppb value %d outside accepted range (max_adj = %d)", (cor > inc) ? ppb : -ppb, ptp->max_adj); ++ return -1; ++ } ++ ++ pc = (((fep->cycle_speed / (2*ppb)) - 1) + 1) & ~ 0x1; // + 1) & ~ 0x1 returns the closest even value ++ } ++ ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); ++ ++ writel_relaxed((cor << FEC_T_INC_CORR_OFFSET) | (inc << FEC_T_INC_OFFSET), fep->hwp + FEC_ATIME_INC); ++ writel_relaxed(pc, fep->hwp + FEC_ATIME_CORR); ++ ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * fec_ptp_adjtime ++ * @ptp: the ptp clock structure ++ * @delta: offset to adjust the cycle counter by ++ * ++ * adjust the timer by updating the HW counter AND the timecounter structure ++ * as well. Since updating the HW register requires a read and a write, make ++ * a crude estimate of the read time and subtract it from the desired delta. ++ */ ++static int fec_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) ++{ ++ struct fec_enet_private *fep = ++ container_of(ptp, struct fec_enet_private, ptp_caps); ++ unsigned long flags; ++ u64 now, then; ++ s64 real_delta; ++ ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); ++ ++ now = timecounter_read(&fep->tc); ++ then = timecounter_read(&fep->tc); ++ ++ real_delta = delta + (then - now); ++ ++ now = timecounter_read(&fep->tc); ++ now += real_delta; ++ ++ fec_timecounter_set(fep, now); ++ ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * fec_ptp_settime ++ * @ptp: the ptp clock structure ++ * @ts: the timespec containing the new time for the cycle counter ++ * ++ * Update the timecounter to use a new base value instead of the kernel ++ * wall timer value, and update the HW counter as well. ++ */ ++static int fec_ptp_settime(struct ptp_clock_info *ptp, ++ const struct timespec64 *ts) ++{ ++ struct fec_enet_private *fep = ++ container_of(ptp, struct fec_enet_private, ptp_caps); ++ u64 ns; ++ unsigned long flags; ++ ++ ns = ts->tv_sec * 1000000000ULL; ++ ns += ts->tv_nsec; ++ ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); ++ fec_timecounter_set(fep, ns); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ return 0; ++} ++ ++/** ++ * fec_ptp_read ++ * @data: fec private context ptr ++ * @cnt: data pointer for counter value ++ * ++ * Returns status ++ */ ++int fec_ptp_read_cnt(void *data, u32 *cnt) ++{ ++ struct fec_enet_private *fep = data; ++ unsigned long flags; ++ ++ /* Note : it might be possible to avoid taking the lock */ ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); ++ ++ *cnt = (u32) fec_ptp_read(&fep->cc); ++ ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ ++ return 0; ++} ++EXPORT_SYMBOL(fec_ptp_read_cnt); ++ ++/** ++ * fec_ptp_tc_start ++ * @data: fec private context ptr ++ * @id: TC register ID ++ * @ts_0: First timestamp ++ * @ts_1: Second timestamp ++ * @tcsr_val: TCSR register value ++ * ++ * Returns 0 on success, -1 if PTP counter is not ++ * enabled. ++ */ ++int fec_ptp_tc_start(void *data, u8 id, u32 ts_0, u32 ts_1, u32 tcsr_val) ++{ ++ struct fec_enet_private *fep = data; ++ unsigned long flags; ++ u32 ctrl_val; ++ int rc = 0; ++ ++ if (id > MAX_TIMER_CHANNEL) { ++ rc = -1; ++ goto exit; ++ } ++ ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); ++ ++ /* Simple resource sharing handling with pps */ ++ if (fep->pps_enable && (fep->pps_channel == id)) { ++ rc = -1; ++ goto exit_unlock; ++ } ++ ++ ctrl_val = readl_relaxed(fep->hwp + FEC_ATIME_CTRL); ++ if (!(ctrl_val & FEC_T_CTRL_ENABLE)) { ++ rc = -1; ++ goto exit_unlock; ++ } ++ ++ writel_relaxed(ts_0, fep->hwp + FEC_TCCR(id)); ++ writel_relaxed(tcsr_val, fep->hwp + FEC_TCSR(id)); ++ writel_relaxed(ts_1, fep->hwp + FEC_TCCR(id)); ++ ++ fep->rec_enable = 1; ++ fep->rec_channel = id; ++ ++exit_unlock: ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++exit: ++ return rc; ++} ++EXPORT_SYMBOL(fec_ptp_tc_start); ++ ++/** ++ * fec_ptp_tc_stop ++ * @data: fec private context ptr ++ * @id: TC register ID ++ * ++ * Returns none ++ */ ++void fec_ptp_tc_stop(void *data, u8 id) ++{ ++ struct fec_enet_private *fep = data; ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); ++ ++ writel_relaxed(0, fep->hwp + FEC_TCCR(id)); ++ writel_relaxed(FEC_T_TF_MASK, fep->hwp + FEC_TCSR(id)); ++ ++ fep->rec_enable = 0; ++ ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++} ++EXPORT_SYMBOL(fec_ptp_tc_stop); ++ ++/** ++ * fec_ptp_tc_reload ++ * @data: fec private context ptr ++ * @id: TC register ID ++ * @ts: New timestamp to load ++ * ++ * Returns 0 if success, -1 if compare has not occured ++ * or if PTP counter is not enabled. ++ */ ++int fec_ptp_tc_reload(void *data, u8 id, u32 ts) ++{ ++ struct fec_enet_private *fep = data; ++ unsigned long flags; ++ u32 tcsr_val; ++ u32 ctrl_val; ++ int rc = 0; ++ ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); ++ ++ ctrl_val = readl_relaxed(fep->hwp + FEC_ATIME_CTRL); ++ if (!(ctrl_val & FEC_T_CTRL_ENABLE)) { ++ rc = -1; ++ goto exit; ++ } ++ ++ tcsr_val = readl_relaxed(fep->hwp + FEC_TCSR(id)); ++ if (tcsr_val & FEC_T_TF_MASK) { ++ writel_relaxed(ts, fep->hwp + FEC_TCCR(id)); ++ writel_relaxed(tcsr_val, fep->hwp + FEC_TCSR(id)); ++ } ++ else ++ rc = -1; ++ ++exit: ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ return rc; ++} ++EXPORT_SYMBOL(fec_ptp_tc_reload); ++#else /* CONFIG_AVB_SUPPORT */ ++ + /** + * fec_ptp_adjfreq - adjust ptp cycle frequency + * @ptp: the ptp clock structure +@@ -395,7 +705,7 @@ static int fec_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) + else + corr_ns = fep->ptp_inc + corr_inc; + +- spin_lock_irqsave(&fep->tmreg_lock, flags); ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + + tmp = readl(fep->hwp + FEC_ATIME_INC) & FEC_T_INC_MASK; + tmp |= corr_ns << FEC_T_INC_CORR_OFFSET; +@@ -405,7 +715,7 @@ static int fec_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) + /* dummy read to update the timer. */ + timecounter_read(&fep->tc); + +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + return 0; + } +@@ -423,40 +733,9 @@ static int fec_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) + container_of(ptp, struct fec_enet_private, ptp_caps); + unsigned long flags; + +- spin_lock_irqsave(&fep->tmreg_lock, flags); ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + timecounter_adjtime(&fep->tc, delta); +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); +- +- return 0; +-} +- +-/** +- * fec_ptp_gettime +- * @ptp: the ptp clock structure +- * @ts: timespec structure to hold the current time value +- * +- * read the timecounter and return the correct value on ns, +- * after converting it into a struct timespec. +- */ +-static int fec_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +-{ +- struct fec_enet_private *adapter = +- container_of(ptp, struct fec_enet_private, ptp_caps); +- u64 ns; +- unsigned long flags; +- +- mutex_lock(&adapter->ptp_clk_mutex); +- /* Check the ptp clock */ +- if (!adapter->ptp_clk_on) { +- mutex_unlock(&adapter->ptp_clk_mutex); +- return -EINVAL; +- } +- spin_lock_irqsave(&adapter->tmreg_lock, flags); +- ns = timecounter_read(&adapter->tc); +- spin_unlock_irqrestore(&adapter->tmreg_lock, flags); +- mutex_unlock(&adapter->ptp_clk_mutex); +- +- *ts = ns_to_timespec64(ns); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + return 0; + } +@@ -492,21 +771,55 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp, + */ + counter = ns & fep->cc.mask; + +- spin_lock_irqsave(&fep->tmreg_lock, flags); ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + writel(counter, fep->hwp + FEC_ATIME); + timecounter_init(&fep->tc, &fep->cc, ns); +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + mutex_unlock(&fep->ptp_clk_mutex); + return 0; + } ++#endif /* CONFIG_AVB_SUPPORT */ ++ ++/** ++ * fec_ptp_gettime ++ * @ptp: the ptp clock structure ++ * @ts: timespec structure to hold the current time value ++ * ++ * read the timecounter and return the correct value on ns, ++ * after converting it into a struct timespec. ++ */ ++static int fec_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) ++{ ++ struct fec_enet_private *adapter = ++ container_of(ptp, struct fec_enet_private, ptp_caps); ++ u64 ns; ++ u32 remainder; ++ unsigned long flags; ++ ++ mutex_lock(&adapter->ptp_clk_mutex); ++ /* Check the ptp clock */ ++ if (!adapter->ptp_clk_on) { ++ mutex_unlock(&adapter->ptp_clk_mutex); ++ return -EINVAL; ++ } ++ raw_spin_lock_irqsave(&adapter->tmreg_lock, flags); ++ ns = timecounter_read(&adapter->tc); ++ raw_spin_unlock_irqrestore(&adapter->tmreg_lock, flags); ++ mutex_unlock(&adapter->ptp_clk_mutex); ++ ++ ts->tv_sec = div_u64_rem(ns, 1000000000ULL, &remainder); ++ ts->tv_nsec = remainder; ++ ++ return 0; ++} + + static int fec_ptp_pps_disable(struct fec_enet_private *fep, uint channel) + { + unsigned long flags; + +- spin_lock_irqsave(&fep->tmreg_lock, flags); ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + writel(0, fep->hwp + FEC_TCSR(channel)); +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + return 0; + } +@@ -567,10 +880,10 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp, + mutex_unlock(&fep->ptp_clk_mutex); + return -EOPNOTSUPP; + } +- spin_lock_irqsave(&fep->tmreg_lock, flags); ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + /* Read current timestamp */ + curr_time = timecounter_read(&fep->tc); +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + mutex_unlock(&fep->ptp_clk_mutex); + + /* Calculate time difference */ +@@ -676,9 +989,9 @@ static void fec_time_keep(struct work_struct *work) + + mutex_lock(&fep->ptp_clk_mutex); + if (fep->ptp_clk_on) { +- spin_lock_irqsave(&fep->tmreg_lock, flags); ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + timecounter_read(&fep->tc); +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + } + mutex_unlock(&fep->ptp_clk_mutex); + +@@ -694,6 +1007,9 @@ static irqreturn_t fec_pps_interrupt(int irq, void *dev_id) + u8 channel = fep->pps_channel; + struct ptp_clock_event event; + ++ if (fep->pps_enable) ++ goto exit; ++ + val = readl(fep->hwp + FEC_TCSR(channel)); + if (val & FEC_T_TF_MASK) { + /* Write the next next compare(not the next according the spec) +@@ -713,6 +1029,7 @@ static irqreturn_t fec_pps_interrupt(int irq, void *dev_id) + return IRQ_HANDLED; + } + ++exit: + return IRQ_NONE; + } + +@@ -736,7 +1053,18 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx) + fep->ptp_caps.owner = THIS_MODULE; + strscpy(fep->ptp_caps.name, "fec ptp", sizeof(fep->ptp_caps.name)); + ++ fep->cycle_speed = clk_get_rate(fep->clk_ptp); ++ if (!fep->cycle_speed) { ++ fep->cycle_speed = NSEC_PER_SEC; ++ dev_err(&fep->pdev->dev, "clk_ptp clock rate is zero\n"); ++ } ++ fep->ptp_inc = NSEC_PER_SEC / fep->cycle_speed; ++ ++#ifdef CONFIG_AVB_SUPPORT ++ fep->ptp_caps.max_adj = fep->cycle_speed / 2; ++#else + fep->ptp_caps.max_adj = 250000000; ++#endif + fep->ptp_caps.n_alarm = 0; + fep->ptp_caps.n_ext_ts = 0; + fep->ptp_caps.n_per_out = 1; +@@ -748,14 +1076,7 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx) + fep->ptp_caps.settime64 = fec_ptp_settime; + fep->ptp_caps.enable = fec_ptp_enable; + +- fep->cycle_speed = clk_get_rate(fep->clk_ptp); +- if (!fep->cycle_speed) { +- fep->cycle_speed = NSEC_PER_SEC; +- dev_err(&fep->pdev->dev, "clk_ptp clock rate is zero\n"); +- } +- fep->ptp_inc = NSEC_PER_SEC / fep->cycle_speed; +- +- spin_lock_init(&fep->tmreg_lock); ++ raw_spin_lock_init(&fep->tmreg_lock); + + fec_ptp_start_cyclecounter(ndev); + +diff --git a/drivers/net/ethernet/freescale/fec_uio.c b/drivers/net/ethernet/freescale/fec_uio.c +index 7e93c9966c2b..b433868d4c5a 100644 +--- a/drivers/net/ethernet/freescale/fec_uio.c ++++ b/drivers/net/ethernet/freescale/fec_uio.c +@@ -1188,8 +1188,8 @@ static int fec_enet_uio_init(struct net_device *ndev) + return ret; + } + +- tx_ring_size = RING_SIZE_TX; +- rx_ring_size = RING_SIZE_RX; ++ tx_ring_size = FEC_TX_RING_SIZE; ++ rx_ring_size = FEC_RX_RING_SIZE; + + for (i = 0; i < FEC_MAX_Q; i++) { + total_tx_ring_size += tx_ring_size; +diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c +index b2b0d3c26fcc..370fe6e965b7 100644 +--- a/drivers/net/ethernet/freescale/gianfar_ethtool.c ++++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c +@@ -11,6 +11,7 @@ + * Modifier: Sandeep Gopalpet + * + * Copyright 2003-2006, 2008-2009, 2011 Freescale Semiconductor, Inc. ++ * Copyright 2017-2023 NXP + */ + + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +@@ -166,6 +167,32 @@ static void gfar_gdrvinfo(struct net_device *dev, + strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + } + ++static int gfar_get_eee(struct net_device *dev, struct ethtool_eee *et_eee) ++{ ++ struct phy_device *phydev = dev->phydev; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_ethtool_get_eee(phydev, et_eee); ++} ++ ++static int gfar_set_eee(struct net_device *dev, struct ethtool_eee *et_eee) ++{ ++ struct phy_device *phydev = dev->phydev; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ if (et_eee->eee_enabled || ++ et_eee->tx_lpi_enabled || ++ et_eee->tx_lpi_timer) { ++ return -EOPNOTSUPP; ++ } ++ ++ return phy_ethtool_set_eee(phydev, et_eee); ++} ++ + /* Return the length of the register structure */ + static int gfar_reglen(struct net_device *dev) + { +@@ -1489,6 +1516,8 @@ static int gfar_get_ts_info(struct net_device *dev, + const struct ethtool_ops gfar_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_MAX_FRAMES, ++ .get_eee = gfar_get_eee, ++ .set_eee = gfar_set_eee, + .get_drvinfo = gfar_gdrvinfo, + .get_regs_len = gfar_reglen, + .get_regs = gfar_get_regs, +diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/Kconfig b/drivers/net/ethernet/freescale/sdk_dpaa/Kconfig +index bb6693e2d702..9e219bbfd978 100644 +--- a/drivers/net/ethernet/freescale/sdk_dpaa/Kconfig ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/Kconfig +@@ -84,7 +84,7 @@ config FSL_DPAA_ETH_JUMBO_FRAME + config FSL_DPAA_TS + bool "Linux compliant timestamping" + depends on FSL_SDK_DPAA_ETH +- default n ++ default y + help + Enable Linux API compliant timestamping support. + +@@ -96,6 +96,15 @@ config FSL_DPAA_1588 + help + Enable IEEE1588 support code. + ++config FSL_DPAA_ETHERCAT ++ bool "Enable DPAA Ethercat support" ++ depends on FSL_SDK_DPAA_ETH ++ default n ++ help ++ Enable DPAA Ethercat support code, if enabling this feature, will create a new ++ QMan Portal for Ethercat port, and isolate the last cpu core for Ethercat traffic. ++ Regular ethernet traffic will NOT be in the isolated core. ++ + config FSL_DPAA_ETH_MAX_BUF_COUNT + int "Maximum number of buffers in the private bpool" + depends on FSL_SDK_DPAA_ETH +diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/Makefile b/drivers/net/ethernet/freescale/sdk_dpaa/Makefile +index a0623b24fa1c..9f9980099aad 100644 +--- a/drivers/net/ethernet/freescale/sdk_dpaa/Makefile ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/Makefile +@@ -11,6 +11,9 @@ ccflags-y += -I$(NET_DPA) + obj-$(CONFIG_FSL_SDK_DPAA_ETH) += fsl_mac.o fsl_dpa.o + + fsl_dpa-objs += dpaa_ethtool.o dpaa_eth_sysfs.o dpaa_eth.o dpaa_eth_sg.o dpaa_eth_common.o ++ifeq ($(CONFIG_FSL_DPAA_ETHERCAT),y) ++fsl_dpa-objs += dpaa_ethercat.o ++endif + ifeq ($(CONFIG_FSL_DPAA_DBG_LOOP),y) + fsl_dpa-objs += dpaa_debugfs.o + endif +diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h +index f8ab524b368c..1ccf55e475e7 100644 +--- a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h +@@ -1,5 +1,5 @@ + /* Copyright 2008-2012 Freescale Semiconductor Inc. +- * Copyright 2019 NXP ++ * Copyright 2019-2023 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: +@@ -415,6 +415,12 @@ struct dpa_priv_s { + #ifdef CONFIG_FSL_DPAA_CEETM + bool ceetm_en; /* CEETM QoS enabled */ + #endif ++ ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++ uint16_t ethercat_channel; /* "fsl,qman-channel-id" */ ++ struct qman_portal *p; ++ void *ecdev; ++#endif + }; + + struct fm_port_fqs { +@@ -461,6 +467,11 @@ int __hot skb_to_sg_fd(struct dpa_priv_s *priv, + int __cold __attribute__((nonnull)) + _dpa_fq_free(struct device *dev, struct qman_fq *fq); + ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++int dpa_unregister_ethercat(struct net_device *net_dev); ++int ec_dpaa_receive_data(void *pecdev, const void *data, size_t size); ++#endif ++ + /* Turn on HW checksum computation for this outgoing frame. + * If the current protocol is not something we support in this regard + * (or if the stack has already computed the SW checksum), we do nothing. +diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.c b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.c +index 8853c602d6af..641a7c0c0917 100644 +--- a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.c ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.c +@@ -1,4 +1,5 @@ + /* Copyright 2008-2013 Freescale Semiconductor, Inc. ++ * Copyright 2019-2023 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: +@@ -478,7 +479,15 @@ int __cold dpa_remove(struct platform_device *of_dev) + dpaa_eth_sysfs_remove(dev); + + dev_set_drvdata(dev, NULL); ++ ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++ if (priv->ecdev) ++ dpa_unregister_ethercat(net_dev); ++ else ++ unregister_netdev(net_dev); ++#else + unregister_netdev(net_dev); ++#endif + + err = dpa_fq_free(dev, &priv->dpa_fq_list); + +@@ -992,6 +1001,49 @@ void dpa_release_channel(void) + } + EXPORT_SYMBOL(dpa_release_channel); + ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++static int ec_cpu_isolated[NR_CPUS]; ++static int __init ethercat_cpus_setup(char *str) ++{ ++ int last_idx = -1; ++ int dash_flag = 0; ++ int idx = 0; ++ int i = 0; ++ int j = 0; ++ ++ if (!str) ++ return 0; ++ ++ for (i = 0; i < strlen(str); i++) { ++ if (str[i] == ',') { ++ last_idx = -1; ++ dash_flag = 0; ++ continue; ++ } else if ((str[i] == '-') && (last_idx >= 0)) { ++ dash_flag = 1; ++ continue; ++ } ++ ++ if ((str[i] >= '0') && (str[i] <= '9')) { ++ idx = str[i] - '0'; ++ ec_cpu_isolated[idx] = 1; ++ ++ if (dash_flag) { ++ for (j = last_idx; j < idx; j++) ++ ec_cpu_isolated[j] = 1; ++ last_idx = -1; ++ dash_flag = 0; ++ } else { ++ last_idx = idx; ++ } ++ } ++ } ++ ++ return 1; ++} ++__setup("ethercat_cpus=", ethercat_cpus_setup); ++#endif ++ + void dpaa_eth_add_channel(u16 channel) + { + const cpumask_t *cpus = qman_affine_cpus(); +@@ -1000,6 +1052,10 @@ void dpaa_eth_add_channel(u16 channel) + struct qman_portal *portal; + + for_each_cpu(cpu, cpus) { ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++ if (ec_cpu_isolated[cpu]) ++ continue; ++#endif + portal = (struct qman_portal *)qman_get_affine_portal(cpu); + qman_p_static_dequeue_add(portal, pool); + } +diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c +index 24704236b500..30c66abf6a8d 100644 +--- a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c +@@ -1,5 +1,5 @@ + /* Copyright 2012 Freescale Semiconductor Inc. +- * Copyright 2019 NXP ++ * Copyright 2019-2023 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: +@@ -608,10 +608,31 @@ void __hot _dpa_rx(struct net_device *net_dev, + * which case we were in) having been removed from the pool. + */ + (*count_ptr)--; ++#ifndef CONFIG_FSL_DPAA_ETHERCAT + skb->protocol = eth_type_trans(skb, net_dev); +- ++#endif + skb_len = skb->len; + ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++ if (priv->ecdev) { ++ u16 rawcpuid = 0; ++ u16 prot = 0; ++ ++ rawcpuid = (u16)raw_smp_processor_id(); ++ skb_record_rx_queue(skb, rawcpuid); ++ ++ prot = ntohs(*(u16 *)(skb->data + 12)); ++ if (prot == ETH_P_ETHERCAT) ++ ec_dpaa_receive_data(priv->ecdev, skb->data, skb->len); ++ else ++ pr_warn("invalid ethercat protocol 0x%x\n", prot); ++ ++ dev_kfree_skb(skb); ++ goto ethercat_tag; ++ } ++ skb->protocol = eth_type_trans(skb, net_dev); ++#endif ++ + #ifdef CONFIG_FSL_DPAA_DBG_LOOP + if (dpa_skb_loop(priv, skb)) { + percpu_stats->rx_packets++; +@@ -635,6 +656,9 @@ void __hot _dpa_rx(struct net_device *net_dev, + } else if (unlikely(netif_receive_skb(skb) == NET_RX_DROP)) + return; + ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++ethercat_tag: ++#endif + percpu_stats->rx_packets++; + percpu_stats->rx_bytes += skb_len; + +diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ethercat.c b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ethercat.c +new file mode 100644 +index 000000000000..67fa72257a6f +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ethercat.c +@@ -0,0 +1,1217 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2021-2023 NXP ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* arp_hdr_len() */ ++#include /* VLAN_HLEN */ ++#include /* struct icmphdr */ ++#include /* struct iphdr */ ++#include /* struct ipv6hdr */ ++#include /* struct udphdr */ ++#include /* struct tcphdr */ ++#include /* net_ratelimit() */ ++#include /* ETH_P_IP and ETH_P_IPV6 */ ++#include ++#include ++#include ++#include ++#ifdef CONFIG_SOC_BUS ++#include /* soc_device_match */ ++#endif ++ ++#include "fsl_fman.h" ++#include "fm_ext.h" ++#include "fm_port_ext.h" ++ ++#include "mac.h" ++#include "dpaa_eth.h" ++#include "dpaa_eth_common.h" ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++#include "dpaa_debugfs.h" ++#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ ++ ++#define DPA_NAPI_WEIGHT 64 ++ ++/* Valid checksum indication */ ++#define DPA_CSUM_VALID 0xFFFF ++ ++#define DPA_DESCRIPTION "FSL/NXP DPAA Ethercat driver" ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++MODULE_DESCRIPTION(DPA_DESCRIPTION); ++ ++static u8 debug = -1; ++ ++/* This has to work in tandem with the DPA_CS_THRESHOLD_xxx values. */ ++static u16 tx_timeout = 1000; ++ ++static const char rtx[][3] = { ++ [RX] = "RX", ++ [TX] = "TX" ++}; ++ ++/* BM */ ++ ++#define DPAA_ETH_MAX_PAD (L1_CACHE_BYTES * 8) ++ ++static u32 dpa_priv_bpid; ++static u32 dpa_priv_cpuid; ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++struct net_device *dpa_loop_netdevs[20]; ++#endif ++ ++#ifdef CONFIG_PM ++ ++static int dpaa_suspend(struct device *dev) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct mac_device *mac_dev; ++ int err = 0; ++ ++ net_dev = dev_get_drvdata(dev); ++ ++ if (net_dev->flags & IFF_UP) { ++ priv = netdev_priv(net_dev); ++ mac_dev = priv->mac_dev; ++ ++ if (priv->wol & DPAA_WOL_MAGIC) { ++ err = priv->mac_dev->set_wol(mac_dev->port_dev[RX], ++ priv->mac_dev->get_mac_handle(mac_dev), true); ++ if (err) { ++ netdev_err(net_dev, "set_wol() = %d\n", err); ++ goto set_wol_failed; ++ } ++ } ++ ++ err = fm_port_suspend(mac_dev->port_dev[RX]); ++ if (err) { ++ netdev_err(net_dev, "fm_port_suspend(RX) = %d\n", err); ++ goto rx_port_suspend_failed; ++ } ++ ++ err = fm_port_suspend(mac_dev->port_dev[TX]); ++ if (err) { ++ netdev_err(net_dev, "fm_port_suspend(TX) = %d\n", err); ++ goto tx_port_suspend_failed; ++ } ++ } ++ ++ return 0; ++ ++tx_port_suspend_failed: ++ fm_port_resume(mac_dev->port_dev[RX]); ++rx_port_suspend_failed: ++ if (priv->wol & DPAA_WOL_MAGIC) { ++ priv->mac_dev->set_wol(mac_dev->port_dev[RX], ++ priv->mac_dev->get_mac_handle(mac_dev), false); ++ } ++set_wol_failed: ++ return err; ++} ++ ++static int dpaa_resume(struct device *dev) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct mac_device *mac_dev; ++ int err = 0; ++ ++ net_dev = dev_get_drvdata(dev); ++ ++ if (net_dev->flags & IFF_UP) { ++ priv = netdev_priv(net_dev); ++ mac_dev = priv->mac_dev; ++ ++ err = fm_mac_resume(mac_dev->get_mac_handle(mac_dev)); ++ if (err) { ++ netdev_err(net_dev, "fm_mac_resume = %d\n", err); ++ goto resume_failed; ++ } ++ ++ err = fm_port_resume(mac_dev->port_dev[TX]); ++ if (err) { ++ netdev_err(net_dev, "fm_port_resume(TX) = %d\n", err); ++ goto resume_failed; ++ } ++ ++ err = fm_port_resume(mac_dev->port_dev[RX]); ++ if (err) { ++ netdev_err(net_dev, "fm_port_resume(RX) = %d\n", err); ++ goto resume_failed; ++ } ++ ++ if (priv->wol & DPAA_WOL_MAGIC) { ++ err = priv->mac_dev->set_wol(mac_dev->port_dev[RX], ++ priv->mac_dev->get_mac_handle(mac_dev), false); ++ if (err) { ++ netdev_err(net_dev, "set_wol() = %d\n", err); ++ goto resume_failed; ++ } ++ } ++ } ++ ++ return 0; ++ ++resume_failed: ++ return err; ++} ++ ++static const struct dev_pm_ops dpaa_pm_ops = { ++ .suspend = dpaa_suspend, ++ .resume = dpaa_resume, ++}; ++ ++#define DPAA_PM_OPS (&dpaa_pm_ops) ++ ++#else /* CONFIG_PM */ ++ ++#define DPAA_PM_OPS NULL ++ ++#endif /* CONFIG_PM */ ++ ++/* Checks whether the checksum field in Parse Results array is valid ++ * (equals 0xFFFF) and increments the .cse counter otherwise ++ */ ++static inline void ++dpa_csum_validation(const struct dpa_priv_s *priv, ++ struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_fd *fd) ++{ ++ dma_addr_t addr = qm_fd_addr(fd); ++ struct dpa_bp *dpa_bp = priv->dpa_bp; ++ void *frm = phys_to_virt(addr); ++ fm_prs_result_t *parse_result; ++ ++ if (unlikely(!frm)) ++ return; ++ ++ dma_sync_single_for_cpu(dpa_bp->dev, addr, DPA_RX_PRIV_DATA_SIZE + ++ DPA_PARSE_RESULTS_SIZE, DMA_BIDIRECTIONAL); ++ ++ parse_result = (fm_prs_result_t *)(frm + DPA_RX_PRIV_DATA_SIZE); ++ ++ if (parse_result->cksum != DPA_CSUM_VALID) ++ percpu_priv->rx_errors.cse++; ++} ++ ++static void _dpa_rx_error(struct net_device *net_dev, ++ const struct dpa_priv_s *priv, ++ struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_fd *fd, ++ u32 fqid) ++{ ++ /* limit common, possibly innocuous Rx FIFO Overflow errors' ++ * interference with zero-loss convergence benchmark results. ++ */ ++ if (likely(fd->status & FM_FD_STAT_ERR_PHYSICAL)) ++ pr_warn_once("fsl-dpa: non-zero error counters in fman statistics (sysfs)\n"); ++ else ++ if (netif_msg_hw(priv) && net_ratelimit()) ++ netdev_dbg(net_dev, "Err FD status = 0x%08x\n", ++ fd->status & FM_FD_STAT_RX_ERRORS); ++#ifdef CONFIG_FSL_DPAA_HOOKS ++ if (dpaa_eth_hooks.rx_error && ++ dpaa_eth_hooks.rx_error(net_dev, fd, fqid) == DPAA_ETH_STOLEN) ++ /* it's up to the hook to perform resource cleanup */ ++ return; ++#endif ++ percpu_priv->stats.rx_errors++; ++ ++ if (fd->status & FM_PORT_FRM_ERR_DMA) ++ percpu_priv->rx_errors.dme++; ++ if (fd->status & FM_PORT_FRM_ERR_PHYSICAL) ++ percpu_priv->rx_errors.fpe++; ++ if (fd->status & FM_PORT_FRM_ERR_SIZE) ++ percpu_priv->rx_errors.fse++; ++ if (fd->status & FM_PORT_FRM_ERR_PRS_HDR_ERR) ++ percpu_priv->rx_errors.phe++; ++ if (fd->status & FM_FD_STAT_L4CV) ++ dpa_csum_validation(priv, percpu_priv, fd); ++ ++ dpa_fd_release(net_dev, fd); ++} ++ ++static void _dpa_tx_error(struct net_device *net_dev, ++ const struct dpa_priv_s *priv, ++ struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_fd *fd, ++ u32 fqid) ++{ ++ struct sk_buff *skb; ++ ++ if (netif_msg_hw(priv) && net_ratelimit()) ++ netdev_warn(net_dev, "FD status = 0x%08x\n", ++ fd->status & FM_FD_STAT_TX_ERRORS); ++#ifdef CONFIG_FSL_DPAA_HOOKS ++ if (dpaa_eth_hooks.tx_error && ++ dpaa_eth_hooks.tx_error(net_dev, fd, fqid) == DPAA_ETH_STOLEN) ++ /* now the hook must ensure proper cleanup */ ++ return; ++#endif ++ percpu_priv->stats.tx_errors++; ++ ++ /* If we intended the buffers from this frame to go into the bpools ++ * when the FMan transmit was done, we need to put it in manually. ++ */ ++ if (fd->bpid != 0xff) { ++ dpa_fd_release(net_dev, fd); ++ return; ++ } ++ ++ skb = _dpa_cleanup_tx_fd(priv, fd); ++ if (!priv->ecdev) ++ dev_kfree_skb(skb); ++} ++ ++static void __hot _dpa_tx_conf(struct net_device *net_dev, ++ const struct dpa_priv_s *priv, ++ struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_fd *fd, ++ u32 fqid) ++{ ++ struct sk_buff *skb; ++ ++ /* do we need the timestamp for the error frames? */ ++ ++ if (unlikely(fd->status & FM_FD_STAT_TX_ERRORS) != 0) { ++ if (netif_msg_hw(priv) && net_ratelimit()) ++ netdev_warn(net_dev, "FD status = 0x%08x\n", ++ fd->status & FM_FD_STAT_TX_ERRORS); ++ ++ percpu_priv->stats.tx_errors++; ++ } ++ ++ /* hopefully we need not get the timestamp before the hook */ ++#ifdef CONFIG_FSL_DPAA_HOOKS ++ if (dpaa_eth_hooks.tx_confirm && ++ dpaa_eth_hooks.tx_confirm(net_dev, fd, fqid) == DPAA_ETH_STOLEN) ++ /* it's the hook that must now perform cleanup */ ++ return; ++#endif ++ /* This might not perfectly reflect the reality, if the core dequeuing ++ * the Tx confirmation is different from the one that did the enqueue, ++ * but at least it'll show up in the total count. ++ */ ++ percpu_priv->tx_confirm++; ++ ++ skb = _dpa_cleanup_tx_fd(priv, fd); ++ if (!priv->ecdev) ++ dev_kfree_skb(skb); ++} ++ ++static enum qman_cb_dqrr_result ++priv_rx_error_dqrr(struct qman_portal *portal, ++ struct qman_fq *fq, ++ const struct qm_dqrr_entry *dq) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct dpa_percpu_priv_s *percpu_priv; ++ int *count_ptr; ++ ++ net_dev = ((struct dpa_fq *)fq)->net_dev; ++ priv = netdev_priv(net_dev); ++ ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ count_ptr = raw_cpu_ptr(priv->percpu_count); ++ ++ if (unlikely(dpaa_eth_refill_bpools(priv->dpa_bp, count_ptr))) ++ /* Unable to refill the buffer pool due to insufficient ++ * system memory. Just release the frame back into the pool, ++ * otherwise we'll soon end up with an empty buffer pool. ++ */ ++ dpa_fd_release(net_dev, &dq->fd); ++ else ++ _dpa_rx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); ++ ++ return qman_cb_dqrr_consume; ++} ++ ++static enum qman_cb_dqrr_result __hot ++priv_rx_default_dqrr(struct qman_portal *portal, ++ struct qman_fq *fq, ++ const struct qm_dqrr_entry *dq) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct dpa_percpu_priv_s *percpu_priv; ++ int *count_ptr; ++ struct dpa_bp *dpa_bp; ++ ++ net_dev = ((struct dpa_fq *)fq)->net_dev; ++ priv = netdev_priv(net_dev); ++ dpa_bp = priv->dpa_bp; ++ ++ /* IRQ handler, non-migratable; safe to use raw_cpu_ptr here */ ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ count_ptr = raw_cpu_ptr(priv->percpu_count); ++ ++ /* Vale of plenty: make sure we didn't run out of buffers */ ++ ++ if (unlikely(dpaa_eth_refill_bpools(dpa_bp, count_ptr))) ++ /* Unable to refill the buffer pool due to insufficient ++ * system memory. Just release the frame back into the pool, ++ * otherwise we'll soon end up with an empty buffer pool. ++ */ ++ dpa_fd_release(net_dev, &dq->fd); ++ else ++ _dpa_rx(net_dev, portal, priv, percpu_priv, &dq->fd, fq->fqid, ++ count_ptr); ++ ++ return qman_cb_dqrr_consume; ++} ++ ++static enum qman_cb_dqrr_result ++priv_tx_conf_error_dqrr(struct qman_portal *portal, ++ struct qman_fq *fq, ++ const struct qm_dqrr_entry *dq) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct dpa_percpu_priv_s *percpu_priv; ++ ++ net_dev = ((struct dpa_fq *)fq)->net_dev; ++ priv = netdev_priv(net_dev); ++ ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ ++ _dpa_tx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); ++ ++ return qman_cb_dqrr_consume; ++} ++ ++static enum qman_cb_dqrr_result __hot ++priv_tx_conf_default_dqrr(struct qman_portal *portal, ++ struct qman_fq *fq, ++ const struct qm_dqrr_entry *dq) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct dpa_percpu_priv_s *percpu_priv; ++ ++ net_dev = ((struct dpa_fq *)fq)->net_dev; ++ priv = netdev_priv(net_dev); ++ ++ /* Non-migratable context, safe to use raw_cpu_ptr */ ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ ++ _dpa_tx_conf(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); ++ ++ return qman_cb_dqrr_consume; ++} ++ ++static void priv_ern(struct qman_portal *portal, ++ struct qman_fq *fq, ++ const struct qm_mr_entry *msg) ++{ ++ struct net_device *net_dev; ++ const struct dpa_priv_s *priv; ++ struct sk_buff *skb; ++ struct dpa_percpu_priv_s *percpu_priv; ++ struct qm_fd fd = msg->ern.fd; ++ ++ net_dev = ((struct dpa_fq *)fq)->net_dev; ++ priv = netdev_priv(net_dev); ++ /* Non-migratable context, safe to use raw_cpu_ptr */ ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ ++ percpu_priv->stats.tx_dropped++; ++ percpu_priv->stats.tx_fifo_errors++; ++ count_ern(percpu_priv, msg); ++ ++ /* If we intended this buffer to go into the pool ++ * when the FM was done, we need to put it in ++ * manually. ++ */ ++ if (msg->ern.fd.bpid != 0xff) { ++ dpa_fd_release(net_dev, &fd); ++ return; ++ } ++ ++ skb = _dpa_cleanup_tx_fd(priv, &fd); ++ dev_kfree_skb_any(skb); ++} ++ ++static const struct dpa_fq_cbs_t private_fq_cbs = { ++ .rx_defq = { .cb = { .dqrr = priv_rx_default_dqrr } }, ++ .tx_defq = { .cb = { .dqrr = priv_tx_conf_default_dqrr } }, ++ .rx_errq = { .cb = { .dqrr = priv_rx_error_dqrr } }, ++ .tx_errq = { .cb = { .dqrr = priv_tx_conf_error_dqrr } }, ++ .egress_ern = { .cb = { .ern = priv_ern } } ++}; ++ ++static int __cold dpa_eth_priv_start(struct net_device *net_dev) ++{ ++ int err; ++ struct dpa_priv_s *priv; ++ ++ priv = netdev_priv(net_dev); ++ ++ err = dpa_start(net_dev); ++ ++ return err; ++} ++ ++static int __cold dpa_eth_priv_stop(struct net_device *net_dev) ++{ ++ int _errno; ++ struct dpa_priv_s *priv; ++ ++ _errno = dpa_stop(net_dev); ++ /* Allow NAPI to consume any frame still in the Rx/TxConfirm ++ * ingress queues. This is to avoid a race between the current ++ * context and ksoftirqd which could leave NAPI disabled while ++ * in fact there's still Rx traffic to be processed. ++ */ ++ usleep_range(5000, 10000); ++ ++ priv = netdev_priv(net_dev); ++ ++ return _errno; ++} ++ ++static const struct net_device_ops dpa_private_ops = { ++ .ndo_open = dpa_eth_priv_start, ++ .ndo_start_xmit = dpa_tx, ++ .ndo_stop = dpa_eth_priv_stop, ++ .ndo_tx_timeout = dpa_timeout, ++ .ndo_get_stats64 = dpa_get_stats64, ++ .ndo_set_mac_address = dpa_set_mac_address, ++ .ndo_validate_addr = eth_validate_addr, ++#ifdef CONFIG_FMAN_PFC ++ .ndo_select_queue = dpa_select_queue, ++#endif ++ .ndo_set_rx_mode = dpa_set_rx_mode, ++ .ndo_init = dpa_ndo_init, ++ .ndo_set_features = dpa_set_features, ++ .ndo_fix_features = dpa_fix_features, ++ .ndo_do_ioctl = dpa_ioctl, ++}; ++ ++typedef int (*ec_dpaa_receive_cb)(void *pecdev, const void *data, size_t size); ++typedef int (*ec_dpaa_link_cb)(void *pecdev, uint8_t link); ++typedef int (*ec_dpaa_close_cb)(void *pecdev); ++ ++static ec_dpaa_receive_cb ec_dpaa_recv_func; ++static ec_dpaa_close_cb ec_dpaa_close_func; ++static ec_dpaa_link_cb ec_dpaa_link_func; ++ ++int ec_dpaa_receive_data(void *pecdev, const void *data, size_t size) ++{ ++ int ret = 0; ++ ++ if (ec_dpaa_recv_func) ++ ret = ec_dpaa_recv_func(pecdev, data, size); ++ ++ return ret; ++} ++ ++int ec_dpaa_set_func_cb(ec_dpaa_receive_cb recv, ec_dpaa_link_cb link, ec_dpaa_close_cb close) ++{ ++ ec_dpaa_recv_func = recv; ++ ec_dpaa_link_func = link; ++ ec_dpaa_close_func = close; ++ ++ return 0; ++} ++EXPORT_SYMBOL(ec_dpaa_set_func_cb); ++ ++struct module *ec_dpaa_get_module(void) ++{ ++ struct module *m = THIS_MODULE; ++ return m; ++} ++EXPORT_SYMBOL(ec_dpaa_get_module); ++ ++#define MAX_EC_DPAA_NETDEV_CNT (16) ++static int ec_dpaa_netdev_cnt; ++static struct net_device *ec_dpaa_netdev[MAX_EC_DPAA_NETDEV_CNT]; ++ ++struct net_device *ec_dpaa_get_netdev(int idx) ++{ ++ if (idx >= MAX_EC_DPAA_NETDEV_CNT) ++ return NULL; ++ ++ if (idx >= ec_dpaa_netdev_cnt) ++ return NULL; ++ ++ return ec_dpaa_netdev[idx]; ++} ++EXPORT_SYMBOL(ec_dpaa_get_netdev); ++ ++int ec_dpaa_set_ecdev(int idx, void *ecdev) ++{ ++ struct net_device *net_dev = NULL; ++ struct dpa_priv_s *priv = NULL; ++ ++ net_dev = ec_dpaa_get_netdev(idx); ++ if (net_dev) { ++ priv = netdev_priv(net_dev); ++ priv->ecdev = ecdev; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(ec_dpaa_set_ecdev); ++ ++void *ec_dpaa_get_ecdev(int idx) ++{ ++ struct net_device *net_dev = NULL; ++ struct dpa_priv_s *priv = NULL; ++ void *ecdev = NULL; ++ ++ net_dev = ec_dpaa_get_netdev(idx); ++ if (net_dev) { ++ priv = netdev_priv(net_dev); ++ ecdev = priv->ecdev; ++ } ++ ++ return ecdev; ++} ++EXPORT_SYMBOL(ec_dpaa_get_ecdev); ++ ++void ec_dpaa_poll(struct net_device *net_dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ u8 link = net_dev->phydev->state; ++ ++ qman_p_poll_dqrr(priv->p, DPA_NAPI_WEIGHT); ++ ++ if (ec_dpaa_link_func) ++ ec_dpaa_link_func(priv->ecdev, link); ++} ++EXPORT_SYMBOL(ec_dpaa_poll); ++ ++int dpa_unregister_ethercat(struct net_device *net_dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ ++ if (priv->ecdev) { ++ if (ec_dpaa_close_func) ++ ec_dpaa_close_func(priv->ecdev); ++ return 0; ++ } ++ ++ return -1; ++} ++EXPORT_SYMBOL(dpa_unregister_ethercat); ++ ++static int dpa_ethercat_netdev_init(struct net_device *net_dev, ++ const u8 *mac_addr, ++ uint16_t tx_timeout) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ ++ net_dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; ++ ++ net_dev->features |= net_dev->hw_features; ++ net_dev->vlan_features = net_dev->features; ++ ++ memcpy(net_dev->perm_addr, mac_addr, net_dev->addr_len); ++ memcpy(net_dev->dev_addr, mac_addr, net_dev->addr_len); ++ ++ net_dev->ethtool_ops = &dpa_ethtool_ops; ++ ++ net_dev->needed_headroom = priv->tx_headroom; ++ net_dev->watchdog_timeo = msecs_to_jiffies(tx_timeout); ++ ++ if (ec_dpaa_netdev_cnt < MAX_EC_DPAA_NETDEV_CNT) ++ ec_dpaa_netdev[ec_dpaa_netdev_cnt++] = net_dev; ++ ++ return 0; ++} ++ ++static int dpa_private_netdev_init(struct net_device *net_dev) ++{ ++ int i; ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ struct dpa_percpu_priv_s *percpu_priv; ++ const u8 *mac_addr; ++ ++ /* Although we access another CPU's private data here ++ * we do it at initialization so it is safe ++ */ ++ for_each_possible_cpu(i) { ++ percpu_priv = per_cpu_ptr(priv->percpu_priv, i); ++ percpu_priv->net_dev = net_dev; ++ } ++ ++ net_dev->netdev_ops = &dpa_private_ops; ++ mac_addr = priv->mac_dev->addr; ++ ++ net_dev->mem_start = priv->mac_dev->res->start; ++ net_dev->mem_end = priv->mac_dev->res->end; ++ ++ /* Configure the maximum MTU according to the FMan's MAXFRM */ ++ net_dev->min_mtu = ETH_MIN_MTU; ++ net_dev->max_mtu = dpa_get_max_mtu(); ++ ++ net_dev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_LLTX); ++ ++ /* Advertise S/G and HIGHDMA support for private interfaces */ ++ net_dev->hw_features |= NETIF_F_SG | NETIF_F_HIGHDMA; ++ /* Recent kernels enable GSO automatically, if ++ * we declare NETIF_F_SG. For conformity, we'll ++ * still declare GSO explicitly. ++ */ ++ net_dev->features |= NETIF_F_GSO; ++ ++ /* Advertise GRO support */ ++ net_dev->features |= NETIF_F_GRO; ++ ++ return dpa_ethercat_netdev_init(net_dev, mac_addr, tx_timeout); ++} ++ ++static struct dpa_bp * __cold ++dpa_priv_bp_probe(struct device *dev) ++{ ++ struct dpa_bp *dpa_bp; ++ ++ dpa_bp = devm_kzalloc(dev, sizeof(*dpa_bp), GFP_KERNEL); ++ if (unlikely(!dpa_bp)) { ++ dev_err(dev, "devm_kzalloc() failed\n"); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ dpa_bp->target_count = CONFIG_FSL_DPAA_ETH_MAX_BUF_COUNT; ++ ++ dpa_bp->free_buf_cb = _dpa_bp_free_pf; ++ ++ return dpa_bp; ++} ++ ++/* Place all ingress FQs (Rx Default, Rx Error, PCD FQs) in a dedicated CGR. ++ * We won't be sending congestion notifications to FMan; for now, we just use ++ * this CGR to generate enqueue rejections to FMan in order to drop the frames ++ * before they reach our ingress queues and eat up memory. ++ */ ++static int dpaa_eth_priv_ingress_cgr_init(struct dpa_priv_s *priv) ++{ ++ struct qm_mcc_initcgr initcgr; ++ u32 cs_th; ++ int err; ++ ++ err = qman_alloc_cgrid(&priv->ingress_cgr.cgrid); ++ if (err < 0) { ++ pr_err("Error %d allocating CGR ID\n", err); ++ goto out_error; ++ } ++ ++ /* Enable CS TD, but disable Congestion State Change Notifications. */ ++ memset(&initcgr, 0, sizeof(initcgr)); ++ initcgr.we_mask = QM_CGR_WE_CS_THRES; ++ initcgr.cgr.cscn_en = QM_CGR_EN; ++ cs_th = CONFIG_FSL_DPAA_INGRESS_CS_THRESHOLD; ++ qm_cgr_cs_thres_set64(&initcgr.cgr.cs_thres, cs_th, 1); ++ ++ initcgr.we_mask |= QM_CGR_WE_CSTD_EN; ++ initcgr.cgr.cstd_en = QM_CGR_EN; ++ ++ /* This is actually a hack, because this CGR will be associated with ++ * our affine SWP. However, we'll place our ingress FQs in it. ++ */ ++ err = qman_create_cgr(&priv->ingress_cgr, QMAN_CGR_FLAG_USE_INIT, ++ &initcgr); ++ if (err < 0) { ++ pr_err("Error %d creating ingress CGR with ID %d\n", err, ++ priv->ingress_cgr.cgrid); ++ qman_release_cgrid(priv->ingress_cgr.cgrid); ++ goto out_error; ++ } ++ pr_debug("Created ingress CGR %d for netdev with hwaddr %pM\n", ++ priv->ingress_cgr.cgrid, priv->mac_dev->addr); ++ ++ /* struct qman_cgr allows special cgrid values (i.e. outside the 0..255 ++ * range), but we have no common initialization path between the ++ * different variants of the DPAA Eth driver, so we do it here rather ++ * than modifying every other variant than "private Eth". ++ */ ++ priv->use_ingress_cgr = true; ++ ++out_error: ++ return err; ++} ++ ++static void dpa_priv_bp_seed(struct net_device *net_dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ struct dpa_bp *dpa_bp = priv->dpa_bp; ++ int i; ++ ++ /* Give each CPU an allotment of buffers */ ++ for_each_possible_cpu(i) { ++ /* Although we access another CPU's counters here ++ * we do it at boot time so it is safe ++ */ ++ int *count_ptr = per_cpu_ptr(priv->percpu_count, i); ++ ++ dpaa_eth_refill_bpools(dpa_bp, count_ptr); ++ } ++} ++ ++static const struct of_device_id dpa_match[]; ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++static int dpa_new_loop_id(void) ++{ ++ static int if_id; ++ ++ return if_id++; ++} ++#endif ++ ++extern int dpa_bp_create(struct net_device *net_dev, struct dpa_bp *dpa_bp, ++ size_t count); ++ ++static inline void dpa_setup_ingress(const struct dpa_priv_s *priv, ++ struct dpa_fq *fq, ++ const struct qman_fq *template) ++{ ++ fq->fq_base = *template; ++ fq->net_dev = priv->net_dev; ++ ++ fq->flags = QMAN_FQ_FLAG_NO_ENQUEUE; ++ fq->channel = priv->channel; ++} ++ ++static inline void dpa_setup_egress(const struct dpa_priv_s *priv, ++ struct dpa_fq *fq, ++ struct fm_port *port, ++ const struct qman_fq *template) ++{ ++ fq->fq_base = *template; ++ fq->net_dev = priv->net_dev; ++ ++ if (port) { ++ fq->flags = QMAN_FQ_FLAG_TO_DCPORTAL; ++ fq->channel = (u16)fm_get_tx_port_channel(port); ++ } else { ++ fq->flags = QMAN_FQ_FLAG_NO_MODIFY; ++ } ++} ++ ++static void dpa_fq_setup_ethercat(struct dpa_priv_s *priv, const struct dpa_fq_cbs_t *fq_cbs, ++ struct fm_port *tx_port) ++{ ++ struct dpa_fq *fq; ++ u16 portals[NR_CPUS]; ++ int cpu, portal_cnt = 0, num_portals = 0; ++ u32 pcd_fqid, pcd_fqid_hi_prio; ++ const cpumask_t *affine_cpus = qman_affine_cpus(); ++ int egress_cnt = 0, conf_cnt = 0; ++ ++ /* Prepare for PCD FQs init */ ++ for_each_cpu(cpu, affine_cpus) ++ portals[num_portals++] = priv->ethercat_channel; ++ ++ if (num_portals == 0) ++ dev_err(priv->net_dev->dev.parent, ++ "No Qman software (affine) channels found"); ++ ++ pcd_fqid = (priv->mac_dev) ? ++ DPAA_ETH_PCD_FQ_BASE(priv->mac_dev->res->start) : 0; ++ pcd_fqid_hi_prio = (priv->mac_dev) ? ++ DPAA_ETH_PCD_FQ_HI_PRIO_BASE(priv->mac_dev->res->start) : 0; ++ ++ /* Initialize each FQ in the list */ ++ list_for_each_entry(fq, &priv->dpa_fq_list, list) { ++ switch (fq->fq_type) { ++ case FQ_TYPE_RX_DEFAULT: ++ WARN_ON(!priv->mac_dev); ++ dpa_setup_ingress(priv, fq, &fq_cbs->rx_defq); ++ break; ++ case FQ_TYPE_RX_ERROR: ++ WARN_ON(!priv->mac_dev); ++ dpa_setup_ingress(priv, fq, &fq_cbs->rx_errq); ++ break; ++ case FQ_TYPE_RX_PCD: ++ /* For MACless we can't have dynamic Rx queues */ ++ WARN_ON(!priv->mac_dev && !fq->fqid); ++ dpa_setup_ingress(priv, fq, &fq_cbs->rx_defq); ++ if (!fq->fqid) ++ fq->fqid = pcd_fqid++; ++ fq->channel = portals[portal_cnt]; ++ portal_cnt = (portal_cnt + 1) % num_portals; ++ break; ++ case FQ_TYPE_RX_PCD_HI_PRIO: ++ /* For MACless we can't have dynamic Hi Pri Rx queues */ ++ WARN_ON(!priv->mac_dev && !fq->fqid); ++ dpa_setup_ingress(priv, fq, &fq_cbs->rx_defq); ++ if (!fq->fqid) ++ fq->fqid = pcd_fqid_hi_prio++; ++ fq->channel = portals[portal_cnt]; ++ portal_cnt = (portal_cnt + 1) % num_portals; ++ break; ++ case FQ_TYPE_TX: ++ dpa_setup_egress(priv, fq, tx_port, ++ &fq_cbs->egress_ern); ++ /* If we have more Tx queues than the number of cores, ++ * just ignore the extra ones. ++ */ ++ if (egress_cnt < DPAA_ETH_TX_QUEUES) ++ priv->egress_fqs[egress_cnt++] = &fq->fq_base; ++ break; ++ case FQ_TYPE_TX_CONFIRM: ++ WARN_ON(!priv->mac_dev); ++ dpa_setup_ingress(priv, fq, &fq_cbs->tx_defq); ++ break; ++ case FQ_TYPE_TX_CONF_MQ: ++ WARN_ON(!priv->mac_dev); ++ dpa_setup_ingress(priv, fq, &fq_cbs->tx_defq); ++ priv->conf_fqs[conf_cnt++] = &fq->fq_base; ++ break; ++ case FQ_TYPE_TX_ERROR: ++ WARN_ON(!priv->mac_dev); ++ dpa_setup_ingress(priv, fq, &fq_cbs->tx_errq); ++ break; ++ default: ++ dev_warn(priv->net_dev->dev.parent, ++ "Unknown FQ type detected!\n"); ++ break; ++ } ++ } ++ ++ /* The number of Tx queues may be smaller than the number of cores, if ++ * the Tx queue range is specified in the device tree instead of being ++ * dynamically allocated. ++ * Make sure all CPUs receive a corresponding Tx queue. ++ */ ++ while (egress_cnt < DPAA_ETH_TX_QUEUES) { ++ list_for_each_entry(fq, &priv->dpa_fq_list, list) { ++ if (fq->fq_type != FQ_TYPE_TX) ++ continue; ++ priv->egress_fqs[egress_cnt++] = &fq->fq_base; ++ if (egress_cnt == DPAA_ETH_TX_QUEUES) ++ break; ++ } ++ } ++} ++ ++static int dpaa_ethercat_probe(struct platform_device *_of_dev) ++{ ++ int err = 0, i; ++ struct device *dev; ++ struct device_node *dpa_node; ++ struct dpa_bp *dpa_bp; ++ size_t count = 1; ++ struct net_device *net_dev = NULL; ++ struct dpa_priv_s *priv = NULL; ++ struct dpa_percpu_priv_s *percpu_priv; ++ struct fm_port_fqs port_fqs; ++ struct dpa_buffer_layout_s *buf_layout = NULL; ++ struct mac_device *mac_dev; ++ u32 last_cpu = 0; ++ ++ dev = &_of_dev->dev; ++ ++ dpa_node = dev->of_node; ++ ++ if (!of_device_is_available(dpa_node)) ++ return -ENODEV; ++ ++ /* Get the buffer pools assigned to this interface; ++ * run only once the default pool probing code ++ */ ++ ++ err = of_property_read_u32(dpa_node, "fsl,bpid", &dpa_priv_bpid); ++ if (err) { ++ dev_err(dev, "Cannot find buffer pool ID in the device tree\n"); ++ return -EINVAL; ++ } ++ ++ last_cpu = qman_get_affine_last_cpu(); ++ err = of_property_read_u32(dpa_node, "fsl,cpuid", &dpa_priv_cpuid); ++ if (err) { ++ dev_warn(dev, "Cannot find cpuid in the device tree, using the last core\n"); ++ dpa_priv_cpuid = last_cpu; ++ } else if (dpa_priv_cpuid > last_cpu) { ++ dev_warn(dev, "Invalid cpuid in the device tree, using the last core\n"); ++ dpa_priv_cpuid = last_cpu; ++ } ++ ++ dpa_bp = (dpa_bpid2pool(dpa_priv_bpid)) ? : ++ dpa_priv_bp_probe(dev); ++ if (IS_ERR(dpa_bp)) ++ return PTR_ERR(dpa_bp); ++ if (dpa_bp->bpid == 0) ++ dpa_bp->bpid = dpa_priv_bpid; ++ ++ /* Allocate this early, so we can store relevant information in ++ * the private area (needed by 1588 code in dpa_mac_probe) ++ */ ++ net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA_ETH_TX_QUEUES); ++ if (!net_dev) { ++ dev_err(dev, "alloc_etherdev_mq() failed\n"); ++ goto alloc_etherdev_mq_failed; ++ } ++ ++ /* Do this here, so we can be verbose early */ ++ SET_NETDEV_DEV(net_dev, dev); ++ dev_set_drvdata(dev, net_dev); ++ ++ priv = netdev_priv(net_dev); ++ priv->net_dev = net_dev; ++ strcpy(priv->if_type, "private"); ++ ++ priv->msg_enable = netif_msg_init(debug, -1); ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ priv->loop_id = dpa_new_loop_id(); ++ priv->loop_to = -1; /* disabled by default */ ++ dpa_loop_netdevs[priv->loop_id] = net_dev; ++#endif ++ ++ mac_dev = dpa_mac_probe(_of_dev); ++ if (IS_ERR(mac_dev) || !mac_dev) { ++ err = PTR_ERR(mac_dev); ++ goto mac_probe_failed; ++ } ++ ++ /* We have physical ports, so we need to establish ++ * the buffer layout. ++ */ ++ buf_layout = devm_kzalloc(dev, 2 * sizeof(*buf_layout), ++ GFP_KERNEL); ++ if (!buf_layout) { ++ dev_err(dev, "devm_kzalloc() failed\n"); ++ goto alloc_failed; ++ } ++ dpa_set_buffers_layout(mac_dev, buf_layout); ++ ++ /* For private ports, need to compute the size of the default ++ * buffer pool, based on FMan port buffer layout;also update ++ * the maximum buffer size for private ports if necessary ++ */ ++ dpa_bp->size = dpa_bp_size(&buf_layout[RX]); ++ ++#ifdef CONFIG_FSL_DPAA_ETH_JUMBO_FRAME ++ /* We only want to use jumbo frame optimization if we actually have ++ * L2 MAX FRM set for jumbo frames as well. ++ */ ++ if (fm_get_max_frm() < 9600) ++ dev_warn(dev, ++ "Invalid configuration: if jumbo frames support is on, FSL_FM_MAX_FRAME_SIZE should be set to 9600\n"); ++#endif ++ ++ INIT_LIST_HEAD(&priv->dpa_fq_list); ++ ++ memset(&port_fqs, 0, sizeof(port_fqs)); ++ ++ err = dpa_fq_probe_mac(dev, &priv->dpa_fq_list, &port_fqs, true, RX); ++ if (!err) ++ err = dpa_fq_probe_mac(dev, &priv->dpa_fq_list, ++ &port_fqs, true, TX); ++ ++ if (err < 0) ++ goto fq_probe_failed; ++ ++ /* bp init */ ++ ++ err = dpa_bp_create(net_dev, dpa_bp, count); ++ ++ if (err < 0) ++ goto bp_create_failed; ++ ++ priv->mac_dev = mac_dev; ++ priv->ethercat_channel = (u16)qman_affine_channel_ethercat(dpa_priv_cpuid); ++ priv->channel = priv->ethercat_channel; ++ priv->p = qman_get_affine_portal_ethercat(dpa_priv_cpuid); ++ ++ dpa_fq_setup_ethercat(priv, &private_fq_cbs, priv->mac_dev->port_dev[TX]); ++ ++ /* Create a congestion group for this netdev, with ++ * dynamically-allocated CGR ID. ++ * Must be executed after probing the MAC, but before ++ * assigning the egress FQs to the CGRs. ++ */ ++ err = dpaa_eth_cgr_init(priv); ++ if (err < 0) { ++ dev_err(dev, "Error initializing CGR\n"); ++ goto tx_cgr_init_failed; ++ } ++ err = dpaa_eth_priv_ingress_cgr_init(priv); ++ if (err < 0) { ++ dev_err(dev, "Error initializing ingress CGR\n"); ++ goto rx_cgr_init_failed; ++ } ++ ++ /* Add the FQs to the interface, and make them active */ ++ err = dpa_fqs_init(dev, &priv->dpa_fq_list, false); ++ if (err < 0) ++ goto fq_alloc_failed; ++ ++ priv->buf_layout = buf_layout; ++ priv->tx_headroom = dpa_get_headroom(&priv->buf_layout[TX]); ++ priv->rx_headroom = dpa_get_headroom(&priv->buf_layout[RX]); ++ ++ /* All real interfaces need their ports initialized */ ++ dpaa_eth_init_ports(mac_dev, dpa_bp, count, &port_fqs, ++ buf_layout, dev); ++ ++#ifdef CONFIG_FMAN_PFC ++ for (i = 0; i < CONFIG_FMAN_PFC_COS_COUNT; i++) { ++ err = fm_port_set_pfc_priorities_mapping_to_qman_wq(mac_dev->port_dev[TX], i, i); ++ if (unlikely(err != 0)) { ++ dev_err(dev, "Error maping PFC %u to WQ %u\n", i, i); ++ goto pfc_mapping_failed; ++ } ++ } ++#endif ++ ++ priv->percpu_priv = devm_alloc_percpu(dev, *priv->percpu_priv); ++ ++ if (!priv->percpu_priv) { ++ dev_err(dev, "devm_alloc_percpu() failed\n"); ++ err = -ENOMEM; ++ goto alloc_percpu_failed; ++ } ++ ++ for_each_possible_cpu(i) { ++ percpu_priv = per_cpu_ptr(priv->percpu_priv, i); ++ memset(percpu_priv, 0, sizeof(*percpu_priv)); ++ } ++ ++ priv->percpu_count = devm_alloc_percpu(dev, *priv->percpu_count); ++ if (!priv->percpu_count) { ++ dev_err(dev, "devm_alloc_percpu() failed\n"); ++ err = -ENOMEM; ++ goto alloc_percpu_failed; ++ } ++ ++ for_each_possible_cpu(i) { ++ int *percpu_count = per_cpu_ptr(priv->percpu_count, i); ++ *percpu_count = 0; ++ } ++ ++ err = dpa_private_netdev_init(net_dev); ++ ++ dpa_priv_bp_seed(net_dev); ++ ++ if (err < 0) ++ goto netdev_init_failed; ++ ++ pr_info("Ethercat port:%02hx:%02hx:%02hx:%02hx:%02hx:%02hx bpid:%d cpu:%d\n", ++ mac_dev->addr[0], mac_dev->addr[1], mac_dev->addr[2], ++ mac_dev->addr[3], mac_dev->addr[4], mac_dev->addr[5], ++ dpa_priv_bpid, dpa_priv_cpuid); ++ ++#ifdef CONFIG_PM ++ device_set_wakeup_capable(dev, true); ++#endif ++ ++ pr_info("fsl_dpa: Probed interface %s\n", net_dev->name); ++ ++ return 0; ++ ++netdev_init_failed: ++ ++alloc_percpu_failed: ++#ifdef CONFIG_FMAN_PFC ++pfc_mapping_failed: ++#endif ++ dpa_fq_free(dev, &priv->dpa_fq_list); ++fq_alloc_failed: ++ qman_delete_cgr_safe(&priv->ingress_cgr); ++ qman_release_cgrid(priv->ingress_cgr.cgrid); ++rx_cgr_init_failed: ++ qman_delete_cgr_safe(&priv->cgr_data.cgr); ++ qman_release_cgrid(priv->cgr_data.cgr.cgrid); ++tx_cgr_init_failed: ++ ++ dpa_bp_free(priv); ++bp_create_failed: ++fq_probe_failed: ++alloc_failed: ++mac_probe_failed: ++ dev_set_drvdata(dev, NULL); ++ free_netdev(net_dev); ++alloc_etherdev_mq_failed: ++ if (atomic_read(&dpa_bp->refs) == 0) ++ devm_kfree(dev, dpa_bp); ++ ++ return err; ++} ++ ++static const struct of_device_id dpa_match[] = { ++ { ++ .compatible = "fsl,dpa-ethercat" ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, dpa_match); ++ ++static struct platform_driver dpa_driver = { ++ .driver = { ++ .name = KBUILD_MODNAME "-ethercat", ++ .of_match_table = dpa_match, ++ .owner = THIS_MODULE, ++ .pm = DPAA_PM_OPS, ++ }, ++ .probe = dpaa_ethercat_probe, ++ .remove = dpa_remove ++}; ++ ++static int __init __cold dpa_ethercat_load(void) ++{ ++ int _errno; ++ ++ pr_info(DPA_DESCRIPTION "\n"); ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ dpa_debugfs_module_init(); ++#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ ++ ++ /* initialise dpaa_eth mirror values */ ++ dpa_rx_extra_headroom = fm_get_rx_extra_headroom(); ++ dpa_max_frm = fm_get_max_frm(); ++ dpa_num_cpus = num_possible_cpus(); ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ memset(dpa_loop_netdevs, 0, sizeof(dpa_loop_netdevs)); ++#endif ++ ++ _errno = platform_driver_register(&dpa_driver); ++ if (unlikely(_errno < 0)) { ++ pr_err(KBUILD_MODNAME ++ ": %s:%hu:%s(): platform_driver_register() = %d\n", ++ KBUILD_BASENAME ".c", __LINE__, __func__, _errno); ++ } ++ ++ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", ++ KBUILD_BASENAME ".c", __func__); ++ ++ return _errno; ++} ++module_init(dpa_ethercat_load); ++ ++static void __exit __cold dpa_ethercat_unload(void) ++{ ++ pr_debug(KBUILD_MODNAME ": -> %s:%s()\n", ++ KBUILD_BASENAME ".c", __func__); ++ ++ platform_driver_unregister(&dpa_driver); ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ dpa_debugfs_module_exit(); ++#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ ++ ++ /* Only one channel is used and needs to be relased after all ++ * interfaces are removed ++ */ ++ dpa_release_channel(); ++ ++ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", ++ KBUILD_BASENAME ".c", __func__); ++} ++module_exit(dpa_ethercat_unload); +diff --git a/drivers/net/ethernet/fungible/funeth/funeth_txrx.h b/drivers/net/ethernet/fungible/funeth/funeth_txrx.h +index 671f51135c26..53b7e95213a8 100644 +--- a/drivers/net/ethernet/fungible/funeth/funeth_txrx.h ++++ b/drivers/net/ethernet/fungible/funeth/funeth_txrx.h +@@ -206,9 +206,9 @@ struct funeth_rxq { + + #define FUN_QSTAT_READ(q, seq, stats_copy) \ + do { \ +- seq = u64_stats_fetch_begin_irq(&(q)->syncp); \ ++ seq = u64_stats_fetch_begin(&(q)->syncp); \ + stats_copy = (q)->stats; \ +- } while (u64_stats_fetch_retry_irq(&(q)->syncp, (seq))) ++ } while (u64_stats_fetch_retry(&(q)->syncp, (seq))) + + #define FUN_INT_NAME_LEN (IFNAMSIZ + 16) + +diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c +index 033f17cb96be..0a5953089a24 100644 +--- a/drivers/net/ethernet/google/gve/gve_ethtool.c ++++ b/drivers/net/ethernet/google/gve/gve_ethtool.c +@@ -177,14 +177,14 @@ gve_get_ethtool_stats(struct net_device *netdev, + struct gve_rx_ring *rx = &priv->rx[ring]; + + start = +- u64_stats_fetch_begin_irq(&priv->rx[ring].statss); ++ u64_stats_fetch_begin(&priv->rx[ring].statss); + tmp_rx_pkts = rx->rpackets; + tmp_rx_bytes = rx->rbytes; + tmp_rx_skb_alloc_fail = rx->rx_skb_alloc_fail; + tmp_rx_buf_alloc_fail = rx->rx_buf_alloc_fail; + tmp_rx_desc_err_dropped_pkt = + rx->rx_desc_err_dropped_pkt; +- } while (u64_stats_fetch_retry_irq(&priv->rx[ring].statss, ++ } while (u64_stats_fetch_retry(&priv->rx[ring].statss, + start)); + rx_pkts += tmp_rx_pkts; + rx_bytes += tmp_rx_bytes; +@@ -198,10 +198,10 @@ gve_get_ethtool_stats(struct net_device *netdev, + if (priv->tx) { + do { + start = +- u64_stats_fetch_begin_irq(&priv->tx[ring].statss); ++ u64_stats_fetch_begin(&priv->tx[ring].statss); + tmp_tx_pkts = priv->tx[ring].pkt_done; + tmp_tx_bytes = priv->tx[ring].bytes_done; +- } while (u64_stats_fetch_retry_irq(&priv->tx[ring].statss, ++ } while (u64_stats_fetch_retry(&priv->tx[ring].statss, + start)); + tx_pkts += tmp_tx_pkts; + tx_bytes += tmp_tx_bytes; +@@ -259,13 +259,13 @@ gve_get_ethtool_stats(struct net_device *netdev, + data[i++] = rx->fill_cnt - rx->cnt; + do { + start = +- u64_stats_fetch_begin_irq(&priv->rx[ring].statss); ++ u64_stats_fetch_begin(&priv->rx[ring].statss); + tmp_rx_bytes = rx->rbytes; + tmp_rx_skb_alloc_fail = rx->rx_skb_alloc_fail; + tmp_rx_buf_alloc_fail = rx->rx_buf_alloc_fail; + tmp_rx_desc_err_dropped_pkt = + rx->rx_desc_err_dropped_pkt; +- } while (u64_stats_fetch_retry_irq(&priv->rx[ring].statss, ++ } while (u64_stats_fetch_retry(&priv->rx[ring].statss, + start)); + data[i++] = tmp_rx_bytes; + data[i++] = rx->rx_cont_packet_cnt; +@@ -331,9 +331,9 @@ gve_get_ethtool_stats(struct net_device *netdev, + } + do { + start = +- u64_stats_fetch_begin_irq(&priv->tx[ring].statss); ++ u64_stats_fetch_begin(&priv->tx[ring].statss); + tmp_tx_bytes = tx->bytes_done; +- } while (u64_stats_fetch_retry_irq(&priv->tx[ring].statss, ++ } while (u64_stats_fetch_retry(&priv->tx[ring].statss, + start)); + data[i++] = tmp_tx_bytes; + data[i++] = tx->wake_queue; +diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c +index 2e5e0a887270..07b2ab8d054c 100644 +--- a/drivers/net/ethernet/google/gve/gve_main.c ++++ b/drivers/net/ethernet/google/gve/gve_main.c +@@ -51,10 +51,10 @@ static void gve_get_stats(struct net_device *dev, struct rtnl_link_stats64 *s) + for (ring = 0; ring < priv->rx_cfg.num_queues; ring++) { + do { + start = +- u64_stats_fetch_begin_irq(&priv->rx[ring].statss); ++ u64_stats_fetch_begin(&priv->rx[ring].statss); + packets = priv->rx[ring].rpackets; + bytes = priv->rx[ring].rbytes; +- } while (u64_stats_fetch_retry_irq(&priv->rx[ring].statss, ++ } while (u64_stats_fetch_retry(&priv->rx[ring].statss, + start)); + s->rx_packets += packets; + s->rx_bytes += bytes; +@@ -64,10 +64,10 @@ static void gve_get_stats(struct net_device *dev, struct rtnl_link_stats64 *s) + for (ring = 0; ring < priv->tx_cfg.num_queues; ring++) { + do { + start = +- u64_stats_fetch_begin_irq(&priv->tx[ring].statss); ++ u64_stats_fetch_begin(&priv->tx[ring].statss); + packets = priv->tx[ring].pkt_done; + bytes = priv->tx[ring].bytes_done; +- } while (u64_stats_fetch_retry_irq(&priv->tx[ring].statss, ++ } while (u64_stats_fetch_retry(&priv->tx[ring].statss, + start)); + s->tx_packets += packets; + s->tx_bytes += bytes; +@@ -1260,9 +1260,9 @@ void gve_handle_report_stats(struct gve_priv *priv) + } + + do { +- start = u64_stats_fetch_begin_irq(&priv->tx[idx].statss); ++ start = u64_stats_fetch_begin(&priv->tx[idx].statss); + tx_bytes = priv->tx[idx].bytes_done; +- } while (u64_stats_fetch_retry_irq(&priv->tx[idx].statss, start)); ++ } while (u64_stats_fetch_retry(&priv->tx[idx].statss, start)); + stats[stats_idx++] = (struct stats) { + .stat_name = cpu_to_be32(TX_WAKE_CNT), + .value = cpu_to_be64(priv->tx[idx].wake_queue), +diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h +index c693bb701ba3..05c682f712ac 100644 +--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h ++++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + #define HNAE3_MOD_VERSION "1.0" + +diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +index 8aae179554a8..a0aa13885475 100644 +--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c ++++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2496,7 +2497,7 @@ static void hns3_fetch_stats(struct rtnl_link_stats64 *stats, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + if (is_tx) { + stats->tx_bytes += ring->stats.tx_bytes; + stats->tx_packets += ring->stats.tx_pkts; +@@ -2530,7 +2531,7 @@ static void hns3_fetch_stats(struct rtnl_link_stats64 *stats, + stats->multicast += ring->stats.rx_multicast; + stats->rx_length_errors += ring->stats.err_pkt_len; + } +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + } + + static void hns3_nic_get_stats64(struct net_device *netdev, +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c +index d649c6e323c8..ceec8be2a73b 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_rx.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c +@@ -74,14 +74,14 @@ void hinic_rxq_get_stats(struct hinic_rxq *rxq, struct hinic_rxq_stats *stats) + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&rxq_stats->syncp); ++ start = u64_stats_fetch_begin(&rxq_stats->syncp); + stats->pkts = rxq_stats->pkts; + stats->bytes = rxq_stats->bytes; + stats->errors = rxq_stats->csum_errors + + rxq_stats->other_errors; + stats->csum_errors = rxq_stats->csum_errors; + stats->other_errors = rxq_stats->other_errors; +- } while (u64_stats_fetch_retry_irq(&rxq_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); + } + + /** +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c +index e91476c8ff8b..ad47ac51a139 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_tx.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c +@@ -99,14 +99,14 @@ void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats) + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&txq_stats->syncp); ++ start = u64_stats_fetch_begin(&txq_stats->syncp); + stats->pkts = txq_stats->pkts; + stats->bytes = txq_stats->bytes; + stats->tx_busy = txq_stats->tx_busy; + stats->tx_wake = txq_stats->tx_wake; + stats->tx_dropped = txq_stats->tx_dropped; + stats->big_frags_pkts = txq_stats->big_frags_pkts; +- } while (u64_stats_fetch_retry_irq(&txq_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); + } + + /** +diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +index 2cca9e84e31e..34ab5ff9823b 100644 +--- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c ++++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +@@ -1229,10 +1229,10 @@ static void fm10k_get_stats64(struct net_device *netdev, + continue; + + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + packets = ring->stats.packets; + bytes = ring->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + + stats->rx_packets += packets; + stats->rx_bytes += bytes; +@@ -1245,10 +1245,10 @@ static void fm10k_get_stats64(struct net_device *netdev, + continue; + + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + packets = ring->stats.packets; + bytes = ring->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + + stats->tx_packets += packets; + stats->tx_bytes += bytes; +diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h +index a81f918091cc..90d29a297e19 100644 +--- a/drivers/net/ethernet/intel/i40e/i40e.h ++++ b/drivers/net/ethernet/intel/i40e/i40e.h +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +index e632041aed5f..995b2a7c8973 100644 +--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c ++++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +@@ -154,7 +154,7 @@ __i40e_add_ethtool_stats(u64 **data, void *pointer, + * @ring: the ring to copy + * + * Queue statistics must be copied while protected by +- * u64_stats_fetch_begin_irq, so we can't directly use i40e_add_ethtool_stats. ++ * u64_stats_fetch_begin, so we can't directly use i40e_add_ethtool_stats. + * Assumes that queue stats are defined in i40e_gstrings_queue_stats. If the + * ring pointer is null, zero out the queue stat values and update the data + * pointer. Otherwise safely copy the stats from the ring into the supplied +@@ -172,16 +172,16 @@ i40e_add_queue_stats(u64 **data, struct i40e_ring *ring) + + /* To avoid invalid statistics values, ensure that we keep retrying + * the copy until we get a consistent value according to +- * u64_stats_fetch_retry_irq. But first, make sure our ring is ++ * u64_stats_fetch_retry. But first, make sure our ring is + * non-null before attempting to access its syncp. + */ + do { +- start = !ring ? 0 : u64_stats_fetch_begin_irq(&ring->syncp); ++ start = !ring ? 0 : u64_stats_fetch_begin(&ring->syncp); + for (i = 0; i < size; i++) { + i40e_add_one_ethtool_stat(&(*data)[i], ring, + &stats[i]); + } +- } while (ring && u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (ring && u64_stats_fetch_retry(&ring->syncp, start)); + + /* Once we successfully copy the stats in, update the data pointer */ + *data += size; +diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c +index 08ccf0024ce1..6b3f3727c3f1 100644 +--- a/drivers/net/ethernet/intel/i40e/i40e_main.c ++++ b/drivers/net/ethernet/intel/i40e/i40e_main.c +@@ -419,10 +419,10 @@ static void i40e_get_netdev_stats_struct_tx(struct i40e_ring *ring, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + packets = ring->stats.packets; + bytes = ring->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + + stats->tx_packets += packets; + stats->tx_bytes += bytes; +@@ -472,10 +472,10 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev, + if (!ring) + continue; + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + packets = ring->stats.packets; + bytes = ring->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + + stats->rx_packets += packets; + stats->rx_bytes += bytes; +@@ -897,10 +897,10 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) + continue; + + do { +- start = u64_stats_fetch_begin_irq(&p->syncp); ++ start = u64_stats_fetch_begin(&p->syncp); + packets = p->stats.packets; + bytes = p->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&p->syncp, start)); ++ } while (u64_stats_fetch_retry(&p->syncp, start)); + tx_b += bytes; + tx_p += packets; + tx_restart += p->tx_stats.restart_queue; +@@ -915,10 +915,10 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) + continue; + + do { +- start = u64_stats_fetch_begin_irq(&p->syncp); ++ start = u64_stats_fetch_begin(&p->syncp); + packets = p->stats.packets; + bytes = p->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&p->syncp, start)); ++ } while (u64_stats_fetch_retry(&p->syncp, start)); + rx_b += bytes; + rx_p += packets; + rx_buf += p->rx_stats.alloc_buff_failed; +@@ -935,10 +935,10 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) + continue; + + do { +- start = u64_stats_fetch_begin_irq(&p->syncp); ++ start = u64_stats_fetch_begin(&p->syncp); + packets = p->stats.packets; + bytes = p->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&p->syncp, start)); ++ } while (u64_stats_fetch_retry(&p->syncp, start)); + tx_b += bytes; + tx_p += packets; + tx_restart += p->tx_stats.restart_queue; +diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h +index 543931c06bb1..4e36fbc9d9e8 100644 +--- a/drivers/net/ethernet/intel/iavf/iavf.h ++++ b/drivers/net/ethernet/intel/iavf/iavf.h +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c +index fe912b1c468e..a34303ad057d 100644 +--- a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c ++++ b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c +@@ -147,7 +147,7 @@ __iavf_add_ethtool_stats(u64 **data, void *pointer, + * @ring: the ring to copy + * + * Queue statistics must be copied while protected by +- * u64_stats_fetch_begin_irq, so we can't directly use iavf_add_ethtool_stats. ++ * u64_stats_fetch_begin, so we can't directly use iavf_add_ethtool_stats. + * Assumes that queue stats are defined in iavf_gstrings_queue_stats. If the + * ring pointer is null, zero out the queue stat values and update the data + * pointer. Otherwise safely copy the stats from the ring into the supplied +@@ -165,14 +165,14 @@ iavf_add_queue_stats(u64 **data, struct iavf_ring *ring) + + /* To avoid invalid statistics values, ensure that we keep retrying + * the copy until we get a consistent value according to +- * u64_stats_fetch_retry_irq. But first, make sure our ring is ++ * u64_stats_fetch_retry. But first, make sure our ring is + * non-null before attempting to access its syncp. + */ + do { +- start = !ring ? 0 : u64_stats_fetch_begin_irq(&ring->syncp); ++ start = !ring ? 0 : u64_stats_fetch_begin(&ring->syncp); + for (i = 0; i < size; i++) + iavf_add_one_ethtool_stat(&(*data)[i], ring, &stats[i]); +- } while (ring && u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (ring && u64_stats_fetch_retry(&ring->syncp, start)); + + /* Once we successfully copy the stats in, update the data pointer */ + *data += size; +diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h +index f2be383d97df..f7ea8b0439fd 100644 +--- a/drivers/net/ethernet/intel/ice/ice.h ++++ b/drivers/net/ethernet/intel/ice/ice.h +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c +index 3f98781e74b2..a7d9cc1292ec 100644 +--- a/drivers/net/ethernet/intel/ice/ice_main.c ++++ b/drivers/net/ethernet/intel/ice/ice_main.c +@@ -6407,10 +6407,10 @@ ice_fetch_u64_stats_per_ring(struct u64_stats_sync *syncp, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(syncp); ++ start = u64_stats_fetch_begin(syncp); + *pkts = stats.pkts; + *bytes = stats.bytes; +- } while (u64_stats_fetch_retry_irq(syncp, start)); ++ } while (u64_stats_fetch_retry(syncp, start)); + } + + /** +diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c +index 96fa1c420f91..319ed601eaa1 100644 +--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c ++++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c +@@ -2316,15 +2316,15 @@ static void igb_get_ethtool_stats(struct net_device *netdev, + + ring = adapter->tx_ring[j]; + do { +- start = u64_stats_fetch_begin_irq(&ring->tx_syncp); ++ start = u64_stats_fetch_begin(&ring->tx_syncp); + data[i] = ring->tx_stats.packets; + data[i+1] = ring->tx_stats.bytes; + data[i+2] = ring->tx_stats.restart_queue; +- } while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->tx_syncp, start)); + do { +- start = u64_stats_fetch_begin_irq(&ring->tx_syncp2); ++ start = u64_stats_fetch_begin(&ring->tx_syncp2); + restart2 = ring->tx_stats.restart_queue2; +- } while (u64_stats_fetch_retry_irq(&ring->tx_syncp2, start)); ++ } while (u64_stats_fetch_retry(&ring->tx_syncp2, start)); + data[i+2] += restart2; + + i += IGB_TX_QUEUE_STATS_LEN; +@@ -2332,13 +2332,13 @@ static void igb_get_ethtool_stats(struct net_device *netdev, + for (j = 0; j < adapter->num_rx_queues; j++) { + ring = adapter->rx_ring[j]; + do { +- start = u64_stats_fetch_begin_irq(&ring->rx_syncp); ++ start = u64_stats_fetch_begin(&ring->rx_syncp); + data[i] = ring->rx_stats.packets; + data[i+1] = ring->rx_stats.bytes; + data[i+2] = ring->rx_stats.drops; + data[i+3] = ring->rx_stats.csum_err; + data[i+4] = ring->rx_stats.alloc_failed; +- } while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->rx_syncp, start)); + i += IGB_RX_QUEUE_STATS_LEN; + } + spin_unlock(&adapter->stats64_lock); +diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c +index 45ce4ed16146..9824f7cfaca4 100644 +--- a/drivers/net/ethernet/intel/igb/igb_main.c ++++ b/drivers/net/ethernet/intel/igb/igb_main.c +@@ -6660,10 +6660,10 @@ void igb_update_stats(struct igb_adapter *adapter) + } + + do { +- start = u64_stats_fetch_begin_irq(&ring->rx_syncp); ++ start = u64_stats_fetch_begin(&ring->rx_syncp); + _bytes = ring->rx_stats.bytes; + _packets = ring->rx_stats.packets; +- } while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->rx_syncp, start)); + bytes += _bytes; + packets += _packets; + } +@@ -6676,10 +6676,10 @@ void igb_update_stats(struct igb_adapter *adapter) + for (i = 0; i < adapter->num_tx_queues; i++) { + struct igb_ring *ring = adapter->tx_ring[i]; + do { +- start = u64_stats_fetch_begin_irq(&ring->tx_syncp); ++ start = u64_stats_fetch_begin(&ring->tx_syncp); + _bytes = ring->tx_stats.bytes; + _packets = ring->tx_stats.packets; +- } while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->tx_syncp, start)); + bytes += _bytes; + packets += _packets; + } +diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c +index 511fc3f41208..2899109e6fb5 100644 +--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c ++++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c +@@ -839,15 +839,15 @@ static void igc_ethtool_get_stats(struct net_device *netdev, + + ring = adapter->tx_ring[j]; + do { +- start = u64_stats_fetch_begin_irq(&ring->tx_syncp); ++ start = u64_stats_fetch_begin(&ring->tx_syncp); + data[i] = ring->tx_stats.packets; + data[i + 1] = ring->tx_stats.bytes; + data[i + 2] = ring->tx_stats.restart_queue; +- } while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->tx_syncp, start)); + do { +- start = u64_stats_fetch_begin_irq(&ring->tx_syncp2); ++ start = u64_stats_fetch_begin(&ring->tx_syncp2); + restart2 = ring->tx_stats.restart_queue2; +- } while (u64_stats_fetch_retry_irq(&ring->tx_syncp2, start)); ++ } while (u64_stats_fetch_retry(&ring->tx_syncp2, start)); + data[i + 2] += restart2; + + i += IGC_TX_QUEUE_STATS_LEN; +@@ -855,13 +855,13 @@ static void igc_ethtool_get_stats(struct net_device *netdev, + for (j = 0; j < adapter->num_rx_queues; j++) { + ring = adapter->rx_ring[j]; + do { +- start = u64_stats_fetch_begin_irq(&ring->rx_syncp); ++ start = u64_stats_fetch_begin(&ring->rx_syncp); + data[i] = ring->rx_stats.packets; + data[i + 1] = ring->rx_stats.bytes; + data[i + 2] = ring->rx_stats.drops; + data[i + 3] = ring->rx_stats.csum_err; + data[i + 4] = ring->rx_stats.alloc_failed; +- } while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->rx_syncp, start)); + i += IGC_RX_QUEUE_STATS_LEN; + } + spin_unlock(&adapter->stats64_lock); +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index 2f3947cf513b..125669a6f6bb 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -4865,10 +4865,10 @@ void igc_update_stats(struct igc_adapter *adapter) + } + + do { +- start = u64_stats_fetch_begin_irq(&ring->rx_syncp); ++ start = u64_stats_fetch_begin(&ring->rx_syncp); + _bytes = ring->rx_stats.bytes; + _packets = ring->rx_stats.packets; +- } while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->rx_syncp, start)); + bytes += _bytes; + packets += _packets; + } +@@ -4882,10 +4882,10 @@ void igc_update_stats(struct igc_adapter *adapter) + struct igc_ring *ring = adapter->tx_ring[i]; + + do { +- start = u64_stats_fetch_begin_irq(&ring->tx_syncp); ++ start = u64_stats_fetch_begin(&ring->tx_syncp); + _bytes = ring->tx_stats.bytes; + _packets = ring->tx_stats.packets; +- } while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->tx_syncp, start)); + bytes += _bytes; + packets += _packets; + } +diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +index 0051aa676e19..1c22ff2dba9b 100644 +--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c ++++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +@@ -1335,10 +1335,10 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, + } + + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + data[i] = ring->stats.packets; + data[i+1] = ring->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + i += 2; + } + for (j = 0; j < IXGBE_NUM_RX_QUEUES; j++) { +@@ -1351,10 +1351,10 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, + } + + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + data[i] = ring->stats.packets; + data[i+1] = ring->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + i += 2; + } + +diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +index 6105419ae2d5..ec8864beb1ac 100644 +--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c ++++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +@@ -9051,10 +9051,10 @@ static void ixgbe_get_ring_stats64(struct rtnl_link_stats64 *stats, + + if (ring) { + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + packets = ring->stats.packets; + bytes = ring->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + stats->tx_packets += packets; + stats->tx_bytes += bytes; + } +@@ -9074,10 +9074,10 @@ static void ixgbe_get_stats64(struct net_device *netdev, + + if (ring) { + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + packets = ring->stats.packets; + bytes = ring->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + stats->rx_packets += packets; + stats->rx_bytes += bytes; + } +diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c +index ccfa6b91aac6..296915414a7c 100644 +--- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c ++++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c +@@ -458,10 +458,10 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev, + } + + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + data[i] = ring->stats.packets; + data[i + 1] = ring->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + i += 2; + } + +@@ -475,10 +475,10 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev, + } + + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + data[i] = ring->stats.packets; + data[i + 1] = ring->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + i += 2; + } + +@@ -492,10 +492,10 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev, + } + + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + data[i] = ring->stats.packets; + data[i + 1] = ring->stats.bytes; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + i += 2; + } + } +diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +index e338fa572793..a9479ddf68eb 100644 +--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c ++++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +@@ -4350,10 +4350,10 @@ static void ixgbevf_get_tx_ring_stats(struct rtnl_link_stats64 *stats, + + if (ring) { + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + bytes = ring->stats.bytes; + packets = ring->stats.packets; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + stats->tx_bytes += bytes; + stats->tx_packets += packets; + } +@@ -4376,10 +4376,10 @@ static void ixgbevf_get_stats(struct net_device *netdev, + for (i = 0; i < adapter->num_rx_queues; i++) { + ring = adapter->rx_ring[i]; + do { +- start = u64_stats_fetch_begin_irq(&ring->syncp); ++ start = u64_stats_fetch_begin(&ring->syncp); + bytes = ring->stats.bytes; + packets = ring->stats.packets; +- } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); ++ } while (u64_stats_fetch_retry(&ring->syncp, start)); + stats->rx_bytes += bytes; + stats->rx_packets += packets; + } +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index aca5b72cfeec..b9f742518313 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -38,7 +38,7 @@ + #include + #include + #include +-#include ++#include + #include + + /* Registers */ +@@ -813,14 +813,14 @@ mvneta_get_stats64(struct net_device *dev, + + cpu_stats = per_cpu_ptr(pp->stats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); ++ start = u64_stats_fetch_begin(&cpu_stats->syncp); + rx_packets = cpu_stats->es.ps.rx_packets; + rx_bytes = cpu_stats->es.ps.rx_bytes; + rx_dropped = cpu_stats->rx_dropped; + rx_errors = cpu_stats->rx_errors; + tx_packets = cpu_stats->es.ps.tx_packets; + tx_bytes = cpu_stats->es.ps.tx_bytes; +- } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); + + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; +@@ -4762,7 +4762,7 @@ mvneta_ethtool_update_pcpu_stats(struct mvneta_port *pp, + + stats = per_cpu_ptr(pp->stats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&stats->syncp); ++ start = u64_stats_fetch_begin(&stats->syncp); + skb_alloc_error = stats->es.skb_alloc_error; + refill_error = stats->es.refill_error; + xdp_redirect = stats->es.ps.xdp_redirect; +@@ -4772,7 +4772,7 @@ mvneta_ethtool_update_pcpu_stats(struct mvneta_port *pp, + xdp_xmit_err = stats->es.ps.xdp_xmit_err; + xdp_tx = stats->es.ps.xdp_tx; + xdp_tx_err = stats->es.ps.xdp_tx_err; +- } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&stats->syncp, start)); + + es->skb_alloc_error += skb_alloc_error; + es->refill_error += refill_error; +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +index f936640cca4e..8c7470ab4985 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -2008,7 +2008,7 @@ mvpp2_get_xdp_stats(struct mvpp2_port *port, struct mvpp2_pcpu_stats *xdp_stats) + + cpu_stats = per_cpu_ptr(port->stats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); ++ start = u64_stats_fetch_begin(&cpu_stats->syncp); + xdp_redirect = cpu_stats->xdp_redirect; + xdp_pass = cpu_stats->xdp_pass; + xdp_drop = cpu_stats->xdp_drop; +@@ -2016,7 +2016,7 @@ mvpp2_get_xdp_stats(struct mvpp2_port *port, struct mvpp2_pcpu_stats *xdp_stats) + xdp_xmit_err = cpu_stats->xdp_xmit_err; + xdp_tx = cpu_stats->xdp_tx; + xdp_tx_err = cpu_stats->xdp_tx_err; +- } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); + + xdp_stats->xdp_redirect += xdp_redirect; + xdp_stats->xdp_pass += xdp_pass; +@@ -5115,12 +5115,12 @@ mvpp2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) + + cpu_stats = per_cpu_ptr(port->stats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); ++ start = u64_stats_fetch_begin(&cpu_stats->syncp); + rx_packets = cpu_stats->rx_packets; + rx_bytes = cpu_stats->rx_bytes; + tx_packets = cpu_stats->tx_packets; + tx_bytes = cpu_stats->tx_bytes; +- } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); + + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; +diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c +index ab33ba1c3023..ff97b140886a 100644 +--- a/drivers/net/ethernet/marvell/sky2.c ++++ b/drivers/net/ethernet/marvell/sky2.c +@@ -3894,19 +3894,19 @@ static void sky2_get_stats(struct net_device *dev, + u64 _bytes, _packets; + + do { +- start = u64_stats_fetch_begin_irq(&sky2->rx_stats.syncp); ++ start = u64_stats_fetch_begin(&sky2->rx_stats.syncp); + _bytes = sky2->rx_stats.bytes; + _packets = sky2->rx_stats.packets; +- } while (u64_stats_fetch_retry_irq(&sky2->rx_stats.syncp, start)); ++ } while (u64_stats_fetch_retry(&sky2->rx_stats.syncp, start)); + + stats->rx_packets = _packets; + stats->rx_bytes = _bytes; + + do { +- start = u64_stats_fetch_begin_irq(&sky2->tx_stats.syncp); ++ start = u64_stats_fetch_begin(&sky2->tx_stats.syncp); + _bytes = sky2->tx_stats.bytes; + _packets = sky2->tx_stats.packets; +- } while (u64_stats_fetch_retry_irq(&sky2->tx_stats.syncp, start)); ++ } while (u64_stats_fetch_retry(&sky2->tx_stats.syncp, start)); + + stats->tx_packets = _packets; + stats->tx_bytes = _bytes; +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index 0ac5ae16308f..c13cc420c64b 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -866,7 +866,7 @@ static void mtk_get_stats64(struct net_device *dev, + } + + do { +- start = u64_stats_fetch_begin_irq(&hw_stats->syncp); ++ start = u64_stats_fetch_begin(&hw_stats->syncp); + storage->rx_packets = hw_stats->rx_packets; + storage->tx_packets = hw_stats->tx_packets; + storage->rx_bytes = hw_stats->rx_bytes; +@@ -878,7 +878,7 @@ static void mtk_get_stats64(struct net_device *dev, + storage->rx_crc_errors = hw_stats->rx_fcs_errors; + storage->rx_errors = hw_stats->rx_checksum_errors; + storage->tx_aborted_errors = hw_stats->tx_skip; +- } while (u64_stats_fetch_retry_irq(&hw_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&hw_stats->syncp, start)); + + storage->tx_errors = dev->stats.tx_errors; + storage->rx_dropped = dev->stats.rx_dropped; +@@ -3694,13 +3694,13 @@ static void mtk_get_ethtool_stats(struct net_device *dev, + + do { + data_dst = data; +- start = u64_stats_fetch_begin_irq(&hwstats->syncp); ++ start = u64_stats_fetch_begin(&hwstats->syncp); + + for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++) + *data_dst++ = *(data_src + mtk_ethtool_stats[i].offset); + if (mtk_page_pool_enabled(mac->hw)) + mtk_ethtool_pp_stats(mac->hw, data_dst); +- } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start)); ++ } while (u64_stats_fetch_retry(&hwstats->syncp, start)); + } + + static int mtk_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index 4e7daa382bc0..699313b59330 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + #include + #include "eswitch.h" + #include "en.h" +diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +index 67ecdb9e708f..8345499563a4 100644 +--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c ++++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +@@ -827,12 +827,12 @@ mlxsw_sp_port_get_sw_stats64(const struct net_device *dev, + for_each_possible_cpu(i) { + p = per_cpu_ptr(mlxsw_sp_port->pcpu_stats, i); + do { +- start = u64_stats_fetch_begin_irq(&p->syncp); ++ start = u64_stats_fetch_begin(&p->syncp); + rx_packets = p->rx_packets; + rx_bytes = p->rx_bytes; + tx_packets = p->tx_packets; + tx_bytes = p->tx_bytes; +- } while (u64_stats_fetch_retry_irq(&p->syncp, start)); ++ } while (u64_stats_fetch_retry(&p->syncp, start)); + + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; +diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c +index 651d5493ae55..ee78655a1cd5 100644 +--- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c ++++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0+ + + #include ++#include + + #include "lan966x_main.h" + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c +index dc2c3756e3a2..7c174cb4a8b7 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c +@@ -5,6 +5,7 @@ + */ + + #include ++#include + + #include "sparx5_tc.h" + #include "sparx5_main.h" +diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c +index 4f4204432aaa..9cca75423933 100644 +--- a/drivers/net/ethernet/microsoft/mana/mana_en.c ++++ b/drivers/net/ethernet/microsoft/mana/mana_en.c +@@ -316,10 +316,10 @@ static void mana_get_stats64(struct net_device *ndev, + rx_stats = &apc->rxqs[q]->stats; + + do { +- start = u64_stats_fetch_begin_irq(&rx_stats->syncp); ++ start = u64_stats_fetch_begin(&rx_stats->syncp); + packets = rx_stats->packets; + bytes = rx_stats->bytes; +- } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); + + st->rx_packets += packets; + st->rx_bytes += bytes; +@@ -329,10 +329,10 @@ static void mana_get_stats64(struct net_device *ndev, + tx_stats = &apc->tx_qp[q].txq.stats; + + do { +- start = u64_stats_fetch_begin_irq(&tx_stats->syncp); ++ start = u64_stats_fetch_begin(&tx_stats->syncp); + packets = tx_stats->packets; + bytes = tx_stats->bytes; +- } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); + + st->tx_packets += packets; + st->tx_bytes += bytes; +diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c +index c530db76880f..96d55c91c969 100644 +--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c ++++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c +@@ -90,13 +90,13 @@ static void mana_get_ethtool_stats(struct net_device *ndev, + rx_stats = &apc->rxqs[q]->stats; + + do { +- start = u64_stats_fetch_begin_irq(&rx_stats->syncp); ++ start = u64_stats_fetch_begin(&rx_stats->syncp); + packets = rx_stats->packets; + bytes = rx_stats->bytes; + xdp_drop = rx_stats->xdp_drop; + xdp_tx = rx_stats->xdp_tx; + xdp_redirect = rx_stats->xdp_redirect; +- } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); + + data[i++] = packets; + data[i++] = bytes; +@@ -109,11 +109,11 @@ static void mana_get_ethtool_stats(struct net_device *ndev, + tx_stats = &apc->tx_qp[q].txq.stats; + + do { +- start = u64_stats_fetch_begin_irq(&tx_stats->syncp); ++ start = u64_stats_fetch_begin(&tx_stats->syncp); + packets = tx_stats->packets; + bytes = tx_stats->bytes; + xdp_xmit = tx_stats->xdp_xmit; +- } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); + + data[i++] = packets; + data[i++] = bytes; +diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c +index aa069aa433f0..6897441e0eaf 100644 +--- a/drivers/net/ethernet/mscc/ocelot.c ++++ b/drivers/net/ethernet/mscc/ocelot.c +@@ -212,6 +212,12 @@ static void ocelot_vcap_enable(struct ocelot *ocelot, int port) + ocelot_write_gix(ocelot, ANA_PORT_VCAP_CFG_S1_ENA, + ANA_PORT_VCAP_CFG, port); + ++ /* Use key S1_5TUPLE_IP4 in second lookup. */ ++ ocelot_write_ix(ocelot, ++ ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_IP6_CFG(2) | ++ ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_IP4_CFG(2), ++ ANA_PORT_VCAP_S1_KEY_CFG, port, 1); ++ + ocelot_rmw_gix(ocelot, REW_PORT_CFG_ES0_EN, + REW_PORT_CFG_ES0_EN, + REW_PORT_CFG, port); +@@ -345,6 +351,8 @@ static void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port) + struct ocelot_port *ocelot_port = ocelot->ports[port]; + enum ocelot_port_tag_config tag_cfg; + bool uses_native_vlan = false; ++ u32 port_tpid = 0; ++ u32 tag_tpid = 0; + + if (ocelot_port->vlan_aware) { + uses_native_vlan = ocelot_port_uses_native_vlan(ocelot, port); +@@ -355,12 +363,17 @@ static void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port) + tag_cfg = OCELOT_PORT_TAG_DISABLED; + else + tag_cfg = OCELOT_PORT_TAG_TRUNK; ++ ++ if (ocelot->qinq_enable && ocelot_port->qinq_mode) ++ tag_tpid = REW_TAG_CFG_TAG_TPID_CFG(1); ++ else ++ tag_tpid = REW_TAG_CFG_TAG_TPID_CFG(0); + } else { + tag_cfg = OCELOT_PORT_TAG_DISABLED; + } + +- ocelot_rmw_gix(ocelot, REW_TAG_CFG_TAG_CFG(tag_cfg), +- REW_TAG_CFG_TAG_CFG_M, ++ ocelot_rmw_gix(ocelot, REW_TAG_CFG_TAG_CFG(tag_cfg) | tag_tpid, ++ REW_TAG_CFG_TAG_CFG_M | REW_TAG_CFG_TAG_TPID_CFG_M, + REW_TAG_CFG, port); + + if (uses_native_vlan) { +@@ -372,9 +385,16 @@ static void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port) + */ + native_vlan = ocelot_port_find_native_vlan(ocelot, port); + ++ if (ocelot->qinq_enable && ocelot_port->qinq_mode) ++ port_tpid = REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021AD); ++ else ++ port_tpid = REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021Q); ++ + ocelot_rmw_gix(ocelot, +- REW_PORT_VLAN_CFG_PORT_VID(native_vlan->vid), +- REW_PORT_VLAN_CFG_PORT_VID_M, ++ REW_PORT_VLAN_CFG_PORT_VID(native_vlan->vid) | ++ port_tpid, ++ REW_PORT_VLAN_CFG_PORT_VID_M | ++ REW_PORT_VLAN_CFG_PORT_TPID_M, + REW_PORT_VLAN_CFG, port); + } + } +@@ -419,6 +439,10 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, + struct ocelot_port *ocelot_port = ocelot->ports[port]; + u16 pvid = ocelot_vlan_unaware_pvid(ocelot, ocelot_port->bridge); + u32 val = 0; ++ u32 tag_type = 0; ++ ++ if (ocelot->qinq_enable && ocelot_port->qinq_mode) ++ tag_type = ANA_PORT_VLAN_CFG_VLAN_TAG_TYPE; + + ocelot_port->pvid_vlan = pvid_vlan; + +@@ -426,8 +450,8 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, + pvid = pvid_vlan->vid; + + ocelot_rmw_gix(ocelot, +- ANA_PORT_VLAN_CFG_VLAN_VID(pvid), +- ANA_PORT_VLAN_CFG_VLAN_VID_M, ++ ANA_PORT_VLAN_CFG_VLAN_VID(pvid) | tag_type, ++ ANA_PORT_VLAN_CFG_VLAN_VID_M | tag_type, + ANA_PORT_VLAN_CFG, port); + + /* If there's no pvid, we should drop not only untagged traffic (which +@@ -585,6 +609,15 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, + ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1); + else + val = 0; ++ ++ /* if switch is enabled for QinQ, the port for LAN should set ++ * VLAN_CFG.VLAN_POP_CNT=0 && VLAN_CFG.VLAN_AWARE_ENA=0. ++ * the port for MAN should set VLAN_CFG.VLAN_POP_CNT=1 && ++ * VLAN_CFG.VLAN_AWARE_ENA=1. referring to 4.3.3 in VSC9959_1_00_TS.pdf ++ */ ++ if (ocelot->qinq_enable && !ocelot_port->qinq_mode) ++ val = 0; ++ + ocelot_rmw_gix(ocelot, val, + ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | + ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M, +@@ -1517,7 +1550,12 @@ u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port) + if (!ocelot_port) + continue; + +- if (ocelot_port->stp_state == BR_STATE_FORWARDING && ++ /* Keep the bridge port in forward mask if the port is forward ++ * state or force_forward mode. ++ * FRER need to set the port to force_forward mode. ++ */ ++ if ((ocelot_port->stp_state == BR_STATE_FORWARDING || ++ ocelot_port->force_forward) && + ocelot_port->bridge == bridge) + mask |= BIT(port); + } +diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c +index ee052404eb55..ee9656291fff 100644 +--- a/drivers/net/ethernet/mscc/ocelot_flower.c ++++ b/drivers/net/ethernet/mscc/ocelot_flower.c +@@ -468,20 +468,27 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, + switch (ntohs(a->vlan.proto)) { + case ETH_P_8021Q: + tpid = OCELOT_TAG_TPID_SEL_8021Q; ++ filter->action.tag_b_tpid_sel = tpid; ++ filter->action.push_inner_tag = OCELOT_ES0_TAG; ++ filter->action.tag_b_vid_sel = OCELOT_ES0_VID; ++ filter->action.tag_b_pcp_sel = OCELOT_ES0_PCP; ++ filter->action.vid_b_val = a->vlan.vid; ++ filter->action.pcp_b_val = a->vlan.prio; + break; + case ETH_P_8021AD: + tpid = OCELOT_TAG_TPID_SEL_8021AD; ++ filter->action.tag_a_tpid_sel = tpid; ++ filter->action.push_outer_tag = OCELOT_ES0_TAG; ++ filter->action.tag_a_vid_sel = OCELOT_ES0_VID; ++ filter->action.tag_a_pcp_sel = OCELOT_ES0_PCP; ++ filter->action.vid_a_val = a->vlan.vid; ++ filter->action.pcp_a_val = a->vlan.prio; + break; + default: + NL_SET_ERR_MSG_MOD(extack, + "Cannot push custom TPID"); + return -EOPNOTSUPP; + } +- filter->action.tag_a_tpid_sel = tpid; +- filter->action.push_outer_tag = OCELOT_ES0_TAG; +- filter->action.tag_a_vid_sel = OCELOT_ES0_VID; +- filter->action.vid_a_val = a->vlan.vid; +- filter->action.pcp_a_val = a->vlan.prio; + filter->type = OCELOT_VCAP_FILTER_OFFLOAD; + break; + case FLOW_ACTION_GATE: +@@ -586,6 +593,7 @@ ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress, + BIT(FLOW_DISSECTOR_KEY_META) | + BIT(FLOW_DISSECTOR_KEY_PORTS) | + BIT(FLOW_DISSECTOR_KEY_VLAN) | ++ BIT(FLOW_DISSECTOR_KEY_CVLAN) | + BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) { +@@ -617,6 +625,18 @@ ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress, + match_protocol = false; + } + ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CVLAN)) { ++ struct flow_match_vlan match; ++ ++ flow_rule_match_cvlan(rule, &match); ++ filter->key_type = OCELOT_VCAP_KEY_ANY; ++ filter->cvlan.vid.value = match.key->vlan_id; ++ filter->cvlan.vid.mask = match.mask->vlan_id; ++ filter->cvlan.pcp.value[0] = match.key->vlan_priority; ++ filter->cvlan.pcp.mask[0] = match.mask->vlan_priority; ++ match_protocol = false; ++ } ++ + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_match_eth_addrs match; + +diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.c b/drivers/net/ethernet/mscc/ocelot_ptp.c +index cb32234a5bf1..af69d90350e9 100644 +--- a/drivers/net/ethernet/mscc/ocelot_ptp.c ++++ b/drivers/net/ethernet/mscc/ocelot_ptp.c +@@ -532,6 +532,7 @@ int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr) + switch (cfg.tx_type) { + case HWTSTAMP_TX_ON: + ocelot_port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; ++ skb_queue_purge(&ocelot_port->tx_skbs); + break; + case HWTSTAMP_TX_ONESTEP_SYNC: + /* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we +diff --git a/drivers/net/ethernet/mscc/ocelot_vcap.c b/drivers/net/ethernet/mscc/ocelot_vcap.c +index 73cdec5ca6a3..9947b634194a 100644 +--- a/drivers/net/ethernet/mscc/ocelot_vcap.c ++++ b/drivers/net/ethernet/mscc/ocelot_vcap.c +@@ -666,10 +666,11 @@ static void is1_action_set(struct ocelot *ocelot, struct vcap_data *data, + vcap_action_set(vcap, data, VCAP_IS1_ACT_PAG_VAL, a->pag_val); + } + +-static void is1_entry_set(struct ocelot *ocelot, int ix, ++static int is1_entry_set(struct ocelot *ocelot, int ix, + struct ocelot_vcap_filter *filter) + { + const struct vcap_props *vcap = &ocelot->vcap[VCAP_IS1]; ++ struct ocelot_vcap_key_vlan *ctag = &filter->cvlan; + struct ocelot_vcap_key_vlan *tag = &filter->vlan; + struct vcap_data data; + int row = ix / 2; +@@ -689,6 +690,11 @@ static void is1_entry_set(struct ocelot *ocelot, int ix, + if (filter->prio != 0) + data.tg |= data.tg_value; + ++ if (filter->lookup == 1) ++ type = IS1_TYPE_S1_5TUPLE_IP4; ++ else ++ type = IS1_TYPE_S1_NORMAL; ++ + vcap_key_set(vcap, &data, VCAP_IS1_HK_LOOKUP, filter->lookup, 0x3); + vcap_key_set(vcap, &data, VCAP_IS1_HK_IGR_PORT_MASK, 0, + ~filter->ingress_port_mask); +@@ -699,12 +705,31 @@ static void is1_entry_set(struct ocelot *ocelot, int ix, + tag->vid.value, tag->vid.mask); + vcap_key_set(vcap, &data, VCAP_IS1_HK_PCP, + tag->pcp.value[0], tag->pcp.mask[0]); +- type = IS1_TYPE_S1_NORMAL; ++ ++ if (ctag->vid.value != 0) { ++ if (type != IS1_TYPE_S1_5TUPLE_IP4) ++ return -EOPNOTSUPP; ++ ++ vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TPID, ++ OCELOT_VCAP_BIT_1); ++ vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_VLAN_DBL_TAGGED, ++ OCELOT_VCAP_BIT_1); ++ vcap_key_set(vcap, &data, VCAP_IS1_HK_IP4_INNER_VID, ++ ctag->vid.value, ctag->vid.mask); ++ vcap_key_set(vcap, &data, VCAP_IS1_HK_IP4_INNER_PCP, ++ ctag->pcp.value[0], ctag->pcp.mask[0]); ++ } + + switch (filter->key_type) { + case OCELOT_VCAP_KEY_ETYPE: { + struct ocelot_vcap_key_etype *etype = &filter->key.etype; + ++ if (type != IS1_TYPE_S1_NORMAL) ++ return -EOPNOTSUPP; ++ ++ if (etype->dmac.mask[0]) ++ return -EOPNOTSUPP; ++ + vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_L2_SMAC, + etype->smac.value, etype->smac.mask); + vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_ETYPE, +@@ -716,48 +741,103 @@ static void is1_entry_set(struct ocelot *ocelot, int ix, + struct ocelot_vcap_udp_tcp *sport = &ipv4->sport; + struct ocelot_vcap_udp_tcp *dport = &ipv4->dport; + enum ocelot_vcap_bit tcp_udp = OCELOT_VCAP_BIT_0; ++ enum ocelot_vcap_bit fragment = ipv4->fragment; ++ enum ocelot_vcap_bit options = ipv4->options; ++ enum ocelot_vcap_bit tcp = OCELOT_VCAP_BIT_0; + struct ocelot_vcap_u8 proto = ipv4->proto; + struct ocelot_vcap_ipv4 sip = ipv4->sip; ++ struct ocelot_vcap_ipv4 dip = ipv4->dip; ++ struct ocelot_vcap_u32 port; + u32 val, msk; + +- vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP_SNAP, +- OCELOT_VCAP_BIT_1); +- vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP4, +- OCELOT_VCAP_BIT_1); +- vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_ETYPE_LEN, +- OCELOT_VCAP_BIT_1); +- vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_L3_IP4_SIP, +- sip.value.addr, sip.mask.addr); +- +- val = proto.value[0]; +- msk = proto.mask[0]; +- +- if ((val == NEXTHDR_TCP || val == NEXTHDR_UDP) && msk == 0xff) +- tcp_udp = OCELOT_VCAP_BIT_1; +- vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TCP_UDP, tcp_udp); +- +- if (tcp_udp) { +- enum ocelot_vcap_bit tcp = OCELOT_VCAP_BIT_0; +- +- if (val == NEXTHDR_TCP) +- tcp = OCELOT_VCAP_BIT_1; +- +- vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TCP, tcp); +- vcap_key_l4_port_set(vcap, &data, VCAP_IS1_HK_L4_SPORT, +- sport); +- /* Overloaded field */ +- vcap_key_l4_port_set(vcap, &data, VCAP_IS1_HK_ETYPE, +- dport); +- } else { +- /* IPv4 "other" frame */ +- struct ocelot_vcap_u16 etype = {0}; +- +- /* Overloaded field */ +- etype.value[0] = proto.value[0]; +- etype.mask[0] = proto.mask[0]; ++ if (type == IS1_TYPE_S1_5TUPLE_IP4) { ++ vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP4_IP4, ++ OCELOT_VCAP_BIT_1); ++ vcap_key_bit_set(vcap, &data, ++ VCAP_IS1_HK_IP4_L3_FRAGMENT, ++ fragment); ++ vcap_key_bit_set(vcap, &data, ++ VCAP_IS1_HK_IP4_L3_OPTIONS, ++ options); ++ vcap_key_bytes_set(vcap, &data, ++ VCAP_IS1_HK_IP4_L3_IP4_DIP, ++ dip.value.addr, dip.mask.addr); ++ vcap_key_bytes_set(vcap, &data, ++ VCAP_IS1_HK_IP4_L3_IP4_SIP, ++ sip.value.addr, sip.mask.addr); ++ ++ val = proto.value[0]; ++ msk = proto.mask[0]; ++ if ((val == NEXTHDR_TCP || val == NEXTHDR_UDP) && ++ msk == 0xff) { ++ tcp_udp = OCELOT_VCAP_BIT_1; ++ tcp = (val == NEXTHDR_TCP ? ++ OCELOT_VCAP_BIT_1 : OCELOT_VCAP_BIT_0); ++ ++ vcap_key_bit_set(vcap, &data, ++ VCAP_IS1_HK_IP4_TCP_UDP, ++ tcp_udp); ++ vcap_key_bit_set(vcap, &data, ++ VCAP_IS1_HK_IP4_TCP, tcp); ++ ++ port.value[0] = sport->value & 0xFF; ++ port.value[1] = sport->value >> 8; ++ port.value[2] = dport->value & 0xFF; ++ port.value[3] = dport->value >> 8; ++ port.mask[0] = sport->mask & 0xFF; ++ port.mask[1] = sport->mask >> 8; ++ port.mask[2] = dport->mask & 0xFF; ++ port.mask[3] = dport->mask >> 8; ++ vcap_key_bytes_set(vcap, &data, ++ VCAP_IS1_HK_IP4_IP_PAYLOAD_S1_5TUPLE, ++ port.value, port.mask); ++ } + +- vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_ETYPE, +- etype.value, etype.mask); ++ } else if (type == IS1_TYPE_S1_NORMAL) { ++ vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP_SNAP, ++ OCELOT_VCAP_BIT_1); ++ vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP4, ++ OCELOT_VCAP_BIT_1); ++ vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_ETYPE_LEN, ++ OCELOT_VCAP_BIT_1); ++ vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_L3_IP4_SIP, ++ sip.value.addr, sip.mask.addr); ++ ++ if (dip.mask.addr[0]) ++ return -EOPNOTSUPP; ++ ++ val = proto.value[0]; ++ msk = proto.mask[0]; ++ ++ if ((val == NEXTHDR_TCP || val == NEXTHDR_UDP) && ++ msk == 0xff) { ++ tcp_udp = OCELOT_VCAP_BIT_1; ++ tcp = (val == NEXTHDR_TCP ? ++ OCELOT_VCAP_BIT_1 : OCELOT_VCAP_BIT_0); ++ ++ vcap_key_bit_set(vcap, &data, ++ VCAP_IS1_HK_TCP_UDP, tcp_udp); ++ vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TCP, ++ tcp); ++ vcap_key_l4_port_set(vcap, &data, ++ VCAP_IS1_HK_L4_SPORT, ++ sport); ++ /* Overloaded field */ ++ vcap_key_l4_port_set(vcap, &data, ++ VCAP_IS1_HK_ETYPE, ++ dport); ++ } else if (msk == 0xff) { ++ /* IPv4 "other" frame */ ++ struct ocelot_vcap_u16 etype = {0}; ++ ++ /* Overloaded field */ ++ etype.value[0] = proto.value[0]; ++ etype.mask[0] = proto.mask[0]; ++ ++ vcap_key_bytes_set(vcap, &data, ++ VCAP_IS1_HK_ETYPE, ++ etype.value, etype.mask); ++ } + } + break; + } +@@ -775,6 +855,8 @@ static void is1_entry_set(struct ocelot *ocelot, int ix, + vcap_entry2cache(ocelot, vcap, &data); + vcap_action2cache(ocelot, vcap, &data); + vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_WRITE, VCAP_SEL_ALL); ++ ++ return 0; + } + + static void es0_action_set(struct ocelot *ocelot, struct vcap_data *data, +@@ -872,15 +954,17 @@ static void vcap_entry_get(struct ocelot *ocelot, int ix, + filter->stats.pkts = cnt; + } + +-static void vcap_entry_set(struct ocelot *ocelot, int ix, +- struct ocelot_vcap_filter *filter) ++static int vcap_entry_set(struct ocelot *ocelot, int ix, ++ struct ocelot_vcap_filter *filter) + { + if (filter->block_id == VCAP_IS1) + return is1_entry_set(ocelot, ix, filter); + if (filter->block_id == VCAP_IS2) +- return is2_entry_set(ocelot, ix, filter); ++ is2_entry_set(ocelot, ix, filter); + if (filter->block_id == VCAP_ES0) +- return es0_entry_set(ocelot, ix, filter); ++ es0_entry_set(ocelot, ix, filter); ++ ++ return 0; + } + + struct vcap_policer_entry { +@@ -1214,8 +1298,7 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot, + } + + /* Now insert the new filter */ +- vcap_entry_set(ocelot, index, filter); +- return 0; ++ return vcap_entry_set(ocelot, index, filter); + } + EXPORT_SYMBOL(ocelot_vcap_filter_add); + +diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +index 27f4786ace4f..a5ca5c4a7896 100644 +--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c ++++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +@@ -1631,21 +1631,21 @@ static void nfp_net_stat64(struct net_device *netdev, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&r_vec->rx_sync); ++ start = u64_stats_fetch_begin(&r_vec->rx_sync); + data[0] = r_vec->rx_pkts; + data[1] = r_vec->rx_bytes; + data[2] = r_vec->rx_drops; +- } while (u64_stats_fetch_retry_irq(&r_vec->rx_sync, start)); ++ } while (u64_stats_fetch_retry(&r_vec->rx_sync, start)); + stats->rx_packets += data[0]; + stats->rx_bytes += data[1]; + stats->rx_dropped += data[2]; + + do { +- start = u64_stats_fetch_begin_irq(&r_vec->tx_sync); ++ start = u64_stats_fetch_begin(&r_vec->tx_sync); + data[0] = r_vec->tx_pkts; + data[1] = r_vec->tx_bytes; + data[2] = r_vec->tx_errors; +- } while (u64_stats_fetch_retry_irq(&r_vec->tx_sync, start)); ++ } while (u64_stats_fetch_retry(&r_vec->tx_sync, start)); + stats->tx_packets += data[0]; + stats->tx_bytes += data[1]; + stats->tx_errors += data[2]; +diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +index af376b900067..cc97b3d00414 100644 +--- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c ++++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +@@ -881,7 +881,7 @@ static u64 *nfp_vnic_get_sw_stats(struct net_device *netdev, u64 *data) + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&nn->r_vecs[i].rx_sync); ++ start = u64_stats_fetch_begin(&nn->r_vecs[i].rx_sync); + data[0] = nn->r_vecs[i].rx_pkts; + tmp[0] = nn->r_vecs[i].hw_csum_rx_ok; + tmp[1] = nn->r_vecs[i].hw_csum_rx_inner_ok; +@@ -889,10 +889,10 @@ static u64 *nfp_vnic_get_sw_stats(struct net_device *netdev, u64 *data) + tmp[3] = nn->r_vecs[i].hw_csum_rx_error; + tmp[4] = nn->r_vecs[i].rx_replace_buf_alloc_fail; + tmp[5] = nn->r_vecs[i].hw_tls_rx; +- } while (u64_stats_fetch_retry_irq(&nn->r_vecs[i].rx_sync, start)); ++ } while (u64_stats_fetch_retry(&nn->r_vecs[i].rx_sync, start)); + + do { +- start = u64_stats_fetch_begin_irq(&nn->r_vecs[i].tx_sync); ++ start = u64_stats_fetch_begin(&nn->r_vecs[i].tx_sync); + data[1] = nn->r_vecs[i].tx_pkts; + data[2] = nn->r_vecs[i].tx_busy; + tmp[6] = nn->r_vecs[i].hw_csum_tx; +@@ -902,7 +902,7 @@ static u64 *nfp_vnic_get_sw_stats(struct net_device *netdev, u64 *data) + tmp[10] = nn->r_vecs[i].hw_tls_tx; + tmp[11] = nn->r_vecs[i].tls_tx_fallback; + tmp[12] = nn->r_vecs[i].tls_tx_no_fallback; +- } while (u64_stats_fetch_retry_irq(&nn->r_vecs[i].tx_sync, start)); ++ } while (u64_stats_fetch_retry(&nn->r_vecs[i].tx_sync, start)); + + data += NN_RVEC_PER_Q_STATS; + +diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +index 8b77582bdfa0..a6b6ca1fd55e 100644 +--- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c ++++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +@@ -134,13 +134,13 @@ nfp_repr_get_host_stats64(const struct net_device *netdev, + + repr_stats = per_cpu_ptr(repr->stats, i); + do { +- start = u64_stats_fetch_begin_irq(&repr_stats->syncp); ++ start = u64_stats_fetch_begin(&repr_stats->syncp); + tbytes = repr_stats->tx_bytes; + tpkts = repr_stats->tx_packets; + tdrops = repr_stats->tx_drops; + rbytes = repr_stats->rx_bytes; + rpkts = repr_stats->rx_packets; +- } while (u64_stats_fetch_retry_irq(&repr_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&repr_stats->syncp, start)); + + stats->tx_bytes += tbytes; + stats->tx_packets += tpkts; +diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c +index 486cbc8ab224..7a549b834e97 100644 +--- a/drivers/net/ethernet/nvidia/forcedeth.c ++++ b/drivers/net/ethernet/nvidia/forcedeth.c +@@ -1734,12 +1734,12 @@ static void nv_get_stats(int cpu, struct fe_priv *np, + u64 tx_packets, tx_bytes, tx_dropped; + + do { +- syncp_start = u64_stats_fetch_begin_irq(&np->swstats_rx_syncp); ++ syncp_start = u64_stats_fetch_begin(&np->swstats_rx_syncp); + rx_packets = src->stat_rx_packets; + rx_bytes = src->stat_rx_bytes; + rx_dropped = src->stat_rx_dropped; + rx_missed_errors = src->stat_rx_missed_errors; +- } while (u64_stats_fetch_retry_irq(&np->swstats_rx_syncp, syncp_start)); ++ } while (u64_stats_fetch_retry(&np->swstats_rx_syncp, syncp_start)); + + storage->rx_packets += rx_packets; + storage->rx_bytes += rx_bytes; +@@ -1747,11 +1747,11 @@ static void nv_get_stats(int cpu, struct fe_priv *np, + storage->rx_missed_errors += rx_missed_errors; + + do { +- syncp_start = u64_stats_fetch_begin_irq(&np->swstats_tx_syncp); ++ syncp_start = u64_stats_fetch_begin(&np->swstats_tx_syncp); + tx_packets = src->stat_tx_packets; + tx_bytes = src->stat_tx_bytes; + tx_dropped = src->stat_tx_dropped; +- } while (u64_stats_fetch_retry_irq(&np->swstats_tx_syncp, syncp_start)); ++ } while (u64_stats_fetch_retry(&np->swstats_tx_syncp, syncp_start)); + + storage->tx_packets += tx_packets; + storage->tx_bytes += tx_bytes; +diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +index 1b2119b1d48a..3f5e6572d20e 100644 +--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c ++++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +@@ -135,9 +135,9 @@ static void rmnet_get_stats64(struct net_device *dev, + pcpu_ptr = per_cpu_ptr(priv->pcpu_stats, cpu); + + do { +- start = u64_stats_fetch_begin_irq(&pcpu_ptr->syncp); ++ start = u64_stats_fetch_begin(&pcpu_ptr->syncp); + snapshot = pcpu_ptr->stats; /* struct assignment */ +- } while (u64_stats_fetch_retry_irq(&pcpu_ptr->syncp, start)); ++ } while (u64_stats_fetch_retry(&pcpu_ptr->syncp, start)); + + total_stats.rx_pkts += snapshot.rx_pkts; + total_stats.rx_bytes += snapshot.rx_bytes; +diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c +index 469e2e229c6e..9ce0e8a64ba8 100644 +--- a/drivers/net/ethernet/realtek/8139too.c ++++ b/drivers/net/ethernet/realtek/8139too.c +@@ -2532,16 +2532,16 @@ rtl8139_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) + netdev_stats_to_stats64(stats, &dev->stats); + + do { +- start = u64_stats_fetch_begin_irq(&tp->rx_stats.syncp); ++ start = u64_stats_fetch_begin(&tp->rx_stats.syncp); + stats->rx_packets = tp->rx_stats.packets; + stats->rx_bytes = tp->rx_stats.bytes; +- } while (u64_stats_fetch_retry_irq(&tp->rx_stats.syncp, start)); ++ } while (u64_stats_fetch_retry(&tp->rx_stats.syncp, start)); + + do { +- start = u64_stats_fetch_begin_irq(&tp->tx_stats.syncp); ++ start = u64_stats_fetch_begin(&tp->tx_stats.syncp); + stats->tx_packets = tp->tx_stats.packets; + stats->tx_bytes = tp->tx_stats.bytes; +- } while (u64_stats_fetch_retry_irq(&tp->tx_stats.syncp, start)); ++ } while (u64_stats_fetch_retry(&tp->tx_stats.syncp, start)); + } + + /* Set or clear the multicast filter for this adaptor. +diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c +index d2c6a5dfdc0e..b7e24ae92525 100644 +--- a/drivers/net/ethernet/socionext/sni_ave.c ++++ b/drivers/net/ethernet/socionext/sni_ave.c +@@ -1508,16 +1508,16 @@ static void ave_get_stats64(struct net_device *ndev, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&priv->stats_rx.syncp); ++ start = u64_stats_fetch_begin(&priv->stats_rx.syncp); + stats->rx_packets = priv->stats_rx.packets; + stats->rx_bytes = priv->stats_rx.bytes; +- } while (u64_stats_fetch_retry_irq(&priv->stats_rx.syncp, start)); ++ } while (u64_stats_fetch_retry(&priv->stats_rx.syncp, start)); + + do { +- start = u64_stats_fetch_begin_irq(&priv->stats_tx.syncp); ++ start = u64_stats_fetch_begin(&priv->stats_tx.syncp); + stats->tx_packets = priv->stats_tx.packets; + stats->tx_bytes = priv->stats_tx.bytes; +- } while (u64_stats_fetch_retry_irq(&priv->stats_tx.syncp, start)); ++ } while (u64_stats_fetch_retry(&priv->stats_tx.syncp, start)); + + stats->rx_errors = priv->stats_rx.errors; + stats->tx_errors = priv->stats_tx.errors; +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +index 84276eb681d7..7d6d6e3b1df7 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +@@ -84,22 +84,38 @@ static void dwmac4_rx_queue_enable(struct mac_device_info *hw, + } + + static void dwmac4_rx_queue_priority(struct mac_device_info *hw, +- u32 prio, u32 queue) ++ u32 prio_mask, u32 queue) + { + void __iomem *ioaddr = hw->pcsr; +- u32 base_register; +- u32 value; ++ u32 clear_mask = 0; ++ u32 ctrl2, ctrl3; ++ int i; + +- base_register = (queue < 4) ? GMAC_RXQ_CTRL2 : GMAC_RXQ_CTRL3; +- if (queue >= 4) +- queue -= 4; ++ ctrl2 = readl(ioaddr + GMAC_RXQ_CTRL2); ++ ctrl3 = readl(ioaddr + GMAC_RXQ_CTRL3); + +- value = readl(ioaddr + base_register); ++ for (i = 0; i < 4; i++) ++ clear_mask |= ((prio_mask << GMAC_RXQCTRL_PSRQX_SHIFT(i)) & ++ GMAC_RXQCTRL_PSRQX_MASK(i)); ++ ++ ctrl2 &= ~clear_mask; ++ ctrl3 &= ~clear_mask; + +- value &= ~GMAC_RXQCTRL_PSRQX_MASK(queue); +- value |= (prio << GMAC_RXQCTRL_PSRQX_SHIFT(queue)) & ++ if (queue < 4) { ++ ctrl2 |= (prio_mask << GMAC_RXQCTRL_PSRQX_SHIFT(queue)) & + GMAC_RXQCTRL_PSRQX_MASK(queue); +- writel(value, ioaddr + base_register); ++ ++ writel(ctrl2, ioaddr + GMAC_RXQ_CTRL2); ++ writel(ctrl3, ioaddr + GMAC_RXQ_CTRL3); ++ } else { ++ queue -= 4; ++ ++ ctrl3 |= (prio_mask << GMAC_RXQCTRL_PSRQX_SHIFT(queue)) & ++ GMAC_RXQCTRL_PSRQX_MASK(queue); ++ ++ writel(ctrl3, ioaddr + GMAC_RXQ_CTRL3); ++ writel(ctrl2, ioaddr + GMAC_RXQ_CTRL2); ++ } + } + + static void dwmac4_tx_queue_priority(struct mac_device_info *hw, +@@ -1209,6 +1225,7 @@ const struct stmmac_ops dwmac410_ops = { + .est_configure = dwmac5_est_configure, + .est_irq_status = dwmac5_est_irq_status, + .fpe_configure = dwmac5_fpe_configure, ++ .fpe_configure_get = dwmac5_fpe_configure_get, + .fpe_send_mpacket = dwmac5_fpe_send_mpacket, + .fpe_irq_status = dwmac5_fpe_irq_status, + .add_hw_vlan_rx_fltr = dwmac4_add_hw_vlan_rx_fltr, +@@ -1261,6 +1278,7 @@ const struct stmmac_ops dwmac510_ops = { + .est_configure = dwmac5_est_configure, + .est_irq_status = dwmac5_est_irq_status, + .fpe_configure = dwmac5_fpe_configure, ++ .fpe_configure_get = dwmac5_fpe_configure_get, + .fpe_send_mpacket = dwmac5_fpe_send_mpacket, + .fpe_irq_status = dwmac5_fpe_irq_status, + .add_hw_vlan_rx_fltr = dwmac4_add_hw_vlan_rx_fltr, +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c +index e95d35f1e5a0..8633c0fa4c76 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c +@@ -711,10 +711,15 @@ void dwmac5_est_irq_status(void __iomem *ioaddr, struct net_device *dev, + } + + void dwmac5_fpe_configure(void __iomem *ioaddr, u32 num_txq, u32 num_rxq, +- bool enable) ++ bool enable, struct stmmac_fpe *fpe) + { + u32 value; + ++ if (fpe) { ++ value = fpe->p_queues << MTL_FPECTRL_PEC_SHIFT | fpe->fragsize; ++ writel(value, ioaddr + MTL_FPE_CTRL_STS); ++ } ++ + if (!enable) { + value = readl(ioaddr + MAC_FPE_CTRL_STS); + +@@ -734,6 +739,18 @@ void dwmac5_fpe_configure(void __iomem *ioaddr, u32 num_txq, u32 num_rxq, + writel(value, ioaddr + MAC_FPE_CTRL_STS); + } + ++void dwmac5_fpe_configure_get(void __iomem *ioaddr, struct stmmac_fpe *fpe) ++{ ++ u32 value; ++ ++ value = readl(ioaddr + MAC_FPE_CTRL_STS); ++ fpe->enable = value & EFPE; ++ ++ value = readl(ioaddr + MTL_FPE_CTRL_STS); ++ fpe->p_queues = (value >> MTL_FPECTRL_PEC_SHIFT) & GENMASK(7, 0); ++ fpe->fragsize = value & GENMASK(1, 0); ++} ++ + int dwmac5_fpe_irq_status(void __iomem *ioaddr, struct net_device *dev) + { + u32 value; +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h +index 53c138d0ff48..ead1f7d4dc19 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h +@@ -20,6 +20,9 @@ + #define SVER BIT(1) + #define EFPE BIT(0) + ++#define MTL_FPE_CTRL_STS 0x00000c90 ++#define MTL_FPECTRL_PEC_SHIFT 8 ++ + #define MAC_PPS_CONTROL 0x00000b70 + #define PPS_MAXIDX(x) ((((x) + 1) * 8) - 1) + #define PPS_MINIDX(x) ((x) * 8) +@@ -154,7 +157,8 @@ int dwmac5_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg, + void dwmac5_est_irq_status(void __iomem *ioaddr, struct net_device *dev, + struct stmmac_extra_stats *x, u32 txqcnt); + void dwmac5_fpe_configure(void __iomem *ioaddr, u32 num_txq, u32 num_rxq, +- bool enable); ++ bool enable, struct stmmac_fpe *fpe); ++void dwmac5_fpe_configure_get(void __iomem *ioaddr, struct stmmac_fpe *fpe); + void dwmac5_fpe_send_mpacket(void __iomem *ioaddr, + enum stmmac_mpacket_type type); + int dwmac5_fpe_irq_status(void __iomem *ioaddr, struct net_device *dev); +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +index c6c4d7948fe5..a004b2e5d2e3 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +@@ -1430,7 +1430,8 @@ static int dwxgmac3_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg, + } + + static void dwxgmac3_fpe_configure(void __iomem *ioaddr, u32 num_txq, +- u32 num_rxq, bool enable) ++ u32 num_rxq, bool enable, ++ struct stmmac_fpe *fpe) + { + u32 value; + +diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h +index 156071275125..2cea77133e7f 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h ++++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h +@@ -279,6 +279,12 @@ struct stmmac_pps_cfg; + struct stmmac_rss; + struct stmmac_est; + ++struct stmmac_fpe { ++ u8 enable; ++ u8 p_queues; ++ u8 fragsize; ++}; ++ + /* Helpers to program the MAC core */ + struct stmmac_ops { + /* MAC core initialization */ +@@ -391,7 +397,8 @@ struct stmmac_ops { + void (*est_irq_status)(void __iomem *ioaddr, struct net_device *dev, + struct stmmac_extra_stats *x, u32 txqcnt); + void (*fpe_configure)(void __iomem *ioaddr, u32 num_txq, u32 num_rxq, +- bool enable); ++ bool enable, struct stmmac_fpe *fpe); ++ void (*fpe_configure_get)(void __iomem *ioaddr, struct stmmac_fpe *fpe); + void (*fpe_send_mpacket)(void __iomem *ioaddr, + enum stmmac_mpacket_type type); + int (*fpe_irq_status)(void __iomem *ioaddr, struct net_device *dev); +@@ -495,6 +502,8 @@ struct stmmac_ops { + stmmac_do_void_callback(__priv, mac, est_irq_status, __args) + #define stmmac_fpe_configure(__priv, __args...) \ + stmmac_do_void_callback(__priv, mac, fpe_configure, __args) ++#define stmmac_fpe_configure_get(__priv, __args...) \ ++ stmmac_do_void_callback(__priv, mac, fpe_configure_get, __args) + #define stmmac_fpe_send_mpacket(__priv, __args...) \ + stmmac_do_void_callback(__priv, mac, fpe_send_mpacket, __args) + #define stmmac_fpe_irq_status(__priv, __args...) \ +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +index 782825822278..d07b173791e4 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -325,6 +325,7 @@ struct stmmac_priv { + /* XDP BPF Program */ + unsigned long *af_xdp_zc_qps; + struct bpf_prog *xdp_prog; ++ bool fp_enabled_admin; + }; + + enum stmmac_state { +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +index 35c8dd92d369..79988e61be69 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +@@ -19,6 +19,7 @@ + #include "stmmac.h" + #include "dwmac_dma.h" + #include "dwxgmac2.h" ++#include "common.h" + + #define REG_SPACE_SIZE 0x1060 + #define GMAC4_REG_SPACE_SIZE 0x116C +@@ -1149,6 +1150,105 @@ static int stmmac_set_tunable(struct net_device *dev, + return ret; + } + ++static int stmmac_reset_preempt(struct net_device *dev, bool enable) ++{ ++ struct stmmac_priv *priv = netdev_priv(dev); ++ struct stmmac_fpe_cfg *fpe_cfg = priv->plat->fpe_cfg; ++ bool *hs_enable = &fpe_cfg->hs_enable; ++ ++ if (enable) { ++ if (priv->fp_enabled_admin) { ++ priv->plat->fpe_cfg->enable = 1; ++ if (!*hs_enable) ++ stmmac_fpe_configure(priv, priv->ioaddr, priv->plat->tx_queues_to_use, ++ priv->plat->rx_queues_to_use, true, NULL); ++ } ++ } else { ++ priv->plat->fpe_cfg->enable = 0; ++ stmmac_fpe_configure(priv, priv->ioaddr, priv->plat->tx_queues_to_use, ++ priv->plat->rx_queues_to_use, false, NULL); ++ } ++ ++ return 0; ++} ++ ++static int stmmac_set_preempt(struct net_device *dev, struct ethtool_fp *fpcmd) ++{ ++ struct stmmac_priv *priv = netdev_priv(dev); ++ struct stmmac_fpe fpe; ++ ++ if (!priv->dma_cap.fpesel) ++ return -EOPNOTSUPP; ++ ++ if (!fpcmd->disabled && ++ (fpcmd->min_frag_size < 60 || fpcmd->min_frag_size > 252)) ++ return -EINVAL; ++ ++ fpe.p_queues = fpcmd->preemptible_queues_mask; ++ fpe.fragsize = DIV_ROUND_UP((fpcmd->min_frag_size + 4), 64) - 1; ++ ++ if (priv->plat->fpe_cfg->lo_fpe_state == FPE_STATE_ON) ++ fpe.enable = 1; ++ else ++ fpe.enable = fpcmd->fp_enabled ? 0 : 1; ++ ++ /* To support preemption MAC should have more than 1 TX queue with at ++ * least 1 Queue designated as Express Queue. Queue 0 is always used as ++ * preemption queue when preemption MAC is enabled. ++ */ ++ if (fpe.p_queues >= (GENMASK(priv->plat->tx_queues_to_use - 1, 0) - 1)) { ++ netdev_err(priv->dev, ++ "Preemptable queue mask 0x%x not supported.", ++ fpe.p_queues); ++ return -EINVAL; ++ } ++ ++ if (fpcmd->disabled) { ++ fpe.enable = 0; ++ priv->plat->fpe_cfg->enable = 0; ++ priv->fp_enabled_admin = 0; ++ } else { ++ priv->plat->fpe_cfg->enable = 1; ++ priv->fp_enabled_admin = 1; ++ } ++ stmmac_fpe_configure(priv, priv->ioaddr, priv->plat->tx_queues_to_use, ++ priv->plat->rx_queues_to_use, fpe.enable, &fpe); ++ ++ stmmac_fpe_handshake(priv, fpcmd->fp_enabled); ++ ++ return 0; ++} ++ ++static int stmmac_get_preempt(struct net_device *dev, struct ethtool_fp *fpcmd) ++{ ++ struct stmmac_priv *priv = netdev_priv(dev); ++ struct stmmac_fpe fpe; ++ int ret; ++ ++ ret = stmmac_fpe_configure_get(priv, priv->ioaddr, &fpe); ++ if (ret) { ++ fpcmd->fp_supported = 0; ++ fpcmd->supported_queues_mask = 0; ++ fpcmd->preemptible_queues_mask = 0; ++ fpcmd->min_frag_size = 0; ++ ++ return 0; ++ } ++ ++ fpcmd->fp_supported = 1; ++ fpcmd->fp_status = priv->plat->fpe_cfg->enable; ++ fpcmd->fp_active = fpe.enable; ++ fpcmd->supported_queues_mask = GENMASK(priv->plat->tx_queues_to_use - 1, ++ 0); ++ fpcmd->preemptible_queues_mask = fpe.p_queues; ++ /* Queue 0 is always preemption when preemption is active. */ ++ if (fpcmd->fp_active) ++ fpcmd->preemptible_queues_mask |= 1; ++ fpcmd->min_frag_size = (fpe.fragsize + 1) * 64 - 4; ++ ++ return 0; ++} ++ + static const struct ethtool_ops stmmac_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_MAX_FRAMES, +@@ -1188,6 +1288,9 @@ static const struct ethtool_ops stmmac_ethtool_ops = { + .set_tunable = stmmac_set_tunable, + .get_link_ksettings = stmmac_ethtool_get_link_ksettings, + .set_link_ksettings = stmmac_ethtool_set_link_ksettings, ++ .set_preempt = stmmac_set_preempt, ++ .get_preempt = stmmac_get_preempt, ++ .reset_preempt = stmmac_reset_preempt, + }; + + void stmmac_set_ethtool_ops(struct net_device *netdev) +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 72c0b874acee..63a0738ebdf2 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -956,11 +956,17 @@ static void stmmac_fpe_link_state_handle(struct stmmac_priv *priv, bool is_up) + enum stmmac_fpe_state *lp_state = &fpe_cfg->lp_fpe_state; + bool *hs_enable = &fpe_cfg->hs_enable; + +- if (is_up && *hs_enable) { +- stmmac_fpe_send_mpacket(priv, priv->ioaddr, MPACKET_VERIFY); ++ if (is_up) { ++ if (*hs_enable) ++ stmmac_fpe_send_mpacket(priv, priv->ioaddr, MPACKET_VERIFY); + } else { + *lo_state = FPE_STATE_OFF; + *lp_state = FPE_STATE_OFF; ++ priv->plat->fpe_cfg->enable = false; ++ stmmac_fpe_configure(priv, priv->ioaddr, ++ priv->plat->tx_queues_to_use, ++ priv->plat->rx_queues_to_use, ++ false, NULL); + } + } + +@@ -2433,12 +2439,14 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) + /* We are sharing with slow path and stop XSK TX desc submission when + * available TX ring is less than threshold. + */ +- if (unlikely(stmmac_tx_avail(priv, queue) < STMMAC_TX_XSK_AVAIL) || +- !netif_carrier_ok(priv->dev)) { ++ if (unlikely(stmmac_tx_avail(priv, queue) < STMMAC_TX_XSK_AVAIL)) { + work_done = false; + break; + } + ++ if (!netif_carrier_ok(priv->dev)) ++ break; ++ + if (!xsk_tx_peek_desc(pool, &xdp_desc)) + break; + +@@ -6775,7 +6783,9 @@ static const struct net_device_ops stmmac_netdev_ops = { + .ndo_fix_features = stmmac_fix_features, + .ndo_set_features = stmmac_set_features, + .ndo_set_rx_mode = stmmac_set_rx_mode, ++#ifndef CONFIG_NET_SCH_MULTIQ + .ndo_tx_timeout = stmmac_tx_timeout, ++#endif + .ndo_eth_ioctl = stmmac_ioctl, + .ndo_setup_tc = stmmac_setup_tc, + .ndo_select_queue = stmmac_select_queue, +@@ -6928,6 +6938,7 @@ static void stmmac_napi_add(struct net_device *dev) + { + struct stmmac_priv *priv = netdev_priv(dev); + u32 queue, maxq; ++ char name[NAPINAMSIZ]; + + maxq = max(priv->plat->rx_queues_to_use, priv->plat->tx_queues_to_use); + +@@ -6939,16 +6950,22 @@ static void stmmac_napi_add(struct net_device *dev) + spin_lock_init(&ch->lock); + + if (queue < priv->plat->rx_queues_to_use) { +- netif_napi_add(dev, &ch->rx_napi, stmmac_napi_poll_rx); ++ snprintf(name, NAPINAMSIZ, "rx-%d", queue); ++ netif_napi_add_named(dev, &ch->rx_napi, stmmac_napi_poll_rx, ++ NAPI_POLL_WEIGHT, name); + } + if (queue < priv->plat->tx_queues_to_use) { +- netif_napi_add_tx(dev, &ch->tx_napi, +- stmmac_napi_poll_tx); ++ snprintf(name, NAPINAMSIZ, "tx-%d", queue); ++ netif_napi_add_tx_named(dev, &ch->tx_napi, ++ stmmac_napi_poll_tx, ++ NAPI_POLL_WEIGHT, name); + } + if (queue < priv->plat->rx_queues_to_use && + queue < priv->plat->tx_queues_to_use) { +- netif_napi_add(dev, &ch->rxtx_napi, +- stmmac_napi_poll_rxtx); ++ snprintf(name, NAPINAMSIZ, "zc-%d", queue); ++ netif_napi_add_named(dev, &ch->rxtx_napi, ++ stmmac_napi_poll_rxtx, ++ NAPI_POLL_WEIGHT, name); + } + } + } +@@ -7025,7 +7042,6 @@ static void stmmac_fpe_lp_task(struct work_struct *work) + enum stmmac_fpe_state *lo_state = &fpe_cfg->lo_fpe_state; + enum stmmac_fpe_state *lp_state = &fpe_cfg->lp_fpe_state; + bool *hs_enable = &fpe_cfg->hs_enable; +- bool *enable = &fpe_cfg->enable; + int retries = 20; + + while (retries-- > 0) { +@@ -7038,7 +7054,7 @@ static void stmmac_fpe_lp_task(struct work_struct *work) + stmmac_fpe_configure(priv, priv->ioaddr, + priv->plat->tx_queues_to_use, + priv->plat->rx_queues_to_use, +- *enable); ++ true, NULL); + + netdev_info(priv->dev, "configured FPE\n"); + +@@ -7481,7 +7497,7 @@ int stmmac_suspend(struct device *dev) + /* Disable FPE */ + stmmac_fpe_configure(priv, priv->ioaddr, + priv->plat->tx_queues_to_use, +- priv->plat->rx_queues_to_use, false); ++ priv->plat->rx_queues_to_use, false, NULL); + + stmmac_fpe_handshake(priv, false); + stmmac_fpe_stop_wq(priv); +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +index 773e415cc2de..7c1759d99952 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +@@ -922,7 +922,7 @@ static int tc_setup_taprio(struct stmmac_priv *priv, + struct plat_stmmacenet_data *plat = priv->plat; + struct timespec64 time, current_time, qopt_time; + ktime_t current_time_ns; +- bool fpe = false; ++ struct stmmac_fpe fpe; + int i, ret = 0; + u64 ctr; + +@@ -988,6 +988,8 @@ static int tc_setup_taprio(struct stmmac_priv *priv, + priv->plat->est->enable = qopt->enable; + mutex_unlock(&priv->plat->est->lock); + ++ stmmac_fpe_configure_get(priv, priv->ioaddr, &fpe); ++ + for (i = 0; i < size; i++) { + s64 delta_ns = qopt->entries[i].interval; + u32 gates = qopt->entries[i].gate_mask; +@@ -999,16 +1001,18 @@ static int tc_setup_taprio(struct stmmac_priv *priv, + + switch (qopt->entries[i].command) { + case TC_TAPRIO_CMD_SET_GATES: +- if (fpe) ++ if (fpe.enable) + return -EINVAL; + break; + case TC_TAPRIO_CMD_SET_AND_HOLD: ++ if (!fpe.enable) ++ return -EINVAL; + gates |= BIT(0); +- fpe = true; + break; + case TC_TAPRIO_CMD_SET_AND_RELEASE: ++ if (!fpe.enable) ++ return -EINVAL; + gates &= ~BIT(0); +- fpe = true; + break; + default: + return -EOPNOTSUPP; +@@ -1035,16 +1039,6 @@ static int tc_setup_taprio(struct stmmac_priv *priv, + priv->plat->est->ctr[0] = do_div(ctr, NSEC_PER_SEC); + priv->plat->est->ctr[1] = (u32)ctr; + +- if (fpe && !priv->dma_cap.fpesel) { +- mutex_unlock(&priv->plat->est->lock); +- return -EOPNOTSUPP; +- } +- +- /* Actual FPE register configuration will be done after FPE handshake +- * is success. +- */ +- priv->plat->fpe_cfg->enable = fpe; +- + ret = stmmac_est_configure(priv, priv->ioaddr, priv->plat->est, + priv->plat->clk_ptp_rate); + mutex_unlock(&priv->plat->est->lock); +@@ -1055,11 +1049,6 @@ static int tc_setup_taprio(struct stmmac_priv *priv, + + netdev_info(priv->dev, "configured EST\n"); + +- if (fpe) { +- stmmac_fpe_handshake(priv, true); +- netdev_info(priv->dev, "start FPE handshake\n"); +- } +- + return 0; + + disable: +@@ -1071,16 +1060,6 @@ static int tc_setup_taprio(struct stmmac_priv *priv, + mutex_unlock(&priv->plat->est->lock); + } + +- priv->plat->fpe_cfg->enable = false; +- stmmac_fpe_configure(priv, priv->ioaddr, +- priv->plat->tx_queues_to_use, +- priv->plat->rx_queues_to_use, +- false); +- netdev_info(priv->dev, "disabled FPE\n"); +- +- stmmac_fpe_handshake(priv, false); +- netdev_info(priv->dev, "stop FPE handshake\n"); +- + return ret; + } + +diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c +index 25466cbdc16b..450c20d65d19 100644 +--- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c ++++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c +@@ -1376,12 +1376,12 @@ static void am65_cpsw_nuss_ndo_get_stats(struct net_device *dev, + + cpu_stats = per_cpu_ptr(ndev_priv->stats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); ++ start = u64_stats_fetch_begin(&cpu_stats->syncp); + rx_packets = cpu_stats->rx_packets; + rx_bytes = cpu_stats->rx_bytes; + tx_packets = cpu_stats->tx_packets; + tx_bytes = cpu_stats->tx_bytes; +- } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); + + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; +diff --git a/drivers/net/ethernet/ti/cpsw_priv.c b/drivers/net/ethernet/ti/cpsw_priv.c +index 758295c898ac..e966dd47e2db 100644 +--- a/drivers/net/ethernet/ti/cpsw_priv.c ++++ b/drivers/net/ethernet/ti/cpsw_priv.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #include "cpsw.h" + #include "cpts.h" +diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c +index 9eb9eaff4dc9..1bb596a9d8a2 100644 +--- a/drivers/net/ethernet/ti/netcp_core.c ++++ b/drivers/net/ethernet/ti/netcp_core.c +@@ -1916,16 +1916,16 @@ netcp_get_stats(struct net_device *ndev, struct rtnl_link_stats64 *stats) + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&p->syncp_rx); ++ start = u64_stats_fetch_begin(&p->syncp_rx); + rxpackets = p->rx_packets; + rxbytes = p->rx_bytes; +- } while (u64_stats_fetch_retry_irq(&p->syncp_rx, start)); ++ } while (u64_stats_fetch_retry(&p->syncp_rx, start)); + + do { +- start = u64_stats_fetch_begin_irq(&p->syncp_tx); ++ start = u64_stats_fetch_begin(&p->syncp_tx); + txpackets = p->tx_packets; + txbytes = p->tx_bytes; +- } while (u64_stats_fetch_retry_irq(&p->syncp_tx, start)); ++ } while (u64_stats_fetch_retry(&p->syncp_tx, start)); + + stats->rx_packets = rxpackets; + stats->rx_bytes = rxbytes; +diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c +index 0fb15a17b547..d716e6fe26e1 100644 +--- a/drivers/net/ethernet/via/via-rhine.c ++++ b/drivers/net/ethernet/via/via-rhine.c +@@ -2217,16 +2217,16 @@ rhine_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) + netdev_stats_to_stats64(stats, &dev->stats); + + do { +- start = u64_stats_fetch_begin_irq(&rp->rx_stats.syncp); ++ start = u64_stats_fetch_begin(&rp->rx_stats.syncp); + stats->rx_packets = rp->rx_stats.packets; + stats->rx_bytes = rp->rx_stats.bytes; +- } while (u64_stats_fetch_retry_irq(&rp->rx_stats.syncp, start)); ++ } while (u64_stats_fetch_retry(&rp->rx_stats.syncp, start)); + + do { +- start = u64_stats_fetch_begin_irq(&rp->tx_stats.syncp); ++ start = u64_stats_fetch_begin(&rp->tx_stats.syncp); + stats->tx_packets = rp->tx_stats.packets; + stats->tx_bytes = rp->tx_stats.bytes; +- } while (u64_stats_fetch_retry_irq(&rp->tx_stats.syncp, start)); ++ } while (u64_stats_fetch_retry(&rp->tx_stats.syncp, start)); + } + + static void rhine_set_rx_mode(struct net_device *dev) +diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +index 420a73203ca1..ffc943a12762 100644 +--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c ++++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +@@ -1305,16 +1305,16 @@ axienet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) + netdev_stats_to_stats64(stats, &dev->stats); + + do { +- start = u64_stats_fetch_begin_irq(&lp->rx_stat_sync); ++ start = u64_stats_fetch_begin(&lp->rx_stat_sync); + stats->rx_packets = u64_stats_read(&lp->rx_packets); + stats->rx_bytes = u64_stats_read(&lp->rx_bytes); +- } while (u64_stats_fetch_retry_irq(&lp->rx_stat_sync, start)); ++ } while (u64_stats_fetch_retry(&lp->rx_stat_sync, start)); + + do { +- start = u64_stats_fetch_begin_irq(&lp->tx_stat_sync); ++ start = u64_stats_fetch_begin(&lp->tx_stat_sync); + stats->tx_packets = u64_stats_read(&lp->tx_packets); + stats->tx_bytes = u64_stats_read(&lp->tx_bytes); +- } while (u64_stats_fetch_retry_irq(&lp->tx_stat_sync, start)); ++ } while (u64_stats_fetch_retry(&lp->tx_stat_sync, start)); + } + + static const struct net_device_ops axienet_netdev_ops = { +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 89eb4f179a3c..f9b219e6cd58 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -1264,12 +1264,12 @@ static void netvsc_get_vf_stats(struct net_device *net, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&stats->syncp); ++ start = u64_stats_fetch_begin(&stats->syncp); + rx_packets = stats->rx_packets; + tx_packets = stats->tx_packets; + rx_bytes = stats->rx_bytes; + tx_bytes = stats->tx_bytes; +- } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&stats->syncp, start)); + + tot->rx_packets += rx_packets; + tot->tx_packets += tx_packets; +@@ -1294,12 +1294,12 @@ static void netvsc_get_pcpu_stats(struct net_device *net, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&stats->syncp); ++ start = u64_stats_fetch_begin(&stats->syncp); + this_tot->vf_rx_packets = stats->rx_packets; + this_tot->vf_tx_packets = stats->tx_packets; + this_tot->vf_rx_bytes = stats->rx_bytes; + this_tot->vf_tx_bytes = stats->tx_bytes; +- } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&stats->syncp, start)); + this_tot->rx_packets = this_tot->vf_rx_packets; + this_tot->tx_packets = this_tot->vf_tx_packets; + this_tot->rx_bytes = this_tot->vf_rx_bytes; +@@ -1318,20 +1318,20 @@ static void netvsc_get_pcpu_stats(struct net_device *net, + + tx_stats = &nvchan->tx_stats; + do { +- start = u64_stats_fetch_begin_irq(&tx_stats->syncp); ++ start = u64_stats_fetch_begin(&tx_stats->syncp); + packets = tx_stats->packets; + bytes = tx_stats->bytes; +- } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); + + this_tot->tx_bytes += bytes; + this_tot->tx_packets += packets; + + rx_stats = &nvchan->rx_stats; + do { +- start = u64_stats_fetch_begin_irq(&rx_stats->syncp); ++ start = u64_stats_fetch_begin(&rx_stats->syncp); + packets = rx_stats->packets; + bytes = rx_stats->bytes; +- } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); + + this_tot->rx_bytes += bytes; + this_tot->rx_packets += packets; +@@ -1370,21 +1370,21 @@ static void netvsc_get_stats64(struct net_device *net, + + tx_stats = &nvchan->tx_stats; + do { +- start = u64_stats_fetch_begin_irq(&tx_stats->syncp); ++ start = u64_stats_fetch_begin(&tx_stats->syncp); + packets = tx_stats->packets; + bytes = tx_stats->bytes; +- } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); + + t->tx_bytes += bytes; + t->tx_packets += packets; + + rx_stats = &nvchan->rx_stats; + do { +- start = u64_stats_fetch_begin_irq(&rx_stats->syncp); ++ start = u64_stats_fetch_begin(&rx_stats->syncp); + packets = rx_stats->packets; + bytes = rx_stats->bytes; + multicast = rx_stats->multicast + rx_stats->broadcast; +- } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); + + t->rx_bytes += bytes; + t->rx_packets += packets; +@@ -1527,24 +1527,24 @@ static void netvsc_get_ethtool_stats(struct net_device *dev, + tx_stats = &nvdev->chan_table[j].tx_stats; + + do { +- start = u64_stats_fetch_begin_irq(&tx_stats->syncp); ++ start = u64_stats_fetch_begin(&tx_stats->syncp); + packets = tx_stats->packets; + bytes = tx_stats->bytes; + xdp_xmit = tx_stats->xdp_xmit; +- } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); + data[i++] = packets; + data[i++] = bytes; + data[i++] = xdp_xmit; + + rx_stats = &nvdev->chan_table[j].rx_stats; + do { +- start = u64_stats_fetch_begin_irq(&rx_stats->syncp); ++ start = u64_stats_fetch_begin(&rx_stats->syncp); + packets = rx_stats->packets; + bytes = rx_stats->bytes; + xdp_drop = rx_stats->xdp_drop; + xdp_redirect = rx_stats->xdp_redirect; + xdp_tx = rx_stats->xdp_tx; +- } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); + data[i++] = packets; + data[i++] = bytes; + data[i++] = xdp_drop; +diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c +index 1c64d5347b8e..78253ad57b2e 100644 +--- a/drivers/net/ifb.c ++++ b/drivers/net/ifb.c +@@ -162,18 +162,18 @@ static void ifb_stats64(struct net_device *dev, + + for (i = 0; i < dev->num_tx_queues; i++,txp++) { + do { +- start = u64_stats_fetch_begin_irq(&txp->rx_stats.sync); ++ start = u64_stats_fetch_begin(&txp->rx_stats.sync); + packets = txp->rx_stats.packets; + bytes = txp->rx_stats.bytes; +- } while (u64_stats_fetch_retry_irq(&txp->rx_stats.sync, start)); ++ } while (u64_stats_fetch_retry(&txp->rx_stats.sync, start)); + stats->rx_packets += packets; + stats->rx_bytes += bytes; + + do { +- start = u64_stats_fetch_begin_irq(&txp->tx_stats.sync); ++ start = u64_stats_fetch_begin(&txp->tx_stats.sync); + packets = txp->tx_stats.packets; + bytes = txp->tx_stats.bytes; +- } while (u64_stats_fetch_retry_irq(&txp->tx_stats.sync, start)); ++ } while (u64_stats_fetch_retry(&txp->tx_stats.sync, start)); + stats->tx_packets += packets; + stats->tx_bytes += bytes; + } +@@ -245,12 +245,12 @@ static void ifb_fill_stats_data(u64 **data, + int j; + + do { +- start = u64_stats_fetch_begin_irq(&q_stats->sync); ++ start = u64_stats_fetch_begin(&q_stats->sync); + for (j = 0; j < IFB_Q_STATS_LEN; j++) { + offset = ifb_q_stats_desc[j].offset; + (*data)[j] = *(u64 *)(stats_base + offset); + } +- } while (u64_stats_fetch_retry_irq(&q_stats->sync, start)); ++ } while (u64_stats_fetch_retry(&q_stats->sync, start)); + + *data += IFB_Q_STATS_LEN; + } +diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c +index cd16bc8bf154..1b55928e89b8 100644 +--- a/drivers/net/ipvlan/ipvlan_main.c ++++ b/drivers/net/ipvlan/ipvlan_main.c +@@ -301,13 +301,13 @@ static void ipvlan_get_stats64(struct net_device *dev, + for_each_possible_cpu(idx) { + pcptr = per_cpu_ptr(ipvlan->pcpu_stats, idx); + do { +- strt= u64_stats_fetch_begin_irq(&pcptr->syncp); ++ strt = u64_stats_fetch_begin(&pcptr->syncp); + rx_pkts = u64_stats_read(&pcptr->rx_pkts); + rx_bytes = u64_stats_read(&pcptr->rx_bytes); + rx_mcast = u64_stats_read(&pcptr->rx_mcast); + tx_pkts = u64_stats_read(&pcptr->tx_pkts); + tx_bytes = u64_stats_read(&pcptr->tx_bytes); +- } while (u64_stats_fetch_retry_irq(&pcptr->syncp, ++ } while (u64_stats_fetch_retry(&pcptr->syncp, + strt)); + + s->rx_packets += rx_pkts; +diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c +index 2e9742952c4e..f6d53e63ef4e 100644 +--- a/drivers/net/loopback.c ++++ b/drivers/net/loopback.c +@@ -106,10 +106,10 @@ void dev_lstats_read(struct net_device *dev, u64 *packets, u64 *bytes) + + lb_stats = per_cpu_ptr(dev->lstats, i); + do { +- start = u64_stats_fetch_begin_irq(&lb_stats->syncp); ++ start = u64_stats_fetch_begin(&lb_stats->syncp); + tpackets = u64_stats_read(&lb_stats->packets); + tbytes = u64_stats_read(&lb_stats->bytes); +- } while (u64_stats_fetch_retry_irq(&lb_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&lb_stats->syncp, start)); + *bytes += tbytes; + *packets += tpackets; + } +diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c +index 578f470e9fad..2de422ab3e6a 100644 +--- a/drivers/net/macsec.c ++++ b/drivers/net/macsec.c +@@ -2800,9 +2800,9 @@ static void get_rx_sc_stats(struct net_device *dev, + + stats = per_cpu_ptr(rx_sc->stats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&stats->syncp); ++ start = u64_stats_fetch_begin(&stats->syncp); + memcpy(&tmp, &stats->stats, sizeof(tmp)); +- } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&stats->syncp, start)); + + sum->InOctetsValidated += tmp.InOctetsValidated; + sum->InOctetsDecrypted += tmp.InOctetsDecrypted; +@@ -2881,9 +2881,9 @@ static void get_tx_sc_stats(struct net_device *dev, + + stats = per_cpu_ptr(macsec_priv(dev)->secy.tx_sc.stats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&stats->syncp); ++ start = u64_stats_fetch_begin(&stats->syncp); + memcpy(&tmp, &stats->stats, sizeof(tmp)); +- } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&stats->syncp, start)); + + sum->OutPktsProtected += tmp.OutPktsProtected; + sum->OutPktsEncrypted += tmp.OutPktsEncrypted; +@@ -2937,9 +2937,9 @@ static void get_secy_stats(struct net_device *dev, struct macsec_dev_stats *sum) + + stats = per_cpu_ptr(macsec_priv(dev)->stats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&stats->syncp); ++ start = u64_stats_fetch_begin(&stats->syncp); + memcpy(&tmp, &stats->stats, sizeof(tmp)); +- } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&stats->syncp, start)); + + sum->OutPktsUntagged += tmp.OutPktsUntagged; + sum->InPktsUntagged += tmp.InPktsUntagged; +diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c +index b8cc55b2d721..99a971929c8e 100644 +--- a/drivers/net/macvlan.c ++++ b/drivers/net/macvlan.c +@@ -948,13 +948,13 @@ static void macvlan_dev_get_stats64(struct net_device *dev, + for_each_possible_cpu(i) { + p = per_cpu_ptr(vlan->pcpu_stats, i); + do { +- start = u64_stats_fetch_begin_irq(&p->syncp); ++ start = u64_stats_fetch_begin(&p->syncp); + rx_packets = u64_stats_read(&p->rx_packets); + rx_bytes = u64_stats_read(&p->rx_bytes); + rx_multicast = u64_stats_read(&p->rx_multicast); + tx_packets = u64_stats_read(&p->tx_packets); + tx_bytes = u64_stats_read(&p->tx_bytes); +- } while (u64_stats_fetch_retry_irq(&p->syncp, start)); ++ } while (u64_stats_fetch_retry(&p->syncp, start)); + + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; +diff --git a/drivers/net/mhi_net.c b/drivers/net/mhi_net.c +index 0b9d37979133..3d322ac4f6a5 100644 +--- a/drivers/net/mhi_net.c ++++ b/drivers/net/mhi_net.c +@@ -104,19 +104,19 @@ static void mhi_ndo_get_stats64(struct net_device *ndev, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&mhi_netdev->stats.rx_syncp); ++ start = u64_stats_fetch_begin(&mhi_netdev->stats.rx_syncp); + stats->rx_packets = u64_stats_read(&mhi_netdev->stats.rx_packets); + stats->rx_bytes = u64_stats_read(&mhi_netdev->stats.rx_bytes); + stats->rx_errors = u64_stats_read(&mhi_netdev->stats.rx_errors); +- } while (u64_stats_fetch_retry_irq(&mhi_netdev->stats.rx_syncp, start)); ++ } while (u64_stats_fetch_retry(&mhi_netdev->stats.rx_syncp, start)); + + do { +- start = u64_stats_fetch_begin_irq(&mhi_netdev->stats.tx_syncp); ++ start = u64_stats_fetch_begin(&mhi_netdev->stats.tx_syncp); + stats->tx_packets = u64_stats_read(&mhi_netdev->stats.tx_packets); + stats->tx_bytes = u64_stats_read(&mhi_netdev->stats.tx_bytes); + stats->tx_errors = u64_stats_read(&mhi_netdev->stats.tx_errors); + stats->tx_dropped = u64_stats_read(&mhi_netdev->stats.tx_dropped); +- } while (u64_stats_fetch_retry_irq(&mhi_netdev->stats.tx_syncp, start)); ++ } while (u64_stats_fetch_retry(&mhi_netdev->stats.tx_syncp, start)); + } + + static const struct net_device_ops mhi_netdev_ops = { +diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c +index 9a1a5b203624..e470e3398abc 100644 +--- a/drivers/net/netdevsim/netdev.c ++++ b/drivers/net/netdevsim/netdev.c +@@ -67,10 +67,10 @@ nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&ns->syncp); ++ start = u64_stats_fetch_begin(&ns->syncp); + stats->tx_bytes = ns->tx_bytes; + stats->tx_packets = ns->tx_packets; +- } while (u64_stats_fetch_retry_irq(&ns->syncp, start)); ++ } while (u64_stats_fetch_retry(&ns->syncp, start)); + } + + static int +diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c +index 95f07cf7b37c..1c72733f6824 100644 +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -36,7 +36,8 @@ + #include + #include + +-#define PHY_STATE_TIME HZ ++#define PHY_STATE_TIME HZ ++#define PHY_STATE_TIME_MS 100 + + #define PHY_STATE_STR(_state) \ + case PHY_##_state: \ +@@ -1281,7 +1282,7 @@ void phy_state_machine(struct work_struct *work) + */ + mutex_lock(&phydev->lock); + if (phy_polling_mode(phydev) && phy_is_started(phydev)) +- phy_queue_state_machine(phydev, PHY_STATE_TIME); ++ phy_queue_state_machine(phydev, (PHY_STATE_TIME_MS * HZ) / 1000); + mutex_unlock(&phydev->lock); + } + +diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c +index 921ca59822b0..382756c3fb83 100644 +--- a/drivers/net/team/team.c ++++ b/drivers/net/team/team.c +@@ -1866,13 +1866,13 @@ team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) + for_each_possible_cpu(i) { + p = per_cpu_ptr(team->pcpu_stats, i); + do { +- start = u64_stats_fetch_begin_irq(&p->syncp); ++ start = u64_stats_fetch_begin(&p->syncp); + rx_packets = u64_stats_read(&p->rx_packets); + rx_bytes = u64_stats_read(&p->rx_bytes); + rx_multicast = u64_stats_read(&p->rx_multicast); + tx_packets = u64_stats_read(&p->tx_packets); + tx_bytes = u64_stats_read(&p->tx_bytes); +- } while (u64_stats_fetch_retry_irq(&p->syncp, start)); ++ } while (u64_stats_fetch_retry(&p->syncp, start)); + + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; +diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c +index b095a4b4957b..18d99fda997c 100644 +--- a/drivers/net/team/team_mode_loadbalance.c ++++ b/drivers/net/team/team_mode_loadbalance.c +@@ -466,9 +466,9 @@ static void __lb_one_cpu_stats_add(struct lb_stats *acc_stats, + struct lb_stats tmp; + + do { +- start = u64_stats_fetch_begin_irq(syncp); ++ start = u64_stats_fetch_begin(syncp); + tmp.tx_bytes = cpu_stats->tx_bytes; +- } while (u64_stats_fetch_retry_irq(syncp, start)); ++ } while (u64_stats_fetch_retry(syncp, start)); + acc_stats->tx_bytes += tmp.tx_bytes; + } + +diff --git a/drivers/net/veth.c b/drivers/net/veth.c +index 36c5a41f84e4..605f511a886c 100644 +--- a/drivers/net/veth.c ++++ b/drivers/net/veth.c +@@ -182,12 +182,12 @@ static void veth_get_ethtool_stats(struct net_device *dev, + size_t offset; + + do { +- start = u64_stats_fetch_begin_irq(&rq_stats->syncp); ++ start = u64_stats_fetch_begin(&rq_stats->syncp); + for (j = 0; j < VETH_RQ_STATS_LEN; j++) { + offset = veth_rq_stats_desc[j].offset; + data[idx + j] = *(u64 *)(stats_base + offset); + } +- } while (u64_stats_fetch_retry_irq(&rq_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&rq_stats->syncp, start)); + idx += VETH_RQ_STATS_LEN; + } + +@@ -203,12 +203,12 @@ static void veth_get_ethtool_stats(struct net_device *dev, + + tx_idx += (i % dev->real_num_tx_queues) * VETH_TQ_STATS_LEN; + do { +- start = u64_stats_fetch_begin_irq(&rq_stats->syncp); ++ start = u64_stats_fetch_begin(&rq_stats->syncp); + for (j = 0; j < VETH_TQ_STATS_LEN; j++) { + offset = veth_tq_stats_desc[j].offset; + data[tx_idx + j] += *(u64 *)(base + offset); + } +- } while (u64_stats_fetch_retry_irq(&rq_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&rq_stats->syncp, start)); + } + } + +@@ -381,13 +381,13 @@ static void veth_stats_rx(struct veth_stats *result, struct net_device *dev) + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&stats->syncp); ++ start = u64_stats_fetch_begin(&stats->syncp); + peer_tq_xdp_xmit_err = stats->vs.peer_tq_xdp_xmit_err; + xdp_tx_err = stats->vs.xdp_tx_err; + packets = stats->vs.xdp_packets; + bytes = stats->vs.xdp_bytes; + drops = stats->vs.rx_drops; +- } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&stats->syncp, start)); + result->peer_tq_xdp_xmit_err += peer_tq_xdp_xmit_err; + result->xdp_tx_err += xdp_tx_err; + result->xdp_packets += packets; +diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c +index 21d3461fb5d1..666622ae4b9d 100644 +--- a/drivers/net/virtio_net.c ++++ b/drivers/net/virtio_net.c +@@ -2107,18 +2107,18 @@ static void virtnet_stats(struct net_device *dev, + struct send_queue *sq = &vi->sq[i]; + + do { +- start = u64_stats_fetch_begin_irq(&sq->stats.syncp); ++ start = u64_stats_fetch_begin(&sq->stats.syncp); + tpackets = sq->stats.packets; + tbytes = sq->stats.bytes; + terrors = sq->stats.tx_timeouts; +- } while (u64_stats_fetch_retry_irq(&sq->stats.syncp, start)); ++ } while (u64_stats_fetch_retry(&sq->stats.syncp, start)); + + do { +- start = u64_stats_fetch_begin_irq(&rq->stats.syncp); ++ start = u64_stats_fetch_begin(&rq->stats.syncp); + rpackets = rq->stats.packets; + rbytes = rq->stats.bytes; + rdrops = rq->stats.drops; +- } while (u64_stats_fetch_retry_irq(&rq->stats.syncp, start)); ++ } while (u64_stats_fetch_retry(&rq->stats.syncp, start)); + + tot->rx_packets += rpackets; + tot->tx_packets += tpackets; +@@ -2726,12 +2726,12 @@ static void virtnet_get_ethtool_stats(struct net_device *dev, + + stats_base = (u8 *)&rq->stats; + do { +- start = u64_stats_fetch_begin_irq(&rq->stats.syncp); ++ start = u64_stats_fetch_begin(&rq->stats.syncp); + for (j = 0; j < VIRTNET_RQ_STATS_LEN; j++) { + offset = virtnet_rq_stats_desc[j].offset; + data[idx + j] = *(u64 *)(stats_base + offset); + } +- } while (u64_stats_fetch_retry_irq(&rq->stats.syncp, start)); ++ } while (u64_stats_fetch_retry(&rq->stats.syncp, start)); + idx += VIRTNET_RQ_STATS_LEN; + } + +@@ -2740,12 +2740,12 @@ static void virtnet_get_ethtool_stats(struct net_device *dev, + + stats_base = (u8 *)&sq->stats; + do { +- start = u64_stats_fetch_begin_irq(&sq->stats.syncp); ++ start = u64_stats_fetch_begin(&sq->stats.syncp); + for (j = 0; j < VIRTNET_SQ_STATS_LEN; j++) { + offset = virtnet_sq_stats_desc[j].offset; + data[idx + j] = *(u64 *)(stats_base + offset); + } +- } while (u64_stats_fetch_retry_irq(&sq->stats.syncp, start)); ++ } while (u64_stats_fetch_retry(&sq->stats.syncp, start)); + idx += VIRTNET_SQ_STATS_LEN; + } + } +diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c +index f6dcec66f0a4..bdb3a76a352e 100644 +--- a/drivers/net/vrf.c ++++ b/drivers/net/vrf.c +@@ -159,13 +159,13 @@ static void vrf_get_stats64(struct net_device *dev, + + dstats = per_cpu_ptr(dev->dstats, i); + do { +- start = u64_stats_fetch_begin_irq(&dstats->syncp); ++ start = u64_stats_fetch_begin(&dstats->syncp); + tbytes = dstats->tx_bytes; + tpkts = dstats->tx_pkts; + tdrops = dstats->tx_drps; + rbytes = dstats->rx_bytes; + rpkts = dstats->rx_pkts; +- } while (u64_stats_fetch_retry_irq(&dstats->syncp, start)); ++ } while (u64_stats_fetch_retry(&dstats->syncp, start)); + stats->tx_bytes += tbytes; + stats->tx_packets += tpkts; + stats->tx_dropped += tdrops; +diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c +index c5cf55030158..c3ff30ab782e 100644 +--- a/drivers/net/vxlan/vxlan_vnifilter.c ++++ b/drivers/net/vxlan/vxlan_vnifilter.c +@@ -129,9 +129,9 @@ static void vxlan_vnifilter_stats_get(const struct vxlan_vni_node *vninode, + + pstats = per_cpu_ptr(vninode->stats, i); + do { +- start = u64_stats_fetch_begin_irq(&pstats->syncp); ++ start = u64_stats_fetch_begin(&pstats->syncp); + memcpy(&temp, &pstats->stats, sizeof(temp)); +- } while (u64_stats_fetch_retry_irq(&pstats->syncp, start)); ++ } while (u64_stats_fetch_retry(&pstats->syncp, start)); + + dest->rx_packets += temp.rx_packets; + dest->rx_bytes += temp.rx_bytes; +diff --git a/drivers/net/wwan/mhi_wwan_mbim.c b/drivers/net/wwan/mhi_wwan_mbim.c +index ef70bb7c88ad..3f72ae943b29 100644 +--- a/drivers/net/wwan/mhi_wwan_mbim.c ++++ b/drivers/net/wwan/mhi_wwan_mbim.c +@@ -456,19 +456,19 @@ static void mhi_mbim_ndo_get_stats64(struct net_device *ndev, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&link->rx_syncp); ++ start = u64_stats_fetch_begin(&link->rx_syncp); + stats->rx_packets = u64_stats_read(&link->rx_packets); + stats->rx_bytes = u64_stats_read(&link->rx_bytes); + stats->rx_errors = u64_stats_read(&link->rx_errors); +- } while (u64_stats_fetch_retry_irq(&link->rx_syncp, start)); ++ } while (u64_stats_fetch_retry(&link->rx_syncp, start)); + + do { +- start = u64_stats_fetch_begin_irq(&link->tx_syncp); ++ start = u64_stats_fetch_begin(&link->tx_syncp); + stats->tx_packets = u64_stats_read(&link->tx_packets); + stats->tx_bytes = u64_stats_read(&link->tx_bytes); + stats->tx_errors = u64_stats_read(&link->tx_errors); + stats->tx_dropped = u64_stats_read(&link->tx_dropped); +- } while (u64_stats_fetch_retry_irq(&link->tx_syncp, start)); ++ } while (u64_stats_fetch_retry(&link->tx_syncp, start)); + } + + static void mhi_mbim_ul_callback(struct mhi_device *mhi_dev, +diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c +index dc404e05970c..14aec417fa06 100644 +--- a/drivers/net/xen-netfront.c ++++ b/drivers/net/xen-netfront.c +@@ -1392,16 +1392,16 @@ static void xennet_get_stats64(struct net_device *dev, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&tx_stats->syncp); ++ start = u64_stats_fetch_begin(&tx_stats->syncp); + tx_packets = tx_stats->packets; + tx_bytes = tx_stats->bytes; +- } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); + + do { +- start = u64_stats_fetch_begin_irq(&rx_stats->syncp); ++ start = u64_stats_fetch_begin(&rx_stats->syncp); + rx_packets = rx_stats->packets; + rx_bytes = rx_stats->bytes; +- } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); + + tot->rx_packets += rx_packets; + tot->tx_packets += tx_packets; +diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig +index 288c6f1c6979..64d3846b93be 100644 +--- a/drivers/nfc/Kconfig ++++ b/drivers/nfc/Kconfig +@@ -60,6 +60,7 @@ config NFC_VIRTUAL_NCI + + If unsure, say N. + ++source "drivers/nfc/pn5xx/Kconfig" + source "drivers/nfc/fdp/Kconfig" + source "drivers/nfc/pn544/Kconfig" + source "drivers/nfc/pn533/Kconfig" +diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile +index 7b1bfde1d971..62d495c57a15 100644 +--- a/drivers/nfc/Makefile ++++ b/drivers/nfc/Makefile +@@ -3,6 +3,7 @@ + # Makefile for nfc devices + # + ++obj-$(CONFIG_NFC_NXP_PN5XX) += pn5xx/ + obj-$(CONFIG_NFC_FDP) += fdp/ + obj-$(CONFIG_NFC_PN544) += pn544/ + obj-$(CONFIG_NFC_MICROREAD) += microread/ +diff --git a/drivers/nfc/pn5xx/Kconfig b/drivers/nfc/pn5xx/Kconfig +new file mode 100644 +index 000000000000..10db8c403866 +--- /dev/null ++++ b/drivers/nfc/pn5xx/Kconfig +@@ -0,0 +1,12 @@ ++config NFC_NXP_PN5XX ++ tristate "NXP PN5XX based driver" ++ depends on I2C ++ select CRC_CCITT ++ help ++ NXP PN5XX driver based on I2C. ++ This is a driver to provides I2C access to PN5xx and PN7120 NFC ++ Controller devices ++ ++ To compile this driver as a module, choose m here. The module will ++ be called pn5xx_i2c. ++ Say N if unsure. +diff --git a/drivers/nfc/pn5xx/Makefile b/drivers/nfc/pn5xx/Makefile +new file mode 100644 +index 000000000000..b31bbb51423a +--- /dev/null ++++ b/drivers/nfc/pn5xx/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++# ++# Makefile for PN5xx NFC driver ++# ++ ++obj-$(CONFIG_NFC_NXP_PN5XX) += pn5xx_i2c.o +diff --git a/drivers/nfc/pn5xx/README.md b/drivers/nfc/pn5xx/README.md +new file mode 100644 +index 000000000000..c0c8d5c73757 +--- /dev/null ++++ b/drivers/nfc/pn5xx/README.md +@@ -0,0 +1,2 @@ ++# nxp-pn5xx ++NXP's NFC Open Source Kernel mode driver +diff --git a/drivers/nfc/pn5xx/pn5xx_i2c.c b/drivers/nfc/pn5xx/pn5xx_i2c.c +new file mode 100644 +index 000000000000..3511f1441cf6 +--- /dev/null ++++ b/drivers/nfc/pn5xx/pn5xx_i2c.c +@@ -0,0 +1,672 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2010 Trusted Logic S.A. ++ * Copyright 2015,2019-2023 NXP ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "pn5xx_i2c.h" ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define MAX_BUFFER_SIZE 512 ++ ++#define MODE_OFF 0 ++#define MODE_RUN 1 ++#define MODE_FW 2 ++ ++/* pn7120, pn548, pn547 and pn544 are supported */ ++#define CHIP "pn544" ++#define DRIVER_CARD "PN54x NFC" ++#define DRIVER_DESC "NFC driver for PN54x Family" ++ ++#ifndef CONFIG_OF ++#define CONFIG_OF ++#endif ++ ++struct pn54x_dev { ++ wait_queue_head_t read_wq; ++ struct mutex read_mutex; ++ struct i2c_client *client; ++ struct miscdevice pn54x_device; ++ int ven_gpio; ++ int firm_gpio; ++ int irq_gpio; ++ int clkreq_gpio; ++ struct regulator *pvdd_reg; ++ struct regulator *vbat_reg; ++ struct regulator *pmuvcc_reg; ++ struct regulator *sevdd_reg; ++ bool irq_enabled; ++ spinlock_t irq_enabled_lock; ++}; ++ ++static struct task_struct *test_task; ++/* Interrupt control and handler */ ++static void pn54x_disable_irq(struct pn54x_dev *pn54x_dev) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pn54x_dev->irq_enabled_lock, flags); ++ if (pn54x_dev->irq_enabled) { ++ disable_irq_nosync(pn54x_dev->client->irq); ++ pn54x_dev->irq_enabled = false; ++ } ++ spin_unlock_irqrestore(&pn54x_dev->irq_enabled_lock, flags); ++} ++ ++static irqreturn_t pn54x_dev_irq_handler(int irq, void *dev_id) ++{ ++ struct pn54x_dev *pn54x_dev = dev_id; ++ ++ pn54x_disable_irq(pn54x_dev); ++ ++ /* Wake up waiting readers */ ++ wake_up(&pn54x_dev->read_wq); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct pn54x_dev *gpn54x_dev = NULL; ++static int pn54x_dev_loop_handler(void *data) ++{ ++ struct pn54x_dev *pn54x_dev = (struct pn54x_dev *)gpn54x_dev; ++ ++ printk("In pn54x_dev_loop_handler...%x \n", data); ++ while (1) { ++ /* Wake up waiting readers */ ++ wake_up(&pn54x_dev->read_wq); ++ msleep(200); ++ } ++ ++ return 0; ++} ++ ++/* private functions */ ++static int pn544_enable(struct pn54x_dev *dev, int mode) ++{ ++ int r; ++ ++ /* ++ * turn on the regulators ++ * -- if the regulators were specified, they're required ++ */ ++ if (dev->pvdd_reg != NULL) { ++ r = regulator_enable(dev->pvdd_reg); ++ if (r < 0) { ++ pr_err("%s: not able to enable pvdd\n", __func__); ++ return r; ++ } ++ } ++ if (dev->vbat_reg != NULL) { ++ r = regulator_enable(dev->vbat_reg); ++ if (r < 0) { ++ pr_err("%s: not able to enable vbat\n", __func__); ++ goto enable_exit0; ++ } ++ } ++ if (dev->pmuvcc_reg != NULL) { ++ r = regulator_enable(dev->pmuvcc_reg); ++ if (r < 0) { ++ pr_err("%s: not able to enable pmuvcc\n", __func__); ++ goto enable_exit1; ++ } ++ } ++ if (dev->sevdd_reg != NULL) { ++ r = regulator_enable(dev->sevdd_reg); ++ if (r < 0) { ++ pr_err("%s: not able to enable sevdd\n", __func__); ++ goto enable_exit2; ++ } ++ } ++ ++ if (MODE_RUN == mode) { ++ printk("%s power on\n", __func__); ++ if (gpio_is_valid(dev->firm_gpio)) ++ gpio_set_value_cansleep(dev->firm_gpio, 0); ++ msleep(100); ++ } else if (MODE_FW == mode) { ++ /* power on with firmware download (requires hw reset) */ ++ pr_info("%s power on with firmware\n", __func__); ++ msleep(20); ++ if (gpio_is_valid(dev->firm_gpio)) { ++ gpio_set_value(dev->firm_gpio, 1); ++ } else { ++ pr_err("%s Unused Firm GPIO %d\n", __func__, mode); ++ return GPIO_UNUSED; ++ } ++ msleep(20); ++ msleep(100); ++ msleep(20); ++ } else { ++ pr_err("%s bad arg %d\n", __func__, mode); ++ return -EINVAL; ++ } ++ ++ return 0; ++ ++enable_exit2: ++ if (dev->pmuvcc_reg) ++ regulator_disable(dev->pmuvcc_reg); ++enable_exit1: ++ if (dev->vbat_reg) ++ regulator_disable(dev->vbat_reg); ++enable_exit0: ++ if (dev->pvdd_reg) ++ regulator_disable(dev->pvdd_reg); ++ ++ return r; ++} ++ ++static void pn544_disable(struct pn54x_dev *dev) ++{ ++ /* power off */ ++ printk("%s power off\n", __func__); ++ if (gpio_is_valid(dev->firm_gpio)) ++ gpio_set_value_cansleep(dev->firm_gpio, 0); ++ msleep(100); ++ ++ if (dev->sevdd_reg) ++ regulator_disable(dev->sevdd_reg); ++ if (dev->pmuvcc_reg) ++ regulator_disable(dev->pmuvcc_reg); ++ if (dev->vbat_reg) ++ regulator_disable(dev->vbat_reg); ++ if (dev->pvdd_reg) ++ regulator_disable(dev->pvdd_reg); ++} ++ ++/* driver functions */ ++static ssize_t pn54x_dev_read(struct file *filp, char __user *buf, ++ size_t count, loff_t *offset) ++{ ++ struct pn54x_dev *pn54x_dev = filp->private_data; ++ char tmp[MAX_BUFFER_SIZE]; ++ int ret; ++ ++ if (count > MAX_BUFFER_SIZE) ++ count = MAX_BUFFER_SIZE; ++ ++ mutex_lock(&pn54x_dev->read_mutex); ++ ++ /* Read data */ ++ ret = i2c_master_recv(pn54x_dev->client, tmp, count); ++ ++ mutex_unlock(&pn54x_dev->read_mutex); ++ ++ /* ++ * pn54x seems to be slow in handling I2C read requests ++ * so add 1ms delay after recv operation ++ */ ++ udelay(1000); ++ ++ if (ret < 0) ++ return ret; ++ ++ if (ret > count) { ++ pr_err("%s: received too many bytes from i2c (%d)\n", ++ __func__, ret); ++ return -EIO; ++ } ++ if (copy_to_user(buf, tmp, ret)) { ++ pr_err("%s : failed to copy to user space\n", __func__); ++ return -EFAULT; ++ } ++ return ret; ++ ++fail: ++ mutex_unlock(&pn54x_dev->read_mutex); ++ return ret; ++} ++ ++static ssize_t pn54x_dev_write(struct file *filp, const char __user *buf, ++ size_t count, loff_t *offset) ++{ ++ struct pn54x_dev *pn54x_dev; ++ char tmp[MAX_BUFFER_SIZE]; ++ int ret; ++ ++ pn54x_dev = filp->private_data; ++ ++ if (count > MAX_BUFFER_SIZE) ++ count = MAX_BUFFER_SIZE; ++ ++ if (copy_from_user(tmp, buf, count)) { ++ pr_err("%s : failed to copy from user space\n", __func__); ++ return -EFAULT; ++ } ++ ++ pr_debug("%s : writing %zu bytes.\n", __func__, count); ++ /* Write data */ ++ ret = i2c_master_send(pn54x_dev->client, tmp, count); ++ if (ret != count) { ++ pr_err("%s : i2c_master_send returned %d\n", __func__, ret); ++ pr_err("I2C addr is 0x%02X, name is %s\n", ++ pn54x_dev->client->addr, pn54x_dev->client->name); ++ ret = -EIO; ++ } ++ ++ /* ++ * pn54x seems to be slow in handling I2C write requests ++ * so add 1ms delay after I2C send oparation ++ */ ++ udelay(1000); ++ ++ return ret; ++} ++ ++static int pn54x_dev_open(struct inode *inode, struct file *filp) ++{ ++ struct pn54x_dev *pn54x_dev = container_of(filp->private_data, ++ struct pn54x_dev, ++ pn54x_device); ++ ++ filp->private_data = pn54x_dev; ++ ++ pr_info("%s : %d,%d\n", __func__, imajor(inode), iminor(inode)); ++ ++ return 0; ++} ++ ++static int pn54x_dev_release(struct inode *inode, struct file *filp) ++{ ++ pr_info("%s : closing %d,%d\n", __func__, imajor(inode), iminor(inode)); ++ ++ return 0; ++} ++ ++static long pn54x_dev_ioctl(struct file *filp, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct pn54x_dev *pn54x_dev = filp->private_data; ++ ++ pr_info("%s, cmd=%d, arg=%lu\n", __func__, cmd, arg); ++ switch (cmd) { ++ case PN544_SET_PWR: ++ if (arg == 2) { ++ /* power on w/FW */ ++ if (pn544_enable(pn54x_dev, arg) == GPIO_UNUSED) ++ return GPIO_UNUSED; ++ } else if (arg == 1) { ++ /* power on */ ++ pn544_enable(pn54x_dev, arg); ++ } else if (arg == 0) { ++ /* power off */ ++ pn544_disable(pn54x_dev); ++ } else { ++ pr_err("%s bad SET_PWR arg %lu\n", __func__, arg); ++ return -EINVAL; ++ } ++ break; ++ case PN54X_CLK_REQ: ++ if (arg == 1) { ++ if (gpio_is_valid(pn54x_dev->clkreq_gpio)) { ++ gpio_set_value(pn54x_dev->clkreq_gpio, 1); ++ } else { ++ pr_err("%s Unused Clkreq GPIO %lu\n", ++ __func__, arg); ++ return GPIO_UNUSED; ++ } ++ } else if (arg == 0) { ++ if (gpio_is_valid(pn54x_dev->clkreq_gpio)) { ++ gpio_set_value(pn54x_dev->clkreq_gpio, 0); ++ } else { ++ pr_err("%s Unused Clkreq GPIO %lu\n", ++ __func__, arg); ++ return GPIO_UNUSED; ++ } ++ } else { ++ pr_err("%s bad CLK_REQ arg %lu\n", __func__, arg); ++ return -EINVAL; ++ } ++ break; ++ default: ++ pr_err("%s bad ioctl %u\n", __func__, cmd); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct file_operations pn54x_dev_fops = { ++ .owner = THIS_MODULE, ++ .llseek = no_llseek, ++ .read = pn54x_dev_read, ++ .write = pn54x_dev_write, ++ .open = pn54x_dev_open, ++ .release = pn54x_dev_release, ++ .unlocked_ioctl = pn54x_dev_ioctl, ++}; ++ ++/* Handlers for alternative sources of platform_data */ ++#ifdef CONFIG_OF ++/* Translate OpenFirmware node properties into platform_data */ ++static int pn54x_get_pdata(struct device *dev, ++ struct pn544_i2c_platform_data *pdata) ++{ ++ struct device_node *node; ++ u32 flags; ++ int val; ++ ++ /* make sure there is actually a device tree node */ ++ node = dev->of_node; ++ if (!node) ++ return -ENODEV; ++ ++ memset(pdata, 0, sizeof(*pdata)); ++ ++ /* ++ * read the dev tree data ++ * firm pin - controls firmware download - OPTIONAL ++ */ ++ val = of_get_named_gpio_flags(node, "firmware-gpios", 0, &flags); ++ if (val >= 0) { ++ pdata->firm_gpio = val; ++ } else { ++ pdata->firm_gpio = GPIO_UNUSED; ++ dev_warn(dev, "FIRM GPIO error getting from OF node\n"); ++ } ++ ++ /* clkreq pin - controls the clock to the PN547 - OPTIONAL */ ++ val = of_get_named_gpio_flags(node, "nxp,pn54x-clkreq", 0, &flags); ++ if (val >= 0) { ++ pdata->clkreq_gpio = val; ++ } else { ++ pdata->clkreq_gpio = GPIO_UNUSED; ++ dev_warn(dev, ++ "CLKREQ GPIO error getting from OF node\n"); ++ } ++ ++ /* ++ * handle the regulator lines - these are optional ++ * PVdd - pad Vdd (544, 547) ++ * Vbat - Battery (544, 547) ++ * PMUVcc - UICC Power (544, 547) ++ * SEVdd - SE Power (544) ++ * ++ * Will attempt to load a matching Regulator Resource for each ++ * If no resource is provided, then the input will not be controlled ++ * Example: if only PVdd is provided, it is the only one that will be ++ * turned on/off. ++ */ ++ pdata->pvdd_reg = regulator_get(dev, "nxp,pn54x-pvdd"); ++ if (IS_ERR(pdata->pvdd_reg)) { ++ pr_err("%s: could not get nxp,pn54x-pvdd, rc=%ld\n", ++ __func__, PTR_ERR(pdata->pvdd_reg)); ++ pdata->pvdd_reg = NULL; ++ } ++ ++ pdata->vbat_reg = regulator_get(dev, "nxp,pn54x-vbat"); ++ if (IS_ERR(pdata->vbat_reg)) { ++ pr_err("%s: could not get nxp,pn54x-vbat, rc=%ld\n", ++ __func__, PTR_ERR(pdata->vbat_reg)); ++ pdata->vbat_reg = NULL; ++ } ++ ++ pdata->pmuvcc_reg = regulator_get(dev, "nxp,pn54x-pmuvcc"); ++ if (IS_ERR(pdata->pmuvcc_reg)) { ++ pr_err("%s: could not get nxp,pn54x-pmuvcc, rc=%ld\n", ++ __func__, PTR_ERR(pdata->pmuvcc_reg)); ++ pdata->pmuvcc_reg = NULL; ++ } ++ ++ pdata->sevdd_reg = regulator_get(dev, "nxp,pn54x-sevdd"); ++ if (IS_ERR(pdata->sevdd_reg)) { ++ pr_err("%s: could not get nxp,pn54x-sevdd, rc=%ld\n", ++ __func__, PTR_ERR(pdata->sevdd_reg)); ++ pdata->sevdd_reg = NULL; ++ } ++ ++ return 0; ++} ++#else ++static int pn54x_get_pdata(struct device *dev, ++ struct pn544_i2c_platform_data *pdata) ++{ ++ pdata = dev->platform_data; ++ return 0; ++} ++#endif ++ ++/* pn54x_probe */ ++#ifdef KERNEL_3_4_AND_OLDER ++static int __devinit pn54x_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++#else ++static int pn54x_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++#endif ++{ ++ int ret; ++ /* gpio values, from board file or DT */ ++ struct pn544_i2c_platform_data *pdata; ++ struct pn544_i2c_platform_data tmp_pdata; ++ /* internal device specific data */ ++ struct pn54x_dev *pn54x_dev; ++ ++ pr_info("%s\n", __func__); ++ ++ /* ++ * ---- retrieve the platform data ---- ++ * If the dev.platform_data is NULL, then ++ * attempt to read from the device tree ++ */ ++ if (!client->dev.platform_data) { ++ ret = pn54x_get_pdata(&(client->dev), &tmp_pdata); ++ if (ret) ++ return ret; ++ ++ pdata = &tmp_pdata; ++ } else { ++ pdata = client->dev.platform_data; ++ } ++ ++ if (pdata == NULL) { ++ pr_err("%s : nfc probe fail\n", __func__); ++ return -ENODEV; ++ } ++ ++ /* validate the the adapter has basic I2C functionality */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ pr_err("%s : need I2C_FUNC_I2C\n", __func__); ++ return -ENODEV; ++ } ++ ++ if (gpio_is_valid(pdata->firm_gpio)) { ++ pr_info("%s: request firm_gpio %d\n", ++ __func__, pdata->firm_gpio); ++ ret = gpio_request(pdata->firm_gpio, "nfc_firm"); ++ if (ret) { ++ pr_err("%s :not able to get GPIO firm_gpio\n", ++ __func__); ++ goto err_firm; ++ } ++ } ++ ++ if (gpio_is_valid(pdata->clkreq_gpio)) { ++ pr_info("%s: request clkreq_gpio %d\n", ++ __func__, pdata->clkreq_gpio); ++ ret = gpio_request(pdata->clkreq_gpio, "nfc_clkreq"); ++ if (ret) { ++ pr_err("%s :not able to get GPIO clkreq_gpio\n", ++ __func__); ++ goto err_clkreq; ++ } ++ } ++ ++ /* allocate the pn54x driver information structure */ ++ pn54x_dev = kzalloc(sizeof(*pn54x_dev), GFP_KERNEL); ++ if (pn54x_dev == NULL) { ++ dev_err(&client->dev, ++ "failed to allocate memory for module data\n"); ++ ret = -ENOMEM; ++ goto err_exit; ++ } ++ ++ /* store the platform data in the driver info struct */ ++ pn54x_dev->firm_gpio = pdata->firm_gpio; ++ pn54x_dev->clkreq_gpio = pdata->clkreq_gpio; ++ pn54x_dev->pvdd_reg = pdata->pvdd_reg; ++ pn54x_dev->vbat_reg = pdata->vbat_reg; ++ pn54x_dev->pmuvcc_reg = pdata->pmuvcc_reg; ++ pn54x_dev->sevdd_reg = pdata->sevdd_reg; ++ ++ pn54x_dev->client = client; ++ ++ if (gpio_is_valid(pn54x_dev->firm_gpio)) { ++ ret = gpio_direction_output(pn54x_dev->firm_gpio, 0); ++ if (ret < 0) { ++ pr_err("%s : not able to set firm_gpio as output\n", ++ __func__); ++ goto err_exit; ++ } ++ } ++ ++ if (gpio_is_valid(pn54x_dev->clkreq_gpio)) { ++ ret = gpio_direction_output(pn54x_dev->clkreq_gpio, 0); ++ if (ret < 0) { ++ pr_err("%s : not able to set clkreq_gpio as output\n", ++ __func__); ++ goto err_exit; ++ } ++ } ++ ++ /* init mutex and queues */ ++ init_waitqueue_head(&pn54x_dev->read_wq); ++ mutex_init(&pn54x_dev->read_mutex); ++ spin_lock_init(&pn54x_dev->irq_enabled_lock); ++ ++ /* register as a misc device - character based with one entry point */ ++ pn54x_dev->pn54x_device.minor = MISC_DYNAMIC_MINOR; ++ pn54x_dev->pn54x_device.name = CHIP; ++ pn54x_dev->pn54x_device.fops = &pn54x_dev_fops; ++ ret = misc_register(&pn54x_dev->pn54x_device); ++ if (ret) { ++ pr_err("%s : misc_register failed\n", __FILE__); ++ goto err_misc_register; ++ } ++ ++ /* ++ * request irq. the irq is set whenever the chip has data available ++ * for reading. it is cleared when all data has been read. ++ */ ++ i2c_set_clientdata(client, pn54x_dev); ++ ++ return 0; ++ ++err_request_irq_failed: ++ misc_deregister(&pn54x_dev->pn54x_device); ++err_misc_register: ++err_exit: ++ if (gpio_is_valid(pdata->clkreq_gpio)) ++ gpio_free(pdata->clkreq_gpio); ++err_clkreq: ++ if (gpio_is_valid(pdata->firm_gpio)) ++ gpio_free(pdata->firm_gpio); ++err_firm: ++err_ven: ++ return ret; ++} ++ ++#ifdef KERNEL_3_4_AND_OLDER ++static int __devexit pn54x_remove(struct i2c_client *client) ++#else ++static void pn54x_remove(struct i2c_client *client) ++#endif ++{ ++ struct pn54x_dev *pn54x_dev; ++ ++ pr_info("%s\n", __func__); ++ ++ pn54x_dev = i2c_get_clientdata(client); ++ free_irq(client->irq, pn54x_dev); ++ misc_deregister(&pn54x_dev->pn54x_device); ++ mutex_destroy(&pn54x_dev->read_mutex); ++ if (gpio_is_valid(pn54x_dev->firm_gpio)) ++ gpio_free(pn54x_dev->firm_gpio); ++ if (gpio_is_valid(pn54x_dev->clkreq_gpio)) ++ gpio_free(pn54x_dev->clkreq_gpio); ++ regulator_put(pn54x_dev->pvdd_reg); ++ regulator_put(pn54x_dev->vbat_reg); ++ regulator_put(pn54x_dev->pmuvcc_reg); ++ regulator_put(pn54x_dev->sevdd_reg); ++ ++ kfree(pn54x_dev); ++ ++#ifdef KERNEL_3_4_AND_OLDER ++ return 0; ++#else ++ return; ++#endif ++} ++ ++#ifdef CONFIG_OF ++static struct of_device_id pn54x_dt_match[] = { ++ { .compatible = "nxp,pn547", }, ++ { .compatible = "nxp,pn544", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, pn54x_dt_match); ++#endif ++ ++static const struct i2c_device_id pn54x_id[] = { ++ { "pn547", 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(i2c, pn54x_id); ++ ++static struct i2c_driver pn54x_driver = { ++ .id_table = pn54x_id, ++ .probe = pn54x_probe, ++#ifdef KERNEL_3_4_AND_OLDER ++ .remove = __devexit_p(pn54x_remove), ++#else ++ .remove = pn54x_remove, ++#endif ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "pn544", ++ .of_match_table = pn54x_dt_match, ++ }, ++}; ++ ++/* module load/unload record keeping */ ++static int __init pn54x_dev_init(void) ++{ ++ pr_info("%s\n", __func__); ++ return i2c_add_driver(&pn54x_driver); ++} ++ ++static void __exit pn54x_dev_exit(void) ++{ ++ pr_info("%s\n", __func__); ++ i2c_del_driver(&pn54x_driver); ++} ++ ++module_init(pn54x_dev_init); ++module_exit(pn54x_dev_exit); ++ ++MODULE_AUTHOR("Sylvain Fonteneau"); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/nfc/pn5xx/pn5xx_i2c.h b/drivers/nfc/pn5xx/pn5xx_i2c.h +new file mode 100644 +index 000000000000..4b53655007e0 +--- /dev/null ++++ b/drivers/nfc/pn5xx/pn5xx_i2c.h +@@ -0,0 +1,38 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2010 Trusted Logic S.A. ++ * Copyright 2015,2019-2023 NXP ++ * ++ */ ++ ++#define PN544_MAGIC 0xE9 ++ ++/* ++ * PN544 power control via ioctl ++ * PN544_SET_PWR(0): power off ++ * PN544_SET_PWR(1): power on ++ * PN544_SET_PWR(2): reset and power on with firmware download enabled ++ */ ++ ++#define PWR_OFF 0 ++#define PWR_ON 1 ++#define PWR_FW 2 ++ ++#define CLK_OFF 0 ++#define CLK_ON 1 ++ ++#define GPIO_UNUSED -1 ++ ++#define PN544_SET_PWR _IOW(PN544_MAGIC, 0x01, unsigned int) ++#define PN54X_CLK_REQ _IOW(PN544_MAGIC, 0x02, unsigned int) ++ ++struct pn544_i2c_platform_data { ++ unsigned int irq_gpio; ++ unsigned int ven_gpio; ++ unsigned int firm_gpio; ++ unsigned int clkreq_gpio; ++ struct regulator *pvdd_reg; ++ struct regulator *vbat_reg; ++ struct regulator *pmuvcc_reg; ++ struct regulator *sevdd_reg; ++}; +diff --git a/drivers/nfc/pn5xx/sample_devicetree.txt b/drivers/nfc/pn5xx/sample_devicetree.txt +new file mode 100644 +index 000000000000..0e81959052fc +--- /dev/null ++++ b/drivers/nfc/pn5xx/sample_devicetree.txt +@@ -0,0 +1,17 @@ ++Example: ++ ++&i2c{ ++ ++ status = "okay"; ++ ++ pn547: pn547@29 { ++ ++ compatible = "nxp,pn547"; ++ ++ reg = <0x29>; ++ clock-frequency = <400000>; ++ ++ interrupt-gpios = <&gpio2 17 0>; ++ enable-gpios = <&gpio4 21 0>; ++ }; ++}; +diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig +index afba55506632..968ddd1fac18 100644 +--- a/drivers/rpmsg/Kconfig ++++ b/drivers/rpmsg/Kconfig +@@ -81,10 +81,17 @@ config RPMSG_VIRTIO + select RPMSG_NS + select VIRTIO + ++config RPMSG_PERF ++ tristate "RPMSG performance test driver" ++ depends on RPMSG ++ default m ++ help ++ Say Y here to enable the RPMSG performance test driver. ++ + config HAVE_IMX_RPMSG + bool "IMX RPMSG driver on the AMP SOCs" + default y +- depends on IMX_MBOX ++ depends on IMX_MBOX || GENERIC_SOFTWARE_MAILBOX + select RPMSG_VIRTIO + help + Say y here to enable support for the iMX Rpmsg Driver providing +@@ -118,4 +125,11 @@ config IMX_RPMSG_TTY + executed by the command "echo > ", thus + remote M core would receive the string. + ++config RPMSG_8M_BUF ++ bool "RPMSG 8M bytes buffer support" ++ default n ++ depends on HAVE_IMX_RPMSG ++ help ++ Say y here to enable support for 8M bytes Vring buffer for rpmsg. ++ + endmenu +diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile +index 7c4ee52e92c4..f64fa4d0ada2 100644 +--- a/drivers/rpmsg/Makefile ++++ b/drivers/rpmsg/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_RPMSG) += rpmsg_core.o + obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o ++obj-$(CONFIG_RPMSG_PERF) += rpmsg_perf.o + obj-$(CONFIG_RPMSG_CTRL) += rpmsg_ctrl.o + obj-$(CONFIG_RPMSG_NS) += rpmsg_ns.o + obj-$(CONFIG_RPMSG_MTK_SCP) += mtk_rpmsg.o +diff --git a/drivers/rpmsg/imx_rpmsg.c b/drivers/rpmsg/imx_rpmsg.c +index 8d4ecd7360fd..b4277d5cfe37 100644 +--- a/drivers/rpmsg/imx_rpmsg.c ++++ b/drivers/rpmsg/imx_rpmsg.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /* +- * Copyright 2019 NXP ++ * Copyright 2019,2023 NXP + */ + + #include +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -163,8 +164,12 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, + return ERR_PTR(-ENOMEM); + + /* ioremap'ing normal memory, so we cast away sparse's complaints */ +- rpvq->addr = (__force void *) ioremap(virdev->vring[index], +- RPMSG_RING_SIZE); ++ if (of_dma_is_coherent(dev->of_node)) ++ rpvq->addr = (__force void *)ioremap_cache(virdev->vring[index], ++ RPMSG_RING_SIZE); ++ else ++ rpvq->addr = (__force void *)ioremap(virdev->vring[index], ++ RPMSG_RING_SIZE); + if (!rpvq->addr) { + err = -ENOMEM; + goto free_rpvq; +@@ -466,6 +471,13 @@ static int imx_rpmsg_rxdb_channel_init(struct imx_rpmsg_vproc *rpdev) + return ret; + } + ++static int imx_rpmsg_rxdb_channel_deinit(struct imx_rpmsg_vproc *rpdev) ++{ ++ mbox_free_channel(rpdev->rxdb_ch); ++ ++ return 0; ++} ++ + static void imx_rpmsg_rx_callback(struct mbox_client *c, void *msg) + { + int buf_space; +@@ -531,6 +543,14 @@ static int imx_rpmsg_xtr_channel_init(struct imx_rpmsg_vproc *rpdev) + return ret; + } + ++static int imx_rpmsg_xtr_channel_deinit(struct imx_rpmsg_vproc *rpdev) ++{ ++ mbox_free_channel(rpdev->tx_ch); ++ mbox_free_channel(rpdev->rx_ch); ++ ++ return 0; ++} ++ + static int imx_rpmsg_probe(struct platform_device *pdev) + { + int j, ret = 0; +@@ -653,6 +673,36 @@ static int imx_rpmsg_probe(struct platform_device *pdev) + return ret; + } + ++static int imx_rpmsg_remove(struct platform_device *pdev) ++{ ++ struct imx_rpmsg_vproc *rpdev = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ int i; ++ ++#ifdef CONFIG_IMX_SCU ++ if (rpdev->variant == IMX8QXP || rpdev->variant == IMX8QM) { ++ imx_scu_irq_unregister_notifier(&rpdev->proc_nb); ++ imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_REBOOTED, ++ BIT(rpdev->mub_partition), false); ++ } ++#endif ++ imx_rpmsg_rxdb_channel_deinit(rpdev); ++ ++ for (i = 0; i < rpdev->vdev_nums; i++) { ++ unregister_virtio_device(&rpdev->ivdev[i]->vdev); ++ kfree(rpdev->ivdev[i]); ++ } ++ ++ if (rpdev->flags & SPECIFIC_DMA_POOL) ++ of_reserved_mem_device_release(dev); ++ ++ imx_rpmsg_xtr_channel_deinit(rpdev); ++ ++ cancel_delayed_work_sync(&rpdev->rpmsg_work); ++ ++ return 0; ++} ++ + static struct platform_driver imx_rpmsg_driver = { + .driver = { + .owner = THIS_MODULE, +@@ -660,6 +710,7 @@ static struct platform_driver imx_rpmsg_driver = { + .of_match_table = imx_rpmsg_dt_ids, + }, + .probe = imx_rpmsg_probe, ++ .remove = imx_rpmsg_remove, + }; + + static int __init imx_rpmsg_init(void) +diff --git a/drivers/rpmsg/imx_rpmsg_tty.c b/drivers/rpmsg/imx_rpmsg_tty.c +index 6114c686c388..ab3e64cc1a06 100644 +--- a/drivers/rpmsg/imx_rpmsg_tty.c ++++ b/drivers/rpmsg/imx_rpmsg_tty.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /* +- * Copyright 2019 NXP ++ * Copyright 2019,2023 NXP + */ + + #include +@@ -13,8 +13,24 @@ + #include + #include + ++#ifdef CONFIG_RPMSG_8M_BUF ++struct rpmsg_hdr { ++ __rpmsg32 src; ++ __rpmsg32 dst; ++ __rpmsg32 reserved; ++ __rpmsg16 len; ++ __rpmsg16 flags; ++ u8 data[]; ++} __packed; ++ ++#define MAX_RPMSG_BUF_SIZE (512 * 2) ++/* this needs to be less then (RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) */ ++#define RPMSG_MAX_SIZE (MAX_RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) ++#else + /* this needs to be less then (RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) */ + #define RPMSG_MAX_SIZE 256 ++#endif ++ + #define MSG "hello world!" + + /* +diff --git a/drivers/rpmsg/rpmsg_perf.c b/drivers/rpmsg/rpmsg_perf.c +new file mode 100644 +index 000000000000..f58b15b2ea8d +--- /dev/null ++++ b/drivers/rpmsg/rpmsg_perf.c +@@ -0,0 +1,545 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rpmsg_internal.h" ++ ++#define RPMSG_PERF_AS_SENDER_IOCTL _IO(0xb5, 0x5) ++#define RPMSG_PERF_AS_RECEIVER_IOCTL _IO(0xb5, 0x6) ++#define RPMSG_PERF_AS_RECEIVER_END_ACK_IOCTL _IO(0xb5, 0x7) ++#define RPMSG_PERF_GET_RUNNING_STA_IOCTL _IO(0xb5, 0x8) ++ ++#define RPMSG_DEV_MAX (MINORMASK + 1) ++ ++static dev_t rpmsg_major; ++ ++static DEFINE_IDA(rpmsg_perf_minor_ida); ++ ++#define dev_to_eptdev(dev) container_of(dev, struct rpmsg_eptdev, dev) ++#define cdev_to_eptdev(i_cdev) container_of(i_cdev, struct rpmsg_eptdev, cdev) ++ ++struct packet_header { ++ uint32_t preamble; ++ bool no_copy; ++ uint32_t packet_size; ++ uint32_t packet_cnt; ++ uint32_t test_time; /* unit: second */ ++}; ++ ++enum { ++ RPMSG_PERF_PREAMBLE_SENDER_START = 0xBECAACEA, ++ RPMSG_PERF_PREAMBLE_SENDER_END = 0xBECAACEB, ++ RPMSG_PERF_PREAMBLE_SENDER_END_ACK = 0xBECAACEC, ++ RPMSG_PERF_PREAMBLE_RECEIVER_START = 0xBECAACED, ++ RPMSG_PERF_PREAMBLE_RECEIVER_END = 0xBECAACEE, ++ RPMSG_PERF_PREAMBLE_RECEIVER_END_ACK = 0xBECAACEF, ++}; ++ ++enum dev_state { ++ RPMSG_DEV_IDLE, ++ RPMSG_DEV_SENDING, ++ RPMSG_DEV_RECEIVING, ++}; ++ ++struct test_statistic { ++ uint32_t recv_packet_cnt; ++ uint32_t send_packet_cnt; ++ uint32_t packet_size; ++ uint32_t test_time; ++}; ++ ++/** ++ * struct rpmsg_eptdev - endpoint device context ++ * @dev: endpoint device ++ * @cdev: cdev for the endpoint device ++ * @rpdev: underlaying rpmsg device ++ * @chinfo: info used to open the endpoint ++ * @ept_lock: synchronization of @ept modifications ++ * @ept: rpmsg endpoint reference, when open ++ * @default_ept: set to channel default endpoint if the default endpoint should be re-used ++ * on device open to prevent endpoint address update. ++ */ ++struct rpmsg_eptdev { ++ struct device dev; ++ struct cdev cdev; ++ ++ struct rpmsg_device *rpdev; ++ struct rpmsg_channel_info chinfo; ++ ++ struct mutex ept_lock; ++ struct rpmsg_endpoint *ept; ++ struct rpmsg_endpoint *default_ept; ++ ++ enum dev_state state; ++ struct test_statistic statistic; ++ ++ struct packet_header param; ++}; ++ ++static int rpmsg_perf_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, ++ void *priv, u32 addr) ++{ ++ struct packet_header *hdr = (struct packet_header *)buf; ++ struct rpmsg_eptdev *eptdev = priv; ++ struct test_statistic *statistic = &eptdev->statistic; ++ uint32_t rate; ++ ++ switch (hdr->preamble) { ++ case RPMSG_PERF_PREAMBLE_SENDER_END_ACK: ++ if (eptdev->state == RPMSG_DEV_SENDING) { ++ eptdev->state = RPMSG_DEV_IDLE; ++ rate = statistic->send_packet_cnt / ++ statistic->test_time / 1000; ++ pr_info("packet size: %u, sent packets: %u, time: %u s, rate: %u kpps\n", ++ statistic->packet_size, ++ statistic->send_packet_cnt, ++ statistic->test_time, rate); ++ statistic->packet_size = 0; ++ statistic->send_packet_cnt = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ statistic->recv_packet_cnt++; ++ ++ return 0; ++} ++ ++static int rpmsg_perf_eptdev_open(struct inode *inode, struct file *filp) ++{ ++ struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); ++ struct rpmsg_endpoint *ept; ++ struct rpmsg_device *rpdev = eptdev->rpdev; ++ struct device *dev = &eptdev->dev; ++ ++ mutex_lock(&eptdev->ept_lock); ++ if (eptdev->ept) { ++ mutex_unlock(&eptdev->ept_lock); ++ return -EBUSY; ++ } ++ ++ get_device(dev); ++ ++ /* ++ * If the default_ept is set, the rpmsg device default endpoint is used. ++ * Else a new endpoint is created on open that will be destroyed on release. ++ */ ++ if (eptdev->default_ept) ++ ept = eptdev->default_ept; ++ else ++ ept = rpmsg_create_ept(rpdev, rpmsg_perf_ept_cb, eptdev, eptdev->chinfo); ++ ++ if (!ept) { ++ dev_err(dev, "failed to open %s\n", eptdev->chinfo.name); ++ put_device(dev); ++ mutex_unlock(&eptdev->ept_lock); ++ return -EINVAL; ++ } ++ ++ eptdev->ept = ept; ++ filp->private_data = eptdev; ++ mutex_unlock(&eptdev->ept_lock); ++ ++ return 0; ++} ++ ++static int rpmsg_perf_eptdev_release(struct inode *inode, struct file *filp) ++{ ++ struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); ++ struct device *dev = &eptdev->dev; ++ ++ /* Close the endpoint, if it's not already destroyed by the parent */ ++ mutex_lock(&eptdev->ept_lock); ++ if (eptdev->ept) { ++ if (!eptdev->default_ept) ++ rpmsg_destroy_ept(eptdev->ept); ++ eptdev->ept = NULL; ++ } ++ mutex_unlock(&eptdev->ept_lock); ++ ++ put_device(dev); ++ ++ return 0; ++} ++ ++static int rpmsg_perf_sender_thread(void *p) ++{ ++ struct rpmsg_eptdev *eptdev = (struct rpmsg_eptdev *)p; ++ struct packet_header hdr = eptdev->param; ++ unsigned long timeout; ++ uint8_t *data; ++ uint32_t packet_len; ++ int ret; ++ ++ hdr.preamble = RPMSG_PERF_PREAMBLE_SENDER_START; ++ packet_len = hdr.packet_size; ++ eptdev->statistic.send_packet_cnt = 0; ++ ++ ret = rpmsg_sendto(eptdev->ept, &hdr, sizeof(hdr), eptdev->chinfo.dst); ++ if (ret) { ++ pr_err("failed to send packet header\n"); ++ return ret; ++ } ++ ++ /* Prepare data packets */ ++ data = kmalloc(packet_len, GFP_KERNEL); ++ if (data == NULL) { ++ pr_err("allocate data buffer failure\n"); ++ return -ENOMEM; ++ } ++ memset(data, 0, packet_len); ++ ++ udelay(100); ++ timeout = jiffies + msecs_to_jiffies(hdr.test_time * 1000); ++ ++ do { ++ do { ++ ret = rpmsg_trysendto(eptdev->ept, data, packet_len, ++ eptdev->chinfo.dst); ++ } while (ret != 0); ++ eptdev->statistic.send_packet_cnt++; ++ } while (time_before(jiffies, timeout)); ++ ++ memset(&hdr, 0, sizeof(hdr)); ++ hdr.preamble = RPMSG_PERF_PREAMBLE_SENDER_END; ++ ++ ret = rpmsg_sendto(eptdev->ept, &hdr, sizeof(hdr), eptdev->chinfo.dst); ++ if (ret) { ++ pr_err("failed to send RPMSG_PERF_PREAMBLE_SENDER_END packet\n"); ++ } ++ ++ kfree(data); ++ ++ return ret; ++} ++ ++static long rpmsg_perf_eptdev_ioctl(struct file *fp, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct rpmsg_eptdev *eptdev = fp->private_data; ++ struct test_statistic *statistic = &eptdev->statistic; ++ struct task_struct *sender_thread_h; ++ struct packet_header hdr = {0}; ++ uint32_t packet_cnt; ++ uint32_t rate; ++ uint32_t status; ++ long ret; ++ ++ switch (cmd) { ++ case RPMSG_PERF_GET_RUNNING_STA_IOCTL: ++ status = eptdev->state; ++ if (copy_to_user((char __user *)arg, &status, sizeof(status))) { ++ pr_err("copy_to_user() failed\n"); ++ return -EFAULT; ++ } ++ break; ++ case RPMSG_PERF_AS_SENDER_IOCTL: ++ if (eptdev->state != RPMSG_DEV_IDLE) ++ return -EINVAL; ++ ++ eptdev->state = RPMSG_DEV_SENDING; ++ ret = copy_from_user((void *)&hdr, (const void __user *)arg, ++ sizeof(struct packet_header)); ++ if (ret) { ++ pr_err("copy_from_user() failed\n"); ++ return -EFAULT; ++ } ++ ++ eptdev->param.no_copy = hdr.no_copy; ++ eptdev->param.packet_size = hdr.packet_size; ++ eptdev->param.test_time = hdr.test_time; ++ statistic->packet_size = hdr.packet_size; ++ statistic->test_time = hdr.test_time; ++ ++ sender_thread_h = kthread_run(rpmsg_perf_sender_thread, ++ eptdev, "rpmsg_sender"); ++ if (IS_ERR(sender_thread_h)) { ++ pr_err("failed to create sender thread\n"); ++ return -EFAULT; ++ } ++ break; ++ case RPMSG_PERF_AS_RECEIVER_IOCTL: ++ if (eptdev->state != RPMSG_DEV_IDLE) ++ return -EINVAL; ++ ++ eptdev->state = RPMSG_DEV_RECEIVING; ++ ret = copy_from_user((void *)&hdr, (const void __user *)arg, ++ sizeof(struct packet_header)); ++ if (ret) { ++ pr_err("copy_from_user() failed\n"); ++ return -EFAULT; ++ } ++ hdr.preamble = RPMSG_PERF_PREAMBLE_RECEIVER_START; ++ statistic->test_time = hdr.test_time; ++ statistic->packet_size = hdr.packet_size; ++ statistic->recv_packet_cnt = 0; ++ ++ ret = rpmsg_sendto(eptdev->ept, &hdr, sizeof(hdr), ++ eptdev->chinfo.dst); ++ if (ret) ++ pr_err("failed to send RPMSG_PERF_PREAMBLE_RECEIVER_START packet\n"); ++ break; ++ case RPMSG_PERF_AS_RECEIVER_END_ACK_IOCTL: ++ if (eptdev->state != RPMSG_DEV_RECEIVING) ++ return -EINVAL; ++ ++ packet_cnt = statistic->recv_packet_cnt - 1; ++ rate = packet_cnt / statistic->test_time / 1000; ++ pr_info("packet size: %u, received packets: %u, time: %u s, rate: %u kpps\n", ++ statistic->packet_size, packet_cnt, ++ statistic->test_time, rate); ++ ++ hdr.preamble = RPMSG_PERF_PREAMBLE_RECEIVER_END_ACK; ++ hdr.packet_cnt = packet_cnt; ++ ret = rpmsg_sendto(eptdev->ept, &hdr, sizeof(hdr), ++ eptdev->chinfo.dst); ++ if (ret) { ++ pr_err("failed to send RPMSG_PERF_PREAMBLE_RECEIVER_END_ACK packet\n"); ++ return ret; ++ } ++ eptdev->state = RPMSG_DEV_IDLE; ++ statistic->test_time = 0; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static const struct file_operations rpmsg_perf_eptdev_fops = { ++ .owner = THIS_MODULE, ++ .open = rpmsg_perf_eptdev_open, ++ .release = rpmsg_perf_eptdev_release, ++ .unlocked_ioctl = rpmsg_perf_eptdev_ioctl, ++ .compat_ioctl = compat_ptr_ioctl, ++}; ++ ++static ssize_t name_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%s\n", eptdev->chinfo.name); ++} ++static DEVICE_ATTR_RO(name); ++ ++static ssize_t src_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", eptdev->chinfo.src); ++} ++static DEVICE_ATTR_RO(src); ++ ++static ssize_t dst_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", eptdev->chinfo.dst); ++} ++static DEVICE_ATTR_RO(dst); ++ ++static struct attribute *rpmsg_perf_eptdev_attrs[] = { ++ &dev_attr_name.attr, ++ &dev_attr_src.attr, ++ &dev_attr_dst.attr, ++ NULL ++}; ++ATTRIBUTE_GROUPS(rpmsg_perf_eptdev); ++ ++static void rpmsg_eptdev_release_device(struct device *dev) ++{ ++ struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev); ++ ++ ida_simple_remove(&rpmsg_perf_minor_ida, MINOR(eptdev->dev.devt)); ++ kfree(eptdev); ++} ++ ++static struct rpmsg_eptdev *rpmsg_perf_eptdev_alloc(struct rpmsg_device *rpdev, ++ struct device *parent) ++{ ++ struct rpmsg_eptdev *eptdev; ++ struct device *dev; ++ ++ eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL); ++ if (!eptdev) ++ return ERR_PTR(-ENOMEM); ++ ++ eptdev->state = RPMSG_DEV_IDLE; ++ ++ dev = &eptdev->dev; ++ eptdev->rpdev = rpdev; ++ ++ mutex_init(&eptdev->ept_lock); ++ ++ device_initialize(dev); ++ dev->class = rpmsg_class; ++ dev->parent = parent; ++ dev->groups = rpmsg_perf_eptdev_groups; ++ dev_set_drvdata(dev, eptdev); ++ ++ cdev_init(&eptdev->cdev, &rpmsg_perf_eptdev_fops); ++ eptdev->cdev.owner = THIS_MODULE; ++ ++ return eptdev; ++} ++ ++static int rpmsg_perf_eptdev_add(struct rpmsg_eptdev *eptdev, ++ struct rpmsg_channel_info chinfo) ++{ ++ struct device *dev = &eptdev->dev; ++ int dst = eptdev->rpdev->dst; ++ int ret; ++ ++ eptdev->chinfo = chinfo; ++ ++ ret = ida_simple_get(&rpmsg_perf_minor_ida, 0, ++ RPMSG_DEV_MAX, GFP_KERNEL); ++ if (ret < 0) ++ goto free_eptdev; ++ dev->devt = MKDEV(MAJOR(rpmsg_major), ret); ++ ++ dev->id = dst; ++ dev_set_name(dev, "rpmsg-perf%d", dst); ++ ++ ret = cdev_device_add(&eptdev->cdev, &eptdev->dev); ++ if (ret) ++ goto free_minor_ida; ++ ++ /* We can now rely on the release function for cleanup */ ++ dev->release = rpmsg_eptdev_release_device; ++ ++ return ret; ++ ++free_minor_ida: ++ ida_simple_remove(&rpmsg_perf_minor_ida, MINOR(dev->devt)); ++free_eptdev: ++ put_device(dev); ++ kfree(eptdev); ++ ++ return ret; ++} ++ ++static int rpmsg_perf_probe(struct rpmsg_device *rpdev) ++{ ++ struct rpmsg_channel_info chinfo; ++ struct rpmsg_eptdev *eptdev; ++ struct device *dev = &rpdev->dev; ++ ++ memcpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); ++ chinfo.src = rpdev->src; ++ chinfo.dst = rpdev->dst; ++ ++ eptdev = rpmsg_perf_eptdev_alloc(rpdev, dev); ++ if (IS_ERR(eptdev)) ++ return PTR_ERR(eptdev); ++ ++ /* Set the default_ept to the rpmsg device endpoint */ ++ eptdev->default_ept = rpdev->ept; ++ ++ /* ++ * The rpmsg_perf_ept_cb uses *priv parameter to get its ++ * rpmsg_eptdev context. Stored it in default_ept *priv field. ++ */ ++ eptdev->default_ept->priv = eptdev; ++ ++ return rpmsg_perf_eptdev_add(eptdev, chinfo); ++} ++ ++static int rpmsg_perf_eptdev_chrdev_destroy(struct device *dev, void *data) ++{ ++ struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev); ++ ++ mutex_lock(&eptdev->ept_lock); ++ if (eptdev->ept) { ++ /* The default endpoint is released by the rpmsg core */ ++ if (!eptdev->default_ept) ++ rpmsg_destroy_ept(eptdev->ept); ++ eptdev->ept = NULL; ++ } ++ mutex_unlock(&eptdev->ept_lock); ++ ++ cdev_device_del(&eptdev->cdev, &eptdev->dev); ++ put_device(&eptdev->dev); ++ ++ return 0; ++} ++ ++static void rpmsg_perf_remove(struct rpmsg_device *rpdev) ++{ ++ int ret; ++ ++ ret = device_for_each_child(&rpdev->dev, NULL, ++ rpmsg_perf_eptdev_chrdev_destroy); ++ if (ret) ++ dev_warn(&rpdev->dev, "failed to destroy endpoints: %d\n", ret); ++} ++ ++static struct rpmsg_device_id rpmsg_perf_id_table[] = { ++ { .name = "rpmsg-perf" }, ++ { }, ++}; ++ ++static struct rpmsg_driver rpmsg_perf_driver = { ++ .probe = rpmsg_perf_probe, ++ .remove = rpmsg_perf_remove, ++ .callback = rpmsg_perf_ept_cb, ++ .id_table = rpmsg_perf_id_table, ++ .drv.name = "rpmsg_perf", ++}; ++ ++static int rpmsg_perf_init(void) ++{ ++ int ret; ++ ++ ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg_perf"); ++ if (ret < 0) { ++ pr_err("failed to allocate char dev region\n"); ++ return ret; ++ } ++ ++ ret = register_rpmsg_driver(&rpmsg_perf_driver); ++ if (ret < 0) { ++ pr_err("rpmsg: failed to register rpmsg raw driver\n"); ++ goto free_region; ++ } ++ ++ return 0; ++ ++free_region: ++ unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); ++ ++ return ret; ++} ++postcore_initcall(rpmsg_perf_init); ++ ++static void rpmsg_perf_exit(void) ++{ ++ unregister_rpmsg_driver(&rpmsg_perf_driver); ++ unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); ++} ++module_exit(rpmsg_perf_exit); ++ ++MODULE_ALIAS("rpmsg:rpmsg_perf"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c +index 905ac7910c98..d9f341bfd299 100644 +--- a/drivers/rpmsg/virtio_rpmsg_bus.c ++++ b/drivers/rpmsg/virtio_rpmsg_bus.c +@@ -127,8 +127,13 @@ struct virtio_rpmsg_channel { + * can change this without changing anything in the firmware of the remote + * processor. + */ ++#ifdef CONFIG_RPMSG_8M_BUF ++#define MAX_RPMSG_NUM_BUFS (1024 * 8) ++#define MAX_RPMSG_BUF_SIZE (512 * 2) ++#else + #define MAX_RPMSG_NUM_BUFS (512) + #define MAX_RPMSG_BUF_SIZE (512) ++#endif + + /* + * Local addresses are dynamically allocated on-demand. +diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig +index 36d99d7a183c..cd0e5bdd37f6 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -1850,6 +1850,7 @@ config RTC_DRV_IMX_SC + config RTC_DRV_IMX_RPMSG + tristate "NXP RPMSG RTC support" + depends on OF ++ depends on RPMSG + help + If you say yes here you get support for the NXP RPMSG + RTC module. +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index 151fef199c38..825412e799cd 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -127,10 +127,10 @@ do { \ + unsigned int start; \ + pcpu_stats = per_cpu_ptr(in, i); \ + do { \ +- start = u64_stats_fetch_begin_irq( \ ++ start = u64_stats_fetch_begin( \ + &pcpu_stats->syncp); \ + inc = u64_stats_read(&pcpu_stats->field); \ +- } while (u64_stats_fetch_retry_irq( \ ++ } while (u64_stats_fetch_retry( \ + &pcpu_stats->syncp, start)); \ + ret += inc; \ + } \ +diff --git a/drivers/staging/fsl_qbman/qman_driver.c b/drivers/staging/fsl_qbman/qman_driver.c +index 14671df9e4ce..135235595521 100644 +--- a/drivers/staging/fsl_qbman/qman_driver.c ++++ b/drivers/staging/fsl_qbman/qman_driver.c +@@ -1,4 +1,5 @@ + /* Copyright 2008-2012 Freescale Semiconductor, Inc. ++ * Copyright 2019-2023 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: +@@ -722,6 +723,50 @@ static int qman_online_cpu(unsigned int cpu) + + #endif /* CONFIG_HOTPLUG_CPU */ + ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++__init void qman_ethercat_portal_init(int cpu) ++{ ++ struct qm_portal_config *pcfg; ++ struct qman_portal *p; ++ ++ pcfg = get_pcfg(&unused_pcfgs); ++ if (pcfg) { ++ pcfg->public_cfg.cpu = cpu; ++ pcfg->public_cfg.is_shared = 0; ++ ++ pcfg->iommu_domain = NULL; ++ portal_set_cpu(pcfg, pcfg->public_cfg.cpu); ++ p = qman_create_affine_portal_ethercat(pcfg, NULL, cpu); ++ if (p) { ++ pr_info("Qman portal %sinitialised, cpu %d\n", ++ pcfg->public_cfg.is_shared ? "(shared) " : "", ++ pcfg->public_cfg.cpu); ++ } else { ++ pr_crit("Qman portal failure on cpu %d\n", ++ pcfg->public_cfg.cpu); ++ } ++ return; ++ } ++} ++ ++static u32 qman_affine_last_cpu; ++ ++u32 qman_get_affine_last_cpu(void) ++{ ++ return qman_affine_last_cpu; ++} ++ ++__init void qman_ethercat_portal_init_on_cpu(void) ++{ ++ int cpu = 0; ++ ++ for_each_online_cpu(cpu) { ++ qman_affine_last_cpu = cpu; ++ qman_ethercat_portal_init(cpu); ++ } ++} ++#endif ++ + __init int qman_init(void) + { + struct cpumask slave_cpus; +@@ -877,6 +922,11 @@ __init int qman_init(void) + return ret; + } + #endif ++ ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++ qman_ethercat_portal_init_on_cpu(); ++#endif ++ + return 0; + } + +diff --git a/drivers/staging/fsl_qbman/qman_high.c b/drivers/staging/fsl_qbman/qman_high.c +index 961177fa681e..15a38ed75a2b 100644 +--- a/drivers/staging/fsl_qbman/qman_high.c ++++ b/drivers/staging/fsl_qbman/qman_high.c +@@ -1,4 +1,5 @@ + /* Copyright 2008-2012 Freescale Semiconductor, Inc. ++ * Copyright 2019-2023 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: +@@ -776,6 +777,29 @@ struct qman_portal *qman_create_portal( + return NULL; + } + ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++static struct qman_portal ethercat_portal[NR_CPUS]; ++static u16 ethercat_channel[NR_CPUS]; ++static DEFINE_SPINLOCK(ethercat_mask_lock); ++ ++struct qman_portal *qman_create_affine_portal_ethercat ++ (const struct qm_portal_config *config, ++ const struct qman_cgrs *cgrs, int cpu) ++{ ++ struct qman_portal *res; ++ struct qman_portal *portal = NULL; ++ ++ portal = ðercat_portal[cpu]; ++ res = qman_create_portal(portal, config, cgrs); ++ if (res) { ++ spin_lock(ðercat_mask_lock); ++ ethercat_channel[cpu] = config->public_cfg.channel; ++ spin_unlock(ðercat_mask_lock); ++ } ++ return res; ++} ++#endif ++ + struct qman_portal *qman_create_affine_portal( + const struct qm_portal_config *config, + const struct qman_cgrs *cgrs) +@@ -1412,6 +1436,20 @@ void *qman_get_affine_portal(int cpu) + } + EXPORT_SYMBOL(qman_get_affine_portal); + ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++u16 qman_affine_channel_ethercat(int cpu) ++{ ++ return ethercat_channel[cpu]; ++} ++EXPORT_SYMBOL(qman_affine_channel_ethercat); ++ ++void *qman_get_affine_portal_ethercat(int cpu) ++{ ++ return ðercat_portal[cpu]; ++} ++EXPORT_SYMBOL(qman_get_affine_portal_ethercat); ++#endif ++ + int qman_p_poll_dqrr(struct qman_portal *p, unsigned int limit) + { + int ret; +diff --git a/drivers/staging/fsl_qbman/qman_private.h b/drivers/staging/fsl_qbman/qman_private.h +index 4ba7bd5b0bb2..47a06dc35e5a 100644 +--- a/drivers/staging/fsl_qbman/qman_private.h ++++ b/drivers/staging/fsl_qbman/qman_private.h +@@ -1,4 +1,5 @@ + /* Copyright 2008-2012 Freescale Semiconductor, Inc. ++ * Copyright 2019-2023 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: +@@ -232,6 +233,11 @@ struct qman_portal *qman_create_portal( + const struct qm_portal_config *config, + const struct qman_cgrs *cgrs); + ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++struct qman_portal *qman_create_affine_portal_ethercat ++ (const struct qm_portal_config *config, ++ const struct qman_cgrs *cgrs, int cpu); ++#endif + struct qman_portal *qman_create_affine_portal( + const struct qm_portal_config *config, + const struct qman_cgrs *cgrs); +diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig +index cc30ff93e2e4..ccfbd6187e4a 100644 +--- a/drivers/tty/Kconfig ++++ b/drivers/tty/Kconfig +@@ -382,4 +382,6 @@ config RPMSG_TTY + + endif # TTY + ++ ++ + source "drivers/tty/serdev/Kconfig" +diff --git a/drivers/tty/rpmsg_tty.c b/drivers/tty/rpmsg_tty.c +index 29db413bbc03..a5d44406b566 100644 +--- a/drivers/tty/rpmsg_tty.c ++++ b/drivers/tty/rpmsg_tty.c +@@ -1,13 +1,14 @@ + // SPDX-License-Identifier: GPL-2.0 + /* + * Copyright (C) 2021 STMicroelectronics - All Rights Reserved ++ * Copyright 2023 NXP + * + * The rpmsg tty driver implements serial communication on the RPMsg bus to makes + * possible for user-space programs to send and receive rpmsg messages as a standard + * tty protocol. + * +- * The remote processor can instantiate a new tty by requesting a "rpmsg-tty" RPMsg service. +- * The "rpmsg-tty" service is directly used for data exchange. No flow control is implemented yet. ++ * The remote processor can instantiate a new tty by requesting a "srtm-uart-channel" or "rpmsg-tty" RPMsg service. ++ * The "srtm-uart-channel" or "rpmsg-tty" service is directly used for data exchange. No flow control is implemented yet. + */ + + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +@@ -17,6 +18,43 @@ + #include + #include + #include ++#include ++#include ++ ++/* ++ * The srtm (simplified real time message) protocol for uart: ++ * ++ * +---------------+-------------------------------+ ++ * | Byte Offset | Content | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 0 | Category | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 1 ~ 2 | Version | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 3 | Type | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 4 | Command | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 5 | Priority | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 6 | Reserved1 | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 7 | Reserved2 | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 8 | Reserved3 | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 9 | Reserved4 | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 10 | UART BUS ID | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 11 | reserved/ret code | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 12 ~ 13 | Flags | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * | 14 ~ 495 | Data | ++ * +---------------+---+---+---+---+---+---+---+---+ ++ * UART BUS ID: real uart instance, such as: the soc has UART0(UART BUS ID = 0), UART1(UART BUS ID = 1), UART2(UART BUS ID = 2), ... , UARTx(UART BUS ID = x) ++ */ + + #define RPMSG_TTY_NAME "ttyRPMSG" + #define MAX_TTY_RPMSG 32 +@@ -29,18 +67,72 @@ static struct tty_driver *rpmsg_tty_driver; + struct rpmsg_tty_port { + struct tty_port port; /* TTY port data */ + int id; /* TTY rpmsg index */ ++ int bus_id; /* used for srtm uart protocol, which real uart will be used, such as, uart0, uart1, uart2, uart3... */ ++ int flags; /* used for srtm uart protocol */ ++ u16 srtm_uart_msg_data_max_sz; /* used for srtm uart protocol */ ++ bool use_srtm_uart_protocol; /* used for srtm uart protocol */ + struct rpmsg_device *rpdev; /* rpmsg device */ + }; + ++struct srtm_uart_msg_header { ++ struct imx_srtm_head common; ++ u8 bus_id; /* The bus_id is used when send data from acore to mcore; The bus_id is useless when acore received data that from mcore*/ ++ union { ++ u8 reserved; /* used in request packet */ ++ u8 retCode; /* used in response packet */ ++ }; ++ u16 flags; ++} __packed __aligned[1]; ++ ++struct srtm_uart_msg { ++ struct srtm_uart_msg_header header; ++ /* srtm uart Payload Start */ ++ u8 data[1]; ++} __packed __aligned(1); ++ ++struct imx_srtm_uart_data_structure ++{ ++ bool use_srtm_uart_protocol; ++}; ++ ++const static struct imx_srtm_uart_data_structure imx_srtm_uart_data = { ++ .use_srtm_uart_protocol = true, ++}; ++ ++const static struct imx_srtm_uart_data_structure rpmsg_tty_data = { ++ .use_srtm_uart_protocol = false, ++}; ++ + static int rpmsg_tty_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) + { + struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); ++ struct srtm_uart_msg *msg = (struct srtm_uart_msg *)data; + int copied; ++ u8 *payload = data; ++ int payload_len = len; + +- if (!len) ++ if (!payload_len) + return -EINVAL; +- copied = tty_insert_flip_string(&cport->port, data, len); +- if (copied != len) ++ if (cport->use_srtm_uart_protocol) { ++ if (payload_len < (sizeof(struct srtm_uart_msg))) ++ return -EINVAL; ++ payload_len -= (sizeof(struct srtm_uart_msg) - 1); /* 1: msg->data[0] */ ++ ++ if (msg->header.common.type != IMX_SRTM_TYPE_RESPONSE && msg->header.common.type != IMX_SRTM_TYPE_NOTIFY) { ++ return -EINVAL; ++ } ++ ++ if (payload_len > cport->srtm_uart_msg_data_max_sz) { ++ dev_err(&rpdev->dev, ++ "%s failed: data length greater than %d, len=%d\n", ++ __func__, cport->srtm_uart_msg_data_max_sz, payload_len); ++ return -EINVAL; ++ } ++ payload = &msg->data[0]; ++ } ++ ++ copied = tty_insert_flip_string(&cport->port, payload, payload_len); ++ if (copied != payload_len) + dev_err_ratelimited(&rpdev->dev, "Trunc buffer: available space is %d\n", copied); + tty_flip_buffer_push(&cport->port); + +@@ -77,27 +169,52 @@ static int rpmsg_tty_write(struct tty_struct *tty, const u8 *buf, int len) + { + struct rpmsg_tty_port *cport = tty->driver_data; + struct rpmsg_device *rpdev; +- int msg_max_size, msg_size; ++ int msg_max_size, msg_size = -1; ++ struct srtm_uart_msg *msg = NULL; + int ret; ++ void *data = NULL; ++ int data_len = 0; + + rpdev = cport->rpdev; + +- msg_max_size = rpmsg_get_mtu(rpdev->ept); ++ msg_max_size = cport->use_srtm_uart_protocol ? (cport->srtm_uart_msg_data_max_sz) : (rpmsg_get_mtu(rpdev->ept)); + if (msg_max_size < 0) + return msg_max_size; + + msg_size = min(len, msg_max_size); +- ++ if (cport->use_srtm_uart_protocol) { ++ data_len = msg_size + sizeof(struct srtm_uart_msg) - 1; /* srtm uart msg header + data len */ ++ msg = kmalloc(data_len, GFP_KERNEL); ++ if (!msg) ++ return -ENOMEM; ++ ++ memset(msg, 0, data_len); ++ msg->header.common.cate = IMX_SRTM_CATEGORY_UART; ++ msg->header.common.major = IMX_SRTM_VER_UART; ++ msg->header.common.minor = IMX_SRTM_VER_UART >> 8; ++ msg->header.common.type = IMX_SRTM_TYPE_NOTIFY; ++ msg->header.common.cmd = IMX_SRTM_UART_COMMAND_SEND; ++ msg->header.common.reserved[0] = IMX_SRTM_UART_PRIORITY; ++ msg->header.bus_id = cport->bus_id & 0xFF; ++ msg->header.flags = cport->flags & 0xFFFF; ++ memcpy(&msg->data[0], buf, msg_size); ++ data = (void *)msg; ++ } else { ++ data = (void *)buf; ++ data_len = msg_size; ++ } + /* + * Use rpmsg_trysend instead of rpmsg_send to send the message so the caller is not + * hung until a rpmsg buffer is available. In such case rpmsg_trysend returns -ENOMEM. + */ +- ret = rpmsg_trysend(rpdev->ept, (void *)buf, msg_size); ++ ret = rpmsg_trysend(rpdev->ept, data, data_len); + if (ret) { + dev_dbg_ratelimited(&rpdev->dev, "rpmsg_send failed: %d\n", ret); +- return ret; ++ msg_size = ret; + } + ++ kfree(msg); ++ + return msg_size; + } + +@@ -105,8 +222,10 @@ static unsigned int rpmsg_tty_write_room(struct tty_struct *tty) + { + struct rpmsg_tty_port *cport = tty->driver_data; + int size; ++ struct rpmsg_device *rpdev; + +- size = rpmsg_get_mtu(cport->rpdev->ept); ++ rpdev = cport->rpdev; ++ size = cport->use_srtm_uart_protocol ? (cport->srtm_uart_msg_data_max_sz) : (rpmsg_get_mtu(rpdev->ept)); + if (size < 0) + return 0; + +@@ -172,7 +291,12 @@ static int rpmsg_tty_probe(struct rpmsg_device *rpdev) + struct rpmsg_tty_port *cport; + struct device *dev = &rpdev->dev; + struct device *tty_dev; ++ struct device_node *np; ++ struct imx_srtm_uart_data_structure *srtm_uart_data = NULL; ++ struct srtm_uart_msg *msg = NULL; + int ret; ++ int data_len = 0; ++ char buf[64]; + + cport = rpmsg_tty_alloc_cport(); + if (IS_ERR(cport)) +@@ -181,6 +305,23 @@ static int rpmsg_tty_probe(struct rpmsg_device *rpdev) + tty_port_init(&cport->port); + cport->port.ops = &rpmsg_tty_port_ops; + ++ cport->rpdev = rpdev; ++ ++ srtm_uart_data = (struct imx_srtm_uart_data_structure *)rpdev->id.driver_data; ++ if (srtm_uart_data && srtm_uart_data->use_srtm_uart_protocol == true) { ++ cport->bus_id = 0xFF; /* mcore directly print the data that received from acore */ ++ cport->use_srtm_uart_protocol = true; ++ snprintf(buf, sizeof(buf), "uart-rpbus-%d", cport->id); ++ np = of_find_node_by_name(NULL, buf); ++ if (np && of_device_is_compatible(np, "fsl,uart-rpbus") && of_device_is_available(np)) { ++ of_property_read_u32(np, "bus_id", &cport->bus_id); /* mcore will use the id as uart instance, then write data to the real uart instance */ ++ of_property_read_u32(np, "flags", &cport->flags); ++ } ++ cport->srtm_uart_msg_data_max_sz = rpmsg_get_mtu(rpdev->ept) - (sizeof(struct srtm_uart_msg) - 1); /* 1: data[1] of struct srtm_uart_msg */ ++ } else { ++ cport->use_srtm_uart_protocol = false; ++ } ++ + tty_dev = tty_port_register_device(&cport->port, rpmsg_tty_driver, + cport->id, dev); + if (IS_ERR(tty_dev)) { +@@ -189,9 +330,28 @@ static int rpmsg_tty_probe(struct rpmsg_device *rpdev) + return ret; + } + +- cport->rpdev = rpdev; +- +- dev_set_drvdata(dev, cport); ++ dev_set_drvdata(dev, (void *)cport); ++ ++ /* Say hello to remote to acknowleage each other */ ++ if (cport->use_srtm_uart_protocol) { ++ data_len = sizeof(struct srtm_uart_msg); /* srtm uart msg header */ ++ msg = kmalloc(data_len, GFP_KERNEL); ++ if (!msg) ++ return -ENOMEM; ++ ++ memset(msg, 0, data_len); ++ msg->header.common.cate = IMX_SRTM_CATEGORY_UART; ++ msg->header.common.major = IMX_SRTM_VER_UART; ++ msg->header.common.minor = IMX_SRTM_VER_UART >> 8; ++ msg->header.common.type = IMX_SRTM_TYPE_NOTIFY; ++ msg->header.common.cmd = IMX_SRTM_UART_COMMAND_HELLO; ++ msg->header.common.reserved[0] = IMX_SRTM_UART_PRIORITY; ++ msg->header.bus_id = cport->bus_id & 0xFF; ++ msg->header.flags = cport->flags & 0xFFFF; ++ ++ rpmsg_send(rpdev->ept, (void *)msg, data_len); ++ kfree(msg); ++ } + + dev_dbg(dev, "New channel: 0x%x -> 0x%x: " RPMSG_TTY_NAME "%d\n", + rpdev->src, rpdev->dst, cport->id); +@@ -214,7 +374,8 @@ static void rpmsg_tty_remove(struct rpmsg_device *rpdev) + } + + static struct rpmsg_device_id rpmsg_driver_tty_id_table[] = { +- { .name = "rpmsg-tty" }, ++ { .name = "srtm-uart-channel", .driver_data = (kernel_ulong_t)&imx_srtm_uart_data, }, ++ { .name = "rpmsg-tty", .driver_data = (kernel_ulong_t)&rpmsg_tty_data, }, + { }, + }; + MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_tty_id_table); +@@ -283,5 +444,6 @@ module_init(rpmsg_tty_init); + module_exit(rpmsg_tty_exit); + + MODULE_AUTHOR("Arnaud Pouliquen "); ++MODULE_AUTHOR("Biwen Li "); + MODULE_DESCRIPTION("remote processor messaging tty driver"); + MODULE_LICENSE("GPL v2"); +diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h +index eeb7b43ebe53..b17715d340c3 100644 +--- a/drivers/tty/serial/8250/8250.h ++++ b/drivers/tty/serial/8250/8250.h +@@ -176,12 +176,49 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value) + up->dl_write(up, value); + } + ++static inline int serial8250_in_IER(struct uart_8250_port *up) ++{ ++ struct uart_port *port = &up->port; ++ unsigned long flags; ++ bool is_console; ++ int ier; ++ ++ is_console = uart_console(port); ++ ++ if (is_console) ++ printk_cpu_sync_get_irqsave(flags); ++ ++ ier = serial_in(up, UART_IER); ++ ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(flags); ++ ++ return ier; ++} ++ ++static inline void serial8250_set_IER(struct uart_8250_port *up, int ier) ++{ ++ struct uart_port *port = &up->port; ++ unsigned long flags; ++ bool is_console; ++ ++ is_console = uart_console(port); ++ ++ if (is_console) ++ printk_cpu_sync_get_irqsave(flags); ++ ++ serial_out(up, UART_IER, ier); ++ ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(flags); ++} ++ + static inline bool serial8250_set_THRI(struct uart_8250_port *up) + { + if (up->ier & UART_IER_THRI) + return false; + up->ier |= UART_IER_THRI; +- serial_out(up, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + return true; + } + +@@ -190,7 +227,7 @@ static inline bool serial8250_clear_THRI(struct uart_8250_port *up) + if (!(up->ier & UART_IER_THRI)) + return false; + up->ier &= ~UART_IER_THRI; +- serial_out(up, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + return true; + } + +diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c +index 9d2a7856784f..7cc6b527c088 100644 +--- a/drivers/tty/serial/8250/8250_aspeed_vuart.c ++++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c +@@ -278,7 +278,7 @@ static void __aspeed_vuart_set_throttle(struct uart_8250_port *up, + up->ier &= ~irqs; + if (!throttle) + up->ier |= irqs; +- serial_out(up, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + } + static void aspeed_vuart_set_throttle(struct uart_port *port, bool throttle) + { +diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c +index ffc7f67e27e3..8b211e668bc0 100644 +--- a/drivers/tty/serial/8250/8250_bcm7271.c ++++ b/drivers/tty/serial/8250/8250_bcm7271.c +@@ -609,7 +609,7 @@ static int brcmuart_startup(struct uart_port *port) + * will handle this. + */ + up->ier &= ~UART_IER_RDI; +- serial_port_out(port, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + + priv->tx_running = false; + priv->dma.rx_dma = NULL; +@@ -775,10 +775,12 @@ static int brcmuart_handle_irq(struct uart_port *p) + unsigned int iir = serial_port_in(p, UART_IIR); + struct brcmuart_priv *priv = p->private_data; + struct uart_8250_port *up = up_to_u8250p(p); ++ unsigned long cs_flags; + unsigned int status; + unsigned long flags; + unsigned int ier; + unsigned int mcr; ++ bool is_console; + int handled = 0; + + /* +@@ -789,6 +791,10 @@ static int brcmuart_handle_irq(struct uart_port *p) + spin_lock_irqsave(&p->lock, flags); + status = serial_port_in(p, UART_LSR); + if ((status & UART_LSR_DR) == 0) { ++ is_console = uart_console(p); ++ ++ if (is_console) ++ printk_cpu_sync_get_irqsave(cs_flags); + + ier = serial_port_in(p, UART_IER); + /* +@@ -809,6 +815,9 @@ static int brcmuart_handle_irq(struct uart_port *p) + serial_port_in(p, UART_RX); + } + ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(cs_flags); ++ + handled = 1; + } + spin_unlock_irqrestore(&p->lock, flags); +@@ -823,8 +832,10 @@ static enum hrtimer_restart brcmuart_hrtimer_func(struct hrtimer *t) + struct brcmuart_priv *priv = container_of(t, struct brcmuart_priv, hrt); + struct uart_port *p = priv->up; + struct uart_8250_port *up = up_to_u8250p(p); ++ unsigned long cs_flags; + unsigned int status; + unsigned long flags; ++ bool is_console; + + if (priv->shutdown) + return HRTIMER_NORESTART; +@@ -846,12 +857,20 @@ static enum hrtimer_restart brcmuart_hrtimer_func(struct hrtimer *t) + /* re-enable receive unless upper layer has disabled it */ + if ((up->ier & (UART_IER_RLSI | UART_IER_RDI)) == + (UART_IER_RLSI | UART_IER_RDI)) { ++ is_console = uart_console(p); ++ ++ if (is_console) ++ printk_cpu_sync_get_irqsave(cs_flags); ++ + status = serial_port_in(p, UART_IER); + status |= (UART_IER_RLSI | UART_IER_RDI); + serial_port_out(p, UART_IER, status); + status = serial_port_in(p, UART_MCR); + status |= UART_MCR_RTS; + serial_port_out(p, UART_MCR, status); ++ ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(cs_flags); + } + spin_unlock_irqrestore(&p->lock, flags); + return HRTIMER_NORESTART; +diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c +index 81a5dab1a828..536f639ff56c 100644 +--- a/drivers/tty/serial/8250/8250_core.c ++++ b/drivers/tty/serial/8250/8250_core.c +@@ -255,8 +255,11 @@ static void serial8250_timeout(struct timer_list *t) + static void serial8250_backup_timeout(struct timer_list *t) + { + struct uart_8250_port *up = from_timer(up, t, timer); ++ struct uart_port *port = &up->port; + unsigned int iir, ier = 0, lsr; ++ unsigned long cs_flags; + unsigned long flags; ++ bool is_console; + + spin_lock_irqsave(&up->port.lock, flags); + +@@ -265,8 +268,16 @@ static void serial8250_backup_timeout(struct timer_list *t) + * based handler. + */ + if (up->port.irq) { ++ is_console = uart_console(port); ++ ++ if (is_console) ++ printk_cpu_sync_get_irqsave(cs_flags); ++ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); ++ ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(cs_flags); + } + + iir = serial_in(up, UART_IIR); +@@ -289,7 +300,7 @@ static void serial8250_backup_timeout(struct timer_list *t) + serial8250_tx_chars(up); + + if (up->port.irq) +- serial_out(up, UART_IER, ier); ++ serial8250_set_IER(up, ier); + + spin_unlock_irqrestore(&up->port.lock, flags); + +@@ -575,6 +586,14 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) + + #ifdef CONFIG_SERIAL_8250_CONSOLE + ++static void univ8250_console_write_atomic(struct console *co, const char *s, ++ unsigned int count) ++{ ++ struct uart_8250_port *up = &serial8250_ports[co->index]; ++ ++ serial8250_console_write_atomic(up, s, count); ++} ++ + static void univ8250_console_write(struct console *co, const char *s, + unsigned int count) + { +@@ -668,6 +687,7 @@ static int univ8250_console_match(struct console *co, char *name, int idx, + + static struct console univ8250_console = { + .name = "ttyS", ++ .write_atomic = univ8250_console_write_atomic, + .write = univ8250_console_write, + .device = uart_console_device, + .setup = univ8250_console_setup, +@@ -961,7 +981,7 @@ static void serial_8250_overrun_backoff_work(struct work_struct *work) + spin_lock_irqsave(&port->lock, flags); + up->ier |= UART_IER_RLSI | UART_IER_RDI; + up->port.read_status_mask |= UART_LSR_DR; +- serial_out(up, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + spin_unlock_irqrestore(&port->lock, flags); + } + +diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c +index b406cba10b0e..246c32c75a4c 100644 +--- a/drivers/tty/serial/8250/8250_exar.c ++++ b/drivers/tty/serial/8250/8250_exar.c +@@ -189,6 +189,8 @@ static void xr17v35x_set_divisor(struct uart_port *p, unsigned int baud, + + static int xr17v35x_startup(struct uart_port *port) + { ++ struct uart_8250_port *up = up_to_u8250p(port); ++ + /* + * First enable access to IER [7:5], ISR [5:4], FCR [5:4], + * MCR [7:5] and MSR [7:0] +@@ -199,7 +201,7 @@ static int xr17v35x_startup(struct uart_port *port) + * Make sure all interrups are masked until initialization is + * complete and the FIFOs are cleared + */ +- serial_port_out(port, UART_IER, 0); ++ serial8250_set_IER(up, 0); + + return serial8250_do_startup(port); + } +diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c +index 8adfaa183f77..eaf148245a10 100644 +--- a/drivers/tty/serial/8250/8250_fsl.c ++++ b/drivers/tty/serial/8250/8250_fsl.c +@@ -58,7 +58,8 @@ int fsl8250_handle_irq(struct uart_port *port) + if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) { + unsigned long delay; + +- up->ier = port->serial_in(port, UART_IER); ++ up->ier = serial8250_in_IER(up); ++ + if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { + port->ops->stop_rx(port); + } else { +diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c +index 2b2f5d8d24b9..2b78e6c394fb 100644 +--- a/drivers/tty/serial/8250/8250_ingenic.c ++++ b/drivers/tty/serial/8250/8250_ingenic.c +@@ -146,6 +146,7 @@ OF_EARLYCON_DECLARE(x1000_uart, "ingenic,x1000-uart", + + static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) + { ++ struct uart_8250_port *up = up_to_u8250p(p); + int ier; + + switch (offset) { +@@ -167,7 +168,7 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) + * If we have enabled modem status IRQs we should enable + * modem mode. + */ +- ier = p->serial_in(p, UART_IER); ++ ier = serial8250_in_IER(up); + + if (ier & UART_IER_MSI) + value |= UART_MCR_MDCE | UART_MCR_FCM; +diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c +index fb1d5ec0940e..3e7203909d6a 100644 +--- a/drivers/tty/serial/8250/8250_mtk.c ++++ b/drivers/tty/serial/8250/8250_mtk.c +@@ -222,12 +222,40 @@ static void mtk8250_shutdown(struct uart_port *port) + + static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask) + { +- serial_out(up, UART_IER, serial_in(up, UART_IER) & (~mask)); ++ struct uart_port *port = &up->port; ++ unsigned long flags; ++ bool is_console; ++ int ier; ++ ++ is_console = uart_console(port); ++ ++ if (is_console) ++ printk_cpu_sync_get_irqsave(flags); ++ ++ ier = serial_in(up, UART_IER); ++ serial_out(up, UART_IER, ier & (~mask)); ++ ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(flags); + } + + static void mtk8250_enable_intrs(struct uart_8250_port *up, int mask) + { +- serial_out(up, UART_IER, serial_in(up, UART_IER) | mask); ++ struct uart_port *port = &up->port; ++ unsigned long flags; ++ bool is_console; ++ int ier; ++ ++ is_console = uart_console(port); ++ ++ if (is_console) ++ printk_cpu_sync_get_irqsave(flags); ++ ++ ier = serial_in(up, UART_IER); ++ serial_out(up, UART_IER, ier | mask); ++ ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(flags); + } + + static void mtk8250_set_flow_ctrl(struct uart_8250_port *up, int mode) +diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c +index adc85e250822..92a8441ba756 100644 +--- a/drivers/tty/serial/8250/8250_omap.c ++++ b/drivers/tty/serial/8250/8250_omap.c +@@ -328,7 +328,7 @@ static void omap8250_restore_regs(struct uart_8250_port *up) + /* drop TCR + TLR access, we setup XON/XOFF later */ + serial8250_out_MCR(up, mcr); + +- serial_out(up, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_dl_write(up, priv->quot); +@@ -518,7 +518,7 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state, + serial_out(up, UART_EFR, efr | UART_EFR_ECB); + serial_out(up, UART_LCR, 0); + +- serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0); ++ serial8250_set_IER(up, (state != 0) ? UART_IERX_SLEEP : 0); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, efr); + serial_out(up, UART_LCR, 0); +@@ -641,7 +641,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) + + /* Synchronize UART_IER access against the console. */ + spin_lock(&port->lock); +- up->ier = port->serial_in(port, UART_IER); ++ up->ier = serial8250_in_IER(up); + if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { + port->ops->stop_rx(port); + } else { +@@ -701,7 +701,7 @@ static int omap_8250_startup(struct uart_port *port) + goto err; + + up->ier = UART_IER_RLSI | UART_IER_RDI; +- serial_out(up, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + + #ifdef CONFIG_PM + up->capabilities |= UART_CAP_RPM; +@@ -742,7 +742,7 @@ static void omap_8250_shutdown(struct uart_port *port) + serial_out(up, UART_OMAP_EFR2, 0x0); + + up->ier = 0; +- serial_out(up, UART_IER, 0); ++ serial8250_set_IER(up, 0); + + if (up->dma) + serial8250_release_dma(up); +@@ -790,7 +790,7 @@ static void omap_8250_unthrottle(struct uart_port *port) + up->dma->rx_dma(up); + up->ier |= UART_IER_RLSI | UART_IER_RDI; + port->read_status_mask |= UART_LSR_DR; +- serial_out(up, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + spin_unlock_irqrestore(&port->lock, flags); + + pm_runtime_mark_last_busy(port->dev); +@@ -881,7 +881,7 @@ static void __dma_rx_complete(void *param) + __dma_rx_do_complete(p); + if (!priv->throttled) { + p->ier |= UART_IER_RLSI | UART_IER_RDI; +- serial_out(p, UART_IER, p->ier); ++ serial8250_set_IER(p, p->ier); + if (!(priv->habit & UART_HAS_EFR2)) + omap_8250_rx_dma(p); + } +@@ -938,7 +938,7 @@ static int omap_8250_rx_dma(struct uart_8250_port *p) + * callback to run. + */ + p->ier &= ~(UART_IER_RLSI | UART_IER_RDI); +- serial_out(p, UART_IER, p->ier); ++ serial8250_set_IER(p, p->ier); + } + goto out; + } +@@ -1151,12 +1151,12 @@ static void am654_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir, + * periodic timeouts, re-enable interrupts. + */ + up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); +- serial_out(up, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + omap_8250_rx_dma_flush(up); + serial_in(up, UART_IIR); + serial_out(up, UART_OMAP_EFR2, 0x0); + up->ier |= UART_IER_RLSI | UART_IER_RDI; +- serial_out(up, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + } + } + +diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c +index 38760bd6e0c2..b3719e8258f7 100644 +--- a/drivers/tty/serial/8250/8250_port.c ++++ b/drivers/tty/serial/8250/8250_port.c +@@ -744,7 +744,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) + serial_out(p, UART_EFR, UART_EFR_ECB); + serial_out(p, UART_LCR, 0); + } +- serial_out(p, UART_IER, sleep ? UART_IERX_SLEEP : 0); ++ serial8250_set_IER(p, sleep ? UART_IERX_SLEEP : 0); + if (p->capabilities & UART_CAP_EFR) { + serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(p, UART_EFR, efr); +@@ -755,12 +755,29 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) + serial8250_rpm_put(p); + } + +-static void serial8250_clear_IER(struct uart_8250_port *up) ++static unsigned int serial8250_clear_IER(struct uart_8250_port *up) + { ++ struct uart_port *port = &up->port; ++ unsigned int clearval = 0; ++ unsigned long flags; ++ bool is_console; ++ unsigned int prior; ++ ++ is_console = uart_console(port); ++ + if (up->capabilities & UART_CAP_UUE) +- serial_out(up, UART_IER, UART_IER_UUE); +- else +- serial_out(up, UART_IER, 0); ++ clearval = UART_IER_UUE; ++ ++ if (is_console) ++ printk_cpu_sync_get_irqsave(flags); ++ ++ prior = serial_in(up, UART_IER); ++ serial_out(up, UART_IER, clearval); ++ ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(flags); ++ ++ return prior; + } + + #ifdef CONFIG_SERIAL_8250_RSA +@@ -1026,8 +1043,11 @@ static int broken_efr(struct uart_8250_port *up) + */ + static void autoconfig_16550a(struct uart_8250_port *up) + { ++ struct uart_port *port = &up->port; + unsigned char status1, status2; + unsigned int iersave; ++ unsigned long flags; ++ bool is_console; + + up->port.type = PORT_16550A; + up->capabilities |= UART_CAP_FIFO; +@@ -1139,6 +1159,11 @@ static void autoconfig_16550a(struct uart_8250_port *up) + return; + } + ++ is_console = uart_console(port); ++ ++ if (is_console) ++ printk_cpu_sync_get_irqsave(flags); ++ + /* + * Try writing and reading the UART_IER_UUE bit (b6). + * If it works, this is probably one of the Xscale platform's +@@ -1174,6 +1199,9 @@ static void autoconfig_16550a(struct uart_8250_port *up) + } + serial_out(up, UART_IER, iersave); + ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(flags); ++ + /* + * We distinguish between 16550A and U6 16550A by counting + * how many bytes are in the FIFO. +@@ -1196,8 +1224,10 @@ static void autoconfig(struct uart_8250_port *up) + unsigned char status1, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + struct uart_port *port = &up->port; ++ unsigned long cs_flags; + unsigned long flags; + unsigned int old_capabilities; ++ bool is_console; + + if (!port->iobase && !port->mapbase && !port->membase) + return; +@@ -1215,6 +1245,11 @@ static void autoconfig(struct uart_8250_port *up) + up->bugs = 0; + + if (!(port->flags & UPF_BUGGY_UART)) { ++ is_console = uart_console(port); ++ ++ if (is_console) ++ printk_cpu_sync_get_irqsave(cs_flags); ++ + /* + * Do a simple existence test first; if we fail this, + * there's no point trying anything else. +@@ -1244,6 +1279,10 @@ static void autoconfig(struct uart_8250_port *up) + #endif + scratch3 = serial_in(up, UART_IER) & 0x0f; + serial_out(up, UART_IER, scratch); ++ ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(cs_flags); ++ + if (scratch2 != 0 || scratch3 != 0x0F) { + /* + * We failed; there's nothing here +@@ -1367,7 +1406,9 @@ static void autoconfig_irq(struct uart_8250_port *up) + unsigned char save_mcr, save_ier; + unsigned char save_ICP = 0; + unsigned int ICP = 0; ++ unsigned long flags; + unsigned long irqs; ++ bool is_console; + int irq; + + if (port->flags & UPF_FOURPORT) { +@@ -1377,8 +1418,12 @@ static void autoconfig_irq(struct uart_8250_port *up) + inb_p(ICP); + } + +- if (uart_console(port)) ++ is_console = uart_console(port); ++ ++ if (is_console) { + console_lock(); ++ printk_cpu_sync_get_irqsave(flags); ++ } + + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); +@@ -1410,8 +1455,10 @@ static void autoconfig_irq(struct uart_8250_port *up) + if (port->flags & UPF_FOURPORT) + outb_p(save_ICP, ICP); + +- if (uart_console(port)) ++ if (is_console) { ++ printk_cpu_sync_put_irqrestore(flags); + console_unlock(); ++ } + + port->irq = (irq > 0) ? irq : 0; + } +@@ -1424,7 +1471,7 @@ static void serial8250_stop_rx(struct uart_port *port) + + up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); + up->port.read_status_mask &= ~UART_LSR_DR; +- serial_port_out(port, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + + serial8250_rpm_put(up); + } +@@ -1454,7 +1501,7 @@ void serial8250_em485_stop_tx(struct uart_8250_port *p) + serial8250_clear_and_reinit_fifos(p); + + p->ier |= UART_IER_RLSI | UART_IER_RDI; +- serial_port_out(&p->port, UART_IER, p->ier); ++ serial8250_set_IER(p, p->ier); + } + } + EXPORT_SYMBOL_GPL(serial8250_em485_stop_tx); +@@ -1703,7 +1750,7 @@ static void serial8250_disable_ms(struct uart_port *port) + mctrl_gpio_disable_ms(up->gpios); + + up->ier &= ~UART_IER_MSI; +- serial_port_out(port, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + } + + static void serial8250_enable_ms(struct uart_port *port) +@@ -1719,7 +1766,7 @@ static void serial8250_enable_ms(struct uart_port *port) + up->ier |= UART_IER_MSI; + + serial8250_rpm_get(up); +- serial_port_out(port, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + serial8250_rpm_put(up); + } + +@@ -2171,8 +2218,7 @@ static void serial8250_put_poll_char(struct uart_port *port, + /* + * First save the IER then disable the interrupts + */ +- ier = serial_port_in(port, UART_IER); +- serial8250_clear_IER(up); ++ ier = serial8250_clear_IER(up); + + wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); + /* +@@ -2185,7 +2231,7 @@ static void serial8250_put_poll_char(struct uart_port *port, + * and restore the IER + */ + wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); +- serial_port_out(port, UART_IER, ier); ++ serial8250_set_IER(up, ier); + serial8250_rpm_put(up); + } + +@@ -2194,8 +2240,10 @@ static void serial8250_put_poll_char(struct uart_port *port, + int serial8250_do_startup(struct uart_port *port) + { + struct uart_8250_port *up = up_to_u8250p(port); ++ unsigned long cs_flags; + unsigned long flags; + unsigned char iir; ++ bool is_console; + int retval; + u16 lsr; + +@@ -2216,7 +2264,7 @@ int serial8250_do_startup(struct uart_port *port) + up->acr = 0; + serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B); + serial_port_out(port, UART_EFR, UART_EFR_ECB); +- serial_port_out(port, UART_IER, 0); ++ serial8250_set_IER(up, 0); + serial_port_out(port, UART_LCR, 0); + serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ + serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B); +@@ -2226,7 +2274,7 @@ int serial8250_do_startup(struct uart_port *port) + + if (port->type == PORT_DA830) { + /* Reset the port */ +- serial_port_out(port, UART_IER, 0); ++ serial8250_set_IER(up, 0); + serial_port_out(port, UART_DA830_PWREMU_MGMT, 0); + mdelay(10); + +@@ -2325,6 +2373,8 @@ int serial8250_do_startup(struct uart_port *port) + if (retval) + goto out; + ++ is_console = uart_console(port); ++ + if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) { + unsigned char iir1; + +@@ -2341,6 +2391,9 @@ int serial8250_do_startup(struct uart_port *port) + */ + spin_lock_irqsave(&port->lock, flags); + ++ if (is_console) ++ printk_cpu_sync_get_irqsave(cs_flags); ++ + wait_for_xmitr(up, UART_LSR_THRE); + serial_port_out_sync(port, UART_IER, UART_IER_THRI); + udelay(1); /* allow THRE to set */ +@@ -2351,6 +2404,9 @@ int serial8250_do_startup(struct uart_port *port) + iir = serial_port_in(port, UART_IIR); + serial_port_out(port, UART_IER, 0); + ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(cs_flags); ++ + spin_unlock_irqrestore(&port->lock, flags); + + if (port->irqflags & IRQF_SHARED) +@@ -2405,10 +2461,14 @@ int serial8250_do_startup(struct uart_port *port) + * Do a quick test to see if we receive an interrupt when we enable + * the TX irq. + */ ++ if (is_console) ++ printk_cpu_sync_get_irqsave(cs_flags); + serial_port_out(port, UART_IER, UART_IER_THRI); + lsr = serial_port_in(port, UART_LSR); + iir = serial_port_in(port, UART_IIR); + serial_port_out(port, UART_IER, 0); ++ if (is_console) ++ printk_cpu_sync_put_irqrestore(cs_flags); + + if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { + if (!(up->bugs & UART_BUG_TXEN)) { +@@ -2440,7 +2500,7 @@ int serial8250_do_startup(struct uart_port *port) + if (up->dma) { + const char *msg = NULL; + +- if (uart_console(port)) ++ if (is_console) + msg = "forbid DMA for kernel console"; + else if (serial8250_request_dma(up)) + msg = "failed to request DMA"; +@@ -2491,7 +2551,7 @@ void serial8250_do_shutdown(struct uart_port *port) + */ + spin_lock_irqsave(&port->lock, flags); + up->ier = 0; +- serial_port_out(port, UART_IER, 0); ++ serial8250_set_IER(up, 0); + spin_unlock_irqrestore(&port->lock, flags); + + synchronize_irq(port->irq); +@@ -2853,7 +2913,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, + if (up->capabilities & UART_CAP_RTOIE) + up->ier |= UART_IER_RTOIE; + +- serial_port_out(port, UART_IER, up->ier); ++ serial8250_set_IER(up, up->ier); + + if (up->capabilities & UART_CAP_EFR) { + unsigned char efr = 0; +@@ -3318,7 +3378,7 @@ EXPORT_SYMBOL_GPL(serial8250_set_defaults); + + #ifdef CONFIG_SERIAL_8250_CONSOLE + +-static void serial8250_console_putchar(struct uart_port *port, unsigned char ch) ++static void serial8250_console_putchar_locked(struct uart_port *port, unsigned char ch) + { + struct uart_8250_port *up = up_to_u8250p(port); + +@@ -3326,6 +3386,18 @@ static void serial8250_console_putchar(struct uart_port *port, unsigned char ch) + serial_port_out(port, UART_TX, ch); + } + ++static void serial8250_console_putchar(struct uart_port *port, unsigned char ch) ++{ ++ struct uart_8250_port *up = up_to_u8250p(port); ++ unsigned long flags; ++ ++ wait_for_xmitr(up, UART_LSR_THRE); ++ ++ printk_cpu_sync_get_irqsave(flags); ++ serial8250_console_putchar_locked(port, ch); ++ printk_cpu_sync_put_irqrestore(flags); ++} ++ + /* + * Restore serial console when h/w power-off detected + */ +@@ -3352,6 +3424,32 @@ static void serial8250_console_restore(struct uart_8250_port *up) + serial8250_out_MCR(up, up->mcr | UART_MCR_DTR | UART_MCR_RTS); + } + ++void serial8250_console_write_atomic(struct uart_8250_port *up, ++ const char *s, unsigned int count) ++{ ++ struct uart_port *port = &up->port; ++ unsigned long flags; ++ unsigned int ier; ++ ++ printk_cpu_sync_get_irqsave(flags); ++ ++ touch_nmi_watchdog(); ++ ++ ier = serial8250_clear_IER(up); ++ ++ if (atomic_fetch_inc(&up->console_printing)) { ++ uart_console_write(port, "\n", 1, ++ serial8250_console_putchar_locked); ++ } ++ uart_console_write(port, s, count, serial8250_console_putchar_locked); ++ atomic_dec(&up->console_printing); ++ ++ wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); ++ serial8250_set_IER(up, ier); ++ ++ printk_cpu_sync_put_irqrestore(flags); ++} ++ + /* + * Print a string to the serial port using the device FIFO + * +@@ -3397,20 +3495,15 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, + struct uart_port *port = &up->port; + unsigned long flags; + unsigned int ier, use_fifo; +- int locked = 1; + + touch_nmi_watchdog(); + +- if (oops_in_progress) +- locked = spin_trylock_irqsave(&port->lock, flags); +- else +- spin_lock_irqsave(&port->lock, flags); ++ spin_lock_irqsave(&port->lock, flags); + + /* + * First save the IER then disable the interrupts + */ +- ier = serial_port_in(port, UART_IER); +- serial8250_clear_IER(up); ++ ier = serial8250_clear_IER(up); + + /* check scratch reg to see if port powered off during system sleep */ + if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) { +@@ -3444,10 +3537,12 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, + */ + !(up->port.flags & UPF_CONS_FLOW); + ++ atomic_inc(&up->console_printing); + if (likely(use_fifo)) + serial8250_console_fifo_write(up, s, count); + else + uart_console_write(port, s, count, serial8250_console_putchar); ++ atomic_dec(&up->console_printing); + + /* + * Finally, wait for transmitter to become empty +@@ -3460,8 +3555,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, + if (em485->tx_stopped) + up->rs485_stop_tx(up); + } +- +- serial_port_out(port, UART_IER, ier); ++ serial8250_set_IER(up, ier); + + /* + * The receive handling will happen properly because the +@@ -3473,8 +3567,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, + if (up->msr_saved_flags) + serial8250_modem_status(up); + +- if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ spin_unlock_irqrestore(&port->lock, flags); + } + + static unsigned int probe_baud(struct uart_port *port) +@@ -3494,6 +3587,7 @@ static unsigned int probe_baud(struct uart_port *port) + + int serial8250_console_setup(struct uart_port *port, char *options, bool probe) + { ++ struct uart_8250_port *up = up_to_u8250p(port); + int baud = 9600; + int bits = 8; + int parity = 'n'; +@@ -3503,6 +3597,8 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe) + if (!port->iobase && !port->membase) + return -ENODEV; + ++ atomic_set(&up->console_printing, 0); ++ + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else if (probe) +diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig +index 583a340f9934..1f31320820a6 100644 +--- a/drivers/tty/serial/8250/Kconfig ++++ b/drivers/tty/serial/8250/Kconfig +@@ -9,6 +9,7 @@ config SERIAL_8250 + depends on !S390 + select SERIAL_CORE + select SERIAL_MCTRL_GPIO if GPIOLIB ++ select HAVE_ATOMIC_CONSOLE + help + This selects whether you want to include the driver for the standard + serial ports. The standard answer is Y. People who might say N +diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c +index 0a1cc36f93aa..51a8176050f8 100644 +--- a/drivers/tty/serial/amba-pl011.c ++++ b/drivers/tty/serial/amba-pl011.c +@@ -2320,18 +2320,24 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) + { + struct uart_amba_port *uap = amba_ports[co->index]; + unsigned int old_cr = 0, new_cr; +- unsigned long flags; ++ unsigned long flags = 0; + int locked = 1; + + clk_enable(uap->clk); + +- local_irq_save(flags); ++ /* ++ * local_irq_save(flags); ++ * ++ * This local_irq_save() is nonsense. If we come in via sysrq ++ * handling then interrupts are already disabled. Aside of ++ * that the port.sysrq check is racy on SMP regardless. ++ */ + if (uap->port.sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock(&uap->port.lock); ++ locked = spin_trylock_irqsave(&uap->port.lock, flags); + else +- spin_lock(&uap->port.lock); ++ spin_lock_irqsave(&uap->port.lock, flags); + + /* + * First save the CR then disable the interrupts +@@ -2357,8 +2363,7 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) + pl011_write(old_cr, uap, REG_CR); + + if (locked) +- spin_unlock(&uap->port.lock); +- local_irq_restore(flags); ++ spin_unlock_irqrestore(&uap->port.lock, flags); + + clk_disable(uap->clk); + } +diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c +index 7d0d2718ef59..aa216fdbcb1d 100644 +--- a/drivers/tty/serial/omap-serial.c ++++ b/drivers/tty/serial/omap-serial.c +@@ -1241,13 +1241,10 @@ serial_omap_console_write(struct console *co, const char *s, + unsigned int ier; + int locked = 1; + +- local_irq_save(flags); +- if (up->port.sysrq) +- locked = 0; +- else if (oops_in_progress) +- locked = spin_trylock(&up->port.lock); ++ if (up->port.sysrq || oops_in_progress) ++ locked = spin_trylock_irqsave(&up->port.lock, flags); + else +- spin_lock(&up->port.lock); ++ spin_lock_irqsave(&up->port.lock, flags); + + /* + * First save the IER then disable the interrupts +@@ -1274,8 +1271,7 @@ serial_omap_console_write(struct console *co, const char *s, + check_modem_status(up); + + if (locked) +- spin_unlock(&up->port.lock); +- local_irq_restore(flags); ++ spin_unlock_irqrestore(&up->port.lock, flags); + } + + static int __init +diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c +index d2b2720db6ca..18e623325887 100644 +--- a/drivers/tty/sysrq.c ++++ b/drivers/tty/sysrq.c +@@ -581,6 +581,7 @@ void __handle_sysrq(int key, bool check_mask) + + rcu_sysrq_start(); + rcu_read_lock(); ++ printk_prefer_direct_enter(); + /* + * Raise the apparent loglevel to maximum so that the sysrq header + * is shown to provide the user with positive feedback. We do not +@@ -622,6 +623,7 @@ void __handle_sysrq(int key, bool check_mask) + pr_cont("\n"); + console_loglevel = orig_log_level; + } ++ printk_prefer_direct_exit(); + rcu_read_unlock(); + rcu_sysrq_end(); + +diff --git a/drivers/vdpa/vdpa_user/iova_domain.h b/drivers/vdpa/vdpa_user/iova_domain.h +index 4e0e50e7ac15..173e979b84a9 100644 +--- a/drivers/vdpa/vdpa_user/iova_domain.h ++++ b/drivers/vdpa/vdpa_user/iova_domain.h +@@ -14,7 +14,6 @@ + #include + #include + #include +-#include + + #define IOVA_START_PFN 1 + +diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig +index 106b47ab9f4f..d0b8375d10ec 100644 +--- a/drivers/virtio/Kconfig ++++ b/drivers/virtio/Kconfig +@@ -145,6 +145,14 @@ config VIRTIO_INPUT + + If unsure, say M. + ++config VIRTIO_TRANS ++ tristate "VirtIO transfer test driver" ++ depends on VIRTIO ++ help ++ This driver is for VirtIO transfer performance test ++ between front-end and back-end driver. ++ If unsure, say N. ++ + config VIRTIO_MMIO + tristate "Platform bus driver for memory mapped virtio devices" + depends on HAS_IOMEM && HAS_DMA +diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile +index 7960f6493913..bdf34908610c 100644 +--- a/drivers/virtio/Makefile ++++ b/drivers/virtio/Makefile +@@ -13,3 +13,4 @@ obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o + obj-$(CONFIG_VIRTIO_VDPA) += virtio_vdpa.o + obj-$(CONFIG_VIRTIO_MEM) += virtio_mem.o + obj-$(CONFIG_VIRTIO_DMA_SHARED_BUFFER) += virtio_dma_buf.o ++obj-$(CONFIG_VIRTIO_TRANS) += virtio_trans.o +diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c +index dec3cba88458..c65a22c6631d 100644 +--- a/drivers/virtio/virtio_mmio.c ++++ b/drivers/virtio/virtio_mmio.c +@@ -3,6 +3,7 @@ + * Virtio memory mapped device driver + * + * Copyright 2011-2014, ARM Ltd. ++ * Copyright 2022-2023 NXP + * + * This module allows virtio devices to be used over a virtual, memory mapped + * platform device. +@@ -60,7 +61,9 @@ + #include + #include + #include ++#include + #include ++#include + #include + #include + #include +@@ -69,7 +72,7 @@ + #include + #include + #include +- ++#include + + + /* The alignment to use between consumer and producer parts of vring. +@@ -81,6 +84,13 @@ + #define to_virtio_mmio_device(_plat_dev) \ + container_of(_plat_dev, struct virtio_mmio_device, vdev) + ++struct virtio_mmio_wr_op { ++ uint64_t phy_base; ++ uint32_t offset; ++ uint32_t value; ++ uint8_t len; ++}; ++ + struct virtio_mmio_device { + struct virtio_device vdev; + struct platform_device *pdev; +@@ -91,6 +101,16 @@ struct virtio_mmio_device { + /* a list of queues so we can dispatch IRQs */ + spinlock_t lock; + struct list_head virtqueues; ++ ++ /* Hypervisor_less virtio */ ++ bool is_hypervisor_less; ++ phys_addr_t phys; ++ struct virtio_mmio_wr_op *wr_ops; ++ dma_addr_t wr_ops_phys; ++ struct mbox_client mbox_wr_cl; ++ struct mbox_client mbox_irq_cl; ++ struct mbox_chan *mmio_wr_ch; ++ struct mbox_chan *mmio_irq_ch; + }; + + struct virtio_mmio_vq_info { +@@ -101,7 +121,72 @@ struct virtio_mmio_vq_info { + struct list_head node; + }; + ++static void vmd_write(struct virtio_mmio_device *vm_dev, u32 val, u32 off, u8 len) ++{ ++ void __iomem *addr = vm_dev->base + off; ++ struct virtio_mmio_wr_op *wr_ops = vm_dev->wr_ops; ++ int timeout; ++ int ret; ++ ++ switch (len) { ++ case 1: ++ writeb(val, addr); ++ break; ++ case 2: ++ writew(val, addr); ++ break; ++ case 4: ++ writel(val, addr); ++ break; ++ default: ++ return; ++ } ++ ++ if (vm_dev->is_hypervisor_less) { ++ ++ timeout = 1000000; ++ writel(0, vm_dev->base + VIRTIO_MMIO_WD_STATUS); ++ ++ wr_ops->phy_base = (uint64_t)vm_dev->phys; ++ wr_ops->offset = off; ++ wr_ops->value = val; ++ wr_ops->len = len; ++ ++ ret = mbox_send_message(vm_dev->mmio_wr_ch, &vm_dev->wr_ops_phys); ++ if (ret < 0) ++ dev_err(&vm_dev->vdev.dev, "mmio write error: base: %llx offset: %x, val: %x, len: %d\n", ++ wr_ops->phy_base, wr_ops->offset, ++ wr_ops->value, wr_ops->len); ++ ++ /* Unnecessary to sync for virtqueue notification */ ++ if (off == VIRTIO_MMIO_QUEUE_NOTIFY) ++ return; ++ ++ /* poll backend write operation complete */ ++ while (timeout-- && !readl(vm_dev->base + VIRTIO_MMIO_WD_STATUS)) ++ udelay(1); ++ ++ if (timeout < 0) ++ dev_err(&vm_dev->vdev.dev, "mmio write timeout: base: %llx offset: %x, val: %x, len: %d\n", ++ wr_ops->phy_base, wr_ops->offset, ++ wr_ops->value, wr_ops->len); ++ } ++} ++ ++static void vmd_writel(struct virtio_mmio_device *vm_dev, u32 val, u32 off) ++{ ++ vmd_write(vm_dev, val, off, 4); ++} ++ ++static void vmd_writew(struct virtio_mmio_device *vm_dev, u32 val, u32 off) ++{ ++ vmd_write(vm_dev, val, off, 2); ++} + ++static void vmd_writeb(struct virtio_mmio_device *vm_dev, u32 val, u32 off) ++{ ++ vmd_write(vm_dev, val, off, 1); ++} + + /* Configuration interface */ + +@@ -110,11 +195,11 @@ static u64 vm_get_features(struct virtio_device *vdev) + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); + u64 features; + +- writel(1, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL); ++ vmd_writel(vm_dev, 1, VIRTIO_MMIO_DEVICE_FEATURES_SEL); + features = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES); + features <<= 32; + +- writel(0, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL); ++ vmd_writel(vm_dev, 0, VIRTIO_MMIO_DEVICE_FEATURES_SEL); + features |= readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES); + + return features; +@@ -134,13 +219,13 @@ static int vm_finalize_features(struct virtio_device *vdev) + return -EINVAL; + } + +- writel(1, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL); +- writel((u32)(vdev->features >> 32), +- vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES); ++ vmd_writel(vm_dev, 1, VIRTIO_MMIO_DRIVER_FEATURES_SEL); ++ vmd_writel(vm_dev, (u32)(vdev->features >> 32), ++ VIRTIO_MMIO_DRIVER_FEATURES); + +- writel(0, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL); +- writel((u32)vdev->features, +- vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES); ++ vmd_writel(vm_dev, 0, VIRTIO_MMIO_DRIVER_FEATURES_SEL); ++ vmd_writel(vm_dev, (u32)vdev->features, ++ VIRTIO_MMIO_DRIVER_FEATURES); + + return 0; + } +@@ -191,17 +276,13 @@ static void vm_set(struct virtio_device *vdev, unsigned int offset, + const void *buf, unsigned int len) + { + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); +- void __iomem *base = vm_dev->base + VIRTIO_MMIO_CONFIG; ++ u32 base = VIRTIO_MMIO_CONFIG; + u8 b; + __le16 w; + __le32 l; + + if (vm_dev->version == 1) { +- const u8 *ptr = buf; +- int i; +- +- for (i = 0; i < len; i++) +- writeb(ptr[i], base + offset + i); ++ vmd_writel(vm_dev, *(u32 *)buf, base + offset); + + return; + } +@@ -209,21 +290,21 @@ static void vm_set(struct virtio_device *vdev, unsigned int offset, + switch (len) { + case 1: + memcpy(&b, buf, sizeof b); +- writeb(b, base + offset); ++ vmd_writeb(vm_dev, b, base + offset); + break; + case 2: + memcpy(&w, buf, sizeof w); +- writew(le16_to_cpu(w), base + offset); ++ vmd_writew(vm_dev, le16_to_cpu(w), base + offset); + break; + case 4: + memcpy(&l, buf, sizeof l); +- writel(le32_to_cpu(l), base + offset); ++ vmd_writel(vm_dev, le32_to_cpu(l), base + offset); + break; + case 8: + memcpy(&l, buf, sizeof l); +- writel(le32_to_cpu(l), base + offset); ++ vmd_writel(vm_dev, le32_to_cpu(l), base + offset); + memcpy(&l, buf + sizeof l, sizeof l); +- writel(le32_to_cpu(l), base + offset + sizeof l); ++ vmd_writel(vm_dev, le32_to_cpu(l), base + offset + sizeof l); + break; + default: + BUG(); +@@ -259,7 +340,7 @@ static void vm_set_status(struct virtio_device *vdev, u8 status) + * that the cache coherent memory writes have completed + * before writing to the MMIO region. + */ +- writel(status, vm_dev->base + VIRTIO_MMIO_STATUS); ++ vmd_writel(vm_dev, status, VIRTIO_MMIO_STATUS); + } + + static void vm_reset(struct virtio_device *vdev) +@@ -267,7 +348,7 @@ static void vm_reset(struct virtio_device *vdev) + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); + + /* 0 status means a reset. */ +- writel(0, vm_dev->base + VIRTIO_MMIO_STATUS); ++ vmd_writel(vm_dev, 0, VIRTIO_MMIO_STATUS); + } + + +@@ -281,10 +362,40 @@ static bool vm_notify(struct virtqueue *vq) + + /* We write the queue's selector into the notification register to + * signal the other end */ +- writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY); ++ vmd_writel(vm_dev, vq->index, VIRTIO_MMIO_QUEUE_NOTIFY); + return true; + } + ++/* Notify all virtqueues on an interrupt. */ ++static void virtio_mmio_irq_callback(struct mbox_client *c, void *msg) ++{ ++ struct virtio_mmio_device *vm_dev = container_of(c, ++ struct virtio_mmio_device, mbox_irq_cl); ++ struct virtio_mmio_vq_info *info; ++ unsigned long status; ++ unsigned long flags; ++ ++ /* Read and acknowledge interrupts */ ++ status = readl(vm_dev->base + VIRTIO_MMIO_INTERRUPT_STATUS); ++ ++ /* ++ * For Hypervisor-less virtio, write ACK without notification, since ++ * in mailbox RX channel callback function sometimes the timeout occur ++ * calling the vmd_writel(). ++ */ ++ writel(status, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK); ++ ++ if (unlikely(status & VIRTIO_MMIO_INT_CONFIG)) ++ virtio_config_changed(&vm_dev->vdev); ++ ++ if (likely(status & VIRTIO_MMIO_INT_VRING)) { ++ spin_lock_irqsave(&vm_dev->lock, flags); ++ list_for_each_entry(info, &vm_dev->virtqueues, node) ++ vring_interrupt(0, info->vq); ++ spin_unlock_irqrestore(&vm_dev->lock, flags); ++ } ++} ++ + /* Notify all virtqueues on an interrupt. */ + static irqreturn_t vm_interrupt(int irq, void *opaque) + { +@@ -296,7 +407,7 @@ static irqreturn_t vm_interrupt(int irq, void *opaque) + + /* Read and acknowledge interrupts */ + status = readl(vm_dev->base + VIRTIO_MMIO_INTERRUPT_STATUS); +- writel(status, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK); ++ vmd_writel(vm_dev, status, VIRTIO_MMIO_INTERRUPT_ACK); + + if (unlikely(status & VIRTIO_MMIO_INT_CONFIG)) { + virtio_config_changed(&vm_dev->vdev); +@@ -327,11 +438,11 @@ static void vm_del_vq(struct virtqueue *vq) + spin_unlock_irqrestore(&vm_dev->lock, flags); + + /* Select and deactivate the queue */ +- writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); ++ vmd_writel(vm_dev, index, VIRTIO_MMIO_QUEUE_SEL); + if (vm_dev->version == 1) { +- writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); ++ vmd_writel(vm_dev, 0, VIRTIO_MMIO_QUEUE_PFN); + } else { +- writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); ++ vmd_writel(vm_dev, 0, VIRTIO_MMIO_QUEUE_READY); + WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY)); + } + +@@ -348,7 +459,10 @@ static void vm_del_vqs(struct virtio_device *vdev) + list_for_each_entry_safe(vq, n, &vdev->vqs, list) + vm_del_vq(vq); + +- free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev); ++ if (vm_dev->is_hypervisor_less) ++ mbox_free_channel(vm_dev->mmio_irq_ch); ++ else ++ free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev); + } + + static void vm_synchronize_cbs(struct virtio_device *vdev) +@@ -373,7 +487,7 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in + return NULL; + + /* Select the queue we're interested in */ +- writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); ++ vmd_writel(vm_dev, index, VIRTIO_MMIO_QUEUE_SEL); + + /* Queue shouldn't already be set up. */ + if (readl(vm_dev->base + (vm_dev->version == 1 ? +@@ -406,7 +520,7 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in + vq->num_max = num; + + /* Activate the queue */ +- writel(virtqueue_get_vring_size(vq), vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); ++ vmd_writel(vm_dev, virtqueue_get_vring_size(vq), VIRTIO_MMIO_QUEUE_NUM); + if (vm_dev->version == 1) { + u64 q_pfn = virtqueue_get_desc_addr(vq) >> PAGE_SHIFT; + +@@ -423,27 +537,27 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in + goto error_bad_pfn; + } + +- writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN); +- writel(q_pfn, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); ++ vmd_writel(vm_dev, PAGE_SIZE, VIRTIO_MMIO_QUEUE_ALIGN); ++ vmd_writel(vm_dev, q_pfn, VIRTIO_MMIO_QUEUE_PFN); + } else { + u64 addr; + + addr = virtqueue_get_desc_addr(vq); +- writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_LOW); +- writel((u32)(addr >> 32), +- vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_HIGH); ++ vmd_writel(vm_dev, (u32)addr, VIRTIO_MMIO_QUEUE_DESC_LOW); ++ vmd_writel(vm_dev, (u32)(addr >> 32), ++ VIRTIO_MMIO_QUEUE_DESC_HIGH); + + addr = virtqueue_get_avail_addr(vq); +- writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW); +- writel((u32)(addr >> 32), +- vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH); ++ vmd_writel(vm_dev, (u32)addr, VIRTIO_MMIO_QUEUE_AVAIL_LOW); ++ vmd_writel(vm_dev, (u32)(addr >> 32), ++ VIRTIO_MMIO_QUEUE_AVAIL_HIGH); + + addr = virtqueue_get_used_addr(vq); +- writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_USED_LOW); +- writel((u32)(addr >> 32), +- vm_dev->base + VIRTIO_MMIO_QUEUE_USED_HIGH); ++ vmd_writel(vm_dev, (u32)addr, VIRTIO_MMIO_QUEUE_USED_LOW); ++ vmd_writel(vm_dev, (u32)(addr >> 32), ++ VIRTIO_MMIO_QUEUE_USED_HIGH); + +- writel(1, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); ++ vmd_writel(vm_dev, 1, VIRTIO_MMIO_QUEUE_READY); + } + + vq->priv = info; +@@ -459,9 +573,9 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in + vring_del_virtqueue(vq); + error_new_virtqueue: + if (vm_dev->version == 1) { +- writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); ++ vmd_writel(vm_dev, 0, VIRTIO_MMIO_QUEUE_PFN); + } else { +- writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); ++ vmd_writel(vm_dev, 0, VIRTIO_MMIO_QUEUE_READY); + WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY)); + } + kfree(info); +@@ -470,6 +584,28 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in + return ERR_PTR(err); + } + ++static int virtio_mmio_irq_channel_init(struct virtio_mmio_device *vm_dev) ++{ ++ struct platform_device *pdev = vm_dev->pdev; ++ struct device *dev = &pdev->dev; ++ struct mbox_client *cl; ++ int ret = 0; ++ ++ cl = &vm_dev->mbox_irq_cl; ++ cl->dev = dev; ++ cl->rx_callback = virtio_mmio_irq_callback; ++ ++ vm_dev->mmio_irq_ch = mbox_request_channel_byname(cl, "mmioirq"); ++ if (IS_ERR(vm_dev->mmio_irq_ch)) { ++ ret = PTR_ERR(vm_dev->mmio_irq_ch); ++ dev_err(cl->dev, "failed to request mbox irq chan, ret %d\n", ++ ret); ++ return ret; ++ } ++ ++ return ret; ++} ++ + static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], +@@ -478,16 +614,24 @@ static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs, + struct irq_affinity *desc) + { + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); +- int irq = platform_get_irq(vm_dev->pdev, 0); ++ int irq; + int i, err, queue_idx = 0; + +- if (irq < 0) +- return irq; ++ if (vm_dev->is_hypervisor_less) { ++ err = virtio_mmio_irq_channel_init(vm_dev); ++ if (err) ++ return err; ++ } else { ++ irq = platform_get_irq(vm_dev->pdev, 0); ++ if (irq < 0) ++ return irq; + +- err = request_irq(irq, vm_interrupt, IRQF_SHARED, +- dev_name(&vdev->dev), vm_dev); +- if (err) +- return err; ++ err = request_irq(irq, vm_interrupt, IRQF_SHARED, ++ dev_name(&vdev->dev), vm_dev); ++ ++ if (err) ++ return err; ++ } + + if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source")) + enable_irq_wake(irq); +@@ -523,7 +667,7 @@ static bool vm_get_shm_region(struct virtio_device *vdev, + u64 len, addr; + + /* Select the region we're interested in */ +- writel(id, vm_dev->base + VIRTIO_MMIO_SHM_SEL); ++ vmd_writel(vm_dev, id, VIRTIO_MMIO_SHM_SEL); + + /* Read the region size */ + len = (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_LEN_LOW); +@@ -594,6 +738,27 @@ static void virtio_mmio_release_dev(struct device *_d) + kfree(vm_dev); + } + ++static int virtio_mmio_mbox_channel_init(struct virtio_mmio_device *vm_dev) ++{ ++ struct platform_device *pdev = vm_dev->pdev; ++ struct device *dev = &pdev->dev; ++ struct mbox_client *cl; ++ int ret = 0; ++ ++ cl = &vm_dev->mbox_wr_cl; ++ cl->dev = dev; ++ ++ vm_dev->mmio_wr_ch = mbox_request_channel_byname(cl, "mmiowr"); ++ if (IS_ERR(vm_dev->mmio_wr_ch)) { ++ ret = PTR_ERR(vm_dev->mmio_wr_ch); ++ dev_err(cl->dev, "failed to request mbox chan mmio, ret %d\n", ++ ret); ++ return ret; ++ } ++ ++ return ret; ++} ++ + /* Platform device */ + + static int virtio_mmio_probe(struct platform_device *pdev) +@@ -601,11 +766,19 @@ static int virtio_mmio_probe(struct platform_device *pdev) + struct virtio_mmio_device *vm_dev; + unsigned long magic; + int rc; ++ struct resource *res; + + vm_dev = kzalloc(sizeof(*vm_dev), GFP_KERNEL); + if (!vm_dev) + return -ENOMEM; + ++ if (of_property_read_bool(pdev->dev.of_node, "hypervisor_less")) { ++ vm_dev->is_hypervisor_less = true; ++ rc = of_reserved_mem_device_init(&pdev->dev); ++ if (rc) ++ dev_info(&pdev->dev, "Device specific DMA pool is not available\n"); ++ } ++ + vm_dev->vdev.dev.parent = &pdev->dev; + vm_dev->vdev.dev.release = virtio_mmio_release_dev; + vm_dev->vdev.config = &virtio_mmio_config_ops; +@@ -613,15 +786,19 @@ static int virtio_mmio_probe(struct platform_device *pdev) + INIT_LIST_HEAD(&vm_dev->virtqueues); + spin_lock_init(&vm_dev->lock); + +- vm_dev->base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(vm_dev->base)) +- return PTR_ERR(vm_dev->base); ++ vm_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ ++ if (IS_ERR(vm_dev->base)) { ++ rc = PTR_ERR(vm_dev->base); ++ goto err; ++ } + + /* Check magic value */ + magic = readl(vm_dev->base + VIRTIO_MMIO_MAGIC_VALUE); + if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) { + dev_warn(&pdev->dev, "Wrong magic value 0x%08lx!\n", magic); +- return -ENODEV; ++ rc = -ENODEV; ++ goto err; + } + + /* Check device version */ +@@ -629,7 +806,8 @@ static int virtio_mmio_probe(struct platform_device *pdev) + if (vm_dev->version < 1 || vm_dev->version > 2) { + dev_err(&pdev->dev, "Version %ld not supported!\n", + vm_dev->version); +- return -ENXIO; ++ rc = -ENXIO; ++ goto err; + } + + vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID); +@@ -638,12 +816,25 @@ static int virtio_mmio_probe(struct platform_device *pdev) + * virtio-mmio device with an ID 0 is a (dummy) placeholder + * with no function. End probing now with no error reported. + */ +- return -ENODEV; ++ rc = -ENODEV; ++ goto err; + } + vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID); + ++ if (vm_dev->is_hypervisor_less) { ++ vm_dev->phys = res->start; ++ ++ vm_dev->wr_ops = vm_dev->base + VIRTIO_MMIO_RW_OPS_MEM_OFFSET; ++ vm_dev->wr_ops_phys = res->start + VIRTIO_MMIO_RW_OPS_MEM_OFFSET; ++ ++ /* Initialize mailbox mmio channel. */ ++ rc = virtio_mmio_mbox_channel_init(vm_dev); ++ if (rc) ++ goto err; ++ } ++ + if (vm_dev->version == 1) { +- writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); ++ vmd_writel(vm_dev, PAGE_SIZE, VIRTIO_MMIO_GUEST_PAGE_SIZE); + + rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); + /* +@@ -668,11 +859,20 @@ static int virtio_mmio_probe(struct platform_device *pdev) + put_device(&vm_dev->vdev.dev); + + return rc; ++ ++err: ++ if (vm_dev->is_hypervisor_less) ++ of_reserved_mem_device_release(&pdev->dev); ++ ++ return rc; + } + + static int virtio_mmio_remove(struct platform_device *pdev) + { + struct virtio_mmio_device *vm_dev = platform_get_drvdata(pdev); ++ ++ if (vm_dev->is_hypervisor_less) ++ mbox_free_channel(vm_dev->mmio_wr_ch); + unregister_virtio_device(&vm_dev->vdev); + + return 0; +diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c +index 7d320f799ca1..dae64a48bf9e 100644 +--- a/drivers/virtio/virtio_ring.c ++++ b/drivers/virtio/virtio_ring.c +@@ -572,8 +572,6 @@ static inline int virtqueue_add_split(struct virtqueue *_vq, + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */ +- if (out_sgs) +- vq->notify(&vq->vq); + if (indirect) + kfree(desc); + END_USE(vq); +diff --git a/drivers/virtio/virtio_trans.c b/drivers/virtio/virtio_trans.c +new file mode 100644 +index 000000000000..4fe106af4162 +--- /dev/null ++++ b/drivers/virtio/virtio_trans.c +@@ -0,0 +1,793 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MAX_TEST_LEN 2048 ++#define VT_TIMES 1000 ++#define INDIRECT_NUM 1 ++#define WAIT_TO_MS 2000 ++ ++#define VT_TXRX_BIT (1 << 0) ++#define VT_B_COPY_BIT (1 << 1) ++#define VT_F_COPY_BIT (1 << 2) ++#define VT_B_MODE_BIT (1 << 3) ++#define VT_F_MODE_BIT (1 << 4) ++ ++static u32 poll_delay = 10; ++ ++struct virtio_trans { ++ /* The virtio device we're associated with */ ++ struct virtio_device *vdev; ++ struct device *dev; ++ wait_queue_head_t waitqueue; ++ struct work_struct config_work; ++ ++ struct virtqueue *tx_vq, *rx_vq; ++ void *tx_buf, *rx_buf; ++ spinlock_t txvq_lock; ++ spinlock_t rxvq_lock; ++ struct scatterlist *tx_sgl; ++ struct scatterlist *rx_sgl; ++ size_t tx_vring_size; ++ size_t rx_vring_size; ++ ++ bool vt_running; ++ u32 regression; ++ size_t pkt_size; ++ bool tx; ++ bool do_copy; ++ bool poll_mode; ++ bool back_poll_mode; ++ void *data_buf; ++}; ++ ++static void tx_intr(struct virtqueue *vq) ++{ ++ struct virtio_trans *vt = vq->vdev->priv; ++ ++ if (vt->poll_mode) ++ return; ++ ++ if (!vt->vt_running) ++ return; ++ ++ if (!vt->tx) ++ return; ++ ++ wake_up_interruptible(&vt->waitqueue); ++} ++ ++static void virttrans_init_sg(struct scatterlist *sg, size_t sg_num, void *base, size_t len) ++{ ++ int i; ++ ++ sg_init_table(sg, sg_num); ++ if (likely(is_vmalloc_addr(base))) ++ for (i = 0; i < sg_num; i++) ++ sg_set_page(&sg[i], vmalloc_to_page(base + i * len), len, ++ offset_in_page(base)); ++ else ++ for (i = 0; i < sg_num; i++) ++ sg_set_buf(&sg[i], base + i * len, len); ++} ++ ++/* call this func with lock */ ++static void virttrans_queue_txbuf(struct virtio_trans *vt, ++ struct scatterlist *sg, ++ void *buf, size_t pkt_size) ++{ ++ if (vt->do_copy) ++ memcpy(buf, vt->data_buf, pkt_size); ++ virtqueue_add_outbuf(vt->tx_vq, sg, INDIRECT_NUM, buf, GFP_ATOMIC); ++} ++ ++static void virttrans_reclaim_txvq(struct virtio_trans *vt) ++{ ++ void *buf; ++ u32 len; ++ ++ spin_lock(&vt->txvq_lock); ++ while ((buf = virtqueue_get_buf(vt->tx_vq, &len)) != NULL) ++ ; ++ spin_unlock(&vt->txvq_lock); ++ ++} ++ ++static void virttrans_fill_txvq(struct virtio_trans *vt, size_t pkt_size) ++{ ++ int i; ++ ++ spin_lock(&vt->txvq_lock); ++ for (i = 0; i < vt->tx_vring_size; i++) { ++ virttrans_init_sg(&vt->tx_sgl[i * INDIRECT_NUM], INDIRECT_NUM, ++ vt->tx_buf + i * MAX_TEST_LEN * INDIRECT_NUM, ++ pkt_size); ++ virttrans_queue_txbuf(vt, &vt->tx_sgl[i * INDIRECT_NUM], ++ vt->tx_buf + i * MAX_TEST_LEN * ++ INDIRECT_NUM, pkt_size); ++ } ++ spin_unlock(&vt->txvq_lock); ++} ++ ++static void *vt_get_tx_buf(struct virtio_trans *vt) ++{ ++ void *buf; ++ int len; ++ ++ spin_lock(&vt->txvq_lock); ++ buf = virtqueue_get_buf(vt->tx_vq, &len); ++ spin_unlock(&vt->txvq_lock); ++ ++ return buf; ++} ++static int tx_pkts(struct virtio_trans *vt, size_t pkt_size, u32 times) ++{ ++ ++ ktime_t start_time, stop_time; ++ u32 cnt = times; ++ u64 time_period; ++ u32 tx_count; ++ int err; ++ int i; ++ u64 idx = 0; ++ void *buf; ++ ++ if (pkt_size > MAX_TEST_LEN) { ++ dev_err(vt->dev, "pkt_size must be less than 0x%x\n", ++ MAX_TEST_LEN); ++ return -EINVAL; ++ } ++ ++ start_time = ktime_get(); ++ ++ while (vt->vt_running && cnt > 0) { ++ buf = vt_get_tx_buf(vt); ++ while (!buf) { ++ if (!vt->back_poll_mode) ++ virtqueue_kick(vt->tx_vq); ++ ++ if (vt->poll_mode) { ++ udelay(poll_delay); ++ buf = vt_get_tx_buf(vt); ++ continue; ++ } ++ ++ err = wait_event_interruptible_timeout(vt->waitqueue, ++ (buf = vt_get_tx_buf(vt)), ++ msecs_to_jiffies(WAIT_TO_MS)); ++ if (err == -ERESTARTSYS) { ++ dev_info(vt->dev, "%s: interrupt the waiting for tx buffer by signal\n", ++ __func__); ++ goto out; ++ } ++ } ++ ++ spin_lock(&vt->txvq_lock); ++ virttrans_queue_txbuf(vt, &vt->tx_sgl[idx % vt->tx_vring_size], ++ buf, vt->pkt_size); ++ idx++; ++ spin_unlock(&vt->txvq_lock); ++ ++ cnt -= INDIRECT_NUM; ++ } ++ ++out: ++ i = 100; ++ while (i--) { ++ virtqueue_kick(vt->tx_vq); ++ virtio_cread_le(vt->vdev, struct virtio_trans_config, ++ tx_count, &tx_count); ++ if (tx_count >= times - cnt) ++ break; ++ mdelay(10); ++ } ++ ++ if (i < 0) ++ dev_err(vt->dev, "Wait backend completion timeout\n"); ++ ++ stop_time = ktime_get(); ++ time_period = ktime_us_delta(stop_time, start_time); ++ ++ pr_info("tx_test: pkt_size (%zu B), pkt_cnt (%d), period (%llu us)\n", ++ pkt_size, times - cnt, time_period); ++ ++ return err; ++} ++ ++/* call this func with lock */ ++static void virttrans_queue_rxbuf(struct virtio_trans *vt, ++ struct scatterlist *sg, ++ void *buf, size_t pkt_size) ++{ ++ if (vt->do_copy) ++ memcpy(vt->data_buf, buf, pkt_size); ++ virtqueue_add_inbuf(vt->rx_vq, sg, INDIRECT_NUM, buf, GFP_ATOMIC); ++} ++ ++static void virttrans_reclaim_rxvq(struct virtio_trans *vt) ++{ ++ void *buf; ++ u32 len; ++ ++ spin_lock(&vt->rxvq_lock); ++ while ((buf = virtqueue_get_buf(vt->rx_vq, &len)) != NULL) ++ ; ++ spin_unlock(&vt->rxvq_lock); ++ ++} ++ ++static void virttrans_fill_rxvq(struct virtio_trans *vt, size_t pkt_size) ++{ ++ int i; ++ ++ spin_lock(&vt->rxvq_lock); ++ for (i = 0; i < vt->rx_vring_size; i++) { ++ virttrans_init_sg(&vt->rx_sgl[i * INDIRECT_NUM], INDIRECT_NUM, ++ vt->rx_buf + i * MAX_TEST_LEN * INDIRECT_NUM, ++ pkt_size); ++ virttrans_queue_rxbuf(vt, &vt->rx_sgl[i * INDIRECT_NUM], ++ vt->rx_buf + i * MAX_TEST_LEN * ++ INDIRECT_NUM, pkt_size); ++ } ++ spin_unlock(&vt->rxvq_lock); ++} ++ ++static void rx_intr(struct virtqueue *vq) ++{ ++ struct virtio_trans *vt = vq->vdev->priv; ++ ++ if (vt->poll_mode) ++ return; ++ ++ if (!vt->vt_running) ++ return; ++ ++ if (vt->tx) ++ return; ++ ++ wake_up_interruptible(&vt->waitqueue); ++} ++ ++static void *vt_get_rx_buf(struct virtio_trans *vt) ++{ ++ void *buf; ++ int len; ++ ++ spin_lock(&vt->rxvq_lock); ++ buf = virtqueue_get_buf(vt->rx_vq, &len); ++ spin_unlock(&vt->rxvq_lock); ++ ++ return buf; ++} ++ ++static int rx_pkts(struct virtio_trans *vt, size_t pkt_size, u32 times) ++{ ++ ++ ktime_t start_time, stop_time; ++ u32 cnt = times; ++ u64 time_period; ++ int err; ++ void *buf; ++ u64 idx = 0; ++ ++ if (pkt_size > MAX_TEST_LEN) { ++ dev_err(vt->dev, "pkt_size must be less than 0x%x\n", ++ MAX_TEST_LEN); ++ return -EINVAL; ++ } ++ ++ start_time = ktime_get(); ++ while (vt->vt_running && cnt > 0) { ++ buf = vt_get_rx_buf(vt); ++ while (!buf) { ++ if (!vt->back_poll_mode) ++ virtqueue_kick(vt->rx_vq); ++ ++ if (vt->poll_mode) { ++ udelay(poll_delay); ++ buf = vt_get_rx_buf(vt); ++ continue; ++ } ++ ++ err = wait_event_interruptible_timeout(vt->waitqueue, ++ (buf = vt_get_rx_buf(vt)), ++ msecs_to_jiffies(WAIT_TO_MS)); ++ if (err == -ERESTARTSYS) { ++ dev_info(vt->dev, "%s: interrupt the waiting for rx buffer by signal\n", ++ __func__); ++ goto out; ++ } ++ } ++ ++ spin_lock(&vt->rxvq_lock); ++ virttrans_queue_rxbuf(vt, &vt->rx_sgl[idx % vt->rx_vring_size], ++ buf, vt->pkt_size); ++ idx++; ++ spin_unlock(&vt->rxvq_lock); ++ ++ cnt -= INDIRECT_NUM; ++ } ++ ++out: ++ stop_time = ktime_get(); ++ time_period = ktime_us_delta(stop_time, start_time); ++ ++ pr_info("rx_test: pkt_size (%zu B), pkt_cnt (%d), period (%llu us)\n", ++ pkt_size, times - cnt, time_period); ++ ++ return 0; ++} ++ ++static ssize_t vt_control_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct virtio_trans *vt = dev_get_drvdata(dev); ++ u32 control; ++ ++ virtio_cread_le(vt->vdev, struct virtio_trans_config, control, ++ &control); ++ ++ return sprintf(buf, "0: stop\n1: start\ncontrol = %u\n", control); ++} ++ ++static ssize_t vt_control_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t cnt) ++{ ++ struct virtio_trans *vt = dev_get_drvdata(dev); ++ u32 control; ++ unsigned long val; ++ ++ if (kstrtoul(buf, 0, &val) < 0 || (val > 1)) { ++ dev_err(vt->dev, "Invalid param\n"); ++ pr_info("0: stop\n"); ++ pr_info("1: start\n"); ++ return -EINVAL; ++ } ++ ++ virtio_cread_le(vt->vdev, struct virtio_trans_config, control, ++ &control); ++ ++ if (!val) { ++ control &= ~VT_CTRL_START; ++ virtio_cwrite_le(vt->vdev, struct virtio_trans_config, control, ++ &control); ++ vt->vt_running = 0; ++ if (!vt->tx) ++ virttrans_reclaim_rxvq(vt); ++ else ++ virttrans_reclaim_txvq(vt); ++ ++ vt->regression = 0; ++ return cnt; ++ } ++ ++ if (vt->vt_running) { ++ dev_err(vt->dev, "Try again after this test completed\n"); ++ return -EAGAIN; ++ } ++ ++ vt->vt_running = 1; ++ control |= VT_CTRL_START; ++ ++ if (!vt->tx) { ++ virttrans_fill_rxvq(vt, vt->pkt_size); ++ virtio_cwrite_le(vt->vdev, struct virtio_trans_config, control, ++ &control); ++ rx_pkts(vt, vt->pkt_size, vt->regression); ++ } else { ++ virttrans_fill_txvq(vt, vt->pkt_size); ++ virtio_cwrite_le(vt->vdev, struct virtio_trans_config, control, ++ &control); ++ tx_pkts(vt, vt->pkt_size, vt->regression); ++ } ++ ++ control &= ~VT_CTRL_START; ++ virtio_cwrite_le(vt->vdev, struct virtio_trans_config, control, ++ &control); ++ ++ vt->vt_running = 0; ++ vt->regression = 0; ++ ++ return cnt; ++} ++ ++static ssize_t vt_config_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t cnt) ++{ ++ struct virtio_trans *vt = dev_get_drvdata(dev); ++ u32 config; ++ unsigned long val; ++ ++ if (vt->vt_running) { ++ dev_err(dev, "Try again after this test completed\n"); ++ return -EAGAIN; ++ } ++ ++ if (kstrtoul(buf, 0, &val) < 0 || (val & ~0x1f)) { ++ dev_err(dev, "Invalid param\n"); ++ pr_info("bit[0]:\n"); ++ pr_info("\t0: TX\n"); ++ pr_info("\t1: RX\n"); ++ pr_info("bit[1]:\n"); ++ pr_info("\t0: Backend do NOT copy buffer\n"); ++ pr_info("\t1: Backend do copy buffer\n"); ++ pr_info("bit[2]:\n"); ++ pr_info("\t0: Frontend do NOT copy buffer\n"); ++ pr_info("\t1: Frontend do copy buffer\n"); ++ pr_info("bit[3]:\n"); ++ pr_info("\t0: Backend interrupt mode\n"); ++ pr_info("\t1: Backend polling mode\n"); ++ pr_info("bit[4]:\n"); ++ pr_info("\t0: Frontend interrupt mode\n"); ++ pr_info("\t1: Frontend polling mode\n"); ++ return -EINVAL; ++ } ++ ++ if (!vt->pkt_size) ++ vt->pkt_size = 64; ++ ++ if (!vt->regression) { ++ virtio_cwrite_le(vt->vdev, struct virtio_trans_config, ++ regression, &vt->regression); ++ vt->regression = VT_TIMES; ++ } ++ ++ virtio_cread_le(vt->vdev, struct virtio_trans_config, config, &config); ++ ++ config &= ~(VT_CFG_TX | VT_CFG_RX | VT_CFG_COPY | ++ VT_CFG_B_POLL | VT_CFG_F_POLL); ++ vt->do_copy = false; ++ vt->back_poll_mode = false; ++ vt->poll_mode = false; ++ ++ if (val & VT_TXRX_BIT) { ++ vt->tx = false; ++ config |= VT_CFG_RX; ++ } else { ++ vt->tx = true; ++ config |= VT_CFG_TX; ++ } ++ ++ if (val & VT_B_COPY_BIT) ++ config |= VT_CFG_COPY; ++ ++ if (val & VT_F_COPY_BIT) ++ vt->do_copy = true; ++ ++ if (val & VT_B_MODE_BIT) { ++ vt->back_poll_mode = true; ++ config |= VT_CFG_B_POLL; ++ } ++ ++ if (val & VT_F_MODE_BIT) { ++ vt->poll_mode = true; ++ config |= VT_CFG_F_POLL; ++ } ++ ++ pr_info("*********************************************************\n"); ++ pr_info("Front-end: %s mode\n", vt->poll_mode ? "poll" : "interrupt"); ++ pr_info("Back-end: %s mode\n", vt->back_poll_mode ? "poll" : "interrupt"); ++ pr_info("Front-end: do %scopy buffer\n", vt->do_copy ? "" : "NOT "); ++ pr_info("Back-end: do %scopy buffer\n", (config & VT_CFG_COPY) ? "" : "NOT"); ++ ++ pr_info("\tTest case: %s\n\tpkt_size: <%zu>\n\tregress times: <%d>\n\n", ++ vt->tx ? "TX" : "RX", vt->pkt_size, vt->regression); ++ ++ virtio_cwrite_le(vt->vdev, struct virtio_trans_config, config, &config); ++ ++ return cnt; ++} ++static ssize_t vt_config_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct virtio_trans *vt = dev_get_drvdata(dev); ++ u32 config; ++ ++ virtio_cread_le(vt->vdev, struct virtio_trans_config, config, &config); ++ ++ return sprintf(buf, "0x%x\n", config); ++} ++ ++static ssize_t vt_pkt_size_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t cnt) ++{ ++ struct virtio_trans *vt = dev_get_drvdata(dev); ++ u32 val; ++ ++ if (vt->vt_running) { ++ dev_err(dev, "Try again after this test completed\n"); ++ return -EAGAIN; ++ } ++ ++ if (kstrtou32(buf, 0, &val) < 0 || (val > MAX_TEST_LEN)) { ++ dev_err(dev, "pkt_size must be less then 0x%x\n", MAX_TEST_LEN); ++ return -EINVAL; ++ } ++ ++ vt->pkt_size = val; ++ virtio_cwrite_le(vt->vdev, struct virtio_trans_config, pkt_size, &val); ++ ++ dev_dbg(dev, "Update pkt_size to %u\n", val); ++ ++ return cnt; ++} ++static ssize_t vt_pkt_size_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct virtio_trans *vt = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%ld\n", vt->pkt_size); ++} ++ ++static ssize_t vt_regression_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t cnt) ++{ ++ struct virtio_trans *vt = dev_get_drvdata(dev); ++ u32 val; ++ ++ if (vt->vt_running) { ++ dev_err(dev, "Try again after this test completed\n"); ++ return -EAGAIN; ++ } ++ ++ if (kstrtou32(buf, 0, &val) < 0 || val <= 0) { ++ dev_err(dev, "Invalid regression\n"); ++ return -EINVAL; ++ } ++ ++ vt->regression = val; ++ ++ virtio_cwrite_le(vt->vdev, struct virtio_trans_config, regression, ++ &val); ++ dev_dbg(dev, "Update regression to %u\n", val); ++ ++ return cnt; ++} ++static ssize_t vt_regression_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct virtio_trans *vt = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%u\n", vt->regression); ++} ++ ++static DEVICE_ATTR_RW(vt_control); ++static DEVICE_ATTR_RW(vt_config); ++static DEVICE_ATTR_RW(vt_pkt_size); ++static DEVICE_ATTR_RW(vt_regression); ++ ++static struct attribute *sysfs_entries[] = { ++ &dev_attr_vt_config.attr, ++ &dev_attr_vt_control.attr, ++ &dev_attr_vt_pkt_size.attr, ++ &dev_attr_vt_regression.attr, ++ NULL ++}; ++ ++static const struct attribute_group vt_attribute_group = { ++ .name = NULL, /* put in device directory */ ++ .attrs = sysfs_entries, ++}; ++ ++static void config_intr(struct virtio_device *vdev) ++{ ++ struct virtio_trans *vt = vdev->priv; ++ ++ schedule_work(&vt->config_work); ++} ++ ++static void config_work_handler(struct work_struct *work) ++{ ++ /* handle the config change */ ++} ++ ++static int virttrans_init_vqs(struct virtio_trans *vt) ++{ ++ struct virtqueue *vqs[2]; ++ vq_callback_t *cbs[] = { tx_intr, rx_intr}; ++ static const char * const names[] = { "tx", "rx" }; ++ int err; ++ ++ err = virtio_find_vqs(vt->vdev, 2, vqs, cbs, names, NULL); ++ if (err) { ++ dev_err(vt->dev, "Failed to find vqs\n"); ++ return err; ++ } ++ vt->tx_vq = vqs[0]; ++ vt->rx_vq = vqs[1]; ++ ++ return 0; ++} ++ ++static int virttrans_probe(struct virtio_device *vdev) ++{ ++ struct virtio_trans *vt; ++ int err; ++ dma_addr_t tx_buf_dma; ++ ++ vt = kzalloc(sizeof(*vt), GFP_KERNEL); ++ if (!vt) ++ return -ENOMEM; ++ ++ vdev->priv = vt; ++ vt->vdev = vdev; ++ vt->dev = &vdev->dev; ++ vt->pkt_size = 64; ++ vt->regression = VT_TIMES; ++ ++ dev_set_drvdata(vt->dev, vt); ++ ++ err = virttrans_init_vqs(vt); ++ if (err) { ++ dev_err(vt->dev, "Failed to init virtqueues\n"); ++ goto err_init_vq; ++ } ++ ++ vt->tx_vring_size = virtqueue_get_vring_size(vt->tx_vq); ++ vt->rx_vring_size = virtqueue_get_vring_size(vt->rx_vq); ++ ++ vt->data_buf = kzalloc(MAX_TEST_LEN, GFP_KERNEL); ++ if (!vt->data_buf) { ++ err = -ENOMEM; ++ dev_err(vt->dev, "Failed to alloc data buffer\n"); ++ goto alloc_data_buf; ++ } ++ ++ memset(vt->data_buf, 0xa5, MAX_TEST_LEN); ++ ++ vt->tx_sgl = kzalloc(INDIRECT_NUM * (vt->rx_vring_size + ++ vt->tx_vring_size) * sizeof(struct scatterlist), ++ GFP_KERNEL); ++ ++ if (!vt->tx_sgl) { ++ err = -ENOMEM; ++ dev_err(vt->dev, "Failed to alloc SG descriptors\n"); ++ goto alloc_sgl; ++ } ++ ++ vt->rx_sgl = vt->tx_sgl + INDIRECT_NUM * vt->tx_vring_size; ++ ++ vt->tx_buf = dma_alloc_coherent(vdev->dev.parent, MAX_TEST_LEN * ++ INDIRECT_NUM * (vt->rx_vring_size + ++ vt->tx_vring_size), &tx_buf_dma, ++ GFP_KERNEL); ++ if (!vt->tx_buf) { ++ err = -ENOMEM; ++ dev_err(vt->dev, "Failed to alloc Vring buffers\n"); ++ goto alloc_buf; ++ } ++ ++ vt->rx_buf = vt->tx_buf + ++ MAX_TEST_LEN * INDIRECT_NUM * vt->tx_vring_size; ++ ++ spin_lock_init(&vt->txvq_lock); ++ spin_lock_init(&vt->rxvq_lock); ++ init_waitqueue_head(&vt->waitqueue); ++ INIT_WORK(&vt->config_work, &config_work_handler); ++ ++ err = sysfs_create_group(&vt->dev->kobj, &vt_attribute_group); ++ if (err) { ++ dev_err(vt->dev, "Failed to create sysfs device attributes\n"); ++ goto sysfs; ++ } else { ++ /* ++ * Generate a udev event so that appropriate ++ * symlinks can be created based on udev ++ * rules. ++ */ ++ kobject_uevent(&vt->dev->kobj, KOBJ_CHANGE); ++ } ++ ++ virtio_device_ready(vt->vdev); ++ ++ return 0; ++ ++sysfs: ++ dma_free_coherent(vdev->dev.parent, MAX_TEST_LEN * INDIRECT_NUM * ++ (vt->rx_vring_size + vt->tx_vring_size), ++ vt->tx_buf, tx_buf_dma); ++alloc_buf: ++ kfree(vt->tx_sgl); ++alloc_sgl: ++ kfree(vt->data_buf); ++alloc_data_buf: ++ /* TODO: vtrans_deinit_vq(); */ ++err_init_vq: ++ kfree(vt); ++ ++ return err; ++} ++ ++static void virttrans_remove(struct virtio_device *vdev) ++{ ++ struct virtio_trans *vt = vdev->priv; ++ void *buf; ++ ++ virtio_break_device(vdev); ++ flush_work(&vt->config_work); ++ vdev->config->reset(vdev); ++ cancel_work_sync(&vt->config_work); ++ ++ sysfs_remove_group(&vt->dev->kobj, &vt_attribute_group); ++ ++ while ((buf = virtqueue_detach_unused_buf(vt->tx_vq)) != NULL) ++ kfree(buf); ++ vdev->config->del_vqs(vdev); ++ ++ kfree(vt); ++} ++ ++static const struct virtio_device_id id_table[] = { ++ { VIRTIO_ID_TRANS, VIRTIO_DEV_ANY_ID }, ++ { 0 }, ++}; ++ ++static const unsigned int features[] = { ++ /* none */ ++}; ++ ++ ++static struct virtio_driver virtio_transfer = { ++ .feature_table = features, ++ .feature_table_size = ARRAY_SIZE(features), ++ .driver.name = KBUILD_MODNAME, ++ .driver.owner = THIS_MODULE, ++ .id_table = id_table, ++ .probe = virttrans_probe, ++ .remove = virttrans_remove, ++ .config_changed = config_intr, ++}; ++ ++static int __init virtio_transfer_init(void) ++{ ++ int err; ++ ++ err = register_virtio_driver(&virtio_transfer); ++ if (err < 0) { ++ pr_err("Failed to register virtio driver\n"); ++ return err; ++ } ++ return 0; ++} ++ ++static void __exit virtio_transfer_exit(void) ++{ ++ unregister_virtio_driver(&virtio_transfer); ++} ++ ++module_init(virtio_transfer_init); ++module_exit(virtio_transfer_exit); ++ ++MODULE_DEVICE_TABLE(virtio, id_table); ++MODULE_DESCRIPTION("VirtIO transfer test driver"); ++MODULE_LICENSE("GPL"); +-- +2.34.1 + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0008-net-add-RT-NXP-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0008-net-add-RT-NXP-support.patch new file mode 100644 index 000000000..814a6ea9d --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0008-net-add-RT-NXP-support.patch @@ -0,0 +1,1486 @@ +From 05d5158bf43c26c1bc70017da27b125cd8d7cb31 Mon Sep 17 00:00:00 2001 +From: Mike Engel +Date: Mon, 26 Feb 2024 09:25:16 +0100 +Subject: [PATCH 08/10] net: add RT NXP support + +Signed-off-by: Mike Engel +--- + net/8021q/vlan_dev.c | 4 +- + net/bridge/br_multicast.c | 4 +- + net/bridge/br_switchdev.c | 24 +++++ + net/bridge/br_vlan.c | 4 +- + net/core/dev.c | 64 ++++++++--- + net/core/drop_monitor.c | 8 +- + net/core/gen_stats.c | 16 +-- + net/core/skbuff.c | 7 +- + net/devlink/leftover.c | 4 +- + net/dsa/Kconfig | 6 ++ + net/dsa/Makefile | 1 + + net/dsa/slave.c | 53 ++++++++- + net/dsa/tag_netc.c | 168 +++++++++++++++++++++++++++++ + net/dsa/tag_sja1105.c | 5 +- + net/ethtool/Makefile | 2 +- + net/ethtool/ioctl.c | 55 ++++++++++ + net/ethtool/netlink.c | 17 +++ + net/ethtool/netlink.h | 3 + + net/ethtool/preempt.c | 191 +++++++++++++++++++++++++++++++++ + net/ipv4/af_inet.c | 4 +- + net/ipv6/seg6_local.c | 4 +- + net/mac80211/sta_info.c | 8 +- + net/mpls/af_mpls.c | 4 +- + net/netfilter/ipvs/ip_vs_ctl.c | 4 +- + net/netfilter/nf_tables_api.c | 4 +- + net/openvswitch/datapath.c | 4 +- + net/openvswitch/flow_table.c | 9 +- + net/packet/af_packet.c | 9 +- + net/sched/sch_cbs.c | 5 + + net/sched/sch_taprio.c | 20 ++++ + net/socket.c | 27 +++++ + net/tsn/genl_tsn.c | 13 ++- + 32 files changed, 679 insertions(+), 72 deletions(-) + create mode 100644 net/dsa/tag_netc.c + create mode 100644 net/ethtool/preempt.c + +diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c +index d3e511e1eba8..0fa52bcc296b 100644 +--- a/net/8021q/vlan_dev.c ++++ b/net/8021q/vlan_dev.c +@@ -712,13 +712,13 @@ static void vlan_dev_get_stats64(struct net_device *dev, + + p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i); + do { +- start = u64_stats_fetch_begin_irq(&p->syncp); ++ start = u64_stats_fetch_begin(&p->syncp); + rxpackets = u64_stats_read(&p->rx_packets); + rxbytes = u64_stats_read(&p->rx_bytes); + rxmulticast = u64_stats_read(&p->rx_multicast); + txpackets = u64_stats_read(&p->tx_packets); + txbytes = u64_stats_read(&p->tx_bytes); +- } while (u64_stats_fetch_retry_irq(&p->syncp, start)); ++ } while (u64_stats_fetch_retry(&p->syncp, start)); + + stats->rx_packets += rxpackets; + stats->rx_bytes += rxbytes; +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index db4f2641d1cd..7e2a9fb5786c 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4899,9 +4899,9 @@ void br_multicast_get_stats(const struct net_bridge *br, + unsigned int start; + + do { +- start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); ++ start = u64_stats_fetch_begin(&cpu_stats->syncp); + memcpy(&temp, &cpu_stats->mstats, sizeof(temp)); +- } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); + + mcast_stats_add_dir(tdst.igmp_v1queries, temp.igmp_v1queries); + mcast_stats_add_dir(tdst.igmp_v2queries, temp.igmp_v2queries); +diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c +index 4b3982c368b3..7e6fbd0962f6 100644 +--- a/net/bridge/br_switchdev.c ++++ b/net/bridge/br_switchdev.c +@@ -171,6 +171,26 @@ br_switchdev_fdb_notify(struct net_bridge *br, + } + } + ++static u16 br_switchdev_get_bridge_vlan_proto(const struct net_device *dev) ++{ ++ const struct net_device *br = NULL; ++ u16 vlan_proto = ETH_P_8021Q; ++ struct net_bridge_port *p; ++ ++ if (netif_is_bridge_master(dev)) { ++ br = dev; ++ } else { ++ p = br_port_get_rtnl_rcu(dev); ++ if (p) ++ br = p->br->dev; ++ } ++ ++ if (br) ++ br_vlan_get_proto(br, &vlan_proto); ++ ++ return vlan_proto; ++} ++ + int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, + bool changed, struct netlink_ext_ack *extack) + { +@@ -182,6 +202,8 @@ int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, + .changed = changed, + }; + ++ v.proto = br_switchdev_get_bridge_vlan_proto(dev); ++ + return switchdev_port_obj_add(dev, &v.obj, extack); + } + +@@ -193,6 +215,8 @@ int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) + .vid = vid, + }; + ++ v.proto = br_switchdev_get_bridge_vlan_proto(dev); ++ + return switchdev_port_obj_del(dev, &v.obj); + } + +diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c +index 9ffd40b8270c..bc75fa1e4666 100644 +--- a/net/bridge/br_vlan.c ++++ b/net/bridge/br_vlan.c +@@ -1389,12 +1389,12 @@ void br_vlan_get_stats(const struct net_bridge_vlan *v, + + cpu_stats = per_cpu_ptr(v->stats, i); + do { +- start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); ++ start = u64_stats_fetch_begin(&cpu_stats->syncp); + rxpackets = u64_stats_read(&cpu_stats->rx_packets); + rxbytes = u64_stats_read(&cpu_stats->rx_bytes); + txbytes = u64_stats_read(&cpu_stats->tx_bytes); + txpackets = u64_stats_read(&cpu_stats->tx_packets); +- } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); + + u64_stats_add(&stats->rx_packets, rxpackets); + u64_stats_add(&stats->rx_bytes, rxbytes); +diff --git a/net/core/dev.c b/net/core/dev.c +index a2e3c6470ab3..bd2ab2988ca5 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -1385,8 +1385,8 @@ static int napi_kthread_create(struct napi_struct *n) + * TASK_INTERRUPTIBLE mode to avoid the blocked task + * warning and work with loadavg. + */ +- n->thread = kthread_run(napi_threaded_poll, n, "napi/%s-%d", +- n->dev->name, n->napi_id); ++ n->thread = kthread_run(napi_threaded_poll, n, "napi/%s-%s", ++ n->dev->name, n->name); + if (IS_ERR(n->thread)) { + err = PTR_ERR(n->thread); + pr_err("kthread_run failed with err %d\n", err); +@@ -4589,15 +4589,6 @@ static void rps_trigger_softirq(void *data) + + #endif /* CONFIG_RPS */ + +-/* Called from hardirq (IPI) context */ +-static void trigger_rx_softirq(void *data) +-{ +- struct softnet_data *sd = data; +- +- __raise_softirq_irqoff(NET_RX_SOFTIRQ); +- smp_store_release(&sd->defer_ipi_scheduled, 0); +-} +- + /* + * Check if this softnet_data structure is another cpu one + * If yes, queue it to our IPI list and return 1 +@@ -6369,8 +6360,9 @@ int dev_set_threaded(struct net_device *dev, bool threaded) + } + EXPORT_SYMBOL(dev_set_threaded); + +-void netif_napi_add_weight(struct net_device *dev, struct napi_struct *napi, +- int (*poll)(struct napi_struct *, int), int weight) ++void netif_napi_add_named(struct net_device *dev, struct napi_struct *napi, ++ int (*poll)(struct napi_struct *, int), int weight, ++ const char *name) + { + if (WARN_ON(test_and_set_bit(NAPI_STATE_LISTED, &napi->state))) + return; +@@ -6397,6 +6389,12 @@ void netif_napi_add_weight(struct net_device *dev, struct napi_struct *napi, + list_add_rcu(&napi->dev_list, &dev->napi_list); + napi_hash_add(napi); + napi_get_frags_check(napi); ++ ++ if (name) ++ strncpy(napi->name, name, NAPINAMSIZ); ++ else ++ snprintf(napi->name, NAPINAMSIZ, "%d", napi->napi_id); ++ + /* Create kthread for this napi if dev->threaded is set. + * Clear dev->threaded if kthread creation failed so that + * threaded mode will not be enabled in napi_enable(). +@@ -6404,6 +6402,13 @@ void netif_napi_add_weight(struct net_device *dev, struct napi_struct *napi, + if (dev->threaded && napi_kthread_create(napi)) + dev->threaded = 0; + } ++EXPORT_SYMBOL(netif_napi_add_named); ++ ++void netif_napi_add_weight(struct net_device *dev, struct napi_struct *napi, ++ int (*poll)(struct napi_struct *, int), int weight) ++{ ++ netif_napi_add_named(dev, napi, poll, weight, NULL); ++} + EXPORT_SYMBOL(netif_napi_add_weight); + + void napi_disable(struct napi_struct *n) +@@ -6655,6 +6660,30 @@ static void skb_defer_free_flush(struct softnet_data *sd) + } + } + ++#ifndef CONFIG_PREEMPT_RT ++/* Called from hardirq (IPI) context */ ++static void trigger_rx_softirq(void *data) ++{ ++ struct softnet_data *sd = data; ++ ++ __raise_softirq_irqoff(NET_RX_SOFTIRQ); ++ smp_store_release(&sd->defer_ipi_scheduled, 0); ++} ++ ++#else ++ ++static void trigger_rx_softirq(struct work_struct *defer_work) ++{ ++ struct softnet_data *sd; ++ ++ sd = container_of(defer_work, struct softnet_data, defer_work); ++ smp_store_release(&sd->defer_ipi_scheduled, 0); ++ local_bh_disable(); ++ skb_defer_free_flush(sd); ++ local_bh_enable(); ++} ++#endif ++ + static __latent_entropy void net_rx_action(struct softirq_action *h) + { + struct softnet_data *sd = this_cpu_ptr(&softnet_data); +@@ -10476,12 +10505,12 @@ void dev_fetch_sw_netstats(struct rtnl_link_stats64 *s, + + stats = per_cpu_ptr(netstats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&stats->syncp); ++ start = u64_stats_fetch_begin(&stats->syncp); + rx_packets = u64_stats_read(&stats->rx_packets); + rx_bytes = u64_stats_read(&stats->rx_bytes); + tx_packets = u64_stats_read(&stats->tx_packets); + tx_bytes = u64_stats_read(&stats->tx_bytes); +- } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&stats->syncp, start)); + + s->rx_packets += rx_packets; + s->rx_bytes += rx_bytes; +@@ -10607,6 +10636,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, + dev_mc_init(dev); + dev_uc_init(dev); + ++ dev->fast_raw_device = 0; + dev_net_set(dev, &init_net); + + dev->gso_max_size = GSO_LEGACY_MAX_SIZE; +@@ -11396,7 +11426,11 @@ static int __init net_dev_init(void) + INIT_CSD(&sd->csd, rps_trigger_softirq, sd); + sd->cpu = i; + #endif ++#ifndef CONFIG_PREEMPT_RT + INIT_CSD(&sd->defer_csd, trigger_rx_softirq, sd); ++#else ++ INIT_WORK(&sd->defer_work, trigger_rx_softirq); ++#endif + spin_lock_init(&sd->defer_lock); + + init_gro_hash(&sd->backlog); +diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c +index f084a4a6b7ab..11aa6e8a3098 100644 +--- a/net/core/drop_monitor.c ++++ b/net/core/drop_monitor.c +@@ -1432,9 +1432,9 @@ static void net_dm_stats_read(struct net_dm_stats *stats) + u64 dropped; + + do { +- start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); ++ start = u64_stats_fetch_begin(&cpu_stats->syncp); + dropped = u64_stats_read(&cpu_stats->dropped); +- } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); + + u64_stats_add(&stats->dropped, dropped); + } +@@ -1476,9 +1476,9 @@ static void net_dm_hw_stats_read(struct net_dm_stats *stats) + u64 dropped; + + do { +- start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); ++ start = u64_stats_fetch_begin(&cpu_stats->syncp); + dropped = u64_stats_read(&cpu_stats->dropped); +- } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); + + u64_stats_add(&stats->dropped, dropped); + } +diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c +index c8d137ef5980..b71ccaec0991 100644 +--- a/net/core/gen_stats.c ++++ b/net/core/gen_stats.c +@@ -135,10 +135,10 @@ static void gnet_stats_add_basic_cpu(struct gnet_stats_basic_sync *bstats, + u64 bytes, packets; + + do { +- start = u64_stats_fetch_begin_irq(&bcpu->syncp); ++ start = u64_stats_fetch_begin(&bcpu->syncp); + bytes = u64_stats_read(&bcpu->bytes); + packets = u64_stats_read(&bcpu->packets); +- } while (u64_stats_fetch_retry_irq(&bcpu->syncp, start)); ++ } while (u64_stats_fetch_retry(&bcpu->syncp, start)); + + t_bytes += bytes; + t_packets += packets; +@@ -162,10 +162,10 @@ void gnet_stats_add_basic(struct gnet_stats_basic_sync *bstats, + } + do { + if (running) +- start = u64_stats_fetch_begin_irq(&b->syncp); ++ start = u64_stats_fetch_begin(&b->syncp); + bytes = u64_stats_read(&b->bytes); + packets = u64_stats_read(&b->packets); +- } while (running && u64_stats_fetch_retry_irq(&b->syncp, start)); ++ } while (running && u64_stats_fetch_retry(&b->syncp, start)); + + _bstats_update(bstats, bytes, packets); + } +@@ -187,10 +187,10 @@ static void gnet_stats_read_basic(u64 *ret_bytes, u64 *ret_packets, + u64 bytes, packets; + + do { +- start = u64_stats_fetch_begin_irq(&bcpu->syncp); ++ start = u64_stats_fetch_begin(&bcpu->syncp); + bytes = u64_stats_read(&bcpu->bytes); + packets = u64_stats_read(&bcpu->packets); +- } while (u64_stats_fetch_retry_irq(&bcpu->syncp, start)); ++ } while (u64_stats_fetch_retry(&bcpu->syncp, start)); + + t_bytes += bytes; + t_packets += packets; +@@ -201,10 +201,10 @@ static void gnet_stats_read_basic(u64 *ret_bytes, u64 *ret_packets, + } + do { + if (running) +- start = u64_stats_fetch_begin_irq(&b->syncp); ++ start = u64_stats_fetch_begin(&b->syncp); + *ret_bytes = u64_stats_read(&b->bytes); + *ret_packets = u64_stats_read(&b->packets); +- } while (running && u64_stats_fetch_retry_irq(&b->syncp, start)); ++ } while (running && u64_stats_fetch_retry(&b->syncp, start)); + } + + static int +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index bacc7b486a7f..1339c79171a7 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -6705,6 +6705,11 @@ nodefer: __kfree_skb(skb); + /* Make sure to trigger NET_RX_SOFTIRQ on the remote CPU + * if we are unlucky enough (this seems very unlikely). + */ +- if (unlikely(kick) && !cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) ++ if (unlikely(kick) && !cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) { ++#ifndef CONFIG_PREEMPT_RT + smp_call_function_single_async(cpu, &sd->defer_csd); ++#else ++ schedule_work_on(cpu, &sd->defer_work); ++#endif ++ } + } +diff --git a/net/devlink/leftover.c b/net/devlink/leftover.c +index 032c7af065cd..94e8cc3de330 100644 +--- a/net/devlink/leftover.c ++++ b/net/devlink/leftover.c +@@ -8307,10 +8307,10 @@ static void devlink_trap_stats_read(struct devlink_stats __percpu *trap_stats, + + cpu_stats = per_cpu_ptr(trap_stats, i); + do { +- start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); ++ start = u64_stats_fetch_begin(&cpu_stats->syncp); + rx_packets = u64_stats_read(&cpu_stats->rx_packets); + rx_bytes = u64_stats_read(&cpu_stats->rx_bytes); +- } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); + + u64_stats_add(&stats->rx_packets, rx_packets); + u64_stats_add(&stats->rx_bytes, rx_bytes); +diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig +index 3eef72ce99a4..0feef61007ef 100644 +--- a/net/dsa/Kconfig ++++ b/net/dsa/Kconfig +@@ -166,4 +166,10 @@ config NET_DSA_TAG_XRS700X + Say Y or M if you want to enable support for tagging frames for + Arrow SpeedChips XRS700x switches that use a single byte tag trailer. + ++config NET_DSA_TAG_NETC ++ tristate "Tag driver for NETC family of switches, using VLAN" ++ help ++ Say Y or M if you want to enable support for tagging frames with a ++ NXP NETC switch family. The custom 802.1Q VLAN header is available. ++ + endif +diff --git a/net/dsa/Makefile b/net/dsa/Makefile +index bf57ef3bce2a..640101b28f33 100644 +--- a/net/dsa/Makefile ++++ b/net/dsa/Makefile +@@ -29,3 +29,4 @@ obj-$(CONFIG_NET_DSA_TAG_RZN1_A5PSW) += tag_rzn1_a5psw.o + obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o + obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o + obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o ++obj-$(CONFIG_NET_DSA_TAG_NETC) += tag_netc.o +\ No newline at end of file +diff --git a/net/dsa/slave.c b/net/dsa/slave.c +index 5fe075bf479e..637568fcedac 100644 +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -976,12 +976,12 @@ static void dsa_slave_get_ethtool_stats(struct net_device *dev, + + s = per_cpu_ptr(dev->tstats, i); + do { +- start = u64_stats_fetch_begin_irq(&s->syncp); ++ start = u64_stats_fetch_begin(&s->syncp); + tx_packets = u64_stats_read(&s->tx_packets); + tx_bytes = u64_stats_read(&s->tx_bytes); + rx_packets = u64_stats_read(&s->rx_packets); + rx_bytes = u64_stats_read(&s->rx_bytes); +- } while (u64_stats_fetch_retry_irq(&s->syncp, start)); ++ } while (u64_stats_fetch_retry(&s->syncp, start)); + data[0] += tx_packets; + data[1] += tx_bytes; + data[2] += rx_packets; +@@ -1610,6 +1610,41 @@ static int dsa_slave_get_ts_info(struct net_device *dev, + return ds->ops->get_ts_info(ds, p->dp->index, ts); + } + ++static int dsa_slave_reset_preempt(struct net_device *dev, bool enable) ++{ ++ struct dsa_slave_priv *p = netdev_priv(dev); ++ struct dsa_switch *ds = p->dp->ds; ++ ++ if (!ds->ops->reset_preempt) ++ return -EOPNOTSUPP; ++ ++ return ds->ops->reset_preempt(ds, p->dp->index, enable); ++} ++ ++static int dsa_slave_set_preempt(struct net_device *dev, ++ struct ethtool_fp *fpcmd) ++{ ++ struct dsa_slave_priv *p = netdev_priv(dev); ++ struct dsa_switch *ds = p->dp->ds; ++ ++ if (!ds->ops->set_preempt) ++ return -EOPNOTSUPP; ++ ++ return ds->ops->set_preempt(ds, p->dp->index, fpcmd); ++} ++ ++static int dsa_slave_get_preempt(struct net_device *dev, ++ struct ethtool_fp *fpcmd) ++{ ++ struct dsa_slave_priv *p = netdev_priv(dev); ++ struct dsa_switch *ds = p->dp->ds; ++ ++ if (!ds->ops->get_preempt) ++ return -EOPNOTSUPP; ++ ++ return ds->ops->get_preempt(ds, p->dp->index, fpcmd); ++} ++ + static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, + u16 vid) + { +@@ -1619,6 +1654,7 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, + .vid = vid, + /* This API only allows programming tagged, non-PVID VIDs */ + .flags = 0, ++ .proto = ntohs(proto), + }; + struct netlink_ext_ack extack = {0}; + int ret; +@@ -1632,6 +1668,7 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, + } + + /* And CPU port... */ ++ vlan.proto = ETH_P_8021Q; + ret = dsa_port_host_vlan_add(dp, &vlan, &extack); + if (ret) { + if (extack._msg) +@@ -1651,6 +1688,7 @@ static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, + .vid = vid, + /* This API only allows programming tagged, non-PVID VIDs */ + .flags = 0, ++ .proto = ntohs(proto), + }; + int err; + +@@ -2159,6 +2197,9 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = { + .set_rxnfc = dsa_slave_set_rxnfc, + .get_ts_info = dsa_slave_get_ts_info, + .self_test = dsa_slave_net_selftest, ++ .set_preempt = dsa_slave_set_preempt, ++ .get_preempt = dsa_slave_get_preempt, ++ .reset_preempt = dsa_slave_reset_preempt, + }; + + static const struct dcbnl_rtnl_ops __maybe_unused dsa_slave_dcbnl_ops = { +@@ -2770,7 +2811,9 @@ dsa_slave_check_8021q_upper(struct net_device *dev, + struct net_device *br = dsa_port_bridge_dev_get(dp); + struct bridge_vlan_info br_info; + struct netlink_ext_ack *extack; ++ bool update_proto = false; + int err = NOTIFY_DONE; ++ u16 br_proto, proto; + u16 vid; + + if (!br || !br_vlan_enabled(br)) +@@ -2778,13 +2821,17 @@ dsa_slave_check_8021q_upper(struct net_device *dev, + + extack = netdev_notifier_info_to_extack(&info->info); + vid = vlan_dev_vlan_id(info->upper_dev); ++ proto = vlan_dev_vlan_proto(info->upper_dev); ++ err = br_vlan_get_proto(br, &br_proto); ++ if (err == 0 && br_proto != proto) ++ update_proto = true; + + /* br_vlan_get_info() returns -EINVAL or -ENOENT if the + * device, respectively the VID is not found, returning + * 0 means success, which is a failure for us here. + */ + err = br_vlan_get_info(br, vid, &br_info); +- if (err == 0) { ++ if (err == 0 && !update_proto) { + NL_SET_ERR_MSG_MOD(extack, + "This VLAN is already configured by the bridge"); + return notifier_from_errno(-EBUSY); +diff --git a/net/dsa/tag_netc.c b/net/dsa/tag_netc.c +new file mode 100644 +index 000000000000..84bf6e02cf12 +--- /dev/null ++++ b/net/dsa/tag_netc.c +@@ -0,0 +1,168 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include ++#include ++#include ++#include "dsa_priv.h" ++ ++struct netc_tagger_private { ++ struct netc_tagger_data data; /* Must be first */ ++ struct kthread_worker *xmit_worker; ++}; ++ ++/* Similar to is_link_local_ether_addr(hdr->h_dest) but also covers PTP */ ++static inline bool netc_is_link_local(const struct sk_buff *skb) ++{ ++ const struct ethhdr *hdr = eth_hdr(skb); ++ u64 dmac = ether_addr_to_u64(hdr->h_dest); ++ ++ if (ntohs(hdr->h_proto) == ETH_P_NETC_META) ++ return false; ++ ++ if ((dmac & NETC_LINKLOCAL_FILTER_A_MASK) == ++ NETC_LINKLOCAL_FILTER_A) ++ return true; ++ ++ return false; ++} ++ ++static struct sk_buff *netc_defer_xmit(struct dsa_port *dp, ++ struct sk_buff *skb) ++{ ++ struct netc_tagger_private *priv = dp->ds->tagger_data; ++ struct netc_tagger_data *data = &priv->data; ++ void (*xmit_work_fn)(struct kthread_work *work); ++ struct netc_deferred_xmit_work *xmit_work; ++ struct kthread_worker *xmit_worker; ++ ++ xmit_work_fn = data->xmit_work_fn; ++ xmit_worker = priv->xmit_worker; ++ ++ if (!xmit_work_fn || !xmit_worker) ++ return NULL; ++ ++ /* PTP over IP packets need UDP checksumming. We may have inherited ++ * NETIF_F_HW_CSUM from the DSA master, but these packets are not sent ++ * through the DSA master, so calculate the checksum here. ++ */ ++ if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) ++ return NULL; ++ ++ xmit_work = kzalloc(sizeof(*xmit_work), GFP_ATOMIC); ++ if (!xmit_work) ++ return NULL; ++ ++ /* Calls felix_port_deferred_xmit in felix.c */ ++ kthread_init_work(&xmit_work->work, xmit_work_fn); ++ /* Increase refcount so the kfree_skb in dsa_slave_xmit ++ * won't really free the packet. ++ */ ++ xmit_work->dp = dp; ++ xmit_work->skb = skb_get(skb); ++ ++ kthread_queue_work(xmit_worker, &xmit_work->work); ++ ++ return NULL; ++} ++ ++static struct sk_buff *netc_xmit(struct sk_buff *skb, ++ struct net_device *netdev) ++{ ++ struct dsa_port *dp = dsa_slave_to_port(netdev); ++ u16 queue_mapping = skb_get_queue_mapping(skb); ++ u8 pcp = netdev_txq_to_tc(netdev, queue_mapping); ++ u16 tx_vid = dsa_tag_8021q_standalone_vid(dp); ++ ++ if (unlikely(netc_is_link_local(skb))) ++ return netc_defer_xmit(dp, skb); ++ ++ return dsa_8021q_xmit(skb, netdev, ETH_P_8021Q, ++ ((pcp << VLAN_PRIO_SHIFT) | tx_vid)); ++} ++ ++static struct sk_buff *netc_rcv(struct sk_buff *skb, ++ struct net_device *netdev) ++{ ++ int src_port = -1, switch_id = -1, vid = -1; ++ struct ethhdr *hdr; ++ bool is_link_local; ++ ++ hdr = eth_hdr(skb); ++ is_link_local = netc_is_link_local(skb); ++ ++ if (is_link_local) { ++ /* Management traffic path. Switch embeds the switch ID and ++ * port ID into bytes of the destination MAC, courtesy of ++ * the incl_srcpt options. ++ */ ++ src_port = hdr->h_dest[3]; ++ switch_id = hdr->h_dest[4]; ++ } ++ ++ if (skb_vlan_tag_present(skb)) ++ /* Normal traffic path. */ ++ dsa_8021q_rcv(skb, &src_port, &switch_id, &vid); ++ else ++ return NULL; ++ ++ skb->dev = dsa_master_find_slave(netdev, switch_id, src_port); ++ if (!skb->dev) { ++ netdev_warn(netdev, "Couldn't decode source port switch-%d port- %d\n", ++ switch_id, src_port); ++ return NULL; ++ } ++ ++ if (!is_link_local) ++ dsa_default_offload_fwd_mark(skb); ++ ++ return skb; ++} ++ ++static void netc_disconnect(struct dsa_switch *ds) ++{ ++ struct netc_tagger_private *priv = ds->tagger_data; ++ ++ kthread_destroy_worker(priv->xmit_worker); ++ kfree(priv); ++ ds->tagger_data = NULL; ++} ++ ++static int netc_connect(struct dsa_switch *ds) ++{ ++ struct netc_tagger_private *priv; ++ int err; ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->xmit_worker = kthread_create_worker(0, "netc_xmit"); ++ if (IS_ERR(priv->xmit_worker)) { ++ err = PTR_ERR(priv->xmit_worker); ++ kfree(priv); ++ return err; ++ } ++ ++ ds->tagger_data = priv; ++ ++ return 0; ++} ++ ++static const struct dsa_device_ops netc_netdev_ops = { ++ .name = "netc-8021q", ++ .proto = DSA_TAG_PROTO_NETC, ++ .xmit = netc_xmit, ++ .rcv = netc_rcv, ++ .connect = netc_connect, ++ .disconnect = netc_disconnect, ++ .needed_headroom = VLAN_HLEN, ++ .promisc_on_master = true, ++}; ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_NETC); ++ ++module_dsa_tag_driver(netc_netdev_ops); +diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c +index 143348962a21..b472d6b2870e 100644 +--- a/net/dsa/tag_sja1105.c ++++ b/net/dsa/tag_sja1105.c +@@ -262,9 +262,8 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb, + struct net_device *netdev) + { + struct dsa_port *dp = dsa_slave_to_port(netdev); +- u16 queue_mapping = skb_get_queue_mapping(skb); +- u8 pcp = netdev_txq_to_tc(netdev, queue_mapping); + u16 tx_vid = dsa_tag_8021q_standalone_vid(dp); ++ u8 pcp = skb->priority; + + if (skb->offload_fwd_mark) + return sja1105_imprecise_xmit(skb, netdev); +@@ -474,7 +473,7 @@ static bool sja1110_skb_has_inband_control_extension(const struct sk_buff *skb) + static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port, + int *switch_id, int *vbid, u16 *vid) + { +- struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)skb_mac_header(skb); ++ struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); + u16 vlan_tci; + + if (skb_vlan_tag_present(skb)) +diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile +index 72ab0944262a..194eeedf7581 100644 +--- a/net/ethtool/Makefile ++++ b/net/ethtool/Makefile +@@ -8,4 +8,4 @@ ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ + linkstate.o debug.o wol.o features.o privflags.o rings.o \ + channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \ + tunnels.o fec.o eeprom.o stats.o phc_vclocks.o module.o \ +- pse-pd.o ++ pse-pd.o preempt.o +diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c +index e31d1247b9f0..a7b4a749fcb3 100644 +--- a/net/ethtool/ioctl.c ++++ b/net/ethtool/ioctl.c +@@ -2713,6 +2713,49 @@ static int ethtool_set_fecparam(struct net_device *dev, void __user *useraddr) + return dev->ethtool_ops->set_fecparam(dev, &fecparam); + } + ++static int ethtool_get_preempt(struct net_device *dev, void __user *useraddr) ++{ ++ struct ethtool_fp fpparam = { .cmd = ETHTOOL_GFP }; ++ int rc; ++ ++ if (!dev->ethtool_ops->get_preempt) ++ return -EOPNOTSUPP; ++ ++ rc = dev->ethtool_ops->get_preempt(dev, &fpparam); ++ if (rc) ++ return rc; ++ ++ if (copy_to_user(useraddr, &fpparam, sizeof(fpparam))) ++ return -EFAULT; ++ return 0; ++} ++ ++static int ethtool_set_preempt(struct net_device *dev, void __user *useraddr) ++{ ++ struct ethtool_fp fpparam; ++ ++ if (!dev->ethtool_ops->set_preempt) ++ return -EOPNOTSUPP; ++ ++ if (copy_from_user(&fpparam, useraddr, sizeof(fpparam))) ++ return -EFAULT; ++ ++ return dev->ethtool_ops->set_preempt(dev, &fpparam); ++} ++ ++static int ethtool_reset_preempt(struct net_device *dev, void __user *useraddr) ++{ ++ struct ethtool_fp fpparam; ++ ++ if (!dev->ethtool_ops->reset_preempt) ++ return -EOPNOTSUPP; ++ ++ if (copy_from_user(&fpparam, useraddr, sizeof(fpparam))) ++ return -EFAULT; ++ ++ return dev->ethtool_ops->reset_preempt(dev, fpparam.fp_enabled); ++} ++ + /* The main entry point in this file. Called from net/core/dev_ioctl.c */ + + static int +@@ -2772,6 +2815,9 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr, + case ETHTOOL_PHY_GTUNABLE: + case ETHTOOL_GLINKSETTINGS: + case ETHTOOL_GFECPARAM: ++ case ETHTOOL_GFP: ++ case ETHTOOL_SFP: ++ case ETHTOOL_RFP: + break; + default: + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) +@@ -2999,6 +3045,15 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr, + case ETHTOOL_SFECPARAM: + rc = ethtool_set_fecparam(dev, useraddr); + break; ++ case ETHTOOL_GFP: ++ rc = ethtool_get_preempt(dev, useraddr); ++ break; ++ case ETHTOOL_SFP: ++ rc = ethtool_set_preempt(dev, useraddr); ++ break; ++ case ETHTOOL_RFP: ++ rc = ethtool_reset_preempt(dev, useraddr); ++ break; + default: + rc = -EOPNOTSUPP; + } +diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c +index 1a4c11356c96..3e0ed6cf7cce 100644 +--- a/net/ethtool/netlink.c ++++ b/net/ethtool/netlink.c +@@ -287,6 +287,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { + [ETHTOOL_MSG_PHC_VCLOCKS_GET] = ðnl_phc_vclocks_request_ops, + [ETHTOOL_MSG_MODULE_GET] = ðnl_module_request_ops, + [ETHTOOL_MSG_PSE_GET] = ðnl_pse_request_ops, ++ [ETHTOOL_MSG_PREEMPT_GET] = ðnl_preempt_request_ops, + }; + + static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) +@@ -602,6 +603,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = { + [ETHTOOL_MSG_EEE_NTF] = ðnl_eee_request_ops, + [ETHTOOL_MSG_FEC_NTF] = ðnl_fec_request_ops, + [ETHTOOL_MSG_MODULE_NTF] = ðnl_module_request_ops, ++ [ETHTOOL_MSG_PREEMPT_NTF] = ðnl_preempt_request_ops, + }; + + /* default notification handler */ +@@ -695,6 +697,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = { + [ETHTOOL_MSG_EEE_NTF] = ethnl_default_notify, + [ETHTOOL_MSG_FEC_NTF] = ethnl_default_notify, + [ETHTOOL_MSG_MODULE_NTF] = ethnl_default_notify, ++ [ETHTOOL_MSG_PREEMPT_NTF] = ethnl_default_notify, + }; + + void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) +@@ -1040,6 +1043,20 @@ static const struct genl_ops ethtool_genl_ops[] = { + .policy = ethnl_pse_set_policy, + .maxattr = ARRAY_SIZE(ethnl_pse_set_policy) - 1, + }, ++ { ++ .cmd = ETHTOOL_MSG_PREEMPT_GET, ++ .doit = ethnl_default_doit, ++ .start = ethnl_default_start, ++ .dumpit = ethnl_default_dumpit, ++ .done = ethnl_default_done, ++ .policy = preempt_get_policy, ++ .maxattr = ETHTOOL_A_PREEMPT_MAX, ++ }, ++ { ++ .cmd = ETHTOOL_MSG_PREEMPT_SET, ++ .flags = GENL_UNS_ADMIN_PERM, ++ .doit = ethnl_set_preempt, ++ }, + }; + + static const struct genl_multicast_group ethtool_nl_mcgrps[] = { +diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h +index 1bfd374f9718..4fd801446147 100644 +--- a/net/ethtool/netlink.h ++++ b/net/ethtool/netlink.h +@@ -346,6 +346,7 @@ extern const struct ethnl_request_ops ethnl_stats_request_ops; + extern const struct ethnl_request_ops ethnl_phc_vclocks_request_ops; + extern const struct ethnl_request_ops ethnl_module_request_ops; + extern const struct ethnl_request_ops ethnl_pse_request_ops; ++extern const struct ethnl_request_ops ethnl_preempt_request_ops; + + extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1]; + extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1]; +@@ -386,6 +387,7 @@ extern const struct nla_policy ethnl_module_get_policy[ETHTOOL_A_MODULE_HEADER + + extern const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MODE_POLICY + 1]; + extern const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1]; + extern const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1]; ++extern const struct nla_policy preempt_get_policy[ETHTOOL_A_PREEMPT_MAX + 1]; + + int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); + int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); +@@ -406,6 +408,7 @@ int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb); + int ethnl_set_fec(struct sk_buff *skb, struct genl_info *info); + int ethnl_set_module(struct sk_buff *skb, struct genl_info *info); + int ethnl_set_pse(struct sk_buff *skb, struct genl_info *info); ++int ethnl_set_preempt(struct sk_buff *skb, struct genl_info *info); + + extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; + extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN]; +diff --git a/net/ethtool/preempt.c b/net/ethtool/preempt.c +new file mode 100644 +index 000000000000..2d98942002e4 +--- /dev/null ++++ b/net/ethtool/preempt.c +@@ -0,0 +1,191 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++#include "netlink.h" ++#include "common.h" ++ ++struct preempt_req_info { ++ struct ethnl_req_info base; ++}; ++ ++struct preempt_reply_data { ++ struct ethnl_reply_data base; ++ struct ethtool_fp fp; ++}; ++ ++#define PREEMPT_REPDATA(__reply_base) \ ++ container_of(__reply_base, struct preempt_reply_data, base) ++ ++const struct nla_policy preempt_get_policy[ETHTOOL_A_PREEMPT_MAX + 1] = { ++ [ETHTOOL_A_PREEMPT_UNSPEC] = { .type = NLA_REJECT }, ++ [ETHTOOL_A_PREEMPT_HEADER] = { .type = NLA_NESTED }, ++ [ETHTOOL_A_PREEMPT_SUPPORTED] = { .type = NLA_REJECT }, ++ [ETHTOOL_A_PREEMPT_STATUS] = { .type = NLA_REJECT }, ++ [ETHTOOL_A_PREEMPT_ACTIVE] = { .type = NLA_REJECT }, ++ [ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE] = { .type = NLA_REJECT }, ++ [ETHTOOL_A_PREEMPT_QUEUES_SUPPORTED] = { .type = NLA_REJECT }, ++ [ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE] = { .type = NLA_REJECT }, ++}; ++ ++static int preempt_prepare_data(const struct ethnl_req_info *req_base, ++ struct ethnl_reply_data *reply_base, ++ struct genl_info *info) ++{ ++ struct preempt_reply_data *data = PREEMPT_REPDATA(reply_base); ++ struct net_device *dev = reply_base->dev; ++ int ret; ++ ++ if (!dev->ethtool_ops->get_preempt) ++ return -EOPNOTSUPP; ++ ++ ret = ethnl_ops_begin(dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = dev->ethtool_ops->get_preempt(dev, &data->fp); ++ ethnl_ops_complete(dev); ++ ++ return ret; ++} ++ ++static int preempt_reply_size(const struct ethnl_req_info *req_base, ++ const struct ethnl_reply_data *reply_base) ++{ ++ int len = 0; ++ ++ len += nla_total_size(sizeof(u8)); /* _PREEMPT_SUPPORTED */ ++ len += nla_total_size(sizeof(u8)); /* _PREEMPT_STATUS */ ++ len += nla_total_size(sizeof(u8)); /* _PREEMPT_ACTIVE */ ++ len += nla_total_size(sizeof(u32)); /* _PREEMPT_QUEUES_SUPPORTED */ ++ len += nla_total_size(sizeof(u32)); /* _PREEMPT_QUEUES_PREEMPTIBLE */ ++ len += nla_total_size(sizeof(u32)); /* _PREEMPT_MIN_FRAG_SIZE */ ++ ++ return len; ++} ++ ++static int preempt_fill_reply(struct sk_buff *skb, ++ const struct ethnl_req_info *req_base, ++ const struct ethnl_reply_data *reply_base) ++{ ++ const struct preempt_reply_data *data = PREEMPT_REPDATA(reply_base); ++ const struct ethtool_fp *preempt = &data->fp; ++ ++ if (nla_put_u32(skb, ETHTOOL_A_PREEMPT_QUEUES_SUPPORTED, ++ preempt->supported_queues_mask)) ++ return -EMSGSIZE; ++ ++ if (nla_put_u32(skb, ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE, ++ preempt->preemptible_queues_mask)) ++ return -EMSGSIZE; ++ ++ if (nla_put_u8(skb, ETHTOOL_A_PREEMPT_STATUS, preempt->fp_status)) ++ return -EMSGSIZE; ++ ++ if (nla_put_u8(skb, ETHTOOL_A_PREEMPT_ACTIVE, preempt->fp_active)) ++ return -EMSGSIZE; ++ ++ if (nla_put_u8(skb, ETHTOOL_A_PREEMPT_SUPPORTED, ++ preempt->fp_supported)) ++ return -EMSGSIZE; ++ ++ if (nla_put_u32(skb, ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE, ++ preempt->min_frag_size)) ++ return -EMSGSIZE; ++ ++ return 0; ++} ++ ++const struct ethnl_request_ops ethnl_preempt_request_ops = { ++ .request_cmd = ETHTOOL_MSG_PREEMPT_GET, ++ .reply_cmd = ETHTOOL_MSG_PREEMPT_GET_REPLY, ++ .hdr_attr = ETHTOOL_A_PREEMPT_HEADER, ++ .req_info_size = sizeof(struct preempt_req_info), ++ .reply_data_size = sizeof(struct preempt_reply_data), ++ ++ .prepare_data = preempt_prepare_data, ++ .reply_size = preempt_reply_size, ++ .fill_reply = preempt_fill_reply, ++}; ++ ++static const struct nla_policy ++preempt_set_policy[ETHTOOL_A_PREEMPT_MAX + 1] = { ++ [ETHTOOL_A_PREEMPT_UNSPEC] = { .type = NLA_REJECT }, ++ [ETHTOOL_A_PREEMPT_HEADER] = { .type = NLA_NESTED }, ++ [ETHTOOL_A_PREEMPT_DISABLED] = { .type = NLA_FLAG }, ++ [ETHTOOL_A_PREEMPT_SUPPORTED] = { .type = NLA_REJECT }, ++ [ETHTOOL_A_PREEMPT_STATUS] = { .type = NLA_REJECT }, ++ [ETHTOOL_A_PREEMPT_ACTIVE] = { .type = NLA_U8 }, ++ [ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE] = { .type = NLA_U32 }, ++ [ETHTOOL_A_PREEMPT_QUEUES_SUPPORTED] = { .type = NLA_REJECT }, ++ [ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE] = { .type = NLA_U32 }, ++}; ++ ++int ethnl_set_preempt(struct sk_buff *skb, struct genl_info *info) ++{ ++ struct nlattr *tb[ARRAY_SIZE(preempt_set_policy)]; ++ struct ethtool_fp preempt = {}; ++ struct ethnl_req_info req_info = {}; ++ struct net_device *dev; ++ bool mod = false; ++ int ret; ++ ++ ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, ++ ETHTOOL_A_PREEMPT_MAX, preempt_set_policy, ++ info->extack); ++ if (ret < 0) ++ return ret; ++ ++ ret = ethnl_parse_header_dev_get(&req_info, ++ tb[ETHTOOL_A_PREEMPT_HEADER], ++ genl_info_net(info), info->extack, ++ true); ++ if (ret < 0) ++ return ret; ++ dev = req_info.dev; ++ ret = -EOPNOTSUPP; ++ if (!dev->ethtool_ops->get_preempt || ++ !dev->ethtool_ops->set_preempt) ++ goto out_dev; ++ ++ rtnl_lock(); ++ ret = ethnl_ops_begin(dev); ++ if (ret < 0) ++ goto out_rtnl; ++ ++ if (nla_get_flag(tb[ETHTOOL_A_PREEMPT_DISABLED])) { ++ preempt.disabled = 1; ++ mod = true; ++ } ++ if (tb[ETHTOOL_A_PREEMPT_ACTIVE]) { ++ ethnl_update_u8(&preempt.fp_enabled, ++ tb[ETHTOOL_A_PREEMPT_ACTIVE], &mod); ++ mod = true; ++ } ++ if (tb[ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE]) ++ ethnl_update_u32(&preempt.min_frag_size, ++ tb[ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE], &mod); ++ else ++ preempt.min_frag_size = 60; ++ if (tb[ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE]) { ++ ethnl_update_u32(&preempt.preemptible_queues_mask, ++ tb[ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE], &mod); ++ mod = true; ++ } ++ ++ ret = 0; ++ if (!mod) ++ goto out_ops; ++ ++ ret = dev->ethtool_ops->set_preempt(dev, &preempt); ++ if (ret < 0) ++ GENL_SET_ERR_MSG(info, "frame preemption settings update failed"); ++ else ++ ethtool_notify(dev, ETHTOOL_MSG_PREEMPT_NTF, NULL); ++ ++out_ops: ++ ethnl_ops_complete(dev); ++out_rtnl: ++ rtnl_unlock(); ++out_dev: ++ dev_put(dev); ++ return ret; ++} +diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c +index 04853c83c85c..82b46fb51530 100644 +--- a/net/ipv4/af_inet.c ++++ b/net/ipv4/af_inet.c +@@ -1702,9 +1702,9 @@ u64 snmp_get_cpu_field64(void __percpu *mib, int cpu, int offt, + bhptr = per_cpu_ptr(mib, cpu); + syncp = (struct u64_stats_sync *)(bhptr + syncp_offset); + do { +- start = u64_stats_fetch_begin_irq(syncp); ++ start = u64_stats_fetch_begin(syncp); + v = *(((u64 *)bhptr) + offt); +- } while (u64_stats_fetch_retry_irq(syncp, start)); ++ } while (u64_stats_fetch_retry(syncp, start)); + + return v; + } +diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c +index 8370726ae7bf..487f8e98deaa 100644 +--- a/net/ipv6/seg6_local.c ++++ b/net/ipv6/seg6_local.c +@@ -1644,13 +1644,13 @@ static int put_nla_counters(struct sk_buff *skb, struct seg6_local_lwt *slwt) + + pcounters = per_cpu_ptr(slwt->pcpu_counters, i); + do { +- start = u64_stats_fetch_begin_irq(&pcounters->syncp); ++ start = u64_stats_fetch_begin(&pcounters->syncp); + + packets = u64_stats_read(&pcounters->packets); + bytes = u64_stats_read(&pcounters->bytes); + errors = u64_stats_read(&pcounters->errors); + +- } while (u64_stats_fetch_retry_irq(&pcounters->syncp, start)); ++ } while (u64_stats_fetch_retry(&pcounters->syncp, start)); + + counters.packets += packets; + counters.bytes += bytes; +diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c +index b8c6f6a668fc..605bdb673d1e 100644 +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -2397,9 +2397,9 @@ static inline u64 sta_get_tidstats_msdu(struct ieee80211_sta_rx_stats *rxstats, + u64 value; + + do { +- start = u64_stats_fetch_begin_irq(&rxstats->syncp); ++ start = u64_stats_fetch_begin(&rxstats->syncp); + value = rxstats->msdu[tid]; +- } while (u64_stats_fetch_retry_irq(&rxstats->syncp, start)); ++ } while (u64_stats_fetch_retry(&rxstats->syncp, start)); + + return value; + } +@@ -2465,9 +2465,9 @@ static inline u64 sta_get_stats_bytes(struct ieee80211_sta_rx_stats *rxstats) + u64 value; + + do { +- start = u64_stats_fetch_begin_irq(&rxstats->syncp); ++ start = u64_stats_fetch_begin(&rxstats->syncp); + value = rxstats->bytes; +- } while (u64_stats_fetch_retry_irq(&rxstats->syncp, start)); ++ } while (u64_stats_fetch_retry(&rxstats->syncp, start)); + + return value; + } +diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c +index f1f43894efb8..dc5165d3eec4 100644 +--- a/net/mpls/af_mpls.c ++++ b/net/mpls/af_mpls.c +@@ -1079,9 +1079,9 @@ static void mpls_get_stats(struct mpls_dev *mdev, + + p = per_cpu_ptr(mdev->stats, i); + do { +- start = u64_stats_fetch_begin_irq(&p->syncp); ++ start = u64_stats_fetch_begin(&p->syncp); + local = p->stats; +- } while (u64_stats_fetch_retry_irq(&p->syncp, start)); ++ } while (u64_stats_fetch_retry(&p->syncp, start)); + + stats->rx_packets += local.rx_packets; + stats->rx_bytes += local.rx_bytes; +diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c +index 17a1b731a76b..2be696513629 100644 +--- a/net/netfilter/ipvs/ip_vs_ctl.c ++++ b/net/netfilter/ipvs/ip_vs_ctl.c +@@ -2299,13 +2299,13 @@ static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) + u64 conns, inpkts, outpkts, inbytes, outbytes; + + do { +- start = u64_stats_fetch_begin_irq(&u->syncp); ++ start = u64_stats_fetch_begin(&u->syncp); + conns = u64_stats_read(&u->cnt.conns); + inpkts = u64_stats_read(&u->cnt.inpkts); + outpkts = u64_stats_read(&u->cnt.outpkts); + inbytes = u64_stats_read(&u->cnt.inbytes); + outbytes = u64_stats_read(&u->cnt.outbytes); +- } while (u64_stats_fetch_retry_irq(&u->syncp, start)); ++ } while (u64_stats_fetch_retry(&u->syncp, start)); + + seq_printf(seq, "%3X %8LX %8LX %8LX %16LX %16LX\n", + i, (u64)conns, (u64)inpkts, +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 3c5cac9bd9b7..32423597bca0 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -1687,10 +1687,10 @@ static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats) + for_each_possible_cpu(cpu) { + cpu_stats = per_cpu_ptr(stats, cpu); + do { +- seq = u64_stats_fetch_begin_irq(&cpu_stats->syncp); ++ seq = u64_stats_fetch_begin(&cpu_stats->syncp); + pkts = cpu_stats->pkts; + bytes = cpu_stats->bytes; +- } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, seq)); ++ } while (u64_stats_fetch_retry(&cpu_stats->syncp, seq)); + total.pkts += pkts; + total.bytes += bytes; + } +diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c +index 3c7b24535409..0953f531f984 100644 +--- a/net/openvswitch/datapath.c ++++ b/net/openvswitch/datapath.c +@@ -716,9 +716,9 @@ static void get_dp_stats(const struct datapath *dp, struct ovs_dp_stats *stats, + percpu_stats = per_cpu_ptr(dp->stats_percpu, i); + + do { +- start = u64_stats_fetch_begin_irq(&percpu_stats->syncp); ++ start = u64_stats_fetch_begin(&percpu_stats->syncp); + local_stats = *percpu_stats; +- } while (u64_stats_fetch_retry_irq(&percpu_stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&percpu_stats->syncp, start)); + + stats->n_hit += local_stats.n_hit; + stats->n_missed += local_stats.n_missed; +diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c +index d4a2db0b2299..0a0e4c283f02 100644 +--- a/net/openvswitch/flow_table.c ++++ b/net/openvswitch/flow_table.c +@@ -205,9 +205,9 @@ static void tbl_mask_array_reset_counters(struct mask_array *ma) + + stats = per_cpu_ptr(ma->masks_usage_stats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&stats->syncp); ++ start = u64_stats_fetch_begin(&stats->syncp); + counter = stats->usage_cntrs[i]; +- } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); ++ } while (u64_stats_fetch_retry(&stats->syncp, start)); + + ma->masks_usage_zero_cntr[i] += counter; + } +@@ -1136,10 +1136,9 @@ void ovs_flow_masks_rebalance(struct flow_table *table) + + stats = per_cpu_ptr(ma->masks_usage_stats, cpu); + do { +- start = u64_stats_fetch_begin_irq(&stats->syncp); ++ start = u64_stats_fetch_begin(&stats->syncp); + counter = stats->usage_cntrs[i]; +- } while (u64_stats_fetch_retry_irq(&stats->syncp, +- start)); ++ } while (u64_stats_fetch_retry(&stats->syncp, start)); + + masks_and_count[i].counter += counter; + } +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 451bd8bfafd2..08daa7932d63 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -3186,9 +3186,10 @@ static int packet_release(struct socket *sock) + * Attach a packet hook. + */ + +-static int packet_do_bind(struct sock *sk, const char *name, int ifindex, ++static int packet_do_bind(struct socket *sock, const char *name, int ifindex, + __be16 proto) + { ++ struct sock *sk = sock->sk; + struct packet_sock *po = pkt_sk(sk); + struct net_device *dev = NULL; + bool unlisted = false; +@@ -3254,6 +3255,7 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex, + po->prot_hook.dev = dev; + WRITE_ONCE(po->ifindex, dev ? dev->ifindex : 0); + packet_cached_dev_assign(po, dev); ++ sock->ndev = dev; + } + dev_put(dev); + } +@@ -3298,7 +3300,8 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, + memcpy(name, uaddr->sa_data, sizeof(uaddr->sa_data)); + name[sizeof(uaddr->sa_data)] = 0; + +- return packet_do_bind(sk, name, 0, 0); ++ ++ return packet_do_bind(sock, name, 0, 0); + } + + static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +@@ -3315,7 +3318,7 @@ static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len + if (sll->sll_family != AF_PACKET) + return -EINVAL; + +- return packet_do_bind(sk, NULL, sll->sll_ifindex, sll->sll_protocol); ++ return packet_do_bind(sock, NULL, sll->sll_ifindex, sll->sll_protocol); + } + + static struct proto packet_proto = { +diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c +index cac870eb7897..36a6a79697e2 100644 +--- a/net/sched/sch_cbs.c ++++ b/net/sched/sch_cbs.c +@@ -379,6 +379,11 @@ static int cbs_change(struct Qdisc *sch, struct nlattr *opt, + + qopt = nla_data(tb[TCA_CBS_PARMS]); + ++ if (qopt->idleslope < 0 || qopt->sendslope > 0 || qopt->hicredit < 0 || qopt->locredit > 0) { ++ NL_SET_ERR_MSG(extack, "Invalid CBS parameters"); ++ return -EINVAL; ++ } ++ + if (!qopt->offload) { + cbs_set_port_rate(dev, q); + cbs_disable_offload(dev, q); +diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c +index b03898aba101..abfdbea767e2 100644 +--- a/net/sched/sch_taprio.c ++++ b/net/sched/sch_taprio.c +@@ -1237,6 +1237,25 @@ static void taprio_sched_to_offload(struct net_device *dev, + offload->num_entries = i; + } + ++static void ++taprio_mqprio_qopt_reconstruct(struct net_device *dev, ++ struct tc_mqprio_qopt_offload *mqprio) ++{ ++ struct tc_mqprio_qopt *qopt = &mqprio->qopt; ++ int num_tc = netdev_get_num_tc(dev); ++ int tc, prio; ++ ++ qopt->num_tc = num_tc; ++ ++ for (prio = 0; prio <= TC_BITMASK; prio++) ++ qopt->prio_tc_map[prio] = netdev_get_prio_tc_map(dev, prio); ++ ++ for (tc = 0; tc < num_tc; tc++) { ++ qopt->count[tc] = dev->tc_to_txq[tc].count; ++ qopt->offset[tc] = dev->tc_to_txq[tc].offset; ++ } ++} ++ + static int taprio_enable_offload(struct net_device *dev, + struct taprio_sched *q, + struct sched_gate_list *sched, +@@ -1273,6 +1292,7 @@ static int taprio_enable_offload(struct net_device *dev, + return -ENOMEM; + } + offload->enable = 1; ++ taprio_mqprio_qopt_reconstruct(dev, &offload->mqprio); + taprio_sched_to_offload(dev, sched, offload); + + for (tc = 0; tc < TC_MAX_QUEUE; tc++) +diff --git a/net/socket.c b/net/socket.c +index d281a7ef4b1d..092230bb778b 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -107,6 +107,10 @@ + #include + #include + ++static int fast_raw_socket_fd = -1; ++static struct net_device *fast_raw_socket_dev; ++static struct socket *fast_raw_socket_sock = NULL; ++ + #ifdef CONFIG_NET_RX_BUSY_POLL + unsigned int sysctl_net_busy_read __read_mostly; + unsigned int sysctl_net_busy_poll __read_mostly; +@@ -664,6 +668,10 @@ static void __sock_release(struct socket *sock, struct inode *inode) + iput(SOCK_INODE(sock)); + return; + } ++ if (fast_raw_socket_sock != NULL && fast_raw_socket_sock == sock) { ++ fast_raw_socket_sock = NULL; ++ fast_raw_socket_fd = -1; ++ } + sock->file = NULL; + } + +@@ -1778,6 +1786,15 @@ int __sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen) + &address, addrlen); + } + fput_light(sock->file, fput_needed); ++ if (fast_raw_socket_fd < 0) { ++ if (sock->type == SOCK_RAW && sock->sk->sk_family == PF_PACKET) { ++ if (sock->ndev != NULL && sock->ndev->fast_raw_device == 1) { ++ fast_raw_socket_fd = fd; ++ fast_raw_socket_dev = sock->ndev; ++ fast_raw_socket_sock = sock; ++ } ++ } ++ } + } + return err; + } +@@ -2091,6 +2108,10 @@ int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, + struct msghdr msg; + struct iovec iov; + int fput_needed; ++ if (fd == fast_raw_socket_fd) { ++ err = fast_raw_socket_dev->netdev_ops->ndo_fast_xmit(fast_raw_socket_dev, buff, len); ++ return err; ++ } + + err = import_single_range(ITER_SOURCE, buff, len, &iov, &msg.msg_iter); + if (unlikely(err)) +@@ -2111,6 +2132,7 @@ int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, + msg.msg_name = (struct sockaddr *)&address; + msg.msg_namelen = addr_len; + } ++ + if (sock->file->f_flags & O_NONBLOCK) + flags |= MSG_DONTWAIT; + msg.msg_flags = flags; +@@ -2156,6 +2178,11 @@ int __sys_recvfrom(int fd, void __user *ubuf, size_t size, unsigned int flags, + struct iovec iov; + int err, err2; + int fput_needed; ++ int i; ++ if (fd == fast_raw_socket_fd) { ++ err = fast_raw_socket_dev->netdev_ops->ndo_fast_recv(fast_raw_socket_dev, ubuf, size, addr, addr_len); ++ return err; ++ } + + err = import_single_range(ITER_DEST, ubuf, size, &iov, &msg.msg_iter); + if (unlikely(err)) +diff --git a/net/tsn/genl_tsn.c b/net/tsn/genl_tsn.c +index 9adb4ec1e287..f03c35e9090d 100644 +--- a/net/tsn/genl_tsn.c ++++ b/net/tsn/genl_tsn.c +@@ -1132,12 +1132,11 @@ static int cmd_qci_sfi_get(struct genl_info *info) + return valid; + } + +- valid = tsnops->qci_sfi_counters_get(netdev, sfi_handle, +- &sficount); +- if (valid < 0) { ++ ret = tsnops->qci_sfi_counters_get(netdev, sfi_handle, &sficount); ++ if (ret < 0) { + tsn_simple_reply(info, TSN_CMD_REPLY, +- netdev->name, valid); +- return valid; ++ netdev->name, ret); ++ return ret; + } + } + +@@ -2117,6 +2116,9 @@ static int cmd_qbv_set(struct genl_info *info) + if (qbv[TSN_QBV_ATTR_CONFIGCHANGE]) + qbvconfig.config_change = 1; + ++ if (qbv[TSN_QBV_ATTR_MAXSDU]) ++ qbvconfig.maxsdu = nla_get_u32(qbv[TSN_QBV_ATTR_MAXSDU]); ++ + if (!qbv[TSN_QBV_ATTR_ADMINENTRY]) { + tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, -EINVAL); + return -1; +@@ -3414,6 +3416,7 @@ static const struct genl_ops tsnnl_ops[] = { + .cmd = TSN_CMD_PCPMAP_SET, + .doit = tsn_pcpmap_set, + .flags = GENL_ADMIN_PERM, ++ .validate = GENL_DONT_VALIDATE_STRICT, + }, + }; + +-- +2.34.1 + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0009-init-add-NXP-RT-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0009-init-add-NXP-RT-support.patch new file mode 100644 index 000000000..e72286773 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0009-init-add-NXP-RT-support.patch @@ -0,0 +1,28 @@ +From 98287df3a9d231a31fbc8b2e15be6174d6af1673 Mon Sep 17 00:00:00 2001 +From: Mike Engel +Date: Mon, 26 Feb 2024 09:43:05 +0100 +Subject: [PATCH 09/10] init: add NXP RT support + +Signed-off-by: Mike Engel +--- + init/Kconfig | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/init/Kconfig b/init/Kconfig +index de255842f5d0..d45312780b3a 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1582,6 +1582,10 @@ config PRINTK + very difficult to diagnose system problems, saying N here is + strongly discouraged. + ++config HAVE_ATOMIC_CONSOLE ++ bool ++ default n ++ + config BUG + bool "BUG() support" if EXPERT + default y +-- +2.34.1 + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/nxp_rt_conf.cfg b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/nxp_rt_conf.cfg new file mode 100644 index 000000000..5aca538f4 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/nxp_rt_conf.cfg @@ -0,0 +1,31 @@ +.......................................................................... +. WARNING +. +. This file is a kernel configuration fragment, and not a full kernel +. configuration file. The final kernel configuration is made up of +. an assembly of processed fragments, each of which is designed to +. capture a specific part of the final configuration (e.g. platform +. configuration, feature configuration, and board specific hardware +. configuration). For more information on kernel configuration, please +. consult the product documentation. +. +.......................................................................... +CONFIG_PREEMPT_RT=y +CONFIG_EXPERT=y + +# disable SCHED_MC +# CONFIG_MCPM is not set + +# Disable CPUFREQ and CPUIDLE +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set + +# CONFIG_NUMA_BALANCING in not set +# CONFIG_TRANSPARENT_HUGEPAGE is not set +# CONFIG_EFI_DISABLE_RUNTIME is not set +CONFIG_MEDIA_SUPPORT_FILTER=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_LEDS_TRIGGER_CPU is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_HID_MULTITOUCH is not set +CONFIG_FRAMEBUFFER_CONSOLE=y diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey_6.1.bb b/meta-digi-arm/recipes-kernel/linux/linux-dey_6.1.bb index 794b7d441..cba404b1f 100644 --- a/meta-digi-arm/recipes-kernel/linux/linux-dey_6.1.bb +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey_6.1.bb @@ -1,8 +1,27 @@ -# Copyright (C) 2023 Digi International +# Copyright (C) 2023-2024 Digi International require recipes-kernel/linux/linux-dey.inc SRCBRANCH = "v6.1.55/nxp/master" + +# Patch series for RT Kernel +NXP_RT_PATCHES = " \ + file://0001-arch-arm-add-NXP-RT-support.patch \ + file://0002-RT-add-RT-localversion.patch \ + file://0003-arch-arm64-add-NXP-RT-support.patch \ + file://0004-Documentation-add-NXP-RT-support.patch \ + file://0005-include-add-NXP-RT-support.patch \ + file://0006-kernel-add-NXP-RT-support.patch \ + file://0007-drivers-add-NXP-RT-support.patch \ + file://0008-net-add-RT-NXP-support.patch \ + file://0009-init-add-NXP-RT-support.patch \ + file://nxp_rt_conf.cfg \ +" + +SRC_URI:append = " \ + ${@bb.utils.contains('DISTRO_FEATURES', 'rt', '${NXP_RT_PATCHES}', '', d)} \ +" + SRCREV = "${AUTOREV}" # Blacklist btnxpuart module. It will be managed by the bluetooth-init script