From 1a08f346cc40621cb123fe9a6e6a928d15780431 Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Tue, 1 Apr 2025 11:21:14 +0200 Subject: [PATCH] linux-dey-6.6: add RT functionality to NXP platforms Add a unique patch with the RT functionality between NXP tags (sha1) 'lf-6.6.36-2.1.0' (d23d64eea5111e1607efcce1d601834fceec92cb) 'Real-Time-Edge-v3.1-202503' (f03af81d60b7ae14e03fafa8f4c4289c30a73f93) https://onedigi.atlassian.net/browse/DEL-9324 Signed-off-by: Isaac Hermida --- ...support-based-on-latest-linux_6.6.36.patch | 53625 ++++++++++++++++ .../imx-nxp-bsp/fragment-nxp-rt.config | 33 + .../recipes-kernel/linux/linux-dey_6.6.bb | 31 +- 3 files changed, 53678 insertions(+), 11 deletions(-) create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/imx-nxp-bsp/0001-add-RT-support-based-on-latest-linux_6.6.36.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/imx-nxp-bsp/fragment-nxp-rt.config diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/imx-nxp-bsp/0001-add-RT-support-based-on-latest-linux_6.6.36.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/imx-nxp-bsp/0001-add-RT-support-based-on-latest-linux_6.6.36.patch new file mode 100644 index 000000000..3c3588f19 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/imx-nxp-bsp/0001-add-RT-support-based-on-latest-linux_6.6.36.patch @@ -0,0 +1,53625 @@ +From: Isaac Hermida +Date: Tue, 1 Apr 2025 11:13:34 +0200 +Subject: [PATCH] add RT support based on latest linux_6.6.36 + +Add a unique patch with the RT functionality between NXP lf-6.6.36-2.1.0 +and realtime/linux_6.6.36 in https://github.com/nxp-real-time-edge-sw/real-time-edge-linux.git + +https://onedigi.atlassian.net/browse/DEL-9324 + +Signed-off-by: Isaac Hermida +--- + .../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 ++ + arch/arm/Kconfig | 6 +- + arch/arm/boot/dts/nxp/imx/Makefile | 9 + + .../dts/nxp/imx/imx6ul-14x14-evk-ecat.dts | 11 + + .../boot/dts/nxp/imx/imx6ul-14x14-evk-igh.dts | 11 + + .../dts/nxp/imx/imx6ull-14x14-evk-avb-mcr.dts | 59 + + .../dts/nxp/imx/imx6ull-14x14-evk-avb.dts | 29 + + .../dts/nxp/imx/imx6ull-14x14-evk-ecat.dts | 11 + + .../dts/nxp/imx/imx6ull-14x14-evk-igh.dts | 11 + + .../dts/nxp/imx/imx6ull-14x14-evk-lpuart.dts | 25 + + .../dts/nxp/imx/imx6ull-14x14-evk-reve.dtso | 11 + + arch/arm/boot/dts/nxp/ls/Makefile | 3 +- + arch/arm/boot/dts/nxp/ls/ls1021a-iot-bm.dts | 259 ++ + arch/arm/boot/dts/nxp/ls/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/kernel/smp.c | 22 + + arch/arm/mach-imx/Kconfig | 10 + + arch/arm/mm/fault.c | 6 + + arch/arm/vfp/vfpmodule.c | 74 +- + arch/arm64/Kconfig | 1 + + arch/arm64/Kconfig.platforms | 30 + + arch/arm64/boot/dts/freescale/Makefile | 104 +- + .../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/imx8m-rproc-ca53.dtsi | 34 + + .../boot/dts/freescale/imx8mm-evk-avb.dts | 37 + + .../dts/freescale/imx8mm-evk-baremetal.dts | 39 + + .../freescale/imx8mm-evk-ecat-userspace.dts | 15 + + .../boot/dts/freescale/imx8mm-evk-ecat.dts | 10 + + .../dts/freescale/imx8mm-evk-harpoon-avb.dts | 11 + + .../imx8mm-evk-harpoon-industrial.dts | 11 + + .../imx8mm-evk-harpoon-virtio-net.dts | 11 + + .../boot/dts/freescale/imx8mm-evk-harpoon.dts | 31 + + .../boot/dts/freescale/imx8mm-evk-igh.dts | 10 + + .../freescale/imx8mm-evk-multicore-rpmsg.dts | 80 + + .../freescale/imx8mm-evk-multicore-rtos.dts | 78 + + .../imx8mm-evk-qca-wifi-harpoon-avb.dts | 7 + + ...imx8mm-evk-qca-wifi-harpoon-industrial.dts | 7 + + ...imx8mm-evk-qca-wifi-harpoon-virtio-net.dts | 7 + + .../freescale/imx8mm-evk-qca-wifi-harpoon.dts | 7 + + .../dts/freescale/imx8mm-evk-qca-wifi.dts | 123 +- + .../dts/freescale/imx8mm-evk-qca-wifi.dtsi | 124 + + .../dts/freescale/imx8mm-evk-rpmsg-8m-buf.dts | 228 ++ + .../dts/freescale/imx8mm-evk-rpmsg-ca53.dts | 40 + + .../boot/dts/freescale/imx8mm-evk-rpmsg.dts | 96 +- + .../freescale/imx8mm-evk-virtio-net-ca53.dts | 83 + + .../freescale/imx8mm-evk-virtio-net-cm4.dts | 74 + + .../freescale/imx8mm-evk-virtio-perf-ca53.dts | 79 + + .../freescale/imx8mm-evk-virtio-perf-cm4.dts | 63 + + .../dts/freescale/imx8mm-virtio-ca53.dtsi | 46 + + arch/arm64/boot/dts/freescale/imx8mm.dtsi | 11 + + .../dts/freescale/imx8mn-evk-harpoon-avb.dts | 11 + + .../imx8mn-evk-harpoon-industrial.dts | 11 + + .../boot/dts/freescale/imx8mn-evk-harpoon.dts | 31 + + .../boot/dts/freescale/imx8mp-evk-avb.dts | 42 + + .../dts/freescale/imx8mp-evk-baremetal.dts | 37 + + .../dts/freescale/imx8mp-evk-dsa-enetc.dts | 17 + + .../dts/freescale/imx8mp-evk-dsa-fec-swp0.dts | 92 + + .../dts/freescale/imx8mp-evk-dsa-fec-swp3.dts | 92 + + .../boot/dts/freescale/imx8mp-evk-dsa.dts | 92 + + .../freescale/imx8mp-evk-ecat-userspace.dts | 33 + + .../boot/dts/freescale/imx8mp-evk-ecat.dts | 10 + + .../dts/freescale/imx8mp-evk-harpoon-avb.dts | 11 + + .../imx8mp-evk-harpoon-industrial.dts | 15 + + .../imx8mp-evk-harpoon-virtio-net.dts | 40 + + .../boot/dts/freescale/imx8mp-evk-harpoon.dts | 35 + + .../boot/dts/freescale/imx8mp-evk-igh.dts | 10 + + .../freescale/imx8mp-evk-multicore-lwip.dts | 21 + + .../freescale/imx8mp-evk-multicore-rpmsg.dts | 179 + + .../freescale/imx8mp-evk-multicore-rtos.dts | 80 + + .../dts/freescale/imx8mp-evk-rpmsg-ca53.dts | 45 + + .../freescale/imx8mp-evk-virtio-net-ca53.dts | 82 + + .../freescale/imx8mp-evk-virtio-net-cm7.dts | 85 + + .../freescale/imx8mp-evk-virtio-perf-ca53.dts | 69 + + .../freescale/imx8mp-evk-virtio-perf-cm7.dts | 69 + + .../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 + + .../dts/freescale/imx93-11x11-evk-avb.dts | 9 + + .../freescale/imx93-11x11-evk-baremetal.dts | 31 + + .../dts/freescale/imx93-11x11-evk-dpdk.dts | 16 + + .../freescale/imx93-11x11-evk-dsa-enetc.dts | 17 + + .../imx93-11x11-evk-dsa-fec-swp0.dts | 108 + + .../imx93-11x11-evk-dsa-fec-swp3.dts | 108 + + .../dts/freescale/imx93-11x11-evk-dsa.dts | 108 + + .../imx93-11x11-evk-ecat-userspace.dts | 16 + + .../dts/freescale/imx93-11x11-evk-ecat.dts | 11 + + .../freescale/imx93-11x11-evk-harpoon-avb.dts | 10 + + .../imx93-11x11-evk-harpoon-industrial.dts | 19 + + .../imx93-11x11-evk-harpoon-virtio-net.dts | 46 + + .../dts/freescale/imx93-11x11-evk-harpoon.dts | 35 + + .../dts/freescale/imx93-11x11-evk-igh.dts | 11 + + .../imx93-11x11-evk-multicore-rpmsg.dts | 47 + + .../imx93-11x11-evk-multicore-rtos.dts | 39 + + .../imx93-11x11-evk-uart-sharing-cm33.dts | 96 + + .../imx93-11x11-evk-virtio-net-ca55.dts | 56 + + .../imx93-11x11-evk-virtio-net-cm33.dts | 58 + + .../boot/dts/freescale/imx93-11x11-evk.dts | 60 +- + .../freescale/imx93-14x14-evk-aud-hat-avb.dts | 9 + + .../dts/freescale/imx93-14x14-evk-aud-hat.dts | 2 +- + .../imx93-14x14-evk-imxai2eth-ath.dts | 126 + + .../dts/freescale/imx93-14x14-evk-mqs.dts | 70 +- + .../dts/freescale/imx93-14x14-evk-mqs.dtsi | 71 + + .../imx93-14x14-evk-multicore-rpmsg.dts | 47 + + .../imx93-14x14-evk-multicore-rtos.dts | 39 + + .../freescale/imx93-14x14-evk-sja1105-avb.dts | 8 + + .../dts/freescale/imx93-14x14-evk-sja1105.dts | 3 +- + .../dts/freescale/imx93-14x14-evk-tja1103.dts | 63 +- + .../imx93-14x14-evk-uart-sharing-cm33.dts | 96 + + .../imx93-14x14-evk-virtio-net-ca55.dts | 56 + + .../imx93-14x14-evk-virtio-net-cm33.dts | 58 + + .../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 + + .../boot/dts/freescale/imx93-evk-avb.dtsi | 37 + + .../dts/freescale/imx93-generic-mbox.dtsi | 27 + + .../boot/dts/freescale/imx93-rpmsg-ca55.dtsi | 37 + + .../boot/dts/freescale/imx93-rproc-ca55.dtsi | 14 + + .../imx95-15x15-evk-harpoon-industrial.dts | 25 + + .../dts/freescale/imx95-15x15-evk-harpoon.dts | 19 + + .../imx95-15x15-evk-multicore-rtos.dts | 29 + + .../imx95-19x19-evk-harpoon-industrial.dts | 29 + + .../dts/freescale/imx95-19x19-evk-harpoon.dts | 19 + + .../imx95-19x19-evk-multicore-rtos.dts | 29 + + .../dts/freescale/imx95-generic-mbox.dtsi | 27 + + .../boot/dts/freescale/imx95-rpmsg-ca55.dtsi | 37 + + .../boot/dts/freescale/imx95-rproc-ca55.dtsi | 48 + + arch/arm64/configs/imx_avb.config | 8 + + arch/arm64/configs/imx_v8_defconfig | 30 +- + 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/kernel/smp.c | 25 + + arch/powerpc/Kconfig | 2 + + arch/powerpc/include/asm/stackprotector.h | 7 +- + arch/powerpc/kernel/traps.c | 7 +- + arch/powerpc/kvm/Kconfig | 1 + + arch/powerpc/platforms/pseries/Kconfig | 1 + + arch/powerpc/platforms/pseries/iommu.c | 31 +- + arch/riscv/Kconfig | 2 + + arch/riscv/include/asm/cpufeature.h | 2 - + arch/riscv/include/asm/thread_info.h | 2 + + arch/riscv/kernel/cpufeature.c | 90 +- + arch/riscv/kernel/smpboot.c | 1 - + arch/x86/Kconfig | 2 + + arch/x86/include/asm/thread_info.h | 6 +- + drivers/acpi/processor_idle.c | 2 +- + drivers/base/Kconfig | 2 +- + drivers/block/zram/zram_drv.c | 37 + + drivers/block/zram/zram_drv.h | 3 + + drivers/clk/imx/Makefile | 1 + + drivers/clk/imx/clk-frac-pll.c | 130 + + drivers/clk/imx/clk-fracn-gppll.c | 209 +- + drivers/clk/imx/clk-imx8-acm.c | 46 + + 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 + + .../gpu/drm/amd/display/amdgpu_dm/dc_fpu.c | 53 +- + .../drm/amd/display/dc/dcn20/dcn20_resource.c | 10 +- + .../drm/amd/display/dc/dcn21/dcn21_resource.c | 10 +- + .../drm/amd/display/dc/dml/dcn20/dcn20_fpu.c | 23 +- + .../drm/amd/display/dc/dml/dcn20/dcn20_fpu.h | 10 +- + drivers/gpu/drm/i915/Kconfig | 1 - + drivers/gpu/drm/i915/display/intel_crtc.c | 15 +- + drivers/gpu/drm/i915/display/intel_vblank.c | 6 +- + drivers/gpu/drm/i915/gt/intel_breadcrumbs.c | 7 +- + .../drm/i915/gt/intel_execlists_submission.c | 17 +- + drivers/gpu/drm/i915/gt/intel_reset.c | 12 +- + drivers/gpu/drm/i915/gt/uc/intel_guc.h | 2 +- + 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 | 18 + + drivers/net/dsa/netc/Makefile | 14 + + drivers/net/dsa/netc/netc.h | 131 + + drivers/net/dsa/netc/netc_config.c | 696 ++++ + drivers/net/dsa/netc/netc_config.h | 557 +++ + drivers/net/dsa/netc/netc_devlink.c | 107 + + drivers/net/dsa/netc/netc_ethtool.c | 344 ++ + drivers/net/dsa/netc/netc_main.c | 1439 +++++++ + drivers/net/dsa/netc/netc_ptp.c | 375 ++ + drivers/net/dsa/netc/netc_ptp.h | 77 + + drivers/net/dsa/netc/netc_spi.c | 147 + + drivers/net/dsa/ocelot/felix.c | 183 +- + drivers/net/dsa/ocelot/felix.h | 5 + + drivers/net/dsa/ocelot/felix_tsn.c | 6 +- + drivers/net/dsa/ocelot/felix_vsc9959.c | 99 +- + drivers/net/dsa/sja1105/sja1105.h | 5 + + drivers/net/dsa/sja1105/sja1105_clocking.c | 21 +- + drivers/net/dsa/sja1105/sja1105_main.c | 68 +- + drivers/net/dsa/sja1105/sja1105_tas.c | 4 + + drivers/net/ethernet/freescale/Kconfig | 20 + + drivers/net/ethernet/freescale/Makefile | 2 + + drivers/net/ethernet/freescale/enetc/enetc.c | 6 +- + drivers/net/ethernet/freescale/enetc/enetc.h | 8 + + .../ethernet/freescale/enetc/enetc_ethtool.c | 153 + + .../net/ethernet/freescale/enetc/enetc_pf.c | 1 + + .../net/ethernet/freescale/enetc/enetc_ptp.c | 15 +- + .../net/ethernet/freescale/enetc/enetc_qos.c | 5 +- + .../net/ethernet/freescale/enetc/enetc_tsn.c | 179 +- + drivers/net/ethernet/freescale/fec.h | 100 +- + drivers/net/ethernet/freescale/fec_ecat.c | 3063 +++++++++++++++ + drivers/net/ethernet/freescale/fec_ecat.h | 718 ++++ + drivers/net/ethernet/freescale/fec_main.c | 1452 ++++++- + drivers/net/ethernet/freescale/fec_ptp.c | 445 ++- + 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 | 1216 ++++++ + drivers/net/ethernet/mscc/ocelot.c | 45 +- + drivers/net/ethernet/mscc/ocelot_flower.c | 30 +- + drivers/net/ethernet/mscc/ocelot_ptp.c | 125 +- + drivers/net/ethernet/mscc/ocelot_vcap.c | 173 +- + .../net/ethernet/stmicro/stmmac/dwmac4_core.c | 2 + + 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 | 106 + + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 36 +- + .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 38 +- + drivers/net/phy/phy.c | 5 +- + 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 | 669 ++++ + drivers/nfc/pn5xx/pn5xx_i2c.h | 38 + + drivers/nfc/pn5xx/sample_devicetree.txt | 17 + + drivers/pmdomain/imx/scu-pd.c | 1 + + drivers/ptp/ptp_chardev.c | 23 + + drivers/ptp/ptp_clock.c | 19 + + drivers/ptp/ptp_private.h | 18 + + drivers/ptp/ptp_sysfs.c | 16 +- + drivers/ptp/ptp_vclock.c | 204 +- + drivers/remoteproc/imx_rproc.c | 196 +- + drivers/remoteproc/imx_rproc.h | 2 + + drivers/remoteproc/remoteproc_core.c | 8 +- + 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/staging/fsl_qbman/qman_driver.c | 49 + + drivers/staging/fsl_qbman/qman_high.c | 39 + + drivers/staging/fsl_qbman/qman_private.h | 7 + + drivers/tty/rpmsg_tty.c | 191 +- + drivers/tty/serial/21285.c | 8 +- + drivers/tty/serial/8250/8250_aspeed_vuart.c | 6 +- + drivers/tty/serial/8250/8250_bcm7271.c | 28 +- + drivers/tty/serial/8250/8250_core.c | 54 +- + drivers/tty/serial/8250/8250_dma.c | 8 +- + drivers/tty/serial/8250/8250_dw.c | 8 +- + drivers/tty/serial/8250/8250_exar.c | 4 +- + drivers/tty/serial/8250/8250_fsl.c | 6 +- + drivers/tty/serial/8250/8250_mtk.c | 8 +- + drivers/tty/serial/8250/8250_omap.c | 52 +- + drivers/tty/serial/8250/8250_pci1xxxx.c | 8 +- + drivers/tty/serial/8250/8250_port.c | 259 +- + drivers/tty/serial/altera_jtaguart.c | 28 +- + drivers/tty/serial/altera_uart.c | 20 +- + drivers/tty/serial/amba-pl010.c | 20 +- + drivers/tty/serial/amba-pl011.c | 78 +- + drivers/tty/serial/apbuart.c | 8 +- + drivers/tty/serial/ar933x_uart.c | 26 +- + drivers/tty/serial/arc_uart.c | 16 +- + drivers/tty/serial/atmel_serial.c | 24 +- + drivers/tty/serial/bcm63xx_uart.c | 22 +- + drivers/tty/serial/cpm_uart.c | 8 +- + drivers/tty/serial/digicolor-usart.c | 18 +- + drivers/tty/serial/dz.c | 32 +- + drivers/tty/serial/fsl_linflexuart.c | 26 +- + drivers/tty/serial/fsl_lpuart.c | 88 +- + drivers/tty/serial/icom.c | 26 +- + drivers/tty/serial/imx.c | 84 +- + drivers/tty/serial/ip22zilog.c | 36 +- + drivers/tty/serial/jsm/jsm_neo.c | 4 +- + drivers/tty/serial/jsm/jsm_tty.c | 16 +- + drivers/tty/serial/liteuart.c | 20 +- + drivers/tty/serial/lpc32xx_hs.c | 26 +- + drivers/tty/serial/ma35d1_serial.c | 22 +- + drivers/tty/serial/mcf.c | 20 +- + drivers/tty/serial/men_z135_uart.c | 8 +- + drivers/tty/serial/meson_uart.c | 30 +- + drivers/tty/serial/milbeaut_usio.c | 16 +- + drivers/tty/serial/mpc52xx_uart.c | 12 +- + drivers/tty/serial/mps2-uart.c | 16 +- + drivers/tty/serial/msm_serial.c | 38 +- + drivers/tty/serial/mvebu-uart.c | 18 +- + drivers/tty/serial/omap-serial.c | 44 +- + drivers/tty/serial/owl-uart.c | 26 +- + drivers/tty/serial/pch_uart.c | 10 +- + drivers/tty/serial/pic32_uart.c | 20 +- + drivers/tty/serial/pmac_zilog.c | 52 +- + drivers/tty/serial/pxa.c | 30 +- + drivers/tty/serial/qcom_geni_serial.c | 8 +- + drivers/tty/serial/rda-uart.c | 34 +- + drivers/tty/serial/rp2.c | 20 +- + drivers/tty/serial/sa1100.c | 20 +- + drivers/tty/serial/samsung_tty.c | 50 +- + drivers/tty/serial/sb1250-duart.c | 12 +- + drivers/tty/serial/sc16is7xx.c | 5 + + drivers/tty/serial/serial-tegra.c | 32 +- + drivers/tty/serial/serial_core.c | 92 +- + drivers/tty/serial/serial_mctrl_gpio.c | 4 +- + drivers/tty/serial/serial_port.c | 4 +- + drivers/tty/serial/serial_txx9.c | 26 +- + drivers/tty/serial/sh-sci.c | 68 +- + drivers/tty/serial/sifive.c | 16 +- + drivers/tty/serial/sprd_serial.c | 30 +- + drivers/tty/serial/st-asc.c | 18 +- + drivers/tty/serial/stm32-usart.c | 38 +- + drivers/tty/serial/sunhv.c | 28 +- + drivers/tty/serial/sunplus-uart.c | 26 +- + drivers/tty/serial/sunsab.c | 34 +- + drivers/tty/serial/sunsu.c | 46 +- + drivers/tty/serial/sunzilog.c | 42 +- + drivers/tty/serial/timbuart.c | 8 +- + drivers/tty/serial/uartlite.c | 18 +- + drivers/tty/serial/ucc_uart.c | 4 +- + drivers/tty/serial/vt8500_serial.c | 8 +- + drivers/tty/serial/xilinx_uartps.c | 56 +- + drivers/tty/tty_io.c | 11 +- + drivers/virtio/Kconfig | 8 + + drivers/virtio/Makefile | 1 + + drivers/virtio/virtio_mmio.c | 306 +- + drivers/virtio/virtio_ring.c | 2 - + drivers/virtio/virtio_trans.c | 793 ++++ + fs/proc/consoles.c | 14 +- + include/dt-bindings/clock/imx8-clock.h | 10 + + include/dt-bindings/rpmsg/imx_srtm.h | 20 + + include/linux/bottom_half.h | 2 + + include/linux/clk/imx-pll.h | 26 + + include/linux/console.h | 150 + + include/linux/dsa/netc.h | 69 + + include/linux/dsa/sja1105.h | 2 + + include/linux/entry-common.h | 2 +- + include/linux/entry-kvm.h | 2 +- + include/linux/ethtool.h | 7 + + include/linux/fec.h | 120 + + include/linux/fsl_qman.h | 8 + + include/linux/interrupt.h | 29 + + include/linux/ipi_baremetal.h | 27 + + include/linux/net.h | 2 + + include/linux/netdevice.h | 48 + + include/linux/printk.h | 30 +- + include/linux/rpmsg/imx_srtm.h | 65 + + include/linux/sched.h | 16 +- + include/linux/sched/idle.h | 8 +- + include/linux/sched/rt.h | 4 + + include/linux/serial_8250.h | 6 + + include/linux/serial_core.h | 43 +- + include/linux/thread_info.h | 24 + + include/linux/trace_events.h | 8 +- + include/net/dsa.h | 18 + + include/net/flow_offload.h | 9 + + include/net/switchdev.h | 1 + + include/net/tc_act/tc_frer.h | 52 + + include/soc/mscc/ocelot.h | 12 +- + include/soc/mscc/ocelot_ptp.h | 3 - + include/soc/mscc/ocelot_vcap.h | 1 + + include/uapi/linux/ethtool.h | 30 + + include/uapi/linux/ethtool_netlink.h | 21 + + include/uapi/linux/if_ether.h | 1 + + include/uapi/linux/pkt_cls.h | 1 + + include/uapi/linux/ptp_clock.h | 12 + + include/uapi/linux/tc_act/tc_frer.h | 50 + + include/uapi/linux/virtio_ids.h | 1 + + include/uapi/linux/virtio_mmio.h | 7 +- + include/uapi/linux/virtio_trans.h | 60 + + kernel/Kconfig.preempt | 17 +- + kernel/dma/coherent.c | 15 +- + kernel/entry/common.c | 4 +- + kernel/entry/kvm.c | 2 +- + kernel/futex/pi.c | 87 +- + kernel/futex/requeue.c | 6 +- + kernel/ksysfs.c | 12 + + kernel/locking/lockdep.c | 5 + + kernel/locking/rtmutex.c | 37 +- + kernel/locking/rwbase_rt.c | 19 + + kernel/locking/rwsem.c | 8 +- + kernel/locking/spinlock_rt.c | 6 + + kernel/locking/ww_rt_mutex.c | 2 +- + kernel/panic.c | 9 + + kernel/printk/Makefile | 2 +- + kernel/printk/internal.h | 121 + + kernel/printk/nbcon.c | 1665 ++++++++ + kernel/printk/printk.c | 750 +++- + kernel/printk/printk_ringbuffer.c | 360 +- + kernel/printk/printk_ringbuffer.h | 54 +- + kernel/printk/printk_safe.c | 12 + + kernel/rcu/rcutorture.c | 6 + + kernel/rcu/tree_stall.h | 5 + + kernel/sched/core.c | 127 +- + kernel/sched/debug.c | 19 + + kernel/sched/fair.c | 46 +- + kernel/sched/features.h | 2 + + kernel/sched/idle.c | 3 +- + kernel/sched/rt.c | 5 +- + kernel/sched/sched.h | 1 + + kernel/signal.c | 30 +- + kernel/softirq.c | 95 +- + kernel/time/hrtimer.c | 4 +- + kernel/time/tick-sched.c | 2 +- + kernel/time/timer.c | 11 +- + kernel/trace/trace.c | 2 + + kernel/trace/trace_output.c | 16 +- + localversion-rt | 1 + + net/bridge/br_switchdev.c | 24 + + net/core/dev.c | 62 +- + net/core/skbuff.c | 7 +- + net/dsa/Kconfig | 6 + + net/dsa/Makefile | 1 + + net/dsa/slave.c | 49 +- + net/dsa/tag_netc.c | 489 +++ + net/dsa/tag_sja1105.c | 3 +- + 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/packet/af_packet.c | 11 +- + net/sched/Kconfig | 13 + + net/sched/Makefile | 1 + + net/sched/act_frer.c | 731 ++++ + net/sched/sch_cbs.c | 5 + + net/socket.c | 27 + + net/tsn/genl_tsn.c | 12 +- + tools/virtio/vt_test.sh | 79 + + 489 files changed, 35912 insertions(+), 2267 deletions(-) + 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 + create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-ecat.dts + create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-igh.dts + create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb-mcr.dts + create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb.dts + create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-ecat.dts + create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-igh.dts + create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-lpuart.dts + create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-reve.dtso + create mode 100644 arch/arm/boot/dts/nxp/ls/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 + 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/imx8m-rproc-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 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-ecat-userspace.dts + create mode 100755 arch/arm64/boot/dts/freescale/imx8mm-evk-ecat.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-industrial.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-virtio-net.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon.dts + create mode 100755 arch/arm64/boot/dts/freescale/imx8mm-evk-igh.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-qca-wifi-harpoon-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-industrial.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-virtio-net.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dtsi + 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/imx8mm-virtio-ca53.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-industrial.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon.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-enetc.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp0.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp3.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-dsa.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-ecat-userspace.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-ecat.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-industrial.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-virtio-net.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-igh.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-rpmsg-ca53.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-evk-virtio-perf-ca53.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-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-dpdk.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-enetc.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp0.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp3.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-userspace.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-harpoon-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-industrial.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-virtio-net.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-igh.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rpmsg.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-14x14-evk-aud-hat-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-imxai2eth-ath.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rpmsg.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105-avb.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-uart-sharing-cm33.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-ca55.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-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-evk-avb.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx93-generic-mbox.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx93-rpmsg-ca55.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon-industrial.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx95-15x15-evk-multicore-rtos.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon-industrial.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx95-19x19-evk-multicore-rtos.dts + create mode 100644 arch/arm64/boot/dts/freescale/imx95-generic-mbox.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx95-rpmsg-ca55.dtsi + create mode 100644 arch/arm64/boot/dts/freescale/imx95-rproc-ca55.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 + 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_ptp.h + 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 + 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/net/tc_act/tc_frer.h + create mode 100644 include/uapi/linux/tc_act/tc_frer.h + create mode 100644 include/uapi/linux/virtio_trans.h + create mode 100644 kernel/printk/nbcon.c + create mode 100644 localversion-rt + create mode 100644 net/dsa/tag_netc.c + create mode 100644 net/ethtool/preempt.c + create mode 100644 net/sched/act_frer.c + create mode 100755 tools/virtio/vt_test.sh + +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 ab1575b781dd..eb4924742dc2 100644 +--- a/Documentation/devicetree/bindings/net/fsl,fec.yaml ++++ b/Documentation/devicetree/bindings/net/fsl,fec.yaml +@@ -186,6 +186,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. +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 0694e03e20f8..98fc1c4aaade 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -34,6 +34,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 +@@ -73,7 +74,7 @@ config ARM + select HAS_IOPORT + 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 +@@ -96,7 +97,7 @@ config ARM + select HAVE_DYNAMIC_FTRACE_WITH_REGS if HAVE_DYNAMIC_FTRACE + select HAVE_EFFICIENT_UNALIGNED_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && MMU + select HAVE_EXIT_THREAD +- select HAVE_FAST_GUP if ARM_LPAE ++ select HAVE_FAST_GUP if ARM_LPAE && !(PREEMPT_RT && HIGHPTE) + select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL + select HAVE_FUNCTION_ERROR_INJECTION + select HAVE_FUNCTION_GRAPH_TRACER +@@ -118,6 +119,7 @@ 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 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/nxp/imx/Makefile b/arch/arm/boot/dts/nxp/imx/Makefile +index 12e091dc4c4c..65faf0074601 100644 +--- a/arch/arm/boot/dts/nxp/imx/Makefile ++++ b/arch/arm/boot/dts/nxp/imx/Makefile +@@ -336,6 +336,8 @@ 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-igh.dtb \ + imx6ul-14x14-evk-csi.dtb \ + imx6ul-14x14-evk-emmc.dtb \ + imx6ul-14x14-evk-btwifi.dtb \ +@@ -367,6 +369,11 @@ dtb-$(CONFIG_SOC_IMX6UL) += \ + imx6ul-tx6ul-0011.dtb \ + imx6ul-tx6ul-mainboard.dtb \ + imx6ull-14x14-evk.dtb \ ++ imx6ull-14x14-evk-ecat.dtb \ ++ imx6ull-14x14-evk-igh.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 \ +@@ -418,6 +425,7 @@ imx6ul-14x14-evk-reve-ecspi-slave-dtbs := imx6ul-14x14-evk-ecspi-slave.dtb imx6u + imx6ul-14x14-evk-reve-ecspi-dtbs := imx6ul-14x14-evk-ecspi.dtb imx6ul-14x14-evk-reve.dtbo + imx6ul-14x14-evk-reve-gpmi-weim-dtbs := imx6ul-14x14-evk-gpmi-weim.dtb imx6ul-14x14-evk-reve.dtbo + imx6ull-14x14-evk-reve-dtbs := imx6ull-14x14-evk.dtb imx6ul-14x14-evk-reve.dtbo ++imx6ull-14x14-evk-reve-avb-dtbs := imx6ull-14x14-evk-avb.dtb imx6ul-14x14-evk-reve.dtbo imx6ull-14x14-evk-reve.dtbo + imx6ull-14x14-evk-reve-emmc-dtbs := imx6ull-14x14-evk-emmc.dtb imx6ul-14x14-evk-reve.dtbo + imx6ull-14x14-evk-reve-btwifi-dtbs := imx6ull-14x14-evk-btwifi.dtb imx6ul-14x14-evk-reve.dtbo + imx6ull-14x14-evk-reve-gpmi-weim-dtbs := imx6ull-14x14-evk-gpmi-weim.dtb imx6ul-14x14-evk-reve.dtbo +@@ -434,6 +442,7 @@ dtb-$(CONFIG_SOC_IMX6UL) += \ + imx6ul-14x14-evk-reve-ecspi.dtb \ + imx6ul-14x14-evk-reve-gpmi-weim.dtb \ + imx6ull-14x14-evk-reve.dtb \ ++ imx6ull-14x14-evk-reve-avb.dtb \ + imx6ull-14x14-evk-reve-emmc.dtb \ + imx6ull-14x14-evk-reve-btwifi.dtb \ + imx6ull-14x14-evk-reve-gpmi-weim.dtb \ +diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-ecat.dts b/arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-ecat.dts +new file mode 100644 +index 000000000000..2a99ab3960f1 +--- /dev/null ++++ b/arch/arm/boot/dts/nxp/imx/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/nxp/imx/imx6ul-14x14-evk-igh.dts b/arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-igh.dts +new file mode 100644 +index 000000000000..01b4bc6b4fa7 +--- /dev/null ++++ b/arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-igh.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx6ul-14x14-evk.dts" ++ ++ ++&fec1 { ++ compatible = "fsl,imx6ul-fec-igh-native", "fsl,imx6q-fec-igh-native"; ++}; +diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb-mcr.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb-mcr.dts +new file mode 100644 +index 000000000000..e06167bd96bc +--- /dev/null ++++ b/arch/arm/boot/dts/nxp/imx/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/nxp/imx/imx6ull-14x14-evk-avb.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb.dts +new file mode 100644 +index 000000000000..09af5531b3e0 +--- /dev/null ++++ b/arch/arm/boot/dts/nxp/imx/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/nxp/imx/imx6ull-14x14-evk-ecat.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-ecat.dts +new file mode 100644 +index 000000000000..67df18babf56 +--- /dev/null ++++ b/arch/arm/boot/dts/nxp/imx/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/nxp/imx/imx6ull-14x14-evk-igh.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-igh.dts +new file mode 100644 +index 000000000000..5eabf5eab1ab +--- /dev/null ++++ b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-igh.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx6ull-14x14-evk.dts" ++ ++ ++&fec1 { ++ compatible = "fsl,imx6ul-fec-igh-native", "fsl,imx6q-fec-igh-native"; ++}; +diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-lpuart.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-lpuart.dts +new file mode 100644 +index 000000000000..d7a378c5349c +--- /dev/null ++++ b/arch/arm/boot/dts/nxp/imx/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/nxp/imx/imx6ull-14x14-evk-reve.dtso b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-reve.dtso +new file mode 100644 +index 000000000000..e135f3f6e17b +--- /dev/null ++++ b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-reve.dtso +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++&{/} { ++ model = "Freescale i.MX6 ULL 14x14 RevE EVK Board"; ++}; +diff --git a/arch/arm/boot/dts/nxp/ls/Makefile b/arch/arm/boot/dts/nxp/ls/Makefile +index 14759331dba2..766e8dbce535 100644 +--- a/arch/arm/boot/dts/nxp/ls/Makefile ++++ b/arch/arm/boot/dts/nxp/ls/Makefile +@@ -5,4 +5,5 @@ dtb-$(CONFIG_SOC_LS1021A) += \ + ls1021a-qds.dtb \ + ls1021a-tqmls1021a-mbls1021a.dtb \ + ls1021a-tsn.dtb \ +- ls1021a-twr.dtb ++ ls1021a-twr.dtb \ ++ ls1021a-iot.dtb +diff --git a/arch/arm/boot/dts/nxp/ls/ls1021a-iot-bm.dts b/arch/arm/boot/dts/nxp/ls/ls1021a-iot-bm.dts +new file mode 100644 +index 000000000000..3a23b97f0081 +--- /dev/null ++++ b/arch/arm/boot/dts/nxp/ls/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/nxp/ls/ls1021a.dtsi b/arch/arm/boot/dts/nxp/ls/ls1021a.dtsi +index cd0d699985c6..f1657b7c6e8b 100644 +--- a/arch/arm/boot/dts/nxp/ls/ls1021a.dtsi ++++ b/arch/arm/boot/dts/nxp/ls/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 b5a304ce400a..f223f5d9970a 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 472dff26b4b3..8c1ba26181b8 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 +@@ -595,3 +596,5 @@ CONFIG_CRYPTO_SM4_GENERIC=m + CONFIG_CRYPTO_ARIA=m + CONFIG_CRYPTO_HCTR2=m + CONFIG_CRYPTO_SM3_GENERIC=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 c42c2372b339..b3ae89407dd3 100644 +--- a/arch/arm/configs/multi_v7_defconfig ++++ b/arch/arm/configs/multi_v7_defconfig +@@ -1316,3 +1316,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/kernel/smp.c b/arch/arm/kernel/smp.c +index 3431c0553f45..3d44ece9ff74 100644 +--- a/arch/arm/kernel/smp.c ++++ b/arch/arm/kernel/smp.c +@@ -50,6 +50,10 @@ + + #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 +@@ -65,6 +69,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 +@@ -539,6 +546,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); +@@ -669,6 +679,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 fef62e4a9edd..622a30243f4a 100644 +--- a/arch/arm/mm/fault.c ++++ b/arch/arm/mm/fault.c +@@ -404,6 +404,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; + +@@ -474,6 +477,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; + } +diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c +index 7e8773a2d99d..9fde36fcb80c 100644 +--- a/arch/arm/vfp/vfpmodule.c ++++ b/arch/arm/vfp/vfpmodule.c +@@ -55,6 +55,34 @@ extern unsigned int VFP_arch_feroceon __alias(VFP_arch); + */ + union vfp_state *vfp_current_hw_state[NR_CPUS]; + ++/* ++ * Claim ownership of the VFP unit. ++ * ++ * The caller may change VFP registers until vfp_unlock() is called. ++ * ++ * local_bh_disable() is used to disable preemption and to disable VFP ++ * processing in softirq context. On PREEMPT_RT kernels local_bh_disable() is ++ * not sufficient because it only serializes soft interrupt related sections ++ * via a local lock, but stays preemptible. Disabling preemption is the right ++ * choice here as bottom half processing is always in thread context on RT ++ * kernels so it implicitly prevents bottom half processing as well. ++ */ ++static void vfp_lock(void) ++{ ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) ++ local_bh_disable(); ++ else ++ preempt_disable(); ++} ++ ++static void vfp_unlock(void) ++{ ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) ++ local_bh_enable(); ++ else ++ preempt_enable(); ++} ++ + /* + * Is 'thread's most up to date state stored in this CPUs hardware? + * Must be called from non-preemptible context. +@@ -240,7 +268,7 @@ static void vfp_panic(char *reason, u32 inst) + /* + * Process bitmask of exception conditions. + */ +-static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_regs *regs) ++static int vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr) + { + int si_code = 0; + +@@ -248,8 +276,7 @@ static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_ + + if (exceptions == VFP_EXCEPTION_ERROR) { + vfp_panic("unhandled bounce", inst); +- vfp_raise_sigfpe(FPE_FLTINV, regs); +- return; ++ return FPE_FLTINV; + } + + /* +@@ -277,8 +304,7 @@ static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_ + RAISE(FPSCR_OFC, FPSCR_OFE, FPE_FLTOVF); + RAISE(FPSCR_IOC, FPSCR_IOE, FPE_FLTINV); + +- if (si_code) +- vfp_raise_sigfpe(si_code, regs); ++ return si_code; + } + + /* +@@ -324,6 +350,8 @@ static u32 vfp_emulate_instruction(u32 inst, u32 fpscr, struct pt_regs *regs) + static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) + { + u32 fpscr, orig_fpscr, fpsid, exceptions; ++ int si_code2 = 0; ++ int si_code = 0; + + pr_debug("VFP: bounce: trigger %08x fpexc %08x\n", trigger, fpexc); + +@@ -369,8 +397,8 @@ static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) + * unallocated VFP instruction but with FPSCR.IXE set and not + * on VFP subarch 1. + */ +- vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr, regs); +- return; ++ si_code = vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr); ++ goto exit; + } + + /* +@@ -394,14 +422,14 @@ static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) + */ + exceptions = vfp_emulate_instruction(trigger, fpscr, regs); + if (exceptions) +- vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); ++ si_code2 = vfp_raise_exceptions(exceptions, trigger, orig_fpscr); + + /* + * If there isn't a second FP instruction, exit now. Note that + * the FPEXC.FP2V bit is valid only if FPEXC.EX is 1. + */ + if ((fpexc & (FPEXC_EX | FPEXC_FP2V)) != (FPEXC_EX | FPEXC_FP2V)) +- return; ++ goto exit; + + /* + * The barrier() here prevents fpinst2 being read +@@ -413,7 +441,13 @@ static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) + emulate: + exceptions = vfp_emulate_instruction(trigger, orig_fpscr, regs); + if (exceptions) +- vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); ++ si_code = vfp_raise_exceptions(exceptions, trigger, orig_fpscr); ++exit: ++ vfp_unlock(); ++ if (si_code2) ++ vfp_raise_sigfpe(si_code2, regs); ++ if (si_code) ++ vfp_raise_sigfpe(si_code, regs); + } + + static void vfp_enable(void *unused) +@@ -512,11 +546,9 @@ static inline void vfp_pm_init(void) { } + */ + void vfp_sync_hwstate(struct thread_info *thread) + { +- unsigned int cpu = get_cpu(); ++ vfp_lock(); + +- local_bh_disable(); +- +- if (vfp_state_in_hw(cpu, thread)) { ++ if (vfp_state_in_hw(raw_smp_processor_id(), thread)) { + u32 fpexc = fmrx(FPEXC); + + /* +@@ -527,8 +559,7 @@ void vfp_sync_hwstate(struct thread_info *thread) + fmxr(FPEXC, fpexc); + } + +- local_bh_enable(); +- put_cpu(); ++ vfp_unlock(); + } + + /* Ensure that the thread reloads the hardware VFP state on the next use. */ +@@ -683,7 +714,7 @@ static int vfp_support_entry(struct pt_regs *regs, u32 trigger) + if (!user_mode(regs)) + return vfp_kmode_exception(regs, trigger); + +- local_bh_disable(); ++ vfp_lock(); + fpexc = fmrx(FPEXC); + + /* +@@ -748,6 +779,7 @@ static int vfp_support_entry(struct pt_regs *regs, u32 trigger) + * replay the instruction that trapped. + */ + fmxr(FPEXC, fpexc); ++ vfp_unlock(); + } else { + /* Check for synchronous or asynchronous exceptions */ + if (!(fpexc & (FPEXC_EX | FPEXC_DEX))) { +@@ -762,17 +794,17 @@ static int vfp_support_entry(struct pt_regs *regs, u32 trigger) + if (!(fpscr & FPSCR_IXE)) { + if (!(fpscr & FPSCR_LENGTH_MASK)) { + pr_debug("not VFP\n"); +- local_bh_enable(); ++ vfp_unlock(); + return -ENOEXEC; + } + fpexc |= FPEXC_DEX; + } + } + bounce: regs->ARM_pc += 4; ++ /* VFP_bounce() will invoke vfp_unlock() */ + VFP_bounce(trigger, fpexc, regs); + } + +- local_bh_enable(); + return 0; + } + +@@ -819,7 +851,7 @@ void kernel_neon_begin(void) + unsigned int cpu; + u32 fpexc; + +- local_bh_disable(); ++ vfp_lock(); + + /* + * Kernel mode NEON is only allowed outside of hardirq context with +@@ -850,7 +882,7 @@ void kernel_neon_end(void) + { + /* Disable the NEON/VFP unit. */ + fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); +- local_bh_enable(); ++ vfp_unlock(); + } + EXPORT_SYMBOL(kernel_neon_end); + +diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig +index 1731a4c573e7..ff3ee8dc3c79 100644 +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -97,6 +97,7 @@ config ARM64 + select ARCH_SUPPORTS_NUMA_BALANCING + select ARCH_SUPPORTS_PAGE_TABLE_CHECK + select ARCH_SUPPORTS_PER_VMA_LOCK ++ select ARCH_SUPPORTS_RT + select ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH + select ARCH_WANT_COMPAT_IPC_PARSE_VERSION if COMPAT + select ARCH_WANT_DEFAULT_BPF_JIT +diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms +index e3b0450229e9..314b08100c82 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 "NXP i.MX SoC family" + select ARM64_ERRATUM_843419 +@@ -257,6 +275,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 efe32533733e..c4ad4060ad41 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 +@@ -75,13 +86,38 @@ 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-harpoon.dtb imx8mm-evk-harpoon-industrial.dtb imx8mm-evk-harpoon-avb.dtb \ ++ imx8mm-evk-harpoon-virtio-net.dtb \ ++ imx8mm-evk-qca-wifi-harpoon.dtb imx8mm-evk-qca-wifi-harpoon-industrial.dtb \ ++ imx8mm-evk-qca-wifi-harpoon-avb.dtb imx8mm-evk-qca-wifi-harpoon-virtio-net.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 \ ++ imx8mm-evk-igh.dtb \ ++ imx8mm-evk-ecat-userspace.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-rpmsg-ca53.dtb \ ++ imx8mp-evk-virtio-net-ca53.dtb \ ++ imx8mp-evk-virtio-net-cm7.dtb \ ++ imx8mp-evk-virtio-perf-ca53.dtb \ ++ imx8mp-evk-virtio-perf-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 \ +@@ -115,7 +151,8 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mn-ddr4-evk.dtb imx8mn-ddr4-evk-rm67191.dtb imx8mn + imx8mn-ddr4-evk-usd-wifi.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mn-ddr4-evk-rpmsg.dtb imx8mn-ddr3l-evk-rpmsg.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mn-ddr4-evk-root.dtb imx8mn-ddr4-evk-inmate.dtb imx8mn-evk-root.dtb imx8mn-evk-inmate.dtb \ +- imx8mn-evk-lk.dtb imx8mn-ddr4-evk-lk.dtb ++ imx8mn-evk-lk.dtb imx8mn-ddr4-evk-lk.dtb imx8mn-evk-harpoon.dtb imx8mn-evk-harpoon-industrial.dtb \ ++ imx8mn-evk-harpoon-avb.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mn-ddr3l-evk-ak5558.dtb imx8mn-ddr4-evk-ak5558.dtb imx8mn-evk-ak5558.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mn-ab2.dtb imx8mn-ddr3l-ab2.dtb imx8mn-ddr4-ab2.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mn-evk-iqaudio-dacplus.dtb imx8mn-evk-iqaudio-dacpro.dtb imx8mn-evk-hifiberry-dacplus.dtb \ +@@ -123,7 +160,9 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mn-evk-iqaudio-dacplus.dtb imx8mn-evk-iqaudio-dacp + dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk.dtb imx8mp-evk-rm67191.dtb imx8mp-evk-it6263-lvds-dual-channel.dtb \ + imx8mp-evk-pcie-ep.dtb imx8mp-evk-rpmsg.dtb imx8mp-evk-ecspi-slave.dtb \ + imx8mp-evk-jdi-wuxga-lvds-panel.dtb imx8mp-evk-flexcan2.dtb \ +- imx8mp-evk-root.dtb imx8mp-evk-inmate.dtb imx8mp-evk-ov2775.dtb \ ++ imx8mp-evk-root.dtb imx8mp-evk-harpoon.dtb imx8mp-evk-harpoon-industrial.dtb imx8mp-evk-inmate.dtb \ ++ imx8mp-evk-harpoon-avb.dtb imx8mp-evk-harpoon-virtio-net.dtb \ ++ imx8mp-evk-ov2775.dtb \ + imx8mp-evk-ov2775-ov5640.dtb imx8mp-evk-basler-ov5640.dtb imx8mp-evk-basler.dtb \ + imx8mp-evk-basler-ov2775.dtb imx8mp-evk-dual-basler.dtb \ + imx8mp-evk-dual-ov2775.dtb imx8mp-evk-spdif-lb.dtb imx8mp-evk-dsp.dtb \ +@@ -133,13 +172,19 @@ 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 imx8mp-evk-igh.dtb imx8mp-evk-ecat-userspace.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-navq.dtb imx8mp-navq-ov5640-ov5645.dtb imx8mp-navq-ov5647-ov5640.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 ++dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-dsa-enetc.dtb ++dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-dsa-fec-swp0.dtb ++dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-dsa-fec-swp3.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 +@@ -172,6 +217,10 @@ 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 ++imx8mp-evk-revb4-dsa-enetc-dtbs := imx8mp-evk-dsa-enetc.dtb imx8mp-evk-revb4.dtbo ++imx8mp-evk-revb4-dsa-fec-swp0-dtbs := imx8mp-evk-dsa-fec-swp0.dtb imx8mp-evk-revb4.dtbo ++imx8mp-evk-revb4-dsa-fec-swp3-dtbs := imx8mp-evk-dsa-fec-swp3.dtb imx8mp-evk-revb4.dtbo + + dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-rm67191.dtb +@@ -207,6 +256,10 @@ 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) += imx8mp-evk-revb4-dsa-enetc.dtb ++dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-dsa-fec-swp0.dtb ++dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-dsa-fec-swp3.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 \ +@@ -314,7 +367,8 @@ dtb-$(CONFIG_ARCH_MXC) += imx8ulp-evk.dtb imx8ulp-evk-lpspi-slave.dtb \ + imx8ulp-9x9-evk-i3c.dtb imx8ulp-9x9-evk-sof-btsco.dtb \ + imx8ulp-evk-lpa.dtb imx8ulp-9x9-evk-lpa.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 \ +@@ -370,13 +424,21 @@ dtb-$(CONFIG_ARCH_MXC) += imx8qxp-mek.dtb imx8qxp-mek-ov5640.dtb \ + imx8qxp-17x17-val.dtb imx8dx-lpddr4-val.dtb imx8dx-17x17-val.dtb + dtb-$(CONFIG_ARCH_MXC) += imx8qxp-mek-dom0.dtb imx8qxp-mek-root.dtb \ + imx8qxp-mek-inmate.dtb +-dtb-$(CONFIG_ARCH_MXC) += imx93-14x14-evk.dtb \ ++dtb-$(CONFIG_ARCH_MXC) += imx93-14x14-evk.dtb imx93-14x14-evk-aud-hat-avb.dtb \ + imx93-14x14-evk-tja1103.dtb imx93-14x14-evk-rm67199.dtb \ + imx93-14x14-evk-mqs.dtb imx93-14x14-evk-aud-hat.dtb \ +- imx93-14x14-evk-lvds-it6263.dtb imx93-14x14-evk-sja1105.dtb \ ++ imx93-14x14-evk-lvds-it6263.dtb imx93-14x14-evk-sja1105.dtb imx93-14x14-evk-sja1105-avb.dtb \ + imx93-14x14-evk-flexspi-m2.dtb imx93-14x14-evk-dsi-serdes.dtb \ +- imx93-14x14-evk-i3c.dtb ++ imx93-14x14-evk-i3c.dtb imx93-14x14-evk-imxai2eth-ath.dtb \ ++ imx93-14x14-evk-virtio-net-ca55.dtb \ ++ imx93-14x14-evk-virtio-net-cm33.dtb \ ++ imx93-14x14-evk-uart-sharing-cm33.dtb \ ++ imx93-14x14-evk-multicore-rpmsg.dtb \ ++ imx93-14x14-evk-multicore-rtos.dtb + dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-evk.dtb \ ++ imx93-11x11-evk-avb.dtb \ ++ imx93-11x11-evk-harpoon.dtb imx93-11x11-evk-harpoon-industrial.dtb imx93-11x11-evk-harpoon-avb.dtb \ ++ imx93-11x11-evk-harpoon-virtio-net.dtb \ + imx93-11x11-evk-inmate.dtb imx93-11x11-evk-root.dtb imx93-11x11-evk-flexio-i2c.dtb \ + imx93-11x11-evk-i2c-spi-slave.dtb \ + imx93-11x11-evk-i3c.dtb imx93-11x11-evk-lpuart.dtb \ +@@ -386,7 +448,21 @@ dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-evk.dtb \ + imx93-11x11-evk-mt9m114.dtb \ + imx93-11x11-evk-ld.dtb \ + imx93-11x11-evk-iw612-otbr.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-igh.dtb \ ++ imx93-11x11-evk-ecat-userspace.dtb \ ++ imx93-11x11-evk-multicore-rpmsg.dtb \ ++ imx93-11x11-evk-multicore-rtos.dtb \ ++ imx93-11x11-evk-dsa.dtb \ ++ imx93-11x11-evk-dsa-enetc.dtb \ ++ imx93-11x11-evk-dsa-fec-swp0.dtb \ ++ imx93-11x11-evk-dsa-fec-swp3.dtb \ ++ imx93-11x11-evk-dpdk.dtb + + dtb-$(CONFIG_ARCH_MXC) += imx91-11x11-evk.dtb \ + imx91-11x11-evk-flexspi-m2.dtb imx91-11x11-evk-flexspi-nand-m2.dtb \ +@@ -438,9 +514,12 @@ 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-aud-hat.dtb \ + imx93-9x9-qsb-ld.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.dts imx93-9x9-qsb-root.dts + dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba93xxla.dtb + dtb-$(CONFIG_ARCH_MXC) += imx91-9x9-qsb.dtb \ + imx91-9x9-qsb-can1.dtb \ +@@ -456,8 +535,9 @@ dtb-$(CONFIG_ARCH_MXC) += imx91-9x9-qsb-i3c.dtb + dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk.dtb + dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-flexspi-m2.dtb + dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-mqs.dtb ++dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-multicore-rtos.dtb + dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-aud-hat.dtb +-dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-root.dtb imx95-15x15-evk-inmate.dtb ++dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-root.dtb imx95-15x15-evk-inmate.dtb imx95-15x15-evk-harpoon.dtb imx95-15x15-evk-harpoon-industrial.dtb + dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-ab2.dtb + dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-i2c-spi-slave.dtb + DTC_FLAGS_imx95-15x15-evk-boe-wxga-lvds0-panel := -@ +@@ -516,6 +596,8 @@ DTC_FLAGS_imx95-15x15-evk-rpmsg-lpv := -@ + dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-rpmsg.dtb imx95-15x15-evk-rpmsg-lpv.dtb + + dtb-$(CONFIG_ARCH_MXC) += imx95-19x19-evk.dtb imx95-19x19-evk-root.dtb imx95-19x19-evk-inmate.dtb \ ++ imx95-19x19-evk-harpoon.dtb imx95-19x19-evk-harpoon-industrial.dtb \ ++ imx95-19x19-evk-multicore-rtos.dtb \ + imx95-19x19-evk-lpspi-slave.dtb + + imx95-15x15-evk-ox03c10-dtbs := imx95-15x15-evk.dtb imx95-15x15-evk-ox03c10.dtbo +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 51218c553bd8..c65326e89be4 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"; + +@@ -252,6 +265,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 71890c8d3d56..e5f2553a7b7a 100644 +--- a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi +@@ -1113,7 +1113,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 2d1e9ed7dac6..01e1194fff66 100644 +--- a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi +@@ -1063,7 +1063,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 10922fea2fc5..981fd470a1b9 100644 +--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi ++++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi +@@ -1049,7 +1049,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 1145f3298053..ff6cfe8971a0 100644 +--- a/arch/arm64/boot/dts/freescale/imx8-ss-audio.dtsi ++++ b/arch/arm64/boot/dts/freescale/imx8-ss-audio.dtsi +@@ -247,6 +247,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>; +@@ -567,6 +576,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 02a40cf803de..f71727fed226 100644 +--- a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts ++++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts +@@ -320,6 +320,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 { +@@ -343,6 +346,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 c8ec4328ee6e..7df83c1f9e59 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/imx8m-rproc-ca53.dtsi b/arch/arm64/boot/dts/freescale/imx8m-rproc-ca53.dtsi +new file mode 100644 +index 000000000000..c44e6a3d8cf0 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8m-rproc-ca53.dtsi +@@ -0,0 +1,34 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/ { ++ ca53_1: remoteproc-ca53-1 { ++ compatible = "fsl,imx-rproc-psci"; ++ /* bitmask:0b0010, assign A53 Core 1 */ ++ fsl,cpus-bits = <0x2>; ++ memory-region = <&rtos_ca53_reserved>; ++ }; ++ ++ ca53_2: remoteproc-ca53-2 { ++ compatible = "fsl,imx-rproc-psci"; ++ /* bitmask:0b0100, assign A53 Core 2 */ ++ fsl,cpus-bits = <0x4>; ++ memory-region = <&rtos_ca53_reserved>; ++ }; ++ ++ ca53_3: remoteproc-ca53-3 { ++ compatible = "fsl,imx-rproc-psci"; ++ /* bitmask:0b1000, assign A53 Core 3 */ ++ fsl,cpus-bits = <0x8>; ++ memory-region = <&rtos_ca53_reserved>; ++ }; ++ ++ ca53_2_3: remoteproc-ca53-2-3 { ++ compatible = "fsl,imx-rproc-psci"; ++ /* bitmask:0b1100, assign A53 Core 2 and Core 3 */ ++ fsl,cpus-bits = <0xc>; ++ memory-region = <&rtos_ca53_reserved>; ++ }; ++}; +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..f5b15dddea51 +--- /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, 2024 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 = <98304000>; ++ ++ /* 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-userspace.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-ecat-userspace.dts +new file mode 100644 +index 000000000000..a2ff71b9c26b +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-ecat-userspace.dts +@@ -0,0 +1,15 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx8mm-evk.dts" ++ ++ðphy0 { ++ /delete-property/ reset-assert-us; ++ /delete-property/ reset-deassert-us; ++}; ++ ++&fec1 { ++ compatible = "fsl,imx8mm-fec-uio"; ++}; +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-harpoon-avb.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-avb.dts +new file mode 100644 +index 000000000000..866a9a8c239b +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-avb.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++#include "imx8mm-evk-harpoon.dts" ++#include "imx8m-rpmsg-ca53.dtsi" ++ ++&fec1 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-industrial.dts +new file mode 100644 +index 000000000000..bedfd8da1e70 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-industrial.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++#include "imx8mm-evk-root.dts" ++#include "imx8m-rpmsg-ca53.dtsi" ++ ++&fec1 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-virtio-net.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-virtio-net.dts +new file mode 100644 +index 000000000000..e7a560c16aa3 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-virtio-net.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx8mm-evk-root.dts" ++#include "imx8mm-virtio-ca53.dtsi" ++ ++&fec1 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon.dts +new file mode 100644 +index 000000000000..70b2fa9b6b31 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon.dts +@@ -0,0 +1,31 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2021-2023 NXP ++ */ ++ ++#include "imx8mm-evk-root.dts" ++#include "imx8m-rpmsg-ca53.dtsi" ++ ++&i2c3 { ++ status = "disabled"; ++}; ++ ++&sai1 { ++ status = "disabled"; ++}; ++ ++&sai2 { ++ status = "disabled"; ++}; ++ ++&sai3 { ++ status = "disabled"; ++}; ++ ++&sai5 { ++ status = "disabled"; ++}; ++ ++&sai6 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-igh.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-igh.dts +new file mode 100755 +index 000000000000..14bf12d0dd82 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-igh.dts +@@ -0,0 +1,10 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx8mm-evk.dts" ++ ++&fec1 { ++ compatible = "fsl,imx8mm-fec-igh-native", "fsl,imx8mq-fec-igh-native", "fsl,imx6sx-fec-igh-native"; ++}; +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..c403173bd960 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rpmsg.dts +@@ -0,0 +1,80 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023, 2025 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mm-evk-multicore-rtos.dts" ++#include "imx8m-generic-mbox.dtsi" ++#include "imx8m-generic-mbox-1.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ vdev0vring0_ca53_2: vdev0vring0@b8600000 { ++ reg = <0 0xb8600000 0 0x8000>; ++ no-map; ++ }; ++ ++ vdev0vring1_ca53_2: vdev0vring1@b8608000 { ++ reg = <0 0xb8608000 0 0x8000>; ++ no-map; ++ }; ++ ++ rsc_table_ca53_2: rsc-table@b86f0000 { ++ reg = <0 0xb86f0000 0 0x1000>; ++ no-map; ++ }; ++ ++ vdev0buffer_ca53_2: vdev0buffer@b8700000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xb8700000 0 0x100000>; ++ no-map; ++ }; ++ ++ vdev0vring0_ca53_3: vdev0vring0@b8610000 { ++ reg = <0 0xb8610000 0 0x8000>; ++ no-map; ++ }; ++ ++ vdev0vring1_ca53_3: vdev0vring1@b8618000 { ++ reg = <0 0xb8618000 0 0x8000>; ++ no-map; ++ }; ++ ++ rsc_table_ca53_3: rsc-table@b86f1000 { ++ reg = <0 0xb86f1000 0 0x1000>; ++ no-map; ++ }; ++ ++ vdev0buffer_ca53_3: vdev0buffer@b8800000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xb8800000 0 0x100000>; ++ no-map; ++ }; ++ }; ++}; ++ ++&ca53_2 { ++ memory-region = <&vdev0buffer_ca53_2>, <&vdev0vring0_ca53_2>, <&vdev0vring1_ca53_2>, <&rsc_table_ca53_2>, <&rtos_ca53_reserved>; ++ dma-coherent; ++ 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 */ ++ fsl,startup-delay-ms = <50>; ++}; ++ ++&ca53_3 { ++ memory-region = <&vdev0buffer_ca53_3>, <&vdev0vring0_ca53_3>, <&vdev0vring1_ca53_3>, <&rsc_table_ca53_3>, <&rtos_ca53_reserved>; ++ dma-coherent; ++ 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 */ ++ fsl,startup-delay-ms = <50>; ++}; +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..c00cf94ce50e +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rtos.dts +@@ -0,0 +1,78 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023-2024 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mm-evk.dts" ++#include "imx8m-rproc-ca53.dtsi" ++ ++/ { ++ 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-qca-wifi-harpoon-avb.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-avb.dts +new file mode 100644 +index 000000000000..20f26f3b642f +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-avb.dts +@@ -0,0 +1,7 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx8mm-evk-harpoon-avb.dts" ++#include "imx8mm-evk-qca-wifi.dtsi" +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-industrial.dts +new file mode 100644 +index 000000000000..77aae065a883 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-industrial.dts +@@ -0,0 +1,7 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx8mm-evk-harpoon-industrial.dts" ++#include "imx8mm-evk-qca-wifi.dtsi" +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-virtio-net.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-virtio-net.dts +new file mode 100644 +index 000000000000..caf41f260a50 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-virtio-net.dts +@@ -0,0 +1,7 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx8mm-evk-harpoon-virtio-net.dts" ++#include "imx8mm-evk-qca-wifi.dtsi" +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon.dts +new file mode 100644 +index 000000000000..01d7ef234886 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon.dts +@@ -0,0 +1,7 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx8mm-evk-harpoon.dts" ++#include "imx8mm-evk-qca-wifi.dtsi" +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dts +index aa1a25f00f55..cba11387cb08 100644 +--- a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dts ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dts +@@ -1,128 +1,9 @@ + // SPDX-License-Identifier: (GPL-2.0+ OR MIT) + /* +- * Copyright 2019 NXP ++ * Copyright 2019, 2024 NXP + */ + + /dts-v1/; + + #include "imx8mm-evk.dts" +- +-/ { +- model = "FSL i.MX8MM LPDDR4 EVK with QCA WIFI revC board "; +-}; +- +-/delete-node/&pmic_nxp; +- +-&i2c1 { +- pmic_rohm: pmic@4b { +- compatible = "rohm,bd71847"; +- reg = <0x4b>; +- pinctrl-0 = <&pinctrl_pmic>; +- pinctrl-names = "default"; +- interrupt-parent = <&gpio1>; +- interrupts = <3 IRQ_TYPE_LEVEL_LOW>; +- rohm,reset-snvs-powered; +- +- #clock-cells = <0>; +- clocks = <&osc_32k 0>; +- clock-output-names = "clk-32k-out"; +- +- regulators { +- buck1_reg: BUCK1 { +- regulator-name = "BUCK1"; +- regulator-min-microvolt = <700000>; +- regulator-max-microvolt = <1300000>; +- regulator-boot-on; +- regulator-always-on; +- regulator-ramp-delay = <1250>; +- }; +- +- buck2_reg: BUCK2 { +- regulator-name = "BUCK2"; +- regulator-min-microvolt = <700000>; +- regulator-max-microvolt = <1300000>; +- regulator-boot-on; +- regulator-always-on; +- regulator-ramp-delay = <1250>; +- rohm,dvs-run-voltage = <1000000>; +- rohm,dvs-idle-voltage = <900000>; +- }; +- +- buck3_reg: BUCK3 { +- // BUCK5 in datasheet +- regulator-name = "BUCK3"; +- regulator-min-microvolt = <700000>; +- regulator-max-microvolt = <1350000>; +- regulator-boot-on; +- regulator-always-on; +- }; +- +- buck4_reg: BUCK4 { +- // BUCK6 in datasheet +- regulator-name = "BUCK4"; +- regulator-min-microvolt = <3000000>; +- regulator-max-microvolt = <3300000>; +- regulator-boot-on; +- regulator-always-on; +- }; +- +- buck5_reg: BUCK5 { +- // BUCK7 in datasheet +- regulator-name = "BUCK5"; +- regulator-min-microvolt = <1605000>; +- regulator-max-microvolt = <1995000>; +- regulator-boot-on; +- regulator-always-on; +- }; +- +- buck6_reg: BUCK6 { +- // BUCK8 in datasheet +- regulator-name = "BUCK6"; +- regulator-min-microvolt = <800000>; +- regulator-max-microvolt = <1400000>; +- regulator-boot-on; +- regulator-always-on; +- }; +- +- ldo1_reg: LDO1 { +- regulator-name = "LDO1"; +- regulator-min-microvolt = <1600000>; +- regulator-max-microvolt = <3300000>; +- regulator-boot-on; +- regulator-always-on; +- }; +- +- ldo2_reg: LDO2 { +- regulator-name = "LDO2"; +- regulator-min-microvolt = <800000>; +- regulator-max-microvolt = <900000>; +- regulator-boot-on; +- regulator-always-on; +- }; +- +- ldo3_reg: LDO3 { +- regulator-name = "LDO3"; +- regulator-min-microvolt = <1800000>; +- regulator-max-microvolt = <3300000>; +- regulator-boot-on; +- regulator-always-on; +- }; +- +- ldo4_reg: LDO4 { +- regulator-name = "LDO4"; +- regulator-min-microvolt = <900000>; +- regulator-max-microvolt = <1800000>; +- regulator-boot-on; +- regulator-always-on; +- }; +- +- ldo6_reg: LDO6 { +- regulator-name = "LDO6"; +- regulator-min-microvolt = <900000>; +- regulator-max-microvolt = <1800000>; +- regulator-boot-on; +- regulator-always-on; +- }; +- }; +- }; +-}; ++#include "imx8mm-evk-qca-wifi.dtsi" +diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dtsi +new file mode 100644 +index 000000000000..13d3076ac94c +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dtsi +@@ -0,0 +1,124 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/ { ++ model = "FSL i.MX8MM LPDDR4 EVK with QCA WIFI revC board "; ++}; ++ ++/delete-node/&pmic_nxp; ++ ++&i2c1 { ++ pmic_rohm: pmic@4b { ++ compatible = "rohm,bd71847"; ++ reg = <0x4b>; ++ pinctrl-0 = <&pinctrl_pmic>; ++ pinctrl-names = "default"; ++ interrupt-parent = <&gpio1>; ++ interrupts = <3 IRQ_TYPE_LEVEL_LOW>; ++ rohm,reset-snvs-powered; ++ ++ #clock-cells = <0>; ++ clocks = <&osc_32k 0>; ++ clock-output-names = "clk-32k-out"; ++ ++ regulators { ++ buck1_reg: BUCK1 { ++ regulator-name = "BUCK1"; ++ regulator-min-microvolt = <700000>; ++ regulator-max-microvolt = <1300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <1250>; ++ }; ++ ++ buck2_reg: BUCK2 { ++ regulator-name = "BUCK2"; ++ regulator-min-microvolt = <700000>; ++ regulator-max-microvolt = <1300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <1250>; ++ rohm,dvs-run-voltage = <1000000>; ++ rohm,dvs-idle-voltage = <900000>; ++ }; ++ ++ buck3_reg: BUCK3 { ++ // BUCK5 in datasheet ++ regulator-name = "BUCK3"; ++ regulator-min-microvolt = <700000>; ++ regulator-max-microvolt = <1350000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ buck4_reg: BUCK4 { ++ // BUCK6 in datasheet ++ regulator-name = "BUCK4"; ++ regulator-min-microvolt = <3000000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ buck5_reg: BUCK5 { ++ // BUCK7 in datasheet ++ regulator-name = "BUCK5"; ++ regulator-min-microvolt = <1605000>; ++ regulator-max-microvolt = <1995000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ buck6_reg: BUCK6 { ++ // BUCK8 in datasheet ++ regulator-name = "BUCK6"; ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1400000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ ldo1_reg: LDO1 { ++ regulator-name = "LDO1"; ++ regulator-min-microvolt = <1600000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ ldo2_reg: LDO2 { ++ regulator-name = "LDO2"; ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <900000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ ldo3_reg: LDO3 { ++ regulator-name = "LDO3"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ ldo4_reg: LDO4 { ++ regulator-name = "LDO4"; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ ldo6_reg: LDO6 { ++ regulator-name = "LDO6"; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ }; ++ }; ++}; +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..ef2732f6a4fc +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-ca53.dts +@@ -0,0 +1,40 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023-2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mm-evk.dts" ++#include "imx8m-rpmsg-ca53.dtsi" ++#include "imx8m-rproc-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 c59212d17112..9999e4b8d241 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" + + / { +@@ -73,6 +73,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>; +@@ -101,13 +186,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..4e599a5ea4da +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-ca53.dts +@@ -0,0 +1,83 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023-2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mm-evk.dts" ++#include "imx8m-generic-mbox.dtsi" ++#include "imx8m-rproc-ca53.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ virtio_reserved: virtio@b8400000 { ++ no-map; ++ reg = <0 0xb8400000 0x0 0x00100000>; ++ }; ++ ++ /* 512MB */ ++ rtos_ca53_reserved: rtos-ca53@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..f20b8fe94ef9 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-cm4.dts +@@ -0,0 +1,74 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023-2024 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>; ++ }; ++ ++ imx8mm-cm4 { ++ compatible = "fsl,imx8mm-cm4"; ++ clocks = <&clk IMX8MM_CLK_M4_DIV>; ++ mbox-names = "tx", "rx", "rxdb"; ++ mboxes = <&mu 0 1 ++ &mu 1 1 ++ &mu 3 1>; ++ memory-region = <&cm4_reserved>; ++ syscon = <&src>; ++ fsl,startup-delay-ms = <500>; ++ status = "okay"; ++ }; ++}; ++ ++&{/busfreq} { ++ /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ ++ status = "disabled"; ++}; ++ ++&uart4 { ++ status = "disabled"; ++}; ++ ++&clk { ++ init-on-array = ; ++}; ++ ++&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..c287fc44f0a1 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-ca53.dts +@@ -0,0 +1,79 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2022-2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mm-evk.dts" ++#include "imx8m-generic-mbox.dtsi" ++#include "imx8m-rproc-ca53.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ virtio_reserved: virtio@b8400000 { ++ no-map; ++ reg = <0 0xb8400000 0x0 0x00100000>; ++ }; ++ ++ /* 512MB */ ++ rtos_ca53_reserved: rtos-ca53@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..7b4254c60aa9 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-cm4.dts +@@ -0,0 +1,63 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2022-2024 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>; ++ }; ++ ++ imx8mm-cm4 { ++ compatible = "fsl,imx8mm-cm4"; ++ clocks = <&clk IMX8MM_CLK_M4_DIV>; ++ mbox-names = "tx", "rx", "rxdb"; ++ mboxes = <&mu 0 1 ++ &mu 1 1 ++ &mu 3 1>; ++ memory-region = <&cm4_reserved>; ++ syscon = <&src>; ++ fsl,startup-delay-ms = <500>; ++ status = "okay"; ++ }; ++}; ++ ++&{/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/imx8mm-virtio-ca53.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-virtio-ca53.dtsi +new file mode 100644 +index 000000000000..2202ca7b63f4 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mm-virtio-ca53.dtsi +@@ -0,0 +1,46 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ virtio_reserved: virtio@b8400000 { ++ no-map; ++ reg = <0 0xb8400000 0x0 0x00100000>; ++ }; ++ ++ sw_mbox_reserved: gen-sw-mbox@b8500000 { ++ reg = <0 0xb8500000 0 0x1000>; ++ no-map; ++ }; ++ ++ virtio_buffer: virtio-buffer@b3c00000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xb3c00000 0 0x4000000>; ++ }; ++ }; ++ ++ 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"; ++ }; ++ ++ virtio_net@b8400000 { ++ compatible = "virtio,mmio"; ++ reg = <0x0 0xb8400000 0x0 0x1000>; ++ 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 */ ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi +index abd144c2ea7d..47b640f837f3 100644 +--- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi ++++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi +@@ -612,6 +612,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/imx8mn-evk-harpoon-avb.dts b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-avb.dts +new file mode 100644 +index 000000000000..faadcdf51e32 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-avb.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++#include "imx8mn-evk-harpoon.dts" ++#include "imx8m-rpmsg-ca53.dtsi" ++ ++&fec1 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-industrial.dts +new file mode 100644 +index 000000000000..f3183f4e1a16 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-industrial.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++#include "imx8mn-evk-root.dts" ++#include "imx8m-rpmsg-ca53.dtsi" ++ ++&fec1 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon.dts +new file mode 100644 +index 000000000000..1581ea6e9ddc +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon.dts +@@ -0,0 +1,31 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2021-2023 NXP ++ */ ++ ++#include "imx8mn-evk-root.dts" ++#include "imx8m-rpmsg-ca53.dtsi" ++ ++&i2c3 { ++ status = "disabled"; ++}; ++ ++&sai2 { ++ status = "disabled"; ++}; ++ ++&sai3 { ++ status = "disabled"; ++}; ++ ++&sai5 { ++ status = "disabled"; ++}; ++ ++&sai6 { ++ status = "disabled"; ++}; ++ ++&sai7 { ++ status = "disabled"; ++}; +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..fbe9b5d8554f +--- /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, 2024 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 = <98304000>; ++ ++ 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-enetc.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-enetc.dts +new file mode 100644 +index 000000000000..29e5aeecaae9 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-enetc.dts +@@ -0,0 +1,17 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx8mp-evk-dsa.dts" ++ ++&netcdsa_port3 { ++ label = "swp3"; ++ /delete-property/ ethernet; ++}; ++ ++&netcdsa_port4 { ++ label = "cpu"; ++ ethernet = <&eqos>; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp0.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp0.dts +new file mode 100644 +index 000000000000..11c0940c642d +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp0.dts +@@ -0,0 +1,92 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023-2024 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>; ++ ++ netcdsa_ports: ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ netcdsa_port0: port@0 { ++ /* cpu port connected to fec */ ++ ethernet = <&fec>; ++ label = "cpu"; ++ reg = <0>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <100>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port1: port@1 { ++ label = "swp1"; ++ reg = <1>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port2: port@2 { ++ label = "swp2"; ++ reg = <2>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port3: port@3 { ++ label = "swp3"; ++ reg = <3>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port4: port@4 { ++ label = "swp4"; ++ reg = <4>; ++ phy-mode = "sgmii"; ++ status = "disabled"; ++ ++ 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-dsa-fec-swp3.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp3.dts +new file mode 100644 +index 000000000000..e572cb25511a +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp3.dts +@@ -0,0 +1,92 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023-2024 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>; ++ ++ netcdsa_ports: ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ netcdsa_port0: port@0 { ++ label = "swp0"; ++ reg = <0>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <100>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port1: port@1 { ++ label = "swp1"; ++ reg = <1>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port2: port@2 { ++ label = "swp2"; ++ reg = <2>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port3: port@3 { ++ /* cpu port connected to fec */ ++ ethernet = <&fec>; ++ label = "cpu"; ++ reg = <3>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port4: port@4 { ++ label = "swp4"; ++ reg = <4>; ++ phy-mode = "sgmii"; ++ status = "disabled"; ++ ++ 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-dsa.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa.dts +new file mode 100644 +index 000000000000..1d25b520c503 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa.dts +@@ -0,0 +1,92 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023-2024 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>; ++ ++ netcdsa_ports: ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ netcdsa_port0: port@0 { ++ label = "swp0"; ++ reg = <0>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <100>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port1: port@1 { ++ label = "swp1"; ++ reg = <1>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port2: port@2 { ++ label = "swp2"; ++ reg = <2>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port3: port@3 { ++ /* cpu port connected to eqos */ ++ ethernet = <&eqos>; ++ label = "cpu"; ++ reg = <3>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port4: port@4 { ++ label = "swp4"; ++ reg = <4>; ++ phy-mode = "sgmii"; ++ status = "disabled"; ++ ++ 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-userspace.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-ecat-userspace.dts +new file mode 100644 +index 000000000000..c60a00d8f084 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-ecat-userspace.dts +@@ -0,0 +1,33 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx8mp-evk.dts" ++ ++/{ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ /* Limit memory in 4G Bytes for user space EtherCAT */ ++ ecat_reserved: ecat@142000000 { ++ no-map; ++ reg = <0x1 0x42000000 0x0 0x80000000>; ++ }; ++ }; ++}; ++ ++ðphy1 { ++ /delete-property/ reset-gpios; ++ /delete-property/ reset-assert-us; ++ /delete-property/ reset-deassert-us; ++}; ++ ++&fec { ++ compatible = "fsl,imx8mm-fec-uio"; ++}; ++ ++&eqos { ++ compatible = "nxp,imx8mp-dwmac-eqos","snps,dwmac-5.10a"; ++}; +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-harpoon-avb.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-avb.dts +new file mode 100644 +index 000000000000..4604d7e98644 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-avb.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++#include "imx8mp-evk-harpoon.dts" ++#include "imx8mp-rpmsg-ca53.dtsi" ++ ++&eqos { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-industrial.dts +new file mode 100644 +index 000000000000..1bc31b6d52f7 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-industrial.dts +@@ -0,0 +1,15 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2022-2023 NXP ++ */ ++ ++#include "imx8mp-evk-root.dts" ++#include "imx8mp-rpmsg-ca53.dtsi" ++ ++&eqos { ++ status = "disabled"; ++}; ++ ++&flexcan1 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-virtio-net.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-virtio-net.dts +new file mode 100644 +index 000000000000..a3b3a25477c0 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-virtio-net.dts +@@ -0,0 +1,40 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mp-evk-root.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>; ++ }; ++ ++ 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>; ++ 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 */ ++ }; ++}; ++ ++&fec { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon.dts +new file mode 100644 +index 000000000000..828d15424e8a +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon.dts +@@ -0,0 +1,35 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2021-2023 NXP ++ */ ++ ++#include "imx8mp-evk-root.dts" ++#include "imx8mp-rpmsg-ca53.dtsi" ++ ++&i2c3 { ++ status = "disabled"; ++}; ++ ++&sai1 { ++ status = "disabled"; ++}; ++ ++&sai2 { ++ status = "disabled"; ++}; ++ ++&sai3 { ++ status = "disabled"; ++}; ++ ++&sai5 { ++ status = "disabled"; ++}; ++ ++&sai6 { ++ status = "disabled"; ++}; ++ ++&sai7 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-igh.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-igh.dts +new file mode 100644 +index 000000000000..7f21b27837b5 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-igh.dts +@@ -0,0 +1,10 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx8mp-evk.dts" ++ ++&fec { ++ compatible = "fsl,imx8mp-fec-igh-native", "fsl,imx8mq-fec-igh-native", "fsl,imx6sx-fec-igh-native"; ++}; +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..76e3ea45786d +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rpmsg.dts +@@ -0,0 +1,179 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023, 2025 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mp-evk-multicore-rtos.dts" ++#include "imx8mp-generic-mbox.dtsi" ++#include "imx8mp-generic-mbox-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; ++ }; ++ ++ vdev0vring0_ca53_2: vdev0vring0@fe100000 { ++ reg = <0 0xfe100000 0 0x8000>; ++ no-map; ++ }; ++ ++ vdev0vring1_ca53_2: vdev0vring1@fe108000 { ++ reg = <0 0xfe108000 0 0x8000>; ++ no-map; ++ }; ++ ++ rsc_table_ca53_2: rsc-table@fe1f0000 { ++ reg = <0 0xfe1f0000 0 0x1000>; ++ no-map; ++ }; ++ ++ vdev0buffer_ca53_2: vdev0buffer@fe200000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xfe200000 0 0x100000>; ++ no-map; ++ }; ++ ++ vdev0vring0_ca53_3: vdev0vring0@fe110000 { ++ reg = <0 0xfe110000 0 0x8000>; ++ no-map; ++ }; ++ ++ vdev0vring1_ca53_3: vdev0vring1@fe118000 { ++ reg = <0 0xfe118000 0 0x8000>; ++ no-map; ++ }; ++ ++ rsc_table_ca53_3: rsc-table@fe1f1000 { ++ reg = <0 0xfe1f1000 0 0x1000>; ++ no-map; ++ }; ++ ++ vdev0buffer_ca53_3: vdev0buffer@fe300000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xfe300000 0 0x100000>; ++ no-map; ++ }; ++ }; ++ ++ sound-wm8960 { ++ status = "disabled"; ++ }; ++ ++ imx8mp-cm7 { ++ clocks = <&clk IMX8MP_CLK_M7_DIV>, ++ <&audio_blk_ctrl IMX8MP_CLK_AUDIOMIX_AUDPLL_ROOT>; ++ clock-names = "core", "audio"; ++ }; ++}; ++ ++&ca53_2 { ++ memory-region = <&vdev0buffer_ca53_2>, <&vdev0vring0_ca53_2>, <&vdev0vring1_ca53_2>, <&rsc_table_ca53_2>, <&rtos_ca53_reserved>; ++ dma-coherent; ++ 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 */ ++ fsl,startup-delay-ms = <50>; ++}; ++ ++&ca53_3 { ++ memory-region = <&vdev0buffer_ca53_3>, <&vdev0vring0_ca53_3>, <&vdev0vring1_ca53_3>, <&rsc_table_ca53_3>, <&rtos_ca53_reserved>; ++ dma-coherent; ++ 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 */ ++ fsl,startup-delay-ms = <50>; ++}; ++ ++&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..66ff72d1497c +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rtos.dts +@@ -0,0 +1,80 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023-2024 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mp-evk.dts" ++#include "imx8m-rproc-ca53.dtsi" ++ ++/ { ++ 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@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-rpmsg-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-rpmsg-ca53.dts +new file mode 100644 +index 000000000000..2ee7a41da41e +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-rpmsg-ca53.dts +@@ -0,0 +1,45 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx8mp-evk.dts" ++#include "imx8mp-rpmsg-ca53.dtsi" ++#include "imx8m-rproc-ca53.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ rtos_ca53_reserved: rtos-ca53@c0000000 { ++ reg = <0 0xc0000000 0x0 0x3000000>; ++ no-map; ++ }; ++ ++ }; ++}; ++ ++&{/busfreq} { ++ /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ ++ status = "disabled"; ++}; ++ ++&uart3 { ++ 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..630df227725f +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-ca53.dts +@@ -0,0 +1,82 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023-2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mp-evk.dts" ++#include "imx8mp-generic-mbox.dtsi" ++#include "imx8m-rproc-ca53.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ virtio_reserved: virtio@fc700000 { ++ no-map; ++ reg = <0 0xfc700000 0x0 0x00100000>; ++ }; ++ ++ /* 512MB */ ++ rtos_ca53_reserved: rtos-ca53@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..6fbb9549d143 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-cm7.dts +@@ -0,0 +1,85 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023-2024 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>; ++ }; ++ ++ imx8mp-cm7 { ++ compatible = "fsl,imx8mn-cm7"; ++ 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 = <&cm7_reserved>; ++ fsl,startup-delay-ms = <500>; ++ status = "okay"; ++ }; ++}; ++ ++&{/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-evk-virtio-perf-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-ca53.dts +new file mode 100644 +index 000000000000..ccf566ec5fdd +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-ca53.dts +@@ -0,0 +1,69 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mp-evk.dts" ++#include "imx8mp-generic-mbox.dtsi" ++#include "imx8m-rproc-ca53.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ virtio_reserved: virtio@fc700000 { ++ no-map; ++ reg = <0 0xfc700000 0x0 0x00100000>; ++ }; ++ ++ /* 512MB */ ++ rtos_ca53_reserved: rtos-ca53@c0000000 { ++ no-map; ++ reg = <0 0xc0000000 0x0 0x20000000>; ++ }; ++ ++ virtio_buffer: virtio-buffer@f8700000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xf8700000 0 0x4000000>; ++ }; ++ }; ++ ++ virtio_trans@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 ++ >; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-cm7.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-cm7.dts +new file mode 100644 +index 000000000000..5897c49fa04a +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-cm7.dts +@@ -0,0 +1,69 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx8mp-evk.dts" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ linux,cma { ++ size = <0 0x20000000>; ++ alloc-ranges = <0 0x40000000 0 0x80000000>; ++ }; ++ ++ cm7_reserved: cm7@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; ++ mbox-names = "mmiowr", "mmioirq"; ++ mboxes = <&mu 0 2 ++ &mu 1 2>; ++ }; ++ ++ imx8mp-cm7 { ++ compatible = "fsl,imx8mn-cm7"; ++ 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 = <&cm7_reserved>; ++ status = "okay"; ++ fsl,startup-delay-ms = <500>; ++ }; ++}; ++ ++&{/busfreq} { ++ /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ ++ status = "disabled"; ++}; ++ ++&uart4 { ++ status = "disabled"; ++}; ++ ++&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/imx93-11x11-evk-avb.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-avb.dts +new file mode 100644 +index 000000000000..5f2f0ee80e09 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-avb.dts +@@ -0,0 +1,9 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023-2024 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx93-11x11-evk.dts" ++#include "imx93-evk-avb.dtsi" +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-dpdk.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dpdk.dts +new file mode 100644 +index 000000000000..5ac7f92b3a6b +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dpdk.dts +@@ -0,0 +1,16 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx93-11x11-evk.dts" ++ ++ðphy2 { ++ /delete-property/ reset-gpios; ++ /delete-property/ reset-assert-us; ++ /delete-property/ reset-deassert-us; ++}; ++ ++&fec { ++ compatible = "fsl,imx8mm-fec-uio"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-enetc.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-enetc.dts +new file mode 100644 +index 000000000000..f45760d6cd3e +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-enetc.dts +@@ -0,0 +1,17 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx93-11x11-evk-dsa.dts" ++ ++&netcdsa_port3 { ++ label = "swp3"; ++ /delete-property/ ethernet; ++}; ++ ++&netcdsa_port4 { ++ label = "cpu"; ++ ethernet = <&eqos>; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp0.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp0.dts +new file mode 100644 +index 000000000000..64e72b7aa9de +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp0.dts +@@ -0,0 +1,108 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023-2024 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 */ ++ ++ netcdsa_ports: ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ netcdsa_port0: port@0 { ++ /* cpu port connected to fec */ ++ ethernet = <&fec>; ++ label = "cpu"; ++ reg = <0>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <100>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port1: port@1 { ++ label = "swp1"; ++ reg = <1>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port2: port@2 { ++ label = "swp2"; ++ reg = <2>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port3: port@3 { ++ label = "swp3"; ++ reg = <3>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port4: port@4 { ++ label = "swp4"; ++ reg = <4>; ++ phy-mode = "sgmii"; ++ status = "disabled"; ++ ++ 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-dsa-fec-swp3.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp3.dts +new file mode 100644 +index 000000000000..6ae448c99103 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp3.dts +@@ -0,0 +1,108 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023-2024 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 */ ++ ++ netcdsa_ports: ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ netcdsa_port0: port@0 { ++ label = "swp0"; ++ reg = <0>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <100>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port1: port@1 { ++ label = "swp1"; ++ reg = <1>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port2: port@2 { ++ label = "swp2"; ++ reg = <2>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port3: port@3 { ++ /* cpu port connected to fec */ ++ ethernet = <&fec>; ++ label = "cpu"; ++ reg = <3>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port4: port@4 { ++ label = "swp4"; ++ reg = <4>; ++ phy-mode = "sgmii"; ++ status = "disabled"; ++ ++ 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-dsa.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa.dts +new file mode 100644 +index 000000000000..940a7a4ac441 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa.dts +@@ -0,0 +1,108 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023-2024 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 */ ++ ++ netcdsa_ports: ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ netcdsa_port0: port@0 { ++ label = "swp0"; ++ reg = <0>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <100>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port1: port@1 { ++ label = "swp1"; ++ reg = <1>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port2: port@2 { ++ label = "swp2"; ++ reg = <2>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port3: port@3 { ++ /* cpu port connected to eqos */ ++ ethernet = <&eqos>; ++ label = "cpu"; ++ reg = <3>; ++ phy-mode = "sgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ netcdsa_port4: port@4 { ++ label = "swp4"; ++ reg = <4>; ++ phy-mode = "sgmii"; ++ status = "disabled"; ++ ++ 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-userspace.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat-userspace.dts +new file mode 100644 +index 000000000000..5ac7f92b3a6b +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat-userspace.dts +@@ -0,0 +1,16 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx93-11x11-evk.dts" ++ ++ðphy2 { ++ /delete-property/ reset-gpios; ++ /delete-property/ reset-assert-us; ++ /delete-property/ reset-deassert-us; ++}; ++ ++&fec { ++ compatible = "fsl,imx8mm-fec-uio"; ++}; +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-harpoon-avb.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-avb.dts +new file mode 100644 +index 000000000000..95a4d0578fcb +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-avb.dts +@@ -0,0 +1,10 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx93-11x11-evk-harpoon.dts" ++ ++&eqos { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-industrial.dts +new file mode 100644 +index 000000000000..d0a18a1dd6dd +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-industrial.dts +@@ -0,0 +1,19 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx93-11x11-evk-root.dts" ++#include "imx93-rpmsg-ca55.dtsi" ++ ++&eqos { ++ status = "disabled"; ++}; ++ ++&flexcan2 { ++ status = "disabled"; ++}; ++ ++®_can2_stby { ++ status = "disabled"; ++}; +\ No newline at end of file +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-virtio-net.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-virtio-net.dts +new file mode 100644 +index 000000000000..75ff20e30ab6 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-virtio-net.dts +@@ -0,0 +1,46 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++/dts-v1/; ++#include "imx93-11x11-evk-root.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>; ++ 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 */ ++ }; ++}; ++ ++&fec { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon.dts +new file mode 100644 +index 000000000000..2f3ba10e7033 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon.dts +@@ -0,0 +1,35 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx93-11x11-evk-root.dts" ++#include "imx93-rpmsg-ca55.dtsi" ++ ++&dsi { ++ status = "disabled"; ++}; ++ ++&lpi2c1 { ++ status = "disabled"; ++}; ++ ++&lpi2c4 { ++ status = "disabled"; ++}; ++ ++&sai1 { ++ status = "disabled"; ++}; ++ ++&sai2 { ++ status = "disabled"; ++}; ++ ++&sai3 { ++ status = "disabled"; ++}; ++ ++&flexcan2 { ++ status = "disabled"; ++}; +\ No newline at end of file +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-igh.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-igh.dts +new file mode 100644 +index 000000000000..c2648d5b2662 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-igh.dts +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx93-11x11-evk.dts" ++ ++ ++&fec { ++ compatible = "fsl,imx93-fec-igh-native", "fsl,imx8mp-fec-igh-native", "fsl,imx8mq-fec-igh-native"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rpmsg.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rpmsg.dts +new file mode 100644 +index 000000000000..0b5bdeb94367 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rpmsg.dts +@@ -0,0 +1,47 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2025 NXP ++ */ ++ ++/dts-v1/; ++#include "imx93-11x11-evk-multicore-rtos.dts" ++#include "imx93-generic-mbox.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ vdev0vring0_ca55: vdev0vring0@fe100000 { ++ reg = <0 0xfe100000 0 0x8000>; ++ no-map; ++ }; ++ ++ vdev0vring1_ca55: vdev0vring1@fe108000 { ++ reg = <0 0xfe108000 0 0x8000>; ++ no-map; ++ }; ++ ++ rsc_table_ca55: rsc-table@fe1f0000 { ++ reg = <0 0xfe1f0000 0 0x1000>; ++ no-map; ++ }; ++ ++ vdev0buffer_ca55: vdev0buffer@fe200000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xfe200000 0 0x100000>; ++ no-map; ++ }; ++ }; ++}; ++ ++&ca55_1 { ++ memory-region = <&vdev0buffer_ca55>, <&vdev0vring0_ca55>, <&vdev0vring1_ca55>, <&rsc_table_ca55>, <&rtos_ca55_reserved>; ++ dma-coherent; ++ 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 */ ++ fsl,startup-delay-ms = <50>; ++}; +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..13db26b61ad1 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023-2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx93-11x11-evk.dts" ++#include "imx93-rproc-ca55.dtsi" ++ ++/ { ++ 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..0783e2e025ca +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-ca55.dts +@@ -0,0 +1,56 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023-2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx93-11x11-evk.dts" ++#include "imx93-generic-mbox.dtsi" ++#include "imx93-rproc-ca55.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ virtio_reserved: virtio@fc700000 { ++ no-map; ++ reg = <0 0xfc700000 0x0 0x00100000>; ++ }; ++ ++ /* 512MB */ ++ rtos_ca55_reserved: rtos-ca55@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..52a650c3ec25 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-cm33.dts +@@ -0,0 +1,58 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2023-2024 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"; ++}; ++ ++&cm33 { ++ memory-region = <&cm33_reserved>; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts +index 4d1aa281f33e..1d022b134f38 100644 +--- a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts ++++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts +@@ -324,7 +324,7 @@ &xcvr { + assigned-clock-rates = <12288000>, <200000000>; + status = "okay"; + }; +- ++ + &adc1 { + vref-supply = <®_vref_1v8>; + status = "okay"; +@@ -381,6 +381,9 @@ &eqos { + pinctrl-1 = <&pinctrl_eqos_sleep>; + 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 { +@@ -397,6 +400,61 @@ ethphy1: ethernet-phy@1 { + realtek,clkout-disable; + }; + }; ++ ++ 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-14x14-evk-aud-hat-avb.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat-avb.dts +new file mode 100644 +index 000000000000..332495ce5320 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat-avb.dts +@@ -0,0 +1,9 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx93-14x14-evk-aud-hat.dts" ++#include "imx93-evk-avb.dtsi" +diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat.dts +index 8c4ede5931db..470641838b78 100644 +--- a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat.dts ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat.dts +@@ -209,7 +209,7 @@ cs42448: audio-codec@48 { + VA-supply = <®_audio_pwr>; + VD-supply = <®_audio_pwr>; + VLS-supply = <®_audio_pwr>; +- VLC-supply = <®_audio_pwr>; ++ VLC-supply = <®_exp_sel>; + reset-gpio = <&gpio2 25 GPIO_ACTIVE_LOW>; + status = "okay"; + }; +diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-imxai2eth-ath.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-imxai2eth-ath.dts +new file mode 100644 +index 000000000000..84a9a16a06e3 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-imxai2eth-ath.dts +@@ -0,0 +1,126 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx93-14x14-evk.dts" ++ ++&lpi2c2 { ++ max7322: gpio@68 { ++ compatible = "maxim,max7322"; ++ reg = <0x68>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ reset-gpios = <&pcal6524 15 GPIO_ACTIVE_LOW>; ++ }; ++}; ++ ++&eqos { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_eqos_rgmii>; ++ phy-mode = "rgmii-id"; ++ phy-handle = <&eqosphy>; ++ pinctrl-assert-gpios = <&max7322 0 GPIO_ACTIVE_HIGH>; ++ snps,force_thresh_dma_mode; ++ snps,mtl-tx-config = <&mtl_tx_setup>; ++ snps,mtl-rx-config = <&mtl_rx_setup>; ++ ++ snps,clk-csr = <5>; ++ status = "okay"; ++ ++ mdio { ++ compatible = "snps,dwmac-mdio"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ clock-frequency = <2500000>; ++ ++ eqosphy: ethernet-phy@1 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <1>; ++ qca,disable-smarteee; ++ ++ vddio-supply = <&vddio1>; ++ ++ vddio1: vddio-regulator { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ }; ++ }; ++ }; ++ ++ 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>; ++ }; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl_eqos_rgmii: eqosrgmiigrp { ++ fsl,pins = < ++ MX93_PAD_ENET1_MDC__ENET_QOS_MDC 0x57e ++ MX93_PAD_ENET1_MDIO__ENET_QOS_MDIO 0x57e ++ MX93_PAD_ENET1_RD0__ENET_QOS_RGMII_RD0 0x57e ++ MX93_PAD_ENET1_RD1__ENET_QOS_RGMII_RD1 0x57e ++ MX93_PAD_ENET1_RD2__ENET_QOS_RGMII_RD2 0x57e ++ MX93_PAD_ENET1_RD3__ENET_QOS_RGMII_RD3 0x57e ++ MX93_PAD_ENET1_RXC__CCM_ENET_QOS_CLOCK_GENERATE_RX_CLK 0x57e ++ MX93_PAD_ENET1_RX_CTL__ENET_QOS_RGMII_RX_CTL 0x57e ++ MX93_PAD_ENET1_TD0__ENET_QOS_RGMII_TD0 0x57e ++ MX93_PAD_ENET1_TD1__ENET_QOS_RGMII_TD1 0x57e ++ MX93_PAD_ENET1_TD2__ENET_QOS_RGMII_TD2 0x57e ++ MX93_PAD_ENET1_TD3__ENET_QOS_RGMII_TD3 0x57e ++ MX93_PAD_ENET1_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK 0x57e ++ MX93_PAD_ENET1_TX_CTL__ENET_QOS_RGMII_TX_CTL 0x57e ++ >; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dts +index e3920484ae87..50e5b35b2c75 100644 +--- a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dts ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dts +@@ -1,73 +1,7 @@ + // SPDX-License-Identifier: (GPL-2.0+ OR MIT) + /* +- * Copyright 2023 NXP ++ * Copyright 2023-2024 NXP + */ + + #include "imx93-14x14-evk.dts" +- +-/ { +- reg_mqs_switch: regulator-mqs-switch { +- compatible = "regulator-fixed"; +- regulator-name = "mqs-switch"; +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; +- gpio = <&pcal6524_2 2 GPIO_ACTIVE_HIGH>; +- enable-active-high; +- regulator-always-on; +- }; +- +- reg_mqs_amp_en: regulator-mqs-amp-en { +- compatible = "regulator-fixed"; +- regulator-name = "mqs-amp-en"; +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; +- gpio = <&pcal6524_2 1 GPIO_ACTIVE_HIGH>; +- enable-active-high; +- regulator-always-on; +- }; +- +- sound-mqs { +- compatible = "fsl,imx6sx-sdb-mqs", +- "fsl,imx-audio-mqs"; +- model = "mqs-audio"; +- audio-cpu = <&sai1>; +- audio-codec = <&mqs1>; +- }; +- +- sound-bt-sco { +- status = "disabled"; +- }; +-}; +- +-&flexcan1 { +- status = "disabled"; +-}; +- +-&sai1 { +- #sound-dai-cells = <0>; +- clocks = <&clk IMX93_CLK_SAI1_IPG>, <&clk IMX93_CLK_DUMMY>, +- <&clk IMX93_CLK_SAI1_GATE>, <&clk IMX93_CLK_DUMMY>, +- <&clk IMX93_CLK_DUMMY>, <&clk IMX93_CLK_AUDIO_PLL>; +- clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3", "pll8k"; +- assigned-clocks = <&clk IMX93_CLK_SAI1>; +- assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>; +- assigned-clock-rates = <24576000>; +- status = "okay"; +-}; +- +-&mqs1 { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_mqs1>; +- clocks = <&clk IMX93_CLK_MQS1_GATE>; +- clock-names = "mclk"; +- status = "okay"; +-}; +- +-&iomuxc { +- pinctrl_mqs1: mqs1grp { +- fsl,pins = < +- MX93_PAD_PDM_CLK__MQS1_LEFT 0x31e +- MX93_PAD_PDM_BIT_STREAM0__MQS1_RIGHT 0x31e +- >; +- }; +-}; ++#include "imx93-14x14-evk-mqs.dtsi" +diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dtsi b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dtsi +new file mode 100644 +index 000000000000..4cb9a3280128 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dtsi +@@ -0,0 +1,71 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/ { ++ reg_mqs_switch: regulator-mqs-switch { ++ compatible = "regulator-fixed"; ++ regulator-name = "mqs-switch"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ gpio = <&pcal6524_2 2 GPIO_ACTIVE_HIGH>; ++ enable-active-high; ++ regulator-always-on; ++ }; ++ ++ reg_mqs_amp_en: regulator-mqs-amp-en { ++ compatible = "regulator-fixed"; ++ regulator-name = "mqs-amp-en"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ gpio = <&pcal6524_2 1 GPIO_ACTIVE_HIGH>; ++ enable-active-high; ++ regulator-always-on; ++ }; ++ ++ sound-mqs { ++ compatible = "fsl,imx6sx-sdb-mqs", ++ "fsl,imx-audio-mqs"; ++ model = "mqs-audio"; ++ audio-cpu = <&sai1>; ++ audio-codec = <&mqs1>; ++ }; ++ ++ sound-bt-sco { ++ status = "disabled"; ++ }; ++}; ++ ++&flexcan1 { ++ status = "disabled"; ++}; ++ ++&sai1 { ++ #sound-dai-cells = <0>; ++ clocks = <&clk IMX93_CLK_SAI1_IPG>, <&clk IMX93_CLK_DUMMY>, ++ <&clk IMX93_CLK_SAI1_GATE>, <&clk IMX93_CLK_DUMMY>, ++ <&clk IMX93_CLK_DUMMY>, <&clk IMX93_CLK_AUDIO_PLL>; ++ clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3", "pll8k"; ++ assigned-clocks = <&clk IMX93_CLK_SAI1>; ++ assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>; ++ assigned-clock-rates = <24576000>; ++ status = "okay"; ++}; ++ ++&mqs1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_mqs1>; ++ clocks = <&clk IMX93_CLK_MQS1_GATE>; ++ clock-names = "mclk"; ++ status = "okay"; ++}; ++ ++&iomuxc { ++ pinctrl_mqs1: mqs1grp { ++ fsl,pins = < ++ MX93_PAD_PDM_CLK__MQS1_LEFT 0x31e ++ MX93_PAD_PDM_BIT_STREAM0__MQS1_RIGHT 0x31e ++ >; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rpmsg.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rpmsg.dts +new file mode 100644 +index 000000000000..d6d07c82a766 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rpmsg.dts +@@ -0,0 +1,47 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2025 NXP ++ */ ++ ++/dts-v1/; ++#include "imx93-14x14-evk-multicore-rtos.dts" ++#include "imx93-generic-mbox.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ vdev0vring0_ca55: vdev0vring0@fe100000 { ++ reg = <0 0xfe100000 0 0x8000>; ++ no-map; ++ }; ++ ++ vdev0vring1_ca55: vdev0vring1@fe108000 { ++ reg = <0 0xfe108000 0 0x8000>; ++ no-map; ++ }; ++ ++ rsc_table_ca55: rsc-table@fe1f0000 { ++ reg = <0 0xfe1f0000 0 0x1000>; ++ no-map; ++ }; ++ ++ vdev0buffer_ca55: vdev0buffer@fe200000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xfe200000 0 0x100000>; ++ no-map; ++ }; ++ }; ++}; ++ ++&ca55_1 { ++ memory-region = <&vdev0buffer_ca55>, <&vdev0vring0_ca55>, <&vdev0vring1_ca55>, <&rsc_table_ca55>, <&rtos_ca55_reserved>; ++ dma-coherent; ++ 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 */ ++ fsl,startup-delay-ms = <50>; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts +new file mode 100644 +index 000000000000..7bed244c703c +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx93-14x14-evk.dts" ++#include "imx93-rproc-ca55.dtsi" ++ ++/ { ++ 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-14x14-evk-sja1105-avb.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105-avb.dts +new file mode 100644 +index 000000000000..693684a10a23 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105-avb.dts +@@ -0,0 +1,8 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx93-14x14-evk-sja1105.dts" ++#include "imx93-14x14-evk-mqs.dtsi" ++#include "imx93-evk-avb.dtsi" +diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105.dts +index 24bd885cd2fd..9da91fe35d74 100644 +--- a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105.dts ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105.dts +@@ -29,7 +29,7 @@ &eqos { + phy-mode = "rgmii-rxid"; + phy-handle = <&fixed0>; + pinctrl-assert-gpios = <&max7322 0 GPIO_ACTIVE_LOW>; +- snps,clk-csr = <6>; ++ snps,clk-csr = <5>; + status = "okay"; + + fixed0: fixed-link { +@@ -90,6 +90,7 @@ sja1105@0 { + spi-cpha; + clocks = <&clock_sja1105>; + pinctrl-assert-gpios = <&max7322 0 GPIO_ACTIVE_LOW>; ++ hostprio = <5>; + + ports { + #address-cells = <1>; +diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-tja1103.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-tja1103.dts +index 7e049848c4c7..91bb3eec6fe2 100644 +--- a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-tja1103.dts ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-tja1103.dts +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: (GPL-2.0+ OR MIT) + /* +- * Copyright 2023 NXP ++ * Copyright 2023-2024 NXP + */ + + #include "imx93-14x14-evk.dts" +@@ -10,13 +10,17 @@ &eqos { + pinctrl-0 = <&pinctrl_eqos_rmii>; + phy-mode = "rmii"; + phy-handle = <&eqosphy>; ++ snps,force_thresh_dma_mode; ++ snps,mtl-tx-config = <&mtl_tx_setup>; ++ snps,mtl-rx-config = <&mtl_rx_setup>; + + assigned-clocks = <&clk IMX93_CLK_ENET_TIMER2>, + <&clk IMX93_CLK_ENET>; + assigned-clock-parents = <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>, + <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>; + assigned-clock-rates = <100000000>, <50000000>; +- snps,clk-csr = <6>; ++ ++ snps,clk-csr = <5>; + status = "okay"; + + mdio { +@@ -32,6 +36,61 @@ eqosphy: ethernet-phy@2 { + reg = <2>; + }; + }; ++ ++ 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>; ++ }; ++ }; + }; + + &iomuxc { +diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-uart-sharing-cm33.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-uart-sharing-cm33.dts +new file mode 100644 +index 000000000000..7ff128fcf2b9 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-uart-sharing-cm33.dts +@@ -0,0 +1,96 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++ ++#include ++#include "imx93-14x14-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-14x14-evk-virtio-net-ca55.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-ca55.dts +new file mode 100644 +index 000000000000..2dca2b07a972 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-ca55.dts +@@ -0,0 +1,56 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx93-14x14-evk.dts" ++#include "imx93-generic-mbox.dtsi" ++#include "imx93-rproc-ca55.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ virtio_reserved: virtio@fc700000 { ++ no-map; ++ reg = <0 0xfc700000 0x0 0x00100000>; ++ }; ++ ++ /* 512MB */ ++ rtos_ca55_reserved: rtos-ca55@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-14x14-evk-virtio-net-cm33.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-cm33.dts +new file mode 100644 +index 000000000000..8d699f7c8389 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-cm33.dts +@@ -0,0 +1,58 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx93-14x14-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"; ++}; ++ ++&cm33 { ++ memory-region = <&cm33_reserved>; ++ status = "okay"; ++}; +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 7798271da63b..9534747b6a09 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 { +@@ -241,6 +244,61 @@ ethphy1: ethernet-phy@1 { + realtek,clkout-disable; + }; + }; ++ ++ 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-evk-avb.dtsi b/arch/arm64/boot/dts/freescale/imx93-evk-avb.dtsi +new file mode 100644 +index 000000000000..76bcb8a7979e +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-evk-avb.dtsi +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/* 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 = <49152000>; ++ ++ /* 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-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/boot/dts/freescale/imx93-rpmsg-ca55.dtsi b/arch/arm64/boot/dts/freescale/imx93-rpmsg-ca55.dtsi +new file mode 100644 +index 000000000000..5db78b628c44 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-rpmsg-ca55.dtsi +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include "imx93-generic-mbox.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ rpmsg_ca55_reserved: rpmsg-ca55@fe100000 { ++ reg = <0 0xfe100000 0 0x10000>; ++ no-map; ++ }; ++ ++ vdevbuffer_ca55: vdevbuffer-ca55@fe200000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xfe200000 0 0x100000>; ++ no-map; ++ }; ++ }; ++ ++ rpmsg-ca55 { ++ compatible = "fsl,imx8mm-rpmsg"; ++ reg = <0x0 0xfe100000 0x0 0x10000>; /* 64K for one rpmsg instance */ ++ dma-coherent; ++ memory-region = <&vdevbuffer_ca55>; ++ 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/imx93-rproc-ca55.dtsi b/arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi +new file mode 100644 +index 000000000000..4cc9342cf9a4 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi +@@ -0,0 +1,14 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/ { ++ ca55_1: remoteproc-ca55-1 { ++ compatible = "fsl,imx-rproc-psci"; ++ /* bitmask:0b10, assign A55 Core 1 */ ++ fsl,cpus-bits = <0x2>; ++ memory-region = <&rtos_ca55_reserved>; ++ }; ++}; ++ +diff --git a/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon-industrial.dts +new file mode 100644 +index 000000000000..e3a6ce1bbff4 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon-industrial.dts +@@ -0,0 +1,25 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2025 NXP ++ */ ++ ++#include "imx95-15x15-evk-root.dts" ++#include "imx95-rpmsg-ca55.dtsi" ++ ++&lpuart3 { ++ /* To use this, needs dedicated SM to allow RTOS access lpuart3 and lpuart3 pinmux */ ++ status = "disabled"; ++}; ++ ++/* This Message Unit is used for communication between the RTOS cell and the SM */ ++&mu3 { ++ status = "disabled"; ++}; ++ ++&flexcan2 { ++ status = "disabled"; ++}; ++ ++®_can2_stby { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon.dts +new file mode 100644 +index 000000000000..8838cd44871c +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon.dts +@@ -0,0 +1,19 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx95-15x15-evk-root.dts" ++#include "imx95-rpmsg-ca55.dtsi" ++ ++&lpuart3 { ++ /* To use this, needs dedicated SM to allow RTOS access lpuart3 and lpuart3 pinmux */ ++ status = "disabled"; ++}; ++ ++/* This Message Unit is used for communication between the RTOS cell and the SM */ ++&mu3 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx95-15x15-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-multicore-rtos.dts +new file mode 100644 +index 000000000000..359a54275e9c +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-multicore-rtos.dts +@@ -0,0 +1,29 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2025 NXP ++ */ ++ ++/dts-v1/; ++#include "imx95-15x15-evk.dts" ++#include "imx95-rproc-ca55.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* ++ * Reserve up to 3 x 16MB for three possible FreeRTOS instances running on ++ * Cortex-A Cores when booting Linux. ++ */ ++ rtos_ca55_reserved: rtos-ca55@d0000000 { ++ no-map; ++ reg = <0 0xd0000000 0x0 0x3000000>; ++ }; ++ }; ++}; ++ ++&lpuart3 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon-industrial.dts +new file mode 100644 +index 000000000000..59fde8aa0a65 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon-industrial.dts +@@ -0,0 +1,29 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx95-19x19-evk-root.dts" ++#include "imx95-rpmsg-ca55.dtsi" ++ ++&lpuart3 { ++ /* To use this, needs dedicated SM to allow RTOS access lpuart3 and lpuart3 pinmux */ ++ status = "disabled"; ++}; ++ ++/* This Message Unit is used for communication between the RTOS cell and the SM */ ++&mu3 { ++ status = "disabled"; ++}; ++ ++&flexcan2 { ++ status = "disabled"; ++}; ++ ++®_can2_en { ++ status = "disabled"; ++}; ++ ++®_can2_stby { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon.dts +new file mode 100644 +index 000000000000..8f0e6f8c38e1 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon.dts +@@ -0,0 +1,19 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++ ++#include "imx95-19x19-evk-root.dts" ++#include "imx95-rpmsg-ca55.dtsi" ++ ++&lpuart3 { ++ /* To use this, needs dedicated SM to allow RTOS access lpuart3 and lpuart3 pinmux */ ++ status = "disabled"; ++}; ++ ++/* This Message Unit is used for communication between the RTOS cell and the SM */ ++&mu3 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-multicore-rtos.dts +new file mode 100644 +index 000000000000..b28f9d22dbc2 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-multicore-rtos.dts +@@ -0,0 +1,29 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/dts-v1/; ++#include "imx95-19x19-evk.dts" ++#include "imx95-rproc-ca55.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* ++ * Reserve up to 3 x 16MB for three possible FreeRTOS instances running on ++ * Cortex-A Cores when booting Linux. ++ */ ++ rtos_ca55_reserved: rtos-ca55@d0000000 { ++ no-map; ++ reg = <0 0xd0000000 0x0 0x3000000>; ++ }; ++ }; ++}; ++ ++&lpuart3 { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx95-generic-mbox.dtsi b/arch/arm64/boot/dts/freescale/imx95-generic-mbox.dtsi +new file mode 100644 +index 000000000000..81fd2a34faaf +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx95-generic-mbox.dtsi +@@ -0,0 +1,27 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ gen_sw_mbox_reserved: gen-sw-mbox@c0000000 { ++ reg = <0 0xc0000000 0 0x1000>; ++ no-map; ++ }; ++ ++ }; ++ ++ gen_sw_mbox: generic-software-mailbox@c0000000 { ++ compatible = "fsl,generic-software-mbox"; ++ reg = <0 0xc0000000 0 0x1000>; ++ #mbox-cells = <3>; ++ /* Use 2 unused SPI interrupts */ ++ interrupts = , ; ++ interrupt-names = "irq", "remote_irq"; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/freescale/imx95-rpmsg-ca55.dtsi b/arch/arm64/boot/dts/freescale/imx95-rpmsg-ca55.dtsi +new file mode 100644 +index 000000000000..fc72fab7b791 +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx95-rpmsg-ca55.dtsi +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#include "imx95-generic-mbox.dtsi" ++ ++/ { ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ rpmsg_ca55_reserved: rpmsg-ca55@c0100000 { ++ reg = <0 0xc0100000 0 0x10000>; ++ no-map; ++ }; ++ ++ vdevbuffer_ca55: vdevbuffer-ca55@c0200000 { ++ compatible = "shared-dma-pool"; ++ reg = <0 0xc0200000 0 0x100000>; ++ no-map; ++ }; ++ }; ++ ++ rpmsg-ca55 { ++ compatible = "fsl,imx8mm-rpmsg"; ++ reg = <0x0 0xc0100000 0x0 0x10000>; /* 64K for one rpmsg instance */ ++ dma-coherent; ++ memory-region = <&vdevbuffer_ca55>; ++ 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/imx95-rproc-ca55.dtsi b/arch/arm64/boot/dts/freescale/imx95-rproc-ca55.dtsi +new file mode 100644 +index 000000000000..94c5d9db84ad +--- /dev/null ++++ b/arch/arm64/boot/dts/freescale/imx95-rproc-ca55.dtsi +@@ -0,0 +1,48 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Copyright 2024 NXP ++ */ ++ ++/ { ++ ca55_1: remoteproc-ca55-1 { ++ compatible = "fsl,imx-rproc-psci"; ++ /* bitmask:0b000010, assign A55 Core 1 */ ++ fsl,cpus-bits = <0x2>; ++ memory-region = <&rtos_ca55_reserved>; ++ }; ++ ++ ca55_2: remoteproc-ca55-2 { ++ compatible = "fsl,imx-rproc-psci"; ++ /* bitmask:0b000100, assign A55 Core 2 */ ++ fsl,cpus-bits = <0x4>; ++ memory-region = <&rtos_ca55_reserved>; ++ }; ++ ++ ca55_3: remoteproc-ca55-3 { ++ compatible = "fsl,imx-rproc-psci"; ++ /* bitmask:0b001000, assign A55 Core 3 */ ++ fsl,cpus-bits = <0x8>; ++ memory-region = <&rtos_ca55_reserved>; ++ }; ++ ++ ca55_4: remoteproc-ca55-4 { ++ compatible = "fsl,imx-rproc-psci"; ++ /* bitmask:0b010000, assign A55 Core 4 */ ++ fsl,cpus-bits = <0x10>; ++ memory-region = <&rtos_ca55_reserved>; ++ }; ++ ++ ca55_5: remoteproc-ca55-5 { ++ compatible = "fsl,imx-rproc-psci"; ++ /* bitmask:0b100000, assign A55 Core 5 */ ++ fsl,cpus-bits = <0x20>; ++ memory-region = <&rtos_ca55_reserved>; ++ }; ++ ++ ca55_4_5: remoteproc-ca55-4-5 { ++ compatible = "fsl,imx-rproc-psci"; ++ /* bitmask:0b110000, assign A55 Core 4 and 5 */ ++ fsl,cpus-bits = <0x30>; ++ memory-region = <&rtos_ca55_reserved>; ++ }; ++}; +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 f076bf99e0cd..f083f63a6ac9 100644 +--- a/arch/arm64/configs/imx_v8_defconfig ++++ b/arch/arm64/configs/imx_v8_defconfig +@@ -6,6 +6,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 +@@ -137,6 +139,10 @@ 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 +@@ -152,17 +158,22 @@ CONFIG_NET_CLS_U32=y + CONFIG_NET_CLS_FLOWER=y + CONFIG_NET_CLS_ACT=y + CONFIG_NET_ACT_GACT=m +-CONFIG_NET_ACT_MIRRED=m ++CONFIG_NET_ACT_MIRRED=y + CONFIG_NET_ACT_SKBEDIT=y ++CONFIG_NET_ACT_FRER=y + CONFIG_NET_ACT_GATE=y + CONFIG_NET_ACT_POLICE=y ++CONFIG_NET_EMATCH=y ++CONFIG_NET_EMATCH_STACK=32 ++CONFIG_NET_EMATCH_U32=y ++CONFIG_NET_EMATCH_META=y + CONFIG_TSN=y + CONFIG_VSOCKETS=y + CONFIG_VIRTIO_VSOCKETS=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 +@@ -292,8 +303,10 @@ CONFIG_MACVLAN=m + CONFIG_MACVTAP=m + CONFIG_TUN=y + CONFIG_VETH=m +-CONFIG_VIRTIO_NET=y ++CONFIG_VIRTIO_NET=m + CONFIG_NET_DSA_MSCC_FELIX=m ++CONFIG_NET_DSA_NETC=m ++CONFIG_NET_DSA_NETC_PTP=y + CONFIG_NET_DSA_SJA1105=m + CONFIG_NET_DSA_SJA1105_PTP=y + CONFIG_NET_DSA_SJA1105_TAS=y +@@ -306,6 +319,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 +@@ -931,7 +945,7 @@ CONFIG_VFIO_FSL_MC=y + CONFIG_VIRTIO_PCI=y + CONFIG_VIRTIO_BALLOON=y + CONFIG_VIRTIO_INPUT=y +-CONFIG_VIRTIO_MMIO=y ++CONFIG_VIRTIO_MMIO=m + CONFIG_VIRTIO_IVSHMEM=y + CONFIG_VHOST_VSOCK=y + CONFIG_XEN_GNTDEV=y +@@ -1205,3 +1219,11 @@ CONFIG_MEMTEST=y + CONFIG_VHOST_NET=m + CONFIG_IMX_ISPMU=m + CONFIG_IMX_ISP_REMOTEPROC=m ++ ++# 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=m +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 d97ef5280ed2..f17f9aef7868 100644 +--- a/arch/arm64/configs/lsdk.config ++++ b/arch/arm64/configs/lsdk.config +@@ -84,6 +84,10 @@ 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_10G=y +@@ -92,6 +96,15 @@ CONFIG_MTIP_BACKPLANE_PHY=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 +@@ -99,7 +112,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 +@@ -183,6 +195,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 +@@ -194,10 +207,18 @@ 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 + CONFIG_FSL_IFC=y + CONFIG_MTD_NAND_FSL_IFC=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/kernel/smp.c b/arch/arm64/kernel/smp.c +index be87f2f7f77c..1ad60cee856f 100644 +--- a/arch/arm64/kernel/smp.c ++++ b/arch/arm64/kernel/smp.c +@@ -53,6 +53,10 @@ + + #include + ++#ifdef CONFIG_BAREMETAL ++#include ++#endif ++ + DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number); + EXPORT_PER_CPU_SYMBOL(cpu_number); + +@@ -73,6 +77,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 + }; + +@@ -767,6 +778,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); +@@ -907,6 +921,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; +diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig +index 61fb49dca45b..3c61d0541c99 100644 +--- a/arch/powerpc/Kconfig ++++ b/arch/powerpc/Kconfig +@@ -166,6 +166,7 @@ config PPC + select ARCH_STACKWALK + select ARCH_SUPPORTS_ATOMIC_RMW + select ARCH_SUPPORTS_DEBUG_PAGEALLOC if PPC_BOOK3S || PPC_8xx || 40x ++ select ARCH_SUPPORTS_RT if HAVE_POSIX_CPU_TIMERS_TASK_WORK + select ARCH_USE_BUILTIN_BSWAP + select ARCH_USE_CMPXCHG_LOCKREF if PPC64 + select ARCH_USE_MEMTEST +@@ -268,6 +269,7 @@ config PPC + select HAVE_PERF_USER_STACK_DUMP + select HAVE_REGS_AND_STACK_ACCESS_API + select HAVE_RELIABLE_STACKTRACE ++ select HAVE_POSIX_CPU_TIMERS_TASK_WORK if !KVM + select HAVE_RSEQ + select HAVE_SETUP_PER_CPU_AREA if PPC64 + select HAVE_SOFTIRQ_ON_OWN_STACK +diff --git a/arch/powerpc/include/asm/stackprotector.h b/arch/powerpc/include/asm/stackprotector.h +index 283c34647856..4727f40052dd 100644 +--- a/arch/powerpc/include/asm/stackprotector.h ++++ b/arch/powerpc/include/asm/stackprotector.h +@@ -19,8 +19,13 @@ + */ + static __always_inline void boot_init_stack_canary(void) + { +- unsigned long canary = get_random_canary(); ++ unsigned long canary; + ++#ifndef CONFIG_PREEMPT_RT ++ canary = get_random_canary(); ++#else ++ canary = ((unsigned long)&canary) & CANARY_MASK; ++#endif + current->stack_canary = canary; + #ifdef CONFIG_PPC64 + get_paca()->canary = canary; +diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c +index 2de7f6dcd32b..739f5b179a7f 100644 +--- a/arch/powerpc/kernel/traps.c ++++ b/arch/powerpc/kernel/traps.c +@@ -261,12 +261,17 @@ static char *get_mmu_str(void) + + static int __die(const char *str, struct pt_regs *regs, long err) + { ++ const char *pr = ""; ++ + printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter); + ++ if (IS_ENABLED(CONFIG_PREEMPTION)) ++ pr = IS_ENABLED(CONFIG_PREEMPT_RT) ? " PREEMPT_RT" : " PREEMPT"; ++ + printk("%s PAGE_SIZE=%luK%s%s%s%s%s%s %s\n", + IS_ENABLED(CONFIG_CPU_LITTLE_ENDIAN) ? "LE" : "BE", + PAGE_SIZE / 1024, get_mmu_str(), +- IS_ENABLED(CONFIG_PREEMPT) ? " PREEMPT" : "", ++ pr, + IS_ENABLED(CONFIG_SMP) ? " SMP" : "", + IS_ENABLED(CONFIG_SMP) ? (" NR_CPUS=" __stringify(NR_CPUS)) : "", + debug_pagealloc_enabled() ? " DEBUG_PAGEALLOC" : "", +diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig +index 902611954200..2f188137f830 100644 +--- a/arch/powerpc/kvm/Kconfig ++++ b/arch/powerpc/kvm/Kconfig +@@ -224,6 +224,7 @@ config KVM_E500MC + config KVM_MPIC + bool "KVM in-kernel MPIC emulation" + depends on KVM && PPC_E500 ++ depends on !PREEMPT_RT + select HAVE_KVM_IRQCHIP + select HAVE_KVM_IRQFD + select HAVE_KVM_IRQ_ROUTING +diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig +index 4ebf2ef2845d..381c3be3bec1 100644 +--- a/arch/powerpc/platforms/pseries/Kconfig ++++ b/arch/powerpc/platforms/pseries/Kconfig +@@ -2,6 +2,7 @@ + config PPC_PSERIES + depends on PPC64 && PPC_BOOK3S + bool "IBM pSeries & new (POWER5-based) iSeries" ++ select GENERIC_ALLOCATOR + select HAVE_PCSPKR_PLATFORM + select MPIC + select OF_DYNAMIC +diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c +index b1e6d275cda9..9a8d3970da3c 100644 +--- a/arch/powerpc/platforms/pseries/iommu.c ++++ b/arch/powerpc/platforms/pseries/iommu.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -206,7 +207,13 @@ static int tce_build_pSeriesLP(unsigned long liobn, long tcenum, long tceshift, + return ret; + } + +-static DEFINE_PER_CPU(__be64 *, tce_page); ++struct tce_page { ++ __be64 * page; ++ local_lock_t lock; ++}; ++static DEFINE_PER_CPU(struct tce_page, tce_page) = { ++ .lock = INIT_LOCAL_LOCK(lock), ++}; + + static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, + long npages, unsigned long uaddr, +@@ -229,9 +236,10 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, + direction, attrs); + } + +- local_irq_save(flags); /* to protect tcep and the page behind it */ ++ /* to protect tcep and the page behind it */ ++ local_lock_irqsave(&tce_page.lock, flags); + +- tcep = __this_cpu_read(tce_page); ++ tcep = __this_cpu_read(tce_page.page); + + /* This is safe to do since interrupts are off when we're called + * from iommu_alloc{,_sg}() +@@ -240,12 +248,12 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, + tcep = (__be64 *)__get_free_page(GFP_ATOMIC); + /* If allocation fails, fall back to the loop implementation */ + if (!tcep) { +- local_irq_restore(flags); ++ local_unlock_irqrestore(&tce_page.lock, flags); + return tce_build_pSeriesLP(tbl->it_index, tcenum, + tceshift, + npages, uaddr, direction, attrs); + } +- __this_cpu_write(tce_page, tcep); ++ __this_cpu_write(tce_page.page, tcep); + } + + rpn = __pa(uaddr) >> tceshift; +@@ -275,7 +283,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, + tcenum += limit; + } while (npages > 0 && !rc); + +- local_irq_restore(flags); ++ local_unlock_irqrestore(&tce_page.lock, flags); + + if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) { + ret = (int)rc; +@@ -459,16 +467,17 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn, + DMA_BIDIRECTIONAL, 0); + } + +- local_irq_disable(); /* to protect tcep and the page behind it */ +- tcep = __this_cpu_read(tce_page); ++ /* to protect tcep and the page behind it */ ++ local_lock_irq(&tce_page.lock); ++ tcep = __this_cpu_read(tce_page.page); + + if (!tcep) { + tcep = (__be64 *)__get_free_page(GFP_ATOMIC); + if (!tcep) { +- local_irq_enable(); ++ local_unlock_irq(&tce_page.lock); + return -ENOMEM; + } +- __this_cpu_write(tce_page, tcep); ++ __this_cpu_write(tce_page.page, tcep); + } + + proto_tce = TCE_PCI_READ | TCE_PCI_WRITE; +@@ -511,7 +520,7 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn, + + /* error cleanup: caller will clear whole range */ + +- local_irq_enable(); ++ local_unlock_irq(&tce_page.lock); + return rc; + } + +diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig +index fe30b24d52e1..2bdf6a7d0f8c 100644 +--- a/arch/riscv/Kconfig ++++ b/arch/riscv/Kconfig +@@ -49,6 +49,7 @@ config RISCV + select ARCH_SUPPORTS_HUGETLBFS if MMU + select ARCH_SUPPORTS_PAGE_TABLE_CHECK if MMU + select ARCH_SUPPORTS_PER_VMA_LOCK if MMU ++ select ARCH_SUPPORTS_RT + select ARCH_USE_MEMTEST + select ARCH_USE_QUEUED_RWLOCKS + select ARCH_USES_CFI_TRAPS if CFI_CLANG +@@ -136,6 +137,7 @@ config RISCV + select HAVE_PERF_USER_STACK_DUMP + select HAVE_POSIX_CPU_TIMERS_TASK_WORK + select HAVE_PREEMPT_DYNAMIC_KEY if !XIP_KERNEL ++ select HAVE_PREEMPT_AUTO + select HAVE_REGS_AND_STACK_ACCESS_API + select HAVE_RETHOOK if !XIP_KERNEL + select HAVE_RSEQ +diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h +index d0345bd659c9..23fed53b8815 100644 +--- a/arch/riscv/include/asm/cpufeature.h ++++ b/arch/riscv/include/asm/cpufeature.h +@@ -30,6 +30,4 @@ DECLARE_PER_CPU(long, misaligned_access_speed); + /* Per-cpu ISA extensions. */ + extern struct riscv_isainfo hart_isa[NR_CPUS]; + +-void check_unaligned_access(int cpu); +- + #endif +diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h +index d18ce0113ca1..e18710fe51f0 100644 +--- a/arch/riscv/include/asm/thread_info.h ++++ b/arch/riscv/include/asm/thread_info.h +@@ -82,6 +82,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); + * - pending work-to-be-done flags are in lowest half-word + * - other flags in upper half-word(s) + */ ++#define TIF_ARCH_RESCHED_LAZY 0 /* Lazy rescheduling */ + #define TIF_NOTIFY_RESUME 1 /* callback before returning to user */ + #define TIF_SIGPENDING 2 /* signal pending */ + #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +@@ -96,6 +97,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); + #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) + #define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) + #define _TIF_UPROBE (1 << TIF_UPROBE) ++#define _TIF_ARCH_RESCHED_LAZY (1 << TIF_ARCH_RESCHED_LAZY) + + #define _TIF_WORK_MASK \ + (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED | \ +diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c +index e39a905aca24..dd118773e717 100644 +--- a/arch/riscv/kernel/cpufeature.c ++++ b/arch/riscv/kernel/cpufeature.c +@@ -8,6 +8,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -30,6 +31,7 @@ + + #define MISALIGNED_ACCESS_JIFFIES_LG2 1 + #define MISALIGNED_BUFFER_SIZE 0x4000 ++#define MISALIGNED_BUFFER_ORDER get_order(MISALIGNED_BUFFER_SIZE) + #define MISALIGNED_COPY_SIZE ((MISALIGNED_BUFFER_SIZE / 2) - 0x80) + + unsigned long elf_hwcap __read_mostly; +@@ -571,14 +573,15 @@ unsigned long riscv_get_elf_hwcap(void) + return hwcap; + } + +-void check_unaligned_access(int cpu) ++static int check_unaligned_access(void *param) + { ++ int cpu = smp_processor_id(); + u64 start_cycles, end_cycles; + u64 word_cycles; + u64 byte_cycles; + int ratio; + unsigned long start_jiffies, now; +- struct page *page; ++ struct page *page = param; + void *dst; + void *src; + long speed = RISCV_HWPROBE_MISALIGNED_SLOW; +@@ -587,12 +590,6 @@ void check_unaligned_access(int cpu) + if (per_cpu(misaligned_access_speed, cpu) != RISCV_HWPROBE_MISALIGNED_UNKNOWN) + return; + +- page = alloc_pages(GFP_NOWAIT, get_order(MISALIGNED_BUFFER_SIZE)); +- if (!page) { +- pr_warn("Can't alloc pages to measure memcpy performance"); +- return; +- } +- + /* Make an unaligned destination buffer. */ + dst = (void *)((unsigned long)page_address(page) | 0x1); + /* Unalign src as well, but differently (off by 1 + 2 = 3). */ +@@ -645,7 +642,7 @@ void check_unaligned_access(int cpu) + pr_warn("cpu%d: rdtime lacks granularity needed to measure unaligned access speed\n", + cpu); + +- goto out; ++ return 0; + } + + if (word_cycles < byte_cycles) +@@ -659,18 +656,83 @@ void check_unaligned_access(int cpu) + (speed == RISCV_HWPROBE_MISALIGNED_FAST) ? "fast" : "slow"); + + per_cpu(misaligned_access_speed, cpu) = speed; ++ return 0; ++} + +-out: +- __free_pages(page, get_order(MISALIGNED_BUFFER_SIZE)); ++static void check_unaligned_access_nonboot_cpu(void *param) ++{ ++ unsigned int cpu = smp_processor_id(); ++ struct page **pages = param; ++ ++ if (smp_processor_id() != 0) ++ check_unaligned_access(pages[cpu]); + } + +-static int check_unaligned_access_boot_cpu(void) ++static int riscv_online_cpu(unsigned int cpu) + { +- check_unaligned_access(0); ++ static struct page *buf; ++ ++ /* We are already set since the last check */ ++ if (per_cpu(misaligned_access_speed, cpu) != RISCV_HWPROBE_MISALIGNED_UNKNOWN) ++ return 0; ++ ++ buf = alloc_pages(GFP_KERNEL, MISALIGNED_BUFFER_ORDER); ++ if (!buf) { ++ pr_warn("Allocation failure, not measuring misaligned performance\n"); ++ return -ENOMEM; ++ } ++ ++ check_unaligned_access(buf); ++ __free_pages(buf, MISALIGNED_BUFFER_ORDER); ++ return 0; ++} ++ ++/* Measure unaligned access on all CPUs present at boot in parallel. */ ++static int check_unaligned_access_all_cpus(void) ++{ ++ unsigned int cpu; ++ unsigned int cpu_count = num_possible_cpus(); ++ struct page **bufs = kzalloc(cpu_count * sizeof(struct page *), ++ GFP_KERNEL); ++ ++ if (!bufs) { ++ pr_warn("Allocation failure, not measuring misaligned performance\n"); ++ return 0; ++ } ++ ++ /* ++ * Allocate separate buffers for each CPU so there's no fighting over ++ * cache lines. ++ */ ++ for_each_cpu(cpu, cpu_online_mask) { ++ bufs[cpu] = alloc_pages(GFP_KERNEL, MISALIGNED_BUFFER_ORDER); ++ if (!bufs[cpu]) { ++ pr_warn("Allocation failure, not measuring misaligned performance\n"); ++ goto out; ++ } ++ } ++ ++ /* Check everybody except 0, who stays behind to tend jiffies. */ ++ on_each_cpu(check_unaligned_access_nonboot_cpu, bufs, 1); ++ ++ /* Check core 0. */ ++ smp_call_on_cpu(0, check_unaligned_access, bufs[0], true); ++ ++ /* Setup hotplug callback for any new CPUs that come online. */ ++ cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "riscv:online", ++ riscv_online_cpu, NULL); ++ ++out: ++ for_each_cpu(cpu, cpu_online_mask) { ++ if (bufs[cpu]) ++ __free_pages(bufs[cpu], MISALIGNED_BUFFER_ORDER); ++ } ++ ++ kfree(bufs); + return 0; + } + +-arch_initcall(check_unaligned_access_boot_cpu); ++arch_initcall(check_unaligned_access_all_cpus); + + #ifdef CONFIG_RISCV_ALTERNATIVE + /* +diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c +index 1b8da4e40a4d..2cb5e651412c 100644 +--- a/arch/riscv/kernel/smpboot.c ++++ b/arch/riscv/kernel/smpboot.c +@@ -246,7 +246,6 @@ asmlinkage __visible void smp_callin(void) + + numa_add_cpu(curr_cpuid); + set_cpu_online(curr_cpuid, 1); +- check_unaligned_access(curr_cpuid); + + if (has_vector()) { + if (riscv_v_setup_vsize()) +diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig +index 82d12c93feab..7bbf835fe725 100644 +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -118,6 +118,7 @@ config X86 + select ARCH_USES_CFI_TRAPS if X86_64 && CFI_CLANG + select ARCH_SUPPORTS_LTO_CLANG + select ARCH_SUPPORTS_LTO_CLANG_THIN ++ select ARCH_SUPPORTS_RT + select ARCH_USE_BUILTIN_BSWAP + select ARCH_USE_MEMTEST + select ARCH_USE_QUEUED_RWLOCKS +@@ -272,6 +273,7 @@ config X86 + select HAVE_STATIC_CALL + select HAVE_STATIC_CALL_INLINE if HAVE_OBJTOOL + select HAVE_PREEMPT_DYNAMIC_CALL ++ select HAVE_PREEMPT_AUTO + select HAVE_RSEQ + select HAVE_RUST if X86_64 + select HAVE_SYSCALL_TRACEPOINTS +diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h +index d63b02940747..1ff38ebbd588 100644 +--- a/arch/x86/include/asm/thread_info.h ++++ b/arch/x86/include/asm/thread_info.h +@@ -81,8 +81,9 @@ struct thread_info { + #define TIF_NOTIFY_RESUME 1 /* callback before returning to user */ + #define TIF_SIGPENDING 2 /* signal pending */ + #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +-#define TIF_SINGLESTEP 4 /* reenable singlestep on user return*/ +-#define TIF_SSBD 5 /* Speculative store bypass disable */ ++#define TIF_ARCH_RESCHED_LAZY 4 /* Lazy rescheduling */ ++#define TIF_SINGLESTEP 5 /* reenable singlestep on user return*/ ++#define TIF_SSBD 6 /* Speculative store bypass disable */ + #define TIF_SPEC_IB 9 /* Indirect branch speculation mitigation */ + #define TIF_SPEC_L1D_FLUSH 10 /* Flush L1D on mm switches (processes) */ + #define TIF_USER_RETURN_NOTIFY 11 /* notify kernel of userspace return */ +@@ -104,6 +105,7 @@ struct thread_info { + #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) + #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) + #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) ++#define _TIF_ARCH_RESCHED_LAZY (1 << TIF_ARCH_RESCHED_LAZY) + #define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP) + #define _TIF_SSBD (1 << TIF_SSBD) + #define _TIF_SPEC_IB (1 << TIF_SPEC_IB) +diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c +index 831fa4a12159..5af3ebec0f74 100644 +--- a/drivers/acpi/processor_idle.c ++++ b/drivers/acpi/processor_idle.c +@@ -107,7 +107,7 @@ static const struct dmi_system_id processor_power_dmi_table[] = { + */ + static void __cpuidle acpi_safe_halt(void) + { +- if (!tif_need_resched()) { ++ if (!need_resched()) { + raw_safe_halt(); + raw_local_irq_disable(); + } +diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig +index 2b8fd6bb7da0..0125f92bb805 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 06673c6ca255..a5d0f7c06342 100644 +--- a/drivers/block/zram/zram_drv.c ++++ b/drivers/block/zram/zram_drv.c +@@ -57,6 +57,41 @@ static void zram_free_page(struct zram *zram, size_t index); + static int zram_read_page(struct zram *zram, struct page *page, u32 index, + struct bio *parent); + ++#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) + { + return bit_spin_trylock(ZRAM_LOCK, &zram->table[index].flags); +@@ -71,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) + { +@@ -1245,6 +1281,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 ca7a15bd4845..e64eb607eb45 100644 +--- a/drivers/block/zram/zram_drv.h ++++ b/drivers/block/zram/zram_drv.h +@@ -69,6 +69,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/clk/imx/Makefile b/drivers/clk/imx/Makefile +index 9e62d3dd2e34..aab46cfd41cd 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 e8fa9170c68f..07a23f3fff19 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) +@@ -63,10 +64,20 @@ + .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; + u32 flags; + }; +@@ -87,7 +98,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) + }; + +@@ -150,18 +161,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); +@@ -172,22 +184,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; + +@@ -208,13 +204,50 @@ static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned lon + do_div(fvco, rdiv * odiv); + } else { + /* Fvco = (Fref / rdiv) * (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; +@@ -232,6 +265,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); +@@ -350,6 +388,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, ++}; ++ + static 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, +@@ -385,6 +539,13 @@ static struct clk_hw *_imx_clk_fracn_gppll(const char *name, const char *parent_ + return ERR_PTR(ret); + } + ++ if (pll_flags & CLK_FRACN_GPPLL_FRACN) { ++ 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; + } + +diff --git a/drivers/clk/imx/clk-imx8-acm.c b/drivers/clk/imx/clk-imx8-acm.c +index 1c95ae905eec..e8ad426cd844 100644 +--- a/drivers/clk/imx/clk-imx8-acm.c ++++ b/drivers/clk/imx/clk-imx8-acm.c +@@ -235,11 +235,20 @@ static const struct clk_parent_data imx8dxl_mclk_sels[] = { + { .fw_name = "acm_aud_clk1_sel" }, + }; + ++static const struct clk_parent_data imx8dxl_gpt_mux_clk_sels[] = { ++ { .fw_name = "aud_pll_div_clk0_lpcg_clk", .name = "aud_pll_div_clk0_lpcg_clk" }, ++ { .fw_name = "aud_pll_div_clk1_lpcg_clk", .name = "aud_pll_div_clk1_lpcg_clk" }, ++ { .fw_name = "acm_aud_clk0_sel", .name = "acm_aud_clk0_sel" }, ++ { .fw_name = "acm_aud_clk1_sel", .name = "acm_aud_clk1_sel" }, ++ { .index = -1 }, ++}; ++ + static struct clk_imx8_acm_sel imx8dxl_sels[] = { + { "acm_aud_clk0_sel", IMX_ADMA_ACM_AUD_CLK0_SEL, imx8dxl_aud_clk_sels, ARRAY_SIZE(imx8dxl_aud_clk_sels), 0x000000, 0, 5 }, + { "acm_aud_clk1_sel", IMX_ADMA_ACM_AUD_CLK1_SEL, imx8dxl_aud_clk_sels, ARRAY_SIZE(imx8dxl_aud_clk_sels), 0x010000, 0, 5 }, + { "acm_mclkout0_sel", IMX_ADMA_ACM_MCLKOUT0_SEL, imx8dxl_mclk_out_sels, ARRAY_SIZE(imx8dxl_mclk_out_sels), 0x020000, 0, 3 }, + { "acm_mclkout1_sel", IMX_ADMA_ACM_MCLKOUT1_SEL, imx8dxl_mclk_out_sels, ARRAY_SIZE(imx8dxl_mclk_out_sels), 0x030000, 0, 3 }, ++ { "acm_gpt0_mux_clk_sel", IMX_ADMA_ACM_GPT0_MUX_CLK_SEL, imx8dxl_gpt_mux_clk_sels, ARRAY_SIZE(imx8dxl_gpt_mux_clk_sels), 0x080000, 0, 3 }, + { "acm_sai0_mclk_sel", IMX_ADMA_ACM_SAI0_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x0E0000, 0, 2 }, + { "acm_sai1_mclk_sel", IMX_ADMA_ACM_SAI1_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x0F0000, 0, 2 }, + { "acm_sai2_mclk_sel", IMX_ADMA_ACM_SAI2_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x100000, 0, 2 }, +@@ -326,6 +335,41 @@ static int clk_imx_acm_detach_pm_domains(struct device *dev, + return 0; + } + ++/* The number of cells for the GPT capture device tree attribute */ ++#define OF_GPT_CAPTURE_CELLS_NB 2 ++ ++static void clk_imx_acm_gpt_input_mux(struct device_node *np, struct imx8_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 imx8_acm_clk_probe(struct platform_device *pdev) + { + struct clk_hw_onecell_data *clk_hw_data; +@@ -386,6 +430,8 @@ static int imx8_acm_clk_probe(struct platform_device *pdev) + goto err_clk_register; + } + ++ clk_imx_acm_gpt_input_mux(dev->of_node, priv); ++ + pm_runtime_put_sync(&pdev->dev); + return 0; + +diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c +index 10a4bfb825e3..8432477370e4 100644 +--- a/drivers/clk/imx/clk-imx8mq.c ++++ b/drivers/clk/imx/clk-imx8mq.c +@@ -217,7 +217,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 6c17786ecb9f..e68030c4a24f 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; + }; + +@@ -536,6 +542,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) +@@ -590,6 +703,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 6f6d4589f7eb..b74b48f3fc90 100644 +--- a/drivers/clk/imx/clk-pllv3.c ++++ b/drivers/clk/imx/clk-pllv3.c +@@ -15,6 +15,7 @@ + #include + #include + #include "clk.h" ++#include "clk-pll.h" + + #define PLL_NUM_OFFSET 0x10 + #define PLL_DENOM_OFFSET 0x20 +@@ -48,6 +49,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; +@@ -441,6 +446,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) + { +@@ -496,6 +601,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; +@@ -533,6 +639,9 @@ 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; + } + EXPORT_SYMBOL_GPL(imx_clk_hw_pllv3); +diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c +index c790e82c24c8..d0765cf9c3bc 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; + }; + + /* +@@ -466,6 +469,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) +@@ -514,6 +593,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/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c +index 172aa10a8800..4ae4720535a5 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c +@@ -60,11 +60,9 @@ static DEFINE_PER_CPU(int, fpu_recursion_depth); + */ + inline void dc_assert_fp_enabled(void) + { +- int *pcpu, depth = 0; ++ int depth; + +- pcpu = get_cpu_ptr(&fpu_recursion_depth); +- depth = *pcpu; +- put_cpu_ptr(&fpu_recursion_depth); ++ depth = __this_cpu_read(fpu_recursion_depth); + + ASSERT(depth >= 1); + } +@@ -84,33 +82,28 @@ inline void dc_assert_fp_enabled(void) + */ + void dc_fpu_begin(const char *function_name, const int line) + { +- int *pcpu; ++ int depth; + +- pcpu = get_cpu_ptr(&fpu_recursion_depth); +- *pcpu += 1; ++ WARN_ON_ONCE(!in_task()); ++ preempt_disable(); ++ depth = __this_cpu_inc_return(fpu_recursion_depth); + +- if (*pcpu == 1) { ++ if (depth == 1) { + #if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH) +- migrate_disable(); + kernel_fpu_begin(); + #elif defined(CONFIG_PPC64) +- if (cpu_has_feature(CPU_FTR_VSX_COMP)) { +- preempt_disable(); ++ if (cpu_has_feature(CPU_FTR_VSX_COMP)) + enable_kernel_vsx(); +- } else if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP)) { +- preempt_disable(); ++ else if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP)) + enable_kernel_altivec(); +- } else if (!cpu_has_feature(CPU_FTR_FPU_UNAVAILABLE)) { +- preempt_disable(); ++ else if (!cpu_has_feature(CPU_FTR_FPU_UNAVAILABLE)) + enable_kernel_fp(); +- } + #elif defined(CONFIG_ARM64) + kernel_neon_begin(); + #endif + } + +- TRACE_DCN_FPU(true, function_name, line, *pcpu); +- put_cpu_ptr(&fpu_recursion_depth); ++ TRACE_DCN_FPU(true, function_name, line, depth); + } + + /** +@@ -125,30 +118,26 @@ void dc_fpu_begin(const char *function_name, const int line) + */ + void dc_fpu_end(const char *function_name, const int line) + { +- int *pcpu; ++ int depth; + +- pcpu = get_cpu_ptr(&fpu_recursion_depth); +- *pcpu -= 1; +- if (*pcpu <= 0) { ++ depth = __this_cpu_dec_return(fpu_recursion_depth); ++ if (depth == 0) { + #if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH) + kernel_fpu_end(); +- migrate_enable(); + #elif defined(CONFIG_PPC64) +- if (cpu_has_feature(CPU_FTR_VSX_COMP)) { ++ if (cpu_has_feature(CPU_FTR_VSX_COMP)) + disable_kernel_vsx(); +- preempt_enable(); +- } else if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP)) { ++ else if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP)) + disable_kernel_altivec(); +- preempt_enable(); +- } else if (!cpu_has_feature(CPU_FTR_FPU_UNAVAILABLE)) { ++ else if (!cpu_has_feature(CPU_FTR_FPU_UNAVAILABLE)) + disable_kernel_fp(); +- preempt_enable(); +- } + #elif defined(CONFIG_ARM64) + kernel_neon_end(); + #endif ++ } else { ++ WARN_ON_ONCE(depth < 0); + } + +- TRACE_DCN_FPU(false, function_name, line, *pcpu); +- put_cpu_ptr(&fpu_recursion_depth); ++ TRACE_DCN_FPU(false, function_name, line, depth); ++ preempt_enable(); + } +diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c +index d587f807dfd7..5036a3e60832 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c +@@ -2141,9 +2141,17 @@ bool dcn20_validate_bandwidth(struct dc *dc, struct dc_state *context, + bool fast_validate) + { + bool voltage_supported; ++ display_e2e_pipe_params_st *pipes; ++ ++ pipes = kcalloc(dc->res_pool->pipe_count, sizeof(display_e2e_pipe_params_st), GFP_KERNEL); ++ if (!pipes) ++ return false; ++ + DC_FP_START(); +- voltage_supported = dcn20_validate_bandwidth_fp(dc, context, fast_validate); ++ voltage_supported = dcn20_validate_bandwidth_fp(dc, context, fast_validate, pipes); + DC_FP_END(); ++ ++ kfree(pipes); + return voltage_supported; + } + +diff --git a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c +index d1a25fe6c44f..5674c3450fc3 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c +@@ -953,9 +953,17 @@ static bool dcn21_validate_bandwidth(struct dc *dc, struct dc_state *context, + bool fast_validate) + { + bool voltage_supported; ++ display_e2e_pipe_params_st *pipes; ++ ++ pipes = kcalloc(dc->res_pool->pipe_count, sizeof(display_e2e_pipe_params_st), GFP_KERNEL); ++ if (!pipes) ++ return false; ++ + DC_FP_START(); +- voltage_supported = dcn21_validate_bandwidth_fp(dc, context, fast_validate); ++ voltage_supported = dcn21_validate_bandwidth_fp(dc, context, fast_validate, pipes); + DC_FP_END(); ++ ++ kfree(pipes); + return voltage_supported; + } + +diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c +index 8a5a038fd855..68970d6cf031 100644 +--- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c ++++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c +@@ -2018,7 +2018,7 @@ void dcn20_patch_bounding_box(struct dc *dc, struct _vcs_dpi_soc_bounding_box_st + } + + static bool dcn20_validate_bandwidth_internal(struct dc *dc, struct dc_state *context, +- bool fast_validate) ++ bool fast_validate, display_e2e_pipe_params_st *pipes) + { + bool out = false; + +@@ -2027,7 +2027,6 @@ static bool dcn20_validate_bandwidth_internal(struct dc *dc, struct dc_state *co + int vlevel = 0; + int pipe_split_from[MAX_PIPES]; + int pipe_cnt = 0; +- display_e2e_pipe_params_st *pipes = kzalloc(dc->res_pool->pipe_count * sizeof(display_e2e_pipe_params_st), GFP_ATOMIC); + DC_LOGGER_INIT(dc->ctx->logger); + + BW_VAL_TRACE_COUNT(); +@@ -2062,16 +2061,14 @@ static bool dcn20_validate_bandwidth_internal(struct dc *dc, struct dc_state *co + out = false; + + validate_out: +- kfree(pipes); + + BW_VAL_TRACE_FINISH(); + + return out; + } + +-bool dcn20_validate_bandwidth_fp(struct dc *dc, +- struct dc_state *context, +- bool fast_validate) ++bool dcn20_validate_bandwidth_fp(struct dc *dc, struct dc_state *context, ++ bool fast_validate, display_e2e_pipe_params_st *pipes) + { + bool voltage_supported = false; + bool full_pstate_supported = false; +@@ -2090,11 +2087,11 @@ bool dcn20_validate_bandwidth_fp(struct dc *dc, + ASSERT(context != dc->current_state); + + if (fast_validate) { +- return dcn20_validate_bandwidth_internal(dc, context, true); ++ return dcn20_validate_bandwidth_internal(dc, context, true, pipes); + } + + // Best case, we support full UCLK switch latency +- voltage_supported = dcn20_validate_bandwidth_internal(dc, context, false); ++ voltage_supported = dcn20_validate_bandwidth_internal(dc, context, false, pipes); + full_pstate_supported = context->bw_ctx.bw.dcn.clk.p_state_change_support; + + if (context->bw_ctx.dml.soc.dummy_pstate_latency_us == 0 || +@@ -2106,7 +2103,8 @@ bool dcn20_validate_bandwidth_fp(struct dc *dc, + // Fallback: Try to only support G6 temperature read latency + context->bw_ctx.dml.soc.dram_clock_change_latency_us = context->bw_ctx.dml.soc.dummy_pstate_latency_us; + +- voltage_supported = dcn20_validate_bandwidth_internal(dc, context, false); ++ memset(pipes, 0, dc->res_pool->pipe_count * sizeof(display_e2e_pipe_params_st)); ++ voltage_supported = dcn20_validate_bandwidth_internal(dc, context, false, pipes); + dummy_pstate_supported = context->bw_ctx.bw.dcn.clk.p_state_change_support; + + if (voltage_supported && (dummy_pstate_supported || !(context->stream_count))) { +@@ -2311,9 +2309,8 @@ static void dcn21_calculate_wm(struct dc *dc, struct dc_state *context, + &context->bw_ctx.dml, pipes, pipe_cnt); + } + +-bool dcn21_validate_bandwidth_fp(struct dc *dc, +- struct dc_state *context, +- bool fast_validate) ++bool dcn21_validate_bandwidth_fp(struct dc *dc, struct dc_state *context, ++ bool fast_validate, display_e2e_pipe_params_st *pipes) + { + bool out = false; + +@@ -2322,7 +2319,6 @@ bool dcn21_validate_bandwidth_fp(struct dc *dc, + int vlevel = 0; + int pipe_split_from[MAX_PIPES]; + int pipe_cnt = 0; +- display_e2e_pipe_params_st *pipes = kzalloc(dc->res_pool->pipe_count * sizeof(display_e2e_pipe_params_st), GFP_ATOMIC); + DC_LOGGER_INIT(dc->ctx->logger); + + BW_VAL_TRACE_COUNT(); +@@ -2362,7 +2358,6 @@ bool dcn21_validate_bandwidth_fp(struct dc *dc, + out = false; + + validate_out: +- kfree(pipes); + + BW_VAL_TRACE_FINISH(); + +diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h +index c51badf7b68a..b6c34198ddc8 100644 +--- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h ++++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h +@@ -61,9 +61,8 @@ void dcn20_update_bounding_box(struct dc *dc, + unsigned int num_states); + void dcn20_patch_bounding_box(struct dc *dc, + struct _vcs_dpi_soc_bounding_box_st *bb); +-bool dcn20_validate_bandwidth_fp(struct dc *dc, +- struct dc_state *context, +- bool fast_validate); ++bool dcn20_validate_bandwidth_fp(struct dc *dc, struct dc_state *context, ++ bool fast_validate, display_e2e_pipe_params_st *pipes); + void dcn20_fpu_set_wm_ranges(int i, + struct pp_smu_wm_range_sets *ranges, + struct _vcs_dpi_soc_bounding_box_st *loaded_bb); +@@ -77,9 +76,8 @@ int dcn21_populate_dml_pipes_from_context(struct dc *dc, + struct dc_state *context, + display_e2e_pipe_params_st *pipes, + bool fast_validate); +-bool dcn21_validate_bandwidth_fp(struct dc *dc, +- struct dc_state *context, +- bool fast_validate); ++bool dcn21_validate_bandwidth_fp(struct dc *dc, struct dc_state *context, bool ++ fast_validate, display_e2e_pipe_params_st *pipes); + void dcn21_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params); + + void dcn21_clk_mgr_set_bw_params_wm_table(struct clk_bw_params *bw_params); +diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig +index ce397a8797f7..98c3f532822d 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 cfbfbfed3f5e..da2becfbc86c 100644 +--- a/drivers/gpu/drm/i915/display/intel_crtc.c ++++ b/drivers/gpu/drm/i915/display/intel_crtc.c +@@ -562,7 +562,8 @@ void intel_pipe_update_start(struct intel_atomic_state *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; +@@ -587,11 +588,13 @@ void intel_pipe_update_start(struct intel_atomic_state *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); +@@ -624,7 +627,8 @@ void intel_pipe_update_start(struct intel_atomic_state *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) +@@ -728,7 +732,8 @@ void intel_pipe_update_end(struct intel_atomic_state *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/display/intel_vblank.c b/drivers/gpu/drm/i915/display/intel_vblank.c +index f5659ebd08eb..5b6d2f55528d 100644 +--- a/drivers/gpu/drm/i915/display/intel_vblank.c ++++ b/drivers/gpu/drm/i915/display/intel_vblank.c +@@ -294,7 +294,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) +@@ -358,7 +359,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/gt/intel_breadcrumbs.c b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c +index f2973cd1a8aa..aa77f8601b8a 100644 +--- a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c ++++ b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c +@@ -315,7 +315,12 @@ void __intel_breadcrumbs_park(struct intel_breadcrumbs *b) + return; + + /* Kick the work once more to drain the signalers, and disarm the irq */ +- irq_work_queue(&b->irq_work); ++ irq_work_sync(&b->irq_work); ++ while (READ_ONCE(b->irq_armed) && !atomic_read(&b->active)) { ++ irq_work_queue(&b->irq_work); ++ cond_resched(); ++ irq_work_sync(&b->irq_work); ++ } + } + + void intel_breadcrumbs_free(struct kref *kref) +diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +index 2065be5a196b..73d815fc514b 100644 +--- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c ++++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +@@ -1303,7 +1303,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 +@@ -1403,7 +1403,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; + } + } +@@ -1429,7 +1429,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 */ + } + +@@ -1591,7 +1591,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 +@@ -1617,13 +1617,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); +@@ -2478,7 +2471,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/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c +index 13fb8e5042c5..b51fb0c97772 100644 +--- a/drivers/gpu/drm/i915/gt/intel_reset.c ++++ b/drivers/gpu/drm/i915/gt/intel_reset.c +@@ -164,13 +164,13 @@ static int i915_do_reset(struct intel_gt *gt, + /* Assert reset for at least 20 usec, and wait for acknowledgement. */ + pci_write_config_byte(pdev, I915_GDRST, GRDOM_RESET_ENABLE); + udelay(50); +- err = wait_for_atomic(i915_in_reset(pdev), 50); ++ err = _wait_for_atomic(i915_in_reset(pdev), 50, 0); + + /* Clear the reset request. */ + pci_write_config_byte(pdev, I915_GDRST, 0); + udelay(50); + if (!err) +- err = wait_for_atomic(!i915_in_reset(pdev), 50); ++ err = _wait_for_atomic(!i915_in_reset(pdev), 50, 0); + + return err; + } +@@ -190,7 +190,7 @@ static int g33_do_reset(struct intel_gt *gt, + struct pci_dev *pdev = to_pci_dev(gt->i915->drm.dev); + + pci_write_config_byte(pdev, I915_GDRST, GRDOM_RESET_ENABLE); +- return wait_for_atomic(g4x_reset_complete(pdev), 50); ++ return _wait_for_atomic(g4x_reset_complete(pdev), 50, 0); + } + + static int g4x_do_reset(struct intel_gt *gt, +@@ -207,7 +207,7 @@ static int g4x_do_reset(struct intel_gt *gt, + + pci_write_config_byte(pdev, I915_GDRST, + GRDOM_MEDIA | GRDOM_RESET_ENABLE); +- ret = wait_for_atomic(g4x_reset_complete(pdev), 50); ++ ret = _wait_for_atomic(g4x_reset_complete(pdev), 50, 0); + if (ret) { + GT_TRACE(gt, "Wait for media reset failed\n"); + goto out; +@@ -215,7 +215,7 @@ static int g4x_do_reset(struct intel_gt *gt, + + pci_write_config_byte(pdev, I915_GDRST, + GRDOM_RENDER | GRDOM_RESET_ENABLE); +- ret = wait_for_atomic(g4x_reset_complete(pdev), 50); ++ ret = _wait_for_atomic(g4x_reset_complete(pdev), 50, 0); + if (ret) { + GT_TRACE(gt, "Wait for render reset failed\n"); + goto out; +@@ -785,9 +785,7 @@ int __intel_gt_reset(struct intel_gt *gt, intel_engine_mask_t engine_mask) + reset_mask = wa_14015076503_start(gt, engine_mask, !retry); + + GT_TRACE(gt, "engine_mask=%x\n", reset_mask); +- preempt_disable(); + ret = reset(gt, reset_mask, retry); +- preempt_enable(); + + wa_14015076503_end(gt, reset_mask); + } +diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h +index 8dc291ff0093..5b8d084c9c58 100644 +--- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h ++++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h +@@ -317,7 +317,7 @@ static inline int intel_guc_send_busy_loop(struct intel_guc *guc, + { + int err; + unsigned int sleep_period_ms = 1; +- bool not_atomic = !in_atomic() && !irqs_disabled(); ++ bool not_atomic = !in_atomic() && !irqs_disabled() && !rcu_preempt_depth(); + + /* + * FIXME: Have caller pass in if we are in an atomic context to avoid +diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c +index f59081066a19..014d02029a41 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 ce1cbee1b39d..3c51620d011b 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 +@@ -322,7 +326,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 c61066498bf2..48e19e55d6b0 100644 +--- a/drivers/gpu/drm/i915/i915_utils.h ++++ b/drivers/gpu/drm/i915/i915_utils.h +@@ -288,7 +288,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 e575048af139..fa58ec612b67 100644 +--- a/drivers/irqchip/Kconfig ++++ b/drivers/irqchip/Kconfig +@@ -689,4 +689,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 ffd945fe71aa..4d6fef2c9aee 100644 +--- a/drivers/irqchip/Makefile ++++ b/drivers/irqchip/Makefile +@@ -120,3 +120,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 a09883e5006c..7069a2bdab7e 100644 +--- a/drivers/irqchip/irq-gic-v3.c ++++ b/drivers/irqchip/irq-gic-v3.c +@@ -28,6 +28,10 @@ + #include + #include + ++#ifdef CONFIG_BAREMETAL ++#include ++#endif ++ + #include + #include + #include +@@ -1430,12 +1434,12 @@ 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, 8, NUMA_NO_NODE, &sgi_fwspec); ++ /* Register all 16 non-secure SGIs */ ++ base_sgi = irq_domain_alloc_irqs(gic_data.domain, 16, NUMA_NO_NODE, &sgi_fwspec); + 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 412196a7dad5..5448b1441577 100644 +--- a/drivers/irqchip/irq-gic.c ++++ b/drivers/irqchip/irq-gic.c +@@ -868,11 +868,11 @@ 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, 8, NUMA_NO_NODE, &sgi_fwspec); ++ base_sgi = irq_domain_alloc_irqs(gic_data[0].domain, 16, NUMA_NO_NODE, &sgi_fwspec); + 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 bc2e265cb02d..84158265ae08 100644 +--- a/drivers/mailbox/Kconfig ++++ b/drivers/mailbox/Kconfig +@@ -295,4 +295,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 ebff3baf3045..3b6f153e1523 100644 +--- a/drivers/mailbox/mailbox.c ++++ b/drivers/mailbox/mailbox.c +@@ -159,7 +159,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 925aaffc5d95..d9f521174ce0 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 f8c1d73b251d..6365e73296cf 100644 +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -89,6 +89,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 cb9a97340e58..f641c13c533c 100644 +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -26,3 +26,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..a665ab225e2b +--- /dev/null ++++ b/drivers/net/dsa/netc/Kconfig +@@ -0,0 +1,18 @@ ++# 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. ++ ++config NET_DSA_NETC_PTP ++ bool "Support for the PTP clock on the NXP NETC Ethernet switch" ++ depends on NET_DSA_NETC ++ depends on PTP_1588_CLOCK ++ help ++ This enables support for timestamping and PTP clock manipulations in ++ the NETC DSA driver. +\ No newline at end of file +diff --git a/drivers/net/dsa/netc/Makefile b/drivers/net/dsa/netc/Makefile +new file mode 100644 +index 000000000000..f9eb97417999 +--- /dev/null ++++ b/drivers/net/dsa/netc/Makefile +@@ -0,0 +1,14 @@ ++# 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_main.o ++ ++ifdef CONFIG_NET_DSA_NETC_PTP ++netcdsa-objs += netc_ptp.o ++endif +\ No newline at end of file +diff --git a/drivers/net/dsa/netc/netc.h b/drivers/net/dsa/netc/netc.h +new file mode 100644 +index 000000000000..3c7a3f4a17a4 +--- /dev/null ++++ b/drivers/net/dsa/netc/netc.h +@@ -0,0 +1,131 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#ifndef _NETC_H ++#define _NETC_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "netc_config.h" ++#include "netc_ptp.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_psfp_list { ++ struct list_head stream_list; ++#define MAX_SSIDS 512 ++ uint16_t ssids[MAX_SSIDS]; ++ int num_ssids; ++ /* Serialize access to the lists */ ++ struct mutex lock; ++}; ++ ++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; ++ ++ 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; ++ /* Serializes accesses to the FDB */ ++ struct mutex fdb_lock; ++ ++ struct devlink_region **regions; ++ ++ /* PTP two-step TX timestamp ID, and its serialization lock */ ++ spinlock_t ts_id_lock; ++ u32 ts_id; ++ unsigned long hwts_tx_en; ++ unsigned long hwts_rx_en; ++ struct netc_ptp_data ptp_data; ++ ++ struct netc_psfp_list psfp; ++}; ++ ++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, ++ struct ptp_system_timestamp *ptp_sts); ++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); ++int netc_xfer_write_u64(const struct netc_private *priv, ++ enum netc_cmd cmd, uint64_t value, ++ struct ptp_system_timestamp *ptp_sts); ++int netc_xfer_read_u64(const struct netc_private *priv, ++ enum netc_cmd cmd, uint64_t *value, ++ struct ptp_system_timestamp *ptp_sts); ++ ++/* 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..e2bd3c1b29dc +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_config.c +@@ -0,0 +1,696 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023-2024 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, int port) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_fdb fdb_del = {0}; ++ int rc; ++ ++ ether_addr_copy(fdb_del.mac_addr, mac_addr); ++ fdb_del.vid = vid; ++ fdb_del.port = (uint8_t)port;; ++ ++ 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_streamid_set(struct netc_private *priv, int port_mask, uint16_t handle, ++ const unsigned char *mac, uint16_t vid, tsn_cb_streamid_type type) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_nullstreamid streamid = {0}; ++ int rc; ++ ++ streamid.type = type; ++ streamid.handle = handle; ++ streamid.vid = vid; ++ streamid.port_mask = port_mask; ++ ether_addr_copy(streamid.mac_addr, mac); ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_STREAMID_SET, &streamid, sizeof(streamid)); ++ if (rc < 0) { ++ dev_err(dev, "failed to add streamid: %d\n", handle); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int netc_streamid_del(struct netc_private *priv, uint16_t stream_handle) ++{ ++ struct device *dev = priv->ds->dev; ++ int rc; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_STREAMID_DEL, &stream_handle, sizeof(stream_handle)); ++ if (rc < 0) { ++ dev_err(dev, "failed to delete streamid: %d\n", stream_handle); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int netc_port_priority_map(struct netc_private *priv, int port, uint8_t *map) ++{ ++ struct netc_cmd_priority_map prio_map; ++ int rc; ++ ++ prio_map.port = port; ++ memcpy(prio_map.map, map, sizeof(prio_map.map)); ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_PRIORITY_MAP_SET, &prio_map, sizeof(prio_map)); ++ ++ return rc; ++} ++ ++int netc_qbv_set(struct netc_private *priv, int port, int enable, ++ struct tc_taprio_qopt_offload *taprio) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_qbv_set_p1 qbvconf_p1 = {0}; ++ struct netc_cmd_qbv_set_p2 qbvconf_p2 = {0}; ++ struct netc_cmd_qbv_gcl qbvconf_gcl = {0}; ++ uint8_t buffer[16]; ++ uint8_t offset; ++ int rc; ++ ++ qbvconf_p1.port = port; ++ qbvconf_p1.enabled = enable; ++ qbvconf_p1.base_time = taprio->base_time; ++ qbvconf_p1.cycle_time = taprio->cycle_time; ++ qbvconf_p1.gcl_len = taprio->num_entries; ++ if (enable) ++ qbvconf_p1.gcl_len = taprio->num_entries; ++ else ++ qbvconf_p1.gcl_len = 0; ++ ++ if (qbvconf_p1.gcl_len > NETC_QBV_LIST_MAX_ENTRIES) ++ return -EINVAL; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_QBV_SET_P1, &qbvconf_p1, sizeof(qbvconf_p1)); ++ if (rc < 0) ++ goto err; ++ ++ qbvconf_p2.cycle_time_ext = taprio->cycle_time_extension; ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_QBV_SET_P2, &qbvconf_p2, sizeof(qbvconf_p2)); ++ if (rc < 0) ++ goto err; ++ ++ offset = 0; ++ memset(buffer, 0, sizeof(buffer)); ++ for (int i = 0; i < qbvconf_p1.gcl_len; i++) { ++ qbvconf_gcl.interval = taprio->entries[i].interval; ++ qbvconf_gcl.gate_mask = taprio->entries[i].gate_mask; ++ qbvconf_gcl.operation = taprio->entries[i].command; ++ memcpy(&buffer[offset], &qbvconf_gcl, sizeof(qbvconf_gcl)); ++ if (offset) { ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_QBV_SET_GCL, buffer, sizeof(buffer)); ++ if (rc < 0) ++ goto err; ++ ++ offset = 0; ++ memset(buffer, 0, sizeof(buffer)); ++ } else { ++ offset = sizeof(qbvconf_gcl); ++ } ++ } ++ ++ if (offset) { ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_QBV_SET_GCL, buffer, sizeof(buffer)); ++ if (rc < 0) ++ goto err; ++ } ++ ++ return 0; ++err: ++ dev_err(dev, "failed to set qbv on port: %d\n", port); ++ ++ return rc; ++} ++ ++int netc_port_set_preemptible_tcs(struct dsa_switch *ds, int port, ++ unsigned long preemptible_tcs) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_cmd_qbu_set qbu_conf; ++ ++ memset(&qbu_conf, 0, sizeof(qbu_conf)); ++ qbu_conf.port = port; ++ qbu_conf.preemption_mask = (uint8_t)preemptible_tcs; ++ ++ return netc_xfer_set_cmd(priv, NETC_CMD_QBU_SET, ++ &qbu_conf, sizeof(qbu_conf)); ++} ++ ++int netc_port_set_mm(struct dsa_switch *ds, int port, ++ struct ethtool_mm_cfg *cfg, ++ struct netlink_ext_ack *extack) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_cmd_set_get_mm mm_conf; ++ u32 add_frag_size = 0; ++ int rc = 0; ++ ++ memset(&mm_conf, 0, sizeof(mm_conf)); ++ mm_conf.verify_time = (uint32_t)cfg->verify_time; ++ mm_conf.verify_enabled = cfg->verify_enabled ? 1 : 0; ++ mm_conf.tx_enabled = cfg->tx_enabled ? 1 : 0; ++ mm_conf.pmac_enabled = cfg->pmac_enabled ? 1 : 0; ++ mm_conf.port = (uint8_t)port; ++ ++ rc = ethtool_mm_frag_size_min_to_add((u32)cfg->tx_min_frag_size, ++ &add_frag_size, extack); ++ if (rc) ++ return rc; ++ ++ mm_conf.add_frag_size = (int32_t)add_frag_size; ++ ++ return netc_xfer_set_cmd(priv, NETC_CMD_MM_SET, &mm_conf, sizeof(mm_conf)); ++} ++ ++int netc_port_get_mm(struct dsa_switch *ds, int port, ++ struct ethtool_mm_state *state) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_cmd_set_get_mm mm_conf; ++ int rc; ++ ++ memset(&mm_conf, 0, sizeof(mm_conf)); ++ mm_conf.port = (uint8_t)port; ++ rc = netc_xfer_get_cmd(priv, NETC_CMD_MM_GET, port, &mm_conf, sizeof(mm_conf)); ++ if (rc) ++ return rc; ++ ++ state->verify_time = (u32)mm_conf.verify_time; ++ state->max_verify_time = NETC_GET_MM_MAX_VERIFY_TIME; ++ state->verify_status = (enum ethtool_mm_verify_status)mm_conf.verify_status; ++ state->tx_enabled = !!mm_conf.tx_enabled; ++ state->tx_active = !!mm_conf.tx_active; ++ state->pmac_enabled = !!mm_conf.pmac_enabled; ++ state->verify_enabled = !!mm_conf.verify_enabled; ++ state->tx_min_frag_size = ++ ethtool_mm_frag_size_add_to_min(mm_conf.add_frag_size); ++ state->rx_min_frag_size = state->tx_min_frag_size; ++ ++ return 0; ++} ++ ++static int netc_qci_sg_set(struct netc_private *priv, struct netc_stream_filter *filter) ++{ ++ struct action_gate_entry *entry = NULL; ++ struct netc_cmd_psfp_sg_p1 sg_p1 = {0}; ++ struct netc_cmd_psfp_sg_p2 sg_p2 = {0}; ++ struct netc_cmd_psfp_sgl sgl = {0}; ++ int rc; ++ ++ sg_p1.index = filter->stream_handle; ++ sg_p1.base_time = filter->qci.gate.basetime; ++ sg_p1.cycle_time = filter->qci.gate.cycletime; ++ sg_p1.gcl_len = filter->qci.gate.num_entries; ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_SG_SET_P1, &sg_p1, sizeof(sg_p1)); ++ if (rc < 0) ++ return rc; ++ ++ sg_p2.cycle_time_ext = filter->qci.gate.cycletimeext; ++ sg_p2.prio = filter->qci.gate.prio; ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_SG_SET_P2, &sg_p2, sizeof(sg_p2)); ++ if (rc < 0) ++ return rc; ++ ++ for (int i = 0; i < filter->qci.gate.num_entries; i++) { ++ entry = &filter->qci.gate.entries[i]; ++ sgl.interval = entry->interval; ++ sgl.maxoctets = entry->maxoctets; ++ sgl.ipv = entry->ipv; ++ sgl.gate_state = entry->gate_state; ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_SG_SET_GCL, &sgl, sizeof(sgl)); ++ if (rc < 0) ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int netc_qci_fm_set(struct netc_private *priv, struct netc_stream_filter *filter) ++{ ++ struct netc_cmd_psfp_fm fm = {0}; ++ int rc; ++ ++ fm.index = filter->stream_handle; ++ fm.rate = filter->qci.police.rate; ++ fm.burst = filter->qci.police.burst; ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_FM_SET, &fm, sizeof(fm)); ++ if (rc < 0) ++ return rc; ++ ++ return 0; ++} ++ ++int netc_qci_set(struct netc_private *priv, struct netc_stream_filter *filter, int port) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_psfp_sf sf = {0}; ++ int rc; ++ ++ sf.stream_handle = filter->stream_handle; ++ sf.priority_spec = filter->qci.priority_spec; ++ sf.port = port; ++ sf.maxsdu = filter->qci.maxsdu; ++ if (filter->qci.gate.num_entries) { ++ sf.sg_enable = 1; ++ rc = netc_qci_sg_set(priv, filter); ++ if (rc < 0) ++ return rc; ++ } ++ ++ if (filter->qci.police.rate) { ++ sf.fm_enable = 1; ++ rc = netc_qci_fm_set(priv, filter); ++ if (rc < 0) ++ return rc; ++ } ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_SF_SET, ++ &sf, sizeof(sf)); ++ if (rc < 0) { ++ dev_err(dev, "failed to set Qci setting: %d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int netc_qci_del(struct netc_private *priv, uint16_t handle, ++ uint32_t port) ++{ ++ struct device *dev = priv->ds->dev; ++ int rc; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_DEL, ++ &handle, sizeof(handle)); ++ if (rc < 0) { ++ dev_err(dev, "failed to delete Qci setting: %d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int netc_qci_get(struct netc_private *priv, uint16_t handle, struct flow_stats *stats) ++{ ++ struct netc_cmd_psfp_response psfp_resp = {0}; ++ int rc; ++ ++ rc = netc_xfer_get_cmd(priv, NETC_CMD_QCI_GET, handle, ++ &psfp_resp, sizeof(psfp_resp)); ++ ++ if (rc != 0) ++ return rc; ++ ++ stats->pkts = psfp_resp.pkts; ++ stats->drops = psfp_resp.drops; ++ ++ return 0; ++} ++ ++int netc_frer_seqgen(struct netc_private *priv, struct netc_stream_filter *filter) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_frer_sg frer_sg = {0}; ++ int rc; ++ ++ frer_sg.stream_handle = filter->stream_handle; ++ frer_sg.iport = filter->seqgen.iport; ++ frer_sg.encap = filter->seqgen.enc; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_FRER_SG_SET, ++ &frer_sg, sizeof(frer_sg)); ++ if (rc < 0) { ++ dev_err(dev, "failed to set FRER sequence generation: %d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int netc_frer_seqrec(struct netc_private *priv, struct netc_stream_filter *filter) ++{ ++ struct device *dev = priv->ds->dev; ++ struct netc_cmd_frer_sr frer_sr = {0}; ++ int rc; ++ ++ frer_sr.stream_handle = filter->stream_handle; ++ frer_sr.eport = filter->seqrec.eport; ++ frer_sr.encap = filter->seqrec.enc; ++ frer_sr.alg = filter->seqrec.alg; ++ frer_sr.his_len = filter->seqrec.his_len; ++ frer_sr.reset_timeout = filter->seqrec.reset_timeout; ++ frer_sr.rtag_pop_en = filter->seqrec.rtag_pop_en; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_FRER_SR_SET, ++ &frer_sr, sizeof(frer_sr)); ++ if (rc < 0) { ++ dev_err(dev, "failed to set FRER sequence recovery: %d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int netc_frer_sg_del(struct netc_private *priv, uint16_t stream_handle, uint32_t port) ++{ ++ struct device *dev = priv->ds->dev; ++ int rc; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_FRER_SG_DEL, ++ &stream_handle, sizeof(stream_handle)); ++ if (rc < 0) { ++ dev_err(dev, "failed to delete FRER setting: %d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int netc_frer_sr_del(struct netc_private *priv, uint16_t stream_handle, uint32_t port) ++{ ++ struct device *dev = priv->ds->dev; ++ int rc; ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_FRER_SR_DEL, ++ &stream_handle, sizeof(stream_handle)); ++ if (rc < 0) { ++ dev_err(dev, "failed to delete FRER setting: %d\n", rc); ++ return rc; ++ } ++ ++ 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..8bd64a1e7142 +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_config.h +@@ -0,0 +1,557 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023-2024 NXP ++ */ ++ ++#ifndef _NETC_CONFIG_H ++#define _NETC_CONFIG_H ++ ++#include ++#include ++#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_QBV_LIST_MAX_ENTRIES 256 ++ ++#define NETC_SPI_WORD_BITS 8 ++#define NETC_SPI_MSG_WORD_BYTES 4 ++#define NETC_SPI_MSG_HEADER_SIZE 20 ++#define NETC_SPI_MSG_PARAM_SIZE 16 ++#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 ++ ++#define NETC_GET_MM_MAX_VERIFY_TIME (128U) ++ ++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_TIMER_CUR_SET, ++ NETC_CMD_TIMER_CUR_GET, ++ NETC_CMD_TIMER_RATE_SET, ++ NETC_CMD_TIMER_RATE_GET, ++ NETC_CMD_TIMER_ADJTIME_SET, ++ NETC_CMD_TIMER_ADJFINE_SET, ++ NETC_CMD_TIMER_PPS_START, ++ NETC_CMD_TIMER_PPS_STOP, ++ NETC_CMD_TIMER_EXTTS_START, ++ NETC_CMD_TIMER_EXTTS_STOP, ++ ++ NETC_CMD_QBV_SET_P1 = 0x3000, ++ NETC_CMD_QBV_SET_P2, ++ NETC_CMD_QBV_SET_GCL, ++ NETC_CMD_QBU_SET, ++ NETC_CMD_MM_SET, ++ NETC_CMD_MM_GET, ++ NETC_CMD_QCI_SF_SET, ++ NETC_CMD_QCI_SG_SET_P1, ++ NETC_CMD_QCI_SG_SET_P2, ++ NETC_CMD_QCI_SG_SET_GCL, ++ NETC_CMD_QCI_FM_SET, ++ NETC_CMD_QCI_DEL, ++ NETC_CMD_QCI_GET, ++ NETC_CMD_FRER_SG_SET, ++ NETC_CMD_FRER_SG_DEL, ++ NETC_CMD_FRER_SR_SET, ++ NETC_CMD_FRER_SR_DEL, ++ NETC_CMD_STREAMID_SET, ++ NETC_CMD_STREAMID_DEL, ++ NETC_CMD_PRIORITY_MAP_SET, ++ ++ 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_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; ++}; ++ ++/* command param for NETC_CMD_TIMER_PPS_START */ ++struct netc_cmd_timer_pps { ++ uint64_t pin_start; ++ uint32_t pin_duration32; ++}; ++ ++/* command param for NETC PTP */ ++struct netc_ptp_ctl_param { ++ union { ++ uint64_t ns; ++ int64_t offset; ++ int64_t ppb; ++ }; ++ uint8_t clock_id; ++}; ++ ++/* stream identification */ ++typedef enum { ++ STREAMID_RESERVED = 0, ++ /* Null Stream identification */ ++ STREAMID_NULL, ++ /* Source MAC and VLAN Stream identification */ ++ STREAMID_SMAC_VLAN, ++ /* Active Destination MAC and VLAN stream identification */ ++ STREAMID_DMAC_VLAN, ++ /* IP stream identification */ ++ STREAMID_IP, ++} tsn_cb_streamid_type; ++ ++enum netc_action_type { ++ NETC_STREAM_NULL, ++ NETC_STREAM_FRER_SEQGEN, ++ NETC_STREAM_FRER_SEQREC, ++ NETC_STREAM_QCI, ++}; ++ ++struct netc_stream { ++ struct list_head list; ++ unsigned long id; ++ int port_mask; ++ uint8_t mac[ETH_ALEN]; ++ uint16_t vid; ++ tsn_cb_streamid_type type; ++ enum netc_action_type action; ++ uint16_t handle; ++ s8 prio; ++ bool update; ++}; ++ ++typedef enum { ++ NETC_SEQI_RTAG = 1, ++ NETC_SEQI_HSR_SEQ_TAG, ++ NETC_SEQI_PRP_SEQ_TRAILER, ++} netc_encapsulation_t; ++ ++typedef enum { ++ NETC_SEQR_VECTOR = 0, ++ NETC_SEQR_MATCH, ++} netc_seqr_algorithm_t; ++ ++struct netc_stream_seqgen { ++ netc_encapsulation_t enc; ++ uint8_t iport; ++}; ++ ++struct netc_stream_seqrec { ++ netc_encapsulation_t enc; ++ netc_seqr_algorithm_t alg; ++ uint16_t reset_timeout; ++ uint8_t his_len; ++ uint8_t rtag_pop_en; ++ uint8_t eport; ++}; ++ ++enum tc_frer_tag_action { ++ FRER_TAG_NULL, ++ FRER_TAG_PUSH, ++ FRER_TAG_POP, ++}; ++ ++struct netc_stream_qci { ++ uint32_t maxsdu; ++ int8_t priority_spec; ++ struct { ++ int32_t prio; ++ uint64_t basetime; ++ uint32_t cycletime; ++ uint32_t cycletimeext; ++ uint16_t num_entries; ++ struct action_gate_entry *entries; ++ } gate; ++ struct { ++ uint32_t burst; ++ uint64_t rate; ++ } police; ++}; ++ ++struct netc_stream_filter { ++ struct list_head list; ++ uint16_t stream_handle; ++ union { ++ struct netc_stream_seqgen seqgen; ++ struct netc_stream_seqrec seqrec; ++ struct netc_stream_qci qci; ++ }; ++}; ++ ++/* command data for NETC_CMD_STREAMID_SET */ ++struct netc_cmd_nullstreamid { ++ uint8_t mac_addr[ETH_ALEN]; ++ uint16_t vid; ++ uint16_t handle; ++ uint8_t type; ++ uint8_t port_mask; ++}; ++ ++/* command data for NETC_CMD_FRER_SG_SET */ ++struct netc_cmd_frer_sg { ++ uint16_t stream_handle; ++ uint8_t encap; ++ uint8_t iport; ++}; ++ ++/* command data for NETC_CMD_FRER_SR_SET */ ++struct netc_cmd_frer_sr { ++ uint16_t stream_handle; ++ uint16_t reset_timeout; ++ uint8_t his_len; ++ uint8_t encap; ++ uint8_t alg; ++ uint8_t rtag_pop_en; ++ uint8_t eport; ++ uint8_t reserved[3]; ++}; ++ ++struct netc_cmd_psfp_sg_p1 { ++ uint64_t base_time; ++ uint32_t cycle_time; ++ uint16_t gcl_len; ++ uint16_t index; ++}; ++ ++struct netc_cmd_psfp_sg_p2 { ++ uint32_t cycle_time_ext; ++ int32_t prio; ++ uint8_t reserved[8]; ++}; ++ ++struct netc_cmd_psfp_sgl { ++ uint32_t interval; ++ int32_t maxoctets; ++ int32_t ipv; ++ uint8_t gate_state; ++ uint8_t reserved[3]; ++}; ++ ++struct netc_cmd_psfp_fm { ++ uint64_t rate; ++ uint32_t burst; ++ uint16_t index; ++ uint8_t reserved[2]; ++}; ++ ++struct netc_cmd_psfp_sf { ++ uint32_t maxsdu; ++ uint16_t stream_handle; ++ int8_t priority_spec; ++ uint8_t sg_enable; ++ uint8_t fm_enable; ++ uint8_t port; ++ uint8_t reserved[6]; ++}; ++ ++struct netc_cmd_psfp_response { ++ uint64_t pkts; ++ uint64_t drops; ++}; ++ ++struct netc_cmd_priority_map { ++ uint8_t port; ++ uint8_t map[8]; ++ uint8_t reserved[7]; ++}; ++ ++struct netc_cmd_qbv_gcl { ++ uint32_t interval; ++ uint16_t gate_mask; ++ uint16_t operation; ++}; ++ ++struct netc_cmd_qbv_set_p1 { ++ uint64_t base_time; ++ uint32_t cycle_time; ++ uint16_t gcl_len; ++ uint8_t enabled; ++ uint8_t port; ++}; ++ ++struct netc_cmd_qbv_set_p2 { ++ uint32_t cycle_time_ext; ++ uint8_t reserved[12]; ++}; ++ ++struct netc_cmd_set_get_mm { ++ uint32_t verify_time; ++ uint32_t add_frag_size; ++ uint8_t verify_enabled; ++ uint8_t verify_status; ++ uint8_t tx_enabled; ++ uint8_t pmac_enabled; ++ uint8_t tx_active; ++ uint8_t port; ++ uint8_t reserved[2]; ++}; ++ ++struct netc_cmd_qbu_set { ++ uint8_t preemption_mask; ++ uint8_t port; ++ uint8_t reserved[2]; ++}; ++ ++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 port); ++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); ++ ++int netc_streamid_set(struct netc_private *priv, int port_mask, uint16_t handle, ++ const unsigned char *mac, uint16_t vid, tsn_cb_streamid_type type); ++int netc_streamid_del(struct netc_private *priv, uint16_t handle); ++ ++int netc_frer_seqgen(struct netc_private *priv, ++ struct netc_stream_filter *filter); ++int netc_frer_seqrec(struct netc_private *priv, ++ struct netc_stream_filter *filter); ++int netc_frer_sg_del(struct netc_private *priv, uint16_t handle, uint32_t port); ++int netc_frer_sr_del(struct netc_private *priv, uint16_t handle, uint32_t port); ++ ++int netc_qci_set(struct netc_private *priv, ++ struct netc_stream_filter *filter, int port); ++int netc_qci_del(struct netc_private *priv, ++ uint16_t handle, uint32_t port); ++int netc_qci_get(struct netc_private *priv, uint16_t handle, struct flow_stats *stats); ++int netc_qbv_set(struct netc_private *priv, int port, int enable, ++ struct tc_taprio_qopt_offload *taprio); ++int netc_port_priority_map(struct netc_private *priv, int port, uint8_t *map); ++ ++int netc_port_set_preemptible_tcs(struct dsa_switch *ds, int port, ++ unsigned long preemptible_tcs); ++int netc_port_set_mm(struct dsa_switch *ds, int port, ++ struct ethtool_mm_cfg *cfg, ++ struct netlink_ext_ack *extack); ++int netc_port_get_mm(struct dsa_switch *ds, int port, ++ struct ethtool_mm_state *state); ++#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..963a6fd8775c +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_devlink.c +@@ -0,0 +1,107 @@ ++// 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_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..5aa93b3b295c +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_main.c +@@ -0,0 +1,1439 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023-2024 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" ++ ++uint8_t netc_default_priority_map[] = {0, 1, 2, 3, 4, 5, 6, 7}; ++ ++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; ++ int rc; ++ ++ 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); ++ ++ mutex_lock(&priv->fdb_lock); ++ rc = netc_fdb_entry_add(priv, addr, vid, port); ++ mutex_unlock(&priv->fdb_lock); ++ ++ return rc; ++} ++ ++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; ++ int rc; ++ ++ 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; ++ } ++ } ++ ++ mutex_lock(&priv->fdb_lock); ++ rc = netc_fdb_entry_del(priv, addr, vid, port); ++ mutex_unlock(&priv->fdb_lock); ++ ++ return rc; ++} ++ ++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_mdb_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb, ++ struct dsa_db db) ++{ ++ return netc_fdb_add(ds, port, mdb->addr, mdb->vid, db); ++} ++ ++static int netc_mdb_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb, ++ struct dsa_db db) ++{ ++ return netc_fdb_del(ds, port, mdb->addr, mdb->vid, db); ++} ++ ++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; ++ ++ 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; ++} ++ ++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->meta_tstamp_handler = netc_process_meta_tstamp; ++ ++ return 0; ++} ++ ++static int netc_stream_identify(struct flow_cls_offload *f, struct netc_stream *stream) ++{ ++ struct flow_rule *rule = flow_cls_offload_flow_rule(f); ++ struct flow_dissector *dissector = rule->match.dissector; ++ ++ if (dissector->used_keys & ++ ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | ++ BIT(FLOW_DISSECTOR_KEY_BASIC) | ++ BIT(FLOW_DISSECTOR_KEY_VLAN) | ++ BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) ++ return -EOPNOTSUPP; ++ ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { ++ struct flow_match_eth_addrs match; ++ ++ flow_rule_match_eth_addrs(rule, &match); ++ if (is_zero_ether_addr(match.mask->src) && ++ !is_zero_ether_addr(match.mask->dst)) { ++ ether_addr_copy(stream->mac, match.key->dst); ++ stream->type = STREAMID_NULL; ++ } else if (!is_zero_ether_addr(match.mask->src) && ++ is_zero_ether_addr(match.mask->dst)) { ++ ether_addr_copy(stream->mac, match.key->src); ++ stream->type = STREAMID_SMAC_VLAN; ++ } else ++ return -EOPNOTSUPP; ++ } else { ++ return -EOPNOTSUPP; ++ } ++ ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { ++ struct flow_match_vlan match; ++ ++ flow_rule_match_vlan(rule, &match); ++ if (match.mask->vlan_priority) ++ stream->prio = match.key->vlan_priority; ++ else ++ stream->prio = -1; ++ ++ if (!match.mask->vlan_id) ++ return -EOPNOTSUPP; ++ stream->vid = match.key->vlan_id; ++ } else { ++ stream->vid = 0; ++ } ++ ++ stream->id = f->cookie; ++ ++ return 0; ++} ++ ++static struct netc_stream * ++netc_stream_table_lookup(struct list_head *stream_list, ++ struct netc_stream *stream) ++{ ++ struct netc_stream *tmp; ++ ++ list_for_each_entry(tmp, stream_list, list) ++ if (ether_addr_equal(tmp->mac, stream->mac) && ++ tmp->vid == stream->vid && tmp->port_mask == stream->port_mask && ++ tmp->type == stream->type) ++ return tmp; ++ ++ return NULL; ++} ++ ++static int netc_stream_handle_alloc(struct netc_private *priv) ++{ ++ int i; ++ for (i = 0; i < MAX_SSIDS; i++) { ++ if (priv->psfp.ssids[i] == 0) { ++ priv->psfp.ssids[i] = 1; ++ priv->psfp.num_ssids++; ++ return i; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++static int netc_stream_handle_del(struct netc_private *priv, u32 handle) ++{ ++ if (handle < 0 || handle > MAX_SSIDS) ++ return -EINVAL; ++ ++ if (priv->psfp.ssids[handle] == 1) { ++ priv->psfp.ssids[handle] = 0; ++ priv->psfp.num_ssids--; ++ } ++ ++ return 0; ++} ++ ++static int netc_stream_table_add(struct netc_private *priv, struct list_head *stream_list, ++ struct netc_stream *stream, struct netlink_ext_ack *extack) ++{ ++ struct netc_stream *stream_entry; ++ int rc; ++ ++ stream_entry = kmemdup(stream, sizeof(*stream_entry), GFP_KERNEL); ++ if (!stream_entry) ++ return -ENOMEM; ++ ++ if (stream->update) { ++ rc = netc_streamid_set(priv, stream_entry->port_mask, stream_entry->handle, ++ stream_entry->mac, stream_entry->vid, stream_entry->type); ++ if (rc) { ++ kfree(stream_entry); ++ return rc; ++ } ++ } ++ ++ list_add_tail(&stream_entry->list, stream_list); ++ ++ return 0; ++} ++ ++static struct netc_stream * ++netc_stream_table_get(struct list_head *stream_list, unsigned long id) ++{ ++ struct netc_stream *tmp; ++ ++ list_for_each_entry(tmp, stream_list, list) ++ if (tmp->id == id) ++ return tmp; ++ ++ return NULL; ++} ++ ++static int netc_cls_flower_add(struct dsa_switch *ds, int port, ++ struct flow_cls_offload *f, bool ingress) ++{ ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct netc_private *priv = ds->priv; ++ struct netlink_ext_ack *extack = f->common.extack; ++ const struct flow_action_entry *a; ++ struct netc_stream stream = {.action = NETC_STREAM_NULL}; ++ struct netc_stream *stream_entry; ++ struct netc_psfp_list *psfp; ++ struct netc_stream_filter filter = {0}; ++ int cpu_port = dp->cpu_dp->index; ++ uint64_t rate; ++ int i, rc; ++ uint32_t handle; ++ bool set_stream = false; ++ ++ psfp = &priv->psfp; ++ ++ rc = netc_stream_identify(f, &stream); ++ if (rc) { ++ NL_SET_ERR_MSG_MOD(extack, "Only can match on VID and dest MAC"); ++ return rc; ++ } ++ ++ mutex_lock(&psfp->lock); ++ ++ flow_action_for_each(i, a, &f->rule->action) { ++ switch (a->id) { ++ case FLOW_ACTION_FRER: ++ if ((a->frer.recover && a->frer.tag_action == FRER_TAG_PUSH) || ++ (!a->frer.recover && a->frer.tag_action != FRER_TAG_PUSH)) { ++ NL_SET_ERR_MSG_MOD(extack, ++ "Non-supported tag action"); ++ rc = -EOPNOTSUPP; ++ goto err; ++ } ++ ++ if (a->frer.recover) { ++ stream.action = NETC_STREAM_FRER_SEQREC; ++ filter.seqrec.enc = a->frer.tag_type; ++ filter.seqrec.alg = a->frer.rcvy_alg; ++ filter.seqrec.his_len = a->frer.rcvy_history_len; ++ filter.seqrec.reset_timeout = a->frer.rcvy_reset_msec; ++ filter.seqrec.rtag_pop_en = ++ (a->frer.tag_action == FRER_TAG_POP) ? 1 : 0; ++ if (ingress) { ++ stream.port_mask = 0xF & ~BIT(cpu_port); ++ filter.seqrec.eport = cpu_port; ++ } else { ++ stream.port_mask = 0xF & ~BIT(port); ++ filter.seqrec.eport = port; ++ } ++ } else { ++ stream.action = NETC_STREAM_FRER_SEQGEN; ++ filter.seqgen.enc = a->frer.tag_type; ++ if (ingress) { ++ filter.seqgen.iport = port; ++ stream.port_mask = BIT(port); ++ } else { ++ filter.seqgen.iport = cpu_port; ++ stream.port_mask = BIT(cpu_port); ++ } ++ } ++ set_stream = true; ++ break; ++ ++ case FLOW_ACTION_GATE: ++ stream.port_mask = BIT(port); ++ stream.action = NETC_STREAM_QCI; ++ filter.qci.gate.prio = a->gate.prio; ++ filter.qci.gate.basetime = a->gate.basetime; ++ filter.qci.gate.cycletime = a->gate.cycletime; ++ filter.qci.gate.cycletimeext = a->gate.cycletimeext; ++ filter.qci.gate.num_entries = a->gate.num_entries; ++ filter.qci.gate.entries = a->gate.entries; ++ set_stream = true; ++ break; ++ ++ case FLOW_ACTION_POLICE: ++ stream.port_mask = BIT(port); ++ stream.action = NETC_STREAM_QCI; ++ if (a->police.mtu < 0) { ++ NL_SET_ERR_MSG_MOD(extack, ++ "invalided maxsdu size"); ++ rc = -EINVAL; ++ goto err; ++ } ++ filter.qci.maxsdu = a->police.mtu; ++ ++ rate = a->police.rate_bytes_ps; ++ if (rate) { ++ filter.qci.police.burst = a->police.burst; ++ filter.qci.police.rate = rate * 8; ++ } ++ set_stream = true; ++ break; ++ ++ case FLOW_ACTION_MIRRED: ++ if (stream.type != STREAMID_NULL) { ++ NL_SET_ERR_MSG_MOD(extack, ++ "Only support destination MAC"); ++ rc = -EOPNOTSUPP; ++ goto err; ++ } ++ dp = dsa_port_from_netdev(a->dev); ++ if (IS_ERR(dp)) { ++ rc = -EINVAL; ++ goto err; ++ } ++ if (netc_fdb_entry_add(priv, stream.mac, stream.vid, dp->index) < 0) { ++ rc = -EINVAL; ++ goto err; ++ } ++ break; ++ ++ default: ++ rc = -EOPNOTSUPP; ++ goto err; ++ } ++ } ++ ++ if (!set_stream) ++ goto exit; ++ ++ stream_entry = netc_stream_table_lookup(&psfp->stream_list, &stream); ++ if (stream_entry) { ++ stream.handle = stream_entry->handle; ++ stream.update = false; ++ } else { ++ handle = netc_stream_handle_alloc(priv); ++ stream.handle = handle; ++ stream.update = true; ++ } ++ ++ rc = netc_stream_table_add(priv, &psfp->stream_list, ++ &stream, extack); ++ if (rc) { ++ NL_SET_ERR_MSG_MOD(extack, "Failed to add new stream table"); ++ goto err; ++ } ++ ++ filter.stream_handle = stream.handle; ++ ++ switch (stream.action) { ++ case NETC_STREAM_FRER_SEQGEN: ++ rc = netc_frer_seqgen(priv, &filter); ++ if (rc) { ++ goto err; ++ } ++ break; ++ case NETC_STREAM_FRER_SEQREC: ++ rc = netc_frer_seqrec(priv, &filter); ++ if (rc) { ++ goto err; ++ } ++ break; ++ case NETC_STREAM_QCI: ++ filter.qci.priority_spec = stream.prio; ++ rc = netc_qci_set(priv, &filter, port); ++ if (rc) { ++ goto err; ++ } ++ break; ++ default: ++ mutex_unlock(&psfp->lock); ++ return -EOPNOTSUPP; ++ } ++ ++exit: ++ mutex_unlock(&psfp->lock); ++ ++ return 0; ++err: ++ mutex_unlock(&psfp->lock); ++ ++ return rc; ++} ++ ++static int netc_cls_flower_del(struct dsa_switch *ds, int port, ++ struct flow_cls_offload *cls, bool ingress) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_stream *stream, *tmp; ++ struct netc_psfp_list *psfp; ++ u32 stream_handle; ++ int rc; ++ ++ psfp = &priv->psfp; ++ ++ mutex_lock(&psfp->lock); ++ ++ stream = netc_stream_table_get(&psfp->stream_list, cls->cookie); ++ if (!stream) { ++ mutex_unlock(&psfp->lock); ++ return 0; ++ } ++ ++ stream_handle = stream->handle; ++ ++ switch (stream->action) { ++ case NETC_STREAM_FRER_SEQGEN: ++ rc = netc_frer_sg_del(priv, stream_handle, port); ++ if (rc) ++ goto err; ++ break; ++ case NETC_STREAM_FRER_SEQREC: ++ rc = netc_frer_sr_del(priv, stream_handle, port); ++ if (rc) ++ goto err; ++ break; ++ case NETC_STREAM_QCI: ++ rc = netc_qci_del(priv, stream_handle, port); ++ if (rc) ++ goto err; ++ break; ++ default: ++ mutex_unlock(&psfp->lock); ++ return -EOPNOTSUPP; ++ } ++ ++ list_del(&stream->list); ++ ++ tmp = netc_stream_table_lookup(&psfp->stream_list, stream); ++ if (!tmp) { ++ rc = netc_streamid_del(priv, stream->handle); ++ if (rc) ++ goto err; ++ rc = netc_stream_handle_del(priv, stream->handle); ++ if (rc) ++ goto err; ++ } ++ ++ kfree(stream); ++ mutex_unlock(&psfp->lock); ++ ++ return 0; ++ ++err: ++ mutex_unlock(&psfp->lock); ++ return rc; ++} ++ ++static int netc_cls_flower_stats(struct dsa_switch *ds, int port, ++ struct flow_cls_offload *cls, bool ingress) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_psfp_list *psfp; ++ struct netc_stream *stream; ++ struct flow_stats stats; ++ int rc; ++ ++ psfp = &priv->psfp; ++ ++ mutex_lock(&psfp->lock); ++ ++ stream = netc_stream_table_get(&psfp->stream_list, cls->cookie); ++ if (!stream) { ++ mutex_unlock(&psfp->lock); ++ return 0; ++ } ++ mutex_unlock(&psfp->lock); ++ ++ rc = netc_qci_get(priv, stream->handle, &stats); ++ if (rc < 0) ++ return rc; ++ ++ flow_stats_update(&cls->stats, 0x0, stats.pkts, stats.drops, 0x0, ++ FLOW_ACTION_HW_STATS_IMMEDIATE); ++ ++ return 0; ++} ++ ++static int netc_port_mqprio_set(struct dsa_switch *ds, int port, ++ struct tc_mqprio_qopt_offload *mqprio) ++{ ++ struct netc_private *priv = ds->priv; ++ struct tc_mqprio_qopt *qopt = &mqprio->qopt; ++ struct dsa_port *dp; ++ uint8_t *map; ++ int rc; ++ ++ dp = dsa_to_port(ds, port); ++ ++ if (dp->bridge) ++ map = qopt->prio_tc_map; ++ else ++ map = netc_default_priority_map; ++ ++ if (!qopt->num_tc) ++ map = netc_default_priority_map; ++ ++ rc = netc_port_set_preemptible_tcs(ds, port, mqprio->preemptible_tcs); ++ if (rc) ++ return rc; ++ ++ return netc_port_priority_map(priv, port, map); ++} ++ ++static int netc_port_taprio_set(struct dsa_switch *ds, int port, ++ struct tc_taprio_qopt_offload *taprio) ++{ ++ struct netc_private *priv = ds->priv; ++ int enable = 1; ++ int rc; ++ ++ if (taprio->cmd == TAPRIO_CMD_DESTROY) { ++ enable = 0; ++ } else if (taprio->cmd != TAPRIO_CMD_REPLACE) { ++ rc = -EOPNOTSUPP; ++ return rc; ++ } ++ ++ netc_port_mqprio_set(ds, port, &taprio->mqprio); ++ ++ rc = netc_qbv_set(priv, port, enable, taprio); ++ ++ return rc; ++} ++ ++static int netc_qos_query_caps(struct tc_query_caps_base *base) ++{ ++ switch (base->type) { ++ case TC_SETUP_QDISC_TAPRIO: { ++ struct tc_taprio_caps *caps = base->caps; ++ ++ caps->supports_queue_max_sdu = true; ++ ++ return 0; ++ } ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++ ++static int netc_port_setup_tc(struct dsa_switch *ds, int port, ++ enum tc_setup_type type, ++ void *type_data) ++{ ++ switch (type) { ++ case TC_QUERY_CAPS: ++ return netc_qos_query_caps(type_data); ++ case TC_SETUP_QDISC_TAPRIO: ++ return netc_port_taprio_set(ds, port, type_data); ++ case TC_SETUP_QDISC_CBS: ++ dev_info(ds->dev, "TC_SETUP_QDISC_CBS not support yet!\n"); ++ return -EOPNOTSUPP; ++ case TC_SETUP_QDISC_MQPRIO: ++ return netc_port_mqprio_set(ds, port, type_data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++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_ptp_clock_register(ds); ++ if (rc < 0) { ++ dev_err(ds->dev, "Failed to register PTP clock: %d\n", rc); ++ goto out_config_free; ++ } ++ ++ rc = netc_devlink_setup(ds); ++ if (rc < 0) ++ goto out_ptp_teardown; ++ ++ 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_ptp_teardown: ++ netc_ptp_clock_unregister(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_ptp_clock_unregister(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_mdb_add = netc_mdb_add, ++ .port_mdb_del = netc_mdb_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, ++ .port_hwtstamp_get = netc_hwtstamp_get, ++ .port_hwtstamp_set = netc_hwtstamp_set, ++ .port_rxtstamp = netc_port_rxtstamp, ++ .port_txtstamp = netc_port_txtstamp, ++ .get_ts_info = netc_get_ts_info, ++ .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, ++ .cls_flower_add = netc_cls_flower_add, ++ .cls_flower_del = netc_cls_flower_del, ++ .cls_flower_stats = netc_cls_flower_stats, ++ .port_setup_tc = netc_port_setup_tc, ++ .set_mm = netc_port_set_mm, ++ .get_mm = netc_port_get_mm, ++}; ++ ++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); ++ mutex_init(&priv->fdb_lock); ++ spin_lock_init(&priv->ts_id_lock); ++ ++ rc = netc_parse_dt(priv); ++ if (rc < 0) { ++ dev_err(ds->dev, "Failed to parse DT: %d\n", rc); ++ return rc; ++ } ++ ++ //for tc filter ++ INIT_LIST_HEAD(&priv->psfp.stream_list); ++ memset(priv->psfp.ssids, 0, sizeof(priv->psfp.ssids)); ++ priv->psfp.num_ssids = 0; ++ mutex_init(&priv->psfp.lock); ++ ++ 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..3e03641b7ae9 +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_ptp.c +@@ -0,0 +1,375 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023-2024 NXP ++ */ ++ ++#include "netc.h" ++ ++#define extts_to_data(t) \ ++ container_of((t), struct netc_ptp_data, extts_timer) ++#define ptp_caps_to_data(d) \ ++ container_of((d), struct netc_ptp_data, caps) ++#define ptp_data_to_netc(d) \ ++ container_of((d), struct netc_private, ptp_data) ++ ++int netc_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr) ++{ ++ struct netc_private *priv = ds->priv; ++ struct hwtstamp_config config; ++ ++ if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) ++ return -EFAULT; ++ ++ switch (config.tx_type) { ++ case HWTSTAMP_TX_OFF: ++ priv->hwts_tx_en &= ~BIT(port); ++ break; ++ case HWTSTAMP_TX_ON: ++ priv->hwts_tx_en |= BIT(port); ++ break; ++ default: ++ return -ERANGE; ++ } ++ ++ switch (config.rx_filter) { ++ case HWTSTAMP_FILTER_NONE: ++ priv->hwts_rx_en &= ~BIT(port); ++ break; ++ default: ++ priv->hwts_rx_en |= BIT(port); ++ break; ++ } ++ ++ if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) ++ return -EFAULT; ++ return 0; ++} ++ ++int netc_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr) ++{ ++ struct netc_private *priv = ds->priv; ++ struct hwtstamp_config config; ++ ++ config.flags = 0; ++ if (priv->hwts_tx_en & BIT(port)) ++ config.tx_type = HWTSTAMP_TX_ON; ++ else ++ config.tx_type = HWTSTAMP_TX_OFF; ++ if (priv->hwts_rx_en & BIT(port)) ++ config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; ++ else ++ config.rx_filter = HWTSTAMP_FILTER_NONE; ++ ++ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? ++ -EFAULT : 0; ++} ++ ++int netc_get_ts_info(struct dsa_switch *ds, int port, ++ struct ethtool_ts_info *info) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_ptp_data *ptp_data = &priv->ptp_data; ++ ++ /* Called during cleanup */ ++ if (!ptp_data->clock) ++ return -ENODEV; ++ ++ info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | ++ SOF_TIMESTAMPING_RX_HARDWARE | ++ SOF_TIMESTAMPING_RAW_HARDWARE; ++ info->tx_types = (1 << HWTSTAMP_TX_OFF) | ++ (1 << HWTSTAMP_TX_ON); ++ info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | ++ (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT); ++ info->phc_index = ptp_clock_index(ptp_data->clock); ++ ++ return 0; ++} ++ ++/* Called from dsa_skb_defer_rx_timestamp */ ++bool netc_port_rxtstamp(struct dsa_switch *ds, int port, ++ struct sk_buff *skb, unsigned int type) ++{ ++ struct skb_shared_hwtstamps *shwt = skb_hwtstamps(skb); ++ u64 ts = NETC_SKB_CB(skb)->tstamp; ++ ++ *shwt = (struct skb_shared_hwtstamps) {0}; ++ ++ shwt->hwtstamp = ns_to_ktime(ts); ++ ++ /* Don't defer */ ++ return false; ++} ++ ++void netc_process_meta_tstamp(struct dsa_switch *ds, int port, ++ u32 ts_id, u64 tstamp) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_ptp_data *ptp_data = &priv->ptp_data; ++ struct sk_buff *skb, *skb_tmp, *skb_match = NULL; ++ struct skb_shared_hwtstamps shwt = {0}; ++ ++ spin_lock(&ptp_data->skb_txtstamp_queue.lock); ++ ++ skb_queue_walk_safe(&ptp_data->skb_txtstamp_queue, skb, skb_tmp) { ++ if (NETC_SKB_CB(skb)->ts_id != ts_id) ++ continue; ++ ++ __skb_unlink(skb, &ptp_data->skb_txtstamp_queue); ++ skb_match = skb; ++ ++ break; ++ } ++ ++ spin_unlock(&ptp_data->skb_txtstamp_queue.lock); ++ ++ if (WARN_ON(!skb_match)) ++ return; ++ ++ shwt.hwtstamp = ns_to_ktime(tstamp); ++ skb_complete_tx_timestamp(skb_match, &shwt); ++} ++ ++/* Called from dsa_skb_tx_timestamp. This callback is just to clone ++ * the skb and have it available in NETC_SKB_CB in the .port_deferred_xmit ++ * callback, where we will timestamp it synchronously. ++ */ ++void netc_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb) ++{ ++ struct netc_private *priv = ds->priv; ++ struct sk_buff *clone; ++ struct netc_ptp_data *ptp_data = &priv->ptp_data; ++ u32 ts_id; ++ ++ if (!(priv->hwts_tx_en & BIT(port))) ++ return; ++ ++ clone = skb_clone_sk(skb); ++ if (!clone) ++ return; ++ ++ NETC_SKB_CB(skb)->clone = clone; ++ ++ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; ++ ++ spin_lock(&priv->ts_id_lock); ++ ++ ts_id = priv->ts_id; ++ /* Deal automatically with 8-bit wraparound */ ++ priv->ts_id++; ++ ++ NETC_SKB_CB(clone)->ts_id = ts_id; ++ ++ spin_unlock(&priv->ts_id_lock); ++ ++ skb_queue_tail(&ptp_data->skb_txtstamp_queue, clone); ++} ++ ++static int netc_ptp_reset(struct dsa_switch *ds) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_ptp_data *ptp_data = &priv->ptp_data; ++ int rc; ++ u64 data = 1; ++ ++ dev_dbg(ds->dev, "Resetting PTP clock\n"); ++ ++ mutex_lock(&ptp_data->lock); ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_PTP_SYNC_SET, ++ &data, sizeof(data)); ++ ++ mutex_unlock(&ptp_data->lock); ++ ++ return rc; ++} ++ ++static int netc_ptp_gettimex(struct ptp_clock_info *ptp, ++ struct timespec64 *ts, ++ struct ptp_system_timestamp *ptp_sts) ++{ ++ struct netc_ptp_data *ptp_data = ptp_caps_to_data(ptp); ++ struct netc_private *priv = ptp_data_to_netc(ptp_data); ++ u64 now = 0; ++ int rc; ++ ++ mutex_lock(&ptp_data->lock); ++ ++ rc = netc_xfer_read_u64(priv, NETC_CMD_TIMER_CUR_GET, &now, ptp_sts); ++ ++ mutex_unlock(&ptp_data->lock); ++ ++ *ts = ns_to_timespec64(now); ++ ++ if (rc < 0) ++ dev_err(priv->ds->dev, "Failed to read PTP clock: %d\n", rc); ++ ++ return rc; ++} ++ ++static int netc_ptp_settime(struct ptp_clock_info *ptp, ++ const struct timespec64 *ts) ++{ ++ struct netc_ptp_data *ptp_data = ptp_caps_to_data(ptp); ++ struct netc_private *priv = ptp_data_to_netc(ptp_data); ++ struct netc_ptp_ctl_param param; ++ int rc; ++ ++ param.ns = timespec64_to_ns(ts); ++ param.clock_id = 0; ++ ++ mutex_lock(&ptp_data->lock); ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_TIMER_CUR_SET, ++ ¶m, sizeof(param)); ++ ++ mutex_unlock(&ptp_data->lock); ++ ++ return rc; ++} ++ ++static int netc_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) ++{ ++ struct netc_ptp_data *ptp_data = ptp_caps_to_data(ptp); ++ struct netc_private *priv = ptp_data_to_netc(ptp_data); ++ struct netc_ptp_ctl_param param; ++ int rc; ++ ++ param.ppb = scaled_ppm_to_ppb(scaled_ppm);; ++ param.clock_id = 0; ++ ++ mutex_lock(&ptp_data->lock); ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_TIMER_ADJFINE_SET, ++ ¶m, sizeof(param)); ++ ++ mutex_unlock(&ptp_data->lock); ++ ++ return rc; ++} ++ ++static int netc_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) ++{ ++ struct netc_ptp_data *ptp_data = ptp_caps_to_data(ptp); ++ struct netc_private *priv = ptp_data_to_netc(ptp_data); ++ struct netc_ptp_ctl_param param; ++ int rc; ++ ++ param.offset = delta; ++ param.clock_id = 0; ++ ++ mutex_lock(&ptp_data->lock); ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_TIMER_ADJTIME_SET, ++ ¶m, sizeof(param)); ++ ++ mutex_unlock(&ptp_data->lock); ++ ++ return rc; ++} ++ ++static int netc_per_out_enable(struct netc_private *priv, ++ struct ptp_perout_request *perout, ++ bool on) ++{ ++ struct netc_ptp_data *ptp_data = &priv->ptp_data; ++ struct netc_cmd_timer_pps param; ++ int rc; ++ ++ /* We only support one channel */ ++ if (perout->index != 0) ++ return -EOPNOTSUPP; ++ ++ /* Reject requests with unsupported flags */ ++ if (perout->flags) ++ return -EOPNOTSUPP; ++ ++ mutex_lock(&ptp_data->lock); ++ ++ if (on) { ++ struct timespec64 pin_duration_ts = { ++ .tv_sec = perout->period.sec, ++ .tv_nsec = perout->period.nsec, ++ }; ++ struct timespec64 pin_start_ts = { ++ .tv_sec = perout->start.sec, ++ .tv_nsec = perout->start.nsec, ++ }; ++ u64 pin_duration = timespec64_to_ns(&pin_duration_ts); ++ if (pin_duration > U32_MAX) { ++ rc = -ERANGE; ++ goto _out; ++ } ++ param.pin_duration32 = (u32) pin_duration; ++ param.pin_start = timespec64_to_ns(&pin_start_ts); ++ ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_TIMER_PPS_START, ++ ¶m, sizeof(param)); ++ } else ++ rc = netc_xfer_set_cmd(priv, NETC_CMD_TIMER_PPS_STOP, ++ NULL, 0); ++ ++_out: ++ mutex_unlock(&ptp_data->lock); ++ ++ return rc; ++} ++ ++static int netc_ptp_enable(struct ptp_clock_info *ptp, ++ struct ptp_clock_request *req, int on) ++{ ++ struct netc_ptp_data *ptp_data = ptp_caps_to_data(ptp); ++ struct netc_private *priv = ptp_data_to_netc(ptp_data); ++ int rc = -EOPNOTSUPP; ++ ++ if (req->type == PTP_CLK_REQ_PEROUT) ++ rc = netc_per_out_enable(priv, &req->perout, on); ++ ++ return rc; ++} ++ ++struct ptp_clock_info netc_clock_caps = { ++ .owner = THIS_MODULE, ++ .name = "NETC PHC", ++ .max_adj = 1000000, ++ .n_alarm = 2, ++ .n_ext_ts = 2, ++ .n_per_out = 3, ++ .n_pins = 0, ++ .pps = 1, ++ .adjfine = netc_ptp_adjfine, ++ .adjtime = netc_ptp_adjtime, ++ .gettimex64 = netc_ptp_gettimex, ++ .settime64 = netc_ptp_settime, ++ .enable = netc_ptp_enable, ++}; ++ ++int netc_ptp_clock_register(struct dsa_switch *ds) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_ptp_data *ptp_data = &priv->ptp_data; ++ ++ skb_queue_head_init(&ptp_data->skb_txtstamp_queue); ++ ++ ptp_data->caps = netc_clock_caps; ++ ptp_data->clock = ptp_clock_register(&ptp_data->caps, ds->dev); ++ if (IS_ERR_OR_NULL(ptp_data->clock)) ++ return PTR_ERR(ptp_data->clock); ++ ++ return netc_ptp_reset(ds); ++} ++ ++void netc_ptp_clock_unregister(struct dsa_switch *ds) ++{ ++ struct netc_private *priv = ds->priv; ++ struct netc_ptp_data *ptp_data = &priv->ptp_data; ++ ++ if (IS_ERR_OR_NULL(ptp_data->clock)) ++ return; ++ ++ del_timer_sync(&ptp_data->extts_timer); ++ ptp_cancel_worker_sync(ptp_data->clock); ++ skb_queue_purge(&ptp_data->skb_txtstamp_queue); ++ ptp_clock_unregister(ptp_data->clock); ++ ptp_data->clock = NULL; ++} +diff --git a/drivers/net/dsa/netc/netc_ptp.h b/drivers/net/dsa/netc/netc_ptp.h +new file mode 100644 +index 000000000000..efdb15e90223 +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_ptp.h +@@ -0,0 +1,77 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2024 NXP ++ */ ++ ++#ifndef _NETC_PTP_H ++#define _NETC_PTP_H ++ ++#include ++ ++#if IS_ENABLED(CONFIG_NET_DSA_NETC_PTP) ++ ++struct netc_ptp_data { ++ struct timer_list extts_timer; ++ /* Used on NETC where meta frames are generated only for ++ * 2-step TX timestamps ++ */ ++ struct sk_buff_head skb_txtstamp_queue; ++ struct ptp_clock *clock; ++ struct ptp_clock_info caps; ++ /* Serializes all operations on the PTP hardware clock */ ++ struct mutex lock; ++ bool extts_enabled; ++ u64 ptpsyncts; ++}; ++ ++int netc_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr); ++ ++int netc_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr); ++ ++void netc_process_meta_tstamp(struct dsa_switch *ds, int port, ++ u32 ts_id, u64 tstamp); ++ ++int netc_get_ts_info(struct dsa_switch *ds, int port, ++ struct ethtool_ts_info *ts); ++ ++bool netc_port_rxtstamp(struct dsa_switch *ds, int port, ++ struct sk_buff *skb, unsigned int type); ++ ++void netc_port_txtstamp(struct dsa_switch *ds, int port, ++ struct sk_buff *skb); ++ ++int netc_ptp_clock_register(struct dsa_switch *ds); ++ ++void netc_ptp_clock_unregister(struct dsa_switch *ds); ++ ++ ++#else ++ ++struct netc_ptp_data { ++ struct mutex lock; ++}; ++ ++static inline int netc_ptp_clock_register(struct dsa_switch *ds) ++{ ++ return 0; ++} ++ ++static inline void netc_ptp_clock_unregister(struct dsa_switch *ds) ++{ ++} ++ ++#define netc_get_ts_info NULL ++ ++#define netc_port_rxtstamp NULL ++ ++#define netc_port_txtstamp NULL ++ ++#define netc_hwtstamp_get NULL ++ ++#define netc_hwtstamp_set NULL ++ ++#define netc_process_meta_tstamp NULL ++ ++#endif /* IS_ENABLED(CONFIG_NET_DSA_NETC_PTP) */ ++ ++#endif /* _NETC_PTP_H */ +diff --git a/drivers/net/dsa/netc/netc_spi.c b/drivers/net/dsa/netc/netc_spi.c +new file mode 100644 +index 000000000000..41cfbc3686ad +--- /dev/null ++++ b/drivers/net/dsa/netc/netc_spi.c +@@ -0,0 +1,147 @@ ++// 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 ptp_system_timestamp *ptp_sts) ++{ ++ 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; ++ hdr_xfer.ptp_sts_word_pre = hdr_xfer.len - 1; ++ hdr_xfer.ptp_sts_word_post = hdr_xfer.len - 1; ++ hdr_xfer.ptp_sts = ptp_sts; ++ ++ 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; ++ ++ resp_xfer.ptp_sts_word_pre = resp_xfer.len - 1; ++ resp_xfer.ptp_sts_word_post = resp_xfer.len - 1; ++ resp_xfer.ptp_sts = ptp_sts; ++ ++ 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, NULL); ++} ++ ++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, NULL); ++} ++ ++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)); ++} ++ ++int netc_xfer_write_u64(const struct netc_private *priv, ++ enum netc_cmd cmd, uint64_t value, ++ struct ptp_system_timestamp *ptp_sts) ++{ ++ return netc_xfer_cmd(priv, SPI_WRITE, cmd, ++ &value, sizeof(value), ++ NULL, 0, ++ ptp_sts); ++} ++ ++int netc_xfer_read_u64(const struct netc_private *priv, ++ enum netc_cmd cmd, uint64_t *value, ++ struct ptp_system_timestamp *ptp_sts) ++{ ++ return netc_xfer_cmd(priv, SPI_READ, cmd, ++ NULL, 0, ++ value, sizeof(*value), ++ ptp_sts); ++} +diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c +index 3e5105267c57..18f6cddf2478 100644 +--- a/drivers/net/dsa/ocelot/felix.c ++++ b/drivers/net/dsa/ocelot/felix.c +@@ -1027,17 +1027,50 @@ static int felix_vlan_add(struct dsa_switch *ds, int port, + if (err) + return err; + +- return ocelot_vlan_add(ocelot, port, vlan->vid, ++ 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; ++ ++ 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 ocelot_vlan_del(ocelot, port, vlan->vid); ++ return 0; + } + + static const u32 felix_phy_match_table[PHY_INTERFACE_MODE_MAX] = { +@@ -1108,6 +1141,9 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, + + ocelot_phylink_mac_link_down(ocelot, port, link_an_mode, interface, + felix->info->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, +@@ -1126,6 +1162,9 @@ static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, + + if (felix->info->port_sched_speed_set) + felix->info->port_sched_speed_set(ocelot, port, speed); ++ ++ if (felix->info->port_preempt_reset) ++ felix->info->port_preempt_reset(ocelot, port, 1); + } + + static int felix_port_enable(struct dsa_switch *ds, int port, +@@ -1249,6 +1288,44 @@ static int felix_get_ts_info(struct dsa_switch *ds, int port, + return ocelot_get_ts_info(ocelot, port, info); + } + ++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 struct regmap *felix_request_regmap_by_name(struct felix *felix, + const char *resource_name) + { +@@ -1476,6 +1553,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)); ++} ++ + static int felix_setup(struct dsa_switch *ds) + { + struct ocelot *ocelot = ds->priv; +@@ -1526,6 +1694,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: +@@ -1553,6 +1725,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); + +@@ -2036,6 +2210,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_mac_config = felix_phylink_mac_config, + .phylink_mac_select_pcs = felix_phylink_mac_select_pcs, +@@ -2091,6 +2268,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 558a60de64b2..ac21a5451232 100644 +--- a/drivers/net/dsa/ocelot/felix.h ++++ b/drivers/net/dsa/ocelot/felix.h +@@ -65,6 +65,11 @@ struct felix_info { + const struct phylink_link_state *state); + int (*configure_serdes)(struct ocelot *ocelot, int port, + struct device_node *portnp); ++ 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 be3576445afc..0d0f647915ed 100644 +--- a/drivers/net/dsa/ocelot/felix_tsn.c ++++ b/drivers/net/dsa/ocelot/felix_tsn.c +@@ -415,10 +415,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; + } +@@ -1456,7 +1458,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 79a06db6b458..10cf8db2c8f0 100644 +--- a/drivers/net/dsa/ocelot/felix_vsc9959.c ++++ b/drivers/net/dsa/ocelot/felix_vsc9959.c +@@ -1556,15 +1556,15 @@ static void vsc9959_tas_clock_adjust(struct ocelot *ocelot) + if (!taprio) + continue; + ++ /* Disable time-aware shaper */ ++ ocelot_rmw_rix(ocelot, 0, QSYS_TAG_CONFIG_ENABLE, ++ QSYS_TAG_CONFIG, port); ++ + ocelot_rmw(ocelot, + QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM(port), + QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM_M, + QSYS_TAS_PARAM_CFG_CTRL); + +- /* Disable time-aware shaper */ +- ocelot_rmw_rix(ocelot, 0, QSYS_TAG_CONFIG_ENABLE, +- QSYS_TAG_CONFIG, port); +- + vsc9959_new_base_time(ocelot, taprio->base_time, + taprio->cycle_time, &base_ts); + +@@ -1577,6 +1577,9 @@ static void vsc9959_tas_clock_adjust(struct ocelot *ocelot) + QSYS_PARAM_CFG_REG_3_BASE_TIME_SEC_MSB_M, + QSYS_PARAM_CFG_REG_3); + ++ for (int i = 0; i < taprio->num_entries; i++) ++ vsc9959_tas_gcl_set(ocelot, i, &taprio->entries[i]); ++ + ocelot_rmw(ocelot, QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE, + QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE, + QSYS_TAS_PARAM_CFG_CTRL); +@@ -2629,6 +2632,91 @@ static const struct ocelot_ops vsc9959_ops = { + .tas_guard_bands_update = vsc9959_tas_guard_bands_update, + }; + ++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]; ++ u8 preemptible_tcs = fpcmd->preemptible_queues_mask; ++ struct ethtool_mm_cfg cfg; ++ int ret; ++ ++ if (!fpcmd->disabled && ++ (fpcmd->min_frag_size < 60 || fpcmd->min_frag_size > 252)) ++ return -EINVAL; ++ ++ cfg.tx_min_frag_size = fpcmd->min_frag_size; ++ ++ if (!fpcmd->disabled) { ++ ocelot_port->fp_enabled_admin = 1; ++ cfg.pmac_enabled = 1; ++ cfg.tx_enabled = 1; ++ } else { ++ ocelot_port->fp_enabled_admin = 0; ++ cfg.pmac_enabled = 0; ++ cfg.tx_enabled = 0; ++ } ++ ++ cfg.verify_enabled = fpcmd->fp_enabled; ++ cfg.verify_time = 0xa; ++ ++ ret = ocelot_port_set_mm(ocelot, port, &cfg, NULL); ++ if (ret) ++ return ret; ++ ++ mutex_lock(&ocelot->fwd_domain_lock); ++ ret = ocelot_port_change_fp(ocelot, port, preemptible_tcs); ++ mutex_unlock(&ocelot->fwd_domain_lock); ++ ++ return ret; ++} ++ ++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_STAT_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), +@@ -2652,6 +2740,9 @@ static const struct felix_info felix_info_vsc9959 = { + .port_modes = vsc9959_port_modes, + .port_setup_tc = vsc9959_port_setup_tc, + .port_sched_speed_set = vsc9959_sched_speed_set, ++ .port_set_preempt = vsc9959_port_set_preempt, ++ .port_get_preempt = vsc9959_port_get_preempt, ++ .port_preempt_reset = vsc9959_port_preempt_reset, + }; + + /* The INTB interrupt is shared between for PTP TX timestamp availability +diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h +index 8c66d3bf61f0..eabc5b90a552 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" + +@@ -255,6 +256,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; +@@ -298,6 +300,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, +@@ -305,6 +308,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 1a367e64bc3b..fd0a3fcdac59 100644 +--- a/drivers/net/dsa/sja1105/sja1105_main.c ++++ b/drivers/net/dsa/sja1105/sja1105_main.c +@@ -862,7 +862,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, +@@ -1255,6 +1255,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); + +@@ -2229,8 +2238,8 @@ static int sja1105_setup_tc_cbs(struct dsa_switch *ds, int port, + * but deduce the port transmit rate from idleslope - sendslope. + */ + port_transmit_rate_kbps = offload->idleslope - offload->sendslope; +- cbs->idle_slope = div_s64(offload->idleslope * BYTES_PER_KBIT, +- port_transmit_rate_kbps); ++ cbs->idle_slope = DIV_ROUND_UP_ULL(offload->idleslope * BYTES_PER_KBIT, ++ port_transmit_rate_kbps); + cbs->send_slope = div_s64(abs(offload->sendslope * BYTES_PER_KBIT), + port_transmit_rate_kbps); + /* Convert the negative values from 64-bit 2's complement +@@ -2275,6 +2284,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 +@@ -2424,6 +2434,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. +@@ -2825,6 +2885,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 d7818710bc02..71073c3a413b 100644 +--- a/drivers/net/dsa/sja1105/sja1105_tas.c ++++ b/drivers/net/dsa/sja1105/sja1105_tas.c +@@ -528,6 +528,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); + } else if (admin->cmd != TAPRIO_CMD_REPLACE) { + return -EOPNOTSUPP; +@@ -578,6 +580,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/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig +index 2a8bdf9d1d68..81c4c7d2ca1a 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.c b/drivers/net/ethernet/freescale/enetc/enetc.c +index 20d6f2f66c4d..7c65a112b0d2 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc.c +@@ -3735,6 +3735,7 @@ static int enetc_bdr_init(struct enetc_ndev_priv *priv, int i, int v_tx_rings) + { + struct enetc_int_vector *v __free(kfree); + struct enetc_bdr *bdr; ++ char name[NAPINAMSIZ]; + int j, err; + + v = kzalloc(struct_size(v, tx_ring, v_tx_rings), GFP_KERNEL); +@@ -3768,7 +3769,10 @@ static int enetc_bdr_init(struct enetc_ndev_priv *priv, int i, int v_tx_rings) + v->rx_dim_en = true; + } + INIT_WORK(&v->rx_dim.work, enetc_rx_dim_work); +- netif_napi_add(priv->ndev, &v->napi, enetc_poll); ++ ++ snprintf(name, NAPINAMSIZ, "rxtx-%d", i); ++ netif_napi_add_named(priv->ndev, &v->napi, enetc_poll, ++ NAPI_POLL_WEIGHT, name); + v->count_tx_rings = v_tx_rings; + + for (j = 0; j < v_tx_rings; j++) { +diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h +index e6f29cca3fe0..824cffe4cfc4 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc.h ++++ b/drivers/net/ethernet/freescale/enetc/enetc.h +@@ -533,6 +533,8 @@ struct enetc_ndev_priv { + * and link state updates + */ + struct mutex mm_lock; ++ ++ bool fp_enabled_admin; + }; + + #define ENETC_CBD(R, i) (&(((struct enetc_cbd *)((R).bd_base))[i])) +@@ -582,6 +584,8 @@ void enetc_refresh_vlan_ht_filter(struct enetc_si *si); + void enetc_set_ethtool_ops(struct net_device *ndev); + void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link); + void enetc_mm_commit_preemptible_tcs(struct enetc_ndev_priv *priv); ++int enetc_preempt_reset(struct net_device *ndev, bool enable); ++int enetc_pmac_reset(struct net_device *ndev, bool enable); + void enetc_eee_mode_set(struct net_device *dev, bool enable); + + /* control buffer descriptor ring (CBDR) */ +@@ -802,6 +806,7 @@ 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_ptp_clock_update(void); + + #else + +@@ -813,6 +818,9 @@ static inline void enetc_tsn_pf_deinit(struct net_device *netdev) + { + } + ++static inline void enetc_ptp_clock_update(void) ++{ ++} + #endif + + #if IS_ENABLED(CONFIG_DEBUG_FS) +diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +index 8e63dff9d4ec..97a675c6d9ef 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +@@ -8,6 +8,8 @@ + #include + #include "enetc_pf.h" + ++static void enetc_configure_port_pmac(struct enetc_hw *hw, bool enable); ++ + static const u32 enetc_si_regs[] = { + ENETC_SIMR, ENETC_SIPMAR0, ENETC_SIPMAR1, ENETC_SICBDRMR, + ENETC_SICBDRSR, ENETC_SICBDRBAR0, ENETC_SICBDRBAR1, ENETC_SICBDRPIR, +@@ -1313,6 +1315,27 @@ static int enetc_set_rxfh(struct net_device *ndev, const u32 *indir, + return err; + } + ++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 *ch) + { +@@ -1754,6 +1777,34 @@ static int enetc_get_mm(struct net_device *ndev, struct ethtool_mm_state *state) + return 0; + } + ++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); ++ ++ 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_mm_wait_tx_active(struct enetc_hw *hw, int verify_time) + { + int timeout = verify_time * USEC_PER_MSEC * ENETC_MM_VERIFY_RETRIES; +@@ -2051,6 +2102,105 @@ void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link) + } + EXPORT_SYMBOL_GPL(enetc_mm_link_state_update); + ++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_PTCFPR_FPE); ++ else ++ enetc_port_wr(&priv->si->hw, ++ ENETC_PTCFPR(i), ++ temp & ~ENETC_PTCFPR_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); ++ } ++ ++ 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 | +@@ -2087,6 +2237,9 @@ static const struct ethtool_ops enetc_pf_ethtool_ops = { + .get_mm = enetc_get_mm, + .set_mm = enetc_set_mm, + .get_mm_stats = enetc_get_mm_stats, ++ .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_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c +index f9f37e7ee258..d528efd900c5 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c +@@ -634,6 +634,7 @@ static void enetc_pl_mac_link_down(struct phylink_config *config, + enetc_mm_link_state_update(priv, false); + + enetc_mac_enable(si, 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_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c +index 582ddbff1c94..441bce3dfef8 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c +@@ -2403,10 +2403,9 @@ static int enetc_config_clsflower(struct enetc_ndev_priv *priv, + err = enetc_psfp_parse_clsflower(priv, cls_flower); + else + err = enetc4_psfp_parse_clsflower(priv, cls_flower); +- if (err) { +- NL_SET_ERR_MSG_MOD(extack, "Invalid PSFP inputs"); ++ ++ if (err) + return err; +- } + } else { + NL_SET_ERR_MSG_MOD(extack, "Unsupported actions"); + return -EOPNOTSUPP; +diff --git a/drivers/net/ethernet/freescale/enetc/enetc_tsn.c b/drivers/net/ethernet/freescale/enetc/enetc_tsn.c +index 2da3055b2703..e0e7fea5fc36 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc_tsn.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_tsn.c +@@ -5,6 +5,13 @@ + + #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; +@@ -86,8 +93,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; +@@ -120,6 +127,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; +@@ -142,8 +150,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; +@@ -151,7 +163,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) { +@@ -160,10 +172,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; +@@ -298,6 +315,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) +@@ -1383,6 +1514,14 @@ static int enetc_qbu_set(struct net_device *ndev, u8 preemptible_tcs) + + enetc_change_preemptible_tcs(priv, preemptible_tcs); + ++ /* Set pMAC step lock */ ++ val = enetc_port_rd(&priv->si->hw, ENETC_PFPMR); ++ enetc_port_wr(&priv->si->hw, ENETC_PFPMR, ++ val | ENETC_PFPMR_PMACE); ++ ++ val = enetc_port_rd(&priv->si->hw, ENETC_MMCSR); ++ enetc_port_wr(&priv->si->hw, ENETC_MMCSR, val | ENETC_MMCSR_ME); ++ + return 0; + } + +@@ -1734,6 +1873,33 @@ static const 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; +@@ -1744,9 +1910,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 b4e819ff34f7..7faa09477396 100644 +--- a/drivers/net/ethernet/freescale/fec.h ++++ b/drivers/net/ethernet/freescale/fec.h +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ + defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ +@@ -334,30 +335,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 1024 /* 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) +@@ -402,9 +403,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 + +@@ -559,20 +565,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; +@@ -622,6 +639,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; +@@ -654,12 +677,19 @@ struct fec_enet_private { + + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_caps; +- spinlock_t tmreg_lock; ++ raw_spinlock_t tmreg_lock; + struct cyclecounter cc; + struct timecounter tc; + 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_mdio; + struct regulator *reg_phy; +@@ -696,9 +726,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..642cfe340930 +--- /dev/null ++++ b/drivers/net/ethernet/freescale/fec_ecat.c +@@ -0,0 +1,3063 @@ ++// 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_c22(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; ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret < 0) ++ return ret; ++ ++ /* 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_read_c45(struct mii_bus *bus, int mii_id, ++ int devad, int regnum) ++{ ++ struct fec_enet_private *fep = bus->priv; ++ struct device *dev = &fep->pdev->dev; ++ int ret = 0, frame_start, frame_op; ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret < 0) ++ return ret; ++ ++ frame_start = FEC_MMFR_ST_C45; ++ ++ /* write address */ ++ writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | ++ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | ++ 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; ++ ++ /* start a read op */ ++ writel(frame_start | frame_op | ++ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | ++ 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_c22(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; ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret < 0) ++ return ret; ++ ++ /* 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"); ++ ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); ++ ++ return ret; ++} ++ ++static int fec_enet_mdio_write_c45(struct mii_bus *bus, int mii_id, ++ int devad, int regnum, u16 value) ++{ ++ struct fec_enet_private *fep = bus->priv; ++ struct device *dev = &fep->pdev->dev; ++ int ret, frame_start; ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret < 0) ++ return ret; ++ ++ frame_start = FEC_MMFR_ST_C45; ++ ++ /* write address */ ++ writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | ++ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | ++ 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; ++ } ++ ++ /* start a write op */ ++ writel(frame_start | FEC_MMFR_OP_WRITE | ++ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | ++ 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_c22; ++ fep->mii_bus->write = fec_enet_mdio_write_c22; ++ if (fep->quirks & FEC_QUIRK_HAS_MDIO_C45) { ++ fep->mii_bus->read_c45 = fec_enet_mdio_read_c45; ++ fep->mii_bus->write_c45 = fec_enet_mdio_write_c45; ++ } ++ 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..e8235a0978a8 +--- /dev/null ++++ b/drivers/net/ethernet/freescale/fec_ecat.h +@@ -0,0 +1,718 @@ ++// 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 BIT(23) ++ ++/* Not all FEC hardware block MDIOs support accesses in C45 mode. ++ * Older blocks in the ColdFire parts do not support it. ++ */ ++#define FEC_QUIRK_HAS_MDIO_C45 BIT(24) ++ ++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 fb0497a4cd79..be3f9740a0b6 100644 +--- a/drivers/net/ethernet/freescale/fec_main.c ++++ b/drivers/net/ethernet/freescale/fec_main.c +@@ -79,14 +79,22 @@ + + 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 fec_enet_private *fep, + int cpu, struct xdp_buff *xdp, + u32 dma_sync_len); ++#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 +@@ -357,6 +365,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) + { +@@ -488,6 +514,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].buf_p = (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, +@@ -697,7 +970,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; + } +@@ -914,6 +1192,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 +@@ -938,6 +1221,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. */ +@@ -961,6 +1247,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]; +@@ -970,6 +1273,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].buf_p) { ++ skb = NULL; ++ if (fep->avb_enabled) { ++ struct avb_tx_desc *desc = (struct avb_tx_desc *)txq->tx_buf[i].buf_p; ++ ++ 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].buf_p; ++ ++ if (skb) { ++ dev_kfree_skb_any(skb); ++ } ++ ++ txq->tx_buf[i].buf_p = 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].buf_p) { ++ dev_kfree_skb_any(txq->tx_buf[i].buf_p); ++ txq->tx_buf[i].buf_p = 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))) +@@ -998,6 +1337,7 @@ static void fec_enet_bd_init(struct net_device *dev) + txq->tx_buf[i].buf_p = NULL; + /* restore default tx buffer type: FEC_TXBUF_T_SKB */ + 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); + } +@@ -1023,6 +1363,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++) { +@@ -1041,10 +1382,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); + } + + /* +@@ -1064,13 +1420,7 @@ fec_restart(struct net_device *ndev) + * For i.MX6SX SOC, enet use AXI bus, we use disable MAC + * instead of reset MAC itself. + */ +- 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, +@@ -1227,9 +1577,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); +@@ -1319,17 +1666,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(FEC_ECR_RESET, 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); +@@ -1374,7 +1718,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); +@@ -1389,14 +1741,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].buf_p; ++ ++ 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].buf_p = 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, int budget) + { +@@ -1512,6 +2044,11 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id, int budget) + 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); + } + +@@ -1578,6 +2115,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 cpu) +@@ -1619,29 +2157,299 @@ fec_enet_run_xdp(struct fec_enet_private *fep, struct bpf_prog *prog, + goto xdp_err; + } + +- ret = FEC_ENET_XDP_TX; +- break; ++ ret = FEC_ENET_XDP_TX; ++ break; ++ ++ default: ++ bpf_warn_invalid_xdp_action(fep->netdev, prog, act); ++ fallthrough; ++ ++ case XDP_ABORTED: ++ fallthrough; /* handle aborts by dropping packet */ ++ ++ case XDP_DROP: ++ rxq->stats[RX_XDP_DROP]++; ++xdp_err: ++ ret = FEC_ENET_XDP_CONSUMED; ++ page = virt_to_head_page(xdp->data); ++ page_pool_put_page(rxq->page_pool, page, sync, true); ++ if (act != XDP_DROP) ++ trace_xdp_exception(fep->netdev, prog, act); ++ 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; ++} + +- default: +- bpf_warn_invalid_xdp_action(fep->netdev, prog, act); +- fallthrough; ++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; + +- case XDP_ABORTED: +- fallthrough; /* handle aborts by dropping packet */ ++ for (queue_id = 0; queue_id < fep->num_rx_queues; queue_id++) ++ rc |= fec_enet_rx_queue_avb(ndev, queue_id); + +- case XDP_DROP: +- rxq->stats[RX_XDP_DROP]++; +-xdp_err: +- ret = FEC_ENET_XDP_CONSUMED; +- page = virt_to_head_page(xdp->data); +- page_pool_put_page(rxq->page_pool, page, sync, true); +- if (act != XDP_DROP) +- trace_xdp_exception(fep->netdev, prog, act); +- break; ++ 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; ++ ++ rc = fec_enet_rx_avb(ndev); ++ ++ 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 +@@ -1664,11 +2472,13 @@ 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; + int cpu = smp_processor_id(); + struct xdp_buff xdp; ++#endif ++ u32 data_start = FEC_ENET_XDP_HEADROOM; + struct page *page; + u32 sub_len = 4; + +@@ -1691,7 +2501,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)) { + +@@ -1741,6 +2553,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 */ +@@ -1751,6 +2564,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 +@@ -1799,10 +2613,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))) { +@@ -1853,8 +2673,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; + } +@@ -1895,6 +2717,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); +@@ -1911,10 +2737,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, budget); +- } 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, budget); ++ } while ((done < budget) && fec_enet_collect_events(fep)); ++ } + + if (done < budget) { + napi_complete_done(napi, done); +@@ -2031,12 +2865,34 @@ 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) { + netif_stop_queue(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); +@@ -2046,7 +2902,15 @@ static void fec_enet_adjust_link(struct net_device *ndev) + netif_stop_queue(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_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; +@@ -2420,6 +3284,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; + +@@ -3291,24 +4161,93 @@ static void fec_enet_free_buffers(struct net_device *ndev) + unsigned int i; + struct fec_enet_priv_tx_q *txq; + struct fec_enet_priv_rx_q *rxq; ++#ifdef CONFIG_AVB_SUPPORT ++ struct sk_buff *skb; ++#endif + unsigned int q; + + 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].buf_p) { ++ skb = NULL; ++ if (fep->avb_enabled) { ++ struct avb_tx_desc *desc = (struct avb_tx_desc *)txq->tx_buf[i].buf_p; ++ ++ 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].buf_p; ++ } ++ ++ if (skb) ++ dev_kfree_skb(skb); ++ ++ txq->tx_buf[i].buf_p = 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; +@@ -3331,6 +4270,7 @@ static void fec_enet_free_buffers(struct net_device *ndev) + txq->tx_buf[i].buf_p = NULL; + txq->tx_buf[i].type = FEC_TXBUF_T_SKB; + } ++#endif + } + } + +@@ -3370,11 +4310,18 @@ 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; ++ ++ txq->tx_wake_threshold = ++ (txq->bd.ring_size - txq->tx_stop_threshold) / 2; ++#else + txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; + txq->tx_wake_threshold = FEC_MAX_SKB_DESCS + 2 * MAX_SKB_FRAGS; ++#endif + + txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, + txq->bd.ring_size * TSO_HEADER_SIZE, +@@ -3394,7 +4341,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; +@@ -3415,32 +4362,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. */ +@@ -3463,17 +4455,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); +@@ -3482,6 +4485,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; + +@@ -3511,15 +4515,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 +@@ -3562,6 +4582,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 & +@@ -3572,12 +4598,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; + } + +@@ -3591,9 +4628,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; + +@@ -3602,7 +4650,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); + +@@ -3613,6 +4663,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; + } + +@@ -3769,6 +4824,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); +@@ -3970,6 +5026,7 @@ static int fec_enet_xdp_xmit(struct net_device *dev, + + return sent_frames; + } ++#endif /* !CONFIG_AVB_SUPPORT */ + + static int fec_hwtstamp_get(struct net_device *ndev, + struct kernel_hwtstamp_config *config) +@@ -4013,8 +5070,11 @@ static const struct net_device_ops fec_netdev_ops = { + .ndo_set_mac_address = fec_set_mac_address, + .ndo_eth_ioctl = phy_do_ioctl_running, + .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 + .ndo_hwtstamp_get = fec_hwtstamp_get, + .ndo_hwtstamp_set = fec_hwtstamp_set, + }; +@@ -4027,6 +5087,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. + * +@@ -4111,10 +5361,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; +@@ -4132,8 +5386,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; + } + +@@ -4144,9 +5403,11 @@ static int fec_enet_init(struct net_device *ndev) + + ndev->hw_features = ndev->features; + ++#ifndef CONFIG_AVB_SUPPORT + if (!(fep->quirks & FEC_QUIRK_SWAP_FRAME)) + ndev->xdp_features = NETDEV_XDP_ACT_BASIC | + NETDEV_XDP_ACT_REDIRECT; ++#endif + + fec_restart(ndev); + +@@ -4469,6 +5730,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; +@@ -4518,12 +5786,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; +@@ -4566,7 +5854,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; +@@ -4679,6 +5969,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); +@@ -4777,6 +6071,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); + if (fep->phy_reset_in_suspend) +diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c +index 92fc03f29ca1..88215a644d82 100644 +--- a/drivers/net/ethernet/freescale/fec_ptp.c ++++ b/drivers/net/ethernet/freescale/fec_ptp.c +@@ -104,16 +104,22 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) + struct timespec64 ts; + u64 ns; + +- spin_lock_irqsave(&fep->tmreg_lock, flags); ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + + if (fep->pps_enable == enable) { +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + return 0; + } + + fep->pps_channel = DEFAULT_PPS_CHANNEL; + fep->reload_period = PPS_OUPUT_RELOAD_PERIOD; + ++#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. + */ +@@ -197,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; + } +@@ -208,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); +@@ -231,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; + } + +@@ -259,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; + } +@@ -274,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 +@@ -288,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); + } + + /** +@@ -315,29 +341,315 @@ 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 ++ ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++} ++ ++#ifdef CONFIG_AVB_SUPPORT ++/** ++ * fec_ptp_adjfreq - adjust ptp cycle frequency ++ * @ptp: the ptp clock structure ++ * @scaled_ppm: scaled parts per million adjustment from base ++ * ++ * Adjust the frequency of the ptp cycle counter by the ++ * indicated amount from the base frequency. ++ * ++ * Scaled parts per million is ppm with a 16-bit binary fractional field. ++ * ++ * 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_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) ++{ ++ struct fec_enet_private *fep = ++ container_of(ptp, struct fec_enet_private, ptp_caps); ++ s32 ppb = scaled_ppm_to_ppb(scaled_ppm); ++ 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 ++ } + +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ 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_adjfine - adjust ptp cycle frequency + * @ptp: the ptp clock structure +@@ -399,7 +711,7 @@ static int fec_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) + 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; +@@ -409,7 +721,7 @@ static int fec_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) + /* 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; + } +@@ -427,27 +739,30 @@ 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); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + return 0; + } + + /** +- * fec_ptp_gettime ++ * fec_ptp_settime + * @ptp: the ptp clock structure +- * @ts: timespec structure to hold the current time value ++ * @ts: the timespec containing the new time for the cycle counter + * +- * read the timecounter and return the correct value on ns, +- * after converting it into a struct timespec. ++ * reset the timecounter to use a new base value instead of the kernel ++ * wall timer value. + */ +-static int fec_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) ++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; ++ u32 counter; + + mutex_lock(&fep->ptp_clk_mutex); + /* Check the ptp clock */ +@@ -455,33 +770,37 @@ static int fec_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) + mutex_unlock(&fep->ptp_clk_mutex); + return -EINVAL; + } +- spin_lock_irqsave(&fep->tmreg_lock, flags); +- ns = timecounter_read(&fep->tc); +- spin_unlock_irqrestore(&fep->tmreg_lock, flags); +- mutex_unlock(&fep->ptp_clk_mutex); + +- *ts = ns_to_timespec64(ns); ++ ns = timespec64_to_ns(ts); ++ /* Get the timer value based on timestamp. ++ * Update the counter with the masked value. ++ */ ++ counter = ns & fep->cc.mask; + ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); ++ writel(counter, fep->hwp + FEC_ATIME); ++ timecounter_init(&fep->tc, &fep->cc, ns); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ mutex_unlock(&fep->ptp_clk_mutex); + return 0; + } ++#endif /* CONFIG_AVB_SUPPORT */ + + /** +- * fec_ptp_settime ++ * fec_ptp_gettime + * @ptp: the ptp clock structure +- * @ts: the timespec containing the new time for the cycle counter ++ * @ts: timespec structure to hold the current time value + * +- * reset the timecounter to use a new base value instead of the kernel +- * wall timer value. ++ * read the timecounter and return the correct value on ns, ++ * after converting it into a struct timespec. + */ +-static int fec_ptp_settime(struct ptp_clock_info *ptp, +- const struct timespec64 *ts) ++static int fec_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) + { + struct fec_enet_private *fep = + container_of(ptp, struct fec_enet_private, ptp_caps); +- + u64 ns; ++ u32 remainder; + unsigned long flags; +- u32 counter; + + mutex_lock(&fep->ptp_clk_mutex); + /* Check the ptp clock */ +@@ -489,18 +808,14 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp, + mutex_unlock(&fep->ptp_clk_mutex); + return -EINVAL; + } ++ raw_spin_lock_irqsave(&fep->tmreg_lock, flags); ++ ns = timecounter_read(&fep->tc); ++ raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); ++ mutex_unlock(&fep->ptp_clk_mutex); + +- ns = timespec64_to_ns(ts); +- /* Get the timer value based on timestamp. +- * Update the counter with the masked value. +- */ +- counter = ns & fep->cc.mask; ++ ts->tv_sec = div_u64_rem(ns, 1000000000ULL, &remainder); ++ ts->tv_nsec = remainder; + +- 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); +- mutex_unlock(&fep->ptp_clk_mutex); + return 0; + } + +@@ -508,9 +823,9 @@ 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; + } +@@ -574,10 +889,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 */ +@@ -662,9 +977,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); + +@@ -680,6 +995,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) +@@ -699,6 +1017,7 @@ static irqreturn_t fec_pps_interrupt(int irq, void *dev_id) + return IRQ_HANDLED; + } + ++exit: + return IRQ_NONE; + } + +@@ -722,7 +1041,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; +@@ -734,14 +1064,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 17049d99c3e7..3503c3142dec 100644 +--- a/drivers/net/ethernet/freescale/fec_uio.c ++++ b/drivers/net/ethernet/freescale/fec_uio.c +@@ -1249,8 +1249,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 7a15b9245698..4d2fba3b2dd2 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 +@@ -168,6 +169,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) + { +@@ -1491,6 +1518,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 a8f59cd1b4f6..857705fb85f3 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 b31bd1b0461b..2967d09905e6 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: +@@ -471,7 +472,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); + +@@ -985,6 +994,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(); +@@ -993,6 +1045,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 14015cf6a9a6..0834a5d8265f 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: +@@ -610,10 +610,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++; +@@ -637,6 +658,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..74a77460fecf +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ethercat.c +@@ -0,0 +1,1216 @@ ++// 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_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/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c +index 62733d7e139f..f26026b5a511 100644 +--- a/drivers/net/ethernet/mscc/ocelot.c ++++ b/drivers/net/ethernet/mscc/ocelot.c +@@ -247,6 +247,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); +@@ -380,6 +386,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); +@@ -390,12 +398,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) { +@@ -407,9 +420,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); + } + } +@@ -454,6 +474,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; + +@@ -461,8 +485,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 +@@ -620,6 +644,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, +diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c +index 33b438c6aec5..0fdcefeed2ef 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_ULL(FLOW_DISSECTOR_KEY_META) | + BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) | + BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) | ++ BIT_ULL(FLOW_DISSECTOR_KEY_CVLAN) | + BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS))) { +@@ -627,6 +635,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..e1ca9d1081e6 100644 +--- a/drivers/net/ethernet/mscc/ocelot_ptp.c ++++ b/drivers/net/ethernet/mscc/ocelot_ptp.c +@@ -14,6 +14,8 @@ + #include + #include "ocelot.h" + ++#define OCELOT_PTP_TX_TSTAMP_TIMEOUT (5 * HZ) ++ + int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts) + { + struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info); +@@ -325,6 +327,7 @@ static void + ocelot_populate_ipv4_ptp_event_trap_key(struct ocelot_vcap_filter *trap) + { + trap->key_type = OCELOT_VCAP_KEY_IPV4; ++ trap->key.ipv4.fragment = OCELOT_VCAP_BIT_0; + trap->key.ipv4.proto.value[0] = IPPROTO_UDP; + trap->key.ipv4.proto.mask[0] = 0xff; + trap->key.ipv4.dport.value = PTP_EV_PORT; +@@ -345,6 +348,7 @@ static void + ocelot_populate_ipv4_ptp_general_trap_key(struct ocelot_vcap_filter *trap) + { + trap->key_type = OCELOT_VCAP_KEY_IPV4; ++ trap->key.ipv4.fragment = OCELOT_VCAP_BIT_0; + trap->key.ipv4.proto.value[0] = IPPROTO_UDP; + trap->key.ipv4.proto.mask[0] = 0xff; + trap->key.ipv4.dport.value = PTP_GEN_PORT; +@@ -532,6 +536,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 +@@ -607,34 +612,91 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port, + } + EXPORT_SYMBOL(ocelot_get_ts_info); + +-static int ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, +- struct sk_buff *clone) ++static struct sk_buff * ++ocelot_port_remove_ptp_tx_skb(struct ocelot_port *ocelot_port, u8 ts_id) + { +- struct ocelot_port *ocelot_port = ocelot->ports[port]; ++ struct sk_buff *skb, *skb_tmp, *skb_match = NULL; + unsigned long flags; + +- spin_lock_irqsave(&ocelot->ts_id_lock, flags); ++ spin_lock_irqsave(&ocelot_port->tx_skbs.lock, flags); + +- if (ocelot_port->ptp_skbs_in_flight == OCELOT_MAX_PTP_ID || +- ocelot->ptp_skbs_in_flight == OCELOT_PTP_FIFO_SIZE) { +- spin_unlock_irqrestore(&ocelot->ts_id_lock, flags); +- return -EBUSY; ++ skb_queue_walk_safe(&ocelot_port->tx_skbs, skb, skb_tmp) { ++ if (OCELOT_SKB_CB(skb)->ts_id != ts_id) ++ continue; ++ __skb_unlink(skb, &ocelot_port->tx_skbs); ++ skb_match = skb; ++ break; + } + +- skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; +- /* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */ +- OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id; ++ spin_unlock_irqrestore(&ocelot_port->tx_skbs.lock, flags); + +- ocelot_port->ts_id++; +- if (ocelot_port->ts_id == OCELOT_MAX_PTP_ID) +- ocelot_port->ts_id = 0; ++ return skb_match; ++} + +- ocelot_port->ptp_skbs_in_flight++; ++static int ocelot_port_ts_id_get(struct ocelot *ocelot, int port, u8 *ts_id) ++{ ++ struct ocelot_port *ocelot_port = ocelot->ports[port]; ++ unsigned long n; ++ ++ /* To get a better chance of acquiring a timestamp ID, first flush the ++ * stale packets still waiting in the TX timestamping queue. They are ++ * probably lost. ++ */ ++ for_each_set_bit(n, ocelot_port->ts_id_in_flight, OCELOT_MAX_PTP_ID) { ++ if (time_before(ocelot_port->ptp_tx_time[n] + ++ OCELOT_PTP_TX_TSTAMP_TIMEOUT, jiffies)) { ++ dev_warn_ratelimited(ocelot->dev, ++ "port %d invalidating stale timestamp ID %lu which seems lost\n", ++ port, n); ++ __clear_bit(n, ocelot_port->ts_id_in_flight); ++ ocelot->ptp_skbs_in_flight--; ++ ocelot_port_remove_ptp_tx_skb(ocelot_port, n); ++ } ++ } ++ ++ if (ocelot->ptp_skbs_in_flight == OCELOT_PTP_FIFO_SIZE) ++ return -EBUSY; ++ ++ n = find_first_zero_bit(ocelot_port->ts_id_in_flight, ++ OCELOT_MAX_PTP_ID); ++ if (n == OCELOT_MAX_PTP_ID) ++ return -EBUSY; ++ ++ /* Found an available timestamp ID, use it */ ++ __set_bit(n, ocelot_port->ts_id_in_flight); ++ *ts_id = n; ++ ocelot_port->ptp_tx_time[n] = jiffies; + ocelot->ptp_skbs_in_flight++; ++ dev_dbg_ratelimited(ocelot->dev, "port %d timestamp id %lu\n", port, n); + +- skb_queue_tail(&ocelot_port->tx_skbs, clone); ++ return 0; ++} ++ ++static void ocelot_port_ts_id_put(struct ocelot *ocelot, int port, int ts_id) ++{ ++ struct ocelot_port *ocelot_port = ocelot->ports[port]; ++ ++ __clear_bit(ts_id, ocelot_port->ts_id_in_flight); ++ ocelot->ptp_skbs_in_flight--; ++} + ++static int ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, ++ struct sk_buff *clone) ++{ ++ struct ocelot_port *ocelot_port = ocelot->ports[port]; ++ unsigned long flags; ++ int err; ++ ++ /* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */ ++ spin_lock_irqsave(&ocelot->ts_id_lock, flags); ++ err = ocelot_port_ts_id_get(ocelot, port, &OCELOT_SKB_CB(clone)->ts_id); + spin_unlock_irqrestore(&ocelot->ts_id_lock, flags); ++ if (err) ++ return err; ++ ++ skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; ++ ++ skb_queue_tail(&ocelot_port->tx_skbs, clone); + + return 0; + } +@@ -746,12 +808,11 @@ void ocelot_get_txtstamp(struct ocelot *ocelot) + int budget = OCELOT_PTP_QUEUE_SZ; + + while (budget--) { +- struct sk_buff *skb, *skb_tmp, *skb_match = NULL; + struct skb_shared_hwtstamps shhwtstamps; + u32 val, id, seqid, txport; ++ struct sk_buff *skb_match; + struct ocelot_port *port; + struct timespec64 ts; +- unsigned long flags; + + val = ocelot_read(ocelot, SYS_PTP_STATUS); + +@@ -769,32 +830,24 @@ void ocelot_get_txtstamp(struct ocelot *ocelot) + port = ocelot->ports[txport]; + + spin_lock(&ocelot->ts_id_lock); +- port->ptp_skbs_in_flight--; +- ocelot->ptp_skbs_in_flight--; ++ ocelot_port_ts_id_put(ocelot, txport, id); + spin_unlock(&ocelot->ts_id_lock); + + /* Retrieve its associated skb */ + try_again: +- spin_lock_irqsave(&port->tx_skbs.lock, flags); +- +- skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { +- if (OCELOT_SKB_CB(skb)->ts_id != id) +- continue; +- __skb_unlink(skb, &port->tx_skbs); +- skb_match = skb; +- break; +- } +- +- spin_unlock_irqrestore(&port->tx_skbs.lock, flags); +- +- if (WARN_ON(!skb_match)) ++ skb_match = ocelot_port_remove_ptp_tx_skb(port, id); ++ if (!skb_match) { ++ dev_warn_ratelimited(ocelot->dev, ++ "port %d received TX timestamp (seqid %d, ts id %u) for packet previously declared stale\n", ++ txport, seqid, id); + continue; ++ } + + if (!ocelot_validate_ptp_skb(skb_match, seqid)) { + dev_err_ratelimited(ocelot->dev, +- "port %d received stale TX timestamp for seqid %d, discarding\n", +- txport, seqid); +- dev_kfree_skb_any(skb); ++ "port %d received stale TX timestamp (seqid %d, ts id %u), discarding\n", ++ txport, seqid, id); ++ dev_kfree_skb_any(skb_match); + goto try_again; + } + +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/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +index a9837985a483..419912680e56 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +@@ -1244,6 +1244,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, +@@ -1297,6 +1298,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 8fd167501fa0..74825f602482 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c +@@ -712,10 +712,15 @@ void dwmac5_est_irq_status(void __iomem *ioaddr, struct net_device *dev, + + void dwmac5_fpe_configure(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg, + 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) { + cfg->fpe_csr = EFPE; + value = readl(ioaddr + GMAC_RXQ_CTRL1); +@@ -728,6 +733,18 @@ void dwmac5_fpe_configure(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg, + writel(cfg->fpe_csr, 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 34e620790eb3..344aad5f3ee1 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) +@@ -155,7 +158,8 @@ 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, struct stmmac_fpe_cfg *cfg, + 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, + struct stmmac_fpe_cfg *cfg, + enum stmmac_mpacket_type type); +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +index 052566f5b7f3..4dd0de102ced 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +@@ -1558,7 +1558,8 @@ static int dwxgmac3_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg, + + static void dwxgmac3_fpe_configure(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg, + 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 47fb8e1646c2..610b90a31be2 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h ++++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h +@@ -296,6 +296,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 */ +@@ -414,7 +420,8 @@ struct stmmac_ops { + struct stmmac_extra_stats *x, u32 txqcnt); + void (*fpe_configure)(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg, + 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, + struct stmmac_fpe_cfg *cfg, + enum stmmac_mpacket_type type); +@@ -521,6 +528,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 04ac04d805fd..ecc164b5d9b9 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -335,6 +335,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 521b1b5ffebb..892113ef5a06 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 +@@ -1261,6 +1262,108 @@ 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, fpe_cfg, ++ 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, fpe_cfg, ++ 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->fpe_cfg, ++ 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, +@@ -1300,6 +1403,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 e7dcf9b2d3a4..673761ba3507 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -969,6 +969,12 @@ static void stmmac_fpe_link_state_handle(struct stmmac_priv *priv, bool is_up) + } else { + *lo_state = FPE_STATE_OFF; + *lp_state = FPE_STATE_OFF; ++ priv->plat->fpe_cfg->enable = false; ++ stmmac_fpe_configure(priv, priv->ioaddr, ++ fpe_cfg, ++ priv->plat->tx_queues_to_use, ++ priv->plat->rx_queues_to_use, ++ false, NULL); + } + } + +@@ -2455,12 +2461,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; + +@@ -6993,7 +7001,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_get_stats64 = stmmac_get_stats64, + .ndo_setup_tc = stmmac_setup_tc, +@@ -7145,6 +7155,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); + +@@ -7156,16 +7167,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); + } + } + } +@@ -7251,7 +7268,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) { +@@ -7265,7 +7281,7 @@ static void stmmac_fpe_lp_task(struct work_struct *work) + fpe_cfg, + priv->plat->tx_queues_to_use, + priv->plat->rx_queues_to_use, +- *enable); ++ true, NULL); + + netdev_info(priv->dev, "configured FPE\n"); + +@@ -7773,7 +7789,7 @@ int stmmac_suspend(struct device *dev) + stmmac_fpe_configure(priv, priv->ioaddr, + priv->plat->fpe_cfg, + 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 77245f856dd0..176edb91f40f 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +@@ -923,7 +923,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; + +@@ -995,6 +995,8 @@ static int tc_setup_taprio(struct stmmac_priv *priv, + priv->plat->est->enable = qopt->cmd == TAPRIO_CMD_REPLACE; + 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; +@@ -1006,16 +1008,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; +@@ -1042,16 +1046,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); +@@ -1062,11 +1056,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: +@@ -1078,17 +1067,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->fpe_cfg, +- 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/phy/phy.c b/drivers/net/phy/phy.c +index 95a9c35e293c..6eff60426a08 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: \ +@@ -1544,7 +1545,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/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..4b85b12aa7ff +--- /dev/null ++++ b/drivers/nfc/pn5xx/pn5xx_i2c.c +@@ -0,0 +1,669 @@ ++// 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; ++ 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(node, "firmware-gpios", 0); ++ 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(node, "nxp,pn54x-clkreq", 0); ++ 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) ++#else ++static int pn54x_probe(struct i2c_client *client) ++#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/pmdomain/imx/scu-pd.c b/drivers/pmdomain/imx/scu-pd.c +index 38f3cdd21042..97d1a537917a 100644 +--- a/drivers/pmdomain/imx/scu-pd.c ++++ b/drivers/pmdomain/imx/scu-pd.c +@@ -171,6 +171,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/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c +index 91cc6ffa0095..f32e633a3f65 100644 +--- a/drivers/ptp/ptp_chardev.c ++++ b/drivers/ptp/ptp_chardev.c +@@ -121,6 +121,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) + struct ptp_clock_time *pct; + unsigned int i, pin_index; + struct ptp_pin_desc pd; ++ struct ptp_convert_timestamps convert_ts; + struct timespec64 ts; + int enable, err = 0; + +@@ -422,6 +423,28 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) + mutex_unlock(&ptp->pincfg_mux); + break; + ++ case PTP_CONVERT_TIMESTAMPS: ++ if (copy_from_user(&convert_ts, (void __user *)arg, sizeof(convert_ts))) { ++ err = -EFAULT; ++ break; ++ } ++ ++ if (convert_ts.n_ts > PTP_MAX_CONVERT_TS_NUM) { ++ err = -EINVAL; ++ break; ++ } ++ ++ if (ptp->is_virtual_clock) ++ err = ptp_vclock_convert_timestamps(ptp, convert_ts.src_ts, convert_ts.n_ts, ++ convert_ts.dst_phc_index, convert_ts.dst_ts); ++ else ++ err = ptp_clock_convert_timestamps(ptp, convert_ts.src_ts, convert_ts.n_ts, ++ convert_ts.dst_phc_index, convert_ts.dst_ts); ++ ++ if (!err && copy_to_user((void __user *)arg, &convert_ts, sizeof(convert_ts))) ++ err = -EFAULT; ++ ++ break; + default: + err = -ENOTTY; + break; +diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c +index 9a50bfb56453..b857cfca55b7 100644 +--- a/drivers/ptp/ptp_clock.c ++++ b/drivers/ptp/ptp_clock.c +@@ -201,6 +201,25 @@ static void ptp_aux_kworker(struct kthread_work *work) + kthread_queue_delayed_work(ptp->kworker, &ptp->aux_work, delay); + } + ++/* Convert from physical to virtual time domains. */ ++int ptp_clock_convert_timestamps(struct ptp_clock *ptp, ++ struct ptp_clock_time *src_ts, unsigned int n_ts, int dst_phc_index, ++ struct ptp_clock_time *dst_ts) ++{ ++ int rc; ++ ++ /* If the destination time domain is the same as the source, quick return. */ ++ if (ptp->index == dst_phc_index) { ++ memcpy(dst_ts, src_ts, n_ts * sizeof(struct ptp_clock_time)); ++ return 0; ++ } ++ ++ rc = ptp_vclock_convert_from_hw_timestamps(ptp, src_ts, n_ts, ++ dst_phc_index, dst_ts); ++ ++ return rc; ++} ++ + /* public interface */ + + struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, +diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h +index b8d4f61f14be..03136915423f 100644 +--- a/drivers/ptp/ptp_private.h ++++ b/drivers/ptp/ptp_private.h +@@ -141,6 +141,24 @@ extern const struct attribute_group *ptp_groups[]; + int ptp_populate_pin_groups(struct ptp_clock *ptp); + void ptp_cleanup_pin_groups(struct ptp_clock *ptp); + ++/* ++ * see ptp_vclock.c ++ */ ++ + struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock); + void ptp_vclock_unregister(struct ptp_vclock *vclock); ++int ptp_vclock_convert_from_hw_timestamps(struct ptp_clock *ptp, ++ struct ptp_clock_time *src_ts, unsigned int n_ts, int dst_phc_index, ++ struct ptp_clock_time *dst_ts); ++int ptp_vclock_convert_timestamps(struct ptp_clock *ptp, struct ptp_clock_time *src_ts, ++ unsigned int n_ts, int dst_phc_index, ++ struct ptp_clock_time *dst_ts); ++ ++/* ++ * see ptp_clock.c ++ */ ++ ++int ptp_clock_convert_timestamps(struct ptp_clock *ptp, ++ struct ptp_clock_time *src_ts, unsigned int n_ts, int dst_phc_index, ++ struct ptp_clock_time *dst_ts); + #endif +diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c +index aefc06ae5d09..dd762d776de8 100644 +--- a/drivers/ptp/ptp_sysfs.c ++++ b/drivers/ptp/ptp_sysfs.c +@@ -220,14 +220,21 @@ static ssize_t n_vclocks_store(struct device *dev, + + /* Need to create more vclocks */ + if (num > ptp->n_vclocks) { +- for (i = 0; i < num - ptp->n_vclocks; i++) { ++ unsigned int nb_vclocks = num - ptp->n_vclocks; ++ ++ for (i = 0; i < nb_vclocks; i++) { + vclock = ptp_vclock_register(ptp); +- if (!vclock) ++ if (!vclock) { ++ dev_err(dev, "failed to create more than %u (out of %u) virtual clocks for ptp%d\n", ++ i, nb_vclocks, ptp->index); + goto out; ++ } + +- *(ptp->vclock_index + ptp->n_vclocks + i) = ++ *(ptp->vclock_index + ptp->n_vclocks) = + vclock->clock->index; + ++ ptp->n_vclocks++; ++ + dev_info(dev, "new virtual clock ptp%d\n", + vclock->clock->index); + } +@@ -241,6 +248,8 @@ static ssize_t n_vclocks_store(struct device *dev, + + for (i = 1; i <= ptp->n_vclocks - num; i++) + *(ptp->vclock_index + ptp->n_vclocks - i) = -1; ++ ++ ptp->n_vclocks = num; + } + + /* Need to inform about changed physical clock behavior */ +@@ -251,7 +260,6 @@ static ssize_t n_vclocks_store(struct device *dev, + dev_info(dev, "guarantee physical clock free running\n"); + } + +- ptp->n_vclocks = num; + mutex_unlock(&ptp->n_vclocks_mux); + + return count; +diff --git a/drivers/ptp/ptp_vclock.c b/drivers/ptp/ptp_vclock.c +index dcf752c9e045..bb26d2535218 100644 +--- a/drivers/ptp/ptp_vclock.c ++++ b/drivers/ptp/ptp_vclock.c +@@ -40,6 +40,24 @@ static void ptp_vclock_hash_del(struct ptp_vclock *vclock) + synchronize_rcu(); + } + ++/* This function and its return value (the vclock pointer) must be used ++ * inside the same RCU read critical section ++ */ ++static struct ptp_vclock *ptp_vclock_lookup(int vclock_index) ++{ ++ unsigned int hash = vclock_index % HASH_SIZE(vclock_hash); ++ struct ptp_vclock *vclock; ++ ++ hlist_for_each_entry_rcu(vclock, &vclock_hash[hash], vclock_hash_node) { ++ if (vclock->clock->index != vclock_index) ++ continue; ++ ++ return vclock; ++ } ++ ++ return NULL; ++} ++ + static int ptp_vclock_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) + { + struct ptp_vclock *vclock = info_to_vclock(ptp); +@@ -154,6 +172,182 @@ static long ptp_vclock_refresh(struct ptp_clock_info *ptp) + return PTP_VCLOCK_REFRESH_INTERVAL; + } + ++/* Convert from virtual clock to physical time domain */ ++static u64 ptp_vclock_to_hw_time(const struct timecounter *tc, u64 nsec) ++{ ++ u64 ns_hw = tc->cycle_last; ++ u64 delta; ++ ++ /* TODO implement a less costly conversion using a shift/mult rather ++ * than an integer division ? ++ */ ++ if (nsec > tc->nsec) { ++ delta = nsec - tc->nsec; ++ delta <<= tc->cc->shift; ++ ns_hw += div_u64(delta, tc->cc->mult); ++ } else { ++ delta = tc->nsec - nsec; ++ delta <<= tc->cc->shift; ++ ns_hw -= div_u64(delta, tc->cc->mult); ++ } ++ ++ return ns_hw; ++} ++ ++static inline s64 ptp_clock_time_to_ns(const struct ptp_clock_time *ptp_time) ++{ ++ struct timespec64 ts; ++ ++ ts.tv_sec = ptp_time->sec; ++ ts.tv_nsec = ptp_time->nsec; ++ ++ return timespec64_to_ns(&ts); ++} ++ ++static inline struct ptp_clock_time ns_to_ptp_clock_time(s64 nsec) ++{ ++ struct ptp_clock_time ptp_time = { 0 }; ++ struct timespec64 ts; ++ ++ ts = ns_to_timespec64(nsec); ++ ++ ptp_time.sec = ts.tv_sec; ++ ptp_time.nsec = ts.tv_nsec; ++ ++ return ptp_time; ++} ++ ++/* This function converts from a virtual domain to a destination ++ * clock domain (either virtual or physical) ++ */ ++int ptp_vclock_convert_timestamps(struct ptp_clock *ptp, struct ptp_clock_time *src_ts, ++ unsigned int n_ts, int dst_phc_index, ++ struct ptp_clock_time *dst_ts) ++{ ++ unsigned int hash = dst_phc_index % HASH_SIZE(vclock_hash); ++ struct ptp_vclock *vclock = info_to_vclock(ptp->info); ++ struct ptp_vclock *vclock_dst; ++ int i, rc = 0; ++ u64 dst_ns; ++ ++ /* The destination clock domain is the same as the source, early exit. */ ++ if (dst_phc_index == vclock->clock->index) { ++ memcpy(dst_ts, src_ts, n_ts * sizeof(struct ptp_clock_time)); ++ goto out; ++ } ++ ++ rcu_read_lock(); ++ ++ vclock_dst = ptp_vclock_lookup(dst_phc_index); ++ ++ if (vclock_dst) { ++ /* Check that both virtual clocks share the same physical parent. */ ++ if (vclock_dst->pclock != vclock->pclock) { ++ rc = -EINVAL; ++ goto out_unlock_rcu; ++ } ++ ++ if (mutex_lock_interruptible(&vclock->lock)) { ++ rc = -ERESTARTSYS; ++ goto out_unlock_rcu; ++ } ++ ++ if (mutex_lock_interruptible(&vclock_dst->lock)) { ++ mutex_unlock(&vclock->lock); ++ rc = -ERESTARTSYS; ++ goto out_unlock_rcu; ++ } ++ ++ for (i = 0; i < n_ts; i++) { ++ /* Convert from source virtual to physical time domain. */ ++ dst_ns = ptp_vclock_to_hw_time(&vclock->tc, ++ ptp_clock_time_to_ns(src_ts + i)); ++ /* Convert from physical to destination virtual time domain. */ ++ dst_ns = timecounter_cyc2time(&vclock_dst->tc, dst_ns); ++ ++ *(dst_ts + i) = ns_to_ptp_clock_time(dst_ns); ++ } ++ ++ mutex_unlock(&vclock_dst->lock); ++ mutex_unlock(&vclock->lock); ++ } else { ++ /* Check that the destination physical clock is the parent of the source ++ * virtual clock. ++ */ ++ if (vclock->pclock->index != dst_phc_index) { ++ rc = -EINVAL; ++ goto out_unlock_rcu; ++ } ++ ++ if (mutex_lock_interruptible(&vclock->lock)) { ++ rc = -EINTR; ++ goto out_unlock_rcu; ++ } ++ ++ /* Convert from virtual to physical time domain . */ ++ for (i = 0; i < n_ts; i++) { ++ dst_ns = ptp_vclock_to_hw_time(&vclock->tc, ++ ptp_clock_time_to_ns(src_ts + i)); ++ *(dst_ts + i) = ns_to_ptp_clock_time(dst_ns); ++ } ++ ++ mutex_unlock(&vclock->lock); ++ ++ } ++ ++out_unlock_rcu: ++ rcu_read_unlock(); ++ ++out: ++ return rc; ++} ++ ++/* Convert from physical to virtual time domain. */ ++int ptp_vclock_convert_from_hw_timestamps(struct ptp_clock *ptp, struct ptp_clock_time *src_ts, ++ unsigned int n_ts, int dst_vclock_index, ++ struct ptp_clock_time *dst_ts) ++{ ++ unsigned int hash = dst_vclock_index % HASH_SIZE(vclock_hash); ++ struct ptp_vclock *vclock; ++ int i, rc = 0; ++ u64 dst_ns; ++ ++ rcu_read_lock(); ++ ++ vclock = ptp_vclock_lookup(dst_vclock_index); ++ ++ /* Check that dst_vclock_index point to a virtual clock. */ ++ if (!vclock) { ++ rc = -EINVAL; ++ goto out_unlock_rcu; ++ } ++ ++ /* Check that the source physical clock is the parent of the ++ * destination virtual clock. ++ */ ++ if (vclock->pclock != ptp) { ++ rc = -EINVAL; ++ goto out_unlock_rcu; ++ } ++ ++ if (mutex_lock_interruptible(&vclock->lock)) { ++ rc = -ERESTARTSYS; ++ goto out_unlock_rcu; ++ } ++ ++ for (i = 0; i < n_ts; i++) { ++ dst_ns = timecounter_cyc2time(&vclock->tc, ptp_clock_time_to_ns(src_ts + i)); ++ *(dst_ts + i) = ns_to_ptp_clock_time(dst_ns); ++ } ++ ++ mutex_unlock(&vclock->lock); ++ ++out_unlock_rcu: ++ rcu_read_unlock(); ++ ++ return rc; ++} ++ + static const struct ptp_clock_info ptp_vclock_info = { + .owner = THIS_MODULE, + .name = "ptp virtual clock", +@@ -276,17 +470,15 @@ ktime_t ptp_convert_timestamp(const ktime_t *hwtstamp, int vclock_index) + + rcu_read_lock(); + +- hlist_for_each_entry_rcu(vclock, &vclock_hash[hash], vclock_hash_node) { +- if (vclock->clock->index != vclock_index) +- continue; +- ++ vclock = ptp_vclock_lookup(vclock_index); ++ if (vclock) { + if (mutex_lock_interruptible(&vclock->lock)) +- break; ++ goto out_unlock_rcu; + vclock_ns = timecounter_cyc2time(&vclock->tc, ns); + mutex_unlock(&vclock->lock); +- break; + } + ++out_unlock_rcu: + rcu_read_unlock(); + + return ns_to_ktime(vclock_ns); +diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c +index 5e15dd127bde..449ff92129cf 100644 +--- a/drivers/remoteproc/imx_rproc.c ++++ b/drivers/remoteproc/imx_rproc.c +@@ -6,7 +6,9 @@ + #include + #include + #include ++#include + #include ++#include + #include + #include + #include +@@ -20,11 +22,16 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + ++#include ++ ++#include ++ + #include "remoteproc_elf_helpers.h" + #include "remoteproc_internal.h" + +@@ -73,6 +80,8 @@ + #define IMX_SIP_RPROC_STARTED 0x01 + #define IMX_SIP_RPROC_STOP 0x02 + ++#define IMX_SIP_CPU_OFF 0xC200000D ++ + #define IMX_SC_IRQ_GROUP_REBOOTED 5 + + /** +@@ -125,6 +134,8 @@ struct imx_rproc { + struct device_link **pd_dev_link; + u32 startup_delay; + struct sys_off_data data; ++ cpumask_t cpus; ++ cpumask_t offlined_cpus; + }; + + static const struct imx_rproc_att imx_rproc_att_imx95_m7[] = { +@@ -157,6 +168,9 @@ static const struct imx_rproc_att imx_rproc_att_imx93[] = { + { 0x80000000, 0x80000000, 0x10000000, 0 }, + { 0x90000000, 0x80000000, 0x10000000, 0 }, + ++ { 0xa0000000, 0xa0000000, 0x10000000, 0 }, ++ { 0xb0000000, 0xa0000000, 0x10000000, 0 }, ++ + { 0xC0000000, 0xC0000000, 0x10000000, 0 }, + { 0xD0000000, 0xC0000000, 0x10000000, 0 }, + }; +@@ -384,12 +398,102 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx93 = { + .method = IMX_RPROC_SMC, + }; + ++static const struct imx_rproc_dcfg imx_rproc_cfg_psci = { ++ .att = NULL, ++ .att_size = 0, ++ .method = IMX_RPROC_PSCI, ++}; ++ + static const struct imx_rproc_dcfg imx_rproc_cfg_imx95_m7 = { + .att = imx_rproc_att_imx95_m7, + .att_size = ARRAY_SIZE(imx_rproc_att_imx95_m7), + .method = IMX_RPROC_SMC, + }; + ++static int imx_rproc_psci_start(struct rproc *rproc) ++{ ++ struct imx_rproc *priv = rproc->priv; ++ struct device *dev = priv->dev; ++ unsigned int cpu; ++ int ret; ++ ++ if (cpumask_empty(&priv->cpus)) { ++ dev_err(dev, "No CPU Core assigned!\n"); ++ return -ENODEV; ++ } ++ ++ for_each_cpu(cpu, &priv->cpus) { ++ if (cpu_online(cpu)) { ++ ret = remove_cpu(cpu); ++ if (ret) ++ goto err; ++ cpumask_set_cpu(cpu, &priv->offlined_cpus); ++ } ++ } ++ ++ cpu = cpumask_first(&priv->cpus); ++ ret = psci_ops.cpu_on(cpu_logical_map(cpu), rproc->bootaddr); ++ if (ret) { ++ dev_err(dev, "Boot failed on CPU Core %d\n", cpu); ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ for_each_cpu(cpu, &priv->cpus) { ++ if (!cpu_online(cpu) && add_cpu(cpu) == 0) ++ cpumask_clear_cpu(cpu, &priv->offlined_cpus); ++ } ++ ++ return ret; ++} ++ ++static int imx_rproc_psci_stop(struct rproc *rproc) ++{ ++ struct imx_rproc *priv = rproc->priv; ++ struct device *dev = priv->dev; ++ struct arm_smccc_res res; ++ unsigned int cpu; ++ unsigned long start, end; ++ int err; ++ ++ for_each_cpu(cpu, &priv->cpus) { ++ /* Check CPU status */ ++ err = psci_ops.affinity_info(cpu_logical_map(cpu), 0); ++ if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) ++ continue; ++ ++ /* Bring CPU to be off */ ++ arm_smccc_smc(IMX_SIP_CPU_OFF, cpu, 0, ++ 0, 0, 0, 0, 0, &res); ++ start = jiffies; ++ end = start + msecs_to_jiffies(100); ++ do { ++ err = psci_ops.affinity_info(cpu_logical_map(cpu), 0); ++ if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) { ++ pr_info("CPU%d is killed (polled %d ms)\n", cpu, ++ jiffies_to_msecs(jiffies - start)); ++ break; ++ } ++ ++ usleep_range(100, 1000); ++ } while (time_before(jiffies, end)); ++ } ++ ++ /* Return back freed CPU Core to Linux kernel */ ++ for_each_cpu(cpu, &priv->cpus) { ++ if (cpumask_test_cpu(cpu, &priv->offlined_cpus)) { ++ if (add_cpu(cpu) != 0) ++ dev_err(dev, "Failed to bring CPU %d back to be online", cpu); ++ cpumask_clear_cpu(cpu, &priv->offlined_cpus); ++ } ++ } ++ ++ return 0; ++} ++ ++ + static int imx_rproc_start(struct rproc *rproc) + { + struct imx_rproc *priv = rproc->priv; +@@ -424,6 +528,9 @@ static int imx_rproc_start(struct rproc *rproc) + case IMX_RPROC_SCU_API: + ret = imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, true, priv->entry); + break; ++ case IMX_RPROC_PSCI: ++ ret = imx_rproc_psci_start(rproc); ++ break; + default: + return -EOPNOTSUPP; + } +@@ -470,6 +577,9 @@ static int imx_rproc_stop(struct rproc *rproc) + case IMX_RPROC_SCU_API: + ret = imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, false, priv->entry); + break; ++ case IMX_RPROC_PSCI: ++ ret = imx_rproc_psci_stop(rproc); ++ break; + default: + return -EOPNOTSUPP; + } +@@ -488,6 +598,12 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da, + const struct imx_rproc_dcfg *dcfg = priv->dcfg; + int i; + ++ /* No need to translate for Cortex-A Core */ ++ if (dcfg->method == IMX_RPROC_PSCI) { ++ *sys = da; ++ return 0; ++ } ++ + /* parse address translation table */ + for (i = 0; i < dcfg->att_size; i++) { + const struct imx_rproc_att *att = &dcfg->att[i]; +@@ -558,7 +674,13 @@ static int imx_rproc_mem_alloc(struct rproc *rproc, + void *va; + + dev_dbg(dev, "map memory: %p+%zx\n", &mem->dma, mem->len); +- va = ioremap_wc(mem->dma, mem->len); ++ if (dev_is_dma_coherent(dev) && ++ !strncmp(mem->name, "vdev0vring", strlen("vdev0vring"))) { ++ va = ioremap_cache(mem->dma, mem->len); ++ } else { ++ va = ioremap_wc(mem->dma, mem->len); ++ } ++ + if (IS_ERR_OR_NULL(va)) { + dev_err(dev, "Unable to map memory region: %p+%zx\n", + &mem->dma, mem->len); +@@ -588,19 +710,15 @@ static int imx_rproc_prepare(struct rproc *rproc) + struct rproc_mem_entry *mem; + struct reserved_mem *rmem; + u32 da; ++ int index = 0; + + /* Register associated reserved memory regions */ + of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); + while (of_phandle_iterator_next(&it) == 0) { +- /* +- * Ignore the first memory region which will be used vdev buffer. +- * No need to do extra handlings, rproc_add_virtio_dev will handle it. +- */ +- if (!strcmp(it.node->name, "vdev0buffer")) +- continue; +- +- if (!strcmp(it.node->name, "rsc-table")) ++ if (!strcmp(it.node->name, "rsc-table")) { ++ index++; + continue; ++ } + + rmem = of_reserved_mem_lookup(it.node); + if (!rmem) { +@@ -612,19 +730,28 @@ static int imx_rproc_prepare(struct rproc *rproc) + /* No need to translate pa to da, i.MX use same map */ + da = rmem->base; + +- /* Register memory region */ +- mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)rmem->base, rmem->size, da, +- imx_rproc_mem_alloc, imx_rproc_mem_release, +- it.node->name); ++ if (strcmp(it.node->name, "vdev0buffer")) { ++ /* Register memory region */ ++ mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)rmem->base, ++ rmem->size, da, imx_rproc_mem_alloc, ++ imx_rproc_mem_release, it.node->name); + +- if (mem) { +- rproc_coredump_add_segment(rproc, da, rmem->size); ++ if (mem) { ++ rproc_coredump_add_segment(rproc, da, rmem->size); ++ } + } else { ++ ++ /* Register reserved memory for vdev buffer alloc */ ++ mem = rproc_of_resm_mem_entry_init(priv->dev, index, rmem->size, ++ rmem->base, it.node->name); ++ } ++ ++ if (!mem) { + of_node_put(it.node); + return -ENOMEM; + } +- + rproc_add_carveout(rproc, mem); ++ index++; + } + + return 0; +@@ -1046,6 +1173,25 @@ static int imx_rproc_detect_mode(struct imx_rproc *priv) + case IMX_RPROC_NONE: + priv->rproc->state = RPROC_DETACHED; + return 0; ++ case IMX_RPROC_PSCI: ++ unsigned int cpu; ++ int cpu_aff; ++ ++ priv->rproc->state = RPROC_DETACHED; ++ for_each_cpu(cpu, &priv->cpus) { ++ cpu_aff = psci_ops.affinity_info(cpu_logical_map(cpu), 0); ++ if (cpu_aff == PSCI_0_2_AFFINITY_LEVEL_OFF) { ++ priv->rproc->state = RPROC_OFFLINE; ++ break; ++ } ++ ++ /* in psci on state but not running Linux */ ++ if (cpu_online(cpu)) { ++ priv->rproc->state = RPROC_OFFLINE; ++ break; ++ } ++ } ++ return 0; + case IMX_RPROC_SMC: + arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_STARTED, 0, 0, 0, 0, 0, 0, &res); + if (res.a0) +@@ -1210,10 +1356,12 @@ static int imx_rproc_probe(struct platform_device *pdev) + struct imx_rproc *priv; + struct rproc *rproc; + const struct imx_rproc_dcfg *dcfg; ++ unsigned int cpus; ++ unsigned long cpus_bits; + int ret; + +- /* set some other name then imx */ +- rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops, ++ /* set node name to be remote processor name */ ++ rproc = rproc_alloc(dev, np->name, &imx_rproc_ops, + NULL, sizeof(*priv)); + if (!rproc) + return -ENOMEM; +@@ -1249,6 +1397,17 @@ static int imx_rproc_probe(struct platform_device *pdev) + goto err_put_mbox; + } + ++ /* Init priv->cpus before detect mode */ ++ ret = of_property_read_u32(dev->of_node, "fsl,cpus-bits", &cpus); ++ if (ret) { ++ cpumask_clear(&priv->cpus); ++ } else { ++ cpus_bits = cpus; ++ bitmap_copy(cpumask_bits(&priv->cpus), &cpus_bits, ++ min((unsigned int)nr_cpumask_bits, ++ (unsigned int)sizeof(unsigned long))); ++ } ++ + ret = imx_rproc_detect_mode(priv); + if (ret) + goto err_put_mbox; +@@ -1332,6 +1491,7 @@ static const struct of_device_id imx_rproc_of_match[] = { + { .compatible = "fsl,imx8ulp-cm33", .data = &imx_rproc_cfg_imx8ulp }, + { .compatible = "fsl,imx93-cm33", .data = &imx_rproc_cfg_imx93 }, + { .compatible = "fsl,imx95-cm7", .data = &imx_rproc_cfg_imx95_m7 }, ++ { .compatible = "fsl,imx-rproc-psci", .data = &imx_rproc_cfg_psci }, + {}, + }; + MODULE_DEVICE_TABLE(of, imx_rproc_of_match); +diff --git a/drivers/remoteproc/imx_rproc.h b/drivers/remoteproc/imx_rproc.h +index 79a1b8956d14..052d43fe563a 100644 +--- a/drivers/remoteproc/imx_rproc.h ++++ b/drivers/remoteproc/imx_rproc.h +@@ -24,6 +24,8 @@ enum imx_rproc_method { + IMX_RPROC_SMC, + /* Through System Control Unit API */ + IMX_RPROC_SCU_API, ++ /* Through ARM64 PSCI */ ++ IMX_RPROC_PSCI, + }; + + struct imx_rproc_dcfg { +diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c +index 695cce218e8c..3104f28d871d 100644 +--- a/drivers/remoteproc/remoteproc_core.c ++++ b/drivers/remoteproc/remoteproc_core.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + + #include "remoteproc_internal.h" + +@@ -522,6 +523,9 @@ static int rproc_handle_vdev(struct rproc *rproc, void *ptr, + return PTR_ERR(pdev); + } + ++ /* inherit parent's dma_coherent */ ++ pdev->dev.dma_coherent = dev_is_dma_coherent(dev); ++ + return 0; + } + +@@ -1285,7 +1289,7 @@ static int rproc_start(struct rproc *rproc, const struct firmware *fw) + * that any subsequent changes will be applied to the loaded version. + */ + loaded_table = rproc_find_loaded_rsc_table(rproc, fw); +- if (loaded_table) { ++ if (loaded_table && rproc->cached_table) { + memcpy(loaded_table, rproc->cached_table, rproc->table_sz); + rproc->table_ptr = loaded_table; + } +@@ -2463,6 +2467,8 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, + rproc->dev.type = &rproc_type; + rproc->dev.class = &rproc_class; + rproc->dev.driver_data = rproc; ++ /* inherit parent's dma_coherent */ ++ rproc->dev.dma_coherent = dev_is_dma_coherent(dev); + idr_init(&rproc->notifyids); + + rproc->name = kstrdup_const(name, GFP_KERNEL); +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 aaf343fc1e54..431a33afc0ad 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 f1af0f674615..40ee347fcc9b 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 882d0c850c10..80a8b479c90f 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -1835,6 +1835,7 @@ config RTC_DRV_IMX_SM + 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/staging/fsl_qbman/qman_driver.c b/drivers/staging/fsl_qbman/qman_driver.c +index a752faa4e85d..2f17d4a510e2 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: +@@ -723,6 +724,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, bool need_cleanup) ++{ ++ 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, need_cleanup); ++ 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(bool need_cleanup) ++{ ++ int cpu = 0; ++ ++ for_each_online_cpu(cpu) { ++ qman_affine_last_cpu = cpu; ++ qman_ethercat_portal_init(cpu, need_cleanup); ++ } ++} ++#endif ++ + __init int qman_init(void) + { + struct cpumask slave_cpus; +@@ -897,6 +942,10 @@ __init int qman_init(void) + qman_enable_irqs(); + } + ++#ifdef CONFIG_FSL_DPAA_ETHERCAT ++ qman_ethercat_portal_init_on_cpu(need_cleanup); ++#endif ++ + return 0; + } + +diff --git a/drivers/staging/fsl_qbman/qman_high.c b/drivers/staging/fsl_qbman/qman_high.c +index d1cbfaa987fe..feb47294a02f 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: +@@ -802,6 +803,30 @@ 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, ++ bool need_cleanup) ++{ ++ struct qman_portal *res; ++ struct qman_portal *portal = NULL; ++ ++ portal = ðercat_portal[cpu]; ++ res = qman_create_portal(portal, config, cgrs, need_cleanup); ++ 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, +@@ -1468,6 +1493,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 615d5a135702..9803d6990353 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: +@@ -233,6 +234,12 @@ struct qman_portal *qman_create_portal( + const struct qman_cgrs *cgrs, + bool need_cleanup); + ++#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, ++ bool need_cleanup); ++#endif + struct qman_portal *qman_create_affine_portal( + const struct qm_portal_config *config, + const struct qman_cgrs *cgrs, +diff --git a/drivers/tty/rpmsg_tty.c b/drivers/tty/rpmsg_tty.c +index 60a2915f5cfe..7f3f0373c9cf 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); + +@@ -78,27 +170,53 @@ static ssize_t rpmsg_tty_write(struct tty_struct *tty, const u8 *buf, + { + 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_t(unsigned int, 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; + } + +@@ -106,8 +224,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; + +@@ -173,7 +293,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)) +@@ -182,6 +307,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)) { +@@ -190,9 +332,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); +@@ -215,7 +376,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); +@@ -284,5 +446,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/21285.c b/drivers/tty/serial/21285.c +index d756fcc884cb..4de0c975ebdc 100644 +--- a/drivers/tty/serial/21285.c ++++ b/drivers/tty/serial/21285.c +@@ -185,14 +185,14 @@ static void serial21285_break_ctl(struct uart_port *port, int break_state) + unsigned long flags; + unsigned int h_lcr; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + h_lcr = *CSR_H_UBRLCR; + if (break_state) + h_lcr |= H_UBRLCR_BREAK; + else + h_lcr &= ~H_UBRLCR_BREAK; + *CSR_H_UBRLCR = h_lcr; +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int serial21285_startup(struct uart_port *port) +@@ -272,7 +272,7 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios, + if (port->fifosize) + h_lcr |= H_UBRLCR_FIFO; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* + * Update the per-port timeout. +@@ -309,7 +309,7 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios, + *CSR_H_UBRLCR = h_lcr; + *CSR_UARTCON = 1; + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *serial21285_type(struct uart_port *port) +diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c +index 4a9e71b2dbbc..021949f252f8 100644 +--- a/drivers/tty/serial/8250/8250_aspeed_vuart.c ++++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c +@@ -288,9 +288,9 @@ static void aspeed_vuart_set_throttle(struct uart_port *port, bool throttle) + struct uart_8250_port *up = up_to_u8250p(port); + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + __aspeed_vuart_set_throttle(up, throttle); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void aspeed_vuart_throttle(struct uart_port *port) +@@ -340,7 +340,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) + if (iir & UART_IIR_NO_INT) + return 0; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + lsr = serial_port_in(port, UART_LSR); + +diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c +index 9afd5979c9e0..db23b3a02aef 100644 +--- a/drivers/tty/serial/8250/8250_bcm7271.c ++++ b/drivers/tty/serial/8250/8250_bcm7271.c +@@ -567,7 +567,7 @@ static irqreturn_t brcmuart_isr(int irq, void *dev_id) + if (interrupts == 0) + return IRQ_NONE; + +- spin_lock_irqsave(&up->lock, flags); ++ uart_port_lock_irqsave(up, &flags); + + /* Clear all interrupts */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_CLEAR, interrupts); +@@ -581,7 +581,7 @@ static irqreturn_t brcmuart_isr(int irq, void *dev_id) + if ((rval | tval) == 0) + dev_warn(dev, "Spurious interrupt: 0x%x\n", interrupts); + +- spin_unlock_irqrestore(&up->lock, flags); ++ uart_port_unlock_irqrestore(up, flags); + return IRQ_HANDLED; + } + +@@ -608,10 +608,10 @@ static int brcmuart_startup(struct uart_port *port) + * + * Synchronize UART_IER access against the console. + */ +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + up->ier &= ~UART_IER_RDI; + serial_port_out(port, UART_IER, up->ier); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + + priv->tx_running = false; + priv->dma.rx_dma = NULL; +@@ -629,7 +629,7 @@ static void brcmuart_shutdown(struct uart_port *port) + struct brcmuart_priv *priv = up->port.private_data; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + priv->shutdown = true; + if (priv->dma_enabled) { + stop_rx_dma(up); +@@ -645,7 +645,7 @@ static void brcmuart_shutdown(struct uart_port *port) + */ + up->dma = NULL; + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + serial8250_do_shutdown(port); + } + +@@ -807,7 +807,7 @@ static int brcmuart_handle_irq(struct uart_port *p) + * interrupt but there is no data ready. + */ + if (((iir & UART_IIR_ID) == UART_IIR_RX_TIMEOUT) && !(priv->shutdown)) { +- spin_lock_irqsave(&p->lock, flags); ++ uart_port_lock_irqsave(p, &flags); + status = serial_port_in(p, UART_LSR); + if ((status & UART_LSR_DR) == 0) { + +@@ -832,7 +832,7 @@ static int brcmuart_handle_irq(struct uart_port *p) + + handled = 1; + } +- spin_unlock_irqrestore(&p->lock, flags); ++ uart_port_unlock_irqrestore(p, flags); + if (handled) + return 1; + } +@@ -850,7 +850,7 @@ static enum hrtimer_restart brcmuart_hrtimer_func(struct hrtimer *t) + if (priv->shutdown) + return HRTIMER_NORESTART; + +- spin_lock_irqsave(&p->lock, flags); ++ uart_port_lock_irqsave(p, &flags); + status = serial_port_in(p, UART_LSR); + + /* +@@ -874,7 +874,7 @@ static enum hrtimer_restart brcmuart_hrtimer_func(struct hrtimer *t) + status |= UART_MCR_RTS; + serial_port_out(p, UART_MCR, status); + } +- spin_unlock_irqrestore(&p->lock, flags); ++ uart_port_unlock_irqrestore(p, flags); + return HRTIMER_NORESTART; + } + +@@ -1173,10 +1173,10 @@ static int __maybe_unused brcmuart_suspend(struct device *dev) + * This will prevent resume from enabling RTS before the + * baud rate has been restored. + */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + priv->saved_mctrl = port->mctrl; + port->mctrl &= ~TIOCM_RTS; +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + serial8250_suspend_port(priv->line); + clk_disable_unprepare(priv->baud_mux_clk); +@@ -1215,10 +1215,10 @@ static int __maybe_unused brcmuart_resume(struct device *dev) + + if (priv->saved_mctrl & TIOCM_RTS) { + /* Restore RTS */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + port->mctrl |= TIOCM_RTS; + port->ops->set_mctrl(port, port->mctrl); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + return 0; +diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c +index 3449f8790e46..30434718fad8 100644 +--- a/drivers/tty/serial/8250/8250_core.c ++++ b/drivers/tty/serial/8250/8250_core.c +@@ -259,7 +259,7 @@ static void serial8250_backup_timeout(struct timer_list *t) + unsigned int iir, ier = 0, lsr; + unsigned long flags; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* + * Must disable interrupts or else we risk racing with the interrupt +@@ -292,7 +292,7 @@ static void serial8250_backup_timeout(struct timer_list *t) + if (up->port.irq) + serial_out(up, UART_IER, ier); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + /* Standard timer interval plus 0.2s to keep the port running */ + mod_timer(&up->timer, +@@ -592,6 +592,7 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) + + #ifdef CONFIG_SERIAL_8250_CONSOLE + ++#ifdef CONFIG_SERIAL_8250_LEGACY_CONSOLE + static void univ8250_console_write(struct console *co, const char *s, + unsigned int count) + { +@@ -599,6 +600,37 @@ static void univ8250_console_write(struct console *co, const char *s, + + serial8250_console_write(up, s, count); + } ++#else ++static bool univ8250_console_write_atomic(struct console *co, ++ struct nbcon_write_context *wctxt) ++{ ++ struct uart_8250_port *up = &serial8250_ports[co->index]; ++ ++ return serial8250_console_write_atomic(up, wctxt); ++} ++ ++static bool univ8250_console_write_thread(struct console *co, ++ struct nbcon_write_context *wctxt) ++{ ++ struct uart_8250_port *up = &serial8250_ports[co->index]; ++ ++ return serial8250_console_write_thread(up, wctxt); ++} ++ ++static void univ8250_console_driver_enter(struct console *con, unsigned long *flags) ++{ ++ struct uart_port *up = &serial8250_ports[con->index].port; ++ ++ __uart_port_lock_irqsave(up, flags); ++} ++ ++static void univ8250_console_driver_exit(struct console *con, unsigned long flags) ++{ ++ struct uart_port *up = &serial8250_ports[con->index].port; ++ ++ __uart_port_unlock_irqrestore(up, flags); ++} ++#endif /* CONFIG_SERIAL_8250_LEGACY_CONSOLE */ + + static int univ8250_console_setup(struct console *co, char *options) + { +@@ -698,12 +730,20 @@ static int univ8250_console_match(struct console *co, char *name, int idx, + + static struct console univ8250_console = { + .name = "ttyS", ++#ifdef CONFIG_SERIAL_8250_LEGACY_CONSOLE + .write = univ8250_console_write, ++ .flags = CON_PRINTBUFFER | CON_ANYTIME, ++#else ++ .write_atomic = univ8250_console_write_atomic, ++ .write_thread = univ8250_console_write_thread, ++ .driver_enter = univ8250_console_driver_enter, ++ .driver_exit = univ8250_console_driver_exit, ++ .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_NBCON, ++#endif + .device = uart_console_device, + .setup = univ8250_console_setup, + .exit = univ8250_console_exit, + .match = univ8250_console_match, +- .flags = CON_PRINTBUFFER | CON_ANYTIME, + .index = -1, + .data = &serial8250_reg, + }; +@@ -992,11 +1032,11 @@ static void serial_8250_overrun_backoff_work(struct work_struct *work) + struct uart_port *port = &up->port; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + up->ier |= UART_IER_RLSI | UART_IER_RDI; + up->port.read_status_mask |= UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /** +@@ -1194,9 +1234,9 @@ void serial8250_unregister_port(int line) + if (uart->em485) { + unsigned long flags; + +- spin_lock_irqsave(&uart->port.lock, flags); ++ uart_port_lock_irqsave(&uart->port, &flags); + serial8250_em485_destroy(uart); +- spin_unlock_irqrestore(&uart->port.lock, flags); ++ uart_port_unlock_irqrestore(&uart->port, flags); + } + + uart_remove_one_port(&serial8250_reg, &uart->port); +diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c +index 7fa66501792d..8b30ca8fdd3f 100644 +--- a/drivers/tty/serial/8250/8250_dma.c ++++ b/drivers/tty/serial/8250/8250_dma.c +@@ -22,7 +22,7 @@ static void __dma_tx_complete(void *param) + dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + +- spin_lock_irqsave(&p->port.lock, flags); ++ uart_port_lock_irqsave(&p->port, &flags); + + dma->tx_running = 0; + +@@ -35,7 +35,7 @@ static void __dma_tx_complete(void *param) + if (ret || !dma->tx_running) + serial8250_set_THRI(p); + +- spin_unlock_irqrestore(&p->port.lock, flags); ++ uart_port_unlock_irqrestore(&p->port, flags); + } + + static void __dma_rx_complete(struct uart_8250_port *p) +@@ -70,7 +70,7 @@ static void dma_rx_complete(void *param) + struct uart_8250_dma *dma = p->dma; + unsigned long flags; + +- spin_lock_irqsave(&p->port.lock, flags); ++ uart_port_lock_irqsave(&p->port, &flags); + if (dma->rx_running) + __dma_rx_complete(p); + +@@ -80,7 +80,7 @@ static void dma_rx_complete(void *param) + */ + if (!dma->rx_running && (serial_lsr_in(p) & UART_LSR_DR)) + p->dma->rx_dma(p); +- spin_unlock_irqrestore(&p->port.lock, flags); ++ uart_port_unlock_irqrestore(&p->port, flags); + } + + int serial8250_tx_dma(struct uart_8250_port *p) +diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c +index 8aed33be2ebf..5367bcc6256c 100644 +--- a/drivers/tty/serial/8250/8250_dw.c ++++ b/drivers/tty/serial/8250/8250_dw.c +@@ -290,20 +290,20 @@ static int dw8250_handle_irq(struct uart_port *p) + * so we limit the workaround only to non-DMA mode. + */ + if (!up->dma && rx_timeout) { +- spin_lock_irqsave(&p->lock, flags); ++ uart_port_lock_irqsave(p, &flags); + status = serial_lsr_in(up); + + if (!(status & (UART_LSR_DR | UART_LSR_BI))) + (void) p->serial_in(p, UART_RX); + +- spin_unlock_irqrestore(&p->lock, flags); ++ uart_port_unlock_irqrestore(p, flags); + } + + /* Manually stop the Rx DMA transfer when acting as flow controller */ + if (quirks & DW_UART_QUIRK_IS_DMA_FC && up->dma && up->dma->rx_running && rx_timeout) { +- spin_lock_irqsave(&p->lock, flags); ++ uart_port_lock_irqsave(p, &flags); + status = serial_lsr_in(up); +- spin_unlock_irqrestore(&p->lock, flags); ++ uart_port_unlock_irqrestore(p, flags); + + if (status & (UART_LSR_DR | UART_LSR_BI)) { + dw8250_writel_ext(p, RZN1_UART_RDMACR, 0); +diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c +index 27430fdd9e76..17be6ad24a0f 100644 +--- a/drivers/tty/serial/8250/8250_exar.c ++++ b/drivers/tty/serial/8250/8250_exar.c +@@ -243,9 +243,9 @@ static int xr17v35x_startup(struct uart_port *port) + * + * Synchronize UART_IER access against the console. + */ +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + serial_port_out(port, UART_IER, 0); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + + return serial8250_do_startup(port); + } +diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c +index 6af4e1c1210a..f522eb5026c9 100644 +--- a/drivers/tty/serial/8250/8250_fsl.c ++++ b/drivers/tty/serial/8250/8250_fsl.c +@@ -30,11 +30,11 @@ int fsl8250_handle_irq(struct uart_port *port) + unsigned int iir; + struct uart_8250_port *up = up_to_u8250p(port); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + iir = port->serial_in(port, UART_IIR); + if (iir & UART_IIR_NO_INT) { +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + return 0; + } + +@@ -54,7 +54,7 @@ int fsl8250_handle_irq(struct uart_port *port) + if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { + up->lsr_saved_flags &= ~UART_LSR_BI; + port->serial_in(port, UART_RX); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + return 1; + } + +diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c +index 28f9a2679a20..33699e86eb52 100644 +--- a/drivers/tty/serial/8250/8250_mtk.c ++++ b/drivers/tty/serial/8250/8250_mtk.c +@@ -102,7 +102,7 @@ static void mtk8250_dma_rx_complete(void *param) + if (data->rx_status == DMA_RX_SHUTDOWN) + return; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + total = dma->rx_size - state.residue; +@@ -128,7 +128,7 @@ static void mtk8250_dma_rx_complete(void *param) + + mtk8250_rx_dma(up); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static void mtk8250_rx_dma(struct uart_8250_port *up) +@@ -372,7 +372,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* + * Update the per-port timeout. +@@ -420,7 +420,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, + if (uart_console(port)) + up->port.cons->cflag = termios->c_cflag; + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c +index 8f472a2080ff..78fc1f17d5e2 100644 +--- a/drivers/tty/serial/8250/8250_omap.c ++++ b/drivers/tty/serial/8250/8250_omap.c +@@ -405,7 +405,7 @@ static void omap_8250_set_termios(struct uart_port *port, + * interrupts disabled. + */ + pm_runtime_get_sync(port->dev); +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + + /* + * Update the per-port timeout. +@@ -508,7 +508,7 @@ static void omap_8250_set_termios(struct uart_port *port, + } + omap8250_restore_regs(up); + +- spin_unlock_irq(&up->port.lock); ++ uart_port_unlock_irq(&up->port); + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); + +@@ -533,7 +533,7 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state, + pm_runtime_get_sync(port->dev); + + /* Synchronize UART_IER access against the console. */ +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + efr = serial_in(up, UART_EFR); +@@ -545,7 +545,7 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state, + serial_out(up, UART_EFR, efr); + serial_out(up, UART_LCR, 0); + +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); +@@ -676,7 +676,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) + unsigned long delay; + + /* Synchronize UART_IER access against the console. */ +- spin_lock(&port->lock); ++ uart_port_lock(port); + up->ier = port->serial_in(port, UART_IER); + if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { + port->ops->stop_rx(port); +@@ -686,7 +686,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) + */ + cancel_delayed_work(&up->overrun_backoff); + } +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + delay = msecs_to_jiffies(up->overrun_backoff_time_ms); + schedule_delayed_work(&up->overrun_backoff, delay); +@@ -733,10 +733,10 @@ static int omap_8250_startup(struct uart_port *port) + } + + /* Synchronize UART_IER access against the console. */ +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + + #ifdef CONFIG_PM + up->capabilities |= UART_CAP_RPM; +@@ -749,9 +749,9 @@ static int omap_8250_startup(struct uart_port *port) + serial_out(up, UART_OMAP_WER, priv->wer); + + if (up->dma && !(priv->habit & UART_HAS_EFR2)) { +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + up->dma->rx_dma(up); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + } + + enable_irq(up->port.irq); +@@ -777,10 +777,10 @@ static void omap_8250_shutdown(struct uart_port *port) + serial_out(up, UART_OMAP_EFR2, 0x0); + + /* Synchronize UART_IER access against the console. */ +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + up->ier = 0; + serial_out(up, UART_IER, 0); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + disable_irq_nosync(up->port.irq); + dev_pm_clear_wake_irq(port->dev); + +@@ -805,10 +805,10 @@ static void omap_8250_throttle(struct uart_port *port) + + pm_runtime_get_sync(port->dev); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + port->ops->stop_rx(port); + priv->throttled = true; +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); +@@ -823,14 +823,14 @@ static void omap_8250_unthrottle(struct uart_port *port) + pm_runtime_get_sync(port->dev); + + /* Synchronize UART_IER access against the console. */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + priv->throttled = false; + if (up->dma) + 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); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); +@@ -974,7 +974,7 @@ static void __dma_rx_complete(void *param) + unsigned long flags; + + /* Synchronize UART_IER access against the console. */ +- spin_lock_irqsave(&p->port.lock, flags); ++ uart_port_lock_irqsave(&p->port, &flags); + + /* + * If the tx status is not DMA_COMPLETE, then this is a delayed +@@ -983,7 +983,7 @@ static void __dma_rx_complete(void *param) + */ + if (dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state) != + DMA_COMPLETE) { +- spin_unlock_irqrestore(&p->port.lock, flags); ++ uart_port_unlock_irqrestore(&p->port, flags); + return; + } + __dma_rx_do_complete(p); +@@ -994,7 +994,7 @@ static void __dma_rx_complete(void *param) + omap_8250_rx_dma(p); + } + +- spin_unlock_irqrestore(&p->port.lock, flags); ++ uart_port_unlock_irqrestore(&p->port, flags); + } + + static void omap_8250_rx_dma_flush(struct uart_8250_port *p) +@@ -1099,7 +1099,7 @@ static void omap_8250_dma_tx_complete(void *param) + dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + +- spin_lock_irqsave(&p->port.lock, flags); ++ uart_port_lock_irqsave(&p->port, &flags); + + dma->tx_running = 0; + +@@ -1128,7 +1128,7 @@ static void omap_8250_dma_tx_complete(void *param) + serial8250_set_THRI(p); + } + +- spin_unlock_irqrestore(&p->port.lock, flags); ++ uart_port_unlock_irqrestore(&p->port, flags); + } + + static int omap_8250_tx_dma(struct uart_8250_port *p) +@@ -1294,7 +1294,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) + return IRQ_HANDLED; + } + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + status = serial_port_in(port, UART_LSR); + +@@ -1774,15 +1774,15 @@ static int omap8250_runtime_resume(struct device *dev) + up = serial8250_get_port(priv->line); + + if (up && omap8250_lost_context(up)) { +- spin_lock_irq(&up->port.lock); ++ uart_port_lock_irq(&up->port); + omap8250_restore_regs(up); +- spin_unlock_irq(&up->port.lock); ++ uart_port_unlock_irq(&up->port); + } + + if (up && up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2)) { +- spin_lock_irq(&up->port.lock); ++ uart_port_lock_irq(&up->port); + omap_8250_rx_dma(up); +- spin_unlock_irq(&up->port.lock); ++ uart_port_unlock_irq(&up->port); + } + + priv->latency = priv->calc_latency; +diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c +index a3b25779d921..53e238c8cc89 100644 +--- a/drivers/tty/serial/8250/8250_pci1xxxx.c ++++ b/drivers/tty/serial/8250/8250_pci1xxxx.c +@@ -225,10 +225,10 @@ static bool pci1xxxx_port_suspend(int line) + if (port->suspended == 0 && port->dev) { + wakeup_mask = readb(up->port.membase + UART_WAKE_MASK_REG); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + port->mctrl &= ~TIOCM_OUT2; + port->ops->set_mctrl(port, port->mctrl); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + ret = (wakeup_mask & UART_WAKE_SRCS) != UART_WAKE_SRCS; + } +@@ -251,10 +251,10 @@ static void pci1xxxx_port_resume(int line) + writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG); + + if (port->suspended == 0) { +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + port->mctrl |= TIOCM_OUT2; + port->ops->set_mctrl(port, port->mctrl); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + mutex_unlock(&tport->mutex); + } +diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c +index a17803da83f8..2d4e775cd7cb 100644 +--- a/drivers/tty/serial/8250/8250_port.c ++++ b/drivers/tty/serial/8250/8250_port.c +@@ -557,6 +557,11 @@ static int serial8250_em485_init(struct uart_8250_port *p) + if (!p->em485) + return -ENOMEM; + ++#ifndef CONFIG_SERIAL_8250_LEGACY_CONSOLE ++ if (uart_console(&p->port)) ++ dev_warn(p->port.dev, "no atomic printing for rs485 consoles\n"); ++#endif ++ + hrtimer_init(&p->em485->stop_tx_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + hrtimer_init(&p->em485->start_tx_timer, CLOCK_MONOTONIC, +@@ -689,7 +694,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) + + if (p->capabilities & UART_CAP_SLEEP) { + /* Synchronize UART_IER access against the console. */ +- spin_lock_irq(&p->port.lock); ++ uart_port_lock_irq(&p->port); + if (p->capabilities & UART_CAP_EFR) { + lcr = serial_in(p, UART_LCR); + efr = serial_in(p, UART_EFR); +@@ -703,13 +708,17 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) + serial_out(p, UART_EFR, efr); + serial_out(p, UART_LCR, lcr); + } +- spin_unlock_irq(&p->port.lock); ++ uart_port_unlock_irq(&p->port); + } + + serial8250_rpm_put(p); + } + +-static void serial8250_clear_IER(struct uart_8250_port *up) ++/* ++ * Only to be used by write_atomic() and the legacy write(), which do not ++ * require port lock. ++ */ ++static void __serial8250_clear_IER(struct uart_8250_port *up) + { + if (up->capabilities & UART_CAP_UUE) + serial_out(up, UART_IER, UART_IER_UUE); +@@ -717,6 +726,14 @@ static void serial8250_clear_IER(struct uart_8250_port *up) + serial_out(up, UART_IER, 0); + } + ++static inline void serial8250_clear_IER(struct uart_8250_port *up) ++{ ++ /* Port locked to synchronize UART_IER access against the console. */ ++ lockdep_assert_held_once(&up->port.lock); ++ ++ __serial8250_clear_IER(up); ++} ++ + #ifdef CONFIG_SERIAL_8250_RSA + /* + * Attempts to turn on the RSA FIFO. Returns zero on failure. +@@ -746,9 +763,9 @@ static void enable_rsa(struct uart_8250_port *up) + { + if (up->port.type == PORT_RSA) { + if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { +- spin_lock_irq(&up->port.lock); ++ uart_port_lock_irq(&up->port); + __enable_rsa(up); +- spin_unlock_irq(&up->port.lock); ++ uart_port_unlock_irq(&up->port); + } + if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) + serial_out(up, UART_RSA_FRR, 0); +@@ -768,7 +785,7 @@ static void disable_rsa(struct uart_8250_port *up) + + if (up->port.type == PORT_RSA && + up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { +- spin_lock_irq(&up->port.lock); ++ uart_port_lock_irq(&up->port); + + mode = serial_in(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); +@@ -781,7 +798,7 @@ static void disable_rsa(struct uart_8250_port *up) + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; +- spin_unlock_irq(&up->port.lock); ++ uart_port_unlock_irq(&up->port); + } + } + #endif /* CONFIG_SERIAL_8250_RSA */ +@@ -1172,7 +1189,7 @@ static void autoconfig(struct uart_8250_port *up) + * + * Synchronize UART_IER access against the console. + */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + up->capabilities = 0; + up->bugs = 0; +@@ -1211,7 +1228,7 @@ static void autoconfig(struct uart_8250_port *up) + /* + * We failed; there's nothing here + */ +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", + scratch2, scratch3); + goto out; +@@ -1235,7 +1252,7 @@ static void autoconfig(struct uart_8250_port *up) + status1 = serial_in(up, UART_MSR) & UART_MSR_STATUS_BITS; + serial8250_out_MCR(up, save_mcr); + if (status1 != (UART_MSR_DCD | UART_MSR_CTS)) { +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + DEBUG_AUTOCONF("LOOP test failed (%02x) ", + status1); + goto out; +@@ -1304,7 +1321,7 @@ static void autoconfig(struct uart_8250_port *up) + serial8250_clear_IER(up); + + out_unlock: +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + /* + * Check if the device is a Fintek F81216A +@@ -1341,9 +1358,9 @@ static void autoconfig_irq(struct uart_8250_port *up) + probe_irq_off(probe_irq_on()); + save_mcr = serial8250_in_MCR(up); + /* Synchronize UART_IER access against the console. */ +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + save_ier = serial_in(up, UART_IER); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + serial8250_out_MCR(up, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); +@@ -1356,9 +1373,9 @@ static void autoconfig_irq(struct uart_8250_port *up) + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + /* Synchronize UART_IER access against the console. */ +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + serial_out(up, UART_IER, UART_IER_ALL_INTR); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + serial_in(up, UART_LSR); + serial_in(up, UART_RX); + serial_in(up, UART_IIR); +@@ -1369,9 +1386,9 @@ static void autoconfig_irq(struct uart_8250_port *up) + + serial8250_out_MCR(up, save_mcr); + /* Synchronize UART_IER access against the console. */ +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + serial_out(up, UART_IER, save_ier); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + + if (port->flags & UPF_FOURPORT) + outb_p(save_ICP, ICP); +@@ -1436,13 +1453,13 @@ static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) + unsigned long flags; + + serial8250_rpm_get(p); +- spin_lock_irqsave(&p->port.lock, flags); ++ uart_port_lock_irqsave(&p->port, &flags); + if (em485->active_timer == &em485->stop_tx_timer) { + p->rs485_stop_tx(p); + em485->active_timer = NULL; + em485->tx_stopped = true; + } +- spin_unlock_irqrestore(&p->port.lock, flags); ++ uart_port_unlock_irqrestore(&p->port, flags); + serial8250_rpm_put(p); + + return HRTIMER_NORESTART; +@@ -1624,12 +1641,12 @@ static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t) + struct uart_8250_port *p = em485->port; + unsigned long flags; + +- spin_lock_irqsave(&p->port.lock, flags); ++ uart_port_lock_irqsave(&p->port, &flags); + if (em485->active_timer == &em485->start_tx_timer) { + __start_tx(&p->port); + em485->active_timer = NULL; + } +- spin_unlock_irqrestore(&p->port.lock, flags); ++ uart_port_unlock_irqrestore(&p->port, flags); + + return HRTIMER_NORESTART; + } +@@ -1912,7 +1929,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) + if (iir & UART_IIR_NO_INT) + return 0; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + status = serial_lsr_in(up); + +@@ -1982,9 +1999,9 @@ static int serial8250_tx_threshold_handle_irq(struct uart_port *port) + if ((iir & UART_IIR_ID) == UART_IIR_THRI) { + struct uart_8250_port *up = up_to_u8250p(port); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + serial8250_tx_chars(up); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + iir = serial_port_in(port, UART_IIR); +@@ -1999,10 +2016,10 @@ static unsigned int serial8250_tx_empty(struct uart_port *port) + + serial8250_rpm_get(up); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (!serial8250_tx_dma_running(up) && uart_lsr_tx_empty(serial_lsr_in(up))) + result = TIOCSER_TEMT; +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + serial8250_rpm_put(up); + +@@ -2064,13 +2081,13 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) + unsigned long flags; + + serial8250_rpm_get(up); +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_port_out(port, UART_LCR, up->lcr); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + serial8250_rpm_put(up); + } + +@@ -2205,7 +2222,7 @@ int serial8250_do_startup(struct uart_port *port) + * + * Synchronize UART_IER access against the console. + */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + up->acr = 0; + serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B); + serial_port_out(port, UART_EFR, UART_EFR_ECB); +@@ -2215,7 +2232,7 @@ int serial8250_do_startup(struct uart_port *port) + 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_LCR, 0); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + if (port->type == PORT_DA830) { +@@ -2224,10 +2241,10 @@ int serial8250_do_startup(struct uart_port *port) + * + * Synchronize UART_IER access against the console. + */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + serial_port_out(port, UART_IER, 0); + serial_port_out(port, UART_DA830_PWREMU_MGMT, 0); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + mdelay(10); + + /* Enable Tx, Rx and free run mode */ +@@ -2341,7 +2358,7 @@ int serial8250_do_startup(struct uart_port *port) + * + * Synchronize UART_IER access against the console. + */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + wait_for_xmitr(up, UART_LSR_THRE); + serial_port_out_sync(port, UART_IER, UART_IER_THRI); +@@ -2353,7 +2370,7 @@ int serial8250_do_startup(struct uart_port *port) + iir = serial_port_in(port, UART_IIR); + serial_port_out(port, UART_IER, 0); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (port->irqflags & IRQF_SHARED) + enable_irq(port->irq); +@@ -2376,7 +2393,7 @@ int serial8250_do_startup(struct uart_port *port) + */ + serial_port_out(port, UART_LCR, UART_LCR_WLEN8); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (up->port.flags & UPF_FOURPORT) { + if (!up->port.irq) + up->port.mctrl |= TIOCM_OUT1; +@@ -2422,7 +2439,7 @@ int serial8250_do_startup(struct uart_port *port) + } + + dont_test_tx_en: +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + /* + * Clear the interrupt registers again for luck, and clear the +@@ -2493,17 +2510,17 @@ void serial8250_do_shutdown(struct uart_port *port) + * + * Synchronize UART_IER access against the console. + */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + up->ier = 0; + serial_port_out(port, UART_IER, 0); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + synchronize_irq(port->irq); + + if (up->dma) + serial8250_release_dma(up); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (port->flags & UPF_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + inb((port->iobase & 0xfe0) | 0x1f); +@@ -2512,7 +2529,7 @@ void serial8250_do_shutdown(struct uart_port *port) + port->mctrl &= ~TIOCM_OUT2; + + serial8250_set_mctrl(port, port->mctrl); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + /* + * Disable break condition and FIFOs +@@ -2748,14 +2765,14 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) + quot = serial8250_get_divisor(port, baud, &frac); + + serial8250_rpm_get(up); +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + uart_update_timeout(port, termios->c_cflag, baud); + + serial8250_set_divisor(port, baud, quot, frac); + serial_port_out(port, UART_LCR, up->lcr); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + serial8250_rpm_put(up); + + out_unlock: +@@ -2792,7 +2809,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, + * Synchronize UART_IER access against the console. + */ + serial8250_rpm_get(up); +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + up->lcr = cval; /* Save computed LCR */ + +@@ -2895,7 +2912,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, + serial_port_out(port, UART_FCR, up->fcr); /* set fcr */ + } + serial8250_set_mctrl(port, port->mctrl); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + serial8250_rpm_put(up); + + /* Don't rewrite B0 */ +@@ -2918,15 +2935,15 @@ void serial8250_do_set_ldisc(struct uart_port *port, struct ktermios *termios) + { + if (termios->c_line == N_PPS) { + port->flags |= UPF_HARDPPS_CD; +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + serial8250_enable_ms(port); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + } else { + port->flags &= ~UPF_HARDPPS_CD; + if (!UART_ENABLE_MS(port, termios->c_cflag)) { +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + serial8250_disable_ms(port); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + } + } + } +@@ -3322,6 +3339,11 @@ static void serial8250_console_putchar(struct uart_port *port, unsigned char ch) + + wait_for_xmitr(up, UART_LSR_THRE); + serial_port_out(port, UART_TX, ch); ++ ++ if (ch == '\n') ++ up->console_newline_needed = false; ++ else ++ up->console_newline_needed = true; + } + + /* +@@ -3350,6 +3372,7 @@ static void serial8250_console_restore(struct uart_8250_port *up) + serial8250_out_MCR(up, up->mcr | UART_MCR_DTR | UART_MCR_RTS); + } + ++#ifdef CONFIG_SERIAL_8250_LEGACY_CONSOLE + /* + * Print a string to the serial port using the device FIFO + * +@@ -3400,15 +3423,15 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, + touch_nmi_watchdog(); + + if (oops_in_progress) +- locked = spin_trylock_irqsave(&port->lock, flags); ++ locked = uart_port_trylock_irqsave(port, &flags); + else +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_port_in(port, UART_IER); +- serial8250_clear_IER(up); ++ __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))) { +@@ -3472,8 +3495,137 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, + serial8250_modem_status(up); + + if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } ++#else ++bool serial8250_console_write_thread(struct uart_8250_port *up, ++ struct nbcon_write_context *wctxt) ++{ ++ struct uart_8250_em485 *em485 = up->em485; ++ struct uart_port *port = &up->port; ++ bool done = false; ++ unsigned int ier; ++ ++ touch_nmi_watchdog(); ++ ++ if (!nbcon_enter_unsafe(wctxt)) ++ return false; ++ ++ /* First save IER then disable the interrupts. */ ++ ier = serial_port_in(port, UART_IER); ++ serial8250_clear_IER(up); ++ ++ /* Check scratch reg if port powered off during system sleep. */ ++ if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) { ++ serial8250_console_restore(up); ++ up->canary = 0; ++ } ++ ++ if (em485) { ++ if (em485->tx_stopped) ++ up->rs485_start_tx(up); ++ mdelay(port->rs485.delay_rts_before_send); ++ } ++ ++ if (nbcon_exit_unsafe(wctxt)) { ++ int len = READ_ONCE(wctxt->len); ++ int i; ++ ++ /* ++ * Write out the message. Toggle unsafe for each byte in order ++ * to give another (higher priority) context the opportunity ++ * for a friendly takeover. If such a takeover occurs, this ++ * context must reacquire ownership in order to perform final ++ * actions (such as re-enabling the interrupts). ++ * ++ * IMPORTANT: wctxt->outbuf and wctxt->len are no longer valid ++ * after a reacquire so writing the message must be ++ * aborted. ++ */ ++ for (i = 0; i < len; i++) { ++ if (!nbcon_enter_unsafe(wctxt)) { ++ nbcon_reacquire(wctxt); ++ break; ++ } ++ ++ uart_console_write(port, wctxt->outbuf + i, 1, serial8250_console_putchar); ++ ++ if (!nbcon_exit_unsafe(wctxt)) { ++ nbcon_reacquire(wctxt); ++ break; ++ } ++ } ++ done = (i == len); ++ } else { ++ nbcon_reacquire(wctxt); ++ } ++ ++ while (!nbcon_enter_unsafe(wctxt)) ++ nbcon_reacquire(wctxt); ++ ++ /* Finally, wait for transmitter to become empty and restore IER. */ ++ wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); ++ if (em485) { ++ mdelay(port->rs485.delay_rts_after_send); ++ if (em485->tx_stopped) ++ up->rs485_stop_tx(up); ++ } ++ serial_port_out(port, UART_IER, ier); ++ ++ /* ++ * The receive handling will happen properly because the receive ready ++ * bit will still be set; it is not cleared on read. However, modem ++ * control will not, we must call it if we have saved something in the ++ * saved flags while processing with interrupts off. ++ */ ++ if (up->msr_saved_flags) ++ serial8250_modem_status(up); ++ ++ /* Success if no handover/takeover and message fully printed. */ ++ return (nbcon_exit_unsafe(wctxt) && done); ++} ++ ++bool serial8250_console_write_atomic(struct uart_8250_port *up, ++ struct nbcon_write_context *wctxt) ++{ ++ struct uart_port *port = &up->port; ++ unsigned int ier; ++ ++ /* Atomic console not supported for rs485 mode. */ ++ if (up->em485) ++ return false; ++ ++ touch_nmi_watchdog(); ++ ++ if (!nbcon_enter_unsafe(wctxt)) ++ return false; ++ ++ /* ++ * First save IER then disable the interrupts. The special variant to ++ * clear IER is used because atomic printing may occur without holding ++ * the port lock. ++ */ ++ ier = serial_port_in(port, UART_IER); ++ __serial8250_clear_IER(up); ++ ++ /* Check scratch reg if port powered off during system sleep. */ ++ if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) { ++ serial8250_console_restore(up); ++ up->canary = 0; ++ } ++ ++ if (up->console_newline_needed) ++ uart_console_write(port, "\n", 1, serial8250_console_putchar); ++ uart_console_write(port, wctxt->outbuf, wctxt->len, serial8250_console_putchar); ++ ++ /* Finally, wait for transmitter to become empty and restore IER. */ ++ wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); ++ serial_port_out(port, UART_IER, ier); ++ ++ /* Success if no handover/takeover. */ ++ return nbcon_exit_unsafe(wctxt); ++} ++#endif /* CONFIG_SERIAL_8250_LEGACY_CONSOLE */ + + static unsigned int probe_baud(struct uart_port *port) + { +@@ -3492,6 +3644,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'; +@@ -3501,6 +3654,8 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe) + if (!port->iobase && !port->membase) + return -ENODEV; + ++ up->console_newline_needed = false; ++ + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else if (probe) +diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c +index 5fab4c978891..7090b251dd4d 100644 +--- a/drivers/tty/serial/altera_jtaguart.c ++++ b/drivers/tty/serial/altera_jtaguart.c +@@ -147,14 +147,14 @@ static irqreturn_t altera_jtaguart_interrupt(int irq, void *data) + isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >> + ALTERA_JTAGUART_CONTROL_RI_OFF) & port->read_status_mask; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK) + altera_jtaguart_rx_chars(port); + if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK) + altera_jtaguart_tx_chars(port); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_RETVAL(isr); + } +@@ -180,14 +180,14 @@ static int altera_jtaguart_startup(struct uart_port *port) + return ret; + } + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Enable RX interrupts now */ + port->read_status_mask = ALTERA_JTAGUART_CONTROL_RE_MSK; + writel(port->read_status_mask, + port->membase + ALTERA_JTAGUART_CONTROL_REG); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return 0; + } +@@ -196,14 +196,14 @@ static void altera_jtaguart_shutdown(struct uart_port *port) + { + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Disable all interrupts now */ + port->read_status_mask = 0; + writel(port->read_status_mask, + port->membase + ALTERA_JTAGUART_CONTROL_REG); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + free_irq(port->irq, port); + } +@@ -264,33 +264,33 @@ static void altera_jtaguart_console_putc(struct uart_port *port, unsigned char c + unsigned long flags; + u32 status; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + while (!altera_jtaguart_tx_space(port, &status)) { +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) { + return; /* no connection activity */ + } + + cpu_relax(); +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + #else + static void altera_jtaguart_console_putc(struct uart_port *port, unsigned char c) + { + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + while (!altera_jtaguart_tx_space(port, NULL)) { +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + cpu_relax(); +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + #endif + +diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c +index a9c41942190c..77835ac68df2 100644 +--- a/drivers/tty/serial/altera_uart.c ++++ b/drivers/tty/serial/altera_uart.c +@@ -164,13 +164,13 @@ static void altera_uart_break_ctl(struct uart_port *port, int break_state) + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (break_state == -1) + pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK; + else + pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK; + altera_uart_update_ctrl_reg(pp); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void altera_uart_set_termios(struct uart_port *port, +@@ -187,10 +187,10 @@ static void altera_uart_set_termios(struct uart_port *port, + tty_termios_copy_hw(termios, old); + tty_termios_encode_baud_rate(termios, baud, baud); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + uart_update_timeout(port, termios->c_cflag, baud); + altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + /* + * FIXME: port->read_status_mask and port->ignore_status_mask +@@ -264,12 +264,12 @@ static irqreturn_t altera_uart_interrupt(int irq, void *data) + + isr = altera_uart_readl(port, ALTERA_UART_STATUS_REG) & pp->imr; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (isr & ALTERA_UART_STATUS_RRDY_MSK) + altera_uart_rx_chars(port); + if (isr & ALTERA_UART_STATUS_TRDY_MSK) + altera_uart_tx_chars(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return IRQ_RETVAL(isr); + } +@@ -313,13 +313,13 @@ static int altera_uart_startup(struct uart_port *port) + } + } + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Enable RX interrupts now */ + pp->imr = ALTERA_UART_CONTROL_RRDY_MSK; + altera_uart_update_ctrl_reg(pp); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return 0; + } +@@ -329,13 +329,13 @@ static void altera_uart_shutdown(struct uart_port *port) + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Disable all interrupts now */ + pp->imr = 0; + altera_uart_update_ctrl_reg(pp); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (port->irq) + free_irq(port->irq, port); +diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c +index b5a7404cbacb..eabbf8afc9b5 100644 +--- a/drivers/tty/serial/amba-pl010.c ++++ b/drivers/tty/serial/amba-pl010.c +@@ -207,7 +207,7 @@ static irqreturn_t pl010_int(int irq, void *dev_id) + unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; + int handled = 0; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + status = readb(port->membase + UART010_IIR); + if (status) { +@@ -228,7 +228,7 @@ static irqreturn_t pl010_int(int irq, void *dev_id) + handled = 1; + } + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_RETVAL(handled); + } +@@ -270,14 +270,14 @@ static void pl010_break_ctl(struct uart_port *port, int break_state) + unsigned long flags; + unsigned int lcr_h; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + lcr_h = readb(port->membase + UART010_LCRH); + if (break_state == -1) + lcr_h |= UART01x_LCRH_BRK; + else + lcr_h &= ~UART01x_LCRH_BRK; + writel(lcr_h, port->membase + UART010_LCRH); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int pl010_startup(struct uart_port *port) +@@ -385,7 +385,7 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios, + if (port->fifosize > 1) + lcr_h |= UART01x_LCRH_FEN; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* + * Update the per-port timeout. +@@ -438,22 +438,22 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios, + writel(lcr_h, port->membase + UART010_LCRH); + writel(old_cr, port->membase + UART010_CR); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void pl010_set_ldisc(struct uart_port *port, struct ktermios *termios) + { + if (termios->c_line == N_PPS) { + port->flags |= UPF_HARDPPS_CD; +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + pl010_enable_ms(port); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + } else { + port->flags &= ~UPF_HARDPPS_CD; + if (!UART_ENABLE_MS(port, termios->c_cflag)) { +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + pl010_disable_ms(port); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + } + } + } +diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c +index 362bbcdece0d..9cd660edb165 100644 +--- a/drivers/tty/serial/amba-pl011.c ++++ b/drivers/tty/serial/amba-pl011.c +@@ -347,9 +347,9 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap) + flag = TTY_FRAME; + } + +- spin_unlock(&uap->port.lock); ++ uart_port_unlock(&uap->port); + sysrq = uart_handle_sysrq_char(&uap->port, ch & 255); +- spin_lock(&uap->port.lock); ++ uart_port_lock(&uap->port); + + if (!sysrq) + uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag); +@@ -544,7 +544,7 @@ static void pl011_dma_tx_callback(void *data) + unsigned long flags; + u16 dmacr; + +- spin_lock_irqsave(&uap->port.lock, flags); ++ uart_port_lock_irqsave(&uap->port, &flags); + if (uap->dmatx.queued) + dma_unmap_single(dmatx->chan->device->dev, dmatx->dma, + dmatx->len, DMA_TO_DEVICE); +@@ -565,7 +565,7 @@ static void pl011_dma_tx_callback(void *data) + if (!(dmacr & UART011_TXDMAE) || uart_tx_stopped(&uap->port) || + uart_circ_empty(&uap->port.state->xmit)) { + uap->dmatx.queued = false; +- spin_unlock_irqrestore(&uap->port.lock, flags); ++ uart_port_unlock_irqrestore(&uap->port, flags); + return; + } + +@@ -576,7 +576,7 @@ static void pl011_dma_tx_callback(void *data) + */ + pl011_start_tx_pio(uap); + +- spin_unlock_irqrestore(&uap->port.lock, flags); ++ uart_port_unlock_irqrestore(&uap->port, flags); + } + + /* +@@ -1004,7 +1004,7 @@ static void pl011_dma_rx_callback(void *data) + * routine to flush out the secondary DMA buffer while + * we immediately trigger the next DMA job. + */ +- spin_lock_irq(&uap->port.lock); ++ uart_port_lock_irq(&uap->port); + /* + * Rx data can be taken by the UART interrupts during + * the DMA irq handler. So we check the residue here. +@@ -1020,7 +1020,7 @@ static void pl011_dma_rx_callback(void *data) + ret = pl011_dma_rx_trigger_dma(uap); + + pl011_dma_rx_chars(uap, pending, lastbuf, false); +- spin_unlock_irq(&uap->port.lock); ++ uart_port_unlock_irq(&uap->port); + /* + * Do this check after we picked the DMA chars so we don't + * get some IRQ immediately from RX. +@@ -1086,11 +1086,11 @@ static void pl011_dma_rx_poll(struct timer_list *t) + if (jiffies_to_msecs(jiffies - dmarx->last_jiffies) + > uap->dmarx.poll_timeout) { + +- spin_lock_irqsave(&uap->port.lock, flags); ++ uart_port_lock_irqsave(&uap->port, &flags); + pl011_dma_rx_stop(uap); + uap->im |= UART011_RXIM; + pl011_write(uap->im, uap, REG_IMSC); +- spin_unlock_irqrestore(&uap->port.lock, flags); ++ uart_port_unlock_irqrestore(&uap->port, flags); + + uap->dmarx.running = false; + dmaengine_terminate_all(rxchan); +@@ -1186,10 +1186,10 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap) + while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy) + cpu_relax(); + +- spin_lock_irq(&uap->port.lock); ++ uart_port_lock_irq(&uap->port); + uap->dmacr &= ~(UART011_DMAONERR | UART011_RXDMAE | UART011_TXDMAE); + pl011_write(uap->dmacr, uap, REG_DMACR); +- spin_unlock_irq(&uap->port.lock); ++ uart_port_unlock_irq(&uap->port); + + if (uap->using_tx_dma) { + /* In theory, this should already be done by pl011_dma_flush_buffer */ +@@ -1400,9 +1400,9 @@ static void pl011_throttle_rx(struct uart_port *port) + { + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + pl011_stop_rx(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void pl011_enable_ms(struct uart_port *port) +@@ -1420,7 +1420,7 @@ __acquires(&uap->port.lock) + { + pl011_fifo_to_tty(uap); + +- spin_unlock(&uap->port.lock); ++ uart_port_unlock(&uap->port); + tty_flip_buffer_push(&uap->port.state->port); + /* + * If we were temporarily out of DMA mode for a while, +@@ -1445,7 +1445,7 @@ __acquires(&uap->port.lock) + #endif + } + } +- spin_lock(&uap->port.lock); ++ uart_port_lock(&uap->port); + } + + static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, +@@ -1551,7 +1551,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id) + unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; + int handled = 0; + +- spin_lock_irqsave(&uap->port.lock, flags); ++ uart_port_lock_irqsave(&uap->port, &flags); + status = pl011_read(uap, REG_RIS) & uap->im; + if (status) { + do { +@@ -1581,7 +1581,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id) + handled = 1; + } + +- spin_unlock_irqrestore(&uap->port.lock, flags); ++ uart_port_unlock_irqrestore(&uap->port, flags); + + return IRQ_RETVAL(handled); + } +@@ -1653,14 +1653,14 @@ static void pl011_break_ctl(struct uart_port *port, int break_state) + unsigned long flags; + unsigned int lcr_h; + +- spin_lock_irqsave(&uap->port.lock, flags); ++ uart_port_lock_irqsave(&uap->port, &flags); + lcr_h = pl011_read(uap, REG_LCRH_TX); + if (break_state == -1) + lcr_h |= UART01x_LCRH_BRK; + else + lcr_h &= ~UART01x_LCRH_BRK; + pl011_write(lcr_h, uap, REG_LCRH_TX); +- spin_unlock_irqrestore(&uap->port.lock, flags); ++ uart_port_unlock_irqrestore(&uap->port, flags); + } + + #ifdef CONFIG_CONSOLE_POLL +@@ -1799,7 +1799,7 @@ static void pl011_enable_interrupts(struct uart_amba_port *uap) + unsigned long flags; + unsigned int i; + +- spin_lock_irqsave(&uap->port.lock, flags); ++ uart_port_lock_irqsave(&uap->port, &flags); + + /* Clear out any spuriously appearing RX interrupts */ + pl011_write(UART011_RTIS | UART011_RXIS, uap, REG_ICR); +@@ -1821,7 +1821,7 @@ static void pl011_enable_interrupts(struct uart_amba_port *uap) + if (!pl011_dma_rx_running(uap)) + uap->im |= UART011_RXIM; + pl011_write(uap->im, uap, REG_IMSC); +- spin_unlock_irqrestore(&uap->port.lock, flags); ++ uart_port_unlock_irqrestore(&uap->port, flags); + } + + static void pl011_unthrottle_rx(struct uart_port *port) +@@ -1829,7 +1829,7 @@ static void pl011_unthrottle_rx(struct uart_port *port) + struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); + unsigned long flags; + +- spin_lock_irqsave(&uap->port.lock, flags); ++ uart_port_lock_irqsave(&uap->port, &flags); + + uap->im = UART011_RTIM; + if (!pl011_dma_rx_running(uap)) +@@ -1837,7 +1837,7 @@ static void pl011_unthrottle_rx(struct uart_port *port) + + pl011_write(uap->im, uap, REG_IMSC); + +- spin_unlock_irqrestore(&uap->port.lock, flags); ++ uart_port_unlock_irqrestore(&uap->port, flags); + } + + static int pl011_startup(struct uart_port *port) +@@ -1857,7 +1857,7 @@ static int pl011_startup(struct uart_port *port) + + pl011_write(uap->vendor->ifls, uap, REG_IFLS); + +- spin_lock_irq(&uap->port.lock); ++ uart_port_lock_irq(&uap->port); + + cr = pl011_read(uap, REG_CR); + cr &= UART011_CR_RTS | UART011_CR_DTR; +@@ -1868,7 +1868,7 @@ static int pl011_startup(struct uart_port *port) + + pl011_write(cr, uap, REG_CR); + +- spin_unlock_irq(&uap->port.lock); ++ uart_port_unlock_irq(&uap->port); + + /* + * initialise the old status of the modem signals +@@ -1929,12 +1929,12 @@ static void pl011_disable_uart(struct uart_amba_port *uap) + unsigned int cr; + + uap->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS); +- spin_lock_irq(&uap->port.lock); ++ uart_port_lock_irq(&uap->port); + cr = pl011_read(uap, REG_CR); + cr &= UART011_CR_RTS | UART011_CR_DTR; + cr |= UART01x_CR_UARTEN | UART011_CR_TXE; + pl011_write(cr, uap, REG_CR); +- spin_unlock_irq(&uap->port.lock); ++ uart_port_unlock_irq(&uap->port); + + /* + * disable break condition and fifos +@@ -1946,14 +1946,14 @@ static void pl011_disable_uart(struct uart_amba_port *uap) + + static void pl011_disable_interrupts(struct uart_amba_port *uap) + { +- spin_lock_irq(&uap->port.lock); ++ uart_port_lock_irq(&uap->port); + + /* mask all interrupts and clear all pending ones */ + uap->im = 0; + pl011_write(uap->im, uap, REG_IMSC); + pl011_write(0xffff, uap, REG_ICR); + +- spin_unlock_irq(&uap->port.lock); ++ uart_port_unlock_irq(&uap->port); + } + + static void pl011_shutdown(struct uart_port *port) +@@ -2098,7 +2098,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, + + bits = tty_get_frame_size(termios->c_cflag); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* + * Update the per-port timeout. +@@ -2172,7 +2172,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, + old_cr |= UART011_CR_RXE; + pl011_write(old_cr, uap, REG_CR); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void +@@ -2190,10 +2190,10 @@ sbsa_uart_set_termios(struct uart_port *port, struct ktermios *termios, + termios->c_cflag &= ~(CMSPAR | CRTSCTS); + termios->c_cflag |= CS8 | CLOCAL; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + uart_update_timeout(port, CS8, uap->fixed_baud); + pl011_setup_status_masks(port, termios); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *pl011_type(struct uart_port *port) +@@ -2328,13 +2328,10 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) + + clk_enable(uap->clk); + +- local_irq_save(flags); +- if (uap->port.sysrq) +- locked = 0; +- else if (oops_in_progress) +- locked = spin_trylock(&uap->port.lock); ++ if (uap->port.sysrq || oops_in_progress) ++ locked = uart_port_trylock_irqsave(&uap->port, &flags); + else +- spin_lock(&uap->port.lock); ++ uart_port_lock_irqsave(&uap->port, &flags); + + /* + * First save the CR then disable the interrupts +@@ -2360,8 +2357,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); ++ uart_port_unlock_irqrestore(&uap->port, flags); + + clk_disable(uap->clk); + } +diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c +index d3cb341f2c55..364599f256db 100644 +--- a/drivers/tty/serial/apbuart.c ++++ b/drivers/tty/serial/apbuart.c +@@ -133,7 +133,7 @@ static irqreturn_t apbuart_int(int irq, void *dev_id) + struct uart_port *port = dev_id; + unsigned int status; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + status = UART_GET_STATUS(port); + if (status & UART_STATUS_DR) +@@ -141,7 +141,7 @@ static irqreturn_t apbuart_int(int irq, void *dev_id) + if (status & UART_STATUS_THE) + apbuart_tx_chars(port); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -228,7 +228,7 @@ static void apbuart_set_termios(struct uart_port *port, + if (termios->c_cflag & CRTSCTS) + cr |= UART_CTRL_FL; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, baud); +@@ -251,7 +251,7 @@ static void apbuart_set_termios(struct uart_port *port, + UART_PUT_SCAL(port, quot); + UART_PUT_CTRL(port, cr); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *apbuart_type(struct uart_port *port) +diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c +index 924c1a89347c..ffd234673177 100644 +--- a/drivers/tty/serial/ar933x_uart.c ++++ b/drivers/tty/serial/ar933x_uart.c +@@ -133,9 +133,9 @@ static unsigned int ar933x_uart_tx_empty(struct uart_port *port) + unsigned long flags; + unsigned int rdata; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + return (rdata & AR933X_UART_DATA_TX_CSR) ? 0 : TIOCSER_TEMT; + } +@@ -220,14 +220,14 @@ static void ar933x_uart_break_ctl(struct uart_port *port, int break_state) + container_of(port, struct ar933x_uart_port, port); + unsigned long flags; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + if (break_state == -1) + ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, + AR933X_UART_CS_TX_BREAK); + else + ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG, + AR933X_UART_CS_TX_BREAK); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + /* +@@ -318,7 +318,7 @@ static void ar933x_uart_set_termios(struct uart_port *port, + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* disable the UART */ + ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG, +@@ -352,7 +352,7 @@ static void ar933x_uart_set_termios(struct uart_port *port, + AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S, + AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + if (tty_termios_baud_rate(new)) + tty_termios_encode_baud_rate(new, baud, baud); +@@ -450,7 +450,7 @@ static irqreturn_t ar933x_uart_interrupt(int irq, void *dev_id) + if ((status & AR933X_UART_CS_HOST_INT) == 0) + return IRQ_NONE; + +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + + status = ar933x_uart_read(up, AR933X_UART_INT_REG); + status &= ar933x_uart_read(up, AR933X_UART_INT_EN_REG); +@@ -468,7 +468,7 @@ static irqreturn_t ar933x_uart_interrupt(int irq, void *dev_id) + ar933x_uart_tx_chars(up); + } + +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + + return IRQ_HANDLED; + } +@@ -485,7 +485,7 @@ static int ar933x_uart_startup(struct uart_port *port) + if (ret) + return ret; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* Enable HOST interrupts */ + ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, +@@ -498,7 +498,7 @@ static int ar933x_uart_startup(struct uart_port *port) + /* Enable RX interrupts */ + ar933x_uart_start_rx_interrupt(up); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + return 0; + } +@@ -632,9 +632,9 @@ static void ar933x_uart_console_write(struct console *co, const char *s, + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock(&up->port.lock); ++ locked = uart_port_trylock(&up->port); + else +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + + /* + * First save the IER then disable the interrupts +@@ -654,7 +654,7 @@ static void ar933x_uart_console_write(struct console *co, const char *s, + ar933x_uart_write(up, AR933X_UART_INT_REG, AR933X_UART_INT_ALLINTS); + + if (locked) +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + + local_irq_restore(flags); + } +diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c +index ad4ae19b6ce3..1aa5b2b49c26 100644 +--- a/drivers/tty/serial/arc_uart.c ++++ b/drivers/tty/serial/arc_uart.c +@@ -279,9 +279,9 @@ static irqreturn_t arc_serial_isr(int irq, void *dev_id) + if (status & RXIENB) { + + /* already in ISR, no need of xx_irqsave */ +- spin_lock(&port->lock); ++ uart_port_lock(port); + arc_serial_rx_chars(port, status); +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + } + + if ((status & TXIENB) && (status & TXEMPTY)) { +@@ -291,12 +291,12 @@ static irqreturn_t arc_serial_isr(int irq, void *dev_id) + */ + UART_TX_IRQ_DISABLE(port); + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + if (!uart_tx_stopped(port)) + arc_serial_tx_chars(port); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + } + + return IRQ_HANDLED; +@@ -366,7 +366,7 @@ arc_serial_set_termios(struct uart_port *port, struct ktermios *new, + uartl = hw_val & 0xFF; + uarth = (hw_val >> 8) & 0xFF; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + UART_ALL_IRQ_DISABLE(port); + +@@ -391,7 +391,7 @@ arc_serial_set_termios(struct uart_port *port, struct ktermios *new, + + uart_update_timeout(port, new->c_cflag, baud); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *arc_serial_type(struct uart_port *port) +@@ -521,9 +521,9 @@ static void arc_serial_console_write(struct console *co, const char *s, + struct uart_port *port = &arc_uart_ports[co->index].port; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + uart_console_write(port, s, count, arc_serial_console_putchar); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static struct console arc_console = { +diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c +index bcca5627afac..5a3f2fc476f9 100644 +--- a/drivers/tty/serial/atmel_serial.c ++++ b/drivers/tty/serial/atmel_serial.c +@@ -861,7 +861,7 @@ static void atmel_complete_tx_dma(void *arg) + struct dma_chan *chan = atmel_port->chan_tx; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + if (chan) + dmaengine_terminate_all(chan); +@@ -893,7 +893,7 @@ static void atmel_complete_tx_dma(void *arg) + atmel_port->tx_done_mask); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void atmel_release_tx_dma(struct uart_port *port) +@@ -1711,9 +1711,9 @@ static void atmel_tasklet_rx_func(struct tasklet_struct *t) + struct uart_port *port = &atmel_port->uart; + + /* The interrupt handler does not take the lock */ +- spin_lock(&port->lock); ++ uart_port_lock(port); + atmel_port->schedule_rx(port); +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + } + + static void atmel_tasklet_tx_func(struct tasklet_struct *t) +@@ -1723,9 +1723,9 @@ static void atmel_tasklet_tx_func(struct tasklet_struct *t) + struct uart_port *port = &atmel_port->uart; + + /* The interrupt handler does not take the lock */ +- spin_lock(&port->lock); ++ uart_port_lock(port); + atmel_port->schedule_tx(port); +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + } + + static void atmel_init_property(struct atmel_uart_port *atmel_port, +@@ -2175,7 +2175,7 @@ static void atmel_set_termios(struct uart_port *port, + } else + mode |= ATMEL_US_PAR_NONE; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + port->read_status_mask = ATMEL_US_OVRE; + if (termios->c_iflag & INPCK) +@@ -2377,22 +2377,22 @@ static void atmel_set_termios(struct uart_port *port, + else + atmel_disable_ms(port); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void atmel_set_ldisc(struct uart_port *port, struct ktermios *termios) + { + if (termios->c_line == N_PPS) { + port->flags |= UPF_HARDPPS_CD; +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + atmel_enable_ms(port); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + } else { + port->flags &= ~UPF_HARDPPS_CD; + if (!UART_ENABLE_MS(port, termios->c_cflag)) { +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + atmel_disable_ms(port); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + } + } + } +diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c +index 44c27e5cefbc..b104c36ce5c0 100644 +--- a/drivers/tty/serial/bcm63xx_uart.c ++++ b/drivers/tty/serial/bcm63xx_uart.c +@@ -201,7 +201,7 @@ static void bcm_uart_break_ctl(struct uart_port *port, int ctl) + unsigned long flags; + unsigned int val; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + val = bcm_uart_readl(port, UART_CTL_REG); + if (ctl) +@@ -210,7 +210,7 @@ static void bcm_uart_break_ctl(struct uart_port *port, int ctl) + val &= ~UART_CTL_XMITBRK_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* +@@ -335,7 +335,7 @@ static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) + unsigned int irqstat; + + port = dev_id; +- spin_lock(&port->lock); ++ uart_port_lock(port); + + irqstat = bcm_uart_readl(port, UART_IR_REG); + if (irqstat & UART_RX_INT_STAT) +@@ -356,7 +356,7 @@ static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) + estat & UART_EXTINP_DCD_MASK); + } + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + return IRQ_HANDLED; + } + +@@ -454,9 +454,9 @@ static void bcm_uart_shutdown(struct uart_port *port) + { + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + bcm_uart_writel(port, 0, UART_IR_REG); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + bcm_uart_disable(port); + bcm_uart_flush(port); +@@ -473,7 +473,7 @@ static void bcm_uart_set_termios(struct uart_port *port, struct ktermios *new, + unsigned long flags; + int tries; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Drain the hot tub fully before we power it off for the winter. */ + for (tries = 3; !bcm_uart_tx_empty(port) && tries; tries--) +@@ -549,7 +549,7 @@ static void bcm_uart_set_termios(struct uart_port *port, struct ktermios *new, + + uart_update_timeout(port, new->c_cflag, baud); + bcm_uart_enable(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* +@@ -715,9 +715,9 @@ static void bcm_console_write(struct console *co, const char *s, + /* bcm_uart_interrupt() already took the lock */ + locked = 0; + } else if (oops_in_progress) { +- locked = spin_trylock(&port->lock); ++ locked = uart_port_trylock(port); + } else { +- spin_lock(&port->lock); ++ uart_port_lock(port); + locked = 1; + } + +@@ -728,7 +728,7 @@ static void bcm_console_write(struct console *co, const char *s, + wait_for_xmitr(port); + + if (locked) +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + local_irq_restore(flags); + } + +diff --git a/drivers/tty/serial/cpm_uart.c b/drivers/tty/serial/cpm_uart.c +index 626423022d62..be4af6eda4c2 100644 +--- a/drivers/tty/serial/cpm_uart.c ++++ b/drivers/tty/serial/cpm_uart.c +@@ -569,7 +569,7 @@ static void cpm_uart_set_termios(struct uart_port *port, + if ((termios->c_cflag & CREAD) == 0) + port->read_status_mask &= ~BD_SC_EMPTY; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + if (IS_SMC(pinfo)) { + unsigned int bits = tty_get_frame_size(termios->c_cflag); +@@ -609,7 +609,7 @@ static void cpm_uart_set_termios(struct uart_port *port, + clk_set_rate(pinfo->clk, baud); + else + cpm_setbrg(pinfo->brg - 1, baud); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *cpm_uart_type(struct uart_port *port) +@@ -1386,9 +1386,9 @@ static void cpm_uart_console_write(struct console *co, const char *s, + cpm_uart_early_write(pinfo, s, count, true); + local_irq_restore(flags); + } else { +- spin_lock_irqsave(&pinfo->port.lock, flags); ++ uart_port_lock_irqsave(&pinfo->port, &flags); + cpm_uart_early_write(pinfo, s, count, true); +- spin_unlock_irqrestore(&pinfo->port.lock, flags); ++ uart_port_unlock_irqrestore(&pinfo->port, flags); + } + } + +diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c +index 128b5479e813..5004125f3045 100644 +--- a/drivers/tty/serial/digicolor-usart.c ++++ b/drivers/tty/serial/digicolor-usart.c +@@ -133,7 +133,7 @@ static void digicolor_uart_rx(struct uart_port *port) + { + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + while (1) { + u8 status, ch, ch_flag; +@@ -172,7 +172,7 @@ static void digicolor_uart_rx(struct uart_port *port) + ch_flag); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + tty_flip_buffer_push(&port->state->port); + } +@@ -185,7 +185,7 @@ static void digicolor_uart_tx(struct uart_port *port) + if (digicolor_uart_tx_full(port)) + return; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + if (port->x_char) { + writeb_relaxed(port->x_char, port->membase + UA_EMI_REC); +@@ -211,7 +211,7 @@ static void digicolor_uart_tx(struct uart_port *port) + uart_write_wakeup(port); + + out: +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static irqreturn_t digicolor_uart_int(int irq, void *dev_id) +@@ -333,7 +333,7 @@ static void digicolor_uart_set_termios(struct uart_port *port, + port->ignore_status_mask |= UA_STATUS_OVERRUN_ERR + | UA_STATUS_PARITY_ERR | UA_STATUS_FRAME_ERR; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + uart_update_timeout(port, termios->c_cflag, baud); + +@@ -341,7 +341,7 @@ static void digicolor_uart_set_termios(struct uart_port *port, + writeb_relaxed(divisor & 0xff, port->membase + UA_HBAUD_LO); + writeb_relaxed(divisor >> 8, port->membase + UA_HBAUD_HI); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *digicolor_uart_type(struct uart_port *port) +@@ -398,14 +398,14 @@ static void digicolor_uart_console_write(struct console *co, const char *c, + int locked = 1; + + if (oops_in_progress) +- locked = spin_trylock_irqsave(&port->lock, flags); ++ locked = uart_port_trylock_irqsave(port, &flags); + else +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + uart_console_write(port, c, n, digicolor_uart_console_putchar); + + if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + /* Wait for transmitter to become empty */ + do { +diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c +index 667f52e83277..6df7af9edc1c 100644 +--- a/drivers/tty/serial/dz.c ++++ b/drivers/tty/serial/dz.c +@@ -268,9 +268,9 @@ static inline void dz_transmit_chars(struct dz_mux *mux) + } + /* If nothing to do or stopped or hardware stopped. */ + if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { +- spin_lock(&dport->port.lock); ++ uart_port_lock(&dport->port); + dz_stop_tx(&dport->port); +- spin_unlock(&dport->port.lock); ++ uart_port_unlock(&dport->port); + return; + } + +@@ -287,9 +287,9 @@ static inline void dz_transmit_chars(struct dz_mux *mux) + + /* Are we are done. */ + if (uart_circ_empty(xmit)) { +- spin_lock(&dport->port.lock); ++ uart_port_lock(&dport->port); + dz_stop_tx(&dport->port); +- spin_unlock(&dport->port.lock); ++ uart_port_unlock(&dport->port); + } + } + +@@ -415,14 +415,14 @@ static int dz_startup(struct uart_port *uport) + return ret; + } + +- spin_lock_irqsave(&dport->port.lock, flags); ++ uart_port_lock_irqsave(&dport->port, &flags); + + /* Enable interrupts. */ + tmp = dz_in(dport, DZ_CSR); + tmp |= DZ_RIE | DZ_TIE; + dz_out(dport, DZ_CSR, tmp); + +- spin_unlock_irqrestore(&dport->port.lock, flags); ++ uart_port_unlock_irqrestore(&dport->port, flags); + + return 0; + } +@@ -443,9 +443,9 @@ static void dz_shutdown(struct uart_port *uport) + int irq_guard; + u16 tmp; + +- spin_lock_irqsave(&dport->port.lock, flags); ++ uart_port_lock_irqsave(&dport->port, &flags); + dz_stop_tx(&dport->port); +- spin_unlock_irqrestore(&dport->port.lock, flags); ++ uart_port_unlock_irqrestore(&dport->port, flags); + + irq_guard = atomic_add_return(-1, &mux->irq_guard); + if (!irq_guard) { +@@ -491,14 +491,14 @@ static void dz_break_ctl(struct uart_port *uport, int break_state) + unsigned long flags; + unsigned short tmp, mask = 1 << dport->port.line; + +- spin_lock_irqsave(&uport->lock, flags); ++ uart_port_lock_irqsave(uport, &flags); + tmp = dz_in(dport, DZ_TCR); + if (break_state) + tmp |= mask; + else + tmp &= ~mask; + dz_out(dport, DZ_TCR, tmp); +- spin_unlock_irqrestore(&uport->lock, flags); ++ uart_port_unlock_irqrestore(uport, flags); + } + + static int dz_encode_baud_rate(unsigned int baud) +@@ -608,7 +608,7 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, + if (termios->c_cflag & CREAD) + cflag |= DZ_RXENAB; + +- spin_lock_irqsave(&dport->port.lock, flags); ++ uart_port_lock_irqsave(&dport->port, &flags); + + uart_update_timeout(uport, termios->c_cflag, baud); + +@@ -631,7 +631,7 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, + if (termios->c_iflag & IGNBRK) + dport->port.ignore_status_mask |= DZ_BREAK; + +- spin_unlock_irqrestore(&dport->port.lock, flags); ++ uart_port_unlock_irqrestore(&dport->port, flags); + } + + /* +@@ -645,12 +645,12 @@ static void dz_pm(struct uart_port *uport, unsigned int state, + struct dz_port *dport = to_dport(uport); + unsigned long flags; + +- spin_lock_irqsave(&dport->port.lock, flags); ++ uart_port_lock_irqsave(&dport->port, &flags); + if (state < 3) + dz_start_tx(&dport->port); + else + dz_stop_tx(&dport->port); +- spin_unlock_irqrestore(&dport->port.lock, flags); ++ uart_port_unlock_irqrestore(&dport->port, flags); + } + + +@@ -811,7 +811,7 @@ static void dz_console_putchar(struct uart_port *uport, unsigned char ch) + unsigned short csr, tcr, trdy, mask; + int loops = 10000; + +- spin_lock_irqsave(&dport->port.lock, flags); ++ uart_port_lock_irqsave(&dport->port, &flags); + csr = dz_in(dport, DZ_CSR); + dz_out(dport, DZ_CSR, csr & ~DZ_TIE); + tcr = dz_in(dport, DZ_TCR); +@@ -819,7 +819,7 @@ static void dz_console_putchar(struct uart_port *uport, unsigned char ch) + mask = tcr; + dz_out(dport, DZ_TCR, mask); + iob(); +- spin_unlock_irqrestore(&dport->port.lock, flags); ++ uart_port_unlock_irqrestore(&dport->port, flags); + + do { + trdy = dz_in(dport, DZ_CSR); +diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c +index 249cb380c3c6..7fa809a405e8 100644 +--- a/drivers/tty/serial/fsl_linflexuart.c ++++ b/drivers/tty/serial/fsl_linflexuart.c +@@ -203,7 +203,7 @@ static irqreturn_t linflex_txint(int irq, void *dev_id) + struct circ_buf *xmit = &sport->state->xmit; + unsigned long flags; + +- spin_lock_irqsave(&sport->lock, flags); ++ uart_port_lock_irqsave(sport, &flags); + + if (sport->x_char) { + linflex_put_char(sport, sport->x_char); +@@ -217,7 +217,7 @@ static irqreturn_t linflex_txint(int irq, void *dev_id) + + linflex_transmit_buffer(sport); + out: +- spin_unlock_irqrestore(&sport->lock, flags); ++ uart_port_unlock_irqrestore(sport, flags); + return IRQ_HANDLED; + } + +@@ -230,7 +230,7 @@ static irqreturn_t linflex_rxint(int irq, void *dev_id) + unsigned char rx; + bool brk; + +- spin_lock_irqsave(&sport->lock, flags); ++ uart_port_lock_irqsave(sport, &flags); + + status = readl(sport->membase + UARTSR); + while (status & LINFLEXD_UARTSR_RMB) { +@@ -266,7 +266,7 @@ static irqreturn_t linflex_rxint(int irq, void *dev_id) + } + } + +- spin_unlock_irqrestore(&sport->lock, flags); ++ uart_port_unlock_irqrestore(sport, flags); + + tty_flip_buffer_push(port); + +@@ -369,11 +369,11 @@ static int linflex_startup(struct uart_port *port) + int ret = 0; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + linflex_setup_watermark(port); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + ret = devm_request_irq(port->dev, port->irq, linflex_int, 0, + DRIVER_NAME, port); +@@ -386,14 +386,14 @@ static void linflex_shutdown(struct uart_port *port) + unsigned long ier; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* disable interrupts */ + ier = readl(port->membase + LINIER); + ier &= ~(LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE); + writel(ier, port->membase + LINIER); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + devm_free_irq(port->dev, port->irq, port); + } +@@ -474,7 +474,7 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, + cr &= ~LINFLEXD_UARTCR_PCE; + } + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + port->read_status_mask = 0; + +@@ -507,7 +507,7 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, + + writel(cr1, port->membase + LINCR1); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *linflex_type(struct uart_port *port) +@@ -646,14 +646,14 @@ linflex_console_write(struct console *co, const char *s, unsigned int count) + if (sport->sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock_irqsave(&sport->lock, flags); ++ locked = uart_port_trylock_irqsave(sport, &flags); + else +- spin_lock_irqsave(&sport->lock, flags); ++ uart_port_lock_irqsave(sport, &flags); + + linflex_string_write(sport, s, count); + + if (locked) +- spin_unlock_irqrestore(&sport->lock, flags); ++ uart_port_unlock_irqrestore(sport, flags); + } + + /* +diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c +index 11f6fabce051..1f7478c72e66 100644 +--- a/drivers/tty/serial/fsl_lpuart.c ++++ b/drivers/tty/serial/fsl_lpuart.c +@@ -547,9 +547,9 @@ static void lpuart_dma_tx_complete(void *arg) + struct dma_chan *chan = sport->dma_tx_chan; + unsigned long flags; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + if (!sport->dma_tx_in_progress) { +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + return; + } + +@@ -558,7 +558,7 @@ static void lpuart_dma_tx_complete(void *arg) + + uart_xmit_advance(&sport->port, sport->dma_tx_bytes); + sport->dma_tx_in_progress = false; +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); +@@ -568,12 +568,12 @@ static void lpuart_dma_tx_complete(void *arg) + return; + } + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + if (!lpuart_stopped_or_empty(&sport->port)) + lpuart_dma_tx(sport); + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + static dma_addr_t lpuart_dma_datareg_addr(struct lpuart_port *sport) +@@ -666,7 +666,7 @@ static int lpuart_poll_init(struct uart_port *port) + + sport->port.fifosize = 0; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + /* Disable Rx & Tx */ + writeb(0, sport->port.membase + UARTCR2); + +@@ -690,7 +690,7 @@ static int lpuart_poll_init(struct uart_port *port) + + /* Enable Rx and Tx */ + writeb(UARTCR2_RE | UARTCR2_TE, sport->port.membase + UARTCR2); +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + return 0; + } +@@ -718,7 +718,7 @@ static int lpuart32_poll_init(struct uart_port *port) + + sport->port.fifosize = 0; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + /* Disable Rx & Tx */ + lpuart32_write(&sport->port, 0, UARTCTRL); +@@ -739,7 +739,7 @@ static int lpuart32_poll_init(struct uart_port *port) + + /* Enable Rx and Tx */ + lpuart32_write(&sport->port, UARTCTRL_RE | UARTCTRL_TE, UARTCTRL); +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + return 0; + } +@@ -902,9 +902,9 @@ static unsigned int lpuart32_tx_empty(struct uart_port *port) + + static void lpuart_txint(struct lpuart_port *sport) + { +- spin_lock(&sport->port.lock); ++ uart_port_lock(&sport->port); + lpuart_transmit_buffer(sport); +- spin_unlock(&sport->port.lock); ++ uart_port_unlock(&sport->port); + } + + static void lpuart_rxint(struct lpuart_port *sport) +@@ -913,7 +913,7 @@ static void lpuart_rxint(struct lpuart_port *sport) + struct tty_port *port = &sport->port.state->port; + unsigned char rx, sr; + +- spin_lock(&sport->port.lock); ++ uart_port_lock(&sport->port); + + while (!(readb(sport->port.membase + UARTSFIFO) & UARTSFIFO_RXEMPT)) { + flg = TTY_NORMAL; +@@ -979,9 +979,9 @@ static void lpuart_rxint(struct lpuart_port *sport) + + static void lpuart32_txint(struct lpuart_port *sport) + { +- spin_lock(&sport->port.lock); ++ uart_port_lock(&sport->port); + lpuart32_transmit_buffer(sport); +- spin_unlock(&sport->port.lock); ++ uart_port_unlock(&sport->port); + } + + static void lpuart32_rxint(struct lpuart_port *sport) +@@ -991,7 +991,7 @@ static void lpuart32_rxint(struct lpuart_port *sport) + unsigned long rx, sr; + bool is_break; + +- spin_lock(&sport->port.lock); ++ uart_port_lock(&sport->port); + + while (!(lpuart32_read(&sport->port, UARTFIFO) & UARTFIFO_RXEMPT)) { + flg = TTY_NORMAL; +@@ -1193,12 +1193,12 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) + + async_tx_ack(sport->dma_rx_desc); + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + dmastat = dmaengine_tx_status(chan, sport->dma_rx_cookie, &state); + if (dmastat == DMA_ERROR) { + dev_err(sport->port.dev, "Rx DMA transfer failed!\n"); +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + return; + } + +@@ -1267,7 +1267,7 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) + dma_sync_sg_for_device(chan->device->dev, &sport->rx_sgl, 1, + DMA_FROM_DEVICE); + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + tty_flip_buffer_push(port); + if (!sport->dma_idle_int) +@@ -1358,9 +1358,9 @@ static void lpuart_timer_func(struct timer_list *t) + mod_timer(&sport->lpuart_timer, + jiffies + sport->dma_rx_timeout); + +- if (spin_trylock_irqsave(&sport->port.lock, flags)) { ++ if (uart_port_trylock_irqsave(&sport->port, &flags)) { + sport->last_residue = state.residue; +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + } + +@@ -1838,14 +1838,14 @@ static void lpuart_hw_setup(struct lpuart_port *sport) + { + unsigned long flags; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + lpuart_setup_watermark_enable(sport); + + lpuart_rx_dma_startup(sport); + lpuart_tx_dma_startup(sport); + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + static int lpuart_startup(struct uart_port *port) +@@ -1895,7 +1895,7 @@ static void lpuart32_hw_setup(struct lpuart_port *sport) + { + unsigned long flags; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + lpuart32_hw_disable(sport); + +@@ -1905,7 +1905,7 @@ static void lpuart32_hw_setup(struct lpuart_port *sport) + lpuart32_setup_watermark_enable(sport); + lpuart32_configure(sport); + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + static int lpuart32_startup(struct uart_port *port) +@@ -1977,7 +1977,7 @@ static void lpuart_shutdown(struct uart_port *port) + unsigned char temp; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* disable Rx/Tx and interrupts */ + temp = readb(port->membase + UARTCR2); +@@ -1985,7 +1985,7 @@ static void lpuart_shutdown(struct uart_port *port) + UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE); + writeb(temp, port->membase + UARTCR2); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + lpuart_dma_shutdown(sport); + } +@@ -1997,7 +1997,7 @@ static void lpuart32_shutdown(struct uart_port *port) + unsigned long temp; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* clear status */ + temp = lpuart32_read(&sport->port, UARTSTAT); +@@ -2014,7 +2014,7 @@ static void lpuart32_shutdown(struct uart_port *port) + UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE | UARTCTRL_SBK); + lpuart32_write(port, temp, UARTCTRL); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + lpuart_dma_shutdown(sport); + } +@@ -2114,7 +2114,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, + if (old && sport->lpuart_dma_rx_use) + lpuart_dma_rx_free(&sport->port); + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + sport->port.read_status_mask = 0; + if (termios->c_iflag & INPCK) +@@ -2169,7 +2169,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, + sport->lpuart_dma_rx_use = false; + } + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + static void __lpuart32_serial_setbrg(struct uart_port *port, +@@ -2358,7 +2358,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, + if (old && sport->lpuart_dma_rx_use) + lpuart_dma_rx_free(&sport->port); + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + sport->port.read_status_mask = 0; + if (termios->c_iflag & INPCK) +@@ -2416,7 +2416,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, + sport->lpuart_dma_rx_use = false; + } + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + static const char *lpuart_type(struct uart_port *port) +@@ -2534,9 +2534,9 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) + int locked = 1; + + if (oops_in_progress) +- locked = spin_trylock_irqsave(&sport->port.lock, flags); ++ locked = uart_port_trylock_irqsave(&sport->port, &flags); + else +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + /* first save CR2 and then disable interrupts */ + cr2 = old_cr2 = readb(sport->port.membase + UARTCR2); +@@ -2552,7 +2552,7 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) + writeb(old_cr2, sport->port.membase + UARTCR2); + + if (locked) +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + static void +@@ -2564,9 +2564,9 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count) + int locked = 1; + + if (oops_in_progress) +- locked = spin_trylock_irqsave(&sport->port.lock, flags); ++ locked = uart_port_trylock_irqsave(&sport->port, &flags); + else +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + /* first save CR2 and then disable interrupts */ + cr = old_cr = lpuart32_read(&sport->port, UARTCTRL); +@@ -2582,7 +2582,7 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count) + lpuart32_write(&sport->port, old_cr, UARTCTRL); + + if (locked) +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + /* +@@ -3158,7 +3158,7 @@ static int lpuart_suspend(struct device *dev) + uart_suspend_port(&lpuart_reg, &sport->port); + + if (lpuart_uport_is_active(sport)) { +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + if (lpuart_is_32(sport)) { + /* disable Rx/Tx and interrupts */ + temp = lpuart32_read(&sport->port, UARTCTRL); +@@ -3170,7 +3170,7 @@ static int lpuart_suspend(struct device *dev) + temp &= ~(UARTCR2_TE | UARTCR2_TIE | UARTCR2_TCIE); + writeb(temp, sport->port.membase + UARTCR2); + } +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + if (sport->lpuart_dma_rx_use) { + /* +@@ -3183,7 +3183,7 @@ static int lpuart_suspend(struct device *dev) + lpuart_dma_rx_free(&sport->port); + + /* Disable Rx DMA to use UART port as wakeup source */ +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + if (lpuart_is_32(sport)) { + temp = lpuart32_read(&sport->port, UARTBAUD); + lpuart32_write(&sport->port, temp & ~UARTBAUD_RDMAE, +@@ -3192,11 +3192,11 @@ static int lpuart_suspend(struct device *dev) + writeb(readb(sport->port.membase + UARTCR5) & + ~UARTCR5_RDMAS, sport->port.membase + UARTCR5); + } +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + if (sport->lpuart_dma_tx_use) { +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + if (lpuart_is_32(sport)) { + temp = lpuart32_read(&sport->port, UARTBAUD); + temp &= ~UARTBAUD_TDMAE; +@@ -3206,7 +3206,7 @@ static int lpuart_suspend(struct device *dev) + temp &= ~UARTCR5_TDMAS; + writeb(temp, sport->port.membase + UARTCR5); + } +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + sport->dma_tx_in_progress = false; + dmaengine_terminate_sync(sport->dma_tx_chan); + } +diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c +index 819f957b6b84..a75eafbcbea3 100644 +--- a/drivers/tty/serial/icom.c ++++ b/drivers/tty/serial/icom.c +@@ -929,7 +929,7 @@ static inline void check_modem_status(struct icom_port *icom_port) + char delta_status; + unsigned char status; + +- spin_lock(&icom_port->uart_port.lock); ++ uart_port_lock(&icom_port->uart_port); + + /*modem input register */ + status = readb(&icom_port->dram->isr); +@@ -951,7 +951,7 @@ static inline void check_modem_status(struct icom_port *icom_port) + port.delta_msr_wait); + old_status = status; + } +- spin_unlock(&icom_port->uart_port.lock); ++ uart_port_unlock(&icom_port->uart_port); + } + + static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) +@@ -1093,7 +1093,7 @@ static void process_interrupt(u16 port_int_reg, + struct icom_port *icom_port) + { + +- spin_lock(&icom_port->uart_port.lock); ++ uart_port_lock(&icom_port->uart_port); + trace(icom_port, "INTERRUPT", port_int_reg); + + if (port_int_reg & (INT_XMIT_COMPLETED | INT_XMIT_DISABLED)) +@@ -1102,7 +1102,7 @@ static void process_interrupt(u16 port_int_reg, + if (port_int_reg & INT_RCV_COMPLETED) + recv_interrupt(port_int_reg, icom_port); + +- spin_unlock(&icom_port->uart_port.lock); ++ uart_port_unlock(&icom_port->uart_port); + } + + static irqreturn_t icom_interrupt(int irq, void *dev_id) +@@ -1186,14 +1186,14 @@ static unsigned int icom_tx_empty(struct uart_port *port) + int ret; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (le16_to_cpu(icom_port->statStg->xmit[0].flags) & + SA_FLAGS_READY_TO_XMIT) + ret = TIOCSER_TEMT; + else + ret = 0; + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + return ret; + } + +@@ -1276,7 +1276,7 @@ static void icom_send_xchar(struct uart_port *port, char ch) + + /* wait .1 sec to send char */ + for (index = 0; index < 10; index++) { +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + xdata = readb(&icom_port->dram->xchar); + if (xdata == 0x00) { + trace(icom_port, "QUICK_WRITE", 0); +@@ -1284,10 +1284,10 @@ static void icom_send_xchar(struct uart_port *port, char ch) + + /* flush write operation */ + xdata = readb(&icom_port->dram->xchar); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + break; + } +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + msleep(10); + } + } +@@ -1307,7 +1307,7 @@ static void icom_break(struct uart_port *port, int break_state) + unsigned char cmdReg; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + trace(icom_port, "BREAK", 0); + cmdReg = readb(&icom_port->dram->CmdReg); + if (break_state == -1) { +@@ -1315,7 +1315,7 @@ static void icom_break(struct uart_port *port, int break_state) + } else { + writeb(cmdReg & ~CMD_SND_BREAK, &icom_port->dram->CmdReg); + } +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int icom_open(struct uart_port *port) +@@ -1365,7 +1365,7 @@ static void icom_set_termios(struct uart_port *port, struct ktermios *termios, + unsigned long offset; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + trace(icom_port, "CHANGE_SPEED", 0); + + cflag = termios->c_cflag; +@@ -1516,7 +1516,7 @@ static void icom_set_termios(struct uart_port *port, struct ktermios *termios, + trace(icom_port, "XR_ENAB", 0); + writeb(CMD_XMIT_RCV_ENABLE, &icom_port->dram->CmdReg); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *icom_type(struct uart_port *port) +diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c +index 70641b53e008..88a45ba65123 100644 +--- a/drivers/tty/serial/imx.c ++++ b/drivers/tty/serial/imx.c +@@ -596,7 +596,7 @@ static void imx_uart_dma_tx_callback(void *data) + unsigned long flags; + u32 ucr1; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); + +@@ -621,7 +621,7 @@ static void imx_uart_dma_tx_callback(void *data) + imx_uart_writel(sport, ucr4, UCR4); + } + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + /* called with port.lock taken and irqs off */ +@@ -795,11 +795,11 @@ static irqreturn_t imx_uart_rtsint(int irq, void *dev_id) + struct imx_port *sport = dev_id; + irqreturn_t ret; + +- spin_lock(&sport->port.lock); ++ uart_port_lock(&sport->port); + + ret = __imx_uart_rtsint(irq, dev_id); + +- spin_unlock(&sport->port.lock); ++ uart_port_unlock(&sport->port); + + return ret; + } +@@ -808,9 +808,9 @@ static irqreturn_t imx_uart_txint(int irq, void *dev_id) + { + struct imx_port *sport = dev_id; + +- spin_lock(&sport->port.lock); ++ uart_port_lock(&sport->port); + imx_uart_transmit_buffer(sport); +- spin_unlock(&sport->port.lock); ++ uart_port_unlock(&sport->port); + return IRQ_HANDLED; + } + +@@ -924,11 +924,11 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id) + struct imx_port *sport = dev_id; + irqreturn_t ret; + +- spin_lock(&sport->port.lock); ++ uart_port_lock(&sport->port); + + ret = __imx_uart_rxint(irq, dev_id); + +- spin_unlock(&sport->port.lock); ++ uart_port_unlock(&sport->port); + + return ret; + } +@@ -991,7 +991,7 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id) + unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4; + irqreturn_t ret = IRQ_NONE; + +- spin_lock(&sport->port.lock); ++ uart_port_lock(&sport->port); + + usr1 = imx_uart_readl(sport, USR1); + usr2 = imx_uart_readl(sport, USR2); +@@ -1061,7 +1061,7 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id) + ret = IRQ_HANDLED; + } + +- spin_unlock(&sport->port.lock); ++ uart_port_unlock(&sport->port); + + return ret; + } +@@ -1144,7 +1144,7 @@ static void imx_uart_break_ctl(struct uart_port *port, int break_state) + unsigned long flags; + u32 ucr1; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + ucr1 = imx_uart_readl(sport, UCR1) & ~UCR1_SNDBRK; + +@@ -1153,7 +1153,7 @@ static void imx_uart_break_ctl(struct uart_port *port, int break_state) + + imx_uart_writel(sport, ucr1, UCR1); + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + /* +@@ -1166,9 +1166,9 @@ static void imx_uart_timeout(struct timer_list *t) + unsigned long flags; + + if (sport->port.state) { +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + imx_uart_mctrl_check(sport); +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); + } +@@ -1198,9 +1198,9 @@ static void imx_uart_dma_rx_callback(void *data) + status = dmaengine_tx_status(chan, sport->rx_cookie, &state); + + if (status == DMA_ERROR) { +- spin_lock(&sport->port.lock); ++ uart_port_lock(&sport->port); + imx_uart_clear_rx_errors(sport); +- spin_unlock(&sport->port.lock); ++ uart_port_unlock(&sport->port); + return; + } + +@@ -1229,9 +1229,9 @@ static void imx_uart_dma_rx_callback(void *data) + r_bytes = rx_ring->head - rx_ring->tail; + + /* If we received something, check for 0xff flood */ +- spin_lock(&sport->port.lock); ++ uart_port_lock(&sport->port); + imx_uart_check_flood(sport, imx_uart_readl(sport, USR2)); +- spin_unlock(&sport->port.lock); ++ uart_port_unlock(&sport->port); + + if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) { + +@@ -1504,7 +1504,7 @@ static int imx_uart_startup(struct uart_port *port) + if (!uart_console(port) && imx_uart_dma_init(sport) == 0) + dma_is_inited = 1; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + /* Reset fifo's and state machines */ + imx_uart_soft_reset(sport); +@@ -1578,7 +1578,7 @@ static int imx_uart_startup(struct uart_port *port) + + imx_uart_disable_loopback_rs485(sport); + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + return 0; + } +@@ -1603,21 +1603,21 @@ static void imx_uart_shutdown(struct uart_port *port) + sport->dma_is_rxing = 0; + } + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + imx_uart_stop_tx(port); + imx_uart_stop_rx(port); + imx_uart_disable_dma(sport); +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + imx_uart_dma_exit(sport); + } + + mctrl_gpio_disable_ms(sport->gpios); + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + ucr2 = imx_uart_readl(sport, UCR2); + ucr2 &= ~(UCR2_TXEN | UCR2_ATEN); + imx_uart_writel(sport, ucr2, UCR2); +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + /* + * Stop our timer. +@@ -1628,7 +1628,7 @@ static void imx_uart_shutdown(struct uart_port *port) + * Disable all interrupts, port and break condition. + */ + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + ucr1 = imx_uart_readl(sport, UCR1); + ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_RXDMAEN | +@@ -1650,7 +1650,7 @@ static void imx_uart_shutdown(struct uart_port *port) + ucr4 &= ~UCR4_TCEN; + imx_uart_writel(sport, ucr4, UCR4); + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + clk_disable_unprepare(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); +@@ -1713,7 +1713,7 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + quot = uart_get_divisor(port, baud); + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + /* + * Read current UCR2 and save it for future use, then clear all the bits +@@ -1841,7 +1841,7 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, + if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) + imx_uart_enable_ms(&sport->port); + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + static const char *imx_uart_type(struct uart_port *port) +@@ -1903,7 +1903,7 @@ static int imx_uart_poll_init(struct uart_port *port) + + imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + /* + * Be careful about the order of enabling bits here. First enable the +@@ -1931,7 +1931,7 @@ static int imx_uart_poll_init(struct uart_port *port) + imx_uart_writel(sport, ucr1 | UCR1_RRDYEN, UCR1); + imx_uart_writel(sport, ucr2 | UCR2_ATEN, UCR2); + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + return 0; + } +@@ -2051,9 +2051,9 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count) + if (sport->port.sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock_irqsave(&sport->port.lock, flags); ++ locked = uart_port_trylock_irqsave(&sport->port, &flags); + else +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + /* + * First, save UCR1/2/3 and then disable interrupts +@@ -2081,7 +2081,7 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count) + imx_uart_ucrs_restore(sport, &old_ucr); + + if (locked) +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + /* +@@ -2239,10 +2239,10 @@ static enum hrtimer_restart imx_trigger_start_tx(struct hrtimer *t) + struct imx_port *sport = container_of(t, struct imx_port, trigger_start_tx); + unsigned long flags; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + if (sport->tx_state == WAIT_AFTER_RTS) + imx_uart_start_tx(&sport->port); +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + return HRTIMER_NORESTART; + } +@@ -2252,10 +2252,10 @@ static enum hrtimer_restart imx_trigger_stop_tx(struct hrtimer *t) + struct imx_port *sport = container_of(t, struct imx_port, trigger_stop_tx); + unsigned long flags; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + if (sport->tx_state == WAIT_AFTER_SEND) + imx_uart_stop_tx(&sport->port); +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + return HRTIMER_NORESTART; + } +@@ -2583,9 +2583,9 @@ static void imx_uart_restore_context(struct imx_port *sport) + { + unsigned long flags; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + if (!sport->context_saved) { +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + return; + } + +@@ -2600,7 +2600,7 @@ static void imx_uart_restore_context(struct imx_port *sport) + imx_uart_writel(sport, sport->saved_reg[2], UCR3); + imx_uart_writel(sport, sport->saved_reg[3], UCR4); + sport->context_saved = false; +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + static void imx_uart_save_context(struct imx_port *sport) +@@ -2608,7 +2608,7 @@ static void imx_uart_save_context(struct imx_port *sport) + unsigned long flags; + + /* Save necessary regs */ +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + sport->saved_reg[0] = imx_uart_readl(sport, UCR1); + sport->saved_reg[1] = imx_uart_readl(sport, UCR2); + sport->saved_reg[2] = imx_uart_readl(sport, UCR3); +@@ -2620,7 +2620,7 @@ static void imx_uart_save_context(struct imx_port *sport) + sport->saved_reg[8] = imx_uart_readl(sport, UBMR); + sport->saved_reg[9] = imx_uart_readl(sport, IMX21_UTS); + sport->context_saved = true; +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + static void imx_uart_enable_wakeup(struct imx_port *sport, bool on) +diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c +index 845ff706bc59..320b29cd4683 100644 +--- a/drivers/tty/serial/ip22zilog.c ++++ b/drivers/tty/serial/ip22zilog.c +@@ -432,7 +432,7 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) + unsigned char r3; + bool push = false; + +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + r3 = read_zsreg(channel, R3); + + /* Channel A */ +@@ -448,7 +448,7 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) + if (r3 & CHATxIP) + ip22zilog_transmit_chars(up, channel); + } +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + + if (push) + tty_flip_buffer_push(&up->port.state->port); +@@ -458,7 +458,7 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + push = false; + +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { + writeb(RES_H_IUS, &channel->control); + ZSDELAY(); +@@ -471,7 +471,7 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) + if (r3 & CHBTxIP) + ip22zilog_transmit_chars(up, channel); + } +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + + if (push) + tty_flip_buffer_push(&up->port.state->port); +@@ -504,11 +504,11 @@ static unsigned int ip22zilog_tx_empty(struct uart_port *port) + unsigned char status; + unsigned int ret; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + status = ip22zilog_read_channel_status(port); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (status & Tx_BUF_EMP) + ret = TIOCSER_TEMT; +@@ -664,7 +664,7 @@ static void ip22zilog_break_ctl(struct uart_port *port, int break_state) + else + clear_bits |= SND_BRK; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + new_reg = (up->curregs[R5] | set_bits) & ~clear_bits; + if (new_reg != up->curregs[R5]) { +@@ -674,7 +674,7 @@ static void ip22zilog_break_ctl(struct uart_port *port, int break_state) + write_zsreg(channel, R5, up->curregs[R5]); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void __ip22zilog_reset(struct uart_ip22zilog_port *up) +@@ -735,9 +735,9 @@ static int ip22zilog_startup(struct uart_port *port) + if (ZS_IS_CONS(up)) + return 0; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + __ip22zilog_startup(up); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + return 0; + } + +@@ -775,7 +775,7 @@ static void ip22zilog_shutdown(struct uart_port *port) + if (ZS_IS_CONS(up)) + return; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + channel = ZILOG_CHANNEL_FROM_PORT(port); + +@@ -788,7 +788,7 @@ static void ip22zilog_shutdown(struct uart_port *port) + up->curregs[R5] &= ~SND_BRK; + ip22zilog_maybe_update_regs(up, channel); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* Shared by TTY driver and serial console setup. The port lock is held +@@ -880,7 +880,7 @@ ip22zilog_set_termios(struct uart_port *port, struct ktermios *termios, + + baud = uart_get_baud_rate(port, termios, old, 1200, 76800); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); + +@@ -894,7 +894,7 @@ ip22zilog_set_termios(struct uart_port *port, struct ktermios *termios, + ip22zilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port)); + uart_update_timeout(port, termios->c_cflag, baud); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static const char *ip22zilog_type(struct uart_port *port) +@@ -1016,10 +1016,10 @@ ip22zilog_console_write(struct console *con, const char *s, unsigned int count) + struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index]; + unsigned long flags; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + uart_console_write(&up->port, s, count, ip22zilog_put_char); + udelay(2); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static int __init ip22zilog_console_setup(struct console *con, char *options) +@@ -1034,13 +1034,13 @@ static int __init ip22zilog_console_setup(struct console *con, char *options) + + printk(KERN_INFO "Console: ttyS%d (IP22-Zilog)\n", con->index); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + up->curregs[R15] |= BRKIE; + + __ip22zilog_startup(up); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); +diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c +index 0c78f66276cd..2bd640428970 100644 +--- a/drivers/tty/serial/jsm/jsm_neo.c ++++ b/drivers/tty/serial/jsm/jsm_neo.c +@@ -816,9 +816,9 @@ static void neo_parse_isr(struct jsm_board *brd, u32 port) + /* Parse any modem signal changes */ + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "MOD_STAT: sending to parse_modem_sigs\n"); +- spin_lock_irqsave(&ch->uart_port.lock, lock_flags); ++ uart_port_lock_irqsave(&ch->uart_port, &lock_flags); + neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr)); +- spin_unlock_irqrestore(&ch->uart_port.lock, lock_flags); ++ uart_port_unlock_irqrestore(&ch->uart_port, lock_flags); + } + } + +diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c +index 222afc270c88..ce0fef7e2c66 100644 +--- a/drivers/tty/serial/jsm/jsm_tty.c ++++ b/drivers/tty/serial/jsm/jsm_tty.c +@@ -152,14 +152,14 @@ static void jsm_tty_send_xchar(struct uart_port *port, char ch) + container_of(port, struct jsm_channel, uart_port); + struct ktermios *termios; + +- spin_lock_irqsave(&port->lock, lock_flags); ++ uart_port_lock_irqsave(port, &lock_flags); + termios = &port->state->port.tty->termios; + if (ch == termios->c_cc[VSTART]) + channel->ch_bd->bd_ops->send_start_character(channel); + + if (ch == termios->c_cc[VSTOP]) + channel->ch_bd->bd_ops->send_stop_character(channel); +- spin_unlock_irqrestore(&port->lock, lock_flags); ++ uart_port_unlock_irqrestore(port, lock_flags); + } + + static void jsm_tty_stop_rx(struct uart_port *port) +@@ -176,13 +176,13 @@ static void jsm_tty_break(struct uart_port *port, int break_state) + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); + +- spin_lock_irqsave(&port->lock, lock_flags); ++ uart_port_lock_irqsave(port, &lock_flags); + if (break_state == -1) + channel->ch_bd->bd_ops->send_break(channel); + else + channel->ch_bd->bd_ops->clear_break(channel); + +- spin_unlock_irqrestore(&port->lock, lock_flags); ++ uart_port_unlock_irqrestore(port, lock_flags); + } + + static int jsm_tty_open(struct uart_port *port) +@@ -241,7 +241,7 @@ static int jsm_tty_open(struct uart_port *port) + channel->ch_cached_lsr = 0; + channel->ch_stops_sent = 0; + +- spin_lock_irqsave(&port->lock, lock_flags); ++ uart_port_lock_irqsave(port, &lock_flags); + termios = &port->state->port.tty->termios; + channel->ch_c_cflag = termios->c_cflag; + channel->ch_c_iflag = termios->c_iflag; +@@ -261,7 +261,7 @@ static int jsm_tty_open(struct uart_port *port) + jsm_carrier(channel); + + channel->ch_open_count++; +- spin_unlock_irqrestore(&port->lock, lock_flags); ++ uart_port_unlock_irqrestore(port, lock_flags); + + jsm_dbg(OPEN, &channel->ch_bd->pci_dev, "finish\n"); + return 0; +@@ -307,7 +307,7 @@ static void jsm_tty_set_termios(struct uart_port *port, + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); + +- spin_lock_irqsave(&port->lock, lock_flags); ++ uart_port_lock_irqsave(port, &lock_flags); + channel->ch_c_cflag = termios->c_cflag; + channel->ch_c_iflag = termios->c_iflag; + channel->ch_c_oflag = termios->c_oflag; +@@ -317,7 +317,7 @@ static void jsm_tty_set_termios(struct uart_port *port, + + channel->ch_bd->bd_ops->param(channel); + jsm_carrier(channel); +- spin_unlock_irqrestore(&port->lock, lock_flags); ++ uart_port_unlock_irqrestore(port, lock_flags); + } + + static const char *jsm_tty_type(struct uart_port *port) +diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c +index d881cdd2a58f..a25ab1efe38f 100644 +--- a/drivers/tty/serial/liteuart.c ++++ b/drivers/tty/serial/liteuart.c +@@ -139,13 +139,13 @@ static irqreturn_t liteuart_interrupt(int irq, void *data) + * if polling, the context would be "in_serving_softirq", so use + * irq[save|restore] spin_lock variants to cover all possibilities + */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + isr = litex_read8(port->membase + OFF_EV_PENDING) & uart->irq_reg; + if (isr & EV_RX) + liteuart_rx_chars(port); + if (isr & EV_TX) + liteuart_tx_chars(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return IRQ_RETVAL(isr); + } +@@ -195,10 +195,10 @@ static int liteuart_startup(struct uart_port *port) + } + } + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + /* only enabling rx irqs during startup */ + liteuart_update_irq_reg(port, true, EV_RX); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (!port->irq) { + timer_setup(&uart->timer, liteuart_timer, 0); +@@ -213,9 +213,9 @@ static void liteuart_shutdown(struct uart_port *port) + struct liteuart_port *uart = to_liteuart_port(port); + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + liteuart_update_irq_reg(port, false, EV_RX | EV_TX); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (port->irq) + free_irq(port->irq, port); +@@ -229,13 +229,13 @@ static void liteuart_set_termios(struct uart_port *port, struct ktermios *new, + unsigned int baud; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* update baudrate */ + baud = uart_get_baud_rate(port, new, old, 0, 460800); + uart_update_timeout(port, new->c_cflag, baud); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *liteuart_type(struct uart_port *port) +@@ -382,9 +382,9 @@ static void liteuart_console_write(struct console *co, const char *s, + uart = (struct liteuart_port *)xa_load(&liteuart_array, co->index); + port = &uart->port; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + uart_console_write(port, s, count, liteuart_putchar); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int liteuart_console_setup(struct console *co, char *options) +diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c +index b38fe4728c26..5149a947b7fe 100644 +--- a/drivers/tty/serial/lpc32xx_hs.c ++++ b/drivers/tty/serial/lpc32xx_hs.c +@@ -140,15 +140,15 @@ static void lpc32xx_hsuart_console_write(struct console *co, const char *s, + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock(&up->port.lock); ++ locked = uart_port_trylock(&up->port); + else +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + + uart_console_write(&up->port, s, count, lpc32xx_hsuart_console_putchar); + wait_for_xmit_empty(&up->port); + + if (locked) +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + local_irq_restore(flags); + } + +@@ -298,7 +298,7 @@ static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) + struct tty_port *tport = &port->state->port; + u32 status; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + /* Read UART status and clear latched interrupts */ + status = readl(LPC32XX_HSUART_IIR(port->membase)); +@@ -333,7 +333,7 @@ static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) + __serial_lpc32xx_tx(port); + } + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -404,14 +404,14 @@ static void serial_lpc32xx_break_ctl(struct uart_port *port, + unsigned long flags; + u32 tmp; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + if (break_state != 0) + tmp |= LPC32XX_HSU_BREAK; + else + tmp &= ~LPC32XX_HSU_BREAK; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* port->lock is not held. */ +@@ -421,7 +421,7 @@ static int serial_lpc32xx_startup(struct uart_port *port) + unsigned long flags; + u32 tmp; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + __serial_uart_flush(port); + +@@ -441,7 +441,7 @@ static int serial_lpc32xx_startup(struct uart_port *port) + + lpc32xx_loopback_set(port->mapbase, 0); /* get out of loopback mode */ + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + retval = request_irq(port->irq, serial_lpc32xx_interrupt, + 0, MODNAME, port); +@@ -458,7 +458,7 @@ static void serial_lpc32xx_shutdown(struct uart_port *port) + u32 tmp; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + tmp = LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B | + LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B; +@@ -466,7 +466,7 @@ static void serial_lpc32xx_shutdown(struct uart_port *port) + + lpc32xx_loopback_set(port->mapbase, 1); /* go to loopback mode */ + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + free_irq(port->irq, port); + } +@@ -491,7 +491,7 @@ static void serial_lpc32xx_set_termios(struct uart_port *port, + + quot = __serial_get_clock_div(port->uartclk, baud); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Ignore characters? */ + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); +@@ -505,7 +505,7 @@ static void serial_lpc32xx_set_termios(struct uart_port *port, + + uart_update_timeout(port, termios->c_cflag, baud); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) +diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c +index 99225f1e02ac..faccd772c68c 100644 +--- a/drivers/tty/serial/ma35d1_serial.c ++++ b/drivers/tty/serial/ma35d1_serial.c +@@ -269,16 +269,16 @@ static void receive_chars(struct uart_ma35d1_port *up) + if (uart_handle_sysrq_char(&up->port, ch)) + continue; + +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + uart_insert_char(&up->port, fsr, MA35_FSR_RX_OVER_IF, ch, flag); +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + + fsr = serial_in(up, MA35_FSR_REG); + } while (!(fsr & MA35_FSR_RX_EMPTY) && (max_count-- > 0)); + +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + tty_flip_buffer_push(&up->port.state->port); +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + } + + static irqreturn_t ma35d1serial_interrupt(int irq, void *dev_id) +@@ -364,14 +364,14 @@ static void ma35d1serial_break_ctl(struct uart_port *port, int break_state) + unsigned long flags; + u32 lcr; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + lcr = serial_in(up, MA35_LCR_REG); + if (break_state != 0) + lcr |= MA35_LCR_BREAK; + else + lcr &= ~MA35_LCR_BREAK; + serial_out(up, MA35_LCR_REG, lcr); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static int ma35d1serial_startup(struct uart_port *port) +@@ -441,7 +441,7 @@ static void ma35d1serial_set_termios(struct uart_port *port, + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + up->port.read_status_mask = MA35_FSR_RX_OVER_IF; + if (termios->c_iflag & INPCK) +@@ -475,7 +475,7 @@ static void ma35d1serial_set_termios(struct uart_port *port, + + serial_out(up, MA35_LCR_REG, lcr); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static const char *ma35d1serial_type(struct uart_port *port) +@@ -568,9 +568,9 @@ static void ma35d1serial_console_write(struct console *co, const char *s, u32 co + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock_irqsave(&up->port.lock, flags); ++ locked = uart_port_trylock_irqsave(&up->port, &flags); + else +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* + * First save the IER then disable the interrupts +@@ -584,7 +584,7 @@ static void ma35d1serial_console_write(struct console *co, const char *s, u32 co + serial_out(up, MA35_IER_REG, ier); + + if (locked) +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static int __init ma35d1serial_console_setup(struct console *co, char *options) +diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c +index aea29b4e6567..ee40af20a08f 100644 +--- a/drivers/tty/serial/mcf.c ++++ b/drivers/tty/serial/mcf.c +@@ -135,12 +135,12 @@ static void mcf_break_ctl(struct uart_port *port, int break_state) + { + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (break_state == -1) + writeb(MCFUART_UCR_CMDBREAKSTART, port->membase + MCFUART_UCR); + else + writeb(MCFUART_UCR_CMDBREAKSTOP, port->membase + MCFUART_UCR); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /****************************************************************************/ +@@ -150,7 +150,7 @@ static int mcf_startup(struct uart_port *port) + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Reset UART, get it into known state... */ + writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); +@@ -164,7 +164,7 @@ static int mcf_startup(struct uart_port *port) + pp->imr = MCFUART_UIR_RXREADY; + writeb(pp->imr, port->membase + MCFUART_UIMR); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return 0; + } +@@ -176,7 +176,7 @@ static void mcf_shutdown(struct uart_port *port) + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Disable all interrupts now */ + pp->imr = 0; +@@ -186,7 +186,7 @@ static void mcf_shutdown(struct uart_port *port) + writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); + writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /****************************************************************************/ +@@ -252,7 +252,7 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, + mr2 |= MCFUART_MR2_TXCTS; + } + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + mr2 |= MCFUART_MR2_TXRTS; +@@ -273,7 +273,7 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, + port->membase + MCFUART_UCSR); + writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE, + port->membase + MCFUART_UCR); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /****************************************************************************/ +@@ -350,7 +350,7 @@ static irqreturn_t mcf_interrupt(int irq, void *data) + + isr = readb(port->membase + MCFUART_UISR) & pp->imr; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + if (isr & MCFUART_UIR_RXREADY) { + mcf_rx_chars(pp); + ret = IRQ_HANDLED; +@@ -359,7 +359,7 @@ static irqreturn_t mcf_interrupt(int irq, void *data) + mcf_tx_chars(pp); + ret = IRQ_HANDLED; + } +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return ret; + } +diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c +index d2502aaa3e8c..8048fa542fc4 100644 +--- a/drivers/tty/serial/men_z135_uart.c ++++ b/drivers/tty/serial/men_z135_uart.c +@@ -392,7 +392,7 @@ static irqreturn_t men_z135_intr(int irq, void *data) + if (!irq_id) + goto out; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + /* It's save to write to IIR[7:6] RXC[9:8] */ + iowrite8(irq_id, port->membase + MEN_Z135_STAT_REG); + +@@ -418,7 +418,7 @@ static irqreturn_t men_z135_intr(int irq, void *data) + handled = true; + } + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + out: + return IRQ_RETVAL(handled); + } +@@ -708,7 +708,7 @@ static void men_z135_set_termios(struct uart_port *port, + + baud = uart_get_baud_rate(port, termios, old, 0, uart_freq / 16); + +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + +@@ -716,7 +716,7 @@ static void men_z135_set_termios(struct uart_port *port, + iowrite32(bd_reg, port->membase + MEN_Z135_BAUD_REG); + + uart_update_timeout(port, termios->c_cflag, baud); +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + } + + static const char *men_z135_type(struct uart_port *port) +diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c +index 9388b9ddea3b..4c1d2089a0bb 100644 +--- a/drivers/tty/serial/meson_uart.c ++++ b/drivers/tty/serial/meson_uart.c +@@ -129,14 +129,14 @@ static void meson_uart_shutdown(struct uart_port *port) + + free_irq(port->irq, port); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + val = readl(port->membase + AML_UART_CONTROL); + val &= ~AML_UART_RX_EN; + val &= ~(AML_UART_RX_INT_EN | AML_UART_TX_INT_EN); + writel(val, port->membase + AML_UART_CONTROL); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void meson_uart_start_tx(struct uart_port *port) +@@ -238,7 +238,7 @@ static irqreturn_t meson_uart_interrupt(int irq, void *dev_id) + { + struct uart_port *port = (struct uart_port *)dev_id; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY)) + meson_receive_chars(port); +@@ -248,7 +248,7 @@ static irqreturn_t meson_uart_interrupt(int irq, void *dev_id) + meson_uart_start_tx(port); + } + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -284,7 +284,7 @@ static int meson_uart_startup(struct uart_port *port) + u32 val; + int ret = 0; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + val = readl(port->membase + AML_UART_CONTROL); + val |= AML_UART_CLEAR_ERR; +@@ -301,7 +301,7 @@ static int meson_uart_startup(struct uart_port *port) + val = (AML_UART_RECV_IRQ(1) | AML_UART_XMIT_IRQ(port->fifosize / 2)); + writel(val, port->membase + AML_UART_MISC); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + ret = request_irq(port->irq, meson_uart_interrupt, 0, + port->name, port); +@@ -341,7 +341,7 @@ static void meson_uart_set_termios(struct uart_port *port, + unsigned long flags; + u32 val; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + cflags = termios->c_cflag; + iflags = termios->c_iflag; +@@ -405,7 +405,7 @@ static void meson_uart_set_termios(struct uart_port *port, + AML_UART_FRAME_ERR; + + uart_update_timeout(port, termios->c_cflag, baud); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int meson_uart_verify_port(struct uart_port *port, +@@ -464,14 +464,14 @@ static int meson_uart_poll_get_char(struct uart_port *port) + u32 c; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + if (readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY) + c = NO_POLL_CHAR; + else + c = readl(port->membase + AML_UART_RFIFO); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return c; + } +@@ -482,7 +482,7 @@ static void meson_uart_poll_put_char(struct uart_port *port, unsigned char c) + u32 reg; + int ret; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Wait until FIFO is empty or timeout */ + ret = readl_poll_timeout_atomic(port->membase + AML_UART_STATUS, reg, +@@ -506,7 +506,7 @@ static void meson_uart_poll_put_char(struct uart_port *port, unsigned char c) + dev_err(port->dev, "Timeout waiting for UART TX EMPTY\n"); + + out: +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + #endif /* CONFIG_CONSOLE_POLL */ +@@ -563,9 +563,9 @@ static void meson_serial_port_write(struct uart_port *port, const char *s, + if (port->sysrq) { + locked = 0; + } else if (oops_in_progress) { +- locked = spin_trylock(&port->lock); ++ locked = uart_port_trylock(port); + } else { +- spin_lock(&port->lock); ++ uart_port_lock(port); + locked = 1; + } + +@@ -577,7 +577,7 @@ static void meson_serial_port_write(struct uart_port *port, const char *s, + writel(val, port->membase + AML_UART_CONTROL); + + if (locked) +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + local_irq_restore(flags); + } + +diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c +index 70a910085e93..db3b81f2aa57 100644 +--- a/drivers/tty/serial/milbeaut_usio.c ++++ b/drivers/tty/serial/milbeaut_usio.c +@@ -207,9 +207,9 @@ static irqreturn_t mlb_usio_rx_irq(int irq, void *dev_id) + { + struct uart_port *port = dev_id; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + mlb_usio_rx_chars(port); +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -218,10 +218,10 @@ static irqreturn_t mlb_usio_tx_irq(int irq, void *dev_id) + { + struct uart_port *port = dev_id; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) + mlb_usio_tx_chars(port); +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -267,7 +267,7 @@ static int mlb_usio_startup(struct uart_port *port) + escr = readb(port->membase + MLB_USIO_REG_ESCR); + if (of_property_read_bool(port->dev->of_node, "auto-flow-control")) + escr |= MLB_USIO_ESCR_FLWEN; +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + writeb(0, port->membase + MLB_USIO_REG_SCR); + writeb(escr, port->membase + MLB_USIO_REG_ESCR); + writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR); +@@ -282,7 +282,7 @@ static int mlb_usio_startup(struct uart_port *port) + + writeb(MLB_USIO_SCR_TXE | MLB_USIO_SCR_RIE | MLB_USIO_SCR_TBIE | + MLB_USIO_SCR_RXE, port->membase + MLB_USIO_REG_SCR); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return 0; + } +@@ -337,7 +337,7 @@ static void mlb_usio_set_termios(struct uart_port *port, + else + quot = 0; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + uart_update_timeout(port, termios->c_cflag, baud); + port->read_status_mask = MLB_USIO_SSR_ORE | MLB_USIO_SSR_RDRF | + MLB_USIO_SSR_TDRE; +@@ -367,7 +367,7 @@ static void mlb_usio_set_termios(struct uart_port *port, + writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE); + writeb(MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE | MLB_USIO_SCR_TBIE | + MLB_USIO_SCR_TXE, port->membase + MLB_USIO_REG_SCR); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *mlb_usio_type(struct uart_port *port) +diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c +index 916507b8f31d..a252465e745f 100644 +--- a/drivers/tty/serial/mpc52xx_uart.c ++++ b/drivers/tty/serial/mpc52xx_uart.c +@@ -1096,14 +1096,14 @@ static void + mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) + { + unsigned long flags; +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + if (ctl == -1) + psc_ops->command(port, MPC52xx_PSC_START_BRK); + else + psc_ops->command(port, MPC52xx_PSC_STOP_BRK); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int +@@ -1214,7 +1214,7 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, + } + + /* Get the lock */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Do our best to flush TX & RX, so we don't lose anything */ + /* But we don't wait indefinitely ! */ +@@ -1250,7 +1250,7 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, + psc_ops->command(port, MPC52xx_PSC_RX_ENABLE); + + /* We're all set, release the lock */ +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char * +@@ -1477,11 +1477,11 @@ mpc52xx_uart_int(int irq, void *dev_id) + struct uart_port *port = dev_id; + irqreturn_t ret; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + ret = psc_ops->handle_irq(port); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return ret; + } +diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c +index ea5a7911cb15..2a4c09f3a834 100644 +--- a/drivers/tty/serial/mps2-uart.c ++++ b/drivers/tty/serial/mps2-uart.c +@@ -188,12 +188,12 @@ static irqreturn_t mps2_uart_rxirq(int irq, void *data) + if (unlikely(!(irqflag & UARTn_INT_RX))) + return IRQ_NONE; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT); + mps2_uart_rx_chars(port); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -206,12 +206,12 @@ static irqreturn_t mps2_uart_txirq(int irq, void *data) + if (unlikely(!(irqflag & UARTn_INT_TX))) + return IRQ_NONE; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT); + mps2_uart_tx_chars(port); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -222,7 +222,7 @@ static irqreturn_t mps2_uart_oerrirq(int irq, void *data) + struct uart_port *port = data; + u8 irqflag = mps2_uart_read8(port, UARTn_INT); + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + if (irqflag & UARTn_INT_RX_OVERRUN) { + struct tty_port *tport = &port->state->port; +@@ -244,7 +244,7 @@ static irqreturn_t mps2_uart_oerrirq(int irq, void *data) + handled = IRQ_HANDLED; + } + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return handled; + } +@@ -356,12 +356,12 @@ mps2_uart_set_termios(struct uart_port *port, struct ktermios *termios, + + bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + uart_update_timeout(port, termios->c_cflag, baud); + mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c +index 90953e679e38..597264b546fd 100644 +--- a/drivers/tty/serial/msm_serial.c ++++ b/drivers/tty/serial/msm_serial.c +@@ -444,7 +444,7 @@ static void msm_complete_tx_dma(void *args) + unsigned int count; + u32 val; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Already stopped */ + if (!dma->count) +@@ -476,7 +476,7 @@ static void msm_complete_tx_dma(void *args) + + msm_handle_tx(port); + done: +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) +@@ -549,7 +549,7 @@ static void msm_complete_rx_dma(void *args) + unsigned long flags; + u32 val; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Already stopped */ + if (!dma->count) +@@ -587,16 +587,16 @@ static void msm_complete_rx_dma(void *args) + if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK)) + flag = TTY_NORMAL; + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + sysrq = uart_handle_sysrq_char(port, dma->virt[i]); +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (!sysrq) + tty_insert_flip_char(tport, dma->virt[i], flag); + } + + msm_start_rx_dma(msm_port); + done: +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (count) + tty_flip_buffer_push(tport); +@@ -762,9 +762,9 @@ static void msm_handle_rx_dm(struct uart_port *port, unsigned int misr) + if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK)) + flag = TTY_NORMAL; + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + sysrq = uart_handle_sysrq_char(port, buf[i]); +- spin_lock(&port->lock); ++ uart_port_lock(port); + if (!sysrq) + tty_insert_flip_char(tport, buf[i], flag); + } +@@ -824,9 +824,9 @@ static void msm_handle_rx(struct uart_port *port) + else if (sr & MSM_UART_SR_PAR_FRAME_ERR) + flag = TTY_FRAME; + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + sysrq = uart_handle_sysrq_char(port, c); +- spin_lock(&port->lock); ++ uart_port_lock(port); + if (!sysrq) + tty_insert_flip_char(tport, c, flag); + } +@@ -951,7 +951,7 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) + unsigned int misr; + u32 val; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + misr = msm_read(port, MSM_UART_MISR); + msm_write(port, 0, MSM_UART_IMR); /* disable interrupt */ + +@@ -983,7 +983,7 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) + msm_handle_delta_cts(port); + + msm_write(port, msm_port->imr, MSM_UART_IMR); /* restore interrupt */ +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return IRQ_HANDLED; + } +@@ -1128,13 +1128,13 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, + unsigned long flags, rate; + + flags = *saved_flags; +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + entry = msm_find_best_baud(port, baud, &rate); + clk_set_rate(msm_port->clk, rate); + baud = rate / 16 / entry->divisor; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + *saved_flags = flags; + port->uartclk = rate; + +@@ -1266,7 +1266,7 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios, + unsigned long flags; + unsigned int baud, mr; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + if (dma->chan) /* Terminate if any */ + msm_stop_dma(port, dma); +@@ -1338,7 +1338,7 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios, + /* Try to use DMA */ + msm_start_rx_dma(msm_port); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *msm_type(struct uart_port *port) +@@ -1620,9 +1620,9 @@ static void __msm_console_write(struct uart_port *port, const char *s, + if (port->sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock(&port->lock); ++ locked = uart_port_trylock(port); + else +- spin_lock(&port->lock); ++ uart_port_lock(port); + + if (is_uartdm) + msm_reset_dm_count(port, count); +@@ -1661,7 +1661,7 @@ static void __msm_console_write(struct uart_port *port, const char *s, + } + + if (locked) +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + local_irq_restore(flags); + } +diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c +index ea924e9b913b..0255646bc175 100644 +--- a/drivers/tty/serial/mvebu-uart.c ++++ b/drivers/tty/serial/mvebu-uart.c +@@ -187,9 +187,9 @@ static unsigned int mvebu_uart_tx_empty(struct uart_port *port) + unsigned long flags; + unsigned int st; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + st = readl(port->membase + UART_STAT); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return (st & STAT_TX_EMP) ? TIOCSER_TEMT : 0; + } +@@ -249,14 +249,14 @@ static void mvebu_uart_break_ctl(struct uart_port *port, int brk) + unsigned int ctl; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + ctl = readl(port->membase + UART_CTRL(port)); + if (brk == -1) + ctl |= CTRL_SND_BRK_SEQ; + else + ctl &= ~CTRL_SND_BRK_SEQ; + writel(ctl, port->membase + UART_CTRL(port)); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void mvebu_uart_rx_chars(struct uart_port *port, unsigned int status) +@@ -540,7 +540,7 @@ static void mvebu_uart_set_termios(struct uart_port *port, + unsigned long flags; + unsigned int baud, min_baud, max_baud; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + port->read_status_mask = STAT_RX_RDY(port) | STAT_OVR_ERR | + STAT_TX_RDY(port) | STAT_TX_FIFO_FUL; +@@ -589,7 +589,7 @@ static void mvebu_uart_set_termios(struct uart_port *port, + uart_update_timeout(port, termios->c_cflag, baud); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *mvebu_uart_type(struct uart_port *port) +@@ -735,9 +735,9 @@ static void mvebu_uart_console_write(struct console *co, const char *s, + int locked = 1; + + if (oops_in_progress) +- locked = spin_trylock_irqsave(&port->lock, flags); ++ locked = uart_port_trylock_irqsave(port, &flags); + else +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + ier = readl(port->membase + UART_CTRL(port)) & CTRL_BRK_INT; + intr = readl(port->membase + UART_INTR(port)) & +@@ -758,7 +758,7 @@ static void mvebu_uart_console_write(struct console *co, const char *s, + } + + if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int mvebu_uart_console_setup(struct console *co, char *options) +diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c +index 135a838f517a..1097fca22307 100644 +--- a/drivers/tty/serial/omap-serial.c ++++ b/drivers/tty/serial/omap-serial.c +@@ -390,10 +390,10 @@ static void serial_omap_throttle(struct uart_port *port) + struct uart_omap_port *up = to_uart_omap_port(port); + unsigned long flags; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); + serial_out(up, UART_IER, up->ier); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static void serial_omap_unthrottle(struct uart_port *port) +@@ -401,10 +401,10 @@ static void serial_omap_unthrottle(struct uart_port *port) + struct uart_omap_port *up = to_uart_omap_port(port); + unsigned long flags; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + up->ier |= UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static unsigned int check_modem_status(struct uart_omap_port *up) +@@ -527,7 +527,7 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id) + irqreturn_t ret = IRQ_NONE; + int max_count = 256; + +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + + do { + iir = serial_in(up, UART_IIR); +@@ -563,7 +563,7 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id) + } + } while (max_count--); + +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + + tty_flip_buffer_push(&up->port.state->port); + +@@ -579,9 +579,9 @@ static unsigned int serial_omap_tx_empty(struct uart_port *port) + unsigned int ret = 0; + + dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->port.line); +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + return ret; + } +@@ -647,13 +647,13 @@ static void serial_omap_break_ctl(struct uart_port *port, int break_state) + unsigned long flags; + + dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->port.line); +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static int serial_omap_startup(struct uart_port *port) +@@ -701,13 +701,13 @@ static int serial_omap_startup(struct uart_port *port) + * Now, initialize the UART + */ + serial_out(up, UART_LCR, UART_LCR_WLEN8); +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + up->port.mctrl |= TIOCM_OUT2; + serial_omap_set_mctrl(&up->port, up->port.mctrl); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + up->msr_saved_flags = 0; + /* +@@ -742,10 +742,10 @@ static void serial_omap_shutdown(struct uart_port *port) + up->ier = 0; + serial_out(up, UART_IER, 0); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + up->port.mctrl &= ~TIOCM_OUT2; + serial_omap_set_mctrl(&up->port, up->port.mctrl); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + /* + * Disable break condition and FIFOs +@@ -815,7 +815,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* + * Update the per-port timeout. +@@ -1013,7 +1013,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, + + serial_omap_set_mctrl(&up->port, up->port.mctrl); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line); + } + +@@ -1212,13 +1212,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 = uart_port_trylock_irqsave(&up->port, &flags); + else +- spin_lock(&up->port.lock); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* + * First save the IER then disable the interrupts +@@ -1245,8 +1242,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); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static int __init +diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c +index e99970a9437f..919f5e5aa0f1 100644 +--- a/drivers/tty/serial/owl-uart.c ++++ b/drivers/tty/serial/owl-uart.c +@@ -125,12 +125,12 @@ static unsigned int owl_uart_tx_empty(struct uart_port *port) + u32 val; + unsigned int ret; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + val = owl_uart_read(port, OWL_UART_STAT); + ret = (val & OWL_UART_STAT_TFES) ? TIOCSER_TEMT : 0; + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return ret; + } +@@ -232,7 +232,7 @@ static irqreturn_t owl_uart_irq(int irq, void *dev_id) + unsigned long flags; + u32 stat; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + stat = owl_uart_read(port, OWL_UART_STAT); + +@@ -246,7 +246,7 @@ static irqreturn_t owl_uart_irq(int irq, void *dev_id) + stat |= OWL_UART_STAT_RIP | OWL_UART_STAT_TIP; + owl_uart_write(port, stat, OWL_UART_STAT); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return IRQ_HANDLED; + } +@@ -256,14 +256,14 @@ static void owl_uart_shutdown(struct uart_port *port) + u32 val; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + val = owl_uart_read(port, OWL_UART_CTL); + val &= ~(OWL_UART_CTL_TXIE | OWL_UART_CTL_RXIE + | OWL_UART_CTL_TXDE | OWL_UART_CTL_RXDE | OWL_UART_CTL_EN); + owl_uart_write(port, val, OWL_UART_CTL); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + free_irq(port->irq, port); + } +@@ -279,7 +279,7 @@ static int owl_uart_startup(struct uart_port *port) + if (ret) + return ret; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + val = owl_uart_read(port, OWL_UART_STAT); + val |= OWL_UART_STAT_RIP | OWL_UART_STAT_TIP +@@ -291,7 +291,7 @@ static int owl_uart_startup(struct uart_port *port) + val |= OWL_UART_CTL_EN; + owl_uart_write(port, val, OWL_UART_CTL); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return 0; + } +@@ -311,7 +311,7 @@ static void owl_uart_set_termios(struct uart_port *port, + u32 ctl; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + ctl = owl_uart_read(port, OWL_UART_CTL); + +@@ -371,7 +371,7 @@ static void owl_uart_set_termios(struct uart_port *port, + + uart_update_timeout(port, termios->c_cflag, baud); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void owl_uart_release_port(struct uart_port *port) +@@ -515,9 +515,9 @@ static void owl_uart_port_write(struct uart_port *port, const char *s, + if (port->sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock(&port->lock); ++ locked = uart_port_trylock(port); + else { +- spin_lock(&port->lock); ++ uart_port_lock(port); + locked = 1; + } + +@@ -541,7 +541,7 @@ static void owl_uart_port_write(struct uart_port *port, const char *s, + owl_uart_write(port, old_ctl, OWL_UART_CTL); + + if (locked) +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + local_irq_restore(flags); + } +diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c +index cc83b772b7ca..436cc6d52a11 100644 +--- a/drivers/tty/serial/pch_uart.c ++++ b/drivers/tty/serial/pch_uart.c +@@ -1347,7 +1347,7 @@ static void pch_uart_set_termios(struct uart_port *port, + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + + spin_lock_irqsave(&priv->lock, flags); +- spin_lock(&port->lock); ++ uart_port_lock(port); + + uart_update_timeout(port, termios->c_cflag, baud); + rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb); +@@ -1360,7 +1360,7 @@ static void pch_uart_set_termios(struct uart_port *port, + tty_termios_encode_baud_rate(termios, baud, baud); + + out: +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + spin_unlock_irqrestore(&priv->lock, flags); + } + +@@ -1581,10 +1581,10 @@ pch_console_write(struct console *co, const char *s, unsigned int count) + port_locked = 0; + } else if (oops_in_progress) { + priv_locked = spin_trylock(&priv->lock); +- port_locked = spin_trylock(&priv->port.lock); ++ port_locked = uart_port_trylock(&priv->port); + } else { + spin_lock(&priv->lock); +- spin_lock(&priv->port.lock); ++ uart_port_lock(&priv->port); + } + + /* +@@ -1604,7 +1604,7 @@ pch_console_write(struct console *co, const char *s, unsigned int count) + iowrite8(ier, priv->membase + UART_IER); + + if (port_locked) +- spin_unlock(&priv->port.lock); ++ uart_port_unlock(&priv->port); + if (priv_locked) + spin_unlock(&priv->lock); + local_irq_restore(flags); +diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c +index e308d5022b3f..3a95bf5d55d3 100644 +--- a/drivers/tty/serial/pic32_uart.c ++++ b/drivers/tty/serial/pic32_uart.c +@@ -243,7 +243,7 @@ static void pic32_uart_break_ctl(struct uart_port *port, int ctl) + struct pic32_sport *sport = to_pic32_sport(port); + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + if (ctl) + pic32_uart_writel(sport, PIC32_SET(PIC32_UART_STA), +@@ -252,7 +252,7 @@ static void pic32_uart_break_ctl(struct uart_port *port, int ctl) + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA), + PIC32_UART_STA_UTXBRK); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* get port type in string format */ +@@ -274,7 +274,7 @@ static void pic32_uart_do_rx(struct uart_port *port) + */ + max_count = PIC32_UART_RX_FIFO_DEPTH; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + tty = &port->state->port; + +@@ -331,7 +331,7 @@ static void pic32_uart_do_rx(struct uart_port *port) + + } while (--max_count); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + tty_flip_buffer_push(tty); + } +@@ -410,9 +410,9 @@ static irqreturn_t pic32_uart_tx_interrupt(int irq, void *dev_id) + struct uart_port *port = dev_id; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + pic32_uart_do_tx(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return IRQ_HANDLED; + } +@@ -580,9 +580,9 @@ static void pic32_uart_shutdown(struct uart_port *port) + unsigned long flags; + + /* disable uart */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + pic32_uart_dsbl_and_mask(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + clk_disable_unprepare(sport->clk); + + /* free all 3 interrupts for this UART */ +@@ -604,7 +604,7 @@ static void pic32_uart_set_termios(struct uart_port *port, + unsigned int quot; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* disable uart and mask all interrupts while changing speed */ + pic32_uart_dsbl_and_mask(port); +@@ -672,7 +672,7 @@ static void pic32_uart_set_termios(struct uart_port *port, + /* enable uart */ + pic32_uart_en_and_unmask(port); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* serial core request to claim uart iomem */ +diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c +index 29bc80d39e8b..77691fbbf779 100644 +--- a/drivers/tty/serial/pmac_zilog.c ++++ b/drivers/tty/serial/pmac_zilog.c +@@ -245,9 +245,9 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) + #endif /* USE_CTRL_O_SYSRQ */ + if (uap->port.sysrq) { + int swallow; +- spin_unlock(&uap->port.lock); ++ uart_port_unlock(&uap->port); + swallow = uart_handle_sysrq_char(&uap->port, ch); +- spin_lock(&uap->port.lock); ++ uart_port_lock(&uap->port); + if (swallow) + goto next_char; + } +@@ -421,7 +421,7 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) + uap_a = pmz_get_port_A(uap); + uap_b = uap_a->mate; + +- spin_lock(&uap_a->port.lock); ++ uart_port_lock(&uap_a->port); + r3 = read_zsreg(uap_a, R3); + + /* Channel A */ +@@ -442,14 +442,14 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) + rc = IRQ_HANDLED; + } + skip_a: +- spin_unlock(&uap_a->port.lock); ++ uart_port_unlock(&uap_a->port); + if (push) + tty_flip_buffer_push(&uap->port.state->port); + + if (!uap_b) + goto out; + +- spin_lock(&uap_b->port.lock); ++ uart_port_lock(&uap_b->port); + push = false; + if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { + if (!ZS_IS_OPEN(uap_b)) { +@@ -467,7 +467,7 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) + rc = IRQ_HANDLED; + } + skip_b: +- spin_unlock(&uap_b->port.lock); ++ uart_port_unlock(&uap_b->port); + if (push) + tty_flip_buffer_push(&uap->port.state->port); + +@@ -483,9 +483,9 @@ static inline u8 pmz_peek_status(struct uart_pmac_port *uap) + unsigned long flags; + u8 status; + +- spin_lock_irqsave(&uap->port.lock, flags); ++ uart_port_lock_irqsave(&uap->port, &flags); + status = read_zsreg(uap, R0); +- spin_unlock_irqrestore(&uap->port.lock, flags); ++ uart_port_unlock_irqrestore(&uap->port, flags); + + return status; + } +@@ -671,7 +671,7 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) + else + clear_bits |= SND_BRK; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + new_reg = (uap->curregs[R5] | set_bits) & ~clear_bits; + if (new_reg != uap->curregs[R5]) { +@@ -679,7 +679,7 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) + write_zsreg(uap, R5, uap->curregs[R5]); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + #ifdef CONFIG_PPC_PMAC +@@ -851,18 +851,18 @@ static void pmz_irda_reset(struct uart_pmac_port *uap) + { + unsigned long flags; + +- spin_lock_irqsave(&uap->port.lock, flags); ++ uart_port_lock_irqsave(&uap->port, &flags); + uap->curregs[R5] |= DTR; + write_zsreg(uap, R5, uap->curregs[R5]); + zssync(uap); +- spin_unlock_irqrestore(&uap->port.lock, flags); ++ uart_port_unlock_irqrestore(&uap->port, flags); + msleep(110); + +- spin_lock_irqsave(&uap->port.lock, flags); ++ uart_port_lock_irqsave(&uap->port, &flags); + uap->curregs[R5] &= ~DTR; + write_zsreg(uap, R5, uap->curregs[R5]); + zssync(uap); +- spin_unlock_irqrestore(&uap->port.lock, flags); ++ uart_port_unlock_irqrestore(&uap->port, flags); + msleep(10); + } + +@@ -882,9 +882,9 @@ static int pmz_startup(struct uart_port *port) + * initialize the chip + */ + if (!ZS_IS_CONS(uap)) { +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + pwr_delay = __pmz_startup(uap); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + sprintf(uap->irq_name, PMACZILOG_NAME"%d", uap->port.line); + if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED, +@@ -907,9 +907,9 @@ static int pmz_startup(struct uart_port *port) + pmz_irda_reset(uap); + + /* Enable interrupt requests for the channel */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + pmz_interrupt_control(uap, 1); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return 0; + } +@@ -919,7 +919,7 @@ static void pmz_shutdown(struct uart_port *port) + struct uart_pmac_port *uap = to_pmz(port); + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Disable interrupt requests for the channel */ + pmz_interrupt_control(uap, 0); +@@ -934,19 +934,19 @@ static void pmz_shutdown(struct uart_port *port) + pmz_maybe_update_regs(uap); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + /* Release interrupt handler */ + free_irq(uap->port.irq, uap); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + uap->flags &= ~PMACZILOG_FLAG_IS_OPEN; + + if (!ZS_IS_CONS(uap)) + pmz_set_scc_power(uap, 0); /* Shut the chip down */ + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* Shared by TTY driver and serial console setup. The port lock is held +@@ -1233,7 +1233,7 @@ static void pmz_set_termios(struct uart_port *port, struct ktermios *termios, + struct uart_pmac_port *uap = to_pmz(port); + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Disable IRQs on the port */ + pmz_interrupt_control(uap, 0); +@@ -1245,7 +1245,7 @@ static void pmz_set_termios(struct uart_port *port, struct ktermios *termios, + if (ZS_IS_OPEN(uap)) + pmz_interrupt_control(uap, 1); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *pmz_type(struct uart_port *port) +@@ -1882,7 +1882,7 @@ static void pmz_console_write(struct console *con, const char *s, unsigned int c + struct uart_pmac_port *uap = &pmz_ports[con->index]; + unsigned long flags; + +- spin_lock_irqsave(&uap->port.lock, flags); ++ uart_port_lock_irqsave(&uap->port, &flags); + + /* Turn of interrupts and enable the transmitter. */ + write_zsreg(uap, R1, uap->curregs[1] & ~TxINT_ENAB); +@@ -1894,7 +1894,7 @@ static void pmz_console_write(struct console *con, const char *s, unsigned int c + write_zsreg(uap, R1, uap->curregs[1]); + /* Don't disable the transmitter. */ + +- spin_unlock_irqrestore(&uap->port.lock, flags); ++ uart_port_unlock_irqrestore(&uap->port, flags); + } + + /* +diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c +index 73c60f5ea027..46e70e155aab 100644 +--- a/drivers/tty/serial/pxa.c ++++ b/drivers/tty/serial/pxa.c +@@ -225,14 +225,14 @@ static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id) + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) + return IRQ_NONE; +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + lsr = serial_in(up, UART_LSR); + if (lsr & UART_LSR_DR) + receive_chars(up, &lsr); + check_modem_status(up); + if (lsr & UART_LSR_THRE) + transmit_chars(up); +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + return IRQ_HANDLED; + } + +@@ -242,9 +242,9 @@ static unsigned int serial_pxa_tx_empty(struct uart_port *port) + unsigned long flags; + unsigned int ret; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + return ret; + } +@@ -295,13 +295,13 @@ static void serial_pxa_break_ctl(struct uart_port *port, int break_state) + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned long flags; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static int serial_pxa_startup(struct uart_port *port) +@@ -346,10 +346,10 @@ static int serial_pxa_startup(struct uart_port *port) + */ + serial_out(up, UART_LCR, UART_LCR_WLEN8); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + up->port.mctrl |= TIOCM_OUT2; + serial_pxa_set_mctrl(&up->port, up->port.mctrl); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + /* + * Finally, enable interrupts. Note: Modem status interrupts +@@ -383,10 +383,10 @@ static void serial_pxa_shutdown(struct uart_port *port) + up->ier = 0; + serial_out(up, UART_IER, 0); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + up->port.mctrl &= ~TIOCM_OUT2; + serial_pxa_set_mctrl(&up->port, up->port.mctrl); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + /* + * Disable break condition and FIFOs +@@ -434,7 +434,7 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* + * Ensure the port will be enabled. +@@ -504,7 +504,7 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, + up->lcr = cval; /* Save LCR */ + serial_pxa_set_mctrl(&up->port, up->port.mctrl); + serial_out(up, UART_FCR, fcr); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static void +@@ -608,9 +608,9 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock(&up->port.lock); ++ locked = uart_port_trylock(&up->port); + else +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + + /* + * First save the IER then disable the interrupts +@@ -628,7 +628,7 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) + serial_out(up, UART_IER, ier); + + if (locked) +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + local_irq_restore(flags); + clk_disable(up->clk); + +diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c +index 2e1b1c827dfe..549909644011 100644 +--- a/drivers/tty/serial/qcom_geni_serial.c ++++ b/drivers/tty/serial/qcom_geni_serial.c +@@ -482,9 +482,9 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, + + uport = &port->uport; + if (oops_in_progress) +- locked = spin_trylock_irqsave(&uport->lock, flags); ++ locked = uart_port_trylock_irqsave(uport, &flags); + else +- spin_lock_irqsave(&uport->lock, flags); ++ uart_port_lock_irqsave(uport, &flags); + + geni_status = readl(uport->membase + SE_GENI_STATUS); + +@@ -520,7 +520,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, + qcom_geni_serial_setup_tx(uport, port->tx_remaining); + + if (locked) +- spin_unlock_irqrestore(&uport->lock, flags); ++ uart_port_unlock_irqrestore(uport, flags); + } + + static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) +@@ -972,7 +972,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) + if (uport->suspended) + return IRQ_NONE; + +- spin_lock(&uport->lock); ++ uart_port_lock(uport); + + m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); + s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); +diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c +index be5c842b5ba9..d824c8318f33 100644 +--- a/drivers/tty/serial/rda-uart.c ++++ b/drivers/tty/serial/rda-uart.c +@@ -139,12 +139,12 @@ static unsigned int rda_uart_tx_empty(struct uart_port *port) + unsigned int ret; + u32 val; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + val = rda_uart_read(port, RDA_UART_STATUS); + ret = (val & RDA_UART_TX_FIFO_MASK) ? TIOCSER_TEMT : 0; + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return ret; + } +@@ -246,7 +246,7 @@ static void rda_uart_set_termios(struct uart_port *port, + unsigned int baud; + u32 irq_mask; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + baud = uart_get_baud_rate(port, termios, old, 9600, port->uartclk / 4); + rda_uart_change_baudrate(rda_port, baud); +@@ -325,7 +325,7 @@ static void rda_uart_set_termios(struct uart_port *port, + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void rda_uart_send_chars(struct uart_port *port) +@@ -408,7 +408,7 @@ static irqreturn_t rda_interrupt(int irq, void *dev_id) + unsigned long flags; + u32 val, irq_mask; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Clear IRQ cause */ + val = rda_uart_read(port, RDA_UART_IRQ_CAUSE); +@@ -425,7 +425,7 @@ static irqreturn_t rda_interrupt(int irq, void *dev_id) + rda_uart_send_chars(port); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return IRQ_HANDLED; + } +@@ -436,16 +436,16 @@ static int rda_uart_startup(struct uart_port *port) + int ret; + u32 val; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + rda_uart_write(port, 0, RDA_UART_IRQ_MASK); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + ret = request_irq(port->irq, rda_interrupt, IRQF_NO_SUSPEND, + "rda-uart", port); + if (ret) + return ret; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + val = rda_uart_read(port, RDA_UART_CTRL); + val |= RDA_UART_ENABLE; +@@ -456,7 +456,7 @@ static int rda_uart_startup(struct uart_port *port) + val |= (RDA_UART_RX_DATA_AVAILABLE | RDA_UART_RX_TIMEOUT); + rda_uart_write(port, val, RDA_UART_IRQ_MASK); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return 0; + } +@@ -466,7 +466,7 @@ static void rda_uart_shutdown(struct uart_port *port) + unsigned long flags; + u32 val; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + rda_uart_stop_tx(port); + rda_uart_stop_rx(port); +@@ -475,7 +475,7 @@ static void rda_uart_shutdown(struct uart_port *port) + val &= ~RDA_UART_ENABLE; + rda_uart_write(port, val, RDA_UART_CTRL); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *rda_uart_type(struct uart_port *port) +@@ -515,7 +515,7 @@ static void rda_uart_config_port(struct uart_port *port, int flags) + rda_uart_request_port(port); + } + +- spin_lock_irqsave(&port->lock, irq_flags); ++ uart_port_lock_irqsave(port, &irq_flags); + + /* Clear mask, so no surprise interrupts. */ + rda_uart_write(port, 0, RDA_UART_IRQ_MASK); +@@ -523,7 +523,7 @@ static void rda_uart_config_port(struct uart_port *port, int flags) + /* Clear status register */ + rda_uart_write(port, 0, RDA_UART_STATUS); + +- spin_unlock_irqrestore(&port->lock, irq_flags); ++ uart_port_unlock_irqrestore(port, irq_flags); + } + + static void rda_uart_release_port(struct uart_port *port) +@@ -597,9 +597,9 @@ static void rda_uart_port_write(struct uart_port *port, const char *s, + if (port->sysrq) { + locked = 0; + } else if (oops_in_progress) { +- locked = spin_trylock(&port->lock); ++ locked = uart_port_trylock(port); + } else { +- spin_lock(&port->lock); ++ uart_port_lock(port); + locked = 1; + } + +@@ -615,7 +615,7 @@ static void rda_uart_port_write(struct uart_port *port, const char *s, + rda_uart_write(port, old_irq_mask, RDA_UART_IRQ_MASK); + + if (locked) +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + local_irq_restore(flags); + } +diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c +index de220ac8ca54..d46a81cddfcd 100644 +--- a/drivers/tty/serial/rp2.c ++++ b/drivers/tty/serial/rp2.c +@@ -276,9 +276,9 @@ static unsigned int rp2_uart_tx_empty(struct uart_port *port) + * But the TXEMPTY bit doesn't seem to work unless the TX IRQ is + * enabled. + */ +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + tx_fifo_bytes = readw(up->base + RP2_TX_FIFO_COUNT); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + return tx_fifo_bytes ? 0 : TIOCSER_TEMT; + } +@@ -323,10 +323,10 @@ static void rp2_uart_break_ctl(struct uart_port *port, int break_state) + { + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + rp2_rmw(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_BREAK_m, + break_state ? RP2_TXRX_CTL_BREAK_m : 0); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void rp2_uart_enable_ms(struct uart_port *port) +@@ -383,7 +383,7 @@ static void rp2_uart_set_termios(struct uart_port *port, struct ktermios *new, + if (tty_termios_baud_rate(new)) + tty_termios_encode_baud_rate(new, baud, baud); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* ignore all characters if CREAD is not set */ + port->ignore_status_mask = (new->c_cflag & CREAD) ? 0 : RP2_DUMMY_READ; +@@ -391,7 +391,7 @@ static void rp2_uart_set_termios(struct uart_port *port, struct ktermios *new, + __rp2_uart_set_termios(up, new->c_cflag, new->c_iflag, baud_div); + uart_update_timeout(port, new->c_cflag, baud); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void rp2_rx_chars(struct rp2_uart_port *up) +@@ -440,7 +440,7 @@ static void rp2_ch_interrupt(struct rp2_uart_port *up) + { + u32 status; + +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + + /* + * The IRQ status bits are clear-on-write. Other status bits in +@@ -456,7 +456,7 @@ static void rp2_ch_interrupt(struct rp2_uart_port *up) + if (status & RP2_CHAN_STAT_MS_CHANGED_MASK) + wake_up_interruptible(&up->port.state->port.delta_msr_wait); + +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + } + + static int rp2_asic_interrupt(struct rp2_card *card, unsigned int asic_id) +@@ -516,10 +516,10 @@ static void rp2_uart_shutdown(struct uart_port *port) + + rp2_uart_break_ctl(port, 0); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + rp2_mask_ch_irq(up, up->idx, 0); + rp2_rmw(up, RP2_CHAN_STAT, 0, 0); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *rp2_uart_type(struct uart_port *port) +diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c +index ad011f1e2f4d..be7bcd75d9f4 100644 +--- a/drivers/tty/serial/sa1100.c ++++ b/drivers/tty/serial/sa1100.c +@@ -115,9 +115,9 @@ static void sa1100_timeout(struct timer_list *t) + unsigned long flags; + + if (sport->port.state) { +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + sa1100_mctrl_check(sport); +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + + mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); + } +@@ -247,7 +247,7 @@ static irqreturn_t sa1100_int(int irq, void *dev_id) + struct sa1100_port *sport = dev_id; + unsigned int status, pass_counter = 0; + +- spin_lock(&sport->port.lock); ++ uart_port_lock(&sport->port); + status = UART_GET_UTSR0(sport); + status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS; + do { +@@ -276,7 +276,7 @@ static irqreturn_t sa1100_int(int irq, void *dev_id) + status &= SM_TO_UTSR0(sport->port.read_status_mask) | + ~UTSR0_TFS; + } while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID)); +- spin_unlock(&sport->port.lock); ++ uart_port_unlock(&sport->port); + + return IRQ_HANDLED; + } +@@ -321,14 +321,14 @@ static void sa1100_break_ctl(struct uart_port *port, int break_state) + unsigned long flags; + unsigned int utcr3; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + utcr3 = UART_GET_UTCR3(sport); + if (break_state == -1) + utcr3 |= UTCR3_BRK; + else + utcr3 &= ~UTCR3_BRK; + UART_PUT_UTCR3(sport, utcr3); +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + static int sa1100_startup(struct uart_port *port) +@@ -354,9 +354,9 @@ static int sa1100_startup(struct uart_port *port) + /* + * Enable modem status interrupts + */ +- spin_lock_irq(&sport->port.lock); ++ uart_port_lock_irq(&sport->port); + sa1100_enable_ms(&sport->port); +- spin_unlock_irq(&sport->port.lock); ++ uart_port_unlock_irq(&sport->port); + + return 0; + } +@@ -423,7 +423,7 @@ sa1100_set_termios(struct uart_port *port, struct ktermios *termios, + + del_timer_sync(&sport->timer); + +- spin_lock_irqsave(&sport->port.lock, flags); ++ uart_port_lock_irqsave(&sport->port, &flags); + + sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS); + sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR); +@@ -485,7 +485,7 @@ sa1100_set_termios(struct uart_port *port, struct ktermios *termios, + if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) + sa1100_enable_ms(&sport->port); + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ uart_port_unlock_irqrestore(&sport->port, flags); + } + + static const char *sa1100_type(struct uart_port *port) +diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c +index 5a4d88e13471..a82b65155f6e 100644 +--- a/drivers/tty/serial/samsung_tty.c ++++ b/drivers/tty/serial/samsung_tty.c +@@ -248,7 +248,7 @@ static void s3c24xx_serial_rx_enable(struct uart_port *port) + unsigned int ucon, ufcon; + int count = 10000; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + while (--count && !s3c24xx_serial_txempty_nofifo(port)) + udelay(100); +@@ -262,7 +262,7 @@ static void s3c24xx_serial_rx_enable(struct uart_port *port) + wr_regl(port, S3C2410_UCON, ucon); + + ourport->rx_enabled = 1; +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void s3c24xx_serial_rx_disable(struct uart_port *port) +@@ -271,14 +271,14 @@ static void s3c24xx_serial_rx_disable(struct uart_port *port) + unsigned long flags; + unsigned int ucon; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + ucon = rd_regl(port, S3C2410_UCON); + ucon &= ~S3C2410_UCON_RXIRQMODE; + wr_regl(port, S3C2410_UCON, ucon); + + ourport->rx_enabled = 0; +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void s3c24xx_serial_stop_tx(struct uart_port *port) +@@ -344,7 +344,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args) + dma->tx_transfer_addr, dma->tx_size, + DMA_TO_DEVICE); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + uart_xmit_advance(port, count); + ourport->tx_in_progress = 0; +@@ -353,7 +353,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args) + uart_write_wakeup(port); + + s3c24xx_serial_start_next_tx(ourport); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void enable_tx_dma(struct s3c24xx_uart_port *ourport) +@@ -619,7 +619,7 @@ static void s3c24xx_serial_rx_dma_complete(void *args) + received = dma->rx_bytes_requested - state.residue; + async_tx_ack(dma->rx_desc); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + if (received) + s3c24xx_uart_copy_rx_to_tty(ourport, t, received); +@@ -631,7 +631,7 @@ static void s3c24xx_serial_rx_dma_complete(void *args) + + s3c64xx_start_rx_dma(ourport); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void s3c64xx_start_rx_dma(struct s3c24xx_uart_port *ourport) +@@ -722,7 +722,7 @@ static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) + utrstat = rd_regl(port, S3C2410_UTRSTAT); + rd_regl(port, S3C2410_UFSTAT); + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + if (!(utrstat & S3C2410_UTRSTAT_TIMEOUT)) { + s3c64xx_start_rx_dma(ourport); +@@ -751,7 +751,7 @@ static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) + wr_regl(port, S3C2410_UTRSTAT, S3C2410_UTRSTAT_TIMEOUT); + + finish: +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -849,9 +849,9 @@ static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) + struct s3c24xx_uart_port *ourport = dev_id; + struct uart_port *port = &ourport->port; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + s3c24xx_serial_rx_drain_fifo(ourport); +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -932,11 +932,11 @@ static irqreturn_t s3c24xx_serial_tx_irq(int irq, void *id) + struct s3c24xx_uart_port *ourport = id; + struct uart_port *port = &ourport->port; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + s3c24xx_serial_tx_chars(ourport); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + return IRQ_HANDLED; + } + +@@ -1032,7 +1032,7 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) + unsigned long flags; + unsigned int ucon; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + ucon = rd_regl(port, S3C2410_UCON); + +@@ -1043,7 +1043,7 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) + + wr_regl(port, S3C2410_UCON, ucon); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) +@@ -1302,7 +1302,7 @@ static int s3c64xx_serial_startup(struct uart_port *port) + ourport->rx_enabled = 1; + ourport->tx_enabled = 0; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + ufcon = rd_regl(port, S3C2410_UFCON); + ufcon |= S3C2410_UFCON_RESETRX | S5PV210_UFCON_RXTRIG8; +@@ -1312,7 +1312,7 @@ static int s3c64xx_serial_startup(struct uart_port *port) + + enable_rx_pio(ourport); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + /* Enable Rx Interrupt */ + s3c24xx_clear_bit(port, S3C64XX_UINTM_RXD, S3C64XX_UINTM); +@@ -1340,7 +1340,7 @@ static int apple_s5l_serial_startup(struct uart_port *port) + ourport->rx_enabled = 1; + ourport->tx_enabled = 0; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + ufcon = rd_regl(port, S3C2410_UFCON); + ufcon |= S3C2410_UFCON_RESETRX | S5PV210_UFCON_RXTRIG8; +@@ -1350,7 +1350,7 @@ static int apple_s5l_serial_startup(struct uart_port *port) + + enable_rx_pio(ourport); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + /* Enable Rx Interrupt */ + s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTHRESH_ENA, S3C2410_UCON); +@@ -1625,7 +1625,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, + ulcon |= S3C2410_LCON_PNONE; + } + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + dev_dbg(port->dev, + "setting ulcon to %08x, brddiv to %d, udivslot %08x\n", +@@ -1683,7 +1683,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= RXSTAT_DUMMY_READ; + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *s3c24xx_serial_type(struct uart_port *port) +@@ -2375,14 +2375,14 @@ s3c24xx_serial_console_write(struct console *co, const char *s, + if (cons_uart->sysrq) + locked = false; + else if (oops_in_progress) +- locked = spin_trylock_irqsave(&cons_uart->lock, flags); ++ locked = uart_port_trylock_irqsave(cons_uart, &flags); + else +- spin_lock_irqsave(&cons_uart->lock, flags); ++ uart_port_lock_irqsave(cons_uart, &flags); + + uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar); + + if (locked) +- spin_unlock_irqrestore(&cons_uart->lock, flags); ++ uart_port_unlock_irqrestore(cons_uart, flags); + } + + /* Shouldn't be __init, as it can be instantiated from other module */ +diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c +index f3cd69346482..dbec29d9a6c3 100644 +--- a/drivers/tty/serial/sb1250-duart.c ++++ b/drivers/tty/serial/sb1250-duart.c +@@ -610,7 +610,7 @@ static void sbd_set_termios(struct uart_port *uport, struct ktermios *termios, + else + aux &= ~M_DUART_CTS_CHNG_ENA; + +- spin_lock(&uport->lock); ++ uart_port_lock(uport); + + if (sport->tx_stopped) + command |= M_DUART_TX_DIS; +@@ -632,7 +632,7 @@ static void sbd_set_termios(struct uart_port *uport, struct ktermios *termios, + + write_sbdchn(sport, R_DUART_CMD, command); + +- spin_unlock(&uport->lock); ++ uart_port_unlock(uport); + } + + +@@ -839,22 +839,22 @@ static void sbd_console_write(struct console *co, const char *s, + unsigned int mask; + + /* Disable transmit interrupts and enable the transmitter. */ +- spin_lock_irqsave(&uport->lock, flags); ++ uart_port_lock_irqsave(uport, &flags); + mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2)); + write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), + mask & ~M_DUART_IMR_TX); + write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_EN); +- spin_unlock_irqrestore(&uport->lock, flags); ++ uart_port_unlock_irqrestore(uport, flags); + + uart_console_write(&sport->port, s, count, sbd_console_putchar); + + /* Restore transmit interrupts and the transmitter enable. */ +- spin_lock_irqsave(&uport->lock, flags); ++ uart_port_lock_irqsave(uport, &flags); + sbd_line_drain(sport); + if (sport->tx_stopped) + write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS); + write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask); +- spin_unlock_irqrestore(&uport->lock, flags); ++ uart_port_unlock_irqrestore(uport, flags); + } + + static int __init sbd_console_setup(struct console *co, char *options) +diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c +index 7a9924d9b294..be9ae5cdd1b8 100644 +--- a/drivers/tty/serial/sc16is7xx.c ++++ b/drivers/tty/serial/sc16is7xx.c +@@ -817,6 +817,7 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws) + { + struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); ++ unsigned long flags; + + if ((port->rs485.flags & SER_RS485_ENABLED) && + (port->rs485.delay_rts_before_send > 0)) +@@ -825,6 +826,10 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws) + mutex_lock(&one->efr_lock); + sc16is7xx_handle_tx(port); + mutex_unlock(&one->efr_lock); ++ ++ uart_port_lock_irqsave(port, &flags); ++ sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void sc16is7xx_reconf_rs485(struct uart_port *port) +diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c +index d4ec943cb8e9..6d4006b41975 100644 +--- a/drivers/tty/serial/serial-tegra.c ++++ b/drivers/tty/serial/serial-tegra.c +@@ -411,7 +411,7 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) + divisor = DIV_ROUND_CLOSEST(rate, baud * 16); + } + +- spin_lock_irqsave(&tup->uport.lock, flags); ++ uart_port_lock_irqsave(&tup->uport, &flags); + lcr = tup->lcr_shadow; + lcr |= UART_LCR_DLAB; + tegra_uart_write(tup, lcr, UART_LCR); +@@ -424,7 +424,7 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) + + /* Dummy read to ensure the write is posted */ + tegra_uart_read(tup, UART_SCR); +- spin_unlock_irqrestore(&tup->uport.lock, flags); ++ uart_port_unlock_irqrestore(&tup->uport, flags); + + tup->current_baud = baud; + +@@ -522,13 +522,13 @@ static void tegra_uart_tx_dma_complete(void *args) + dmaengine_tx_status(tup->tx_dma_chan, tup->tx_cookie, &state); + count = tup->tx_bytes_requested - state.residue; + async_tx_ack(tup->tx_dma_desc); +- spin_lock_irqsave(&tup->uport.lock, flags); ++ uart_port_lock_irqsave(&tup->uport, &flags); + uart_xmit_advance(&tup->uport, count); + tup->tx_in_progress = 0; + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&tup->uport); + tegra_uart_start_next_tx(tup); +- spin_unlock_irqrestore(&tup->uport.lock, flags); ++ uart_port_unlock_irqrestore(&tup->uport, flags); + } + + static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup, +@@ -598,13 +598,13 @@ static unsigned int tegra_uart_tx_empty(struct uart_port *u) + unsigned int ret = 0; + unsigned long flags; + +- spin_lock_irqsave(&u->lock, flags); ++ uart_port_lock_irqsave(u, &flags); + if (!tup->tx_in_progress) { + unsigned long lsr = tegra_uart_read(tup, UART_LSR); + if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS) + ret = TIOCSER_TEMT; + } +- spin_unlock_irqrestore(&u->lock, flags); ++ uart_port_unlock_irqrestore(u, flags); + return ret; + } + +@@ -727,7 +727,7 @@ static void tegra_uart_rx_dma_complete(void *args) + struct dma_tx_state state; + enum dma_status status; + +- spin_lock_irqsave(&u->lock, flags); ++ uart_port_lock_irqsave(u, &flags); + + status = dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); + +@@ -749,7 +749,7 @@ static void tegra_uart_rx_dma_complete(void *args) + set_rts(tup, true); + + done: +- spin_unlock_irqrestore(&u->lock, flags); ++ uart_port_unlock_irqrestore(u, flags); + } + + static void tegra_uart_terminate_rx_dma(struct tegra_uart_port *tup) +@@ -836,7 +836,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) + bool is_rx_int = false; + unsigned long flags; + +- spin_lock_irqsave(&u->lock, flags); ++ uart_port_lock_irqsave(u, &flags); + while (1) { + iir = tegra_uart_read(tup, UART_IIR); + if (iir & UART_IIR_NO_INT) { +@@ -852,7 +852,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) + } else if (is_rx_start) { + tegra_uart_start_rx_dma(tup); + } +- spin_unlock_irqrestore(&u->lock, flags); ++ uart_port_unlock_irqrestore(u, flags); + return IRQ_HANDLED; + } + +@@ -969,11 +969,11 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *tup) + } + } + +- spin_lock_irqsave(&tup->uport.lock, flags); ++ uart_port_lock_irqsave(&tup->uport, &flags); + /* Reset the Rx and Tx FIFOs */ + tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); + tup->current_baud = 0; +- spin_unlock_irqrestore(&tup->uport.lock, flags); ++ uart_port_unlock_irqrestore(&tup->uport, flags); + + tup->rx_in_progress = 0; + tup->tx_in_progress = 0; +@@ -1292,7 +1292,7 @@ static void tegra_uart_set_termios(struct uart_port *u, + int ret; + + max_divider *= 16; +- spin_lock_irqsave(&u->lock, flags); ++ uart_port_lock_irqsave(u, &flags); + + /* Changing configuration, it is safe to stop any rx now */ + if (tup->rts_active) +@@ -1341,7 +1341,7 @@ static void tegra_uart_set_termios(struct uart_port *u, + baud = uart_get_baud_rate(u, termios, oldtermios, + parent_clk_rate/max_divider, + parent_clk_rate/16); +- spin_unlock_irqrestore(&u->lock, flags); ++ uart_port_unlock_irqrestore(u, flags); + ret = tegra_set_baudrate(tup, baud); + if (ret < 0) { + dev_err(tup->uport.dev, "Failed to set baud rate\n"); +@@ -1349,7 +1349,7 @@ static void tegra_uart_set_termios(struct uart_port *u, + } + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +- spin_lock_irqsave(&u->lock, flags); ++ uart_port_lock_irqsave(u, &flags); + + /* Flow control */ + if (termios->c_cflag & CRTSCTS) { +@@ -1382,7 +1382,7 @@ static void tegra_uart_set_termios(struct uart_port *u, + if (termios->c_iflag & IGNBRK) + tup->uport.ignore_status_mask |= UART_LSR_BI; + +- spin_unlock_irqrestore(&u->lock, flags); ++ uart_port_unlock_irqrestore(u, flags); + } + + static const char *tegra_uart_type(struct uart_port *u) +diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c +index ed8798fdf522..f0cd976e22e2 100644 +--- a/drivers/tty/serial/serial_core.c ++++ b/drivers/tty/serial/serial_core.c +@@ -79,7 +79,7 @@ static inline void uart_port_deref(struct uart_port *uport) + ({ \ + struct uart_port *__uport = uart_port_ref(state); \ + if (__uport) \ +- spin_lock_irqsave(&__uport->lock, flags); \ ++ uart_port_lock_irqsave(__uport, &flags); \ + __uport; \ + }) + +@@ -87,7 +87,7 @@ static inline void uart_port_deref(struct uart_port *uport) + ({ \ + struct uart_port *__uport = uport; \ + if (__uport) { \ +- spin_unlock_irqrestore(&__uport->lock, flags); \ ++ uart_port_unlock_irqrestore(__uport, flags); \ + uart_port_deref(__uport); \ + } \ + }) +@@ -179,12 +179,12 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) + unsigned long flags; + unsigned int old; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + old = port->mctrl; + port->mctrl = (old & ~clear) | set; + if (old != port->mctrl && !(port->rs485.flags & SER_RS485_ENABLED)) + port->ops->set_mctrl(port, port->mctrl); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + #define uart_set_mctrl(port, set) uart_update_mctrl(port, set, 0) +@@ -219,7 +219,7 @@ static void uart_change_line_settings(struct tty_struct *tty, struct uart_state + /* + * Set modem status enables based on termios cflag + */ +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + if (termios->c_cflag & CRTSCTS) + uport->status |= UPSTAT_CTS_ENABLE; + else +@@ -240,7 +240,7 @@ static void uart_change_line_settings(struct tty_struct *tty, struct uart_state + else + __uart_start(state); + } +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + } + + /* +@@ -715,11 +715,11 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) + if (port->ops->send_xchar) + port->ops->send_xchar(port, ch); + else { +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + port->x_char = ch; + if (ch) + port->ops->start_tx(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + uart_port_deref(port); + } +@@ -1106,9 +1106,9 @@ static int uart_tiocmget(struct tty_struct *tty) + + if (!tty_io_error(tty)) { + result = uport->mctrl; +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + result |= uport->ops->get_mctrl(uport); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + } + out: + mutex_unlock(&port->mutex); +@@ -1244,16 +1244,16 @@ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) + uport = uart_port_ref(state); + if (!uport) + return -EIO; +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + memcpy(&cprev, &uport->icount, sizeof(struct uart_icount)); + uart_enable_ms(uport); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + + add_wait_queue(&port->delta_msr_wait, &wait); + for (;;) { +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + + set_current_state(TASK_INTERRUPTIBLE); + +@@ -1298,9 +1298,9 @@ static int uart_get_icount(struct tty_struct *tty, + uport = uart_port_ref(state); + if (!uport) + return -EIO; +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + uart_port_deref(uport); + + icount->cts = cnow.cts; +@@ -1453,9 +1453,9 @@ static int uart_rs485_config(struct uart_port *port) + uart_set_rs485_termination(port, rs485); + uart_set_rs485_rx_during_tx(port, rs485); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + ret = port->rs485_config(port, NULL, rs485); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + if (ret) { + memset(rs485, 0, sizeof(*rs485)); + /* unset GPIOs */ +@@ -1472,9 +1472,9 @@ static int uart_get_rs485_config(struct uart_port *port, + unsigned long flags; + struct serial_rs485 aux; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + aux = port->rs485; +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (copy_to_user(rs485, &aux, sizeof(aux))) + return -EFAULT; +@@ -1502,7 +1502,7 @@ static int uart_set_rs485_config(struct tty_struct *tty, struct uart_port *port, + uart_set_rs485_termination(port, &rs485); + uart_set_rs485_rx_during_tx(port, &rs485); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + ret = port->rs485_config(port, &tty->termios, &rs485); + if (!ret) { + port->rs485 = rs485; +@@ -1511,7 +1511,7 @@ static int uart_set_rs485_config(struct tty_struct *tty, struct uart_port *port, + if (!(rs485.flags & SER_RS485_ENABLED)) + port->ops->set_mctrl(port, port->mctrl); + } +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + if (ret) { + /* restore old GPIO settings */ + gpiod_set_value_cansleep(port->rs485_term_gpio, +@@ -1536,9 +1536,9 @@ static int uart_get_iso7816_config(struct uart_port *port, + if (!port->iso7816_config) + return -ENOTTY; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + aux = port->iso7816; +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (copy_to_user(iso7816, &aux, sizeof(aux))) + return -EFAULT; +@@ -1567,9 +1567,9 @@ static int uart_set_iso7816_config(struct uart_port *port, + if (iso7816.reserved[i]) + return -EINVAL; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + ret = port->iso7816_config(port, &iso7816); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + if (ret) + return ret; + +@@ -1786,9 +1786,9 @@ static void uart_tty_port_shutdown(struct tty_port *port) + if (WARN(!uport, "detached port still initialized!\n")) + return; + +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + uport->ops->stop_rx(uport); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + + serial_base_port_shutdown(uport); + uart_port_shutdown(port); +@@ -1803,11 +1803,11 @@ static void uart_tty_port_shutdown(struct tty_port *port) + /* + * Free the transmit buffer. + */ +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + uart_circ_clear(&state->xmit); + buf = state->xmit.buf; + state->xmit.buf = NULL; +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + + free_page((unsigned long)buf); + +@@ -1950,10 +1950,10 @@ static bool uart_carrier_raised(struct tty_port *port) + */ + if (WARN_ON(!uport)) + return true; +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + uart_enable_ms(uport); + mctrl = uport->ops->get_mctrl(uport); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + uart_port_deref(uport); + + return mctrl & TIOCM_CAR; +@@ -2070,9 +2070,9 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) + pm_state = state->pm_state; + if (pm_state != UART_PM_STATE_ON) + uart_change_pm(state, UART_PM_STATE_ON); +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + status = uport->ops->get_mctrl(uport); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + if (pm_state != UART_PM_STATE_ON) + uart_change_pm(state, pm_state); + +@@ -2411,9 +2411,9 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) + */ + if (!console_suspend_enabled && uart_console(uport)) { + if (uport->ops->start_rx) { +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + uport->ops->stop_rx(uport); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + } + goto unlock; + } +@@ -2428,7 +2428,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) + tty_port_set_suspended(port, true); + tty_port_set_initialized(port, false); + +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + ops->stop_tx(uport); + if (!(uport->rs485.flags & SER_RS485_ENABLED)) + ops->set_mctrl(uport, 0); +@@ -2436,7 +2436,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) + mctrl = uport->mctrl; + uport->mctrl = 0; + ops->stop_rx(uport); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + + /* + * Wait for the transmitter to empty. +@@ -2508,9 +2508,9 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) + uart_change_pm(state, UART_PM_STATE_ON); + uport->ops->set_termios(uport, &termios, NULL); + if (!console_suspend_enabled && uport->ops->start_rx) { +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + uport->ops->start_rx(uport); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + } + if (console_suspend_enabled) + console_start(uport->cons); +@@ -2521,10 +2521,10 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) + int ret; + + uart_change_pm(state, UART_PM_STATE_ON); +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + if (!(uport->rs485.flags & SER_RS485_ENABLED)) + ops->set_mctrl(uport, 0); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + if (console_suspend_enabled || !uart_console(uport)) { + /* Protected by port mutex for now */ + struct tty_struct *tty = port->tty; +@@ -2534,11 +2534,11 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) + if (tty) + uart_change_line_settings(tty, state, NULL); + uart_rs485_config(uport); +- spin_lock_irq(&uport->lock); ++ uart_port_lock_irq(uport); + if (!(uport->rs485.flags & SER_RS485_ENABLED)) + ops->set_mctrl(uport, uport->mctrl); + ops->start_tx(uport); +- spin_unlock_irq(&uport->lock); ++ uart_port_unlock_irq(uport); + tty_port_set_initialized(port, true); + } else { + /* +@@ -2650,11 +2650,11 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, + * keep the DTR setting that is set in uart_set_options() + * We probably don't need a spinlock around this, but + */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + port->mctrl &= TIOCM_DTR; + if (!(port->rs485.flags & SER_RS485_ENABLED)) + port->ops->set_mctrl(port, port->mctrl); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + uart_rs485_config(port); + +diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c +index 7d5aaa8d422b..e51ca593ab86 100644 +--- a/drivers/tty/serial/serial_mctrl_gpio.c ++++ b/drivers/tty/serial/serial_mctrl_gpio.c +@@ -184,7 +184,7 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) + + mctrl_gpio_get(gpios, &mctrl); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + mctrl_diff = mctrl ^ gpios->mctrl_prev; + gpios->mctrl_prev = mctrl; +@@ -205,7 +205,7 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) + wake_up_interruptible(&port->state->port.delta_msr_wait); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return IRQ_HANDLED; + } +diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c +index 469ad26cde48..66fd117d8aea 100644 +--- a/drivers/tty/serial/serial_port.c ++++ b/drivers/tty/serial/serial_port.c +@@ -38,14 +38,14 @@ static int serial_port_runtime_resume(struct device *dev) + goto out; + + /* Flush any pending TX for the port */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (!port_dev->tx_enabled) + goto unlock; + if (__serial_port_busy(port)) + port->ops->start_tx(port); + + unlock: +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + out: + pm_runtime_mark_last_busy(dev); +diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c +index be08fb6f749c..eaa980722455 100644 +--- a/drivers/tty/serial/serial_txx9.c ++++ b/drivers/tty/serial/serial_txx9.c +@@ -335,13 +335,13 @@ static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id) + unsigned int status; + + while (1) { +- spin_lock(&up->lock); ++ uart_port_lock(up); + status = sio_in(up, TXX9_SIDISR); + if (!(sio_in(up, TXX9_SIDICR) & TXX9_SIDICR_TIE)) + status &= ~TXX9_SIDISR_TDIS; + if (!(status & (TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | + TXX9_SIDISR_TOUT))) { +- spin_unlock(&up->lock); ++ uart_port_unlock(up); + break; + } + +@@ -353,7 +353,7 @@ static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id) + sio_mask(up, TXX9_SIDISR, + TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | + TXX9_SIDISR_TOUT); +- spin_unlock(&up->lock); ++ uart_port_unlock(up); + + if (pass_counter++ > PASS_LIMIT) + break; +@@ -367,9 +367,9 @@ static unsigned int serial_txx9_tx_empty(struct uart_port *up) + unsigned long flags; + unsigned int ret; + +- spin_lock_irqsave(&up->lock, flags); ++ uart_port_lock_irqsave(up, &flags); + ret = (sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS) ? TIOCSER_TEMT : 0; +- spin_unlock_irqrestore(&up->lock, flags); ++ uart_port_unlock_irqrestore(up, flags); + + return ret; + } +@@ -399,12 +399,12 @@ static void serial_txx9_break_ctl(struct uart_port *up, int break_state) + { + unsigned long flags; + +- spin_lock_irqsave(&up->lock, flags); ++ uart_port_lock_irqsave(up, &flags); + if (break_state == -1) + sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); + else + sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); +- spin_unlock_irqrestore(&up->lock, flags); ++ uart_port_unlock_irqrestore(up, flags); + } + + #if defined(CONFIG_SERIAL_TXX9_CONSOLE) || defined(CONFIG_CONSOLE_POLL) +@@ -517,9 +517,9 @@ static int serial_txx9_startup(struct uart_port *up) + /* + * Now, initialize the UART + */ +- spin_lock_irqsave(&up->lock, flags); ++ uart_port_lock_irqsave(up, &flags); + serial_txx9_set_mctrl(up, up->mctrl); +- spin_unlock_irqrestore(&up->lock, flags); ++ uart_port_unlock_irqrestore(up, flags); + + /* Enable RX/TX */ + sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); +@@ -541,9 +541,9 @@ static void serial_txx9_shutdown(struct uart_port *up) + */ + sio_out(up, TXX9_SIDICR, 0); /* disable all intrs */ + +- spin_lock_irqsave(&up->lock, flags); ++ uart_port_lock_irqsave(up, &flags); + serial_txx9_set_mctrl(up, up->mctrl); +- spin_unlock_irqrestore(&up->lock, flags); ++ uart_port_unlock_irqrestore(up, flags); + + /* + * Disable break condition +@@ -625,7 +625,7 @@ serial_txx9_set_termios(struct uart_port *up, struct ktermios *termios, + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ +- spin_lock_irqsave(&up->lock, flags); ++ uart_port_lock_irqsave(up, &flags); + + /* + * Update the per-port timeout. +@@ -676,7 +676,7 @@ serial_txx9_set_termios(struct uart_port *up, struct ktermios *termios, + sio_out(up, TXX9_SIFCR, fcr); + + serial_txx9_set_mctrl(up, up->mctrl); +- spin_unlock_irqrestore(&up->lock, flags); ++ uart_port_unlock_irqrestore(up, flags); + } + + static void +diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c +index f793624fd501..2559c97812fa 100644 +--- a/drivers/tty/serial/sh-sci.c ++++ b/drivers/tty/serial/sh-sci.c +@@ -1205,7 +1205,7 @@ static void sci_dma_tx_complete(void *arg) + + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + uart_xmit_advance(port, s->tx_dma_len); + +@@ -1229,7 +1229,7 @@ static void sci_dma_tx_complete(void *arg) + } + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* Locking: called with port lock held */ +@@ -1325,7 +1325,7 @@ static void sci_dma_rx_complete(void *arg) + dev_dbg(port->dev, "%s(%d) active cookie %d\n", __func__, port->line, + s->active_rx); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + active = sci_dma_rx_find_active(s); + if (active >= 0) +@@ -1352,20 +1352,20 @@ static void sci_dma_rx_complete(void *arg) + + dma_async_issue_pending(chan); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", + __func__, s->cookie_rx[active], active, s->active_rx); + return; + + fail: +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); + /* Switch to PIO */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + dmaengine_terminate_async(chan); + sci_dma_rx_chan_invalidate(s); + sci_dma_rx_reenable_irq(s); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void sci_dma_tx_release(struct sci_port *s) +@@ -1414,13 +1414,13 @@ static int sci_dma_rx_submit(struct sci_port *s, bool port_lock_held) + fail: + /* Switch to PIO */ + if (!port_lock_held) +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + if (i) + dmaengine_terminate_async(chan); + sci_dma_rx_chan_invalidate(s); + sci_start_rx(port); + if (!port_lock_held) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + return -EAGAIN; + } + +@@ -1442,14 +1442,14 @@ static void sci_dma_tx_work_fn(struct work_struct *work) + * transmit till the end, and then the rest. Take the port lock to get a + * consistent xmit buffer state. + */ +- spin_lock_irq(&port->lock); ++ uart_port_lock_irq(port); + head = xmit->head; + tail = xmit->tail; + buf = s->tx_dma_addr + tail; + s->tx_dma_len = CIRC_CNT_TO_END(head, tail, UART_XMIT_SIZE); + if (!s->tx_dma_len) { + /* Transmit buffer has been flushed */ +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + return; + } + +@@ -1457,7 +1457,7 @@ static void sci_dma_tx_work_fn(struct work_struct *work) + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n"); + goto switch_to_pio; + } +@@ -1469,12 +1469,12 @@ static void sci_dma_tx_work_fn(struct work_struct *work) + desc->callback_param = s; + s->cookie_tx = dmaengine_submit(desc); + if (dma_submit_error(s->cookie_tx)) { +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); + goto switch_to_pio; + } + +- spin_unlock_irq(&port->lock); ++ uart_port_unlock_irq(port); + dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", + __func__, xmit->buf, tail, head, s->cookie_tx); + +@@ -1482,10 +1482,10 @@ static void sci_dma_tx_work_fn(struct work_struct *work) + return; + + switch_to_pio: +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + s->chan_tx = NULL; + sci_start_tx(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + return; + } + +@@ -1502,17 +1502,17 @@ static enum hrtimer_restart sci_dma_rx_timer_fn(struct hrtimer *t) + + dev_dbg(port->dev, "DMA Rx timed out\n"); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + active = sci_dma_rx_find_active(s); + if (active < 0) { +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + return HRTIMER_NORESTART; + } + + status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); + if (status == DMA_COMPLETE) { +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + dev_dbg(port->dev, "Cookie %d #%d has already completed\n", + s->active_rx, active); + +@@ -1530,7 +1530,7 @@ static enum hrtimer_restart sci_dma_rx_timer_fn(struct hrtimer *t) + */ + status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); + if (status == DMA_COMPLETE) { +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + dev_dbg(port->dev, "Transaction complete after DMA engine was stopped"); + return HRTIMER_NORESTART; + } +@@ -1551,7 +1551,7 @@ static enum hrtimer_restart sci_dma_rx_timer_fn(struct hrtimer *t) + + sci_dma_rx_reenable_irq(s); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return HRTIMER_NORESTART; + } +@@ -1775,9 +1775,9 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr) + struct uart_port *port = ptr; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + sci_transmit_chars(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return IRQ_HANDLED; + } +@@ -1791,11 +1791,11 @@ static irqreturn_t sci_tx_end_interrupt(int irq, void *ptr) + if (port->type != PORT_SCI) + return sci_tx_interrupt(irq, ptr); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + ctrl = serial_port_in(port, SCSCR); + ctrl &= ~(SCSCR_TE | SCSCR_TEIE); + serial_port_out(port, SCSCR, ctrl); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return IRQ_HANDLED; + } +@@ -2192,7 +2192,7 @@ static void sci_break_ctl(struct uart_port *port, int break_state) + return; + } + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + scsptr = serial_port_in(port, SCSPTR); + scscr = serial_port_in(port, SCSCR); + +@@ -2206,7 +2206,7 @@ static void sci_break_ctl(struct uart_port *port, int break_state) + + serial_port_out(port, SCSPTR, scsptr); + serial_port_out(port, SCSCR, scscr); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int sci_startup(struct uart_port *port) +@@ -2238,7 +2238,7 @@ static void sci_shutdown(struct uart_port *port) + s->autorts = false; + mctrl_gpio_disable_ms(to_sci_port(port)->gpios); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + sci_stop_rx(port); + sci_stop_tx(port); + /* +@@ -2248,7 +2248,7 @@ static void sci_shutdown(struct uart_port *port) + scr = serial_port_in(port, SCSCR); + serial_port_out(port, SCSCR, scr & + (SCSCR_CKE1 | SCSCR_CKE0 | s->hscif_tot)); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + #ifdef CONFIG_SERIAL_SH_SCI_DMA + if (s->chan_rx_saved) { +@@ -2550,7 +2550,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, + serial_port_out(port, SCCKS, sccks); + } + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + sci_reset(port); + +@@ -2672,7 +2672,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, + if ((termios->c_cflag & CREAD) != 0) + sci_start_rx(port); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + sci_port_disable(s); + +@@ -3057,9 +3057,9 @@ static void serial_console_write(struct console *co, const char *s, + if (port->sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock_irqsave(&port->lock, flags); ++ locked = uart_port_trylock_irqsave(port, &flags); + else +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* first save SCSCR then disable interrupts, keep clock source */ + ctrl = serial_port_in(port, SCSCR); +@@ -3079,7 +3079,7 @@ static void serial_console_write(struct console *co, const char *s, + serial_port_out(port, SCSCR, ctrl); + + if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int serial_console_setup(struct console *co, char *options) +diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c +index d195c5de52e7..b296e57a9dee 100644 +--- a/drivers/tty/serial/sifive.c ++++ b/drivers/tty/serial/sifive.c +@@ -521,11 +521,11 @@ static irqreturn_t sifive_serial_irq(int irq, void *dev_id) + struct sifive_serial_port *ssp = dev_id; + u32 ip; + +- spin_lock(&ssp->port.lock); ++ uart_port_lock(&ssp->port); + + ip = __ssp_readl(ssp, SIFIVE_SERIAL_IP_OFFS); + if (!ip) { +- spin_unlock(&ssp->port.lock); ++ uart_port_unlock(&ssp->port); + return IRQ_NONE; + } + +@@ -534,7 +534,7 @@ static irqreturn_t sifive_serial_irq(int irq, void *dev_id) + if (ip & SIFIVE_SERIAL_IP_TXWM_MASK) + __ssp_transmit_chars(ssp); + +- spin_unlock(&ssp->port.lock); ++ uart_port_unlock(&ssp->port); + + return IRQ_HANDLED; + } +@@ -653,7 +653,7 @@ static void sifive_serial_set_termios(struct uart_port *port, + ssp->port.uartclk / 16); + __ssp_update_baud_rate(ssp, rate); + +- spin_lock_irqsave(&ssp->port.lock, flags); ++ uart_port_lock_irqsave(&ssp->port, &flags); + + /* Update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, rate); +@@ -670,7 +670,7 @@ static void sifive_serial_set_termios(struct uart_port *port, + if (v != old_v) + __ssp_writel(v, SIFIVE_SERIAL_RXCTRL_OFFS, ssp); + +- spin_unlock_irqrestore(&ssp->port.lock, flags); ++ uart_port_unlock_irqrestore(&ssp->port, flags); + } + + static void sifive_serial_release_port(struct uart_port *port) +@@ -795,9 +795,9 @@ static void sifive_serial_console_write(struct console *co, const char *s, + if (ssp->port.sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock(&ssp->port.lock); ++ locked = uart_port_trylock(&ssp->port); + else +- spin_lock(&ssp->port.lock); ++ uart_port_lock(&ssp->port); + + ier = __ssp_readl(ssp, SIFIVE_SERIAL_IE_OFFS); + __ssp_writel(0, SIFIVE_SERIAL_IE_OFFS, ssp); +@@ -807,7 +807,7 @@ static void sifive_serial_console_write(struct console *co, const char *s, + __ssp_writel(ier, SIFIVE_SERIAL_IE_OFFS, ssp); + + if (locked) +- spin_unlock(&ssp->port.lock); ++ uart_port_unlock(&ssp->port); + local_irq_restore(flags); + } + +diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c +index f328fa57231f..f257525f9299 100644 +--- a/drivers/tty/serial/sprd_serial.c ++++ b/drivers/tty/serial/sprd_serial.c +@@ -247,7 +247,7 @@ static void sprd_complete_tx_dma(void *data) + struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + dma_unmap_single(port->dev, sp->tx_dma.phys_addr, + sp->tx_dma.trans_len, DMA_TO_DEVICE); + +@@ -260,7 +260,7 @@ static void sprd_complete_tx_dma(void *data) + sprd_tx_dma_config(port)) + sp->tx_dma.trans_len = 0; + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int sprd_uart_dma_submit(struct uart_port *port, +@@ -429,13 +429,13 @@ static void sprd_complete_rx_dma(void *data) + enum dma_status status; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + status = dmaengine_tx_status(sp->rx_dma.chn, + sp->rx_dma.cookie, &state); + if (status != DMA_COMPLETE) { + sprd_stop_rx(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + return; + } + +@@ -449,7 +449,7 @@ static void sprd_complete_rx_dma(void *data) + if (sprd_start_dma_rx(port)) + sprd_stop_rx(port); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int sprd_start_dma_rx(struct uart_port *port) +@@ -638,12 +638,12 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) + struct uart_port *port = dev_id; + unsigned int ims; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + ims = serial_in(port, SPRD_IMSR); + + if (!ims) { +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + return IRQ_NONE; + } + +@@ -660,7 +660,7 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) + if (ims & SPRD_IMSR_TX_FIFO_EMPTY) + sprd_tx(port); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -727,13 +727,13 @@ static int sprd_startup(struct uart_port *port) + serial_out(port, SPRD_CTL1, fc); + + /* enable interrupt */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + ien = serial_in(port, SPRD_IEN); + ien |= SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT; + if (!sp->rx_dma.enable) + ien |= SPRD_IEN_RX_FULL; + serial_out(port, SPRD_IEN, ien); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return 0; + } +@@ -793,7 +793,7 @@ static void sprd_set_termios(struct uart_port *port, struct ktermios *termios, + lcr |= SPRD_LCR_EVEN_PAR; + } + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); +@@ -837,7 +837,7 @@ static void sprd_set_termios(struct uart_port *port, struct ktermios *termios, + fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; + serial_out(port, SPRD_CTL1, fc); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) +@@ -974,9 +974,9 @@ static void sprd_console_write(struct console *co, const char *s, + if (port->sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock_irqsave(&port->lock, flags); ++ locked = uart_port_trylock_irqsave(port, &flags); + else +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + uart_console_write(port, s, count, sprd_console_putchar); + +@@ -984,7 +984,7 @@ static void sprd_console_write(struct console *co, const char *s, + wait_for_xmitr(port); + + if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int sprd_console_setup(struct console *co, char *options) +diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c +index 92b9f6894006..a821f5d76a26 100644 +--- a/drivers/tty/serial/st-asc.c ++++ b/drivers/tty/serial/st-asc.c +@@ -319,7 +319,7 @@ static irqreturn_t asc_interrupt(int irq, void *ptr) + struct uart_port *port = ptr; + u32 status; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + status = asc_in(port, ASC_STA); + +@@ -334,7 +334,7 @@ static irqreturn_t asc_interrupt(int irq, void *ptr) + asc_transmit_chars(port); + } + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -452,10 +452,10 @@ static void asc_pm(struct uart_port *port, unsigned int state, + * we can come to turning it off. Note this is not called with + * the port spinlock held. + */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + ctl = asc_in(port, ASC_CTL) & ~ASC_CTL_RUN; + asc_out(port, ASC_CTL, ctl); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + clk_disable_unprepare(ascport->clk); + break; + } +@@ -480,7 +480,7 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios, + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + cflag = termios->c_cflag; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* read control register */ + ctrl_val = asc_in(port, ASC_CTL); +@@ -594,7 +594,7 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios, + /* write final value and enable port */ + asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN)); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *asc_type(struct uart_port *port) +@@ -849,9 +849,9 @@ static void asc_console_write(struct console *co, const char *s, unsigned count) + if (port->sysrq) + locked = 0; /* asc_interrupt has already claimed the lock */ + else if (oops_in_progress) +- locked = spin_trylock_irqsave(&port->lock, flags); ++ locked = uart_port_trylock_irqsave(port, &flags); + else +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* + * Disable interrupts so we don't get the IRQ line bouncing +@@ -869,7 +869,7 @@ static void asc_console_write(struct console *co, const char *s, unsigned count) + asc_out(port, ASC_INTEN, intenable); + + if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int asc_console_setup(struct console *co, char *options) +diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c +index 9ef90bb30a47..b963f9ccb070 100644 +--- a/drivers/tty/serial/stm32-usart.c ++++ b/drivers/tty/serial/stm32-usart.c +@@ -535,7 +535,7 @@ static void stm32_usart_rx_dma_complete(void *arg) + unsigned int size; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + size = stm32_usart_receive_chars(port, false); + uart_unlock_and_check_sysrq_irqrestore(port, flags); + if (size) +@@ -641,9 +641,9 @@ static void stm32_usart_tx_dma_complete(void *arg) + stm32_usart_tx_dma_terminate(stm32port); + + /* Let's see if we have pending data to send */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + stm32_usart_transmit_chars(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void stm32_usart_tx_interrupt_enable(struct uart_port *port) +@@ -892,7 +892,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) + if (!stm32_port->throttled) { + if (((sr & USART_SR_RXNE) && !stm32_usart_rx_dma_started(stm32_port)) || + ((sr & USART_SR_ERR_MASK) && stm32_usart_rx_dma_started(stm32_port))) { +- spin_lock(&port->lock); ++ uart_port_lock(port); + size = stm32_usart_receive_chars(port, false); + uart_unlock_and_check_sysrq(port); + if (size) +@@ -902,15 +902,15 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) + } + + if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) { +- spin_lock(&port->lock); ++ uart_port_lock(port); + stm32_usart_transmit_chars(port); +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + ret = IRQ_HANDLED; + } + + /* Receiver timeout irq for DMA RX */ + if (stm32_usart_rx_dma_started(stm32_port) && !stm32_port->throttled) { +- spin_lock(&port->lock); ++ uart_port_lock(port); + size = stm32_usart_receive_chars(port, false); + uart_unlock_and_check_sysrq(port); + if (size) +@@ -999,7 +999,7 @@ static void stm32_usart_throttle(struct uart_port *port) + const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* + * Pause DMA transfer, so the RX data gets queued into the FIFO. +@@ -1012,7 +1012,7 @@ static void stm32_usart_throttle(struct uart_port *port) + stm32_usart_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); + + stm32_port->throttled = true; +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* Unthrottle the remote, the input buffer can now accept data. */ +@@ -1022,7 +1022,7 @@ static void stm32_usart_unthrottle(struct uart_port *port) + const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + stm32_usart_set_bits(port, ofs->cr1, stm32_port->cr1_irq); + if (stm32_port->cr3_irq) + stm32_usart_set_bits(port, ofs->cr3, stm32_port->cr3_irq); +@@ -1036,7 +1036,7 @@ static void stm32_usart_unthrottle(struct uart_port *port) + if (stm32_port->rx_ch) + stm32_usart_rx_dma_start_or_resume(port); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* Receive stop */ +@@ -1165,7 +1165,7 @@ static void stm32_usart_set_termios(struct uart_port *port, + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 8); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, + isr, +@@ -1356,7 +1356,7 @@ static void stm32_usart_set_termios(struct uart_port *port, + writel_relaxed(cr1, port->membase + ofs->cr1); + + stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + /* Handle modem control interrupts */ + if (UART_ENABLE_MS(port, termios->c_cflag)) +@@ -1406,9 +1406,9 @@ static void stm32_usart_pm(struct uart_port *port, unsigned int state, + pm_runtime_get_sync(port->dev); + break; + case UART_PM_STATE_OFF: +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + pm_runtime_put_sync(port->dev); + break; + } +@@ -1891,9 +1891,9 @@ static void stm32_usart_console_write(struct console *co, const char *s, + int locked = 1; + + if (oops_in_progress) +- locked = spin_trylock_irqsave(&port->lock, flags); ++ locked = uart_port_trylock_irqsave(port, &flags); + else +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Save and disable interrupts, enable the transmitter */ + old_cr1 = readl_relaxed(port->membase + ofs->cr1); +@@ -1907,7 +1907,7 @@ static void stm32_usart_console_write(struct console *co, const char *s, + writel_relaxed(old_cr1, port->membase + ofs->cr1); + + if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int stm32_usart_console_setup(struct console *co, char *options) +@@ -2042,7 +2042,7 @@ static int __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port, + * low-power mode. + */ + if (stm32_port->rx_ch) { +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + /* Poll data from DMA RX buffer if any */ + if (!stm32_usart_rx_dma_pause(stm32_port)) + size += stm32_usart_receive_chars(port, true); +diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c +index c671d674bce4..5bfc0040f17b 100644 +--- a/drivers/tty/serial/sunhv.c ++++ b/drivers/tty/serial/sunhv.c +@@ -217,10 +217,10 @@ static irqreturn_t sunhv_interrupt(int irq, void *dev_id) + struct tty_port *tport; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + tport = receive_chars(port); + transmit_chars(port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (tport) + tty_flip_buffer_push(tport); +@@ -271,7 +271,7 @@ static void sunhv_send_xchar(struct uart_port *port, char ch) + if (ch == __DISABLED_CHAR) + return; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + while (limit-- > 0) { + long status = sun4v_con_putchar(ch); +@@ -280,7 +280,7 @@ static void sunhv_send_xchar(struct uart_port *port, char ch) + udelay(1); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* port->lock held by caller. */ +@@ -295,7 +295,7 @@ static void sunhv_break_ctl(struct uart_port *port, int break_state) + unsigned long flags; + int limit = 10000; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + while (limit-- > 0) { + long status = sun4v_con_putchar(CON_BREAK); +@@ -304,7 +304,7 @@ static void sunhv_break_ctl(struct uart_port *port, int break_state) + udelay(1); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + } + +@@ -328,7 +328,7 @@ static void sunhv_set_termios(struct uart_port *port, struct ktermios *termios, + unsigned int iflag, cflag; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + iflag = termios->c_iflag; + cflag = termios->c_cflag; +@@ -343,7 +343,7 @@ static void sunhv_set_termios(struct uart_port *port, struct ktermios *termios, + uart_update_timeout(port, cflag, + (port->uartclk / (16 * quot))); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *sunhv_type(struct uart_port *port) +@@ -437,9 +437,9 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign + int locked = 1; + + if (port->sysrq || oops_in_progress) +- locked = spin_trylock_irqsave(&port->lock, flags); ++ locked = uart_port_trylock_irqsave(port, &flags); + else +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + while (n > 0) { + unsigned long ra = __pa(con_write_page); +@@ -470,7 +470,7 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign + } + + if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static inline void sunhv_console_putchar(struct uart_port *port, char c) +@@ -492,9 +492,9 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig + int i, locked = 1; + + if (port->sysrq || oops_in_progress) +- locked = spin_trylock_irqsave(&port->lock, flags); ++ locked = uart_port_trylock_irqsave(port, &flags); + else +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + for (i = 0; i < n; i++) { + if (*s == '\n') +@@ -503,7 +503,7 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig + } + + if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static struct console sunhv_console = { +diff --git a/drivers/tty/serial/sunplus-uart.c b/drivers/tty/serial/sunplus-uart.c +index 3aacd5eb414c..4251f4e1ba99 100644 +--- a/drivers/tty/serial/sunplus-uart.c ++++ b/drivers/tty/serial/sunplus-uart.c +@@ -184,7 +184,7 @@ static void sunplus_break_ctl(struct uart_port *port, int ctl) + unsigned long flags; + unsigned int lcr; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + lcr = readl(port->membase + SUP_UART_LCR); + +@@ -195,7 +195,7 @@ static void sunplus_break_ctl(struct uart_port *port, int ctl) + + writel(lcr, port->membase + SUP_UART_LCR); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void transmit_chars(struct uart_port *port) +@@ -277,7 +277,7 @@ static irqreturn_t sunplus_uart_irq(int irq, void *args) + struct uart_port *port = args; + unsigned int isc; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + isc = readl(port->membase + SUP_UART_ISC); + +@@ -287,7 +287,7 @@ static irqreturn_t sunplus_uart_irq(int irq, void *args) + if (isc & SUP_UART_ISC_TX) + transmit_chars(port); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -302,14 +302,14 @@ static int sunplus_startup(struct uart_port *port) + if (ret) + return ret; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + /* isc define Bit[7:4] int setting, Bit[3:0] int status + * isc register will clean Bit[3:0] int status after read + * only do a write to Bit[7:4] int setting + */ + isc |= SUP_UART_ISC_RXM; + writel(isc, port->membase + SUP_UART_ISC); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return 0; + } +@@ -318,13 +318,13 @@ static void sunplus_shutdown(struct uart_port *port) + { + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + /* isc define Bit[7:4] int setting, Bit[3:0] int status + * isc register will clean Bit[3:0] int status after read + * only do a write to Bit[7:4] int setting + */ + writel(0, port->membase + SUP_UART_ISC); /* disable all interrupt */ +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + free_irq(port->irq, port); + } +@@ -372,7 +372,7 @@ static void sunplus_set_termios(struct uart_port *port, + lcr |= UART_LCR_EPAR; + } + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + uart_update_timeout(port, termios->c_cflag, baud); + +@@ -407,7 +407,7 @@ static void sunplus_set_termios(struct uart_port *port, + writel(div_l, port->membase + SUP_UART_DIV_L); + writel(lcr, port->membase + SUP_UART_LCR); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void sunplus_set_ldisc(struct uart_port *port, struct ktermios *termios) +@@ -517,15 +517,15 @@ static void sunplus_console_write(struct console *co, + if (sunplus_console_ports[co->index]->port.sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock(&sunplus_console_ports[co->index]->port.lock); ++ locked = uart_port_trylock(&sunplus_console_ports[co->index]->port); + else +- spin_lock(&sunplus_console_ports[co->index]->port.lock); ++ uart_port_lock(&sunplus_console_ports[co->index]->port); + + uart_console_write(&sunplus_console_ports[co->index]->port, s, count, + sunplus_uart_console_putchar); + + if (locked) +- spin_unlock(&sunplus_console_ports[co->index]->port.lock); ++ uart_port_unlock(&sunplus_console_ports[co->index]->port); + + local_irq_restore(flags); + } +diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c +index 40eeaf835bba..6aa51a6f8063 100644 +--- a/drivers/tty/serial/sunsab.c ++++ b/drivers/tty/serial/sunsab.c +@@ -310,7 +310,7 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id) + unsigned long flags; + unsigned char gis; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + status.stat = 0; + gis = readb(&up->regs->r.gis) >> up->gis_shift; +@@ -331,7 +331,7 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id) + transmit_chars(up, &status); + } + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + if (port) + tty_flip_buffer_push(port); +@@ -473,12 +473,12 @@ static void sunsab_send_xchar(struct uart_port *port, char ch) + if (ch == __DISABLED_CHAR) + return; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + sunsab_tec_wait(up); + writeb(ch, &up->regs->w.tic); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + /* port->lock held by caller. */ +@@ -499,7 +499,7 @@ static void sunsab_break_ctl(struct uart_port *port, int break_state) + unsigned long flags; + unsigned char val; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + val = up->cached_dafo; + if (break_state) +@@ -512,7 +512,7 @@ static void sunsab_break_ctl(struct uart_port *port, int break_state) + if (test_bit(SAB82532_XPR, &up->irqflags)) + sunsab_tx_idle(up); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + /* port->lock is not held. */ +@@ -527,7 +527,7 @@ static int sunsab_startup(struct uart_port *port) + if (err) + return err; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* + * Wait for any commands or immediate characters +@@ -582,7 +582,7 @@ static int sunsab_startup(struct uart_port *port) + set_bit(SAB82532_ALLS, &up->irqflags); + set_bit(SAB82532_XPR, &up->irqflags); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + return 0; + } +@@ -594,7 +594,7 @@ static void sunsab_shutdown(struct uart_port *port) + container_of(port, struct uart_sunsab_port, port); + unsigned long flags; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* Disable Interrupts */ + up->interrupt_mask0 = 0xff; +@@ -628,7 +628,7 @@ static void sunsab_shutdown(struct uart_port *port) + writeb(tmp, &up->regs->rw.ccr0); + #endif + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + free_irq(up->port.irq, up); + } + +@@ -779,9 +779,9 @@ static void sunsab_set_termios(struct uart_port *port, struct ktermios *termios, + unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + unsigned int quot = uart_get_divisor(port, baud); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + sunsab_convert_to_sab(up, termios->c_cflag, termios->c_iflag, baud, quot); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static const char *sunsab_type(struct uart_port *port) +@@ -857,15 +857,15 @@ static void sunsab_console_write(struct console *con, const char *s, unsigned n) + int locked = 1; + + if (up->port.sysrq || oops_in_progress) +- locked = spin_trylock_irqsave(&up->port.lock, flags); ++ locked = uart_port_trylock_irqsave(&up->port, &flags); + else +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + uart_console_write(&up->port, s, n, sunsab_console_putchar); + sunsab_tec_wait(up); + + if (locked) +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static int sunsab_console_setup(struct console *con, char *options) +@@ -914,7 +914,7 @@ static int sunsab_console_setup(struct console *con, char *options) + */ + sunsab_startup(&up->port); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* + * Finally, enable interrupts +@@ -932,7 +932,7 @@ static int sunsab_console_setup(struct console *con, char *options) + sunsab_convert_to_sab(up, con->cflag, 0, baud, quot); + sunsab_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + return 0; + } +diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c +index 58a4342ad0f9..1e051cc2591c 100644 +--- a/drivers/tty/serial/sunsu.c ++++ b/drivers/tty/serial/sunsu.c +@@ -212,9 +212,9 @@ static void enable_rsa(struct uart_sunsu_port *up) + { + if (up->port.type == PORT_RSA) { + if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { +- spin_lock_irq(&up->port.lock); ++ uart_port_lock_irq(&up->port); + __enable_rsa(up); +- spin_unlock_irq(&up->port.lock); ++ uart_port_unlock_irq(&up->port); + } + if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) + serial_outp(up, UART_RSA_FRR, 0); +@@ -234,7 +234,7 @@ static void disable_rsa(struct uart_sunsu_port *up) + + if (up->port.type == PORT_RSA && + up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { +- spin_lock_irq(&up->port.lock); ++ uart_port_lock_irq(&up->port); + + mode = serial_inp(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); +@@ -247,7 +247,7 @@ static void disable_rsa(struct uart_sunsu_port *up) + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; +- spin_unlock_irq(&up->port.lock); ++ uart_port_unlock_irq(&up->port); + } + } + #endif /* CONFIG_SERIAL_8250_RSA */ +@@ -311,10 +311,10 @@ static void sunsu_enable_ms(struct uart_port *port) + container_of(port, struct uart_sunsu_port, port); + unsigned long flags; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static void +@@ -456,7 +456,7 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) + unsigned long flags; + unsigned char status; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + do { + status = serial_inp(up, UART_LSR); +@@ -470,7 +470,7 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) + + } while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + return IRQ_HANDLED; + } +@@ -545,9 +545,9 @@ static unsigned int sunsu_tx_empty(struct uart_port *port) + unsigned long flags; + unsigned int ret; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + return ret; + } +@@ -599,13 +599,13 @@ static void sunsu_break_ctl(struct uart_port *port, int break_state) + container_of(port, struct uart_sunsu_port, port); + unsigned long flags; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static int sunsu_startup(struct uart_port *port) +@@ -683,12 +683,12 @@ static int sunsu_startup(struct uart_port *port) + */ + serial_outp(up, UART_LCR, UART_LCR_WLEN8); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + up->port.mctrl |= TIOCM_OUT2; + + sunsu_set_mctrl(&up->port, up->port.mctrl); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + /* + * Finally, enable interrupts. Note: Modem status interrupts +@@ -731,7 +731,7 @@ static void sunsu_shutdown(struct uart_port *port) + up->ier = 0; + serial_outp(up, UART_IER, 0); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + if (up->port.flags & UPF_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + inb((up->port.iobase & 0xfe0) | 0x1f); +@@ -740,7 +740,7 @@ static void sunsu_shutdown(struct uart_port *port) + up->port.mctrl &= ~TIOCM_OUT2; + + sunsu_set_mctrl(&up->port, up->port.mctrl); +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + /* + * Disable break condition and FIFOs +@@ -826,7 +826,7 @@ sunsu_change_speed(struct uart_port *port, unsigned int cflag, + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* + * Update the per-port timeout. +@@ -891,7 +891,7 @@ sunsu_change_speed(struct uart_port *port, unsigned int cflag, + + up->cflag = cflag; + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static void +@@ -1038,7 +1038,7 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) + up->type_probed = PORT_UNKNOWN; + up->port.iotype = UPIO_MEM; + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + if (!(up->port.flags & UPF_BUGGY_UART)) { + /* +@@ -1173,7 +1173,7 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) + serial_outp(up, UART_IER, 0); + + out: +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static struct uart_driver sunsu_reg = { +@@ -1298,9 +1298,9 @@ static void sunsu_console_write(struct console *co, const char *s, + int locked = 1; + + if (up->port.sysrq || oops_in_progress) +- locked = spin_trylock_irqsave(&up->port.lock, flags); ++ locked = uart_port_trylock_irqsave(&up->port, &flags); + else +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + /* + * First save the UER then disable the interrupts +@@ -1318,7 +1318,7 @@ static void sunsu_console_write(struct console *co, const char *s, + serial_out(up, UART_IER, ier); + + if (locked) +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + /* +diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c +index c8c71c56264c..d3b5e864b727 100644 +--- a/drivers/tty/serial/sunzilog.c ++++ b/drivers/tty/serial/sunzilog.c +@@ -531,7 +531,7 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) + struct tty_port *port; + unsigned char r3; + +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + r3 = read_zsreg(channel, R3); + + /* Channel A */ +@@ -548,7 +548,7 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) + if (r3 & CHATxIP) + sunzilog_transmit_chars(up, channel); + } +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + + if (port) + tty_flip_buffer_push(port); +@@ -557,7 +557,7 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) + up = up->next; + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + +- spin_lock(&up->port.lock); ++ uart_port_lock(&up->port); + port = NULL; + if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { + writeb(RES_H_IUS, &channel->control); +@@ -571,7 +571,7 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) + if (r3 & CHBTxIP) + sunzilog_transmit_chars(up, channel); + } +- spin_unlock(&up->port.lock); ++ uart_port_unlock(&up->port); + + if (port) + tty_flip_buffer_push(port); +@@ -604,11 +604,11 @@ static unsigned int sunzilog_tx_empty(struct uart_port *port) + unsigned char status; + unsigned int ret; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + status = sunzilog_read_channel_status(port); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (status & Tx_BUF_EMP) + ret = TIOCSER_TEMT; +@@ -764,7 +764,7 @@ static void sunzilog_break_ctl(struct uart_port *port, int break_state) + else + clear_bits |= SND_BRK; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + new_reg = (up->curregs[R5] | set_bits) & ~clear_bits; + if (new_reg != up->curregs[R5]) { +@@ -774,7 +774,7 @@ static void sunzilog_break_ctl(struct uart_port *port, int break_state) + write_zsreg(channel, R5, up->curregs[R5]); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static void __sunzilog_startup(struct uart_sunzilog_port *up) +@@ -800,9 +800,9 @@ static int sunzilog_startup(struct uart_port *port) + if (ZS_IS_CONS(up)) + return 0; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + __sunzilog_startup(up); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + return 0; + } + +@@ -840,7 +840,7 @@ static void sunzilog_shutdown(struct uart_port *port) + if (ZS_IS_CONS(up)) + return; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + channel = ZILOG_CHANNEL_FROM_PORT(port); + +@@ -853,7 +853,7 @@ static void sunzilog_shutdown(struct uart_port *port) + up->curregs[R5] &= ~SND_BRK; + sunzilog_maybe_update_regs(up, channel); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* Shared by TTY driver and serial console setup. The port lock is held +@@ -945,7 +945,7 @@ sunzilog_set_termios(struct uart_port *port, struct ktermios *termios, + + baud = uart_get_baud_rate(port, termios, old, 1200, 76800); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); + +@@ -962,7 +962,7 @@ sunzilog_set_termios(struct uart_port *port, struct ktermios *termios, + + uart_update_timeout(port, termios->c_cflag, baud); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static const char *sunzilog_type(struct uart_port *port) +@@ -1201,15 +1201,15 @@ sunzilog_console_write(struct console *con, const char *s, unsigned int count) + int locked = 1; + + if (up->port.sysrq || oops_in_progress) +- locked = spin_trylock_irqsave(&up->port.lock, flags); ++ locked = uart_port_trylock_irqsave(&up->port, &flags); + else +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + uart_console_write(&up->port, s, count, sunzilog_putchar); + udelay(2); + + if (locked) +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + } + + static int __init sunzilog_console_setup(struct console *con, char *options) +@@ -1244,7 +1244,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options) + + brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + + up->curregs[R15] |= BRKIE; + sunzilog_convert_to_zs(up, con->cflag, 0, brg); +@@ -1252,7 +1252,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options) + sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); + __sunzilog_startup(up); + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + return 0; + } +@@ -1333,7 +1333,7 @@ static void sunzilog_init_hw(struct uart_sunzilog_port *up) + + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + +- spin_lock_irqsave(&up->port.lock, flags); ++ uart_port_lock_irqsave(&up->port, &flags); + if (ZS_IS_CHANNEL_A(up)) { + write_zsreg(channel, R9, FHWRES); + ZSDELAY_LONG(); +@@ -1383,7 +1383,7 @@ static void sunzilog_init_hw(struct uart_sunzilog_port *up) + write_zsreg(channel, R9, up->curregs[R9]); + } + +- spin_unlock_irqrestore(&up->port.lock, flags); ++ uart_port_unlock_irqrestore(&up->port, flags); + + #ifdef CONFIG_SERIO + if (up->flags & (SUNZILOG_FLAG_CONS_KEYB | +diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c +index 0859394a78cd..0cc6524f5e8b 100644 +--- a/drivers/tty/serial/timbuart.c ++++ b/drivers/tty/serial/timbuart.c +@@ -174,7 +174,7 @@ static void timbuart_tasklet(struct tasklet_struct *t) + struct timbuart_port *uart = from_tasklet(uart, t, tasklet); + u32 isr, ier = 0; + +- spin_lock(&uart->port.lock); ++ uart_port_lock(&uart->port); + + isr = ioread32(uart->port.membase + TIMBUART_ISR); + dev_dbg(uart->port.dev, "%s ISR: %x\n", __func__, isr); +@@ -189,7 +189,7 @@ static void timbuart_tasklet(struct tasklet_struct *t) + + iowrite32(ier, uart->port.membase + TIMBUART_IER); + +- spin_unlock(&uart->port.lock); ++ uart_port_unlock(&uart->port); + dev_dbg(uart->port.dev, "%s leaving\n", __func__); + } + +@@ -295,10 +295,10 @@ static void timbuart_set_termios(struct uart_port *port, + tty_termios_copy_hw(termios, old); + tty_termios_encode_baud_rate(termios, baud, baud); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + iowrite8((u8)bindex, port->membase + TIMBUART_BAUDRATE); + uart_update_timeout(port, termios->c_cflag, baud); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *timbuart_type(struct uart_port *port) +diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c +index b225a78f6175..404c14acafa5 100644 +--- a/drivers/tty/serial/uartlite.c ++++ b/drivers/tty/serial/uartlite.c +@@ -216,11 +216,11 @@ static irqreturn_t ulite_isr(int irq, void *dev_id) + unsigned long flags; + + do { +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + stat = uart_in32(ULITE_STATUS, port); + busy = ulite_receive(port, stat); + busy |= ulite_transmit(port, stat); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + n++; + } while (busy); + +@@ -238,9 +238,9 @@ static unsigned int ulite_tx_empty(struct uart_port *port) + unsigned long flags; + unsigned int ret; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + ret = uart_in32(ULITE_STATUS, port); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0; + } +@@ -323,7 +323,7 @@ static void ulite_set_termios(struct uart_port *port, + termios->c_cflag |= pdata->cflags & (PARENB | PARODD | CSIZE); + tty_termios_encode_baud_rate(termios, pdata->baud, pdata->baud); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + port->read_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN + | ULITE_STATUS_TXFULL; +@@ -346,7 +346,7 @@ static void ulite_set_termios(struct uart_port *port, + /* update timeout */ + uart_update_timeout(port, termios->c_cflag, pdata->baud); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *ulite_type(struct uart_port *port) +@@ -495,9 +495,9 @@ static void ulite_console_write(struct console *co, const char *s, + int locked = 1; + + if (oops_in_progress) { +- locked = spin_trylock_irqsave(&port->lock, flags); ++ locked = uart_port_trylock_irqsave(port, &flags); + } else +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* save and disable interrupt */ + ier = uart_in32(ULITE_STATUS, port) & ULITE_STATUS_IE; +@@ -512,7 +512,7 @@ static void ulite_console_write(struct console *co, const char *s, + uart_out32(ULITE_CONTROL_IE, ULITE_CONTROL, port); + + if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static int ulite_console_setup(struct console *co, char *options) +diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c +index b06661b80f41..ed7a6bb5596a 100644 +--- a/drivers/tty/serial/ucc_uart.c ++++ b/drivers/tty/serial/ucc_uart.c +@@ -931,7 +931,7 @@ static void qe_uart_set_termios(struct uart_port *port, + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + + /* Do we really need a spinlock here? */ +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, baud); +@@ -949,7 +949,7 @@ static void qe_uart_set_termios(struct uart_port *port, + qe_setbrg(qe_port->us_info.tx_clock, baud, 16); + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* +diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c +index c5d5c2765119..78a1c1eea11b 100644 +--- a/drivers/tty/serial/vt8500_serial.c ++++ b/drivers/tty/serial/vt8500_serial.c +@@ -227,7 +227,7 @@ static irqreturn_t vt8500_irq(int irq, void *dev_id) + struct uart_port *port = dev_id; + unsigned long isr; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + isr = vt8500_read(port, VT8500_URISR); + + /* Acknowledge active status bits */ +@@ -240,7 +240,7 @@ static irqreturn_t vt8500_irq(int irq, void *dev_id) + if (isr & TCTS) + handle_delta_cts(port); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + return IRQ_HANDLED; + } +@@ -342,7 +342,7 @@ static void vt8500_set_termios(struct uart_port *port, + unsigned int baud, lcr; + unsigned int loops = 1000; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* calculate and set baud rate */ + baud = uart_get_baud_rate(port, termios, old, 900, 921600); +@@ -410,7 +410,7 @@ static void vt8500_set_termios(struct uart_port *port, + vt8500_write(&vt8500_port->uart, 0x881, VT8500_URFCR); + vt8500_write(&vt8500_port->uart, vt8500_port->ier, VT8500_URIER); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + static const char *vt8500_type(struct uart_port *port) +diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c +index 2e5e86a00a77..9c13dac1d4d1 100644 +--- a/drivers/tty/serial/xilinx_uartps.c ++++ b/drivers/tty/serial/xilinx_uartps.c +@@ -346,7 +346,7 @@ static irqreturn_t cdns_uart_isr(int irq, void *dev_id) + struct uart_port *port = (struct uart_port *)dev_id; + unsigned int isrstatus; + +- spin_lock(&port->lock); ++ uart_port_lock(port); + + /* Read the interrupt status register to determine which + * interrupt(s) is/are active and clear them. +@@ -369,7 +369,7 @@ static irqreturn_t cdns_uart_isr(int irq, void *dev_id) + !(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_RX_DIS)) + cdns_uart_handle_rx(dev_id, isrstatus); + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + return IRQ_HANDLED; + } + +@@ -506,14 +506,14 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, + return NOTIFY_BAD; + } + +- spin_lock_irqsave(&cdns_uart->port->lock, flags); ++ uart_port_lock_irqsave(cdns_uart->port, &flags); + + /* Disable the TX and RX to set baud rate */ + ctrl_reg = readl(port->membase + CDNS_UART_CR); + ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; + writel(ctrl_reg, port->membase + CDNS_UART_CR); + +- spin_unlock_irqrestore(&cdns_uart->port->lock, flags); ++ uart_port_unlock_irqrestore(cdns_uart->port, flags); + + return NOTIFY_OK; + } +@@ -523,7 +523,7 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, + * frequency. + */ + +- spin_lock_irqsave(&cdns_uart->port->lock, flags); ++ uart_port_lock_irqsave(cdns_uart->port, &flags); + + locked = 1; + port->uartclk = ndata->new_rate; +@@ -533,7 +533,7 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, + fallthrough; + case ABORT_RATE_CHANGE: + if (!locked) +- spin_lock_irqsave(&cdns_uart->port->lock, flags); ++ uart_port_lock_irqsave(cdns_uart->port, &flags); + + /* Set TX/RX Reset */ + ctrl_reg = readl(port->membase + CDNS_UART_CR); +@@ -555,7 +555,7 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, + ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; + writel(ctrl_reg, port->membase + CDNS_UART_CR); + +- spin_unlock_irqrestore(&cdns_uart->port->lock, flags); ++ uart_port_unlock_irqrestore(cdns_uart->port, flags); + + return NOTIFY_OK; + default: +@@ -652,7 +652,7 @@ static void cdns_uart_break_ctl(struct uart_port *port, int ctl) + unsigned int status; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + status = readl(port->membase + CDNS_UART_CR); + +@@ -664,7 +664,7 @@ static void cdns_uart_break_ctl(struct uart_port *port, int ctl) + writel(CDNS_UART_CR_STOPBRK | status, + port->membase + CDNS_UART_CR); + } +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /** +@@ -683,7 +683,7 @@ static void cdns_uart_set_termios(struct uart_port *port, + unsigned long flags; + unsigned int ctrl_reg, mode_reg; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Disable the TX and RX to set baud rate */ + ctrl_reg = readl(port->membase + CDNS_UART_CR); +@@ -794,7 +794,7 @@ static void cdns_uart_set_termios(struct uart_port *port, + cval &= ~CDNS_UART_MODEMCR_FCM; + writel(cval, port->membase + CDNS_UART_MODEMCR); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /** +@@ -813,7 +813,7 @@ static int cdns_uart_startup(struct uart_port *port) + + is_brk_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Disable the TX and RX */ + writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, +@@ -861,7 +861,7 @@ static int cdns_uart_startup(struct uart_port *port) + writel(readl(port->membase + CDNS_UART_ISR), + port->membase + CDNS_UART_ISR); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + ret = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, port); + if (ret) { +@@ -889,7 +889,7 @@ static void cdns_uart_shutdown(struct uart_port *port) + int status; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Disable interrupts */ + status = readl(port->membase + CDNS_UART_IMR); +@@ -900,7 +900,7 @@ static void cdns_uart_shutdown(struct uart_port *port) + writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, + port->membase + CDNS_UART_CR); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + free_irq(port->irq, port); + } +@@ -1050,7 +1050,7 @@ static int cdns_uart_poll_get_char(struct uart_port *port) + int c; + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Check if FIFO is empty */ + if (readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY) +@@ -1058,7 +1058,7 @@ static int cdns_uart_poll_get_char(struct uart_port *port) + else /* Read a character */ + c = (unsigned char) readl(port->membase + CDNS_UART_FIFO); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + return c; + } +@@ -1067,7 +1067,7 @@ static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) + { + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Wait until FIFO is empty */ + while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) +@@ -1080,7 +1080,7 @@ static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) + while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) + cpu_relax(); + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + #endif + +@@ -1232,9 +1232,9 @@ static void cdns_uart_console_write(struct console *co, const char *s, + if (port->sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock_irqsave(&port->lock, flags); ++ locked = uart_port_trylock_irqsave(port, &flags); + else +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* save and disable interrupt */ + imr = readl(port->membase + CDNS_UART_IMR); +@@ -1257,7 +1257,7 @@ static void cdns_uart_console_write(struct console *co, const char *s, + writel(imr, port->membase + CDNS_UART_IER); + + if (locked) +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /** +@@ -1325,7 +1325,7 @@ static int cdns_uart_suspend(struct device *device) + if (console_suspend_enabled && uart_console(port) && may_wake) { + unsigned long flags; + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + /* Empty the receive FIFO 1st before making changes */ + while (!(readl(port->membase + CDNS_UART_SR) & + CDNS_UART_SR_RXEMPTY)) +@@ -1334,7 +1334,7 @@ static int cdns_uart_suspend(struct device *device) + writel(1, port->membase + CDNS_UART_RXWM); + /* disable RX timeout interrups */ + writel(CDNS_UART_IXR_TOUT, port->membase + CDNS_UART_IDR); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + /* +@@ -1372,7 +1372,7 @@ static int cdns_uart_resume(struct device *device) + return ret; + } + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + /* Set TX/RX Reset */ + ctrl_reg = readl(port->membase + CDNS_UART_CR); +@@ -1392,14 +1392,14 @@ static int cdns_uart_resume(struct device *device) + + clk_disable(cdns_uart->uartclk); + clk_disable(cdns_uart->pclk); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } else { +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + /* restore original rx trigger level */ + writel(rx_trigger_level, port->membase + CDNS_UART_RXWM); + /* enable RX timeout interrupt */ + writel(CDNS_UART_IXR_TOUT, port->membase + CDNS_UART_IER); +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + + return uart_resume_port(cdns_uart->cdns_uart_driver, port); +diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c +index 493fc4742895..afa52883c498 100644 +--- a/drivers/tty/tty_io.c ++++ b/drivers/tty/tty_io.c +@@ -3543,8 +3543,15 @@ static ssize_t show_cons_active(struct device *dev, + for_each_console(c) { + if (!c->device) + continue; +- if (!c->write) +- continue; ++ if (c->flags & CON_NBCON) { ++ if (!c->write_atomic && ++ !(c->write_thread && c->kthread)) { ++ continue; ++ } ++ } else { ++ if (!c->write) ++ continue; ++ } + if ((c->flags & CON_ENABLED) == 0) + continue; + cs[i++] = c; +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 59892a31cf76..f6b983303e31 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,8 +61,10 @@ + #include + #include + #include ++#include + #include + #include ++#include + #include + #include + #include +@@ -70,7 +73,7 @@ + #include + #include + #include +- ++#include + + + /* The alignment to use between consumer and producer parts of vring. +@@ -82,6 +85,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; +@@ -92,6 +102,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 { +@@ -102,6 +122,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 */ +@@ -111,11 +197,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; +@@ -135,13 +221,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; + } +@@ -192,39 +278,34 @@ 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; + } + + 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(); +@@ -260,7 +341,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) +@@ -268,7 +349,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); + } + + +@@ -282,7 +363,7 @@ 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; + } + +@@ -296,6 +377,36 @@ static bool vm_notify_with_data(struct virtqueue *vq) + 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) + { +@@ -307,7 +418,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); +@@ -338,11 +449,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)); + } + +@@ -359,7 +470,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) +@@ -390,7 +504,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 ? +@@ -423,7 +537,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; + +@@ -440,27 +554,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; +@@ -476,9 +590,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); +@@ -487,6 +601,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[], +@@ -495,16 +631,23 @@ 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, ++ err = request_irq(irq, vm_interrupt, IRQF_SHARED, + dev_name(&vdev->dev), vm_dev); +- if (err) +- return err; ++ if (err) ++ return err; ++ } + + if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source")) + enable_irq_wake(irq); +@@ -540,7 +683,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); +@@ -611,6 +754,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) +@@ -618,11 +782,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; +@@ -630,7 +802,7 @@ 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); ++ 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 free_vm_dev; +@@ -641,7 +813,7 @@ static int virtio_mmio_probe(struct platform_device *pdev) + if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) { + dev_warn(&pdev->dev, "Wrong magic value 0x%08lx!\n", magic); + rc = -ENODEV; +- goto free_vm_dev; ++ goto err; + } + + /* Check device version */ +@@ -650,7 +822,7 @@ static int virtio_mmio_probe(struct platform_device *pdev) + dev_err(&pdev->dev, "Version %ld not supported!\n", + vm_dev->version); + rc = -ENXIO; +- goto free_vm_dev; ++ goto err; + } + + vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID); +@@ -660,12 +832,24 @@ static int virtio_mmio_probe(struct platform_device *pdev) + * with no function. End probing now with no error reported. + */ + rc = -ENODEV; +- goto free_vm_dev; ++ 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)); + /* +@@ -691,14 +875,22 @@ static int virtio_mmio_probe(struct platform_device *pdev) + + return rc; + ++err: ++ if (vm_dev->is_hypervisor_less) ++ of_reserved_mem_device_release(&pdev->dev); + free_vm_dev: + kfree(vm_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 80669e05bf0e..99ecfae18d9c 100644 +--- a/drivers/virtio/virtio_ring.c ++++ b/drivers/virtio/virtio_ring.c +@@ -599,8 +599,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..13c003e2ba45 +--- /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 2 ++ ++#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"); +diff --git a/fs/proc/consoles.c b/fs/proc/consoles.c +index e0758fe7936d..2703676549f5 100644 +--- a/fs/proc/consoles.c ++++ b/fs/proc/consoles.c +@@ -21,12 +21,14 @@ static int show_console_dev(struct seq_file *m, void *v) + { CON_ENABLED, 'E' }, + { CON_CONSDEV, 'C' }, + { CON_BOOT, 'B' }, ++ { CON_NBCON, 'N' }, + { CON_PRINTBUFFER, 'p' }, + { CON_BRL, 'b' }, + { CON_ANYTIME, 'a' }, + }; + char flags[ARRAY_SIZE(con_flags) + 1]; + struct console *con = v; ++ char con_write = '-'; + unsigned int a; + dev_t dev = 0; + +@@ -57,9 +59,15 @@ static int show_console_dev(struct seq_file *m, void *v) + seq_setwidth(m, 21 - 1); + seq_printf(m, "%s%d", con->name, con->index); + seq_pad(m, ' '); +- seq_printf(m, "%c%c%c (%s)", con->read ? 'R' : '-', +- con->write ? 'W' : '-', con->unblank ? 'U' : '-', +- flags); ++ if (con->flags & CON_NBCON) { ++ if (con->write_atomic || con->write_thread) ++ con_write = 'W'; ++ } else { ++ if (con->write) ++ con_write = 'W'; ++ } ++ seq_printf(m, "%c%c%c (%s)", con->read ? 'R' : '-', con_write, ++ con->unblank ? 'U' : '-', flags); + if (dev) + seq_printf(m, " %4d:%d", MAJOR(dev), MINOR(dev)); + +diff --git a/include/dt-bindings/clock/imx8-clock.h b/include/dt-bindings/clock/imx8-clock.h +index 940b408fb5ca..ef9bc8c8a80f 100644 +--- a/include/dt-bindings/clock/imx8-clock.h ++++ b/include/dt-bindings/clock/imx8-clock.h +@@ -37,4 +37,14 @@ + + #define IMX_ADMA_ACM_CLK_END 25 + ++/* 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/bottom_half.h b/include/linux/bottom_half.h +index fc53e0ad56d9..448bbef47456 100644 +--- a/include/linux/bottom_half.h ++++ b/include/linux/bottom_half.h +@@ -35,8 +35,10 @@ static inline void local_bh_enable(void) + + #ifdef CONFIG_PREEMPT_RT + extern bool local_bh_blocked(void); ++extern void softirq_preempt(void); + #else + static inline bool local_bh_blocked(void) { return false; } ++static inline void softirq_preempt(void) { } + #endif + + #endif /* _LINUX_BH_H */ +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 7de11c763eb3..1eb9580e9b18 100644 +--- a/include/linux/console.h ++++ b/include/linux/console.h +@@ -16,7 +16,9 @@ + + #include + #include ++#include + #include ++#include + #include + + struct vc_data; +@@ -156,6 +158,8 @@ static inline int con_debug_leave(void) + * /dev/kmesg which requires a larger output buffer. + * @CON_SUSPENDED: Indicates if a console is suspended. If true, the + * printing callbacks must not be called. ++ * @CON_NBCON: Console can operate outside of the legacy style console_lock ++ * constraints. + */ + enum cons_flags { + CON_PRINTBUFFER = BIT(0), +@@ -166,6 +170,111 @@ enum cons_flags { + CON_BRL = BIT(5), + CON_EXTENDED = BIT(6), + CON_SUSPENDED = BIT(7), ++ CON_NBCON = BIT(8), ++}; ++ ++/** ++ * struct nbcon_state - console state for nbcon consoles ++ * @atom: Compound of the state fields for atomic operations ++ * ++ * @req_prio: The priority of a handover request ++ * @prio: The priority of the current owner ++ * @unsafe: Console is busy in a non takeover region ++ * @unsafe_takeover: A hostile takeover in an unsafe state happened in the ++ * past. The console cannot be safe until re-initialized. ++ * @cpu: The CPU on which the owner runs ++ * ++ * To be used for reading and preparing of the value stored in the nbcon ++ * state variable @console::nbcon_state. ++ * ++ * The @prio and @req_prio fields are particularly important to allow ++ * spin-waiting to timeout and give up without the risk of a waiter being ++ * assigned the lock after giving up. ++ */ ++struct nbcon_state { ++ union { ++ unsigned int atom; ++ struct { ++ unsigned int prio : 2; ++ unsigned int req_prio : 2; ++ unsigned int unsafe : 1; ++ unsigned int unsafe_takeover : 1; ++ unsigned int cpu : 24; ++ }; ++ }; ++}; ++ ++/* ++ * The nbcon_state struct is used to easily create and interpret values that ++ * are stored in the @console::nbcon_state variable. Ensure this struct stays ++ * within the size boundaries of the atomic variable's underlying type in ++ * order to avoid any accidental truncation. ++ */ ++static_assert(sizeof(struct nbcon_state) <= sizeof(int)); ++ ++/** ++ * nbcon_prio - console owner priority for nbcon consoles ++ * @NBCON_PRIO_NONE: Unused ++ * @NBCON_PRIO_NORMAL: Normal (non-emergency) usage ++ * @NBCON_PRIO_EMERGENCY: Emergency output (WARN/OOPS...) ++ * @NBCON_PRIO_PANIC: Panic output ++ * @NBCON_PRIO_MAX: The number of priority levels ++ * ++ * A higher priority context can takeover the console when it is ++ * in the safe state. The final attempt to flush consoles in panic() ++ * can be allowed to do so even in an unsafe state (Hope and pray). ++ */ ++enum nbcon_prio { ++ NBCON_PRIO_NONE = 0, ++ NBCON_PRIO_NORMAL, ++ NBCON_PRIO_EMERGENCY, ++ NBCON_PRIO_PANIC, ++ NBCON_PRIO_MAX, ++}; ++ ++struct console; ++struct printk_buffers; ++ ++/** ++ * struct nbcon_context - Context for console acquire/release ++ * @console: The associated console ++ * @spinwait_max_us: Limit for spin-wait acquire ++ * @prio: Priority of the context ++ * @allow_unsafe_takeover: Allow performing takeover even if unsafe. Can ++ * be used only with NBCON_PRIO_PANIC @prio. It ++ * might cause a system freeze when the console ++ * is used later. ++ * @backlog: Ringbuffer has pending records ++ * @pbufs: Pointer to the text buffer for this context ++ * @seq: The sequence number to print for this context ++ */ ++struct nbcon_context { ++ /* members set by caller */ ++ struct console *console; ++ unsigned int spinwait_max_us; ++ enum nbcon_prio prio; ++ unsigned int allow_unsafe_takeover : 1; ++ ++ /* members set by emit */ ++ unsigned int backlog : 1; ++ ++ /* members set by acquire */ ++ struct printk_buffers *pbufs; ++ u64 seq; ++}; ++ ++/** ++ * struct nbcon_write_context - Context handed to the nbcon write callbacks ++ * @ctxt: The core console context ++ * @outbuf: Pointer to the text buffer for output ++ * @len: Length to write ++ * @unsafe_takeover: If a hostile takeover in an unsafe state has occurred ++ */ ++struct nbcon_write_context { ++ struct nbcon_context __private ctxt; ++ char *outbuf; ++ unsigned int len; ++ bool unsafe_takeover; + }; + + /** +@@ -187,6 +296,17 @@ enum cons_flags { + * @dropped: Number of unreported dropped ringbuffer records + * @data: Driver private data + * @node: hlist node for the console list ++ * ++ * @write_atomic: Write callback for atomic context ++ * @write_thread: Write callback for non-atomic context ++ * @driver_enter: Callback to begin synchronization with driver code ++ * @driver_exit: Callback to finish synchronization with driver code ++ * @nbcon_state: State for nbcon consoles ++ * @nbcon_seq: Sequence number of the next record for nbcon to print ++ * @pbufs: Pointer to nbcon private buffer ++ * @kthread: Printer kthread for this console ++ * @rcuwait: RCU-safe wait object for @kthread waking ++ * @irq_work: Defer @kthread waking to IRQ work context + */ + struct console { + char name[16]; +@@ -206,6 +326,20 @@ struct console { + unsigned long dropped; + void *data; + struct hlist_node node; ++ ++ /* nbcon console specific members */ ++ bool (*write_atomic)(struct console *con, ++ struct nbcon_write_context *wctxt); ++ bool (*write_thread)(struct console *con, ++ struct nbcon_write_context *wctxt); ++ void (*driver_enter)(struct console *con, unsigned long *flags); ++ void (*driver_exit)(struct console *con, unsigned long flags); ++ atomic_t __private nbcon_state; ++ atomic_long_t __private nbcon_seq; ++ struct printk_buffers *pbufs; ++ struct task_struct *kthread; ++ struct rcuwait rcuwait; ++ struct irq_work irq_work; + }; + + #ifdef CONFIG_LOCKDEP +@@ -332,6 +466,22 @@ static inline bool console_is_registered(const struct console *con) + lockdep_assert_console_list_lock_held(); \ + hlist_for_each_entry(con, &console_list, node) + ++#ifdef CONFIG_PRINTK ++extern void nbcon_cpu_emergency_enter(void); ++extern void nbcon_cpu_emergency_exit(void); ++extern bool nbcon_can_proceed(struct nbcon_write_context *wctxt); ++extern bool nbcon_enter_unsafe(struct nbcon_write_context *wctxt); ++extern bool nbcon_exit_unsafe(struct nbcon_write_context *wctxt); ++extern void nbcon_reacquire(struct nbcon_write_context *wctxt); ++#else ++static inline void nbcon_cpu_emergency_enter(void) { } ++static inline void nbcon_cpu_emergency_exit(void) { } ++static inline bool nbcon_can_proceed(struct nbcon_write_context *wctxt) { return false; } ++static inline bool nbcon_enter_unsafe(struct nbcon_write_context *wctxt) { return false; } ++static inline bool nbcon_exit_unsafe(struct nbcon_write_context *wctxt) { return false; } ++static inline void nbcon_reacquire(struct nbcon_write_context *wctxt) { } ++#endif ++ + extern int console_set_on_cmdline; + extern struct console *early_console; + +diff --git a/include/linux/dsa/netc.h b/include/linux/dsa/netc.h +new file mode 100644 +index 000000000000..adafcf70ebf1 +--- /dev/null ++++ b/include/linux/dsa/netc.h +@@ -0,0 +1,69 @@ ++// 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 0xDADC ++#define ETH_P_NETC_8021Q ETH_P_8021Q ++ ++#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 ++ ++/* Source and Destination MAC of follow-up meta frames. ++ * Whereas the choice of SMAC only affects the unique identification of the ++ * switch as sender of meta frames, the DMAC must be an address that is present ++ * in the DSA master port's multicast MAC filter. ++ * 01-80-C2-00-00-0E is a good choice for this, as all profiles of IEEE 1588 ++ * over L2 use this address for some purpose already. ++ */ ++#define NETC_META_SMAC 0x222222222222ull ++#define NETC_META_DMAC 0x0180C200000Eull ++ ++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; ++ u32 ts_id; ++}; ++ ++#define NETC_SKB_CB(skb) \ ++ ((struct netc_skb_cb *)((skb)->cb)) ++ ++struct netc_tagger_data { ++ void (*meta_tstamp_handler)(struct dsa_switch *ds, int port, ++ u32 ts_id, u64 tstamp); ++ void (*meta_cmd_handler)(struct dsa_switch *ds, int port, ++ void *buf, size_t len); ++}; ++ ++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..8b3ab0cc1334 100644 +--- a/include/linux/entry-common.h ++++ b/include/linux/entry-common.h +@@ -60,7 +60,7 @@ + #define EXIT_TO_USER_MODE_WORK \ + (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE | \ + _TIF_NEED_RESCHED | _TIF_PATCH_PENDING | _TIF_NOTIFY_SIGNAL | \ +- ARCH_EXIT_TO_USER_MODE_WORK) ++ _TIF_NEED_RESCHED_LAZY | ARCH_EXIT_TO_USER_MODE_WORK) + + /** + * arch_enter_from_user_mode - Architecture specific sanity check for user mode regs +diff --git a/include/linux/entry-kvm.h b/include/linux/entry-kvm.h +index 6813171afccb..674a622c91be 100644 +--- a/include/linux/entry-kvm.h ++++ b/include/linux/entry-kvm.h +@@ -18,7 +18,7 @@ + + #define XFER_TO_GUEST_MODE_WORK \ + (_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL | \ +- _TIF_NOTIFY_RESUME | ARCH_XFER_TO_GUEST_MODE_WORK) ++ _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED_LAZY | ARCH_XFER_TO_GUEST_MODE_WORK) + + struct kvm_vcpu; + +diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h +index 8d497be66da8..8543e5ffea39 100644 +--- a/include/linux/ethtool.h ++++ b/include/linux/ethtool.h +@@ -752,6 +752,8 @@ struct ethtool_mm_stats { + * @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 +@@ -884,6 +886,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..11f1d8bd6290 100644 +--- a/include/linux/fec.h ++++ b/include/linux/fec.h +@@ -19,4 +19,124 @@ 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 1024 /* Must be power of two */ ++#endif ++ ++#ifdef CONFIG_AVB_SUPPORT ++struct avb_desc { ++ u16 offset; ++ u16 len; ++ u32 ts; ++ u32 flags:28; ++ u32 pool_type:4; ++ 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 fd6b8a255969..ee2afdfa6f8c 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: +@@ -2125,6 +2126,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 +@@ -2132,6 +2136,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 4a1dc88ddbff..a5091ac97fc6 100644 +--- a/include/linux/interrupt.h ++++ b/include/linux/interrupt.h +@@ -609,6 +609,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/net.h b/include/linux/net.h +index c9b4a63791a4..57bcc0e9bf65 100644 +--- a/include/linux/net.h ++++ b/include/linux/net.h +@@ -126,6 +126,8 @@ struct socket { + const struct proto_ops *ops; /* Might change with IPV6_ADDRFORM or MPTCP. */ + + struct socket_wq wq; ++ ++ struct net_device *ndev; + }; + + /* +diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h +index b8e60a20416b..2b45c8fdafb9 100644 +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -346,6 +346,8 @@ struct gro_list { + */ + #define GRO_HASH_BUCKETS 8 + ++ ++#define NAPINAMSIZ 8 + /* + * Structure for NAPI scheduling similar to tasklet but with weighting + */ +@@ -380,6 +382,7 @@ struct napi_struct { + /* control-path-only fields follow */ + struct list_head dev_list; + struct hlist_node napi_hash_node; ++ char name[NAPINAMSIZ]; + }; + + enum { +@@ -1414,6 +1417,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); +@@ -2076,6 +2082,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 +@@ -2599,6 +2607,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 +@@ -2672,6 +2695,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 +@@ -3258,7 +3302,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/printk.h b/include/linux/printk.h +index e4878bb58f66..ebebc32e78de 100644 +--- a/include/linux/printk.h ++++ b/include/linux/printk.h +@@ -9,6 +9,8 @@ + #include + #include + ++struct uart_port; ++ + extern const char linux_banner[]; + extern const char linux_proc_banner[]; + +@@ -159,13 +161,16 @@ __printf(1, 2) __cold int _printk_deferred(const char *fmt, ...); + + extern void __printk_safe_enter(void); + extern void __printk_safe_exit(void); ++extern void __printk_deferred_enter(void); ++extern void __printk_deferred_exit(void); ++ + /* + * The printk_deferred_enter/exit macros are available only as a hack for + * some code paths that need to defer all printk console printing. Interrupts + * must be disabled for the deferred duration. + */ +-#define printk_deferred_enter __printk_safe_enter +-#define printk_deferred_exit __printk_safe_exit ++#define printk_deferred_enter() __printk_deferred_enter() ++#define printk_deferred_exit() __printk_deferred_exit() + + /* + * Please don't use printk_ratelimit(), because it shares ratelimiting state +@@ -192,6 +197,10 @@ void show_regs_print_info(const char *log_lvl); + extern asmlinkage void dump_stack_lvl(const char *log_lvl) __cold; + extern asmlinkage void dump_stack(void) __cold; + void printk_trigger_flush(void); ++void printk_legacy_allow_panic_sync(void); ++extern void nbcon_acquire(struct uart_port *up); ++extern void nbcon_release(struct uart_port *up); ++void nbcon_atomic_flush_unsafe(void); + #else + static inline __printf(1, 0) + int vprintk(const char *s, va_list args) +@@ -271,6 +280,23 @@ static inline void dump_stack(void) + static inline void printk_trigger_flush(void) + { + } ++ ++static inline void printk_legacy_allow_panic_sync(void) ++{ ++} ++ ++static inline void nbcon_acquire(struct uart_port *up) ++{ ++} ++ ++static inline void nbcon_release(struct uart_port *up) ++{ ++} ++ ++static inline void nbcon_atomic_flush_unsafe(void) ++{ ++} ++ + #endif + + #ifdef CONFIG_SMP +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 77f01ac385f7..c02fd12b49dc 100644 +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -911,6 +911,9 @@ struct task_struct { + * ->sched_remote_wakeup gets used, so it can be in this word. + */ + unsigned sched_remote_wakeup:1; ++#ifdef CONFIG_RT_MUTEXES ++ unsigned sched_rt_mutex:1; ++#endif + + /* Bit to tell LSMs we're in execve(): */ + unsigned in_execve:1; +@@ -1902,6 +1905,7 @@ static inline int dl_task_check_affinity(struct task_struct *p, const struct cpu + } + #endif + ++extern bool task_is_pi_boosted(const struct task_struct *p); + extern int yield_to(struct task_struct *p, bool preempt); + extern void set_user_nice(struct task_struct *p, long nice); + extern int task_prio(const struct task_struct *p); +@@ -2046,17 +2050,17 @@ static inline void update_tsk_thread_flag(struct task_struct *tsk, int flag, + update_ti_thread_flag(task_thread_info(tsk), flag, value); + } + +-static inline int test_and_set_tsk_thread_flag(struct task_struct *tsk, int flag) ++static inline bool test_and_set_tsk_thread_flag(struct task_struct *tsk, int flag) + { + return test_and_set_ti_thread_flag(task_thread_info(tsk), flag); + } + +-static inline int test_and_clear_tsk_thread_flag(struct task_struct *tsk, int flag) ++static inline bool test_and_clear_tsk_thread_flag(struct task_struct *tsk, int flag) + { + return test_and_clear_ti_thread_flag(task_thread_info(tsk), flag); + } + +-static inline int test_tsk_thread_flag(struct task_struct *tsk, int flag) ++static inline bool test_tsk_thread_flag(struct task_struct *tsk, int flag) + { + return test_ti_thread_flag(task_thread_info(tsk), flag); + } +@@ -2069,9 +2073,11 @@ static inline void set_tsk_need_resched(struct task_struct *tsk) + static inline void clear_tsk_need_resched(struct task_struct *tsk) + { + clear_tsk_thread_flag(tsk,TIF_NEED_RESCHED); ++ if (IS_ENABLED(CONFIG_PREEMPT_BUILD_AUTO)) ++ clear_tsk_thread_flag(tsk, TIF_NEED_RESCHED_LAZY); + } + +-static inline int test_tsk_need_resched(struct task_struct *tsk) ++static inline bool test_tsk_need_resched(struct task_struct *tsk) + { + return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED)); + } +@@ -2252,7 +2258,7 @@ static inline int rwlock_needbreak(rwlock_t *lock) + + static __always_inline bool need_resched(void) + { +- return unlikely(tif_need_resched()); ++ return unlikely(tif_need_resched_lazy() || tif_need_resched()); + } + + /* +diff --git a/include/linux/sched/idle.h b/include/linux/sched/idle.h +index 478084f9105e..719416fe8ddc 100644 +--- a/include/linux/sched/idle.h ++++ b/include/linux/sched/idle.h +@@ -63,7 +63,7 @@ static __always_inline bool __must_check current_set_polling_and_test(void) + */ + smp_mb__after_atomic(); + +- return unlikely(tif_need_resched()); ++ return unlikely(need_resched()); + } + + static __always_inline bool __must_check current_clr_polling_and_test(void) +@@ -76,7 +76,7 @@ static __always_inline bool __must_check current_clr_polling_and_test(void) + */ + smp_mb__after_atomic(); + +- return unlikely(tif_need_resched()); ++ return unlikely(need_resched()); + } + + #else +@@ -85,11 +85,11 @@ static inline void __current_clr_polling(void) { } + + static inline bool __must_check current_set_polling_and_test(void) + { +- return unlikely(tif_need_resched()); ++ return unlikely(need_resched()); + } + static inline bool __must_check current_clr_polling_and_test(void) + { +- return unlikely(tif_need_resched()); ++ return unlikely(need_resched()); + } + #endif + +diff --git a/include/linux/sched/rt.h b/include/linux/sched/rt.h +index 994c25640e15..b2b9e6eb9683 100644 +--- a/include/linux/sched/rt.h ++++ b/include/linux/sched/rt.h +@@ -30,6 +30,10 @@ static inline bool task_is_realtime(struct task_struct *tsk) + } + + #ifdef CONFIG_RT_MUTEXES ++extern void rt_mutex_pre_schedule(void); ++extern void rt_mutex_schedule(void); ++extern void rt_mutex_post_schedule(void); ++ + /* + * Must hold either p->pi_lock or task_rq(p)->lock. + */ +diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h +index be65de65fe61..ec46e3b49ee9 100644 +--- a/include/linux/serial_8250.h ++++ b/include/linux/serial_8250.h +@@ -153,6 +153,8 @@ struct uart_8250_port { + #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA + unsigned char msr_saved_flags; + ++ bool console_newline_needed; ++ + struct uart_8250_dma *dma; + const struct uart_8250_ops *ops; + +@@ -204,6 +206,10 @@ 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); ++bool serial8250_console_write_atomic(struct uart_8250_port *up, ++ struct nbcon_write_context *wctxt); ++bool serial8250_console_write_thread(struct uart_8250_port *up, ++ struct nbcon_write_context *wctxt); + 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/serial_core.h b/include/linux/serial_core.h +index 052df85dfd59..6de3d7aab17d 100644 +--- a/include/linux/serial_core.h ++++ b/include/linux/serial_core.h +@@ -489,6 +489,7 @@ struct uart_port { + struct uart_icount icount; /* statistics */ + + struct console *cons; /* struct console, if any */ ++ bool nbcon_locked_port; /* True, if the port is locked by nbcon */ + /* flags must be updated while holding port mutex */ + upf_t flags; + +@@ -596,6 +597,7 @@ struct uart_port { + static inline void uart_port_lock(struct uart_port *up) + { + spin_lock(&up->lock); ++ nbcon_acquire(up); + } + + /** +@@ -605,6 +607,7 @@ static inline void uart_port_lock(struct uart_port *up) + static inline void uart_port_lock_irq(struct uart_port *up) + { + spin_lock_irq(&up->lock); ++ nbcon_acquire(up); + } + + /** +@@ -615,6 +618,7 @@ static inline void uart_port_lock_irq(struct uart_port *up) + static inline void uart_port_lock_irqsave(struct uart_port *up, unsigned long *flags) + { + spin_lock_irqsave(&up->lock, *flags); ++ nbcon_acquire(up); + } + + /** +@@ -625,7 +629,11 @@ static inline void uart_port_lock_irqsave(struct uart_port *up, unsigned long *f + */ + static inline bool uart_port_trylock(struct uart_port *up) + { +- return spin_trylock(&up->lock); ++ if (!spin_trylock(&up->lock)) ++ return false; ++ ++ nbcon_acquire(up); ++ return true; + } + + /** +@@ -637,7 +645,11 @@ static inline bool uart_port_trylock(struct uart_port *up) + */ + static inline bool uart_port_trylock_irqsave(struct uart_port *up, unsigned long *flags) + { +- return spin_trylock_irqsave(&up->lock, *flags); ++ if (!spin_trylock_irqsave(&up->lock, *flags)) ++ return false; ++ ++ nbcon_acquire(up); ++ return true; + } + + /** +@@ -646,6 +658,7 @@ static inline bool uart_port_trylock_irqsave(struct uart_port *up, unsigned long + */ + static inline void uart_port_unlock(struct uart_port *up) + { ++ nbcon_release(up); + spin_unlock(&up->lock); + } + +@@ -655,6 +668,7 @@ static inline void uart_port_unlock(struct uart_port *up) + */ + static inline void uart_port_unlock_irq(struct uart_port *up) + { ++ nbcon_release(up); + spin_unlock_irq(&up->lock); + } + +@@ -664,6 +678,19 @@ static inline void uart_port_unlock_irq(struct uart_port *up) + * @flags: The saved interrupt flags for restore + */ + static inline void uart_port_unlock_irqrestore(struct uart_port *up, unsigned long flags) ++{ ++ nbcon_release(up); ++ spin_unlock_irqrestore(&up->lock, flags); ++} ++ ++/* Only for use in the console->driver_enter() callback. */ ++static inline void __uart_port_lock_irqsave(struct uart_port *up, unsigned long *flags) ++{ ++ spin_lock_irqsave(&up->lock, *flags); ++} ++ ++/* Only for use in the console->driver_exit() callback. */ ++static inline void __uart_port_unlock_irqrestore(struct uart_port *up, unsigned long flags) + { + spin_unlock_irqrestore(&up->lock, flags); + } +@@ -1078,14 +1105,14 @@ static inline void uart_unlock_and_check_sysrq(struct uart_port *port) + u8 sysrq_ch; + + if (!port->has_sysrq) { +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + return; + } + + sysrq_ch = port->sysrq_ch; + port->sysrq_ch = 0; + +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + + if (sysrq_ch) + handle_sysrq(sysrq_ch); +@@ -1097,14 +1124,14 @@ static inline void uart_unlock_and_check_sysrq_irqrestore(struct uart_port *port + u8 sysrq_ch; + + if (!port->has_sysrq) { +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + return; + } + + sysrq_ch = port->sysrq_ch; + port->sysrq_ch = 0; + +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + + if (sysrq_ch) + handle_sysrq(sysrq_ch); +@@ -1120,12 +1147,12 @@ static inline int uart_prepare_sysrq_char(struct uart_port *port, u8 ch) + } + static inline void uart_unlock_and_check_sysrq(struct uart_port *port) + { +- spin_unlock(&port->lock); ++ uart_port_unlock(port); + } + static inline void uart_unlock_and_check_sysrq_irqrestore(struct uart_port *port, + unsigned long flags) + { +- spin_unlock_irqrestore(&port->lock, flags); ++ uart_port_unlock_irqrestore(port, flags); + } + #endif /* CONFIG_MAGIC_SYSRQ_SERIAL */ + +diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h +index 9ea0b28068f4..5ded1450ac1a 100644 +--- a/include/linux/thread_info.h ++++ b/include/linux/thread_info.h +@@ -59,6 +59,16 @@ enum syscall_work_bit { + + #include + ++#ifdef CONFIG_PREEMPT_BUILD_AUTO ++# define TIF_NEED_RESCHED_LAZY TIF_ARCH_RESCHED_LAZY ++# define _TIF_NEED_RESCHED_LAZY _TIF_ARCH_RESCHED_LAZY ++# define TIF_NEED_RESCHED_LAZY_OFFSET (TIF_NEED_RESCHED_LAZY - TIF_NEED_RESCHED) ++#else ++# define TIF_NEED_RESCHED_LAZY TIF_NEED_RESCHED ++# define _TIF_NEED_RESCHED_LAZY _TIF_NEED_RESCHED ++# define TIF_NEED_RESCHED_LAZY_OFFSET 0 ++#endif ++ + #ifdef __KERNEL__ + + #ifndef arch_set_restart_data +@@ -185,6 +195,13 @@ static __always_inline bool tif_need_resched(void) + (unsigned long *)(¤t_thread_info()->flags)); + } + ++static __always_inline bool tif_need_resched_lazy(void) ++{ ++ return IS_ENABLED(CONFIG_PREEMPT_BUILD_AUTO) && ++ arch_test_bit(TIF_NEED_RESCHED_LAZY, ++ (unsigned long *)(¤t_thread_info()->flags)); ++} ++ + #else + + static __always_inline bool tif_need_resched(void) +@@ -193,6 +210,13 @@ static __always_inline bool tif_need_resched(void) + (unsigned long *)(¤t_thread_info()->flags)); + } + ++static __always_inline bool tif_need_resched_lazy(void) ++{ ++ return IS_ENABLED(CONFIG_PREEMPT_BUILD_AUTO) && ++ test_bit(TIF_NEED_RESCHED_LAZY, ++ (unsigned long *)(¤t_thread_info()->flags)); ++} ++ + #endif /* _ASM_GENERIC_BITOPS_INSTRUMENTED_NON_ATOMIC_H */ + + #ifndef CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES +diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h +index cb8bd759e800..ce1fefc37cb5 100644 +--- a/include/linux/trace_events.h ++++ b/include/linux/trace_events.h +@@ -178,8 +178,8 @@ unsigned int tracing_gen_ctx_irq_test(unsigned int irqs_status); + + enum trace_flag_type { + TRACE_FLAG_IRQS_OFF = 0x01, +- TRACE_FLAG_IRQS_NOSUPPORT = 0x02, +- TRACE_FLAG_NEED_RESCHED = 0x04, ++ TRACE_FLAG_NEED_RESCHED = 0x02, ++ TRACE_FLAG_NEED_RESCHED_LAZY = 0x04, + TRACE_FLAG_HARDIRQ = 0x08, + TRACE_FLAG_SOFTIRQ = 0x10, + TRACE_FLAG_PREEMPT_RESCHED = 0x20, +@@ -205,11 +205,11 @@ static inline unsigned int tracing_gen_ctx(void) + + static inline unsigned int tracing_gen_ctx_flags(unsigned long irqflags) + { +- return tracing_gen_ctx_irq_test(TRACE_FLAG_IRQS_NOSUPPORT); ++ return tracing_gen_ctx_irq_test(0); + } + static inline unsigned int tracing_gen_ctx(void) + { +- return tracing_gen_ctx_irq_test(TRACE_FLAG_IRQS_NOSUPPORT); ++ return tracing_gen_ctx_irq_test(0); + } + #endif + +diff --git a/include/net/dsa.h b/include/net/dsa.h +index 0b9c6aa27047..d6c3e3457401 100644 +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -56,6 +56,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, +@@ -86,6 +87,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; +@@ -956,6 +958,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/flow_offload.h b/include/net/flow_offload.h +index 9efa9a59e81f..e8c472ce2bfb 100644 +--- a/include/net/flow_offload.h ++++ b/include/net/flow_offload.h +@@ -184,6 +184,7 @@ enum flow_action_id { + FLOW_ACTION_VLAN_PUSH_ETH, + FLOW_ACTION_VLAN_POP_ETH, + FLOW_ACTION_CONTINUE, ++ FLOW_ACTION_FRER, + NUM_FLOW_ACTIONS, + }; + +@@ -327,6 +328,14 @@ struct flow_action_entry { + struct { /* FLOW_ACTION_PPPOE_PUSH */ + u16 sid; + } pppoe; ++ struct { ++ u8 tag_type; ++ u8 tag_action; ++ u8 recover; ++ u8 rcvy_alg; ++ u8 rcvy_history_len; ++ u16 rcvy_reset_msec; ++ } frer; + }; + struct flow_action_cookie *user_cookie; /* user defined action cookie */ + }; +diff --git a/include/net/switchdev.h b/include/net/switchdev.h +index 8346b0d29542..990b1a430b81 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/net/tc_act/tc_frer.h b/include/net/tc_act/tc_frer.h +new file mode 100644 +index 000000000000..b2ad2b2a3fe1 +--- /dev/null ++++ b/include/net/tc_act/tc_frer.h +@@ -0,0 +1,52 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* Copyright 2021 NXP */ ++ ++#ifndef __NET_TC_FRER_H ++#define __NET_TC_FRER_H ++ ++#include ++#include ++ ++struct tcf_frer; ++ ++struct tcf_frer_proto_ops { ++ int (*encode)(struct sk_buff *skb, struct tcf_frer *frer_act); ++ int (*decode)(struct sk_buff *skb); ++ void (*tag_pop)(struct sk_buff *skb, struct tcf_frer *frer_act); ++}; ++ ++struct tcf_frer { ++ struct tc_action common; ++ u8 tag_type; ++ u8 tag_action; ++ u8 recover; ++ u8 rcvy_alg; ++ u8 rcvy_history_len; ++ u64 rcvy_reset_msec; ++ u32 gen_seq_num; ++ u32 rcvy_seq_num; ++ u64 seq_space; ++ u32 seq_history; ++ bool take_any; ++ bool rcvy_take_noseq; ++ u32 cps_seq_rcvy_lost_pkts; ++ u32 cps_seq_rcvy_tagless_pkts; ++ u32 cps_seq_rcvy_out_of_order_pkts; ++ u32 cps_seq_rcvy_rogue_pkts; ++ u32 cps_seq_rcvy_resets; ++ struct hrtimer hrtimer; ++ const struct tcf_frer_proto_ops *proto_ops; ++}; ++ ++#define to_frer(a) ((struct tcf_frer *)a) ++ ++static inline bool is_tcf_frer(const struct tc_action *a) ++{ ++#ifdef CONFIG_NET_CLS_ACT ++ if (a->ops && a->ops->id == TCA_ID_FRER) ++ return true; ++#endif ++ return false; ++} ++ ++#endif +diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h +index 4d41baa6e0e1..87613b5746f5 100644 +--- a/include/soc/mscc/ocelot.h ++++ b/include/soc/mscc/ocelot.h +@@ -11,6 +11,9 @@ + #include + #include + ++#define OCELOT_MAX_PTP_ID 63 ++#define OCELOT_PTP_FIFO_SIZE 128 ++ + struct phy; + struct tc_mqprio_qopt_offload; + +@@ -788,7 +791,6 @@ struct ocelot_port { + phy_interface_t phy_mode; + struct phy *serdes; + +- unsigned int ptp_skbs_in_flight; + struct sk_buff_head tx_skbs; + + unsigned int trap_proto; +@@ -796,12 +798,12 @@ struct ocelot_port { + u16 mrp_ring_id; + + u8 ptp_cmd; +- u8 ts_id; + + u8 index; + + u8 stp_state; + bool vlan_aware; ++ bool qinq_mode; + bool is_dsa_8021q_cpu; + bool learn_ena; + +@@ -812,8 +814,11 @@ struct ocelot_port { + bool force_forward; + u8 cut_thru; + u8 cut_thru_selected_by_user; ++ bool fp_enabled_admin; + + int speed; ++ DECLARE_BITMAP(ts_id_in_flight, OCELOT_MAX_PTP_ID); ++ unsigned long ptp_tx_time[OCELOT_MAX_PTP_ID]; + }; + + struct ocelot { +@@ -902,6 +907,9 @@ struct ocelot { + + struct ocelot_mm_state *mm; + ++ bool qinq_enable; ++ struct kref qinq_refcount; ++ + struct ocelot_fdma *fdma; + }; + +diff --git a/include/soc/mscc/ocelot_ptp.h b/include/soc/mscc/ocelot_ptp.h +index f085884b1fa2..ded497d72bdb 100644 +--- a/include/soc/mscc/ocelot_ptp.h ++++ b/include/soc/mscc/ocelot_ptp.h +@@ -13,9 +13,6 @@ + #include + #include + +-#define OCELOT_MAX_PTP_ID 63 +-#define OCELOT_PTP_FIFO_SIZE 128 +- + #define PTP_PIN_CFG_RSZ 0x20 + #define PTP_PIN_TOD_SEC_MSB_RSZ PTP_PIN_CFG_RSZ + #define PTP_PIN_TOD_SEC_LSB_RSZ PTP_PIN_CFG_RSZ +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 421eb57fb6e9..b7a7aa00f499 100644 +--- a/include/uapi/linux/ethtool.h ++++ b/include/uapi/linux/ethtool.h +@@ -379,6 +379,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 +@@ -1671,6 +1697,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 73e2c10dc2cc..45010bfc11ca 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, +@@ -94,6 +96,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, +@@ -975,6 +979,23 @@ enum { + ETHTOOL_A_MM_MAX = (__ETHTOOL_A_MM_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/if_ether.h b/include/uapi/linux/if_ether.h +index 69e0457eb200..9bdcecde2be8 100644 +--- a/include/uapi/linux/if_ether.h ++++ b/include/uapi/linux/if_ether.h +@@ -119,6 +119,7 @@ + #define ETH_P_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */ + #define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */ + #define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */ ++#define ETH_P_RTAG 0xF1C1 /* Redundancy Tag(IEEE 802.1CB) */ + + #define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is more than this value + * then the frame is Ethernet II. Else it is 802.3 */ +diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h +index c7082cc60d21..930eb82f3e20 100644 +--- a/include/uapi/linux/pkt_cls.h ++++ b/include/uapi/linux/pkt_cls.h +@@ -139,6 +139,7 @@ enum tca_id { + TCA_ID_MPLS, + TCA_ID_CT, + TCA_ID_GATE, ++ TCA_ID_FRER, + /* other actions go here */ + __TCA_ID_MAX = 255 + }; +diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h +index 05cc35fc94ac..e9f63daa4d06 100644 +--- a/include/uapi/linux/ptp_clock.h ++++ b/include/uapi/linux/ptp_clock.h +@@ -199,6 +199,17 @@ struct ptp_pin_desc { + unsigned int rsv[5]; + }; + ++#define PTP_MAX_CONVERT_TS_NUM 16 /* Maximum allowed number of timestamps to convert. */ ++ ++struct ptp_convert_timestamps { ++ /* src timestamp to convert in the ptp device clock domain. */ ++ struct ptp_clock_time src_ts[PTP_MAX_CONVERT_TS_NUM]; ++ /* converted timestamp in the destination ptp device clock domain. */ ++ struct ptp_clock_time dst_ts[PTP_MAX_CONVERT_TS_NUM]; ++ unsigned int dst_phc_index; /* destination ptp clock domain. */ ++ unsigned int n_ts; /* Number of timestamps in src_ts array. */ ++}; ++ + #define PTP_CLK_MAGIC '=' + + #define PTP_CLOCK_GETCAPS _IOR(PTP_CLK_MAGIC, 1, struct ptp_clock_caps) +@@ -224,6 +235,7 @@ struct ptp_pin_desc { + _IOWR(PTP_CLK_MAGIC, 17, struct ptp_sys_offset_precise) + #define PTP_SYS_OFFSET_EXTENDED2 \ + _IOWR(PTP_CLK_MAGIC, 18, struct ptp_sys_offset_extended) ++#define PTP_CONVERT_TIMESTAMPS _IOW(PTP_CLK_MAGIC, 21, struct ptp_convert_timestamps) + + struct ptp_extts_event { + struct ptp_clock_time t; /* Time event occured. */ +diff --git a/include/uapi/linux/tc_act/tc_frer.h b/include/uapi/linux/tc_act/tc_frer.h +new file mode 100644 +index 000000000000..fe2420337633 +--- /dev/null ++++ b/include/uapi/linux/tc_act/tc_frer.h +@@ -0,0 +1,50 @@ ++/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ ++/* Copyright 2021 NXP */ ++ ++#ifndef __LINUX_TC_FRER_H ++#define __LINUX_TC_FRER_H ++ ++#include ++ ++struct tc_frer { ++ tc_gen; ++}; ++ ++enum { ++ TCA_FRER_UNSPEC, ++ TCA_FRER_TM, ++ TCA_FRER_PARMS, ++ TCA_FRER_PAD, ++ TCA_FRER_TAG_TYPE, ++ TCA_FRER_TAG_ACTION, ++ TCA_FRER_RECOVER, ++ TCA_FRER_RECOVER_ALG, ++ TCA_FRER_RECOVER_HISTORY_LEN, ++ TCA_FRER_RECOVER_RESET_TM, ++ TCA_FRER_RECOVER_TAGLESS_PKTS, ++ TCA_FRER_RECOVER_OUT_OF_ORDER_PKTS, ++ TCA_FRER_RECOVER_ROGUE_PKTS, ++ TCA_FRER_RECOVER_LOST_PKTS, ++ TCA_FRER_RECOVER_RESETS, ++ __TCA_FRER_MAX, ++}; ++#define TCA_FRER_MAX (__TCA_FRER_MAX - 1) ++ ++enum tc_frer_tag_action { ++ TCA_FRER_TAG_NULL, ++ TCA_FRER_TAG_PUSH, ++ TCA_FRER_TAG_POP, ++}; ++ ++enum tc_frer_tag_type { ++ TCA_FRER_TAG_RTAG = 1, ++ TCA_FRER_TAG_HSR, ++ TCA_FRER_TAG_PRP, ++}; ++ ++enum tc_frer_rcvy_alg { ++ TCA_FRER_RCVY_VECTOR_ALG, ++ TCA_FRER_RCVY_MATCH_ALG, ++}; ++ ++#endif +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 */ +diff --git a/kernel/Kconfig.preempt b/kernel/Kconfig.preempt +index c2f1fd95a821..0f3d4c2a41cb 100644 +--- a/kernel/Kconfig.preempt ++++ b/kernel/Kconfig.preempt +@@ -11,6 +11,13 @@ config PREEMPT_BUILD + select PREEMPTION + select UNINLINE_SPIN_UNLOCK if !ARCH_INLINE_SPIN_UNLOCK + ++config PREEMPT_BUILD_AUTO ++ bool ++ select PREEMPT_BUILD ++ ++config HAVE_PREEMPT_AUTO ++ bool ++ + choice + prompt "Preemption Model" + default PREEMPT_NONE +@@ -67,9 +74,17 @@ config PREEMPT + embedded system with latency requirements in the milliseconds + range. + ++config PREEMPT_AUTO ++ bool "Automagic preemption mode with runtime tweaking support" ++ depends on HAVE_PREEMPT_AUTO ++ select PREEMPT_BUILD_AUTO ++ help ++ Add some sensible blurb here ++ + config PREEMPT_RT + bool "Fully Preemptible Kernel (Real-Time)" + depends on EXPERT && ARCH_SUPPORTS_RT ++ select PREEMPT_BUILD_AUTO if HAVE_PREEMPT_AUTO + select PREEMPTION + help + This option turns the kernel into a real-time kernel by replacing +@@ -95,7 +110,7 @@ config PREEMPTION + + config PREEMPT_DYNAMIC + bool "Preemption behaviour defined on boot" +- depends on HAVE_PREEMPT_DYNAMIC && !PREEMPT_RT ++ depends on HAVE_PREEMPT_DYNAMIC && !PREEMPT_RT && !PREEMPT_AUTO + select JUMP_LABEL if HAVE_PREEMPT_DYNAMIC_KEY + select PREEMPT_BUILD + default y if HAVE_PREEMPT_DYNAMIC_CALL +diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c +index ff5683a57f77..3c659063b73a 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); + +@@ -312,7 +316,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; +@@ -337,9 +341,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 ccf2b1e1b40b..f70d2a164de1 100644 +--- a/kernel/entry/common.c ++++ b/kernel/entry/common.c +@@ -161,7 +161,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 | _TIF_NEED_RESCHED_LAZY)) + schedule(); + + if (ti_work & _TIF_UPROBE) +@@ -392,7 +392,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 (test_tsk_need_resched(current)) + preempt_schedule_irq(); + } + } +diff --git a/kernel/entry/kvm.c b/kernel/entry/kvm.c +index 2e0f75bcb7fd..d952fa5ee880 100644 +--- a/kernel/entry/kvm.c ++++ b/kernel/entry/kvm.c +@@ -13,7 +13,7 @@ static int xfer_to_guest_mode_work(struct kvm_vcpu *vcpu, unsigned long ti_work) + return -EINTR; + } + +- if (ti_work & _TIF_NEED_RESCHED) ++ if (ti_work & (_TIF_NEED_RESCHED | TIF_NEED_RESCHED_LAZY)) + schedule(); + + if (ti_work & _TIF_NOTIFY_RESUME) +diff --git a/kernel/futex/pi.c b/kernel/futex/pi.c +index ce2889f12375..d636a1bbd7d0 100644 +--- a/kernel/futex/pi.c ++++ b/kernel/futex/pi.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-or-later + + #include ++#include + #include + + #include "futex.h" +@@ -610,29 +611,16 @@ int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb, + /* + * Caller must hold a reference on @pi_state. + */ +-static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_pi_state *pi_state) ++static int wake_futex_pi(u32 __user *uaddr, u32 uval, ++ struct futex_pi_state *pi_state, ++ struct rt_mutex_waiter *top_waiter) + { +- struct rt_mutex_waiter *top_waiter; + struct task_struct *new_owner; + bool postunlock = false; + DEFINE_RT_WAKE_Q(wqh); + u32 curval, newval; + int ret = 0; + +- top_waiter = rt_mutex_top_waiter(&pi_state->pi_mutex); +- if (WARN_ON_ONCE(!top_waiter)) { +- /* +- * As per the comment in futex_unlock_pi() this should not happen. +- * +- * When this happens, give up our locks and try again, giving +- * the futex_lock_pi() instance time to complete, either by +- * waiting on the rtmutex or removing itself from the futex +- * queue. +- */ +- ret = -EAGAIN; +- goto out_unlock; +- } +- + new_owner = top_waiter->task; + + /* +@@ -1002,6 +990,12 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl + goto no_block; + } + ++ /* ++ * Must be done before we enqueue the waiter, here is unfortunately ++ * under the hb lock, but that *should* work because it does nothing. ++ */ ++ rt_mutex_pre_schedule(); ++ + rt_mutex_init_waiter(&rt_waiter); + + /* +@@ -1039,19 +1033,37 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl + ret = rt_mutex_wait_proxy_lock(&q.pi_state->pi_mutex, to, &rt_waiter); + + cleanup: +- spin_lock(q.lock_ptr); + /* + * If we failed to acquire the lock (deadlock/signal/timeout), we must +- * first acquire the hb->lock before removing the lock from the +- * rt_mutex waitqueue, such that we can keep the hb and rt_mutex wait +- * lists consistent. ++ * must unwind the above, however we canont lock hb->lock because ++ * rt_mutex already has a waiter enqueued and hb->lock can itself try ++ * and enqueue an rt_waiter through rtlock. ++ * ++ * Doing the cleanup without holding hb->lock can cause inconsistent ++ * state between hb and pi_state, but only in the direction of not ++ * seeing a waiter that is leaving. ++ * ++ * See futex_unlock_pi(), it deals with this inconsistency. ++ * ++ * There be dragons here, since we must deal with the inconsistency on ++ * the way out (here), it is impossible to detect/warn about the race ++ * the other way around (missing an incoming waiter). + * +- * In particular; it is important that futex_unlock_pi() can not +- * observe this inconsistency. ++ * What could possibly go wrong... + */ + if (ret && !rt_mutex_cleanup_proxy_lock(&q.pi_state->pi_mutex, &rt_waiter)) + ret = 0; + ++ /* ++ * Now that the rt_waiter has been dequeued, it is safe to use ++ * spinlock/rtlock (which might enqueue its own rt_waiter) and fix up ++ * the ++ */ ++ spin_lock(q.lock_ptr); ++ /* ++ * Waiter is unqueued. ++ */ ++ rt_mutex_post_schedule(); + no_block: + /* + * Fixup the pi_state owner and possibly acquire the lock if we +@@ -1132,6 +1144,7 @@ int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) + top_waiter = futex_top_waiter(hb, &key); + if (top_waiter) { + struct futex_pi_state *pi_state = top_waiter->pi_state; ++ struct rt_mutex_waiter *rt_waiter; + + ret = -EINVAL; + if (!pi_state) +@@ -1144,22 +1157,39 @@ int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) + if (pi_state->owner != current) + goto out_unlock; + +- get_pi_state(pi_state); + /* + * By taking wait_lock while still holding hb->lock, we ensure +- * there is no point where we hold neither; and therefore +- * wake_futex_p() must observe a state consistent with what we +- * observed. ++ * there is no point where we hold neither; and thereby ++ * wake_futex_pi() must observe any new waiters. ++ * ++ * Since the cleanup: case in futex_lock_pi() removes the ++ * rt_waiter without holding hb->lock, it is possible for ++ * wake_futex_pi() to not find a waiter while the above does, ++ * in this case the waiter is on the way out and it can be ++ * ignored. + * + * In particular; this forces __rt_mutex_start_proxy() to + * complete such that we're guaranteed to observe the +- * rt_waiter. Also see the WARN in wake_futex_pi(). ++ * rt_waiter. + */ + raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock); ++ ++ /* ++ * Futex vs rt_mutex waiter state -- if there are no rt_mutex ++ * waiters even though futex thinks there are, then the waiter ++ * is leaving and the uncontended path is safe to take. ++ */ ++ rt_waiter = rt_mutex_top_waiter(&pi_state->pi_mutex); ++ if (!rt_waiter) { ++ raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock); ++ goto do_uncontended; ++ } ++ ++ get_pi_state(pi_state); + spin_unlock(&hb->lock); + + /* drops pi_state->pi_mutex.wait_lock */ +- ret = wake_futex_pi(uaddr, uval, pi_state); ++ ret = wake_futex_pi(uaddr, uval, pi_state, rt_waiter); + + put_pi_state(pi_state); + +@@ -1187,6 +1217,7 @@ int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) + return ret; + } + ++do_uncontended: + /* + * We have no kernel internal state, i.e. no waiters in the + * kernel. Waiters which are about to queue themselves are stuck +diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c +index cba8b1a6a4cc..4c73e0b81acc 100644 +--- a/kernel/futex/requeue.c ++++ b/kernel/futex/requeue.c +@@ -850,11 +850,13 @@ int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, + pi_mutex = &q.pi_state->pi_mutex; + ret = rt_mutex_wait_proxy_lock(pi_mutex, to, &rt_waiter); + +- /* Current is not longer pi_blocked_on */ +- spin_lock(q.lock_ptr); ++ /* ++ * See futex_unlock_pi()'s cleanup: comment. ++ */ + if (ret && !rt_mutex_cleanup_proxy_lock(pi_mutex, &rt_waiter)) + ret = 0; + ++ spin_lock(q.lock_ptr); + debug_rt_mutex_free_waiter(&rt_waiter); + /* + * Fixup the pi_state owner and possibly acquire the lock if we +diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c +index 1d4bc493b2f4..486c68c11bbe 100644 +--- a/kernel/ksysfs.c ++++ b/kernel/ksysfs.c +@@ -179,6 +179,15 @@ KERNEL_ATTR_RO(crash_elfcorehdr_size); + + #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) +@@ -274,6 +283,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/locking/lockdep.c b/kernel/locking/lockdep.c +index 151bd3de5936..5c21ba41e308 100644 +--- a/kernel/locking/lockdep.c ++++ b/kernel/locking/lockdep.c +@@ -56,6 +56,7 @@ + #include + #include + #include ++#include + + #include + +@@ -3971,6 +3972,8 @@ print_usage_bug(struct task_struct *curr, struct held_lock *this, + if (!debug_locks_off() || debug_locks_silent) + return; + ++ nbcon_cpu_emergency_enter(); ++ + pr_warn("\n"); + pr_warn("================================\n"); + pr_warn("WARNING: inconsistent lock state\n"); +@@ -3999,6 +4002,8 @@ print_usage_bug(struct task_struct *curr, struct held_lock *this, + + pr_warn("\nstack backtrace:\n"); + dump_stack(); ++ ++ nbcon_cpu_emergency_exit(); + } + + /* +diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c +index bf3a28ee7d8f..99129c89120a 100644 +--- a/kernel/locking/rtmutex.c ++++ b/kernel/locking/rtmutex.c +@@ -218,6 +218,11 @@ static __always_inline bool rt_mutex_cmpxchg_acquire(struct rt_mutex_base *lock, + return try_cmpxchg_acquire(&lock->owner, &old, new); + } + ++static __always_inline bool rt_mutex_try_acquire(struct rt_mutex_base *lock) ++{ ++ return rt_mutex_cmpxchg_acquire(lock, NULL, current); ++} ++ + static __always_inline bool rt_mutex_cmpxchg_release(struct rt_mutex_base *lock, + struct task_struct *old, + struct task_struct *new) +@@ -297,6 +302,20 @@ static __always_inline bool rt_mutex_cmpxchg_acquire(struct rt_mutex_base *lock, + + } + ++static int __sched rt_mutex_slowtrylock(struct rt_mutex_base *lock); ++ ++static __always_inline bool rt_mutex_try_acquire(struct rt_mutex_base *lock) ++{ ++ /* ++ * With debug enabled rt_mutex_cmpxchg trylock() will always fail. ++ * ++ * Avoid unconditionally taking the slow path by using ++ * rt_mutex_slow_trylock() which is covered by the debug code and can ++ * acquire a non-contended rtmutex. ++ */ ++ return rt_mutex_slowtrylock(lock); ++} ++ + static __always_inline bool rt_mutex_cmpxchg_release(struct rt_mutex_base *lock, + struct task_struct *old, + struct task_struct *new) +@@ -1613,7 +1632,7 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock, + raw_spin_unlock_irq(&lock->wait_lock); + + if (!owner || !rtmutex_spin_on_owner(lock, waiter, owner)) +- schedule(); ++ rt_mutex_schedule(); + + raw_spin_lock_irq(&lock->wait_lock); + set_current_state(state); +@@ -1643,7 +1662,7 @@ static void __sched rt_mutex_handle_deadlock(int res, int detect_deadlock, + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); +- schedule(); ++ rt_mutex_schedule(); + } + } + +@@ -1738,6 +1757,15 @@ static int __sched rt_mutex_slowlock(struct rt_mutex_base *lock, + unsigned long flags; + int ret; + ++ /* ++ * Do all pre-schedule work here, before we queue a waiter and invoke ++ * PI -- any such work that trips on rtlock (PREEMPT_RT spinlock) would ++ * otherwise recurse back into task_blocks_on_rt_mutex() through ++ * rtlock_slowlock() and will then enqueue a second waiter for this ++ * same task and things get really confusing real fast. ++ */ ++ rt_mutex_pre_schedule(); ++ + /* + * Technically we could use raw_spin_[un]lock_irq() here, but this can + * be called in early boot if the cmpxchg() fast path is disabled +@@ -1749,6 +1777,7 @@ static int __sched rt_mutex_slowlock(struct rt_mutex_base *lock, + raw_spin_lock_irqsave(&lock->wait_lock, flags); + ret = __rt_mutex_slowlock_locked(lock, ww_ctx, state); + raw_spin_unlock_irqrestore(&lock->wait_lock, flags); ++ rt_mutex_post_schedule(); + + return ret; + } +@@ -1756,7 +1785,9 @@ static int __sched rt_mutex_slowlock(struct rt_mutex_base *lock, + static __always_inline int __rt_mutex_lock(struct rt_mutex_base *lock, + unsigned int state) + { +- if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) ++ lockdep_assert(!current->pi_blocked_on); ++ ++ if (likely(rt_mutex_try_acquire(lock))) + return 0; + + return rt_mutex_slowlock(lock, NULL, state); +diff --git a/kernel/locking/rwbase_rt.c b/kernel/locking/rwbase_rt.c +index 25ec0239477c..2eafd73a76eb 100644 +--- a/kernel/locking/rwbase_rt.c ++++ b/kernel/locking/rwbase_rt.c +@@ -71,8 +71,20 @@ static int __sched __rwbase_read_lock(struct rwbase_rt *rwb, + struct rt_mutex_base *rtm = &rwb->rtmutex; + int ret; + ++ rwbase_pre_schedule(); + raw_spin_lock_irq(&rtm->wait_lock); + ++ /* ++ * Allow readers, as long as the writer has not completely ++ * acquired the semaphore for write. ++ */ ++ if (atomic_read(&rwb->readers) != WRITER_BIAS) { ++ atomic_inc(&rwb->readers); ++ raw_spin_unlock_irq(&rtm->wait_lock); ++ rwbase_post_schedule(); ++ return 0; ++ } ++ + /* + * Call into the slow lock path with the rtmutex->wait_lock + * held, so this can't result in the following race: +@@ -125,12 +137,15 @@ static int __sched __rwbase_read_lock(struct rwbase_rt *rwb, + rwbase_rtmutex_unlock(rtm); + + trace_contention_end(rwb, ret); ++ rwbase_post_schedule(); + return ret; + } + + static __always_inline int rwbase_read_lock(struct rwbase_rt *rwb, + unsigned int state) + { ++ lockdep_assert(!current->pi_blocked_on); ++ + if (rwbase_read_trylock(rwb)) + return 0; + +@@ -237,6 +252,8 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb, + /* Force readers into slow path */ + atomic_sub(READER_BIAS, &rwb->readers); + ++ rwbase_pre_schedule(); ++ + raw_spin_lock_irqsave(&rtm->wait_lock, flags); + if (__rwbase_write_trylock(rwb)) + goto out_unlock; +@@ -248,6 +265,7 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb, + if (rwbase_signal_pending_state(state, current)) { + rwbase_restore_current_state(); + __rwbase_write_unlock(rwb, 0, flags); ++ rwbase_post_schedule(); + trace_contention_end(rwb, -EINTR); + return -EINTR; + } +@@ -266,6 +284,7 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb, + + out_unlock: + raw_spin_unlock_irqrestore(&rtm->wait_lock, flags); ++ rwbase_post_schedule(); + return 0; + } + +diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c +index 11ed7ce6579e..62eac9fd809a 100644 +--- a/kernel/locking/rwsem.c ++++ b/kernel/locking/rwsem.c +@@ -1427,8 +1427,14 @@ static inline void __downgrade_write(struct rw_semaphore *sem) + #define rwbase_signal_pending_state(state, current) \ + signal_pending_state(state, current) + ++#define rwbase_pre_schedule() \ ++ rt_mutex_pre_schedule() ++ + #define rwbase_schedule() \ +- schedule() ++ rt_mutex_schedule() ++ ++#define rwbase_post_schedule() \ ++ rt_mutex_post_schedule() + + #include "rwbase_rt.c" + +diff --git a/kernel/locking/spinlock_rt.c b/kernel/locking/spinlock_rt.c +index 48a19ed8486d..38e292454fcc 100644 +--- a/kernel/locking/spinlock_rt.c ++++ b/kernel/locking/spinlock_rt.c +@@ -37,6 +37,8 @@ + + static __always_inline void rtlock_lock(struct rt_mutex_base *rtm) + { ++ lockdep_assert(!current->pi_blocked_on); ++ + if (unlikely(!rt_mutex_cmpxchg_acquire(rtm, NULL, current))) + rtlock_slowlock(rtm); + } +@@ -184,9 +186,13 @@ static __always_inline int rwbase_rtmutex_trylock(struct rt_mutex_base *rtm) + + #define rwbase_signal_pending_state(state, current) (0) + ++#define rwbase_pre_schedule() ++ + #define rwbase_schedule() \ + schedule_rtlock() + ++#define rwbase_post_schedule() ++ + #include "rwbase_rt.c" + /* + * The common functions which get wrapped into the rwlock API. +diff --git a/kernel/locking/ww_rt_mutex.c b/kernel/locking/ww_rt_mutex.c +index d1473c624105..c7196de838ed 100644 +--- a/kernel/locking/ww_rt_mutex.c ++++ b/kernel/locking/ww_rt_mutex.c +@@ -62,7 +62,7 @@ __ww_rt_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ww_ctx, + } + mutex_acquire_nest(&rtm->dep_map, 0, 0, nest_lock, ip); + +- if (likely(rt_mutex_cmpxchg_acquire(&rtm->rtmutex, NULL, current))) { ++ if (likely(rt_mutex_try_acquire(&rtm->rtmutex))) { + if (ww_ctx) + ww_mutex_set_context_fastpath(lock, ww_ctx); + return 0; +diff --git a/kernel/panic.c b/kernel/panic.c +index ef9f9a4e928d..9215df21d8c2 100644 +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -366,6 +366,8 @@ void panic(const char *fmt, ...) + */ + atomic_notifier_call_chain(&panic_notifier_list, 0, buf); + ++ printk_legacy_allow_panic_sync(); ++ + panic_print_sys_info(false); + + kmsg_dump(KMSG_DUMP_PANIC); +@@ -449,6 +451,7 @@ void panic(const char *fmt, ...) + * Explicitly flush the kernel log buffer one last time. + */ + console_flush_on_panic(CONSOLE_FLUSH_PENDING); ++ nbcon_atomic_flush_unsafe(); + + local_irq_enable(); + for (i = 0; ; i += PANIC_TIMER_STEP) { +@@ -627,6 +630,7 @@ bool oops_may_print(void) + */ + void oops_enter(void) + { ++ nbcon_cpu_emergency_enter(); + tracing_off(); + /* can't trust the integrity of the kernel anymore: */ + debug_locks_off(); +@@ -649,6 +653,7 @@ void oops_exit(void) + { + do_oops_enter_exit(); + print_oops_end_marker(); ++ nbcon_cpu_emergency_exit(); + kmsg_dump(KMSG_DUMP_OOPS); + } + +@@ -660,6 +665,8 @@ struct warn_args { + void __warn(const char *file, int line, void *caller, unsigned taint, + struct pt_regs *regs, struct warn_args *args) + { ++ nbcon_cpu_emergency_enter(); ++ + disable_trace_on_warning(); + + if (file) +@@ -690,6 +697,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); ++ ++ nbcon_cpu_emergency_exit(); + } + + #ifdef CONFIG_BUG +diff --git a/kernel/printk/Makefile b/kernel/printk/Makefile +index f5b388e810b9..39a2b61c7232 100644 +--- a/kernel/printk/Makefile ++++ b/kernel/printk/Makefile +@@ -1,6 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-y = printk.o +-obj-$(CONFIG_PRINTK) += printk_safe.o ++obj-$(CONFIG_PRINTK) += printk_safe.o nbcon.o + obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) += braille.o + obj-$(CONFIG_PRINTK_INDEX) += index.o + +diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h +index 7d4979d5c3ce..7db6992c54f3 100644 +--- a/kernel/printk/internal.h ++++ b/kernel/printk/internal.h +@@ -3,6 +3,8 @@ + * internal.h - printk internal definitions + */ + #include ++#include ++#include "printk_ringbuffer.h" + + #if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL) + void __init printk_sysctl_init(void); +@@ -12,6 +14,12 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, + #define printk_sysctl_init() do { } while (0) + #endif + ++#define con_printk(lvl, con, fmt, ...) \ ++ printk(lvl pr_fmt("%s%sconsole [%s%d] " fmt), \ ++ (con->flags & CON_NBCON) ? "" : "legacy ", \ ++ (con->flags & CON_BOOT) ? "boot" : "", \ ++ con->name, con->index, ##__VA_ARGS__) ++ + #ifdef CONFIG_PRINTK + + #ifdef CONFIG_PRINTK_CALLER +@@ -35,6 +43,19 @@ enum printk_info_flags { + LOG_CONT = 8, /* text is a fragment of a continuation line */ + }; + ++extern struct printk_ringbuffer *prb; ++extern bool printk_threads_enabled; ++extern bool have_legacy_console; ++extern bool have_boot_console; ++ ++/* ++ * Specifies if the console lock/unlock dance is needed for console ++ * printing. If @have_boot_console is true, the nbcon consoles will ++ * be printed serially along with the legacy consoles because nbcon ++ * consoles cannot print simultaneously with boot consoles. ++ */ ++#define printing_via_unlock (have_legacy_console || have_boot_console) ++ + __printf(4, 0) + int vprintk_store(int facility, int level, + const struct dev_printk_info *dev_info, +@@ -61,12 +82,90 @@ void defer_console_output(void); + + u16 printk_parse_prefix(const char *text, int *level, + enum printk_info_flags *flags); ++void console_lock_spinning_enable(void); ++int console_lock_spinning_disable_and_check(int cookie); ++ ++u64 nbcon_seq_read(struct console *con); ++void nbcon_seq_force(struct console *con, u64 seq); ++bool nbcon_alloc(struct console *con); ++void nbcon_init(struct console *con); ++void nbcon_free(struct console *con); ++enum nbcon_prio nbcon_get_default_prio(void); ++void nbcon_atomic_flush_all(void); ++bool nbcon_atomic_emit_next_record(struct console *con, bool *handover, int cookie); ++void nbcon_kthread_create(struct console *con); ++void nbcon_wake_threads(void); ++void nbcon_legacy_kthread_create(void); ++ ++/* ++ * Check if the given console is currently capable and allowed to print ++ * records. Note that this function does not consider the current context, ++ * which can also play a role in deciding if @con can be used to print ++ * records. ++ */ ++static inline bool console_is_usable(struct console *con, short flags, bool use_atomic) ++{ ++ if (!(flags & CON_ENABLED)) ++ return false; ++ ++ if ((flags & CON_SUSPENDED)) ++ return false; ++ ++ if (flags & CON_NBCON) { ++ if (use_atomic) { ++ if (!con->write_atomic) ++ return false; ++ } else { ++ if (!con->write_thread || !con->kthread) ++ return false; ++ } ++ } else { ++ if (!con->write) ++ return false; ++ } ++ ++ /* ++ * Console drivers may assume that per-cpu resources have been ++ * allocated. So unless they're explicitly marked as being able to ++ * cope (CON_ANYTIME) don't call them until this CPU is officially up. ++ */ ++ if (!cpu_online(raw_smp_processor_id()) && !(flags & CON_ANYTIME)) ++ return false; ++ ++ return true; ++} ++ ++/** ++ * nbcon_kthread_wake - Wake up a printk thread ++ * @con: Console to operate on ++ */ ++static inline void nbcon_kthread_wake(struct console *con) ++{ ++ /* ++ * Guarantee any new records can be seen by tasks preparing to wait ++ * before this context checks if the rcuwait is empty. ++ * ++ * The full memory barrier in rcuwait_wake_up() pairs with the full ++ * memory barrier within set_current_state() of ++ * ___rcuwait_wait_event(), which is called after prepare_to_rcuwait() ++ * adds the waiter but before it has checked the wait condition. ++ * ++ * This pairs with nbcon_kthread_func:A. ++ */ ++ rcuwait_wake_up(&con->rcuwait); /* LMM(nbcon_kthread_wake:A) */ ++} ++ + #else + + #define PRINTK_PREFIX_MAX 0 + #define PRINTK_MESSAGE_MAX 0 + #define PRINTKRB_RECORD_MAX 0 + ++static inline void nbcon_kthread_wake(struct console *con) { } ++static inline void nbcon_kthread_create(struct console *con) { } ++#define printk_threads_enabled (false) ++#define printing_via_unlock (false) ++ + /* + * In !PRINTK builds we still export console_sem + * semaphore and some of console functions (console_unlock()/etc.), so +@@ -76,8 +175,23 @@ u16 printk_parse_prefix(const char *text, int *level, + #define printk_safe_exit_irqrestore(flags) local_irq_restore(flags) + + static inline bool printk_percpu_data_ready(void) { return false; } ++static inline u64 nbcon_seq_read(struct console *con) { return 0; } ++static inline void nbcon_seq_force(struct console *con, u64 seq) { } ++static inline bool nbcon_alloc(struct console *con) { return false; } ++static inline void nbcon_init(struct console *con) { } ++static inline void nbcon_free(struct console *con) { } ++static inline enum nbcon_prio nbcon_get_default_prio(void) { return NBCON_PRIO_NONE; } ++static inline void nbcon_atomic_flush_all(void) { } ++static inline bool nbcon_atomic_emit_next_record(struct console *con, bool *handover, ++ int cookie) { return false; } ++ ++static inline bool console_is_usable(struct console *con, short flags, ++ bool use_atomic) { return false; } ++ + #endif /* CONFIG_PRINTK */ + ++extern struct printk_buffers printk_shared_pbufs; ++ + /** + * struct printk_buffers - Buffers to read/format/output printk messages. + * @outbuf: After formatting, contains text to output. +@@ -105,3 +219,10 @@ struct printk_message { + }; + + bool other_cpu_in_panic(void); ++bool this_cpu_in_panic(void); ++bool printk_get_next_message(struct printk_message *pmsg, u64 seq, ++ bool is_extended, bool may_supress); ++ ++#ifdef CONFIG_PRINTK ++void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped); ++#endif +diff --git a/kernel/printk/nbcon.c b/kernel/printk/nbcon.c +new file mode 100644 +index 000000000000..b941039ee7d2 +--- /dev/null ++++ b/kernel/printk/nbcon.c +@@ -0,0 +1,1665 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++// Copyright (C) 2022 Linutronix GmbH, John Ogness ++// Copyright (C) 2022 Intel, Thomas Gleixner ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "printk_ringbuffer.h" ++#include "internal.h" ++/* ++ * Printk console printing implementation for consoles which does not depend ++ * on the legacy style console_lock mechanism. ++ * ++ * The state of the console is maintained in the "nbcon_state" atomic ++ * variable. ++ * ++ * The console is locked when: ++ * ++ * - The 'prio' field contains the priority of the context that owns the ++ * console. Only higher priority contexts are allowed to take over the ++ * lock. A value of 0 (NBCON_PRIO_NONE) means the console is not locked. ++ * ++ * - The 'cpu' field denotes on which CPU the console is locked. It is used ++ * to prevent busy waiting on the same CPU. Also it informs the lock owner ++ * that it has lost the lock in a more complex scenario when the lock was ++ * taken over by a higher priority context, released, and taken on another ++ * CPU with the same priority as the interrupted owner. ++ * ++ * The acquire mechanism uses a few more fields: ++ * ++ * - The 'req_prio' field is used by the handover approach to make the ++ * current owner aware that there is a context with a higher priority ++ * waiting for the friendly handover. ++ * ++ * - The 'unsafe' field allows to take over the console in a safe way in the ++ * middle of emitting a message. The field is set only when accessing some ++ * shared resources or when the console device is manipulated. It can be ++ * cleared, for example, after emitting one character when the console ++ * device is in a consistent state. ++ * ++ * - The 'unsafe_takeover' field is set when a hostile takeover took the ++ * console in an unsafe state. The console will stay in the unsafe state ++ * until re-initialized. ++ * ++ * The acquire mechanism uses three approaches: ++ * ++ * 1) Direct acquire when the console is not owned or is owned by a lower ++ * priority context and is in a safe state. ++ * ++ * 2) Friendly handover mechanism uses a request/grant handshake. It is used ++ * when the current owner has lower priority and the console is in an ++ * unsafe state. ++ * ++ * The requesting context: ++ * ++ * a) Sets its priority into the 'req_prio' field. ++ * ++ * b) Waits (with a timeout) for the owning context to unlock the ++ * console. ++ * ++ * c) Takes the lock and clears the 'req_prio' field. ++ * ++ * The owning context: ++ * ++ * a) Observes the 'req_prio' field set on exit from the unsafe ++ * console state. ++ * ++ * b) Gives up console ownership by clearing the 'prio' field. ++ * ++ * 3) Unsafe hostile takeover allows to take over the lock even when the ++ * console is an unsafe state. It is used only in panic() by the final ++ * attempt to flush consoles in a try and hope mode. ++ * ++ * Note that separate record buffers are used in panic(). As a result, ++ * the messages can be read and formatted without any risk even after ++ * using the hostile takeover in unsafe state. ++ * ++ * The release function simply clears the 'prio' field. ++ * ++ * All operations on @console::nbcon_state are atomic cmpxchg based to ++ * handle concurrency. ++ * ++ * The acquire/release functions implement only minimal policies: ++ * ++ * - Preference for higher priority contexts. ++ * - Protection of the panic CPU. ++ * ++ * All other policy decisions must be made at the call sites: ++ * ++ * - What is marked as an unsafe section. ++ * - Whether to spin-wait if there is already an owner and the console is ++ * in an unsafe state. ++ * - Whether to attempt an unsafe hostile takeover. ++ * ++ * The design allows to implement the well known: ++ * ++ * acquire() ++ * output_one_printk_record() ++ * release() ++ * ++ * The output of one printk record might be interrupted with a higher priority ++ * context. The new owner is supposed to reprint the entire interrupted record ++ * from scratch. ++ */ ++ ++/** ++ * nbcon_state_set - Helper function to set the console state ++ * @con: Console to update ++ * @new: The new state to write ++ * ++ * Only to be used when the console is not yet or no longer visible in the ++ * system. Otherwise use nbcon_state_try_cmpxchg(). ++ */ ++static inline void nbcon_state_set(struct console *con, struct nbcon_state *new) ++{ ++ atomic_set(&ACCESS_PRIVATE(con, nbcon_state), new->atom); ++} ++ ++/** ++ * nbcon_state_read - Helper function to read the console state ++ * @con: Console to read ++ * @state: The state to store the result ++ */ ++static inline void nbcon_state_read(struct console *con, struct nbcon_state *state) ++{ ++ state->atom = atomic_read(&ACCESS_PRIVATE(con, nbcon_state)); ++} ++ ++/** ++ * nbcon_state_try_cmpxchg() - Helper function for atomic_try_cmpxchg() on console state ++ * @con: Console to update ++ * @cur: Old/expected state ++ * @new: New state ++ * ++ * Return: True on success. False on fail and @cur is updated. ++ */ ++static inline bool nbcon_state_try_cmpxchg(struct console *con, struct nbcon_state *cur, ++ struct nbcon_state *new) ++{ ++ return atomic_try_cmpxchg(&ACCESS_PRIVATE(con, nbcon_state), &cur->atom, new->atom); ++} ++ ++/** ++ * nbcon_seq_read - Read the current console sequence ++ * @con: Console to read the sequence of ++ * ++ * Return: Sequence number of the next record to print on @con. ++ */ ++u64 nbcon_seq_read(struct console *con) ++{ ++ unsigned long nbcon_seq = atomic_long_read(&ACCESS_PRIVATE(con, nbcon_seq)); ++ ++ return __ulseq_to_u64seq(prb, nbcon_seq); ++} ++ ++/** ++ * nbcon_seq_force - Force console sequence to a specific value ++ * @con: Console to work on ++ * @seq: Sequence number value to set ++ * ++ * Only to be used during init (before registration) or in extreme situations ++ * (such as panic with CONSOLE_REPLAY_ALL). ++ */ ++void nbcon_seq_force(struct console *con, u64 seq) ++{ ++ /* ++ * If the specified record no longer exists, the oldest available record ++ * is chosen. This is especially important on 32bit systems because only ++ * the lower 32 bits of the sequence number are stored. The upper 32 bits ++ * are derived from the sequence numbers available in the ringbuffer. ++ */ ++ u64 valid_seq = max_t(u64, seq, prb_first_valid_seq(prb)); ++ ++ atomic_long_set(&ACCESS_PRIVATE(con, nbcon_seq), __u64seq_to_ulseq(valid_seq)); ++ ++ /* Clear con->seq since nbcon consoles use con->nbcon_seq instead. */ ++ con->seq = 0; ++} ++ ++/** ++ * nbcon_seq_try_update - Try to update the console sequence number ++ * @ctxt: Pointer to an acquire context that contains ++ * all information about the acquire mode ++ * @new_seq: The new sequence number to set ++ * ++ * @ctxt->seq is updated to the new value of @con::nbcon_seq (expanded to ++ * the 64bit value). This could be a different value than @new_seq if ++ * nbcon_seq_force() was used or the current context no longer owns the ++ * console. In the later case, it will stop printing anyway. ++ */ ++static void nbcon_seq_try_update(struct nbcon_context *ctxt, u64 new_seq) ++{ ++ unsigned long nbcon_seq = __u64seq_to_ulseq(ctxt->seq); ++ struct console *con = ctxt->console; ++ ++ if (atomic_long_try_cmpxchg(&ACCESS_PRIVATE(con, nbcon_seq), &nbcon_seq, ++ __u64seq_to_ulseq(new_seq))) { ++ ctxt->seq = new_seq; ++ } else { ++ ctxt->seq = nbcon_seq_read(con); ++ } ++} ++ ++bool printk_threads_enabled __ro_after_init; ++ ++/** ++ * nbcon_context_try_acquire_direct - Try to acquire directly ++ * @ctxt: The context of the caller ++ * @cur: The current console state ++ * ++ * Acquire the console when it is released. Also acquire the console when ++ * the current owner has a lower priority and the console is in a safe state. ++ * ++ * Return: 0 on success. Otherwise, an error code on failure. Also @cur ++ * is updated to the latest state when failed to modify it. ++ * ++ * Errors: ++ * ++ * -EPERM: A panic is in progress and this is not the panic CPU. ++ * Or the current owner or waiter has the same or higher ++ * priority. No acquire method can be successful in ++ * this case. ++ * ++ * -EBUSY: The current owner has a lower priority but the console ++ * in an unsafe state. The caller should try using ++ * the handover acquire method. ++ */ ++static int nbcon_context_try_acquire_direct(struct nbcon_context *ctxt, ++ struct nbcon_state *cur) ++{ ++ unsigned int cpu = smp_processor_id(); ++ struct console *con = ctxt->console; ++ struct nbcon_state new; ++ ++ do { ++ if (other_cpu_in_panic()) ++ return -EPERM; ++ ++ if (ctxt->prio <= cur->prio || ctxt->prio <= cur->req_prio) ++ return -EPERM; ++ ++ if (cur->unsafe) ++ return -EBUSY; ++ ++ /* ++ * The console should never be safe for a direct acquire ++ * if an unsafe hostile takeover has ever happened. ++ */ ++ WARN_ON_ONCE(cur->unsafe_takeover); ++ ++ new.atom = cur->atom; ++ new.prio = ctxt->prio; ++ new.req_prio = NBCON_PRIO_NONE; ++ new.unsafe = cur->unsafe_takeover; ++ new.cpu = cpu; ++ ++ } while (!nbcon_state_try_cmpxchg(con, cur, &new)); ++ ++ return 0; ++} ++ ++static bool nbcon_waiter_matches(struct nbcon_state *cur, int expected_prio) ++{ ++ /* ++ * The request context is well defined by the @req_prio because: ++ * ++ * - Only a context with a higher priority can take over the request. ++ * - There are only three priorities. ++ * - Only one CPU is allowed to request PANIC priority. ++ * - Lower priorities are ignored during panic() until reboot. ++ * ++ * As a result, the following scenario is *not* possible: ++ * ++ * 1. Another context with a higher priority directly takes ownership. ++ * 2. The higher priority context releases the ownership. ++ * 3. A lower priority context takes the ownership. ++ * 4. Another context with the same priority as this context ++ * creates a request and starts waiting. ++ */ ++ ++ return (cur->req_prio == expected_prio); ++} ++ ++/** ++ * nbcon_context_try_acquire_requested - Try to acquire after having ++ * requested a handover ++ * @ctxt: The context of the caller ++ * @cur: The current console state ++ * ++ * This is a helper function for nbcon_context_try_acquire_handover(). ++ * It is called when the console is in an unsafe state. The current ++ * owner will release the console on exit from the unsafe region. ++ * ++ * Return: 0 on success and @cur is updated to the new console state. ++ * Otherwise an error code on failure. ++ * ++ * Errors: ++ * ++ * -EPERM: A panic is in progress and this is not the panic CPU ++ * or this context is no longer the waiter. ++ * ++ * -EBUSY: The console is still locked. The caller should ++ * continue waiting. ++ * ++ * Note: The caller must still remove the request when an error has occurred ++ * except when this context is no longer the waiter. ++ */ ++static int nbcon_context_try_acquire_requested(struct nbcon_context *ctxt, ++ struct nbcon_state *cur) ++{ ++ unsigned int cpu = smp_processor_id(); ++ struct console *con = ctxt->console; ++ struct nbcon_state new; ++ ++ /* Note that the caller must still remove the request! */ ++ if (other_cpu_in_panic()) ++ return -EPERM; ++ ++ /* ++ * Note that the waiter will also change if there was an unsafe ++ * hostile takeover. ++ */ ++ if (!nbcon_waiter_matches(cur, ctxt->prio)) ++ return -EPERM; ++ ++ /* If still locked, caller should continue waiting. */ ++ if (cur->prio != NBCON_PRIO_NONE) ++ return -EBUSY; ++ ++ /* ++ * The previous owner should have never released ownership ++ * in an unsafe region. ++ */ ++ WARN_ON_ONCE(cur->unsafe); ++ ++ new.atom = cur->atom; ++ new.prio = ctxt->prio; ++ new.req_prio = NBCON_PRIO_NONE; ++ new.unsafe = cur->unsafe_takeover; ++ new.cpu = cpu; ++ ++ if (!nbcon_state_try_cmpxchg(con, cur, &new)) { ++ /* ++ * The acquire could fail only when it has been taken ++ * over by a higher priority context. ++ */ ++ WARN_ON_ONCE(nbcon_waiter_matches(cur, ctxt->prio)); ++ return -EPERM; ++ } ++ ++ /* Handover success. This context now owns the console. */ ++ return 0; ++} ++ ++/** ++ * nbcon_context_try_acquire_handover - Try to acquire via handover ++ * @ctxt: The context of the caller ++ * @cur: The current console state ++ * ++ * The function must be called only when the context has higher priority ++ * than the current owner and the console is in an unsafe state. ++ * It is the case when nbcon_context_try_acquire_direct() returns -EBUSY. ++ * ++ * The function sets "req_prio" field to make the current owner aware of ++ * the request. Then it waits until the current owner releases the console, ++ * or an even higher context takes over the request, or timeout expires. ++ * ++ * The current owner checks the "req_prio" field on exit from the unsafe ++ * region and releases the console. It does not touch the "req_prio" field ++ * so that the console stays reserved for the waiter. ++ * ++ * Return: 0 on success. Otherwise, an error code on failure. Also @cur ++ * is updated to the latest state when failed to modify it. ++ * ++ * Errors: ++ * ++ * -EPERM: A panic is in progress and this is not the panic CPU. ++ * Or a higher priority context has taken over the ++ * console or the handover request. ++ * ++ * -EBUSY: The current owner is on the same CPU so that the hand ++ * shake could not work. Or the current owner is not ++ * willing to wait (zero timeout). Or the console does ++ * not enter the safe state before timeout passed. The ++ * caller might still use the unsafe hostile takeover ++ * when allowed. ++ * ++ * -EAGAIN: @cur has changed when creating the handover request. ++ * The caller should retry with direct acquire. ++ */ ++static int nbcon_context_try_acquire_handover(struct nbcon_context *ctxt, ++ struct nbcon_state *cur) ++{ ++ unsigned int cpu = smp_processor_id(); ++ struct console *con = ctxt->console; ++ struct nbcon_state new; ++ int timeout; ++ int request_err = -EBUSY; ++ ++ /* ++ * Check that the handover is called when the direct acquire failed ++ * with -EBUSY. ++ */ ++ WARN_ON_ONCE(ctxt->prio <= cur->prio || ctxt->prio <= cur->req_prio); ++ WARN_ON_ONCE(!cur->unsafe); ++ ++ /* Handover is not possible on the same CPU. */ ++ if (cur->cpu == cpu) ++ return -EBUSY; ++ ++ /* ++ * Console stays unsafe after an unsafe takeover until re-initialized. ++ * Waiting is not going to help in this case. ++ */ ++ if (cur->unsafe_takeover) ++ return -EBUSY; ++ ++ /* Is the caller willing to wait? */ ++ if (ctxt->spinwait_max_us == 0) ++ return -EBUSY; ++ ++ /* ++ * Setup a request for the handover. The caller should try to acquire ++ * the console directly when the current state has been modified. ++ */ ++ new.atom = cur->atom; ++ new.req_prio = ctxt->prio; ++ if (!nbcon_state_try_cmpxchg(con, cur, &new)) ++ return -EAGAIN; ++ ++ cur->atom = new.atom; ++ ++ /* Wait until there is no owner and then acquire the console. */ ++ for (timeout = ctxt->spinwait_max_us; timeout >= 0; timeout--) { ++ /* On successful acquire, this request is cleared. */ ++ request_err = nbcon_context_try_acquire_requested(ctxt, cur); ++ if (!request_err) ++ return 0; ++ ++ /* ++ * If the acquire should be aborted, it must be ensured ++ * that the request is removed before returning to caller. ++ */ ++ if (request_err == -EPERM) ++ break; ++ ++ udelay(1); ++ ++ /* Re-read the state because some time has passed. */ ++ nbcon_state_read(con, cur); ++ } ++ ++ /* Timed out or aborted. Carefully remove handover request. */ ++ do { ++ /* ++ * No need to remove request if there is a new waiter. This ++ * can only happen if a higher priority context has taken over ++ * the console or the handover request. ++ */ ++ if (!nbcon_waiter_matches(cur, ctxt->prio)) ++ return -EPERM; ++ ++ /* Unset request for handover. */ ++ new.atom = cur->atom; ++ new.req_prio = NBCON_PRIO_NONE; ++ if (nbcon_state_try_cmpxchg(con, cur, &new)) { ++ /* ++ * Request successfully unset. Report failure of ++ * acquiring via handover. ++ */ ++ cur->atom = new.atom; ++ return request_err; ++ } ++ ++ /* ++ * Unable to remove request. Try to acquire in case ++ * the owner has released the lock. ++ */ ++ } while (nbcon_context_try_acquire_requested(ctxt, cur)); ++ ++ /* Lucky timing. The acquire succeeded while removing the request. */ ++ return 0; ++} ++ ++/** ++ * nbcon_context_try_acquire_hostile - Acquire via unsafe hostile takeover ++ * @ctxt: The context of the caller ++ * @cur: The current console state ++ * ++ * Acquire the console even in the unsafe state. ++ * ++ * It can be permitted by setting the 'allow_unsafe_takeover' field only ++ * by the final attempt to flush messages in panic(). ++ * ++ * Return: 0 on success. -EPERM when not allowed by the context. ++ */ ++static int nbcon_context_try_acquire_hostile(struct nbcon_context *ctxt, ++ struct nbcon_state *cur) ++{ ++ unsigned int cpu = smp_processor_id(); ++ struct console *con = ctxt->console; ++ struct nbcon_state new; ++ ++ if (!ctxt->allow_unsafe_takeover) ++ return -EPERM; ++ ++ /* Ensure caller is allowed to perform unsafe hostile takeovers. */ ++ if (WARN_ON_ONCE(ctxt->prio != NBCON_PRIO_PANIC)) ++ return -EPERM; ++ ++ /* ++ * Check that try_acquire_direct() and try_acquire_handover() returned ++ * -EBUSY in the right situation. ++ */ ++ WARN_ON_ONCE(ctxt->prio <= cur->prio || ctxt->prio <= cur->req_prio); ++ WARN_ON_ONCE(cur->unsafe != true); ++ ++ do { ++ new.atom = cur->atom; ++ new.cpu = cpu; ++ new.prio = ctxt->prio; ++ new.unsafe |= cur->unsafe_takeover; ++ new.unsafe_takeover |= cur->unsafe; ++ ++ } while (!nbcon_state_try_cmpxchg(con, cur, &new)); ++ ++ return 0; ++} ++ ++static struct printk_buffers panic_nbcon_pbufs; ++ ++/** ++ * nbcon_context_try_acquire - Try to acquire nbcon console ++ * @ctxt: The context of the caller ++ * ++ * Context: Any context which could not be migrated to another CPU. ++ * Return: True if the console was acquired. False otherwise. ++ * ++ * If the caller allowed an unsafe hostile takeover, on success the ++ * caller should check the current console state to see if it is ++ * in an unsafe state. Otherwise, on success the caller may assume ++ * the console is not in an unsafe state. ++ */ ++static bool nbcon_context_try_acquire(struct nbcon_context *ctxt) ++{ ++ unsigned int cpu = smp_processor_id(); ++ struct console *con = ctxt->console; ++ struct nbcon_state cur; ++ int err; ++ ++ nbcon_state_read(con, &cur); ++try_again: ++ err = nbcon_context_try_acquire_direct(ctxt, &cur); ++ if (err != -EBUSY) ++ goto out; ++ ++ err = nbcon_context_try_acquire_handover(ctxt, &cur); ++ if (err == -EAGAIN) ++ goto try_again; ++ if (err != -EBUSY) ++ goto out; ++ ++ err = nbcon_context_try_acquire_hostile(ctxt, &cur); ++out: ++ if (err) ++ return false; ++ ++ /* Acquire succeeded. */ ++ ++ /* Assign the appropriate buffer for this context. */ ++ if (atomic_read(&panic_cpu) == cpu) ++ ctxt->pbufs = &panic_nbcon_pbufs; ++ else ++ ctxt->pbufs = con->pbufs; ++ ++ /* Set the record sequence for this context to print. */ ++ ctxt->seq = nbcon_seq_read(ctxt->console); ++ ++ return true; ++} ++ ++static bool nbcon_owner_matches(struct nbcon_state *cur, int expected_cpu, ++ int expected_prio) ++{ ++ /* ++ * Since consoles can only be acquired by higher priorities, ++ * owning contexts are uniquely identified by @prio. However, ++ * since contexts can unexpectedly lose ownership, it is ++ * possible that later another owner appears with the same ++ * priority. For this reason @cpu is also needed. ++ */ ++ ++ if (cur->prio != expected_prio) ++ return false; ++ ++ if (cur->cpu != expected_cpu) ++ return false; ++ ++ return true; ++} ++ ++/** ++ * nbcon_context_release - Release the console ++ * @ctxt: The nbcon context from nbcon_context_try_acquire() ++ */ ++static void nbcon_context_release(struct nbcon_context *ctxt) ++{ ++ unsigned int cpu = smp_processor_id(); ++ struct console *con = ctxt->console; ++ struct nbcon_state cur; ++ struct nbcon_state new; ++ ++ nbcon_state_read(con, &cur); ++ ++ do { ++ if (!nbcon_owner_matches(&cur, cpu, ctxt->prio)) ++ break; ++ ++ new.atom = cur.atom; ++ new.prio = NBCON_PRIO_NONE; ++ ++ /* ++ * If @unsafe_takeover is set, it is kept set so that ++ * the state remains permanently unsafe. ++ */ ++ new.unsafe |= cur.unsafe_takeover; ++ ++ } while (!nbcon_state_try_cmpxchg(con, &cur, &new)); ++ ++ ctxt->pbufs = NULL; ++} ++ ++/** ++ * nbcon_context_can_proceed - Check whether ownership can proceed ++ * @ctxt: The nbcon context from nbcon_context_try_acquire() ++ * @cur: The current console state ++ * ++ * Return: True if this context still owns the console. False if ++ * ownership was handed over or taken. ++ * ++ * Must be invoked when entering the unsafe state to make sure that it still ++ * owns the lock. Also must be invoked when exiting the unsafe context ++ * to eventually free the lock for a higher priority context which asked ++ * for the friendly handover. ++ * ++ * It can be called inside an unsafe section when the console is just ++ * temporary in safe state instead of exiting and entering the unsafe ++ * state. ++ * ++ * Also it can be called in the safe context before doing an expensive ++ * safe operation. It does not make sense to do the operation when ++ * a higher priority context took the lock. ++ * ++ * When this function returns false then the calling context no longer owns ++ * the console and is no longer allowed to go forward. In this case it must ++ * back out immediately and carefully. The buffer content is also no longer ++ * trusted since it no longer belongs to the calling context. ++ */ ++static bool nbcon_context_can_proceed(struct nbcon_context *ctxt, struct nbcon_state *cur) ++{ ++ unsigned int cpu = smp_processor_id(); ++ ++ /* Make sure this context still owns the console. */ ++ if (!nbcon_owner_matches(cur, cpu, ctxt->prio)) ++ return false; ++ ++ /* The console owner can proceed if there is no waiter. */ ++ if (cur->req_prio == NBCON_PRIO_NONE) ++ return true; ++ ++ /* ++ * A console owner within an unsafe region is always allowed to ++ * proceed, even if there are waiters. It can perform a handover ++ * when exiting the unsafe region. Otherwise the waiter will ++ * need to perform an unsafe hostile takeover. ++ */ ++ if (cur->unsafe) ++ return true; ++ ++ /* Waiters always have higher priorities than owners. */ ++ WARN_ON_ONCE(cur->req_prio <= cur->prio); ++ ++ /* ++ * Having a safe point for take over and eventually a few ++ * duplicated characters or a full line is way better than a ++ * hostile takeover. Post processing can take care of the garbage. ++ * Release and hand over. ++ */ ++ nbcon_context_release(ctxt); ++ ++ /* ++ * It is not clear whether the waiter really took over ownership. The ++ * outermost callsite must make the final decision whether console ++ * ownership is needed for it to proceed. If yes, it must reacquire ++ * ownership (possibly hostile) before carefully proceeding. ++ * ++ * The calling context no longer owns the console so go back all the ++ * way instead of trying to implement reacquire heuristics in tons of ++ * places. ++ */ ++ return false; ++} ++ ++/** ++ * nbcon_can_proceed - Check whether ownership can proceed ++ * @wctxt: The write context that was handed to the write function ++ * ++ * Return: True if this context still owns the console. False if ++ * ownership was handed over or taken. ++ * ++ * It is used in nbcon_enter_unsafe() to make sure that it still owns the ++ * lock. Also it is used in nbcon_exit_unsafe() to eventually free the lock ++ * for a higher priority context which asked for the friendly handover. ++ * ++ * It can be called inside an unsafe section when the console is just ++ * temporary in safe state instead of exiting and entering the unsafe state. ++ * ++ * Also it can be called in the safe context before doing an expensive safe ++ * operation. It does not make sense to do the operation when a higher ++ * priority context took the lock. ++ * ++ * When this function returns false then the calling context no longer owns ++ * the console and is no longer allowed to go forward. In this case it must ++ * back out immediately and carefully. The buffer content is also no longer ++ * trusted since it no longer belongs to the calling context. ++ */ ++bool nbcon_can_proceed(struct nbcon_write_context *wctxt) ++{ ++ struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); ++ struct console *con = ctxt->console; ++ struct nbcon_state cur; ++ ++ nbcon_state_read(con, &cur); ++ ++ return nbcon_context_can_proceed(ctxt, &cur); ++} ++EXPORT_SYMBOL_GPL(nbcon_can_proceed); ++ ++#define nbcon_context_enter_unsafe(c) __nbcon_context_update_unsafe(c, true) ++#define nbcon_context_exit_unsafe(c) __nbcon_context_update_unsafe(c, false) ++ ++/** ++ * __nbcon_context_update_unsafe - Update the unsafe bit in @con->nbcon_state ++ * @ctxt: The nbcon context from nbcon_context_try_acquire() ++ * @unsafe: The new value for the unsafe bit ++ * ++ * Return: True if the unsafe state was updated and this context still ++ * owns the console. Otherwise false if ownership was handed ++ * over or taken. ++ * ++ * This function allows console owners to modify the unsafe status of the ++ * console. ++ * ++ * When this function returns false then the calling context no longer owns ++ * the console and is no longer allowed to go forward. In this case it must ++ * back out immediately and carefully. The buffer content is also no longer ++ * trusted since it no longer belongs to the calling context. ++ * ++ * Internal helper to avoid duplicated code. ++ */ ++static bool __nbcon_context_update_unsafe(struct nbcon_context *ctxt, bool unsafe) ++{ ++ struct console *con = ctxt->console; ++ struct nbcon_state cur; ++ struct nbcon_state new; ++ ++ nbcon_state_read(con, &cur); ++ ++ do { ++ /* ++ * The unsafe bit must not be cleared if an ++ * unsafe hostile takeover has occurred. ++ */ ++ if (!unsafe && cur.unsafe_takeover) ++ goto out; ++ ++ if (!nbcon_context_can_proceed(ctxt, &cur)) ++ return false; ++ ++ new.atom = cur.atom; ++ new.unsafe = unsafe; ++ } while (!nbcon_state_try_cmpxchg(con, &cur, &new)); ++ ++ cur.atom = new.atom; ++out: ++ return nbcon_context_can_proceed(ctxt, &cur); ++} ++ ++/** ++ * nbcon_enter_unsafe - Enter an unsafe region in the driver ++ * @wctxt: The write context that was handed to the write function ++ * ++ * Return: True if this context still owns the console. False if ++ * ownership was handed over or taken. ++ * ++ * When this function returns false then the calling context no longer owns ++ * the console and is no longer allowed to go forward. In this case it must ++ * back out immediately and carefully. The buffer content is also no longer ++ * trusted since it no longer belongs to the calling context. ++ */ ++bool nbcon_enter_unsafe(struct nbcon_write_context *wctxt) ++{ ++ struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); ++ ++ return nbcon_context_enter_unsafe(ctxt); ++} ++EXPORT_SYMBOL_GPL(nbcon_enter_unsafe); ++ ++/** ++ * nbcon_exit_unsafe - Exit an unsafe region in the driver ++ * @wctxt: The write context that was handed to the write function ++ * ++ * Return: True if this context still owns the console. False if ++ * ownership was handed over or taken. ++ * ++ * When this function returns false then the calling context no longer owns ++ * the console and is no longer allowed to go forward. In this case it must ++ * back out immediately and carefully. The buffer content is also no longer ++ * trusted since it no longer belongs to the calling context. ++ */ ++bool nbcon_exit_unsafe(struct nbcon_write_context *wctxt) ++{ ++ struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); ++ ++ return nbcon_context_exit_unsafe(ctxt); ++} ++EXPORT_SYMBOL_GPL(nbcon_exit_unsafe); ++ ++/** ++ * nbcon_reacquire - Reacquire a console after losing ownership ++ * @wctxt: The write context that was handed to the write function ++ * ++ * Since ownership can be lost at any time due to handover or takeover, a ++ * printing context _should_ be prepared to back out immediately and ++ * carefully. However, there are many scenarios where the context _must_ ++ * reacquire ownership in order to finalize or revert hardware changes. ++ * ++ * This function allows a context to reacquire ownership using the same ++ * priority as its previous ownership. ++ * ++ * Note that for printing contexts, after a successful reacquire the ++ * context will have no output buffer because that has been lost. This ++ * function cannot be used to resume printing. ++ */ ++void nbcon_reacquire(struct nbcon_write_context *wctxt) ++{ ++ struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); ++ struct console *con = ctxt->console; ++ struct nbcon_state cur; ++ ++ while (!nbcon_context_try_acquire(ctxt)) ++ cpu_relax(); ++ ++ wctxt->outbuf = NULL; ++ wctxt->len = 0; ++ nbcon_state_read(con, &cur); ++ wctxt->unsafe_takeover = cur.unsafe_takeover; ++} ++EXPORT_SYMBOL_GPL(nbcon_reacquire); ++ ++/** ++ * nbcon_emit_next_record - Emit a record in the acquired context ++ * @wctxt: The write context that will be handed to the write function ++ * @use_atomic: True if the write_atomic callback is to be used ++ * ++ * Return: True if this context still owns the console. False if ++ * ownership was handed over or taken. ++ * ++ * When this function returns false then the calling context no longer owns ++ * the console and is no longer allowed to go forward. In this case it must ++ * back out immediately and carefully. The buffer content is also no longer ++ * trusted since it no longer belongs to the calling context. If the caller ++ * wants to do more it must reacquire the console first. ++ * ++ * When true is returned, @wctxt->ctxt.backlog indicates whether there are ++ * still records pending in the ringbuffer, ++ */ ++static bool nbcon_emit_next_record(struct nbcon_write_context *wctxt, bool use_atomic) ++{ ++ struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); ++ struct console *con = ctxt->console; ++ bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED; ++ struct printk_message pmsg = { ++ .pbufs = ctxt->pbufs, ++ }; ++ unsigned long con_dropped; ++ struct nbcon_state cur; ++ unsigned long dropped; ++ bool done = false; ++ ++ /* ++ * The printk buffers are filled within an unsafe section. This ++ * prevents NBCON_PRIO_NORMAL and NBCON_PRIO_EMERGENCY from ++ * clobbering each other. ++ */ ++ ++ if (!nbcon_context_enter_unsafe(ctxt)) ++ return false; ++ ++ ctxt->backlog = printk_get_next_message(&pmsg, ctxt->seq, is_extended, true); ++ if (!ctxt->backlog) ++ return nbcon_context_exit_unsafe(ctxt); ++ ++ /* ++ * @con->dropped is not protected in case of an unsafe hostile ++ * takeover. In that situation the update can be racy so ++ * annotate it accordingly. ++ */ ++ con_dropped = data_race(READ_ONCE(con->dropped)); ++ ++ dropped = con_dropped + pmsg.dropped; ++ if (dropped && !is_extended) ++ console_prepend_dropped(&pmsg, dropped); ++ ++ if (!nbcon_context_exit_unsafe(ctxt)) ++ return false; ++ ++ /* For skipped records just update seq/dropped in @con. */ ++ if (pmsg.outbuf_len == 0) ++ goto update_con; ++ ++ /* Initialize the write context for driver callbacks. */ ++ wctxt->outbuf = &pmsg.pbufs->outbuf[0]; ++ wctxt->len = pmsg.outbuf_len; ++ nbcon_state_read(con, &cur); ++ wctxt->unsafe_takeover = cur.unsafe_takeover; ++ ++ if (use_atomic && ++ con->write_atomic) { ++ done = con->write_atomic(con, wctxt); ++ ++ } else if (!use_atomic && ++ con->write_thread && ++ con->kthread) { ++ WARN_ON_ONCE(con->kthread != current); ++ done = con->write_thread(con, wctxt); ++ } ++ ++ if (!done) { ++ /* ++ * The emit was aborted, probably due to a loss of ownership. ++ * Ensure ownership was lost or released before reporting the ++ * loss. ++ */ ++ nbcon_context_release(ctxt); ++ return false; ++ } ++ ++ /* ++ * Since any dropped message was successfully output, reset the ++ * dropped count for the console. ++ */ ++ dropped = 0; ++update_con: ++ /* ++ * The dropped count and the sequence number are updated within an ++ * unsafe section. This limits update races to the panic context and ++ * allows the panic context to win. ++ */ ++ ++ if (!nbcon_context_enter_unsafe(ctxt)) ++ return false; ++ ++ if (dropped != con_dropped) { ++ /* Counterpart to the READ_ONCE() above. */ ++ WRITE_ONCE(con->dropped, dropped); ++ } ++ ++ nbcon_seq_try_update(ctxt, pmsg.seq + 1); ++ ++ return nbcon_context_exit_unsafe(ctxt); ++} ++ ++/** ++ * nbcon_kthread_should_wakeup - Check whether a printer thread should wakeup ++ * @con: Console to operate on ++ * @ctxt: The acquire context that contains the state ++ * at console_acquire() ++ * ++ * Return: True if the thread should shutdown or if the console is ++ * allowed to print and a record is available. False otherwise. ++ * ++ * After the thread wakes up, it must first check if it should shutdown before ++ * attempting any printing. ++ */ ++static bool nbcon_kthread_should_wakeup(struct console *con, struct nbcon_context *ctxt) ++{ ++ bool is_usable; ++ short flags; ++ int cookie; ++ ++ if (kthread_should_stop()) ++ return true; ++ ++ cookie = console_srcu_read_lock(); ++ flags = console_srcu_read_flags(con); ++ is_usable = console_is_usable(con, flags, false); ++ console_srcu_read_unlock(cookie); ++ ++ if (!is_usable) ++ return false; ++ ++ /* Bring the sequence in @ctxt up to date */ ++ ctxt->seq = nbcon_seq_read(con); ++ ++ return prb_read_valid(prb, ctxt->seq, NULL); ++} ++ ++/** ++ * nbcon_kthread_func - The printer thread function ++ * @__console: Console to operate on ++ */ ++static int nbcon_kthread_func(void *__console) ++{ ++ struct console *con = __console; ++ struct nbcon_write_context wctxt = { ++ .ctxt.console = con, ++ .ctxt.prio = NBCON_PRIO_NORMAL, ++ }; ++ struct nbcon_context *ctxt = &ACCESS_PRIVATE(&wctxt, ctxt); ++ unsigned long flags; ++ short con_flags; ++ bool backlog; ++ int cookie; ++ int ret; ++ ++wait_for_event: ++ /* ++ * Guarantee this task is visible on the rcuwait before ++ * checking the wake condition. ++ * ++ * The full memory barrier within set_current_state() of ++ * ___rcuwait_wait_event() pairs with the full memory ++ * barrier within rcuwait_has_sleeper(). ++ * ++ * This pairs with rcuwait_has_sleeper:A and nbcon_kthread_wake:A. ++ */ ++ ret = rcuwait_wait_event(&con->rcuwait, ++ nbcon_kthread_should_wakeup(con, ctxt), ++ TASK_INTERRUPTIBLE); /* LMM(nbcon_kthread_func:A) */ ++ ++ if (kthread_should_stop()) ++ return 0; ++ ++ /* Wait was interrupted by a spurious signal, go back to sleep. */ ++ if (ret) ++ goto wait_for_event; ++ ++ do { ++ backlog = false; ++ ++ cookie = console_srcu_read_lock(); ++ ++ con_flags = console_srcu_read_flags(con); ++ ++ if (console_is_usable(con, con_flags, false)) { ++ con->driver_enter(con, &flags); ++ ++ /* ++ * Ensure this stays on the CPU to make handover and ++ * takeover possible. ++ */ ++ cant_migrate(); ++ ++ if (nbcon_context_try_acquire(ctxt)) { ++ /* ++ * If the emit fails, this context is no ++ * longer the owner. ++ */ ++ if (nbcon_emit_next_record(&wctxt, false)) { ++ nbcon_context_release(ctxt); ++ backlog = ctxt->backlog; ++ } ++ } ++ ++ con->driver_exit(con, flags); ++ } ++ ++ console_srcu_read_unlock(cookie); ++ cond_resched(); ++ ++ } while (backlog); ++ ++ goto wait_for_event; ++} ++ ++/** ++ * nbcon_irq_work - irq work to wake printk thread ++ * @irq_work: The irq work to operate on ++ */ ++static void nbcon_irq_work(struct irq_work *irq_work) ++{ ++ struct console *con = container_of(irq_work, struct console, irq_work); ++ ++ nbcon_kthread_wake(con); ++} ++ ++static inline bool rcuwait_has_sleeper(struct rcuwait *w) ++{ ++ bool has_sleeper; ++ ++ rcu_read_lock(); ++ /* ++ * Guarantee any new records can be seen by tasks preparing to wait ++ * before this context checks if the rcuwait is empty. ++ * ++ * This full memory barrier pairs with the full memory barrier within ++ * set_current_state() of ___rcuwait_wait_event(), which is called ++ * after prepare_to_rcuwait() adds the waiter but before it has ++ * checked the wait condition. ++ * ++ * This pairs with nbcon_kthread_func:A. ++ */ ++ smp_mb(); /* LMM(rcuwait_has_sleeper:A) */ ++ has_sleeper = !!rcu_dereference(w->task); ++ rcu_read_unlock(); ++ ++ return has_sleeper; ++} ++ ++/** ++ * nbcon_wake_threads - Wake up printing threads using irq_work ++ */ ++void nbcon_wake_threads(void) ++{ ++ struct console *con; ++ int cookie; ++ ++ cookie = console_srcu_read_lock(); ++ for_each_console_srcu(con) { ++ /* ++ * Only schedule irq_work if the printing thread is ++ * actively waiting. If not waiting, the thread will ++ * notice by itself that it has work to do. ++ */ ++ if (con->kthread && rcuwait_has_sleeper(&con->rcuwait)) ++ irq_work_queue(&con->irq_work); ++ } ++ console_srcu_read_unlock(cookie); ++} ++ ++/* Track the nbcon emergency nesting per CPU. */ ++static DEFINE_PER_CPU(unsigned int, nbcon_pcpu_emergency_nesting); ++static unsigned int early_nbcon_pcpu_emergency_nesting __initdata; ++ ++/** ++ * nbcon_get_cpu_emergency_nesting - Get the per CPU emergency nesting pointer ++ * ++ * Return: Either a pointer to the per CPU emergency nesting counter of ++ * the current CPU or to the init data during early boot. ++ */ ++static __ref unsigned int *nbcon_get_cpu_emergency_nesting(void) ++{ ++ /* ++ * The value of __printk_percpu_data_ready gets set in normal ++ * context and before SMP initialization. As a result it could ++ * never change while inside an nbcon emergency section. ++ */ ++ if (!printk_percpu_data_ready()) ++ return &early_nbcon_pcpu_emergency_nesting; ++ ++ return this_cpu_ptr(&nbcon_pcpu_emergency_nesting); ++} ++ ++/** ++ * nbcon_atomic_emit_one - Print one record for an nbcon console using the ++ * write_atomic() callback ++ * @wctxt: An initialized write context struct to use ++ * for this context ++ * ++ * Return: False if the given console could not print a record or there ++ * are no more records to print, otherwise true. ++ * ++ * This is an internal helper to handle the locking of the console before ++ * calling nbcon_emit_next_record(). ++ */ ++static bool nbcon_atomic_emit_one(struct nbcon_write_context *wctxt) ++{ ++ struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); ++ ++ if (!nbcon_context_try_acquire(ctxt)) ++ return false; ++ ++ /* ++ * nbcon_emit_next_record() returns false when the console was ++ * handed over or taken over. In both cases the context is no ++ * longer valid. ++ */ ++ if (!nbcon_emit_next_record(wctxt, true)) ++ return false; ++ ++ nbcon_context_release(ctxt); ++ ++ return ctxt->backlog; ++} ++ ++/** ++ * nbcon_get_default_prio - The appropriate nbcon priority to use for nbcon ++ * printing on the current CPU ++ * ++ * Context: Any context which could not be migrated to another CPU. ++ * Return: The nbcon_prio to use for acquiring an nbcon console in this ++ * context for printing. ++ */ ++enum nbcon_prio nbcon_get_default_prio(void) ++{ ++ unsigned int *cpu_emergency_nesting; ++ ++ if (this_cpu_in_panic()) ++ return NBCON_PRIO_PANIC; ++ ++ cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting(); ++ if (*cpu_emergency_nesting) ++ return NBCON_PRIO_EMERGENCY; ++ ++ return NBCON_PRIO_NORMAL; ++} ++ ++/** ++ * nbcon_atomic_emit_next_record - Print one record for an nbcon console ++ * using the write_atomic() callback ++ * @con: The console to print on ++ * @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 ++ * both the console_lock and the SRCU read lock. Otherwise it ++ * is set to false. ++ * @cookie: The cookie from the SRCU read lock. ++ * ++ * Context: Any context which could not be migrated to another CPU. ++ * Return: True if a record could be printed, otherwise false. ++ * ++ * This function is meant to be called by console_flush_all() to print records ++ * on nbcon consoles using the write_atomic() callback. Essentially it is the ++ * nbcon version of console_emit_next_record(). ++ */ ++bool nbcon_atomic_emit_next_record(struct console *con, bool *handover, int cookie) ++{ ++ struct nbcon_write_context wctxt = { }; ++ struct nbcon_context *ctxt = &ACCESS_PRIVATE(&wctxt, ctxt); ++ unsigned long driver_flags; ++ bool progress = false; ++ unsigned long flags; ++ ++ *handover = false; ++ ++ /* Use the same locking order as console_emit_next_record(). */ ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) { ++ printk_safe_enter_irqsave(flags); ++ console_lock_spinning_enable(); ++ stop_critical_timings(); ++ } ++ ++ con->driver_enter(con, &driver_flags); ++ cant_migrate(); ++ ++ ctxt->console = con; ++ ctxt->prio = nbcon_get_default_prio(); ++ ++ progress = nbcon_atomic_emit_one(&wctxt); ++ ++ con->driver_exit(con, driver_flags); ++ ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) { ++ start_critical_timings(); ++ *handover = console_lock_spinning_disable_and_check(cookie); ++ printk_safe_exit_irqrestore(flags); ++ } ++ ++ return progress; ++} ++ ++/** ++ * __nbcon_atomic_flush_all - Flush all nbcon consoles using their ++ * write_atomic() callback ++ * @stop_seq: Flush up until this record ++ * @allow_unsafe_takeover: True, to allow unsafe hostile takeovers ++ */ ++static void __nbcon_atomic_flush_all(u64 stop_seq, bool allow_unsafe_takeover) ++{ ++ struct nbcon_write_context wctxt = { }; ++ struct nbcon_context *ctxt = &ACCESS_PRIVATE(&wctxt, ctxt); ++ struct console *con; ++ bool any_progress; ++ int cookie; ++ ++ do { ++ any_progress = false; ++ ++ cookie = console_srcu_read_lock(); ++ for_each_console_srcu(con) { ++ short flags = console_srcu_read_flags(con); ++ unsigned long irq_flags; ++ ++ if (!(flags & CON_NBCON)) ++ continue; ++ ++ if (!console_is_usable(con, flags, true)) ++ continue; ++ ++ if (nbcon_seq_read(con) >= stop_seq) ++ continue; ++ ++ memset(ctxt, 0, sizeof(*ctxt)); ++ ctxt->console = con; ++ ctxt->spinwait_max_us = 2000; ++ ctxt->allow_unsafe_takeover = allow_unsafe_takeover; ++ ++ /* ++ * Atomic flushing does not use console driver ++ * synchronization (i.e. it does not hold the port ++ * lock for uart consoles). Therefore IRQs must be ++ * disabled to avoid being interrupted and then ++ * calling into a driver that will deadlock trying ++ * acquire console ownership. ++ * ++ * This also disables migration in order to get the ++ * current CPU priority. ++ */ ++ local_irq_save(irq_flags); ++ ++ ctxt->prio = nbcon_get_default_prio(); ++ ++ any_progress |= nbcon_atomic_emit_one(&wctxt); ++ ++ local_irq_restore(irq_flags); ++ } ++ console_srcu_read_unlock(cookie); ++ } while (any_progress); ++} ++ ++/** ++ * nbcon_atomic_flush_all - Flush all nbcon consoles using their ++ * write_atomic() callback ++ * ++ * Flush the backlog up through the currently newest record. Any new ++ * records added while flushing will not be flushed. This is to avoid ++ * one CPU printing unbounded because other CPUs continue to add records. ++ */ ++void nbcon_atomic_flush_all(void) ++{ ++ __nbcon_atomic_flush_all(prb_next_reserve_seq(prb), false); ++} ++ ++/** ++ * nbcon_atomic_flush_unsafe - Flush all nbcon consoles using their ++ * write_atomic() callback and allowing unsafe hostile takeovers ++ * ++ * Flush the backlog up through the currently newest record. Unsafe hostile ++ * takeovers will be performed, if necessary. ++ */ ++void nbcon_atomic_flush_unsafe(void) ++{ ++ __nbcon_atomic_flush_all(prb_next_reserve_seq(prb), true); ++} ++ ++/** ++ * nbcon_cpu_emergency_enter - Enter an emergency section where printk() ++ * messages for that CPU are only stored ++ * ++ * Upon exiting the emergency section, all stored messages are flushed. ++ * ++ * Context: Any context. Disables preemption. ++ * ++ * When within an emergency section, no printing occurs on that CPU. This ++ * is to allow all emergency messages to be dumped into the ringbuffer before ++ * flushing the ringbuffer. The actual printing occurs when exiting the ++ * outermost emergency section. ++ */ ++void nbcon_cpu_emergency_enter(void) ++{ ++ unsigned int *cpu_emergency_nesting; ++ ++ preempt_disable(); ++ ++ cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting(); ++ (*cpu_emergency_nesting)++; ++} ++ ++/** ++ * nbcon_cpu_emergency_exit - Exit an emergency section and flush the ++ * stored messages ++ * ++ * Flushing only occurs when exiting all nesting for the CPU. ++ * ++ * Context: Any context. Enables preemption. ++ */ ++void nbcon_cpu_emergency_exit(void) ++{ ++ unsigned int *cpu_emergency_nesting; ++ bool do_trigger_flush = false; ++ ++ cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting(); ++ ++ WARN_ON_ONCE(*cpu_emergency_nesting == 0); ++ ++ if (*cpu_emergency_nesting == 1) ++ do_trigger_flush = true; ++ ++ /* Undo the nesting count of nbcon_cpu_emergency_enter(). */ ++ (*cpu_emergency_nesting)--; ++ ++ preempt_enable(); ++ ++ if (do_trigger_flush) ++ printk_trigger_flush(); ++} ++ ++/** ++ * nbcon_kthread_stop - Stop a printer thread ++ * @con: Console to operate on ++ */ ++static void nbcon_kthread_stop(struct console *con) ++{ ++ lockdep_assert_console_list_lock_held(); ++ ++ if (!con->kthread) ++ return; ++ ++ kthread_stop(con->kthread); ++ con->kthread = NULL; ++} ++ ++/** ++ * nbcon_kthread_create - Create a printer thread ++ * @con: Console to operate on ++ * ++ * If it fails, let the console proceed. The atomic part might ++ * be usable and useful. ++ */ ++void nbcon_kthread_create(struct console *con) ++{ ++ struct task_struct *kt; ++ ++ lockdep_assert_console_list_lock_held(); ++ ++ if (!(con->flags & CON_NBCON) || !con->write_thread) ++ return; ++ ++ if (!printk_threads_enabled || con->kthread) ++ return; ++ ++ /* ++ * Printer threads cannot be started as long as any boot console is ++ * registered because there is no way to synchronize the hardware ++ * registers between boot console code and regular console code. ++ */ ++ if (have_boot_console) ++ return; ++ ++ kt = kthread_run(nbcon_kthread_func, con, "pr/%s%d", con->name, con->index); ++ if (IS_ERR(kt)) { ++ con_printk(KERN_ERR, con, "failed to start printing thread\n"); ++ return; ++ } ++ ++ con->kthread = kt; ++ ++ /* ++ * It is important that console printing threads are scheduled ++ * shortly after a printk call and with generous runtime budgets. ++ */ ++ sched_set_normal(con->kthread, -20); ++} ++ ++static int __init printk_setup_threads(void) ++{ ++ struct console *con; ++ ++ console_list_lock(); ++ printk_threads_enabled = true; ++ for_each_console(con) ++ nbcon_kthread_create(con); ++ if (IS_ENABLED(CONFIG_PREEMPT_RT) && printing_via_unlock) ++ nbcon_legacy_kthread_create(); ++ console_list_unlock(); ++ return 0; ++} ++early_initcall(printk_setup_threads); ++ ++/** ++ * nbcon_alloc - Allocate buffers needed by the nbcon console ++ * @con: Console to allocate buffers for ++ * ++ * Return: True on success. False otherwise and the console cannot ++ * be used. ++ * ++ * This is not part of nbcon_init() because buffer allocation must ++ * be performed earlier in the console registration process. ++ */ ++bool nbcon_alloc(struct console *con) ++{ ++ if (con->flags & CON_BOOT) { ++ /* ++ * Boot console printing is synchronized with legacy console ++ * printing, so boot consoles can share the same global printk ++ * buffers. ++ */ ++ con->pbufs = &printk_shared_pbufs; ++ } else { ++ con->pbufs = kmalloc(sizeof(*con->pbufs), GFP_KERNEL); ++ if (!con->pbufs) { ++ con_printk(KERN_ERR, con, "failed to allocate printing buffer\n"); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/** ++ * nbcon_init - Initialize the nbcon console specific data ++ * @con: Console to initialize ++ * ++ * nbcon_alloc() *must* be called and succeed before this function ++ * is called. ++ * ++ * This function expects that the legacy @con->seq has been set. ++ */ ++void nbcon_init(struct console *con) ++{ ++ struct nbcon_state state = { }; ++ ++ /* nbcon_alloc() must have been called and successful! */ ++ BUG_ON(!con->pbufs); ++ ++ rcuwait_init(&con->rcuwait); ++ init_irq_work(&con->irq_work, nbcon_irq_work); ++ nbcon_seq_force(con, con->seq); ++ nbcon_state_set(con, &state); ++ nbcon_kthread_create(con); ++} ++ ++/** ++ * nbcon_free - Free and cleanup the nbcon console specific data ++ * @con: Console to free/cleanup nbcon data ++ */ ++void nbcon_free(struct console *con) ++{ ++ struct nbcon_state state = { }; ++ ++ nbcon_kthread_stop(con); ++ nbcon_state_set(con, &state); ++ ++ /* Boot consoles share global printk buffers. */ ++ if (!(con->flags & CON_BOOT)) ++ kfree(con->pbufs); ++ ++ con->pbufs = NULL; ++} ++ ++static inline bool uart_is_nbcon(struct uart_port *up) ++{ ++ int cookie; ++ bool ret; ++ ++ if (!uart_console(up)) ++ return false; ++ ++ cookie = console_srcu_read_lock(); ++ ret = (console_srcu_read_flags(up->cons) & CON_NBCON); ++ console_srcu_read_unlock(cookie); ++ return ret; ++} ++ ++/** ++ * nbcon_acquire - The second half of the port locking wrapper ++ * @up: The uart port whose @lock was locked ++ * ++ * The uart_port_lock() wrappers will first lock the spin_lock @up->lock. ++ * Then this function is called to implement nbcon-specific processing. ++ * ++ * If @up is an nbcon console, this console will be acquired and marked as ++ * unsafe. Otherwise this function does nothing. ++ * ++ * nbcon consoles acquired via the port lock wrapper always use priority ++ * NBCON_PRIO_NORMAL. ++ */ ++void nbcon_acquire(struct uart_port *up) ++{ ++ struct console *con = up->cons; ++ struct nbcon_context ctxt; ++ ++ if (!uart_is_nbcon(up)) ++ return; ++ ++ WARN_ON_ONCE(up->nbcon_locked_port); ++ ++ do { ++ do { ++ memset(&ctxt, 0, sizeof(ctxt)); ++ ctxt.console = con; ++ ctxt.prio = NBCON_PRIO_NORMAL; ++ } while (!nbcon_context_try_acquire(&ctxt)); ++ ++ } while (!nbcon_context_enter_unsafe(&ctxt)); ++ ++ up->nbcon_locked_port = true; ++} ++EXPORT_SYMBOL_GPL(nbcon_acquire); ++ ++/** ++ * nbcon_release - The first half of the port unlocking wrapper ++ * @up: The uart port whose @lock is about to be unlocked ++ * ++ * The uart_port_unlock() wrappers will first call this function to implement ++ * nbcon-specific processing. Then afterwards the uart_port_unlock() wrappers ++ * will unlock the spin_lock @up->lock. ++ * ++ * If @up is an nbcon console, the console will be marked as safe and ++ * released. Otherwise this function does nothing. ++ * ++ * nbcon consoles acquired via the port lock wrapper always use priority ++ * NBCON_PRIO_NORMAL. ++ */ ++void nbcon_release(struct uart_port *up) ++{ ++ struct console *con = up->cons; ++ struct nbcon_context ctxt = { ++ .console = con, ++ .prio = NBCON_PRIO_NORMAL, ++ }; ++ ++ if (!up->nbcon_locked_port) ++ return; ++ ++ if (nbcon_context_exit_unsafe(&ctxt)) ++ nbcon_context_release(&ctxt); ++ ++ up->nbcon_locked_port = false; ++} ++EXPORT_SYMBOL_GPL(nbcon_release); ++ ++/** ++ * printk_kthread_shutdown - shutdown all threaded printers ++ * ++ * On system shutdown all threaded printers are stopped. This allows printk ++ * to transition back to atomic printing, thus providing a robust mechanism ++ * for the final shutdown/reboot messages to be output. ++ */ ++static void printk_kthread_shutdown(void) ++{ ++ struct console *con; ++ ++ console_list_lock(); ++ for_each_console(con) { ++ if (con->flags & CON_NBCON) ++ nbcon_kthread_stop(con); ++ } ++ console_list_unlock(); ++} ++ ++static struct syscore_ops printk_syscore_ops = { ++ .shutdown = printk_kthread_shutdown, ++}; ++ ++static int __init printk_init_ops(void) ++{ ++ register_syscore_ops(&printk_syscore_ops); ++ return 0; ++} ++device_initcall(printk_init_ops); +diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c +index 0fca282c0a25..249576a8c351 100644 +--- a/kernel/printk/printk.c ++++ b/kernel/printk/printk.c +@@ -102,12 +102,6 @@ DEFINE_STATIC_SRCU(console_srcu); + */ + int __read_mostly suppress_printk; + +-/* +- * During panic, heavy printk by other CPUs can delay the +- * panic and risk deadlock on console resources. +- */ +-static int __read_mostly suppress_panic_printk; +- + #ifdef CONFIG_LOCKDEP + static struct lockdep_map console_lock_dep_map = { + .name = "console_lock" +@@ -288,6 +282,7 @@ EXPORT_SYMBOL(console_list_unlock); + * Return: A cookie to pass to console_srcu_read_unlock(). + */ + int console_srcu_read_lock(void) ++ __acquires(&console_srcu) + { + return srcu_read_lock_nmisafe(&console_srcu); + } +@@ -301,6 +296,7 @@ EXPORT_SYMBOL(console_srcu_read_lock); + * Counterpart to console_srcu_read_lock() + */ + void console_srcu_read_unlock(int cookie) ++ __releases(&console_srcu) + { + srcu_read_unlock_nmisafe(&console_srcu, cookie); + } +@@ -353,6 +349,29 @@ static bool panic_in_progress(void) + return unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID); + } + ++/* Return true if a panic is in progress on the current CPU. */ ++bool this_cpu_in_panic(void) ++{ ++ /* ++ * We can use raw_smp_processor_id() here because it is impossible for ++ * the task to be migrated to the panic_cpu, or away from it. If ++ * panic_cpu has already been set, and we're not currently executing on ++ * that CPU, then we never will be. ++ */ ++ return unlikely(atomic_read(&panic_cpu) == raw_smp_processor_id()); ++} ++ ++/* ++ * Return true if a panic is in progress on a remote CPU. ++ * ++ * On true, the local CPU should immediately release any printing resources ++ * that may be needed by the panic CPU. ++ */ ++bool other_cpu_in_panic(void) ++{ ++ return (panic_in_progress() && !this_cpu_in_panic()); ++} ++ + /* + * This is used for debugging the mess that is the VT code by + * keeping track if we have the console semaphore held. It's +@@ -444,8 +463,33 @@ static int console_msg_format = MSG_FORMAT_DEFAULT; + /* syslog_lock protects syslog_* variables and write access to clear_seq. */ + static DEFINE_MUTEX(syslog_lock); + ++/* ++ * Specifies if a legacy console is registered. If legacy consoles are ++ * present, it is necessary to perform the console_lock/console_unlock dance ++ * whenever console flushing should occur. ++ */ ++bool have_legacy_console; ++ ++/* ++ * Specifies if an nbcon console is registered. If nbcon consoles are present, ++ * synchronous printing of legacy consoles will not occur during panic until ++ * the backtrace has been stored to the ringbuffer. ++ */ ++bool have_nbcon_console; ++ ++/* ++ * Specifies if a boot console is registered. If boot consoles are present, ++ * nbcon consoles cannot print simultaneously and must be synchronized by ++ * the console lock. This is because boot consoles and nbcon consoles may ++ * have mapped the same hardware. ++ */ ++bool have_boot_console; ++ + #ifdef CONFIG_PRINTK + DECLARE_WAIT_QUEUE_HEAD(log_wait); ++ ++static DECLARE_WAIT_QUEUE_HEAD(legacy_wait); ++ + /* All 3 protected by @syslog_lock. */ + /* the next printk record to read by syslog(READ) or /proc/kmsg */ + static u64 syslog_seq; +@@ -494,7 +538,7 @@ _DEFINE_PRINTKRB(printk_rb_static, CONFIG_LOG_BUF_SHIFT - PRB_AVGBITS, + + static struct printk_ringbuffer printk_rb_dynamic; + +-static struct printk_ringbuffer *prb = &printk_rb_static; ++struct printk_ringbuffer *prb = &printk_rb_static; + + /* + * We cannot access per-CPU data (e.g. per-CPU flush irq_work) before +@@ -698,9 +742,6 @@ static ssize_t msg_print_ext_body(char *buf, size_t size, + return len; + } + +-static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, +- bool is_extended, bool may_supress); +- + /* /dev/kmsg - userspace message inject/listen interface */ + struct devkmsg_user { + atomic64_t seq; +@@ -1848,7 +1889,7 @@ static bool console_waiter; + * there may be a waiter spinning (like a spinlock). Also it must be + * ready to hand over the lock at the end of the section. + */ +-static void console_lock_spinning_enable(void) ++void console_lock_spinning_enable(void) + { + /* + * Do not use spinning in panic(). The panic CPU wants to keep the lock. +@@ -1887,7 +1928,7 @@ static void console_lock_spinning_enable(void) + * + * Return: 1 if the lock rights were passed, 0 otherwise. + */ +-static int console_lock_spinning_disable_and_check(int cookie) ++int console_lock_spinning_disable_and_check(int cookie) + { + int waiter; + +@@ -2298,54 +2339,123 @@ int vprintk_store(int facility, int level, + return ret; + } + ++static bool legacy_allow_panic_sync; ++ ++/* ++ * This acts as a one-way switch to allow legacy consoles to print from ++ * the printk() caller context on a panic CPU. ++ */ ++void printk_legacy_allow_panic_sync(void) ++{ ++ legacy_allow_panic_sync = true; ++} ++ + asmlinkage int vprintk_emit(int facility, int level, + const struct dev_printk_info *dev_info, + const char *fmt, va_list args) + { ++ bool do_trylock_unlock = printing_via_unlock && ++ !IS_ENABLED(CONFIG_PREEMPT_RT); + int printed_len; +- bool in_sched = false; + + /* Suppress unimportant messages after panic happens */ + if (unlikely(suppress_printk)) + return 0; + +- if (unlikely(suppress_panic_printk) && other_cpu_in_panic()) ++ /* ++ * The messages on the panic CPU are the most important. If ++ * non-panic CPUs are generating any messages, they will be ++ * silently dropped. ++ */ ++ if (other_cpu_in_panic()) + return 0; + + if (level == LOGLEVEL_SCHED) { + level = LOGLEVEL_DEFAULT; +- in_sched = true; ++ /* If called from the scheduler, we can not call up(). */ ++ do_trylock_unlock = false; + } + + printk_delay(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 (!have_boot_console && have_nbcon_console) { ++ bool is_panic_context = this_cpu_in_panic(); ++ ++ /* ++ * In panic, the legacy consoles are not allowed to print from ++ * the printk calling context unless explicitly allowed. This ++ * gives the safe nbcon consoles a chance to print out all the ++ * panic messages first. This restriction only applies if ++ * there are nbcon consoles registered. ++ */ ++ if (is_panic_context) ++ do_trylock_unlock &= legacy_allow_panic_sync; ++ ++ /* ++ * There are situations where nbcon atomic printing should ++ * happen in the printk() caller context: ++ * ++ * - When this CPU is in panic. ++ * ++ * - When booting, before the printing threads have been ++ * started. ++ * ++ * - During shutdown, since the printing threads may not get ++ * a chance to print the final messages. ++ * ++ * Note that if boot consoles are registered, the ++ * console_lock/console_unlock dance must be relied upon ++ * instead because nbcon consoles cannot print simultaneously ++ * with boot consoles. ++ */ ++ if (is_panic_context || ++ !printk_threads_enabled || ++ (system_state > SYSTEM_RUNNING)) { ++ nbcon_atomic_flush_all(); ++ } ++ } ++ ++ nbcon_wake_threads(); ++ ++ if (do_trylock_unlock) { + /* + * The caller may be holding system-critical or + * timing-sensitive locks. Disable preemption during + * 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. ++ * ++ * Also, nbcon_get_default_prio() requires migration disabled. + */ + preempt_disable(); ++ + /* +- * Try to acquire and then immediately release the console +- * semaphore. The release will print out buffers. With the +- * spinning variant, this context tries to take over the +- * printing from another printing context. ++ * Do not emit for EMERGENCY priority. The console will be ++ * explicitly flushed when exiting the emergency section. + */ +- if (console_trylock_spinning()) +- console_unlock(); ++ if (nbcon_get_default_prio() == NBCON_PRIO_EMERGENCY) { ++ do_trylock_unlock = false; ++ } else { ++ /* ++ * Try to acquire and then immediately release the ++ * console semaphore. The release will print out ++ * buffers. With the spinning variant, this context ++ * tries to take over the printing from another ++ * printing context. ++ */ ++ if (console_trylock_spinning()) ++ console_unlock(); ++ } ++ + preempt_enable(); + } + +- if (in_sched) +- defer_console_output(); +- else ++ if (do_trylock_unlock) + wake_up_klogd(); ++ else ++ defer_console_output(); + + return printed_len; + } +@@ -2373,6 +2483,14 @@ EXPORT_SYMBOL(_printk); + 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 struct task_struct *nbcon_legacy_kthread; ++ ++static inline void wake_up_legacy_kthread(void) ++{ ++ if (nbcon_legacy_kthread) ++ wake_up_interruptible(&legacy_wait); ++} ++ + #else /* CONFIG_PRINTK */ + + #define printk_time false +@@ -2383,25 +2501,11 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre + + static u64 syslog_seq; + +-static size_t record_print_text(const struct printk_record *r, +- bool syslog, bool time) +-{ +- return 0; +-} +-static ssize_t info_print_ext_header(char *buf, size_t size, +- struct printk_info *info) +-{ +- return 0; +-} +-static ssize_t msg_print_ext_body(char *buf, size_t size, +- char *text, size_t text_len, +- struct dev_printk_info *dev_info) { return 0; } +-static void console_lock_spinning_enable(void) { } +-static int console_lock_spinning_disable_and_check(int cookie) { return 0; } +-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 inline void nbcon_legacy_kthread_create(void) { } ++static inline void wake_up_legacy_kthread(void) { } + #endif /* CONFIG_PRINTK */ + + #ifdef CONFIG_EARLY_PRINTK +@@ -2609,6 +2713,8 @@ void suspend_console(void) + void resume_console(void) + { + struct console *con; ++ short flags; ++ int cookie; + + if (!console_suspend_enabled) + return; +@@ -2625,6 +2731,20 @@ void resume_console(void) + */ + synchronize_srcu(&console_srcu); + ++ /* ++ * Since this runs in task context, wake the threaded printers ++ * directly rather than scheduling irq_work to do it. ++ */ ++ cookie = console_srcu_read_lock(); ++ for_each_console_srcu(con) { ++ flags = console_srcu_read_flags(con); ++ if (flags & CON_NBCON) ++ nbcon_kthread_wake(con); ++ } ++ console_srcu_read_unlock(cookie); ++ ++ wake_up_legacy_kthread(); ++ + pr_flush(1000, true); + } + +@@ -2639,7 +2759,8 @@ void resume_console(void) + */ + static int console_cpu_notify(unsigned int cpu) + { +- if (!cpuhp_tasks_frozen) { ++ if (!cpuhp_tasks_frozen && printing_via_unlock && ++ !IS_ENABLED(CONFIG_PREEMPT_RT)) { + /* If trylock fails, someone else is doing the printing */ + if (console_trylock()) + console_unlock(); +@@ -2647,26 +2768,6 @@ static int console_cpu_notify(unsigned int cpu) + return 0; + } + +-/* +- * Return true if a panic is in progress on a remote CPU. +- * +- * On true, the local CPU should immediately release any printing resources +- * that may be needed by the panic CPU. +- */ +-bool other_cpu_in_panic(void) +-{ +- if (!panic_in_progress()) +- return false; +- +- /* +- * We can use raw_smp_processor_id() here because it is impossible for +- * the task to be migrated to the panic_cpu, or away from it. If +- * panic_cpu has already been set, and we're not currently executing on +- * that CPU, then we never will be. +- */ +- return atomic_read(&panic_cpu) != raw_smp_processor_id(); +-} +- + /** + * console_lock - block the console subsystem from printing + * +@@ -2716,42 +2817,16 @@ int is_console_locked(void) + } + EXPORT_SYMBOL(is_console_locked); + +-/* +- * Check if the given console is currently capable and allowed to print +- * records. +- * +- * Requires the console_srcu_read_lock. +- */ +-static inline bool console_is_usable(struct console *con) +-{ +- short flags = console_srcu_read_flags(con); +- +- if (!(flags & CON_ENABLED)) +- return false; +- +- if ((flags & CON_SUSPENDED)) +- return false; +- +- if (!con->write) +- return false; +- +- /* +- * Console drivers may assume that per-cpu resources have been +- * allocated. So unless they're explicitly marked as being able to +- * cope (CON_ANYTIME) don't call them until this CPU is officially up. +- */ +- if (!cpu_online(raw_smp_processor_id()) && !(flags & CON_ANYTIME)) +- return false; +- +- return true; +-} +- + static void __console_unlock(void) + { + console_locked = 0; + up_console_sem(); + } + ++static DEFINE_WAIT_OVERRIDE_MAP(printk_legacy_map, LD_WAIT_SLEEP); ++ ++#ifdef CONFIG_PRINTK ++ + /* + * Prepend the message in @pmsg->pbufs->outbuf with a "dropped message". This + * is achieved by shifting the existing message over and inserting the dropped +@@ -2766,8 +2841,7 @@ static void __console_unlock(void) + * + * If @pmsg->pbufs->outbuf is modified, @pmsg->outbuf_len is updated. + */ +-#ifdef CONFIG_PRINTK +-static void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped) ++void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped) + { + struct printk_buffers *pbufs = pmsg->pbufs; + const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf); +@@ -2798,9 +2872,6 @@ static void console_prepend_dropped(struct printk_message *pmsg, unsigned long d + memcpy(outbuf, scratchbuf, len); + pmsg->outbuf_len += len; + } +-#else +-#define console_prepend_dropped(pmsg, dropped) +-#endif /* CONFIG_PRINTK */ + + /* + * Read and format the specified record (or a later record if the specified +@@ -2821,11 +2892,9 @@ static void console_prepend_dropped(struct printk_message *pmsg, unsigned long d + * of @pmsg are valid. (See the documentation of struct printk_message + * for information about the @pmsg fields.) + */ +-static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, +- bool is_extended, bool may_suppress) ++bool printk_get_next_message(struct printk_message *pmsg, u64 seq, ++ bool is_extended, bool may_suppress) + { +- static int panic_console_dropped; +- + struct printk_buffers *pbufs = pmsg->pbufs; + const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf); + const size_t outbuf_sz = sizeof(pbufs->outbuf); +@@ -2853,17 +2922,6 @@ static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, + pmsg->seq = r.info->seq; + pmsg->dropped = r.info->seq - seq; + +- /* +- * Check for dropped messages in panic here so that printk +- * suppression can occur as early as possible if necessary. +- */ +- if (pmsg->dropped && +- panic_in_progress() && +- 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"); +- } +- + /* Skip record that has level above the console loglevel. */ + if (may_suppress && suppress_message_printing(r.info->level)) + goto out; +@@ -2880,6 +2938,13 @@ static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, + return true; + } + ++/* ++ * Used as the printk buffers for non-panic, serialized console printing. ++ * This is for legacy (!CON_NBCON) as well as all boot (CON_BOOT) consoles. ++ * Its usage requires the console_lock held. ++ */ ++struct printk_buffers printk_shared_pbufs; ++ + /* + * Print one record for the given console. The record printed is whatever + * record is the next available record for the given console. +@@ -2897,12 +2962,10 @@ static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, + */ + static bool console_emit_next_record(struct console *con, bool *handover, int cookie) + { +- static struct printk_buffers pbufs; +- + bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED; +- char *outbuf = &pbufs.outbuf[0]; ++ char *outbuf = &printk_shared_pbufs.outbuf[0]; + struct printk_message pmsg = { +- .pbufs = &pbufs, ++ .pbufs = &printk_shared_pbufs, + }; + unsigned long flags; + +@@ -2924,35 +2987,59 @@ static bool console_emit_next_record(struct console *con, bool *handover, int co + con->dropped = 0; + } + +- /* +- * 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(); ++ /* Write everything out to the hardware. */ + +- /* Do not trace print latency. */ +- stop_critical_timings(); ++ if (IS_ENABLED(CONFIG_PREEMPT_RT)) { ++ /* ++ * On PREEMPT_RT this function is either in a thread or ++ * panic context. So there is no need for concern about ++ * printk reentrance, handovers, or lockdep complaints. ++ */ + +- /* Write everything out to the hardware. */ +- con->write(con, outbuf, pmsg.outbuf_len); ++ con->write(con, outbuf, pmsg.outbuf_len); ++ con->seq = pmsg.seq + 1; ++ } else { ++ /* ++ * 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(); + +- start_critical_timings(); ++ /* Do not trace print latency. */ ++ stop_critical_timings(); + +- con->seq = pmsg.seq + 1; ++ lock_map_acquire_try(&printk_legacy_map); ++ con->write(con, outbuf, pmsg.outbuf_len); ++ lock_map_release(&printk_legacy_map); + +- *handover = console_lock_spinning_disable_and_check(cookie); +- printk_safe_exit_irqrestore(flags); ++ start_critical_timings(); ++ ++ con->seq = pmsg.seq + 1; ++ ++ *handover = console_lock_spinning_disable_and_check(cookie); ++ printk_safe_exit_irqrestore(flags); ++ } + skip: + return true; + } + ++#else ++ ++static bool console_emit_next_record(struct console *con, bool *handover, int cookie) ++{ ++ *handover = false; ++ return false; ++} ++ ++#endif /* CONFIG_PRINTK */ ++ + /* + * Print out all remaining records to all consoles. + * +@@ -2991,13 +3078,33 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove + + cookie = console_srcu_read_lock(); + for_each_console_srcu(con) { ++ short flags = console_srcu_read_flags(con); ++ u64 printk_seq; + bool progress; + +- if (!console_is_usable(con)) ++ /* ++ * console_flush_all() is only for legacy consoles, ++ * unless the nbcon console has no kthread printer. ++ */ ++ if ((flags & CON_NBCON) && con->kthread) ++ continue; ++ ++ if (!console_is_usable(con, flags, true)) + continue; + any_usable = true; + +- progress = console_emit_next_record(con, handover, cookie); ++ if (flags & CON_NBCON) { ++ ++ lock_map_acquire_try(&printk_legacy_map); ++ progress = nbcon_atomic_emit_next_record(con, handover, cookie); ++ lock_map_release(&printk_legacy_map); ++ ++ printk_seq = nbcon_seq_read(con); ++ } else { ++ progress = console_emit_next_record(con, handover, cookie); ++ ++ printk_seq = con->seq; ++ } + + /* + * If a handover has occurred, the SRCU read lock +@@ -3007,8 +3114,8 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove + return false; + + /* Track the next of the highest seq flushed. */ +- if (con->seq > *next_seq) +- *next_seq = con->seq; ++ if (printk_seq > *next_seq) ++ *next_seq = printk_seq; + + if (!progress) + continue; +@@ -3031,19 +3138,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove + return false; + } + +-/** +- * console_unlock - unblock the console subsystem from printing +- * +- * Releases the console_lock which the caller holds to block printing of +- * the console subsystem. +- * +- * While the console_lock was held, console output may have been buffered +- * by printk(). If this is the case, console_unlock(); emits +- * the output prior to releasing the lock. +- * +- * console_unlock(); may be called from any context. +- */ +-void console_unlock(void) ++static void console_flush_and_unlock(void) + { + bool do_cond_resched; + bool handover; +@@ -3087,6 +3182,32 @@ void console_unlock(void) + */ + } while (prb_read_valid(prb, next_seq, NULL) && console_trylock()); + } ++ ++/** ++ * console_unlock - unblock the console subsystem from printing ++ * ++ * Releases the console_lock which the caller holds to block printing of ++ * the console subsystem. ++ * ++ * While the console_lock was held, console output may have been buffered ++ * by printk(). If this is the case, console_unlock(); emits ++ * the output prior to releasing the lock. ++ * ++ * console_unlock(); may be called from any context. ++ */ ++void console_unlock(void) ++{ ++ /* ++ * PREEMPT_RT relies on kthread and atomic consoles for printing. ++ * It never attempts to print from console_unlock(). ++ */ ++ if (IS_ENABLED(CONFIG_PREEMPT_RT)) { ++ __console_unlock(); ++ return; ++ } ++ ++ console_flush_and_unlock(); ++} + EXPORT_SYMBOL(console_unlock); + + /** +@@ -3197,6 +3318,7 @@ void console_flush_on_panic(enum con_flush_mode mode) + + if (mode == CONSOLE_REPLAY_ALL) { + struct console *c; ++ short flags; + int cookie; + u64 seq; + +@@ -3204,16 +3326,25 @@ void console_flush_on_panic(enum con_flush_mode mode) + + cookie = console_srcu_read_lock(); + for_each_console_srcu(c) { +- /* +- * This is an unsynchronized assignment, but the +- * kernel is in "hope and pray" mode anyway. +- */ +- c->seq = seq; ++ flags = console_srcu_read_flags(c); ++ ++ if (flags & CON_NBCON) { ++ nbcon_seq_force(c, seq); ++ } else { ++ /* ++ * This is an unsynchronized assignment. On ++ * panic legacy consoles are only best effort. ++ */ ++ c->seq = seq; ++ } + } + console_srcu_read_unlock(cookie); + } + +- console_flush_all(false, &next_seq, &handover); ++ nbcon_atomic_flush_all(); ++ ++ if (printing_via_unlock) ++ console_flush_all(false, &next_seq, &handover); + } + + /* +@@ -3270,13 +3401,122 @@ EXPORT_SYMBOL(console_stop); + + void console_start(struct console *console) + { ++ short flags; ++ + console_list_lock(); + console_srcu_write_flags(console, console->flags | CON_ENABLED); ++ flags = console->flags; + console_list_unlock(); ++ ++ /* ++ * Ensure that all SRCU list walks have completed. The related ++ * printing context must be able to see it is enabled so that ++ * it is guaranteed to wake up and resume printing. ++ */ ++ synchronize_srcu(&console_srcu); ++ ++ if (flags & CON_NBCON) ++ nbcon_kthread_wake(console); ++ else ++ wake_up_legacy_kthread(); ++ + __pr_flush(console, 1000, true); + } + EXPORT_SYMBOL(console_start); + ++#ifdef CONFIG_PRINTK ++static bool printer_should_wake(void) ++{ ++ bool available = false; ++ struct console *con; ++ int cookie; ++ ++ if (kthread_should_stop()) ++ return true; ++ ++ cookie = console_srcu_read_lock(); ++ for_each_console_srcu(con) { ++ short flags = console_srcu_read_flags(con); ++ u64 printk_seq; ++ ++ /* ++ * The legacy printer thread is only for legacy consoles, ++ * unless the nbcon console has no kthread printer. ++ */ ++ if ((flags & CON_NBCON) && con->kthread) ++ continue; ++ ++ if (!console_is_usable(con, flags, true)) ++ continue; ++ ++ if (flags & CON_NBCON) { ++ printk_seq = nbcon_seq_read(con); ++ } else { ++ /* ++ * It is safe to read @seq because only this ++ * thread context updates @seq. ++ */ ++ printk_seq = con->seq; ++ } ++ ++ if (prb_read_valid(prb, printk_seq, NULL)) { ++ available = true; ++ break; ++ } ++ } ++ console_srcu_read_unlock(cookie); ++ ++ return available; ++} ++ ++static int nbcon_legacy_kthread_func(void *unused) ++{ ++ int error; ++ ++ for (;;) { ++ error = wait_event_interruptible(legacy_wait, printer_should_wake()); ++ ++ if (kthread_should_stop()) ++ break; ++ ++ if (error) ++ continue; ++ ++ console_lock(); ++ console_flush_and_unlock(); ++ } ++ ++ return 0; ++} ++ ++void nbcon_legacy_kthread_create(void) ++{ ++ struct task_struct *kt; ++ ++ lockdep_assert_held(&console_mutex); ++ ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) ++ return; ++ ++ if (!printk_threads_enabled || nbcon_legacy_kthread) ++ return; ++ ++ kt = kthread_run(nbcon_legacy_kthread_func, NULL, "pr/legacy"); ++ if (IS_ERR(kt)) { ++ pr_err("unable to start legacy printing thread\n"); ++ return; ++ } ++ ++ nbcon_legacy_kthread = kt; ++ ++ /* ++ * It is important that console printing threads are scheduled ++ * shortly after a printk call and with generous runtime budgets. ++ */ ++ sched_set_normal(nbcon_legacy_kthread, -20); ++} ++#endif /* CONFIG_PRINTK */ ++ + static int __read_mostly keep_bootcon; + + static int __init keep_bootcon_setup(char *str) +@@ -3375,11 +3615,6 @@ static void try_enable_default_console(struct console *newcon) + newcon->flags |= CON_CONSDEV; + } + +-#define con_printk(lvl, con, fmt, ...) \ +- printk(lvl pr_fmt("%sconsole [%s%d] " fmt), \ +- (con->flags & CON_BOOT) ? "boot" : "", \ +- con->name, con->index, ##__VA_ARGS__) +- + static void console_init_seq(struct console *newcon, bool bootcon_registered) + { + struct console *con; +@@ -3428,11 +3663,20 @@ static void console_init_seq(struct console *newcon, bool bootcon_registered) + + newcon->seq = prb_next_seq(prb); + for_each_console(con) { +- if ((con->flags & CON_BOOT) && +- (con->flags & CON_ENABLED) && +- con->seq < newcon->seq) { +- newcon->seq = con->seq; ++ u64 seq; ++ ++ if (!((con->flags & CON_BOOT) && ++ (con->flags & CON_ENABLED))) { ++ continue; + } ++ ++ if (con->flags & CON_NBCON) ++ seq = nbcon_seq_read(con); ++ else ++ seq = con->seq; ++ ++ if (seq < newcon->seq) ++ newcon->seq = seq; + } + } + +@@ -3493,6 +3737,15 @@ void register_console(struct console *newcon) + goto unlock; + } + ++ if (newcon->flags & CON_NBCON) { ++ /* ++ * Ensure the nbcon console buffers can be allocated ++ * before modifying any global data. ++ */ ++ if (!nbcon_alloc(newcon)) ++ goto unlock; ++ } ++ + /* + * See if we want to enable this console driver by default. + * +@@ -3520,8 +3773,11 @@ void register_console(struct console *newcon) + err = try_enable_preferred_console(newcon, false); + + /* printk() messages are not printed to the Braille console. */ +- if (err || newcon->flags & CON_BRL) ++ if (err || newcon->flags & CON_BRL) { ++ if (newcon->flags & CON_NBCON) ++ nbcon_free(newcon); + goto unlock; ++ } + + /* + * If we have a bootconsole, and are switching to a real console, +@@ -3537,6 +3793,17 @@ void register_console(struct console *newcon) + newcon->dropped = 0; + console_init_seq(newcon, bootcon_registered); + ++ if (newcon->flags & CON_NBCON) { ++ have_nbcon_console = true; ++ nbcon_init(newcon); ++ } else { ++ have_legacy_console = true; ++ nbcon_legacy_kthread_create(); ++ } ++ ++ if (newcon->flags & CON_BOOT) ++ have_boot_console = true; ++ + /* + * Put this console in the list - keep the + * preferred driver at the head of the list. +@@ -3589,6 +3856,11 @@ EXPORT_SYMBOL(register_console); + /* Must be called under console_list_lock(). */ + static int unregister_console_locked(struct console *console) + { ++ bool is_boot_con = (console->flags & CON_BOOT); ++ bool found_legacy_con = false; ++ bool found_nbcon_con = false; ++ bool found_boot_con = false; ++ struct console *c; + int res; + + lockdep_assert_console_list_lock_held(); +@@ -3628,11 +3900,50 @@ static int unregister_console_locked(struct console *console) + */ + synchronize_srcu(&console_srcu); + ++ if (console->flags & CON_NBCON) ++ nbcon_free(console); ++ + console_sysfs_notify(); + + if (console->exit) + res = console->exit(console); + ++ /* ++ * With this console gone, the global flags tracking registered ++ * console types may have changed. Update them. ++ */ ++ for_each_console(c) { ++ if (c->flags & CON_BOOT) ++ found_boot_con = true; ++ ++ if (c->flags & CON_NBCON) ++ found_nbcon_con = true; ++ else ++ found_legacy_con = true; ++ } ++ if (!found_boot_con) ++ have_boot_console = false; ++ if (!found_legacy_con) ++ have_legacy_console = false; ++ if (!found_nbcon_con) ++ have_nbcon_console = false; ++ ++ /* ++ * When the last boot console unregisters, start up the ++ * printing threads. ++ */ ++ if (is_boot_con && !have_boot_console) { ++ for_each_console(c) ++ nbcon_kthread_create(c); ++ } ++ ++#ifdef CONFIG_PRINTK ++ if (!printing_via_unlock && nbcon_legacy_kthread) { ++ kthread_stop(nbcon_legacy_kthread); ++ nbcon_legacy_kthread = NULL; ++ } ++#endif ++ + return res; + } + +@@ -3777,69 +4088,94 @@ late_initcall(printk_late_init); + /* 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) + { +- int remaining = timeout_ms; ++ unsigned long timeout_jiffies = msecs_to_jiffies(timeout_ms); ++ unsigned long remaining_jiffies = timeout_jiffies; + struct console *c; + u64 last_diff = 0; + u64 printk_seq; ++ short flags; ++ bool locked; + int cookie; + u64 diff; + u64 seq; + + might_sleep(); + +- seq = prb_next_seq(prb); ++ seq = prb_next_reserve_seq(prb); + +- /* Flush the consoles so that records up to @seq are printed. */ +- console_lock(); +- console_unlock(); ++ /* ++ * Flush the consoles so that records up to @seq are printed. ++ * Otherwise this function will just wait for the threaded printers ++ * to print up to @seq. ++ */ ++ if (printing_via_unlock && !IS_ENABLED(CONFIG_PREEMPT_RT)) { ++ console_lock(); ++ console_unlock(); ++ } + + for (;;) { ++ unsigned long begin_jiffies; ++ unsigned long slept_jiffies; ++ ++ locked = false; + diff = 0; + +- /* +- * Hold the console_lock to guarantee safe access to +- * console->seq. Releasing console_lock flushes more +- * records in case @seq is still not printed on all +- * usable consoles. +- */ +- console_lock(); ++ if (printing_via_unlock) { ++ /* ++ * Hold the console_lock to guarantee safe access to ++ * console->seq. Releasing console_lock flushes more ++ * records in case @seq is still not printed on all ++ * usable consoles. ++ */ ++ console_lock(); ++ locked = true; ++ } + + cookie = console_srcu_read_lock(); + for_each_console_srcu(c) { + if (con && con != c) + continue; ++ ++ flags = console_srcu_read_flags(c); ++ + /* + * If consoles are not usable, it cannot be expected + * that they make forward progress, so only increment + * @diff for usable consoles. + */ +- if (!console_is_usable(c)) ++ if (!console_is_usable(c, flags, true) && ++ !console_is_usable(c, flags, false)) { + continue; +- printk_seq = c->seq; ++ } ++ ++ if (flags & CON_NBCON) { ++ printk_seq = nbcon_seq_read(c); ++ } else { ++ WARN_ON_ONCE(!locked); ++ printk_seq = c->seq; ++ } ++ + if (printk_seq < seq) + diff += seq - printk_seq; + } + console_srcu_read_unlock(cookie); + + if (diff != last_diff && reset_on_progress) +- remaining = timeout_ms; ++ remaining_jiffies = timeout_jiffies; + +- console_unlock(); ++ if (locked) ++ console_unlock(); + + /* Note: @diff is 0 if there are no usable consoles. */ +- if (diff == 0 || remaining == 0) ++ if (diff == 0 || remaining_jiffies == 0) + break; + +- if (remaining < 0) { +- /* no timeout limit */ +- msleep(100); +- } else if (remaining < 100) { +- msleep(remaining); +- remaining = 0; +- } else { +- msleep(100); +- remaining -= 100; +- } ++ /* msleep(1) might sleep much longer. Check time by jiffies. */ ++ begin_jiffies = jiffies; ++ msleep(1); ++ slept_jiffies = jiffies - begin_jiffies; ++ ++ remaining_jiffies -= min(slept_jiffies, remaining_jiffies); + + last_diff = diff; + } +@@ -3880,9 +4216,16 @@ 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 trylock fails, someone else is doing the printing */ +- if (console_trylock()) +- console_unlock(); ++ if (IS_ENABLED(CONFIG_PREEMPT_RT)) { ++ wake_up_interruptible(&legacy_wait); ++ } else { ++ /* ++ * If trylock fails, some other context ++ * will do the printing. ++ */ ++ if (console_trylock()) ++ console_unlock(); ++ } + } + + if (pending & PRINTK_PENDING_WAKEUP) +@@ -3950,11 +4293,16 @@ 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; ++ ++ if (printing_via_unlock) ++ val |= PRINTK_PENDING_OUTPUT; ++ __wake_up_klogd(val); + } + + void printk_trigger_flush(void) + { ++ nbcon_wake_threads(); + defer_console_output(); + } + +diff --git a/kernel/printk/printk_ringbuffer.c b/kernel/printk/printk_ringbuffer.c +index fde338606ce8..e7b808b829a0 100644 +--- a/kernel/printk/printk_ringbuffer.c ++++ b/kernel/printk/printk_ringbuffer.c +@@ -6,6 +6,7 @@ + #include + #include + #include "printk_ringbuffer.h" ++#include "internal.h" + + /** + * DOC: printk_ringbuffer overview +@@ -303,6 +304,9 @@ + * + * desc_push_tail:B / desc_reserve:D + * set descriptor reusable (state), then push descriptor tail (id) ++ * ++ * desc_update_last_finalized:A / desc_last_finalized_seq:A ++ * store finalized record, then set new highest finalized sequence number + */ + + #define DATA_SIZE(data_ring) _DATA_SIZE((data_ring)->size_bits) +@@ -1030,9 +1034,13 @@ static char *data_alloc(struct printk_ringbuffer *rb, unsigned int size, + unsigned long next_lpos; + + if (size == 0) { +- /* Specify a data-less block. */ +- blk_lpos->begin = NO_LPOS; +- blk_lpos->next = NO_LPOS; ++ /* ++ * Data blocks are not created for empty lines. Instead, the ++ * reader will recognize these special lpos values and handle ++ * it appropriately. ++ */ ++ blk_lpos->begin = EMPTY_LINE_LPOS; ++ blk_lpos->next = EMPTY_LINE_LPOS; + return NULL; + } + +@@ -1210,10 +1218,18 @@ static const char *get_data(struct prb_data_ring *data_ring, + + /* Data-less data block description. */ + if (BLK_DATALESS(blk_lpos)) { +- if (blk_lpos->begin == NO_LPOS && blk_lpos->next == NO_LPOS) { ++ /* ++ * Records that are just empty lines are also valid, even ++ * though they do not have a data block. For such records ++ * explicitly return empty string data to signify success. ++ */ ++ if (blk_lpos->begin == EMPTY_LINE_LPOS && ++ blk_lpos->next == EMPTY_LINE_LPOS) { + *data_size = 0; + return ""; + } ++ ++ /* Data lost, invalid, or otherwise unavailable. */ + return NULL; + } + +@@ -1441,20 +1457,118 @@ bool prb_reserve_in_last(struct prb_reserved_entry *e, struct printk_ringbuffer + return false; + } + ++/* ++ * @last_finalized_seq value guarantees that all records up to and including ++ * this sequence number are finalized and can be read. The only exception are ++ * too old records which have already been overwritten. ++ * ++ * It is also guaranteed that @last_finalized_seq only increases. ++ * ++ * Be aware that finalized records following non-finalized records are not ++ * reported because they are not yet available to the reader. For example, ++ * a new record stored via printk() will not be available to a printer if ++ * it follows a record that has not been finalized yet. However, once that ++ * non-finalized record becomes finalized, @last_finalized_seq will be ++ * appropriately updated and the full set of finalized records will be ++ * available to the printer. And since each printk() caller will either ++ * directly print or trigger deferred printing of all available unprinted ++ * records, all printk() messages will get printed. ++ */ ++static u64 desc_last_finalized_seq(struct printk_ringbuffer *rb) ++{ ++ struct prb_desc_ring *desc_ring = &rb->desc_ring; ++ unsigned long ulseq; ++ ++ /* ++ * Guarantee the sequence number is loaded before loading the ++ * associated record in order to guarantee that the record can be ++ * seen by this CPU. This pairs with desc_update_last_finalized:A. ++ */ ++ ulseq = atomic_long_read_acquire(&desc_ring->last_finalized_seq ++ ); /* LMM(desc_last_finalized_seq:A) */ ++ ++ return __ulseq_to_u64seq(rb, ulseq); ++} ++ ++static bool _prb_read_valid(struct printk_ringbuffer *rb, u64 *seq, ++ struct printk_record *r, unsigned int *line_count); ++ ++/* ++ * Check if there are records directly following @last_finalized_seq that are ++ * finalized. If so, update @last_finalized_seq to the latest of these ++ * records. It is not allowed to skip over records that are not yet finalized. ++ */ ++static void desc_update_last_finalized(struct printk_ringbuffer *rb) ++{ ++ struct prb_desc_ring *desc_ring = &rb->desc_ring; ++ u64 old_seq = desc_last_finalized_seq(rb); ++ unsigned long oldval; ++ unsigned long newval; ++ u64 finalized_seq; ++ u64 try_seq; ++ ++try_again: ++ finalized_seq = old_seq; ++ try_seq = finalized_seq + 1; ++ ++ /* Try to find later finalized records. */ ++ while (_prb_read_valid(rb, &try_seq, NULL, NULL)) { ++ finalized_seq = try_seq; ++ try_seq++; ++ } ++ ++ /* No update needed if no later finalized record was found. */ ++ if (finalized_seq == old_seq) ++ return; ++ ++ oldval = __u64seq_to_ulseq(old_seq); ++ newval = __u64seq_to_ulseq(finalized_seq); ++ ++ /* ++ * Set the sequence number of a later finalized record that has been ++ * seen. ++ * ++ * Guarantee the record data is visible to other CPUs before storing ++ * its sequence number. This pairs with desc_last_finalized_seq:A. ++ * ++ * Memory barrier involvement: ++ * ++ * If desc_last_finalized_seq:A reads from ++ * desc_update_last_finalized:A, then desc_read:A reads from ++ * _prb_commit:B. ++ * ++ * Relies on: ++ * ++ * RELEASE from _prb_commit:B to desc_update_last_finalized:A ++ * matching ++ * ACQUIRE from desc_last_finalized_seq:A to desc_read:A ++ * ++ * Note: _prb_commit:B and desc_update_last_finalized:A can be ++ * different CPUs. However, the desc_update_last_finalized:A ++ * CPU (which performs the release) must have previously seen ++ * _prb_commit:B. ++ */ ++ if (!atomic_long_try_cmpxchg_release(&desc_ring->last_finalized_seq, ++ &oldval, newval)) { /* LMM(desc_update_last_finalized:A) */ ++ old_seq = __ulseq_to_u64seq(rb, oldval); ++ goto try_again; ++ } ++} ++ + /* + * Attempt to finalize a specified descriptor. If this fails, the descriptor + * is either already final or it will finalize itself when the writer commits. + */ +-static void desc_make_final(struct prb_desc_ring *desc_ring, unsigned long id) ++static void desc_make_final(struct printk_ringbuffer *rb, unsigned long id) + { ++ struct prb_desc_ring *desc_ring = &rb->desc_ring; + unsigned long prev_state_val = DESC_SV(id, desc_committed); + struct prb_desc *d = to_desc(desc_ring, id); + +- atomic_long_cmpxchg_relaxed(&d->state_var, prev_state_val, +- DESC_SV(id, desc_finalized)); /* LMM(desc_make_final:A) */ +- +- /* Best effort to remember the last finalized @id. */ +- atomic_long_set(&desc_ring->last_finalized_id, id); ++ if (atomic_long_try_cmpxchg_relaxed(&d->state_var, &prev_state_val, ++ DESC_SV(id, desc_finalized))) { /* LMM(desc_make_final:A) */ ++ desc_update_last_finalized(rb); ++ } + } + + /** +@@ -1550,7 +1664,7 @@ bool prb_reserve(struct prb_reserved_entry *e, struct printk_ringbuffer *rb, + * readers. (For seq==0 there is no previous descriptor.) + */ + if (info->seq > 0) +- desc_make_final(desc_ring, DESC_ID(id - 1)); ++ desc_make_final(rb, DESC_ID(id - 1)); + + r->text_buf = data_alloc(rb, r->text_buf_size, &d->text_blk_lpos, id); + /* If text data allocation fails, a data-less record is committed. */ +@@ -1643,7 +1757,7 @@ void prb_commit(struct prb_reserved_entry *e) + */ + head_id = atomic_long_read(&desc_ring->head_id); /* LMM(prb_commit:A) */ + if (head_id != e->id) +- desc_make_final(desc_ring, e->id); ++ desc_make_final(e->rb, e->id); + } + + /** +@@ -1663,12 +1777,9 @@ void prb_commit(struct prb_reserved_entry *e) + */ + void prb_final_commit(struct prb_reserved_entry *e) + { +- struct prb_desc_ring *desc_ring = &e->rb->desc_ring; +- + _prb_commit(e, desc_finalized); + +- /* Best effort to remember the last finalized @id. */ +- atomic_long_set(&desc_ring->last_finalized_id, e->id); ++ desc_update_last_finalized(e->rb); + } + + /* +@@ -1746,6 +1857,8 @@ static bool copy_data(struct prb_data_ring *data_ring, + * descriptor. However, it also verifies that the record is finalized and has + * the sequence number @seq. On success, 0 is returned. + * ++ * For the panic CPU, committed descriptors are also considered finalized. ++ * + * Error return values: + * -EINVAL: A finalized record with sequence number @seq does not exist. + * -ENOENT: A finalized record with sequence number @seq exists, but its data +@@ -1764,16 +1877,25 @@ static int desc_read_finalized_seq(struct prb_desc_ring *desc_ring, + + /* + * An unexpected @id (desc_miss) or @seq mismatch means the record +- * does not exist. A descriptor in the reserved or committed state +- * means the record does not yet exist for the reader. ++ * does not exist. A descriptor in the reserved state means the ++ * record does not yet exist for the reader. + */ + if (d_state == desc_miss || + d_state == desc_reserved || +- d_state == desc_committed || + s != seq) { + return -EINVAL; + } + ++ /* ++ * A descriptor in the committed state means the record does not yet ++ * exist for the reader. However, for the panic CPU, committed ++ * records are also handled as finalized records since they contain ++ * message data in a consistent state and may contain additional ++ * hints as to the cause of the panic. ++ */ ++ if (d_state == desc_committed && !this_cpu_in_panic()) ++ return -EINVAL; ++ + /* + * A descriptor in the reusable state may no longer have its data + * available; report it as existing but with lost data. Or the record +@@ -1832,7 +1954,7 @@ static int prb_read(struct printk_ringbuffer *rb, u64 seq, + } + + /* Get the sequence number of the tail descriptor. */ +-static u64 prb_first_seq(struct printk_ringbuffer *rb) ++u64 prb_first_seq(struct printk_ringbuffer *rb) + { + struct prb_desc_ring *desc_ring = &rb->desc_ring; + enum desc_state d_state; +@@ -1875,12 +1997,131 @@ static u64 prb_first_seq(struct printk_ringbuffer *rb) + return seq; + } + ++/** ++ * prb_next_reserve_seq() - Get the sequence number after the most recently ++ * reserved record. ++ * ++ * @rb: The ringbuffer to get the sequence number from. ++ * ++ * This is the public function available to readers to see what sequence ++ * number will be assigned to the next reserved record. ++ * ++ * Note that depending on the situation, this value can be equal to or ++ * higher than the sequence number returned by prb_next_seq(). ++ * ++ * Context: Any context. ++ * Return: The sequence number that will be assigned to the next record ++ * reserved. ++ */ ++u64 prb_next_reserve_seq(struct printk_ringbuffer *rb) ++{ ++ struct prb_desc_ring *desc_ring = &rb->desc_ring; ++ unsigned long last_finalized_id; ++ atomic_long_t *state_var; ++ u64 last_finalized_seq; ++ unsigned long head_id; ++ struct prb_desc desc; ++ unsigned long diff; ++ struct prb_desc *d; ++ int err; ++ ++ /* ++ * It may not be possible to read a sequence number for @head_id. ++ * So the ID of @last_finailzed_seq is used to calculate what the ++ * sequence number of @head_id will be. ++ */ ++ ++try_again: ++ last_finalized_seq = desc_last_finalized_seq(rb); ++ ++ /* ++ * @head_id is loaded after @last_finalized_seq to ensure that it is ++ * at or beyond @last_finalized_seq. ++ * ++ * Memory barrier involvement: ++ * ++ * If desc_last_finalized_seq:A reads from ++ * desc_update_last_finalized:A, then ++ * prb_next_reserve_seq:A reads from desc_reserve:D. ++ * ++ * Relies on: ++ * ++ * RELEASE from desc_reserve:D to desc_update_last_finalized:A ++ * matching ++ * ACQUIRE from desc_last_finalized_seq:A to prb_next_reserve_seq:A ++ * ++ * Note: desc_reserve:D and desc_update_last_finalized:A can be ++ * different CPUs. However, the desc_update_last_finalized:A CPU ++ * (which performs the release) must have previously seen ++ * desc_read:C, which implies desc_reserve:D can be seen. ++ */ ++ head_id = atomic_long_read(&desc_ring->head_id); /* LMM(prb_next_reserve_seq:A) */ ++ ++ d = to_desc(desc_ring, last_finalized_seq); ++ state_var = &d->state_var; ++ ++ /* Extract the ID, used to specify the descriptor to read. */ ++ last_finalized_id = DESC_ID(atomic_long_read(state_var)); ++ ++ /* Ensure @last_finalized_id is correct. */ ++ err = desc_read_finalized_seq(desc_ring, last_finalized_id, last_finalized_seq, &desc); ++ ++ if (err == -EINVAL) { ++ if (last_finalized_seq == 0) { ++ /* ++ * @last_finalized_seq still contains its initial ++ * value. Probably no record has been finalized yet. ++ * This means the ringbuffer is not yet full and the ++ * @head_id value can be used directly (subtracting ++ * off the id value corresponding to seq=0). ++ */ ++ ++ /* ++ * Because of hack#2 of the bootstrapping phase, the ++ * @head_id initial value must be handled separately. ++ */ ++ if (head_id == DESC0_ID(desc_ring->count_bits)) ++ return 0; ++ ++ /* ++ * The @head_id is initialized such that the first ++ * increment will yield the first record (seq=0). ++ * Therefore use the initial value +1 as the base to ++ * subtract from @head_id. ++ */ ++ last_finalized_id = DESC0_ID(desc_ring->count_bits) + 1; ++ } else { ++ /* Record must have been overwritten. Try again. */ ++ goto try_again; ++ } ++ } ++ ++ /* ++ * @diff is the number of records beyond the last record available ++ * to readers. ++ */ ++ diff = head_id - last_finalized_id; ++ ++ /* ++ * @head_id points to the most recently reserved record, but this ++ * function returns the sequence number that will be assigned to the ++ * next (not yet reserved) record. Thus +1 is needed. ++ */ ++ return (last_finalized_seq + diff + 1); ++} ++ + /* +- * Non-blocking read of a record. Updates @seq to the last finalized record +- * (which may have no data available). ++ * Non-blocking read of a record. + * +- * See the description of prb_read_valid() and prb_read_valid_info() +- * for details. ++ * On success @seq is updated to the record that was read and (if provided) ++ * @r and @line_count will contain the read/calculated data. ++ * ++ * On failure @seq is updated to a record that is not yet available to the ++ * reader, but it will be the next record available to the reader. ++ * ++ * Note: When the current CPU is in panic, this function will skip over any ++ * non-existent/non-finalized records in order to allow the panic CPU ++ * to print any and all records that have been finalized. + */ + static bool _prb_read_valid(struct printk_ringbuffer *rb, u64 *seq, + struct printk_record *r, unsigned int *line_count) +@@ -1899,12 +2140,32 @@ static bool _prb_read_valid(struct printk_ringbuffer *rb, u64 *seq, + *seq = tail_seq; + + } else if (err == -ENOENT) { +- /* Record exists, but no data available. Skip. */ ++ /* Record exists, but the data was lost. Skip. */ + (*seq)++; + + } else { +- /* Non-existent/non-finalized record. Must stop. */ +- return false; ++ /* ++ * Non-existent/non-finalized record. Must stop. ++ * ++ * For panic situations it cannot be expected that ++ * non-finalized records will become finalized. But ++ * there may be other finalized records beyond that ++ * need to be printed for a panic situation. If this ++ * is the panic CPU, skip this ++ * non-existent/non-finalized record unless it is ++ * at or beyond the head, in which case it is not ++ * possible to continue. ++ * ++ * Note that new messages printed on panic CPU are ++ * finalized when we are here. The only exception ++ * might be the last message without trailing newline. ++ * But it would have the sequence number returned ++ * by "prb_next_reserve_seq() - 1". ++ */ ++ if (this_cpu_in_panic() && ((*seq + 1) < prb_next_reserve_seq(rb))) ++ (*seq)++; ++ else ++ return false; + } + } + +@@ -1932,7 +2193,7 @@ static bool _prb_read_valid(struct printk_ringbuffer *rb, u64 *seq, + * On success, the reader must check r->info.seq to see which record was + * actually read. This allows the reader to detect dropped records. + * +- * Failure means @seq refers to a not yet written record. ++ * Failure means @seq refers to a record not yet available to the reader. + */ + bool prb_read_valid(struct printk_ringbuffer *rb, u64 seq, + struct printk_record *r) +@@ -1962,7 +2223,7 @@ bool prb_read_valid(struct printk_ringbuffer *rb, u64 seq, + * On success, the reader must check info->seq to see which record meta data + * was actually read. This allows the reader to detect dropped records. + * +- * Failure means @seq refers to a not yet written record. ++ * Failure means @seq refers to a record not yet available to the reader. + */ + bool prb_read_valid_info(struct printk_ringbuffer *rb, u64 seq, + struct printk_info *info, unsigned int *line_count) +@@ -2008,7 +2269,9 @@ u64 prb_first_valid_seq(struct printk_ringbuffer *rb) + * newest sequence number available to readers will be. + * + * This provides readers a sequence number to jump to if all currently +- * available records should be skipped. ++ * available records should be skipped. It is guaranteed that all records ++ * previous to the returned value have been finalized and are (or were) ++ * available to the reader. + * + * Context: Any context. + * Return: The sequence number of the next newest (not yet available) record +@@ -2016,34 +2279,19 @@ u64 prb_first_valid_seq(struct printk_ringbuffer *rb) + */ + u64 prb_next_seq(struct printk_ringbuffer *rb) + { +- struct prb_desc_ring *desc_ring = &rb->desc_ring; +- enum desc_state d_state; +- unsigned long id; + u64 seq; + +- /* Check if the cached @id still points to a valid @seq. */ +- id = atomic_long_read(&desc_ring->last_finalized_id); +- d_state = desc_read(desc_ring, id, NULL, &seq, NULL); ++ seq = desc_last_finalized_seq(rb); + +- if (d_state == desc_finalized || d_state == desc_reusable) { +- /* +- * Begin searching after the last finalized record. +- * +- * On 0, the search must begin at 0 because of hack#2 +- * of the bootstrapping phase it is not known if a +- * record at index 0 exists. +- */ +- if (seq != 0) +- seq++; +- } else { +- /* +- * The information about the last finalized sequence number +- * has gone. It should happen only when there is a flood of +- * new messages and the ringbuffer is rapidly recycled. +- * Give up and start from the beginning. +- */ +- seq = 0; +- } ++ /* ++ * Begin searching after the last finalized record. ++ * ++ * On 0, the search must begin at 0 because of hack#2 ++ * of the bootstrapping phase it is not known if a ++ * record at index 0 exists. ++ */ ++ if (seq != 0) ++ seq++; + + /* + * The information about the last finalized @seq might be inaccurate. +@@ -2085,7 +2333,7 @@ void prb_init(struct printk_ringbuffer *rb, + rb->desc_ring.infos = infos; + atomic_long_set(&rb->desc_ring.head_id, DESC0_ID(descbits)); + atomic_long_set(&rb->desc_ring.tail_id, DESC0_ID(descbits)); +- atomic_long_set(&rb->desc_ring.last_finalized_id, DESC0_ID(descbits)); ++ atomic_long_set(&rb->desc_ring.last_finalized_seq, 0); + + rb->text_data_ring.size_bits = textbits; + rb->text_data_ring.data = text_buf; +diff --git a/kernel/printk/printk_ringbuffer.h b/kernel/printk/printk_ringbuffer.h +index 18cd25e489b8..52626d0f1fa3 100644 +--- a/kernel/printk/printk_ringbuffer.h ++++ b/kernel/printk/printk_ringbuffer.h +@@ -75,7 +75,7 @@ struct prb_desc_ring { + struct printk_info *infos; + atomic_long_t head_id; + atomic_long_t tail_id; +- atomic_long_t last_finalized_id; ++ atomic_long_t last_finalized_seq; + }; + + /* +@@ -127,8 +127,22 @@ enum desc_state { + #define DESC_SV(id, state) (((unsigned long)state << DESC_FLAGS_SHIFT) | id) + #define DESC_ID_MASK (~DESC_FLAGS_MASK) + #define DESC_ID(sv) ((sv) & DESC_ID_MASK) ++ ++/* ++ * Special data block logical position values (for fields of ++ * @prb_desc.text_blk_lpos). ++ * ++ * - Bit0 is used to identify if the record has no data block. (Implemented in ++ * the LPOS_DATALESS() macro.) ++ * ++ * - Bit1 specifies the reason for not having a data block. ++ * ++ * These special values could never be real lpos values because of the ++ * meta data and alignment padding of data blocks. (See to_blk_size() for ++ * details.) ++ */ + #define FAILED_LPOS 0x1 +-#define NO_LPOS 0x3 ++#define EMPTY_LINE_LPOS 0x3 + + #define FAILED_BLK_LPOS \ + { \ +@@ -259,7 +273,7 @@ static struct printk_ringbuffer name = { \ + .infos = &_##name##_infos[0], \ + .head_id = ATOMIC_INIT(DESC0_ID(descbits)), \ + .tail_id = ATOMIC_INIT(DESC0_ID(descbits)), \ +- .last_finalized_id = ATOMIC_INIT(DESC0_ID(descbits)), \ ++ .last_finalized_seq = ATOMIC_INIT(0), \ + }, \ + .text_data_ring = { \ + .size_bits = (avgtextbits) + (descbits), \ +@@ -378,7 +392,41 @@ bool prb_read_valid(struct printk_ringbuffer *rb, u64 seq, + bool prb_read_valid_info(struct printk_ringbuffer *rb, u64 seq, + struct printk_info *info, unsigned int *line_count); + ++u64 prb_first_seq(struct printk_ringbuffer *rb); + u64 prb_first_valid_seq(struct printk_ringbuffer *rb); + u64 prb_next_seq(struct printk_ringbuffer *rb); ++u64 prb_next_reserve_seq(struct printk_ringbuffer *rb); ++ ++#ifdef CONFIG_64BIT ++ ++#define __u64seq_to_ulseq(u64seq) (u64seq) ++#define __ulseq_to_u64seq(rb, ulseq) (ulseq) ++ ++#else /* CONFIG_64BIT */ ++ ++#define __u64seq_to_ulseq(u64seq) ((u32)u64seq) ++ ++static inline u64 __ulseq_to_u64seq(struct printk_ringbuffer *rb, u32 ulseq) ++{ ++ u64 rb_first_seq = prb_first_seq(rb); ++ u64 seq; ++ ++ /* ++ * The provided sequence is only the lower 32 bits of the ringbuffer ++ * sequence. It needs to be expanded to 64bit. Get the first sequence ++ * number from the ringbuffer and fold it. ++ * ++ * Having a 32bit representation in the console is sufficient. ++ * If a console ever gets more than 2^31 records behind ++ * the ringbuffer then this is the least of the problems. ++ * ++ * Also the access to the ring buffer is always safe. ++ */ ++ seq = rb_first_seq - (s32)((u32)rb_first_seq - ulseq); ++ ++ return seq; ++} ++ ++#endif /* CONFIG_64BIT */ + + #endif /* _KERNEL_PRINTK_RINGBUFFER_H */ +diff --git a/kernel/printk/printk_safe.c b/kernel/printk/printk_safe.c +index 6d10927a07d8..8d9408d653de 100644 +--- a/kernel/printk/printk_safe.c ++++ b/kernel/printk/printk_safe.c +@@ -26,6 +26,18 @@ void __printk_safe_exit(void) + this_cpu_dec(printk_context); + } + ++void __printk_deferred_enter(void) ++{ ++ cant_migrate(); ++ this_cpu_inc(printk_context); ++} ++ ++void __printk_deferred_exit(void) ++{ ++ cant_migrate(); ++ this_cpu_dec(printk_context); ++} ++ + asmlinkage int vprintk(const char *fmt, va_list args) + { + #ifdef CONFIG_KGDB_KDB +diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c +index 46612fb15fc6..f893f4cfdd38 100644 +--- a/kernel/rcu/rcutorture.c ++++ b/kernel/rcu/rcutorture.c +@@ -2409,6 +2409,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 11a1fac3a589..6f085a159fca 100644 +--- a/kernel/rcu/tree_stall.h ++++ b/kernel/rcu/tree_stall.h +@@ -8,6 +8,7 @@ + */ + + #include ++#include + + ////////////////////////////////////////////////////////////////////////////// + // +@@ -604,6 +605,8 @@ static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps) + if (rcu_stall_is_suppressed()) + return; + ++ nbcon_cpu_emergency_enter(); ++ + /* + * OK, time to rat on our buddy... + * See Documentation/RCU/stallwarn.rst for info on how to debug +@@ -658,6 +661,8 @@ static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps) + panic_on_rcu_stall(); + + rcu_force_quiescent_state(); /* Kick them all. */ ++ ++ nbcon_cpu_emergency_exit(); + } + + static void print_cpu_stall(unsigned long gps) +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index 9b406d988654..5e65826dd984 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -897,14 +897,15 @@ static inline void hrtick_rq_init(struct rq *rq) + + #if defined(CONFIG_SMP) && defined(TIF_POLLING_NRFLAG) + /* +- * Atomically set TIF_NEED_RESCHED and test for TIF_POLLING_NRFLAG, ++ * Atomically set TIF_NEED_RESCHED[_LAZY] and test for TIF_POLLING_NRFLAG, + * this avoids any races wrt polling state changes and thereby avoids + * spurious IPIs. + */ +-static inline bool set_nr_and_not_polling(struct task_struct *p) ++static inline bool set_nr_and_not_polling(struct task_struct *p, int tif_bit) + { + struct thread_info *ti = task_thread_info(p); +- return !(fetch_or(&ti->flags, _TIF_NEED_RESCHED) & _TIF_POLLING_NRFLAG); ++ ++ return !(fetch_or(&ti->flags, 1 << tif_bit) & _TIF_POLLING_NRFLAG); + } + + /* +@@ -921,7 +922,7 @@ static bool set_nr_if_polling(struct task_struct *p) + for (;;) { + if (!(val & _TIF_POLLING_NRFLAG)) + return false; +- if (val & _TIF_NEED_RESCHED) ++ if (val & (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)) + return true; + if (try_cmpxchg(&ti->flags, &val, val | _TIF_NEED_RESCHED)) + break; +@@ -930,9 +931,9 @@ static bool set_nr_if_polling(struct task_struct *p) + } + + #else +-static inline bool set_nr_and_not_polling(struct task_struct *p) ++static inline bool set_nr_and_not_polling(struct task_struct *p, int tif_bit) + { +- set_tsk_need_resched(p); ++ set_tsk_thread_flag(p, tif_bit); + return true; + } + +@@ -1037,28 +1038,47 @@ void wake_up_q(struct wake_q_head *head) + * might also involve a cross-CPU call to trigger the scheduler on + * the target CPU. + */ +-void resched_curr(struct rq *rq) ++static void __resched_curr(struct rq *rq, int lazy) + { ++ int cpu, tif_bit = TIF_NEED_RESCHED + lazy; + struct task_struct *curr = rq->curr; +- int cpu; + + lockdep_assert_rq_held(rq); + +- if (test_tsk_need_resched(curr)) ++ if (unlikely(test_tsk_thread_flag(curr, tif_bit))) + return; + + cpu = cpu_of(rq); + + if (cpu == smp_processor_id()) { +- set_tsk_need_resched(curr); +- set_preempt_need_resched(); ++ set_tsk_thread_flag(curr, tif_bit); ++ if (!lazy) ++ set_preempt_need_resched(); + return; + } + +- if (set_nr_and_not_polling(curr)) +- smp_send_reschedule(cpu); +- else ++ if (set_nr_and_not_polling(curr, tif_bit)) { ++ if (!lazy) ++ smp_send_reschedule(cpu); ++ } else { + trace_sched_wake_idle_without_ipi(cpu); ++ } ++} ++ ++void resched_curr(struct rq *rq) ++{ ++ __resched_curr(rq, 0); ++} ++ ++void resched_curr_lazy(struct rq *rq) ++{ ++ int lazy = IS_ENABLED(CONFIG_PREEMPT_BUILD_AUTO) && !sched_feat(FORCE_NEED_RESCHED) ? ++ TIF_NEED_RESCHED_LAZY_OFFSET : 0; ++ ++ if (lazy && unlikely(test_tsk_thread_flag(rq->curr, TIF_NEED_RESCHED))) ++ return; ++ ++ __resched_curr(rq, lazy); + } + + void resched_cpu(int cpu) +@@ -1131,7 +1151,7 @@ static void wake_up_idle_cpu(int cpu) + if (cpu == smp_processor_id()) + return; + +- if (set_nr_and_not_polling(rq->idle)) ++ if (set_nr_and_not_polling(rq->idle, TIF_NEED_RESCHED)) + smp_send_reschedule(cpu); + else + trace_sched_wake_idle_without_ipi(cpu); +@@ -6722,10 +6742,14 @@ void __noreturn do_task_dead(void) + + static inline void sched_submit_work(struct task_struct *tsk) + { ++ static DEFINE_WAIT_OVERRIDE_MAP(sched_map, LD_WAIT_CONFIG); + unsigned int task_flags; + +- if (task_is_running(tsk)) +- return; ++ /* ++ * Establish LD_WAIT_CONFIG context to ensure none of the code called ++ * will use a blocking primitive -- which would lead to recursion. ++ */ ++ lock_map_acquire_try(&sched_map); + + task_flags = tsk->flags; + /* +@@ -6751,6 +6775,8 @@ static inline void sched_submit_work(struct task_struct *tsk) + * make sure to submit it to avoid deadlocks. + */ + blk_flush_plug(tsk->plug, true); ++ ++ lock_map_release(&sched_map); + } + + static void sched_update_worker(struct task_struct *tsk) +@@ -6763,16 +6789,26 @@ static void sched_update_worker(struct task_struct *tsk) + } + } + +-asmlinkage __visible void __sched schedule(void) ++static __always_inline void __schedule_loop(unsigned int sched_mode) + { +- struct task_struct *tsk = current; +- +- sched_submit_work(tsk); + do { + preempt_disable(); +- __schedule(SM_NONE); ++ __schedule(sched_mode); + sched_preempt_enable_no_resched(); + } while (need_resched()); ++} ++ ++asmlinkage __visible void __sched schedule(void) ++{ ++ struct task_struct *tsk = current; ++ ++#ifdef CONFIG_RT_MUTEXES ++ lockdep_assert(!tsk->sched_rt_mutex); ++#endif ++ ++ if (!task_is_running(tsk)) ++ sched_submit_work(tsk); ++ __schedule_loop(SM_NONE); + sched_update_worker(tsk); + } + EXPORT_SYMBOL(schedule); +@@ -6836,11 +6872,7 @@ void __sched schedule_preempt_disabled(void) + #ifdef CONFIG_PREEMPT_RT + void __sched notrace schedule_rtlock(void) + { +- do { +- preempt_disable(); +- __schedule(SM_RTLOCK_WAIT); +- sched_preempt_enable_no_resched(); +- } while (need_resched()); ++ __schedule_loop(SM_RTLOCK_WAIT); + } + NOKPROBE_SYMBOL(schedule_rtlock); + #endif +@@ -7036,6 +7068,32 @@ static void __setscheduler_prio(struct task_struct *p, int prio) + + #ifdef CONFIG_RT_MUTEXES + ++/* ++ * Would be more useful with typeof()/auto_type but they don't mix with ++ * bit-fields. Since it's a local thing, use int. Keep the generic sounding ++ * name such that if someone were to implement this function we get to compare ++ * notes. ++ */ ++#define fetch_and_set(x, v) ({ int _x = (x); (x) = (v); _x; }) ++ ++void rt_mutex_pre_schedule(void) ++{ ++ lockdep_assert(!fetch_and_set(current->sched_rt_mutex, 1)); ++ sched_submit_work(current); ++} ++ ++void rt_mutex_schedule(void) ++{ ++ lockdep_assert(current->sched_rt_mutex); ++ __schedule_loop(SM_NONE); ++} ++ ++void rt_mutex_post_schedule(void) ++{ ++ sched_update_worker(current); ++ lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0)); ++} ++ + static inline int __rt_effective_prio(struct task_struct *pi_task, int prio) + { + if (pi_task) +@@ -8886,6 +8944,21 @@ static inline void preempt_dynamic_init(void) { } + + #endif /* #ifdef CONFIG_PREEMPT_DYNAMIC */ + ++/* ++ * task_is_pi_boosted - Check if task has been PI boosted. ++ * @p: Task to check. ++ * ++ * Return true if task is subject to priority inheritance. ++ */ ++bool task_is_pi_boosted(const struct task_struct *p) ++{ ++ int prio = p->prio; ++ ++ if (!rt_prio(prio)) ++ return false; ++ return prio != p->normal_prio; ++} ++ + /** + * yield - yield the current processor to other threads. + * +diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c +index 4c3d0d9f3db6..63e19b89c8c3 100644 +--- a/kernel/sched/debug.c ++++ b/kernel/sched/debug.c +@@ -333,6 +333,23 @@ static const struct file_operations sched_debug_fops = { + .release = seq_release, + }; + ++static ssize_t sched_hog_write(struct file *filp, const char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ unsigned long end = jiffies + 60 * HZ; ++ ++ for (; time_before(jiffies, end) && !signal_pending(current);) ++ cpu_relax(); ++ ++ return cnt; ++} ++ ++static const struct file_operations sched_hog_fops = { ++ .write = sched_hog_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ + static struct dentry *debugfs_sched; + + static __init int sched_init_debug(void) +@@ -374,6 +391,8 @@ static __init int sched_init_debug(void) + + debugfs_create_file("debug", 0444, debugfs_sched, NULL, &sched_debug_fops); + ++ debugfs_create_file("hog", 0200, debugfs_sched, NULL, &sched_hog_fops); ++ + return 0; + } + late_initcall(sched_init_debug); +diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c +index b2e1009e5706..e7e5569fd505 100644 +--- a/kernel/sched/fair.c ++++ b/kernel/sched/fair.c +@@ -1022,8 +1022,10 @@ static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se); + * XXX: strictly: vd_i += N*r_i/w_i such that: vd_i > ve_i + * this is probably good enough. + */ +-static void update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se) ++static void update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se, bool tick) + { ++ struct rq *rq = rq_of(cfs_rq); ++ + if ((s64)(se->vruntime - se->deadline) < 0) + return; + +@@ -1042,10 +1044,19 @@ static void update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se) + /* + * The task has consumed its request, reschedule. + */ +- if (cfs_rq->nr_running > 1) { +- resched_curr(rq_of(cfs_rq)); +- clear_buddies(cfs_rq, se); ++ if (cfs_rq->nr_running < 2) ++ return; ++ ++ if (!IS_ENABLED(CONFIG_PREEMPT_BUILD_AUTO) || sched_feat(FORCE_NEED_RESCHED)) { ++ resched_curr(rq); ++ } else { ++ /* Did the task ignore the lazy reschedule request? */ ++ if (tick && test_tsk_thread_flag(rq->curr, TIF_NEED_RESCHED_LAZY)) ++ resched_curr(rq); ++ else ++ resched_curr_lazy(rq); + } ++ clear_buddies(cfs_rq, se); + } + + #include "pelt.h" +@@ -1153,7 +1164,7 @@ static void update_tg_load_avg(struct cfs_rq *cfs_rq) + /* + * Update the current task's runtime statistics. + */ +-static void update_curr(struct cfs_rq *cfs_rq) ++static void __update_curr(struct cfs_rq *cfs_rq, bool tick) + { + struct sched_entity *curr = cfs_rq->curr; + u64 now = rq_clock_task(rq_of(cfs_rq)); +@@ -1180,7 +1191,7 @@ static void update_curr(struct cfs_rq *cfs_rq) + schedstat_add(cfs_rq->exec_clock, delta_exec); + + curr->vruntime += calc_delta_fair(delta_exec, curr); +- update_deadline(cfs_rq, curr); ++ update_deadline(cfs_rq, curr, tick); + update_min_vruntime(cfs_rq); + + if (entity_is_task(curr)) { +@@ -1194,6 +1205,11 @@ static void update_curr(struct cfs_rq *cfs_rq) + account_cfs_rq_runtime(cfs_rq, delta_exec); + } + ++static inline void update_curr(struct cfs_rq *cfs_rq) ++{ ++ __update_curr(cfs_rq, false); ++} ++ + static void update_curr_fair(struct rq *rq) + { + update_curr(cfs_rq_of(&rq->curr->se)); +@@ -5403,7 +5419,7 @@ entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued) + /* + * Update run-time statistics of the 'current'. + */ +- update_curr(cfs_rq); ++ __update_curr(cfs_rq, true); + + /* + * Ensure that runnable average is periodically updated. +@@ -5417,7 +5433,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; + } + /* +@@ -5563,7 +5579,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 +@@ -5823,7 +5839,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq) + + /* Determine whether we need to wake up potentially idle CPU: */ + if (rq->curr == rq->idle && rq->cfs.nr_running) +- resched_curr(rq); ++ resched_curr_lazy(rq); + } + + #ifdef CONFIG_SMP +@@ -6528,7 +6544,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); +@@ -8206,7 +8222,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ + * prevents us from potentially nominating it as a false LAST_BUDDY + * below. + */ +- if (test_tsk_need_resched(curr)) ++ if (need_resched()) + return; + + /* Idle tasks are by definition preempted by non-idle tasks. */ +@@ -8248,7 +8264,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ + return; + + preempt: +- resched_curr(rq); ++ resched_curr_lazy(rq); + } + + #ifdef CONFIG_SMP +@@ -12394,7 +12410,7 @@ static inline void task_tick_core(struct rq *rq, struct task_struct *curr) + */ + if (rq->core->core_forceidle_count && rq->cfs.nr_running == 1 && + __entity_slice_used(&curr->se, MIN_NR_TASKS_DURING_FORCEIDLE)) +- resched_curr(rq); ++ resched_curr_lazy(rq); + } + + /* +@@ -12559,7 +12575,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 f770168230ae..dd8b35f67fed 100644 +--- a/kernel/sched/features.h ++++ b/kernel/sched/features.h +@@ -89,3 +89,5 @@ SCHED_FEAT(UTIL_EST_FASTUP, true) + SCHED_FEAT(LATENCY_WARN, false) + + SCHED_FEAT(HZ_BW, true) ++ ++SCHED_FEAT(FORCE_NEED_RESCHED, false) +diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c +index 5007b25c5bc6..95e1b3df1400 100644 +--- a/kernel/sched/idle.c ++++ b/kernel/sched/idle.c +@@ -57,8 +57,7 @@ static noinline int __cpuidle cpu_idle_poll(void) + ct_cpuidle_enter(); + + raw_local_irq_enable(); +- while (!tif_need_resched() && +- (cpu_idle_force_poll || tick_check_broadcast_expired())) ++ while (!need_resched() && (cpu_idle_force_poll || tick_check_broadcast_expired())) + cpu_relax(); + raw_local_irq_disable(); + +diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c +index 4ac36eb4cdee..acd1510e8d47 100644 +--- a/kernel/sched/rt.c ++++ b/kernel/sched/rt.c +@@ -2253,8 +2253,11 @@ static int rto_next_cpu(struct root_domain *rd) + + rd->rto_cpu = cpu; + +- if (cpu < nr_cpu_ids) ++ if (cpu < nr_cpu_ids) { ++ if (!has_pushable_tasks(cpu_rq(cpu))) ++ continue; + return cpu; ++ } + + rd->rto_cpu = -1; + +diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h +index 8cbbbea7fdbb..00cdf0db8732 100644 +--- a/kernel/sched/sched.h ++++ b/kernel/sched/sched.h +@@ -2438,6 +2438,7 @@ extern void init_sched_fair_class(void); + extern void reweight_task(struct task_struct *p, const struct load_weight *lw); + + extern void resched_curr(struct rq *rq); ++extern void resched_curr_lazy(struct rq *rq); + extern void resched_cpu(int cpu); + + extern struct rt_bandwidth def_rt_bandwidth; +diff --git a/kernel/signal.c b/kernel/signal.c +index 21903f524ef8..9f240d9cb240 100644 +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -2329,15 +2329,35 @@ static int ptrace_stop(int exit_code, int why, unsigned long message, + do_notify_parent_cldstop(current, false, why); + + /* +- * Don't want to allow preemption here, because +- * sys_ptrace() needs this task to be inactive. ++ * The previous do_notify_parent_cldstop() invocation woke ptracer. ++ * One a PREEMPTION kernel this can result in preemption requirement ++ * which will be fulfilled after read_unlock() and the ptracer will be ++ * put on the CPU. ++ * The ptracer is in wait_task_inactive(, __TASK_TRACED) waiting for ++ * this task wait in schedule(). If this task gets preempted then it ++ * remains enqueued on the runqueue. The ptracer will observe this and ++ * then sleep for a delay of one HZ tick. In the meantime this task ++ * gets scheduled, enters schedule() and will wait for the ptracer. + * +- * XXX: implement read_unlock_no_resched(). ++ * This preemption point is not bad from correctness point of view but ++ * extends the runtime by one HZ tick time due to the ptracer's sleep. ++ * The preempt-disable section ensures that there will be no preemption ++ * between unlock and schedule() and so improving the performance since ++ * the ptracer has no reason to sleep. ++ * ++ * On PREEMPT_RT locking tasklist_lock does not disable preemption. ++ * Therefore the task can be preempted (after ++ * do_notify_parent_cldstop()) before unlocking tasklist_lock so there ++ * is no benefit in doing this. The optimisation is harmful on ++ * PEEMPT_RT because the spinlock_t (in cgroup_enter_frozen()) must not ++ * be acquired with disabled preemption. + */ +- 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 bd9716d7bb63..2fde8af88e48 100644 +--- a/kernel/softirq.c ++++ b/kernel/softirq.c +@@ -247,6 +247,19 @@ void __local_bh_enable_ip(unsigned long ip, unsigned int cnt) + } + EXPORT_SYMBOL(__local_bh_enable_ip); + ++void softirq_preempt(void) ++{ ++ if (WARN_ON_ONCE(!preemptible())) ++ return; ++ ++ if (WARN_ON_ONCE(__this_cpu_read(softirq_ctrl.cnt) != SOFTIRQ_OFFSET)) ++ return; ++ ++ __local_bh_enable(SOFTIRQ_OFFSET, true); ++ /* preemption point */ ++ __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET); ++} ++ + /* + * Invoked from ksoftirqd_run() outside of the interrupt disabled section + * to acquire the per CPU local lock for reentrancy protection. +@@ -623,6 +636,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 +@@ -635,6 +666,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(); + } + +@@ -967,12 +1002,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 57e5cb36f1bc..c4ae45701fab 100644 +--- a/kernel/time/hrtimer.c ++++ b/kernel/time/hrtimer.c +@@ -1812,7 +1812,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); +@@ -1925,7 +1925,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 55cbc49f70d1..1a0ed106b192 100644 +--- a/kernel/time/tick-sched.c ++++ b/kernel/time/tick-sched.c +@@ -795,7 +795,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 63a8ce7177dd..b3fbe97d1e34 100644 +--- a/kernel/time/timer.c ++++ b/kernel/time/timer.c +@@ -1470,9 +1470,16 @@ static inline void timer_base_unlock_expiry(struct timer_base *base) + */ + static void timer_sync_wait_running(struct timer_base *base) + { +- if (atomic_read(&base->timer_waiters)) { ++ bool need_preempt; ++ ++ need_preempt = task_is_pi_boosted(current); ++ if (need_preempt || atomic_read(&base->timer_waiters)) { + raw_spin_unlock_irq(&base->lock); + spin_unlock(&base->expiry_lock); ++ ++ if (need_preempt) ++ softirq_preempt(); ++ + spin_lock(&base->expiry_lock); + raw_spin_lock_irq(&base->lock); + } +@@ -2054,7 +2061,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 be878005e344..fe755b1c2fe6 100644 +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -2708,6 +2708,8 @@ unsigned int tracing_gen_ctx_irq_test(unsigned int irqs_status) + + if (tif_need_resched()) + trace_flags |= TRACE_FLAG_NEED_RESCHED; ++ if (tif_need_resched_lazy()) ++ trace_flags |= TRACE_FLAG_NEED_RESCHED_LAZY; + if (test_preempt_need_resched()) + trace_flags |= TRACE_FLAG_PREEMPT_RESCHED; + return (trace_flags << 16) | (min_t(unsigned int, pc & 0xff, 0xf)) | +diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c +index 3b7d3e9eb6ea..5a4fefbc0856 100644 +--- a/kernel/trace/trace_output.c ++++ b/kernel/trace/trace_output.c +@@ -460,17 +460,29 @@ int trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry) + (entry->flags & TRACE_FLAG_IRQS_OFF && bh_off) ? 'D' : + (entry->flags & TRACE_FLAG_IRQS_OFF) ? 'd' : + bh_off ? 'b' : +- (entry->flags & TRACE_FLAG_IRQS_NOSUPPORT) ? 'X' : ++ !IS_ENABLED(CONFIG_TRACE_IRQFLAGS_SUPPORT) ? 'X' : + '.'; + +- switch (entry->flags & (TRACE_FLAG_NEED_RESCHED | ++ switch (entry->flags & (TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_NEED_RESCHED_LAZY | + TRACE_FLAG_PREEMPT_RESCHED)) { ++ case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_NEED_RESCHED_LAZY | TRACE_FLAG_PREEMPT_RESCHED: ++ need_resched = 'B'; ++ break; + case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_PREEMPT_RESCHED: + need_resched = 'N'; + break; ++ case TRACE_FLAG_NEED_RESCHED_LAZY | TRACE_FLAG_PREEMPT_RESCHED: ++ need_resched = 'L'; ++ break; ++ case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_NEED_RESCHED_LAZY: ++ need_resched = 'b'; ++ break; + case TRACE_FLAG_NEED_RESCHED: + need_resched = 'n'; + break; ++ case TRACE_FLAG_NEED_RESCHED_LAZY: ++ need_resched = 'l'; ++ break; + case TRACE_FLAG_PREEMPT_RESCHED: + need_resched = 'p'; + break; +diff --git a/localversion-rt b/localversion-rt +new file mode 100644 +index 000000000000..366440d74b77 +--- /dev/null ++++ b/localversion-rt +@@ -0,0 +1 @@ ++-rt35 +diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c +index 7b41ee8740cb..294cb699bff2 100644 +--- a/net/bridge/br_switchdev.c ++++ b/net/bridge/br_switchdev.c +@@ -173,6 +173,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) + { +@@ -184,6 +204,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); + } + +@@ -195,6 +217,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/core/dev.c b/net/core/dev.c +index 5a5bd339f11e..c57b4918b2b4 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -1428,8 +1428,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); +@@ -4705,15 +4705,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); +-} +- + /* + * After we queued a packet into sd->input_pkt_queue, + * we need to make sure this queue is serviced soon. +@@ -6426,8 +6417,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; +@@ -6455,6 +6447,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(). +@@ -6462,6 +6460,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) +@@ -6682,6 +6687,32 @@ 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 int napi_threaded_poll(void *data) + { + struct napi_struct *napi = data; +@@ -10792,6 +10823,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; +@@ -11619,7 +11651,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/skbuff.c b/net/core/skbuff.c +index cf3c7a0062b8..c82011571e07 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -6889,8 +6889,13 @@ 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 ++ } + } + + static void skb_splice_csum_page(struct sk_buff *skb, struct page *page, +diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig +index 8e698bea99a3..93bd38d95236 100644 +--- a/net/dsa/Kconfig ++++ b/net/dsa/Kconfig +@@ -172,4 +172,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 12e305824a96..b9d63a7c3a77 100644 +--- a/net/dsa/Makefile ++++ b/net/dsa/Makefile +@@ -38,6 +38,7 @@ 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 + + # for tracing framework to find trace.h + CFLAGS_trace.o := -I$(src) +diff --git a/net/dsa/slave.c b/net/dsa/slave.c +index 48db91b33390..75024f6fd3ef 100644 +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1753,6 +1753,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) + { +@@ -1762,6 +1797,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}; + struct dsa_switch *ds = dp->ds; +@@ -1778,6 +1814,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) +@@ -1836,6 +1873,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), + }; + struct dsa_switch *ds = dp->ds; + struct netdev_hw_addr *ha; +@@ -2387,6 +2425,9 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = { + .get_mm = dsa_slave_get_mm, + .set_mm = dsa_slave_set_mm, + .get_mm_stats = dsa_slave_get_mm_stats, ++ .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 = { +@@ -2999,7 +3040,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)) +@@ -3007,13 +3050,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..b32ae484af9c +--- /dev/null ++++ b/net/dsa/tag_netc.c +@@ -0,0 +1,489 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2023 NXP ++ */ ++ ++#include ++#include ++#include ++#include "tag.h" ++#include "tag_8021q.h" ++ ++#define NETC_8021Q_NAME "netc-8021q" ++ ++/* ++ * NETC HEADRER after Source MAC ++ * ++ * | 2B | 2B | 0 / 4B / 8B / 12B / 16B | ++ * +------------ +-------------+---------------------------+ ++ * | 0xDADC | HEADRER | DATA | ++ * +------------ +------------ +---------------------------+ ++ */ ++ ++#define NETC_HEADER_LEN 4 ++#define NETC_HEADER_DATA_TS_ID_LEN 4 ++#define NETC_HEADER_DATA_TIMESTAP_LEN 8 ++#define NETC_HEADER_DATA_CMD_LEN 16 ++ ++#define NETC_HEADER_HOST_TO_SWITCH BIT(15) ++ ++/* Binary structure of the NETC Header ETH_P_NETC_META: ++ * ++ * | 15 | 14 | 13 | 12 | 11 | 10 - 9 | 7 - 4 | 3 - 0 | ++ * +-----------+------+-----------+-------+-------+--------+-----------+---------+ ++ * | TO HOST 0 | META | HOST Only | RX TS | TX TS | | Switch ID | Port ID | ++ * +-----------+------+-----------+-------+-------+--------+-----------+---------+ ++ */ ++#define NETC_RX_HEADER_IS_METADATA BIT(14) ++#define NETC_RX_HEADER_HOST_ONLY BIT(13) ++#define NETC_RX_HEADER_RX_TIMESTAP BIT(12) ++#define NETC_RX_HEADER_TX_TIMESTAP BIT(11) ++ ++#define NETC_HEADER_PORT_MASK 0x0F ++#define NETC_HEADER_PORT_OFFSET 0 ++#define NETC_HEADER_SWITCH_MASK 0xF0 ++#define NETC_HEADER_SWITCH_OFFSET 4 ++#define NETC_RX_HEADER_PORT_ID(x) ((x) & NETC_HEADER_PORT_MASK) ++#define NETC_RX_HEADER_SWITCH_ID(x) (((x) & NETC_HEADER_SWITCH_MASK) >> NETC_HEADER_SWITCH_OFFSET) ++ ++/* ++ * RX RX_Timestamp: ++ * ++ * | 64 - 0 | ++ * +------------ + ++ * | TimeStamp | ++ * +------------ + ++ */ ++#define NETC_HEADER_TIMESTAMP_LEN 8 ++ ++/* ++ * RX TX_Timestamp: ++ * ++ * | 64 - 0 | 32 - 0 | ++ * +------------ +------------ + ++ * | TimeStamp | TS_ID | ++ * +------------ +------------ + ++ */ ++#define NETC_RX_HEADER_TS_ID_LEN 4 ++ ++/* TX header */ ++ ++/* ++ * Binary structure of the NETC Header ETH_P_NETC_META: ++ * ++ * | 15 | 14 | 13 | 12 | 11 | 10 - 9 | 7 - 4 | 3 - 0 | ++ * +-----------+------+--------+-------+---------+--------+-----------+---------+ ++ * | To SW 1 | META | | | TAKE TS | | SWITCH ID | PORT ID | ++ * +-----------+------+--------+-------+------ -+--------+-----------+---------+ ++ */ ++ ++#define NETC_TX_HEADER_IS_METADATA BIT(14) ++#define NETC_TX_HEADER_TAKE_TS BIT(11) ++ ++#define NETC_TX_HEADER_TSTAMP_ID(x) (x) ++#define NETC_TX_HEADER_SWITCHID(x) (((x) << NETC_HEADER_SWITCH_OFFSET) & NETC_HEADER_SWITCH_MASK) ++#define NETC_TX_HEADER_DESTPORTID(x) ((x) & NETC_HEADER_PORT_MASK) ++ ++/* ++ * TX Take TS: ++ * ++ * | 32 - 0 | ++ * +------------ + ++ * | TS_ID | ++ * +------------ + ++ */ ++#define NETC_TX_HEADER_TS_ID_LEN 4 ++ ++void print_skb_data(struct sk_buff *skb) ++{ ++ u8 *buf = skb->data - ETH_HLEN; ++ int len = skb->len; ++ int i = 0; ++ ++ if (!skb) { ++ printk("Bad skb parameter"); ++ return; ++ } ++ printk("Packet length = 0x%x", len); ++ ++ for (i = 0; i < len; i += 8) { ++ printk("0x%04x: %02x %02x %02x %02x %02x %02x %02x %02x\n", i, ++ buf[i + 0], buf[i + 1], buf[i + 2], buf[i + 3], ++ buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7]); ++ } ++ printk("\n"); ++} ++ ++/* 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) ++ return false; ++ ++ if ((dmac & NETC_LINKLOCAL_FILTER_A_MASK) == ++ NETC_LINKLOCAL_FILTER_A) ++ return true; ++ ++ if ((dmac & NETC_LINKLOCAL_FILTER_B_MASK) == ++ NETC_LINKLOCAL_FILTER_B) ++ return true; ++ ++ return false; ++} ++ ++/* Send VLAN tags with a TPID that blends in with whatever VLAN protocol a ++ * bridge spanning ports of this switch might have. ++ */ ++static u16 netc_xmit_tpid(struct dsa_port *dp) ++{ ++ struct dsa_switch *ds = dp->ds; ++ struct dsa_port *other_dp; ++ u16 proto; ++ ++ if (!dsa_port_is_vlan_filtering(dp)) ++ return ETH_P_NETC_8021Q; ++ ++ /* Port is VLAN-aware, so there is a bridge somewhere (a single one, ++ * we're sure about that). It may not be on this port though, so we ++ * need to find it. ++ */ ++ dsa_switch_for_each_port(other_dp, ds) { ++ struct net_device *br = dsa_port_bridge_dev_get(other_dp); ++ ++ if (!br) ++ continue; ++ ++ /* Error is returned only if CONFIG_BRIDGE_VLAN_FILTERING, ++ * which seems pointless to handle, as our port cannot become ++ * VLAN-aware in that case. ++ */ ++ br_vlan_get_proto(br, &proto); ++ ++ return proto; ++ } ++ ++ WARN_ONCE(1, "Port is VLAN-aware but cannot find associated bridge!\n"); ++ ++ return ETH_P_NETC_8021Q; ++} ++ ++static struct sk_buff *netc_imprecise_xmit(struct sk_buff *skb, ++ struct net_device *netdev) ++{ ++ struct dsa_port *dp = dsa_slave_to_port(netdev); ++ unsigned int bridge_num = dsa_port_bridge_num_get(dp); ++ struct net_device *br = dsa_port_bridge_dev_get(dp); ++ u16 tx_vid; ++ ++ /* If the port is under a VLAN-aware bridge, just slide the ++ * VLAN-tagged packet into the FDB and hope for the best. ++ * This works because we support a single VLAN-aware bridge ++ * across the entire dst, and its VLANs cannot be shared with ++ * any standalone port. ++ */ ++ if (br_vlan_enabled(br)) ++ return skb; ++ ++ /* If the port is under a VLAN-unaware bridge, use an imprecise ++ * TX VLAN that targets the bridge's entire broadcast domain, ++ * instead of just the specific port. ++ */ ++ tx_vid = dsa_tag_8021q_bridge_vid(bridge_num); ++ ++ if (unlikely(skb_vlan_tag_present(skb))) { ++ skb = __vlan_hwaccel_push_inside(skb); ++ if (!skb) { ++ WARN_ONCE(1, "Failed to push VLAN tag to payload!\n"); ++ return NULL; ++ } ++ } ++ ++ return dsa_8021q_xmit(skb, netdev, netc_xmit_tpid(dp), tx_vid); ++} ++ ++static struct sk_buff *netc_meta_xmit(struct sk_buff *skb, ++ struct net_device *netdev) ++{ ++ struct sk_buff *clone = NETC_SKB_CB(skb)->clone; ++ struct dsa_port *dp = dsa_slave_to_port(netdev); ++ int len = NETC_HEADER_LEN; ++ __be16 *tx_header; ++ __be32 *p_ts_id; ++ ++ if (clone) ++ len = len + NETC_TX_HEADER_TS_ID_LEN; ++ ++ skb_push(skb, len); ++ ++ dsa_alloc_etype_header(skb, len); ++ ++ tx_header = dsa_etype_header_pos_tx(skb); ++ ++ tx_header[0] = htons(ETH_P_NETC_META); ++ tx_header[1] = htons(NETC_HEADER_HOST_TO_SWITCH | ++ NETC_TX_HEADER_SWITCHID(dp->ds->index) | ++ NETC_TX_HEADER_DESTPORTID(dp->index)); ++ if(clone) { ++ tx_header[1] |= htons(NETC_TX_HEADER_TAKE_TS); ++ p_ts_id = dsa_etype_header_pos_tx(skb) + NETC_HEADER_LEN; ++ p_ts_id[0] = cpu_to_be32(NETC_SKB_CB(clone)->ts_id); ++ } ++ ++ return skb; ++} ++ ++static struct sk_buff *netc_8021q_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); ++ ++ return dsa_8021q_xmit(skb, netdev, netc_xmit_tpid(dp), ++ ((pcp << VLAN_PRIO_SHIFT) | tx_vid)); ++} ++ ++static struct sk_buff *netc_xmit(struct sk_buff *skb, ++ struct net_device *netdev) ++{ ++ if (skb->offload_fwd_mark) ++ return netc_imprecise_xmit(skb, netdev); ++ ++ if (unlikely(netc_is_link_local(skb))) ++ return netc_meta_xmit(skb, netdev); ++ ++ return netc_8021q_xmit(skb, netdev); ++} ++ ++static bool netc_skb_has_tag_8021q(const struct sk_buff *skb) ++{ ++ u16 tpid = ntohs(eth_hdr(skb)->h_proto); ++ ++ return tpid == ETH_P_NETC || tpid == ETH_P_8021Q || ++ skb_vlan_tag_present(skb); ++} ++ ++static bool netc_skb_has_inband_control_extension(const struct sk_buff *skb) ++{ ++ return ntohs(eth_hdr(skb)->h_proto) == ETH_P_NETC_META; ++} ++ ++static struct sk_buff *netc_rcv_meta_cmd(struct sk_buff *skb, u16 rx_header) ++{ ++ u8 *buf = dsa_etype_header_pos_rx(skb) + NETC_HEADER_LEN; ++ int switch_id = NETC_RX_HEADER_SWITCH_ID(rx_header); ++ int source_port = NETC_RX_HEADER_PORT_ID(rx_header); ++ struct netc_tagger_data *tagger_data; ++ struct net_device *master = skb->dev; ++ struct dsa_port *cpu_dp; ++ struct dsa_switch *ds; ++ ++ cpu_dp = master->dsa_ptr; ++ ds = dsa_switch_find(cpu_dp->dst->index, switch_id); ++ if (!ds) { ++ net_err_ratelimited("%s: cannot find switch id %d\n", ++ master->name, switch_id); ++ return NULL; ++ } ++ ++ tagger_data = netc_tagger_data(ds); ++ if (!tagger_data->meta_cmd_handler) ++ return NULL; ++ ++ if (skb_is_nonlinear(skb)) ++ if(skb_linearize(skb)) ++ return NULL; ++ ++ tagger_data->meta_cmd_handler(ds, source_port, buf, ++ skb->len - NETC_HEADER_LEN - 2 * ETH_ALEN); ++ ++ /* Discard the meta frame */ ++ return NULL; ++} ++ ++static struct sk_buff *netc_rcv_tx_timestap(struct sk_buff *skb, u16 rx_header) ++{ ++ u8 *buf = dsa_etype_header_pos_rx(skb) + NETC_HEADER_LEN; ++ int switch_id = NETC_RX_HEADER_SWITCH_ID(rx_header); ++ int source_port = NETC_RX_HEADER_PORT_ID(rx_header); ++ struct netc_tagger_data *tagger_data; ++ struct net_device *master = skb->dev; ++ struct dsa_port *cpu_dp; ++ struct dsa_switch *ds; ++ u32 ts_id; ++ u64 tstamp; ++ ++ cpu_dp = master->dsa_ptr; ++ ++ ds = dsa_switch_find(cpu_dp->dst->index, switch_id); ++ if (!ds) { ++ net_err_ratelimited("%s: cannot find switch id %d\n", ++ master->name, switch_id); ++ return NULL; ++ } ++ ++ tagger_data = netc_tagger_data(ds); ++ if (!tagger_data->meta_tstamp_handler) ++ return NULL; ++ ++ ++ tstamp = be64_to_cpu(*(__be64 *)buf); ++ ts_id = be32_to_cpu(*(__be32 *)(buf + NETC_HEADER_TIMESTAMP_LEN)); ++ ++ tagger_data->meta_tstamp_handler(ds, source_port, ts_id, tstamp); ++ ++ /* Discard the meta frame, we've consumed the timestamps it contained */ ++ return NULL; ++} ++ ++static struct sk_buff *netc_rcv_inband_control_extension(struct sk_buff *skb, ++ int *source_port, ++ int *switch_id, ++ bool *host_only) ++{ ++ u16 rx_header; ++ int len = 0; ++ ++ if (unlikely(!pskb_may_pull(skb, ++ NETC_HEADER_LEN + ++ NETC_HEADER_TIMESTAMP_LEN + ++ NETC_RX_HEADER_TS_ID_LEN))) ++ return NULL; ++ ++ rx_header = ntohs(*(__be16 *)skb->data); ++ ++ if (rx_header & NETC_RX_HEADER_HOST_ONLY) ++ *host_only = true; ++ ++ if (rx_header & NETC_RX_HEADER_IS_METADATA) ++ return netc_rcv_meta_cmd(skb, rx_header); ++ ++ if (rx_header & NETC_RX_HEADER_TX_TIMESTAP) ++ return netc_rcv_tx_timestap(skb, rx_header); ++ ++ /* RX Timestamp frame */ ++ if (rx_header & NETC_RX_HEADER_RX_TIMESTAP) { ++ u64 *tstamp = &NETC_SKB_CB(skb)->tstamp; ++ u8 *buf = dsa_etype_header_pos_rx(skb) + NETC_HEADER_LEN; ++ ++ *tstamp = be64_to_cpu(*(__be64 *)buf); ++ ++ len += NETC_HEADER_TIMESTAMP_LEN; ++ } ++ ++ *source_port = NETC_RX_HEADER_PORT_ID(rx_header); ++ *switch_id = NETC_RX_HEADER_SWITCH_ID(rx_header); ++ ++ len += NETC_HEADER_LEN; ++ ++ /* Advance skb->data past the DSA header */ ++ skb_pull_rcsum(skb, len); ++ ++ dsa_strip_etype_header(skb, len); ++ ++ /* With skb->data in its final place, update the MAC header ++ * so that eth_hdr() continues to works properly. ++ */ ++ skb_set_mac_header(skb, -ETH_HLEN); ++ ++ return skb; ++} ++ ++/* If the VLAN in the packet is a tag_8021q one, set @source_port and ++ * @switch_id and strip the header. Otherwise set @vid and keep it in the ++ * packet. ++ */ ++static void netc_vlan_rcv(struct sk_buff *skb, int *source_port, ++ int *switch_id, int *vbid, u16 *vid) ++{ ++ struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); ++ u16 vlan_tci; ++ ++ if (skb_vlan_tag_present(skb)) ++ vlan_tci = skb_vlan_tag_get(skb); ++ else ++ vlan_tci = ntohs(hdr->h_vlan_TCI); ++ ++ if (vid_is_dsa_8021q(vlan_tci & VLAN_VID_MASK)) ++ return dsa_8021q_rcv(skb, source_port, switch_id, vbid); ++ ++ /* Try our best with imprecise RX */ ++ *vid = vlan_tci & VLAN_VID_MASK; ++} ++ ++static struct sk_buff *netc_rcv(struct sk_buff *skb, ++ struct net_device *netdev) ++{ ++ int src_port = -1, switch_id = -1, vbid = -1; ++ bool host_only = false; ++ u16 vid = 0; ++ ++ if (netc_skb_has_inband_control_extension(skb)) { ++ skb = netc_rcv_inband_control_extension(skb, &src_port, ++ &switch_id, ++ &host_only); ++ if (!skb) ++ return NULL; ++ } ++ ++ /* Packets with in-band control extensions might still have RX VLANs */ ++ if (likely(netc_skb_has_tag_8021q(skb))) ++ netc_vlan_rcv(skb, &src_port, &switch_id, &vbid, &vid); ++ ++ if (vbid >= 1) ++ skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid); ++ else if (src_port == -1 || switch_id == -1) ++ skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid); ++ else ++ skb->dev = dsa_master_find_slave(netdev, switch_id, src_port); ++ if (!skb->dev) { ++ /* netdev_warn(netdev, "Couldn't decode source port\n"); */ ++ return NULL; ++ } ++ ++ if (!host_only) ++ dsa_default_offload_fwd_mark(skb); ++ ++ return skb; ++} ++ ++static void netc_disconnect(struct dsa_switch *ds) ++{ ++ struct netc_tagger_data *tagger_data = ds->tagger_data; ++ ++ kfree(tagger_data); ++ ds->tagger_data = NULL; ++} ++ ++static int netc_connect(struct dsa_switch *ds) ++{ ++ struct netc_tagger_data *data; ++ ++ data = kzalloc(sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ ds->tagger_data = data; ++ ++ return 0; ++} ++ ++static const struct dsa_device_ops netc_netdev_ops = { ++ .name = NETC_8021Q_NAME, ++ .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, NETC_8021Q_NAME); ++ ++module_dsa_tag_driver(netc_netdev_ops); +diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c +index ade3eeb2f3e6..1f70d9859862 100644 +--- a/net/dsa/tag_sja1105.c ++++ b/net/dsa/tag_sja1105.c +@@ -267,9 +267,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); +diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile +index 504f954a1b28..348993bf8eab 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 rss.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 mm.o \ +- module.o pse-pd.o plca.o mm.o ++ module.o pse-pd.o plca.o mm.o preempt.o +diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c +index 4486cbe2faf0..07b9b939e294 100644 +--- a/net/ethtool/ioctl.c ++++ b/net/ethtool/ioctl.c +@@ -2765,6 +2765,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 +@@ -2824,6 +2867,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)) +@@ -3051,6 +3097,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 fe3553f60bf3..4734689e173f 100644 +--- a/net/ethtool/netlink.c ++++ b/net/ethtool/netlink.c +@@ -306,6 +306,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { + [ETHTOOL_MSG_PLCA_GET_STATUS] = ðnl_plca_status_request_ops, + [ETHTOOL_MSG_MM_GET] = ðnl_mm_request_ops, + [ETHTOOL_MSG_MM_SET] = ðnl_mm_request_ops, ++ [ETHTOOL_MSG_PREEMPT_GET] = ðnl_preempt_request_ops, + }; + + static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) +@@ -639,6 +640,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = { + [ETHTOOL_MSG_MODULE_NTF] = ðnl_module_request_ops, + [ETHTOOL_MSG_PLCA_NTF] = ðnl_plca_cfg_request_ops, + [ETHTOOL_MSG_MM_NTF] = ðnl_mm_request_ops, ++ [ETHTOOL_MSG_PREEMPT_NTF] = ðnl_preempt_request_ops, + }; + + /* default notification handler */ +@@ -737,6 +739,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = { + [ETHTOOL_MSG_MODULE_NTF] = ethnl_default_notify, + [ETHTOOL_MSG_PLCA_NTF] = ethnl_default_notify, + [ETHTOOL_MSG_MM_NTF] = ethnl_default_notify, ++ [ETHTOOL_MSG_PREEMPT_NTF] = ethnl_default_notify, + }; + + void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) +@@ -1129,6 +1132,20 @@ static const struct genl_ops ethtool_genl_ops[] = { + .policy = ethnl_mm_set_policy, + .maxattr = ARRAY_SIZE(ethnl_mm_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 9a333a8d04c1..ee2dddc44224 100644 +--- a/net/ethtool/netlink.h ++++ b/net/ethtool/netlink.h +@@ -395,6 +395,7 @@ extern const struct ethnl_request_ops ethnl_rss_request_ops; + extern const struct ethnl_request_ops ethnl_plca_cfg_request_ops; + extern const struct ethnl_request_ops ethnl_plca_status_request_ops; + extern const struct ethnl_request_ops ethnl_mm_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]; +@@ -441,6 +442,7 @@ extern const struct nla_policy ethnl_plca_set_cfg_policy[ETHTOOL_A_PLCA_MAX + 1] + extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1]; + extern const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1]; + extern const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1]; ++extern const struct nla_policy preempt_get_policy[ETHTOOL_A_PREEMPT_MAX + 1]; + + int ethnl_set_features(struct sk_buff *skb, struct genl_info *info); + int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info); +@@ -448,6 +450,7 @@ int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info); + int ethnl_tunnel_info_doit(struct sk_buff *skb, struct genl_info *info); + int ethnl_tunnel_info_start(struct netlink_callback *cb); + int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb); ++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..06f47fc75c58 +--- /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, ++ const 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/packet/af_packet.c b/net/packet/af_packet.c +index 3e5703537e4e..9a4bae08b82e 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -3261,9 +3261,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; +@@ -3329,6 +3330,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); + } +@@ -3358,7 +3360,6 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex, + static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, + int addr_len) + { +- struct sock *sk = sock->sk; + char name[sizeof(uaddr->sa_data_min) + 1]; + + /* +@@ -3373,13 +3374,13 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, + memcpy(name, uaddr->sa_data, sizeof(uaddr->sa_data_min)); + name[sizeof(uaddr->sa_data_min)] = 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) + { + struct sockaddr_ll *sll = (struct sockaddr_ll *)uaddr; +- struct sock *sk = sock->sk; + + /* + * Check legality +@@ -3390,7 +3391,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/Kconfig b/net/sched/Kconfig +index 470c70deffe2..1f64c0ee84d1 100644 +--- a/net/sched/Kconfig ++++ b/net/sched/Kconfig +@@ -925,6 +925,19 @@ config NET_ACT_GATE + To compile this code as a module, choose M here: the + module will be called act_gate. + ++config NET_ACT_FRER ++ tristate "Frame frer tc action" ++ depends on NET_CLS_ACT ++ help ++ Say Y here to support frame replication and elimination for ++ reliability, which is defined by IEEE 802.1CB. ++ This action allow to add a frer tag. It also allow to remove ++ the frer tag and drop repeat frames. ++ ++ If unsure, say N. ++ To compile this code as a module, choose M here: the ++ module will be called act_frer. ++ + config NET_IFE_SKBMARK + tristate "Support to encoding decoding skb mark on IFE action" + depends on NET_ACT_IFE +diff --git a/net/sched/Makefile b/net/sched/Makefile +index b5fd49641d91..6ff9bc76febc 100644 +--- a/net/sched/Makefile ++++ b/net/sched/Makefile +@@ -32,6 +32,7 @@ obj-$(CONFIG_NET_IFE_SKBTCINDEX) += act_meta_skbtcindex.o + obj-$(CONFIG_NET_ACT_TUNNEL_KEY)+= act_tunnel_key.o + obj-$(CONFIG_NET_ACT_CT) += act_ct.o + obj-$(CONFIG_NET_ACT_GATE) += act_gate.o ++obj-$(CONFIG_NET_ACT_FRER) += act_frer.o + obj-$(CONFIG_NET_SCH_FIFO) += sch_fifo.o + obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o + obj-$(CONFIG_NET_SCH_HFSC) += sch_hfsc.o +diff --git a/net/sched/act_frer.c b/net/sched/act_frer.c +new file mode 100644 +index 000000000000..af455ef08813 +--- /dev/null ++++ b/net/sched/act_frer.c +@@ -0,0 +1,731 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* Copyright 2021 NXP */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define FRER_SEQ_SPACE 16 ++#define FRER_RCVY_RESET_MSEC 100 ++#define FRER_RCVY_INVALID_SEQ 0x100 ++#define FRER_RCVY_PASSED 0 ++#define FRER_RCVY_DISCARDED -1 ++ ++static unsigned int frer_net_id; ++static struct tc_action_ops act_frer_ops; ++ ++struct r_tag { ++ __be16 reserved; ++ __be16 sequence_nr; ++ __be16 encap_proto; ++} __packed; ++ ++struct rtag_ethhdr { ++ struct ethhdr ethhdr; ++ struct r_tag h_rtag; ++} __packed; ++ ++struct rtag_vlan_ethhdr { ++ struct vlan_ethhdr vlanhdr; ++ struct r_tag h_rtag; ++} __packed; ++ ++static const struct nla_policy frer_policy[TCA_FRER_MAX + 1] = { ++ [TCA_FRER_PARMS] = ++ NLA_POLICY_EXACT_LEN(sizeof(struct tc_frer)), ++ [TCA_FRER_TAG_TYPE] = { .type = NLA_U8 }, ++ [TCA_FRER_TAG_ACTION] = { .type = NLA_U8 }, ++ [TCA_FRER_RECOVER] = { .type = NLA_U8 }, ++ [TCA_FRER_RECOVER_ALG] = { .type = NLA_U8 }, ++ [TCA_FRER_RECOVER_HISTORY_LEN] = { .type = NLA_U8 }, ++ [TCA_FRER_RECOVER_RESET_TM] = { .type = NLA_U64 }, ++}; ++ ++static void frer_seq_recovery_reset(struct tcf_frer *frer_act); ++ ++static enum hrtimer_restart frer_hrtimer_func(struct hrtimer *timer) ++{ ++ struct tcf_frer *frer_act = container_of(timer, struct tcf_frer, ++ hrtimer); ++ ktime_t remaining_tm; ++ ++ frer_seq_recovery_reset(frer_act); ++ ++ remaining_tm = (ktime_t)(frer_act->rcvy_reset_msec * 1000000); ++ ++ hrtimer_forward(timer, timer->base->get_time(), remaining_tm); ++ ++ return HRTIMER_RESTART; ++} ++ ++static int frer_rtag_decode(struct sk_buff *skb) ++{ ++ struct rtag_vlan_ethhdr *rtag_vlan_hdr; ++ struct rtag_ethhdr *rtag_hdr; ++ struct vlan_ethhdr *vlanhdr; ++ struct ethhdr *ethhdr; ++ struct r_tag *rtag; ++ bool is_vlan; ++ u16 sequence; ++ u16 proto; ++ ++ ethhdr = (struct ethhdr *)skb_mac_header(skb); ++ proto = ethhdr->h_proto; ++ is_vlan = false; ++ ++ if (proto == htons(ETH_P_8021Q)) { ++ vlanhdr = (struct vlan_ethhdr *)ethhdr; ++ proto = vlanhdr->h_vlan_encapsulated_proto; ++ is_vlan = true; ++ } ++ ++ if (proto != htons(ETH_P_RTAG)) ++ return FRER_RCVY_INVALID_SEQ; ++ ++ if (is_vlan) { ++ rtag_vlan_hdr = (struct rtag_vlan_ethhdr *)ethhdr; ++ rtag = &rtag_vlan_hdr->h_rtag; ++ } else { ++ rtag_hdr = (struct rtag_ethhdr *)ethhdr; ++ rtag = &rtag_hdr->h_rtag; ++ } ++ ++ sequence = ntohs(rtag->sequence_nr); ++ ++ return sequence; ++} ++ ++static int frer_seq_generation_alg(struct tcf_frer *frer_act) ++{ ++ u32 gen_seq_max = frer_act->seq_space - 1; ++ u32 gen_seq_num = frer_act->gen_seq_num; ++ int sequence_number; ++ ++ sequence_number = gen_seq_num; ++ ++ if (gen_seq_num >= gen_seq_max) ++ gen_seq_num = 0; ++ else ++ gen_seq_num++; ++ ++ frer_act->gen_seq_num = gen_seq_num; ++ ++ return sequence_number; ++} ++ ++static int frer_rtag_encode(struct sk_buff *skb, struct tcf_frer *frer_act) ++{ ++ struct vlan_ethhdr *vlanhdr; ++ struct ethhdr *ethhdr; ++ struct r_tag *rtag; ++ int rtag_len, head_len; ++ unsigned char *dst, *src, *p; ++ __be16 *proto, proto_val; ++ ++ ethhdr = (struct ethhdr *)skb_mac_header(skb); ++ if (ethhdr->h_proto == htons(ETH_P_8021Q)) { ++ vlanhdr = (struct vlan_ethhdr *)ethhdr; ++ p = (unsigned char *)(vlanhdr + 1); ++ proto = &vlanhdr->h_vlan_encapsulated_proto; ++ } else { ++ p = (unsigned char *)(ethhdr + 1); ++ proto = ðhdr->h_proto; ++ } ++ ++ proto_val = *proto; ++ *proto = htons(ETH_P_RTAG); ++ ++ src = skb_mac_header(skb); ++ head_len = p - src; ++ ++ rtag_len = sizeof(struct r_tag); ++ if (skb_cow_head(skb, rtag_len) < 0) ++ return -ENOMEM; ++ ++ skb_push(skb, rtag_len); ++ skb_reset_network_header(skb); ++ skb->mac_header -= rtag_len; ++ ++ dst = skb_mac_header(skb); ++ memmove(dst, src, head_len); ++ ++ rtag = (struct r_tag *)(dst + head_len); ++ rtag->encap_proto = proto_val; ++ rtag->sequence_nr = htons(frer_act->gen_seq_num); ++ rtag->reserved = 0; ++ ++ return 0; ++} ++ ++static void frer_rtag_pop(struct sk_buff *skb, struct tcf_frer *frer_act) ++{ ++ struct vlan_ethhdr *vlanhdr; ++ struct ethhdr *ethhdr; ++ struct r_tag *rtag; ++ int rtag_len, head_len; ++ unsigned char *dst, *src, *p; ++ __be16 *proto; ++ ++ ethhdr = (struct ethhdr *)skb_mac_header(skb); ++ ++ if (ethhdr->h_proto == htons(ETH_P_8021Q)) { ++ vlanhdr = (struct vlan_ethhdr *)ethhdr; ++ p = (unsigned char *)(vlanhdr + 1); ++ proto = &vlanhdr->h_vlan_encapsulated_proto; ++ } else { ++ p = (unsigned char *)(ethhdr + 1); ++ proto = ðhdr->h_proto; ++ } ++ ++ if (*proto != htons(ETH_P_RTAG)) ++ return; ++ ++ rtag = (struct r_tag *)p; ++ rtag_len = sizeof(struct r_tag); ++ *proto = rtag->encap_proto; ++ ++ src = skb_mac_header(skb); ++ head_len = p - src; ++ ++ skb->data = skb_mac_header(skb); ++ skb_pull(skb, rtag_len); ++ ++ skb_reset_mac_header(skb); ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) ++ skb->csum_start += rtag_len; ++ ++ dst = skb_mac_header(skb); ++ memmove(dst, src, head_len); ++} ++ ++static const struct tcf_frer_proto_ops rtag_ops = { ++ .encode = frer_rtag_encode, ++ .decode = frer_rtag_decode, ++ .tag_pop = frer_rtag_pop, ++}; ++ ++static int tcf_frer_init(struct net *net, struct nlattr *nla, ++ struct nlattr *est, struct tc_action **a, ++ struct tcf_proto *tp, u32 flags, ++ struct netlink_ext_ack *extack) ++{ ++ struct tc_action_net *tn = net_generic(net, frer_net_id); ++ bool bind = flags & TCA_ACT_FLAGS_BIND; ++ struct nlattr *tb[TCA_FRER_MAX + 1]; ++ struct tcf_chain *goto_ch = NULL; ++ struct tcf_frer *frer_act; ++ struct tc_frer *parm; ++ bool exists = false; ++ int ret = 0, err, index; ++ ktime_t remaining_tm; ++ ++ if (!nla) { ++ NL_SET_ERR_MSG_MOD(extack, "FRER requires attributes to be passed"); ++ return -EINVAL; ++ } ++ ++ err = nla_parse_nested_deprecated(tb, TCA_FRER_MAX, nla, frer_policy, extack); ++ if (err < 0) ++ return err; ++ ++ if (!tb[TCA_FRER_PARMS]) { ++ NL_SET_ERR_MSG_MOD(extack, "Missing required FRER parameters"); ++ return -EINVAL; ++ } ++ ++ parm = nla_data(tb[TCA_FRER_PARMS]); ++ index = parm->index; ++ ++ err = tcf_idr_check_alloc(tn, &index, a, bind); ++ if (err < 0) ++ return err; ++ exists = err; ++ ++ if (exists && bind) ++ return 0; ++ ++ if (!exists) { ++ ret = tcf_idr_create_from_flags(tn, index, est, a, ++ &act_frer_ops, bind, flags); ++ ++ if (ret) { ++ tcf_idr_cleanup(tn, index); ++ return ret; ++ } ++ ret = ACT_P_CREATED; ++ } else if (!(flags & TCA_ACT_FLAGS_REPLACE)) { ++ tcf_idr_release(*a, bind); ++ return -EEXIST; ++ } ++ ++ err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); ++ if (err < 0) ++ goto release_idr; ++ ++ frer_act = to_frer(*a); ++ ++ spin_lock_bh(&frer_act->tcf_lock); ++ goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); ++ ++ frer_act->tag_type = nla_get_u8(tb[TCA_FRER_TAG_TYPE]); ++ frer_act->tag_action = nla_get_u8(tb[TCA_FRER_TAG_ACTION]); ++ frer_act->recover = nla_get_u8(tb[TCA_FRER_RECOVER]); ++ frer_act->rcvy_alg = nla_get_u8(tb[TCA_FRER_RECOVER_ALG]); ++ frer_act->rcvy_history_len = nla_get_u8(tb[TCA_FRER_RECOVER_HISTORY_LEN]); ++ frer_act->rcvy_reset_msec = nla_get_u64(tb[TCA_FRER_RECOVER_RESET_TM]); ++ ++ frer_act->gen_seq_num = 0; ++ frer_act->seq_space = 1 << FRER_SEQ_SPACE; ++ frer_act->rcvy_seq_num = 0; ++ frer_act->seq_history = 0xFFFFFFFF; ++ frer_act->rcvy_take_noseq = true; ++ ++ switch (frer_act->tag_type) { ++ case TCA_FRER_TAG_RTAG: ++ frer_act->proto_ops = &rtag_ops; ++ break; ++ case TCA_FRER_TAG_HSR: ++ case TCA_FRER_TAG_PRP: ++ default: ++ spin_unlock_bh(&frer_act->tcf_lock); ++ return -EOPNOTSUPP; ++ } ++ ++ if (frer_act->recover && frer_act->rcvy_reset_msec) { ++ hrtimer_init(&frer_act->hrtimer, CLOCK_TAI, ++ HRTIMER_MODE_REL_SOFT); ++ frer_act->hrtimer.function = frer_hrtimer_func; ++ ++ remaining_tm = (ktime_t)(frer_act->rcvy_reset_msec * 1000000); ++ hrtimer_start(&frer_act->hrtimer, remaining_tm, ++ HRTIMER_MODE_REL_SOFT); ++ } ++ ++ spin_unlock_bh(&frer_act->tcf_lock); ++ ++ if (goto_ch) ++ tcf_chain_put_by_act(goto_ch); ++ ++ return ret; ++ ++release_idr: ++ tcf_idr_release(*a, bind); ++ return err; ++} ++ ++static void frer_seq_recovery_reset(struct tcf_frer *frer_act) ++{ ++ spin_lock(&frer_act->tcf_lock); ++ if (frer_act->rcvy_alg == TCA_FRER_RCVY_VECTOR_ALG) { ++ frer_act->rcvy_seq_num = frer_act->seq_space - 1; ++ frer_act->seq_history = 0; ++ } ++ frer_act->cps_seq_rcvy_resets++; ++ frer_act->take_any = true; ++ spin_unlock(&frer_act->tcf_lock); ++} ++ ++static void frer_shift_seq_history(int value, struct tcf_frer *frer_act) ++{ ++ int history_len = frer_act->rcvy_history_len; ++ ++ if ((frer_act->seq_history & BIT(history_len - 1)) == 0) ++ frer_act->cps_seq_rcvy_lost_pkts++; ++ ++ frer_act->seq_history <<= 1; ++ ++ if (value) ++ frer_act->seq_history |= BIT(0); ++} ++ ++static int frer_vector_rcvy_alg(struct tcf_frer *frer_act, int sequence, ++ bool individual) ++{ ++ struct hrtimer *timer = &frer_act->hrtimer; ++ bool reset_timer = false; ++ ktime_t remaining_tm; ++ int delta, ret; ++ ++ if (sequence == FRER_RCVY_INVALID_SEQ) { ++ frer_act->cps_seq_rcvy_tagless_pkts++; ++ if (frer_act->rcvy_take_noseq) { ++ reset_timer = true; ++ ret = FRER_RCVY_PASSED; ++ goto out; ++ } else { ++ return FRER_RCVY_DISCARDED; ++ } ++ } ++ ++ delta = (sequence - frer_act->rcvy_seq_num) & (frer_act->seq_space - 1); ++ /* -(RecovSeqSpace/2) <= delta <= ((RecovSeqSpace/2)-1) */ ++ if (delta & (frer_act->seq_space / 2)) ++ delta -= frer_act->seq_space; ++ ++ if (frer_act->take_any) { ++ frer_act->take_any = false; ++ frer_act->seq_history |= BIT(0); ++ frer_act->rcvy_seq_num = sequence; ++ ++ reset_timer = true; ++ ret = FRER_RCVY_PASSED; ++ goto out; ++ } ++ ++ if (delta >= frer_act->rcvy_history_len || ++ delta <= -frer_act->rcvy_history_len) { ++ /* Packet is out-of-range. */ ++ frer_act->cps_seq_rcvy_rogue_pkts++; ++ ++ if (individual) ++ reset_timer = true; ++ ++ ret = FRER_RCVY_DISCARDED; ++ goto out; ++ } else if (delta <= 0) { ++ /* Packet is old and in SequenceHistory. */ ++ if (frer_act->seq_history & BIT(-delta)) { ++ if (individual) ++ reset_timer = true; ++ ++ /* Packet has been seen. */ ++ ret = FRER_RCVY_DISCARDED; ++ goto out; ++ } else { ++ /* Packet has not been seen. */ ++ frer_act->seq_history |= BIT(-delta); ++ frer_act->cps_seq_rcvy_out_of_order_pkts++; ++ ++ reset_timer = true; ++ ret = FRER_RCVY_PASSED; ++ goto out; ++ } ++ } else { ++ /* Packet is not too far ahead of the one we want. */ ++ if (delta != 1) ++ frer_act->cps_seq_rcvy_out_of_order_pkts++; ++ ++ while (--delta) ++ frer_shift_seq_history(0, frer_act); ++ frer_shift_seq_history(1, frer_act); ++ frer_act->rcvy_seq_num = sequence; ++ ++ reset_timer = true; ++ ret = FRER_RCVY_PASSED; ++ goto out; ++ } ++out: ++ if (reset_timer && frer_act->rcvy_reset_msec) { ++ remaining_tm = ++ (ktime_t)(frer_act->rcvy_reset_msec * 1000000); ++ hrtimer_start(timer, remaining_tm, HRTIMER_MODE_REL_SOFT); ++ } ++ ++ return ret; ++} ++ ++static int frer_match_rcvy_alg(struct tcf_frer *frer_act, int sequence, ++ bool individual) ++{ ++ struct hrtimer *timer = &frer_act->hrtimer; ++ bool reset_timer = false; ++ ktime_t remaining_tm; ++ int delta, ret; ++ ++ if (sequence == FRER_RCVY_INVALID_SEQ) { ++ frer_act->cps_seq_rcvy_tagless_pkts++; ++ ++ return FRER_RCVY_PASSED; ++ } ++ ++ if (frer_act->take_any) { ++ frer_act->take_any = false; ++ frer_act->rcvy_seq_num = sequence; ++ ++ reset_timer = true; ++ ret = FRER_RCVY_PASSED; ++ goto out; ++ } ++ ++ delta = sequence - frer_act->rcvy_seq_num; ++ if (delta) { ++ /* Packet has not been seen, accept it. */ ++ if (delta != 1) ++ frer_act->cps_seq_rcvy_out_of_order_pkts++; ++ ++ frer_act->rcvy_seq_num = sequence; ++ ++ reset_timer = true; ++ ret = FRER_RCVY_PASSED; ++ goto out; ++ } else { ++ if (individual) ++ reset_timer = true; ++ ++ /* Packet has been seen. Do not forward. */ ++ ret = FRER_RCVY_DISCARDED; ++ goto out; ++ } ++ ++out: ++ if (reset_timer && frer_act->rcvy_reset_msec) { ++ remaining_tm = (ktime_t)(frer_act->rcvy_reset_msec * 1000000); ++ hrtimer_start(timer, remaining_tm, HRTIMER_MODE_REL_SOFT); ++ } ++ ++ return ret; ++} ++ ++static int tcf_frer_act(struct sk_buff *skb, const struct tc_action *a, ++ struct tcf_result *res) ++{ ++ struct tcf_frer *frer_act = to_frer(a); ++ bool ingress, individual; ++ int ret, retval; ++ int sequence; ++ ++ tcf_lastuse_update(&frer_act->tcf_tm); ++ tcf_action_update_bstats(&frer_act->common, skb); ++ ++ retval = READ_ONCE(frer_act->tcf_action); ++ ++ sequence = frer_act->proto_ops->decode(skb); ++ ++ ingress = skb_at_tc_ingress(skb); ++ individual = ingress; ++ ++ if (frer_act->recover) { ++ spin_lock(&frer_act->tcf_lock); ++ ++ if (frer_act->rcvy_alg == TCA_FRER_RCVY_VECTOR_ALG) ++ ret = frer_vector_rcvy_alg(frer_act, sequence, ++ individual); ++ else ++ ret = frer_match_rcvy_alg(frer_act, sequence, ++ individual); ++ if (ret) { ++ frer_act->tcf_qstats.drops++; ++ retval = TC_ACT_SHOT; ++ } ++ ++ if (frer_act->tag_action == TCA_FRER_TAG_POP) ++ frer_act->proto_ops->tag_pop(skb, frer_act); ++ ++ spin_unlock(&frer_act->tcf_lock); ++ ++ return retval; ++ } ++ ++ if (frer_act->tag_action == TCA_FRER_TAG_PUSH && ++ sequence == FRER_RCVY_INVALID_SEQ) { ++ spin_lock(&frer_act->tcf_lock); ++ ++ frer_seq_generation_alg(frer_act); ++ ++ frer_act->proto_ops->encode(skb, frer_act); ++ ++ spin_unlock(&frer_act->tcf_lock); ++ } ++ ++ return retval; ++} ++ ++static int tcf_frer_dump(struct sk_buff *skb, struct tc_action *a, ++ int bind, int ref) ++{ ++ unsigned char *b = skb_tail_pointer(skb); ++ struct tcf_frer *frer_act = to_frer(a); ++ struct tc_frer opt = { ++ .index = frer_act->tcf_index, ++ .refcnt = refcount_read(&frer_act->tcf_refcnt) - ref, ++ .bindcnt = atomic_read(&frer_act->tcf_bindcnt) - bind, ++ }; ++ struct tcf_t t; ++ ++ spin_lock_bh(&frer_act->tcf_lock); ++ opt.action = frer_act->tcf_action; ++ ++ if (nla_put(skb, TCA_FRER_PARMS, sizeof(opt), &opt)) ++ goto nla_put_failure; ++ ++ if (nla_put_u8(skb, TCA_FRER_TAG_TYPE, frer_act->tag_type)) ++ goto nla_put_failure; ++ ++ if (nla_put_u8(skb, TCA_FRER_TAG_ACTION, frer_act->tag_action)) ++ goto nla_put_failure; ++ ++ if (nla_put_u8(skb, TCA_FRER_RECOVER, frer_act->recover)) ++ goto nla_put_failure; ++ ++ if (nla_put_u8(skb, TCA_FRER_RECOVER_ALG, frer_act->rcvy_alg)) ++ goto nla_put_failure; ++ ++ if (nla_put_u8(skb, TCA_FRER_RECOVER_HISTORY_LEN, ++ frer_act->rcvy_history_len)) ++ goto nla_put_failure; ++ ++ if (nla_put_u64_64bit(skb, TCA_FRER_RECOVER_RESET_TM, ++ frer_act->rcvy_reset_msec, TCA_FRER_PAD)) ++ goto nla_put_failure; ++ ++ if (nla_put_u32(skb, TCA_FRER_RECOVER_TAGLESS_PKTS, ++ frer_act->cps_seq_rcvy_tagless_pkts)) ++ goto nla_put_failure; ++ ++ if (nla_put_u32(skb, TCA_FRER_RECOVER_OUT_OF_ORDER_PKTS, ++ frer_act->cps_seq_rcvy_out_of_order_pkts)) ++ goto nla_put_failure; ++ ++ if (nla_put_u32(skb, TCA_FRER_RECOVER_ROGUE_PKTS, ++ frer_act->cps_seq_rcvy_rogue_pkts)) ++ goto nla_put_failure; ++ ++ if (nla_put_u32(skb, TCA_FRER_RECOVER_LOST_PKTS, ++ frer_act->cps_seq_rcvy_lost_pkts)) ++ goto nla_put_failure; ++ ++ if (nla_put_u32(skb, TCA_FRER_RECOVER_RESETS, ++ frer_act->cps_seq_rcvy_resets)) ++ goto nla_put_failure; ++ ++ tcf_tm_dump(&t, &frer_act->tcf_tm); ++ if (nla_put_64bit(skb, TCA_FRER_TM, sizeof(t), ++ &t, TCA_FRER_PAD)) ++ goto nla_put_failure; ++ spin_unlock_bh(&frer_act->tcf_lock); ++ ++ return skb->len; ++ ++nla_put_failure: ++ spin_unlock_bh(&frer_act->tcf_lock); ++ nlmsg_trim(skb, b); ++ ++ return -1; ++} ++ ++static int tcf_frer_walker(struct net *net, struct sk_buff *skb, ++ struct netlink_callback *cb, int type, ++ const struct tc_action_ops *ops, ++ struct netlink_ext_ack *extack) ++{ ++ struct tc_action_net *tn = net_generic(net, frer_net_id); ++ ++ return tcf_generic_walker(tn, skb, cb, type, ops, extack); ++} ++ ++static int tcf_frer_search(struct net *net, struct tc_action **a, u32 index) ++{ ++ struct tc_action_net *tn = net_generic(net, frer_net_id); ++ ++ return tcf_idr_search(tn, a, index); ++} ++ ++static void tcf_frer_stats_update(struct tc_action *a, u64 bytes, u64 packets, ++ u64 drops, u64 lastuse, bool hw) ++{ ++ struct tcf_frer *frer_act = to_frer(a); ++ struct tcf_t *tm = &frer_act->tcf_tm; ++ ++ tcf_action_update_stats(a, bytes, packets, drops, hw); ++ tm->lastuse = max_t(u64, tm->lastuse, lastuse); ++} ++ ++static void tcf_frer_cleanup(struct tc_action *a) ++{ ++ struct tcf_frer *frer_act = to_frer(a); ++ ++ if (frer_act->rcvy_reset_msec) ++ hrtimer_cancel(&frer_act->hrtimer); ++} ++ ++static size_t tcf_frer_get_fill_size(const struct tc_action *act) ++{ ++ return nla_total_size(sizeof(struct tc_frer)); ++} ++ ++static int tcf_frer_offload_act_setup(struct tc_action *act, void *entry_data, ++ u32 *index_inc, bool bind, ++ struct netlink_ext_ack *extack) ++{ ++ if (bind) { ++ struct flow_action_entry *entry = entry_data; ++ ++ entry->id = FLOW_ACTION_FRER; ++ entry->frer.tag_type = to_frer(act)->tag_type; ++ entry->frer.tag_action = to_frer(act)->tag_action; ++ entry->frer.recover = to_frer(act)->recover; ++ entry->frer.rcvy_alg = to_frer(act)->rcvy_alg; ++ entry->frer.rcvy_history_len = ++ to_frer(act)->rcvy_history_len; ++ entry->frer.rcvy_reset_msec = ++ to_frer(act)->rcvy_reset_msec; ++ ++ *index_inc = 1; ++ } else { ++ struct flow_offload_action *fl_action = entry_data; ++ ++ fl_action->id = FLOW_ACTION_FRER; ++ } ++ ++ return 0; ++} ++ ++static struct tc_action_ops act_frer_ops = { ++ .kind = "frer", ++ .id = TCA_ID_FRER, ++ .owner = THIS_MODULE, ++ .act = tcf_frer_act, ++ .init = tcf_frer_init, ++ .cleanup = tcf_frer_cleanup, ++ .dump = tcf_frer_dump, ++ .walk = tcf_frer_walker, ++ .stats_update = tcf_frer_stats_update, ++ .get_fill_size = tcf_frer_get_fill_size, ++ .offload_act_setup = tcf_frer_offload_act_setup, ++ .lookup = tcf_frer_search, ++ .size = sizeof(struct tcf_frer), ++}; ++ ++static __net_init int frer_init_net(struct net *net) ++{ ++ struct tc_action_net *tn = net_generic(net, frer_net_id); ++ ++ return tc_action_net_init(net, tn, &act_frer_ops); ++} ++ ++static void __net_exit frer_exit_net(struct list_head *net_list) ++{ ++ tc_action_net_exit(net_list, frer_net_id); ++}; ++ ++static struct pernet_operations frer_net_ops = { ++ .init = frer_init_net, ++ .exit_batch = frer_exit_net, ++ .id = &frer_net_id, ++ .size = sizeof(struct tc_action_net), ++}; ++ ++static int __init frer_init_module(void) ++{ ++ return tcf_register_action(&act_frer_ops, &frer_net_ops); ++} ++ ++static void __exit frer_cleanup_module(void) ++{ ++ tcf_unregister_action(&act_frer_ops, &frer_net_ops); ++} ++ ++module_init(frer_init_module); ++module_exit(frer_cleanup_module); ++MODULE_LICENSE("GPL v2"); +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/socket.c b/net/socket.c +index 9db33cd4a71b..69e7d4bc021a 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -110,6 +110,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; +@@ -671,6 +675,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; + } + +@@ -1853,6 +1861,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; + } +@@ -2169,6 +2186,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)) +@@ -2190,6 +2211,7 @@ int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, + msg.msg_namelen = addr_len; + } + flags &= ~MSG_INTERNAL_SENDMSG_FLAGS; ++ + if (sock->file->f_flags & O_NONBLOCK) + flags |= MSG_DONTWAIT; + msg.msg_flags = flags; +@@ -2235,6 +2257,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 13a2d8089609..b8a6c2db906c 100644 +--- a/net/tsn/genl_tsn.c ++++ b/net/tsn/genl_tsn.c +@@ -1136,12 +1136,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; + } + } + +@@ -2121,6 +2120,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; +diff --git a/tools/virtio/vt_test.sh b/tools/virtio/vt_test.sh +new file mode 100755 +index 000000000000..4d9479404bd0 +--- /dev/null ++++ b/tools/virtio/vt_test.sh +@@ -0,0 +1,79 @@ ++#! /bin/bash ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright 2022-2024 NXP ++ ++set -eo pipefail ++ ++usage() ++{ ++ echo "USAGE: $0 [-h] [-s pkt_size] [-r regression] [-t type] [-b backend copy] [-f frontend copy]" ++ echo -e "-s: Packet size: max 2048 Bytes, default: 64 Bytes" ++ echo -e "-r: Regression times: default: 1000" ++ echo -e "-t: Test type: 0: TX (frontend to backend); 1: RX (backend to frontend)" ++ echo -e "-b: Backend copy buffer option: 0: not copy; 1: copy" ++ echo -e "-f: Frontend copy buffer option: 0: not copy; 1: copy" ++ echo -e "-h: This USAGE info" ++ exit 1 ++} ++ ++setvar() ++{ ++ local varname=$1 ++ shift ++ if [ -z "${varname}" ]; then ++ usage ++ else ++ eval "$varname=\"$@\"" ++ fi ++} ++ ++find_virtio_trans () ++{ ++ VIRTIO_TRANS=`find /sys/bus/platform/devices/ -name *.virtio_trans` ++ if [ -z "${VIRTIO_TRANS}" ] || [ ! -d ${VIRTIO_TRANS} ]; then ++ echo "${VIRTIO_TRANS}" ++ exit 2; ++ fi ++} ++ ++while getopts 'hs:t:r:b:f:' c ++do ++ case $c in ++ h) usage ;; ++ s) setvar PKT_SIZE $OPTARG ;; ++ t) setvar TYPE $OPTARG ;; ++ r) setvar REGRESS $OPTARG ;; ++ b) setvar BACK_COPY $OPTARG ;; ++ f) setvar FRONT_COPY $OPTARG ;; ++ esac ++ ++done ++ ++if [ -z "${TYPE}" ]; then ++ TYPE=0; ++fi ++ ++if [ -z "${PKT_SIZE}" ]; then ++ PKT_SIZE=64; ++fi ++ ++if [ -z "${REGRESS}" ]; then ++ REGRESS=1000; ++fi ++ ++if [ -z "${BACK_COPY}" ]; then ++ BACK_COPY=0; ++fi ++ ++if [ -z "${FRONT_COPY}" ]; then ++ FRONT_COPY=0; ++fi ++ ++CONFIG=$(( $(( TYPE << 0 )) | $(( BACK_COPY << 1 )) | $(( FRONT_COPY << 2 )) )) ++ ++find_virtio_trans ++ ++echo ${REGRESS} > ${VIRTIO_TRANS}/virtio0/vt_regression&& ++echo ${PKT_SIZE} > ${VIRTIO_TRANS}/virtio0/vt_pkt_size&& ++echo ${CONFIG} > ${VIRTIO_TRANS}/virtio0/vt_config&& ++echo 1 > ${VIRTIO_TRANS}/virtio0/vt_control; diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/imx-nxp-bsp/fragment-nxp-rt.config b/meta-digi-arm/recipes-kernel/linux/linux-dey/imx-nxp-bsp/fragment-nxp-rt.config new file mode 100644 index 000000000..53f9881e7 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/imx-nxp-bsp/fragment-nxp-rt.config @@ -0,0 +1,33 @@ +CONFIG_BPF_JIT_ALWAYS_ON=y +CONFIG_XDP_SOCKETS=y +CONFIG_NET_DSA_NETC_PTP=y +CONFIG_NET_DSA_SJA1105=m +CONFIG_NET_DSA_SJA1105_PTP=y +CONFIG_NET_DSA_SJA1105_TAS=y +CONFIG_NET_DSA_SJA1105_VL=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_FRER=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_PKTGEN=y +CONFIG_VIRTIO_NET=m +CONFIG_NET_DSA_NETC=m +CONFIG_NET_DSA_NETC_PTP=y +CONFIG_FEC_ECAT=y +CONFIG_VIRTIO_MMIO=m +CONFIG_EXPERT=y +CONFIG_PREEMPT_RT=y +CONFIG_RPMSG_TTY=m +CONFIG_GENERIC_SOFTWARE_MAILBOX=y +CONFIG_VIRTIO_TRANS=m +CONFIG_ACPI_CONTAINER=y +CONFIG_THERMAL=y +CONFIG_CLK_QORIQ=y +# CONFIG_ACPI_PROCESSOR is not set +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_SCHED_MC is not set +# CONFIG_ARM_PSCI_CPUIDLE is not set +# CONFIG_CPU_IDLE is not set diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey_6.6.bb b/meta-digi-arm/recipes-kernel/linux/linux-dey_6.6.bb index 213a1ce88..fbd272215 100644 --- a/meta-digi-arm/recipes-kernel/linux/linux-dey_6.6.bb +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey_6.6.bb @@ -7,21 +7,30 @@ SRCBRANCH:stm32mp2common = "v6.6.48/stm/master" SRCREV = "${AUTOREV}" SRCREV:stm32mp2common = "${AUTOREV}" -STM_RT_FILES = " \ - file://0010-Rebase-on-v6.6.48-rt40.patch \ - file://0011-v6.6-stm32mp-rt-r1.patch \ - file://fragment-08-deactivate-rng.config \ +# Define RT patches per machine +RT_FILES:use-nxp-bsp = " \ + file://0001-add-RT-support-based-on-latest-linux_6.6.36.patch \ + file://fragment-nxp-rt.config \ " -SRC_URI:append:stm32mpcommon = " \ - ${@bb.utils.contains('DISTRO_FEATURES', 'rt', '${STM_RT_FILES}', '', d)} \ +RT_FILES:stm32mpcommon = " \ + file://0010-Rebase-on-v6.6.48-rt40.patch \ + file://0011-v6.6-stm32mp-rt-r1.patch \ + file://fragment-08-deactivate-rng.config \ +" +SRC_URI:append = " \ + ${@bb.utils.contains('DISTRO_FEATURES', 'rt', '${RT_FILES}', '', d)} \ " -STM_RT_CONFIG_FRAGS = " \ - ${S}/arch/arm64/configs/fragment-07-rt.config \ - ${S}/arch/arm64/configs/fragment-07-rt-sysvinit.config \ - ${WORKDIR}/fragment-08-deactivate-rng.config \ +# Define RT config fragments per machine +RT_CONFIG_FRAGS:use-nxp-bsp = " ${WORKDIR}/fragment-nxp-rt.config" +RT_CONFIG_FRAGS:stm32mpcommon = " \ + ${S}/arch/arm64/configs/fragment-07-rt.config \ + ${S}/arch/arm64/configs/fragment-07-rt-sysvinit.config \ + ${WORKDIR}/fragment-08-deactivate-rng.config \ +" +KERNEL_CONFIG_FRAGMENTS:append = " \ + ${@bb.utils.contains('DISTRO_FEATURES', 'rt', '${RT_CONFIG_FRAGS}', '', d)} \ " -KERNEL_CONFIG_FRAGMENTS:append:stm32mpcommon = " ${@bb.utils.contains('DISTRO_FEATURES', 'rt', '${STM_RT_CONFIG_FRAGS}', '', d)}" # Blacklist btnxpuart module. It will be managed by the bluetooth-init script KERNEL_MODULE_PROBECONF:ccimx9 += "btnxpuart"