From 2eb37cf30b9a17b39cd7e28230ca1e7b6ae146e4 Mon Sep 17 00:00:00 2001 From: Mike Engel Date: Fri, 9 Feb 2024 12:05:07 +0100 Subject: [PATCH] linux-dey-6.1: add RT functionality to CCiMX93 This commit adds RT functionality to the CCiMX93 platform. The patches have been extracted from the NXP real time edge BSP and include the maineline RT patches and the NXP RT driver patches and RT Kernel defconfig changes. https://onedigi.atlassian.net/browse/DEL-8881 Signed-off-by: Mike Engel --- .../0001-arch-arm-add-NXP-RT-support.patch | 4297 +++ .../ccimx93/0002-RT-add-RT-localversion.patch | 21 + .../0003-arch-arm64-add-NXP-RT-support.patch | 5862 +++++ ...004-Documentation-add-NXP-RT-support.patch | 581 + .../0005-include-add-NXP-RT-support.patch | 1392 + .../0006-kernel-add-NXP-RT-support.patch | 2435 ++ .../0007-drivers-add-NXP-RT-support.patch | 21976 ++++++++++++++++ .../ccimx93/0008-net-add-RT-NXP-support.patch | 1486 ++ .../0009-init-add-NXP-RT-support.patch | 28 + .../linux/linux-dey/ccimx93/nxp_rt_conf.cfg | 31 + .../recipes-kernel/linux/linux-dey_6.1.bb | 21 +- 11 files changed, 38129 insertions(+), 1 deletion(-) create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0001-arch-arm-add-NXP-RT-support.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0002-RT-add-RT-localversion.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0003-arch-arm64-add-NXP-RT-support.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0004-Documentation-add-NXP-RT-support.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0005-include-add-NXP-RT-support.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0006-kernel-add-NXP-RT-support.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0007-drivers-add-NXP-RT-support.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0008-net-add-RT-NXP-support.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/0009-init-add-NXP-RT-support.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/ccimx93/nxp_rt_conf.cfg 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